actual initial commit

This commit is contained in:
'mr software' 2022-07-07 15:21:56 -07:00
parent 786f200861
commit f86f9cad59
No known key found for this signature in database
GPG Key ID: 7FA9464091B4ABFA
171 changed files with 66882 additions and 0 deletions

48
b Executable file
View File

@ -0,0 +1,48 @@
# if youre not on linux... good luck i guess
if [ "$1" = 'cln' ]; then
rm nacp xmpnx* main.o main.d libxmp/obj/* libxmp/libxmp.o
exit
fi
if [ "$1" = 'dep' ]; then
cd libxmp
export CC="/opt/devkitpro/devkitA64/bin/aarch64-none-elf-gcc -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE"
./build
cd ..
fi
ARCHFLAGS="-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE"
EFLAGS="-g"
/opt/devkitpro/devkitA64/bin/aarch64-none-elf-gcc \
-r main.c libxmp/libxmp.o \
$ARCHFLAGS \
$EFLAGS \
-MMD \
-MP \
-Wall \
-O3 \
-ffunction-sections \
-I/opt/devkitpro/portlibs/switch/include \
-I/opt/devkitpro/libnx/include \
-Ilibxmp \
-D__SWITCH__ \
-o main.o
/opt/devkitpro/devkitA64/bin/aarch64-none-elf-gcc main.o \
$ARCHFLAGS \
-specs=/opt/devkitpro/libnx/switch.specs \
$EFLAGS \
-Wl,-Map,xmpnx.map \
-L/opt/devkitpro/portlibs/switch/lib \
-L/opt/devkitpro/libnx/lib \
-lnx \
-o xmpnx.elf \
`/opt/devkitpro/portlibs/switch/bin/aarch64-none-elf-pkg-config --libs SDL2_mixer`
# idk why this is needed
/opt/devkitpro/devkitA64/bin/aarch64-none-elf-gcc-nm -CSn xmpnx.elf > xmpnx.lst
/opt/devkitpro/tools/bin/nacptool --create "xmpnx" "mothcompute" "0" nacp
/opt/devkitpro/tools/bin/elf2nro xmpnx.elf xmpnx.nro --nacp=nacp

3
libxmp/build Executable file
View File

@ -0,0 +1,3 @@
mkdir -p obj
for i in `find src | grep '\.c$'`; do $CC -c $i -o obj/`basename $i | sed 's/c$/o/'` -I. -Isrc -Isrc/loaders -DLIBXMP_NO_PROWIZARD; done
$CC -r obj/* -o libxmp.o

3
libxmp/readme Normal file
View File

@ -0,0 +1,3 @@
based on https://github.com/libxmp/libxmp/commit/8e4a5e1509c40716344830c25303b1b4ac48ae85. removed all prowizard files and unused code mentioned in https://github.com/libxmp/libxmp/issues/387. works fine on aarch64 and x86_64 so presumably it is just fine. unfortunately due to the depackers it is still all gpl i think
this massacre brought to you by mothcompute

187
libxmp/src/callbackio.h Normal file
View File

@ -0,0 +1,187 @@
#ifndef LIBXMP_CALLBACKIO_H
#define LIBXMP_CALLBACKIO_H
#include <stddef.h>
#include "common.h"
typedef struct {
void *priv;
struct xmp_callbacks callbacks;
int eof;
} CBFILE;
LIBXMP_BEGIN_DECLS
static inline uint8 cbread8(CBFILE *f, int *err)
{
uint8 x = 0xff;
size_t r = f->callbacks.read_func(&x, 1, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (err) *err = f->eof;
return x;
}
static inline int8 cbread8s(CBFILE *f, int *err)
{
return (int8)cbread8(f, err);
}
static inline uint16 cbread16l(CBFILE *f, int *err)
{
uint8 buf[2];
uint16 x = 0xffff;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem16l(buf);
if (err) *err = f->eof;
return x;
}
static inline uint16 cbread16b(CBFILE *f, int *err)
{
uint8 buf[2];
uint16 x = 0xffff;
size_t r = f->callbacks.read_func(buf, 2, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem16b(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread24l(CBFILE *f, int *err)
{
uint8 buf[3];
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem24l(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread24b(CBFILE *f, int *err)
{
uint8 buf[3];
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 3, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem24b(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread32l(CBFILE *f, int *err)
{
uint8 buf[4];
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem32l(buf);
if (err) *err = f->eof;
return x;
}
static inline uint32 cbread32b(CBFILE *f, int *err)
{
uint8 buf[4];
uint32 x = 0xffffffff;
size_t r = f->callbacks.read_func(buf, 4, 1, f->priv);
f->eof = (r == 1) ? 0 : EOF;
if (r) x = readmem32b(buf);
if (err) *err = f->eof;
return x;
}
static inline size_t cbread(void *dest, size_t len, size_t nmemb, CBFILE *f)
{
size_t r = f->callbacks.read_func(dest, (unsigned long)len,
(unsigned long)nmemb, f->priv);
f->eof = (r < nmemb) ? EOF : 0;
return r;
}
static inline int cbseek(CBFILE *f, long offset, int whence)
{
f->eof = 0;
return f->callbacks.seek_func(f->priv, offset, whence);
}
static inline long cbtell(CBFILE *f)
{
return f->callbacks.tell_func(f->priv);
}
static inline int cbeof(CBFILE *f)
{
return f->eof;
}
static inline long cbfilelength(CBFILE *f)
{
long pos = f->callbacks.tell_func(f->priv);
long length;
int r;
if (pos < 0)
return EOF;
r = f->callbacks.seek_func(f->priv, 0, SEEK_END);
if (r < 0)
return EOF;
length = f->callbacks.tell_func(f->priv);
r = f->callbacks.seek_func(f->priv, pos, SEEK_SET);
return length;
}
static inline CBFILE *cbopen(void *priv, struct xmp_callbacks callbacks)
{
CBFILE *f;
if (priv == NULL || callbacks.read_func == NULL ||
callbacks.seek_func == NULL || callbacks.tell_func == NULL)
goto err;
f = (CBFILE *)calloc(1, sizeof(CBFILE));
if (f == NULL)
goto err;
f->priv = priv;
f->callbacks = callbacks;
f->eof = 0;
return f;
err:
if (priv && callbacks.close_func)
callbacks.close_func(priv);
return NULL;
}
static inline int cbclose(CBFILE *f)
{
int r = 0;
if (f->callbacks.close_func != NULL)
r = f->callbacks.close_func(f->priv);
free(f);
return r;
}
LIBXMP_END_DECLS
#endif /* LIBXMP_CALLBACKIO_H */

528
libxmp/src/common.h Normal file
View File

@ -0,0 +1,528 @@
#ifndef LIBXMP_COMMON_H
#define LIBXMP_COMMON_H
#ifdef LIBXMP_CORE_PLAYER
#ifndef LIBXMP_NO_PROWIZARD
#define LIBXMP_NO_PROWIZARD
#endif
#ifndef LIBXMP_NO_DEPACKERS
#define LIBXMP_NO_DEPACKERS
#endif
#endif
#include <stdarg.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "xmp.h"
#undef LIBXMP_EXPORT_VAR
#if defined(EMSCRIPTEN)
#include <emscripten.h>
#define LIBXMP_EXPORT_VAR EMSCRIPTEN_KEEPALIVE
#else
#define LIBXMP_EXPORT_VAR
#endif
#ifndef __cplusplus
#define LIBXMP_BEGIN_DECLS
#define LIBXMP_END_DECLS
#else
#define LIBXMP_BEGIN_DECLS extern "C" {
#define LIBXMP_END_DECLS }
#endif
#if defined(_MSC_VER) && !defined(__cplusplus)
#define inline __inline
#endif
#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
(defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
(defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
#define LIBXMP_RESTRICT __restrict
#else
#define LIBXMP_RESTRICT
#endif
#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__EMX__)
#define XMP_MAXPATH _MAX_PATH
#elif defined(PATH_MAX)
#define XMP_MAXPATH PATH_MAX
#else
#define XMP_MAXPATH 1024
#endif
#if defined(__MORPHOS__) || defined(__AROS__) || defined(__AMIGA__) \
|| defined(__amigaos__) || defined(__amigaos4__) || defined(AMIGA)
#define LIBXMP_AMIGA 1
#endif
#ifdef HAVE_EXTERNAL_VISIBILITY
#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default"),externally_visible))
#else
#define LIBXMP_EXPORT_VERSIONED __attribute__((visibility("default")))
#endif
#ifdef HAVE_ATTRIBUTE_SYMVER
#define LIBXMP_ATTRIB_SYMVER(_sym) __attribute__((__symver__(_sym)))
#else
#define LIBXMP_ATTRIB_SYMVER(_sym)
#endif
/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
*/
#if defined B_BEOS_VERSION
# include <SupportDefs.h>
#elif defined __amigaos4__
# include <exec/types.h>
#else
typedef signed char int8;
typedef signed short int int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;
#endif
#ifdef _MSC_VER /* MSVC6 has no long long */
typedef signed __int64 int64;
typedef unsigned __int64 uint64;
#elif !(defined(B_BEOS_VERSION) || defined(__amigaos4__))
typedef unsigned long long uint64;
typedef signed long long int64;
#endif
#ifndef LIBXMP_CORE_PLAYER
#define LIBXMP_PAULA_SIMULATOR
#endif
/* Constants */
#define PAL_RATE 250.0 /* 1 / (50Hz * 80us) */
#define NTSC_RATE 208.0 /* 1 / (60Hz * 80us) */
#define C4_PAL_RATE 8287 /* 7093789.2 / period (C4) * 2 */
#define C4_NTSC_RATE 8363 /* 7159090.5 / period (C4) * 2 */
/* [Amiga] PAL color carrier frequency (PCCF) = 4.43361825 MHz */
/* [Amiga] CPU clock = 1.6 * PCCF = 7.0937892 MHz */
#define DEFAULT_AMPLIFY 1
#define DEFAULT_MIX 100
#define MSN(x) (((x)&0xf0)>>4)
#define LSN(x) ((x)&0x0f)
#define SET_FLAG(a,b) ((a)|=(b))
#define RESET_FLAG(a,b) ((a)&=~(b))
#define TEST_FLAG(a,b) !!((a)&(b))
/* libxmp_get_filetype() return values */
#define XMP_FILETYPE_NONE 0
#define XMP_FILETYPE_DIR (1 << 0)
#define XMP_FILETYPE_FILE (1 << 1)
#define CLAMP(x,a,b) do { \
if ((x) < (a)) (x) = (a); \
else if ((x) > (b)) (x) = (b); \
} while (0)
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define TRACK_NUM(a,c) m->mod.xxp[a]->index[c]
#define EVENT(a,c,r) m->mod.xxt[TRACK_NUM((a),(c))]->event[r]
#ifdef _MSC_VER
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#ifdef DEBUG
#define D_ libxmp_msvc_dbgprint /* in win32.c */
void libxmp_msvc_dbgprint(const char *text, ...);
#else
/* VS prior to VC7.1 does not support variadic macros.
* VC8.0 does not optimize unused parameters passing. */
#if _MSC_VER < 1400
static void __inline D_(const char *text, ...) {
do { } while (0);
}
#else
#define D_(...) do {} while (0)
#endif
#endif
#elif defined __ANDROID__
#ifdef DEBUG
#include <android/log.h>
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#define D_(...) do { \
__android_log_print(ANDROID_LOG_DEBUG, "libxmp", __VA_ARGS__); \
} while (0)
#else
#define D_(...) do {} while (0)
#endif
#else
#ifdef DEBUG
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define LIBXMP_FUNC __func__
#else
#define LIBXMP_FUNC __FUNCTION__
#endif
#define D_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m"
#define D_(...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, LIBXMP_FUNC, \
__FILE__, __LINE__); printf (__VA_ARGS__); printf ("\x1b[0m\n"); \
} while (0)
#else
#define D_(...) do {} while (0)
#endif
#endif /* !_MSC_VER */
#ifdef _MSC_VER
#define dup _dup
#define fileno _fileno
#define strnicmp _strnicmp
#define fdopen _fdopen
#define open _open
#define close _close
#define unlink _unlink
#define S_ISDIR(x) (((x)&_S_IFDIR) != 0)
#endif
#if defined(_WIN32) || defined(__WATCOMC__) /* in win32.c */
#define USE_LIBXMP_SNPRINTF
/* MSVC 2015+ has C99 compliant snprintf and vsnprintf implementations.
* If __USE_MINGW_ANSI_STDIO is defined for MinGW (which it is by default),
* compliant implementations will be used instead of the broken MSVCRT
* functions. Additionally, GCC may optimize some calls to those functions. */
#if defined(_MSC_VER) && _MSC_VER >= 1900
#undef USE_LIBXMP_SNPRINTF
#endif
#if defined(__MINGW32__) && defined(__USE_MINGW_ANSI_STDIO) && (__USE_MINGW_ANSI_STDIO != 0)
#undef USE_LIBXMP_SNPRINTF
#endif
#ifdef USE_LIBXMP_SNPRINTF
int libxmp_vsnprintf(char *, size_t, const char *, va_list);
int libxmp_snprintf (char *, size_t, const char *, ...);
#define snprintf libxmp_snprintf
#define vsnprintf libxmp_vsnprintf
#endif
#endif
/* Quirks */
#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */
#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */
#define QUIRK_PROTRACK (1 << 2) /* Use Protracker-specific quirks */
#define QUIRK_ST3BUGS (1 << 4) /* Scream Tracker 3 bug compatibility */
#define QUIRK_FINEFX (1 << 5) /* Enable 0xf/0xe for fine effects */
#define QUIRK_VSALL (1 << 6) /* Volume slides in all frames */
#define QUIRK_PBALL (1 << 7) /* Pitch bending in all frames */
#define QUIRK_PERPAT (1 << 8) /* Cancel persistent fx at pat start */
#define QUIRK_VOLPDN (1 << 9) /* Set priority to volume slide down */
#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */
#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */
#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */
#define QUIRK_INVLOOP (1 << 13) /* Enable invert loop */
#define QUIRK_MODRNG (1 << 13) /* Limit periods to MOD range */
#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */
#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */
#define QUIRK_FILTER (1 << 16) /* Enable filter */
#define QUIRK_IGSTPOR (1 << 17) /* Ignore stray tone portamento */
#define QUIRK_KEYOFF (1 << 18) /* Keyoff doesn't reset fadeout */
#define QUIRK_VIBHALF (1 << 19) /* Vibrato is half as deep */
#define QUIRK_VIBALL (1 << 20) /* Vibrato in all frames */
#define QUIRK_VIBINV (1 << 21) /* Vibrato has inverse waveform */
#define QUIRK_PRENV (1 << 22) /* Portamento resets envelope & fade */
#define QUIRK_ITOLDFX (1 << 23) /* IT old effects mode */
#define QUIRK_S3MRTG (1 << 24) /* S3M-style retrig when count == 0 */
#define QUIRK_RTDELAY (1 << 25) /* Delay effect retrigs instrument */
#define QUIRK_FT2BUGS (1 << 26) /* FT2 bug compatibility */
#define QUIRK_MARKER (1 << 27) /* Patterns 0xfe and 0xff reserved */
#define QUIRK_NOBPM (1 << 28) /* Adjust speed only, no BPM */
#define QUIRK_ARPMEM (1 << 29) /* Arpeggio has memory (S3M_ARPEGGIO) */
#define QUIRK_RSTCHN (1 << 30) /* Reset channel on sample end */
#define HAS_QUIRK(x) (m->quirk & (x))
/* Format quirks */
#define QUIRKS_ST3 (QUIRK_S3MLOOP | QUIRK_VOLPDN | QUIRK_FINEFX | \
QUIRK_S3MRTG | QUIRK_MARKER | QUIRK_RSTCHN )
#define QUIRKS_FT2 (QUIRK_RTDELAY | QUIRK_FINEFX )
#define QUIRKS_IT (QUIRK_S3MLOOP | QUIRK_FINEFX | QUIRK_VIBALL | \
QUIRK_ENVFADE | QUIRK_ITVPOR | QUIRK_KEYOFF | \
QUIRK_VIRTUAL | QUIRK_FILTER | QUIRK_RSTCHN | \
QUIRK_IGSTPOR | QUIRK_S3MRTG | QUIRK_MARKER )
/* DSP effects */
#define DSP_EFFECT_CUTOFF 0x02
#define DSP_EFFECT_RESONANCE 0x03
#define DSP_EFFECT_FILTER_A0 0xb0
#define DSP_EFFECT_FILTER_B0 0xb1
#define DSP_EFFECT_FILTER_B1 0xb2
/* Time factor */
#define DEFAULT_TIME_FACTOR 10.0
#define MED_TIME_FACTOR 2.64
#define FAR_TIME_FACTOR 4.01373 /* See far_extras.c */
#define MAX_SEQUENCES 255
#define MAX_SAMPLE_SIZE 0x10000000
#define MAX_SAMPLES 1024
#define MAX_INSTRUMENTS 255
#define MAX_PATTERNS 256
#define IS_PLAYER_MODE_MOD() (m->read_event_type == READ_EVENT_MOD)
#define IS_PLAYER_MODE_FT2() (m->read_event_type == READ_EVENT_FT2)
#define IS_PLAYER_MODE_ST3() (m->read_event_type == READ_EVENT_ST3)
#define IS_PLAYER_MODE_IT() (m->read_event_type == READ_EVENT_IT)
#define IS_PLAYER_MODE_MED() (m->read_event_type == READ_EVENT_MED)
#define IS_PERIOD_MODRNG() (m->period_type == PERIOD_MODRNG)
#define IS_PERIOD_LINEAR() (m->period_type == PERIOD_LINEAR)
#define IS_PERIOD_CSPD() (m->period_type == PERIOD_CSPD)
#define IS_AMIGA_MOD() (IS_PLAYER_MODE_MOD() && IS_PERIOD_MODRNG())
struct ord_data {
int speed;
int bpm;
int gvl;
int time;
int start_row;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed;
#endif
};
/* Context */
struct smix_data {
int chn;
int ins;
int smp;
struct xmp_instrument *xxi;
struct xmp_sample *xxs;
};
/* This will be added to the sample structure in the next API revision */
struct extra_sample_data {
double c5spd;
int sus;
int sue;
};
struct midi_macro {
char data[32];
};
struct midi_macro_data {
struct midi_macro param[16];
struct midi_macro fixed[128];
};
struct module_data {
struct xmp_module mod;
char *dirname; /* file dirname */
char *basename; /* file basename */
const char *filename; /* Module file name */
char *comment; /* Comments, if any */
uint8 md5[16]; /* MD5 message digest */
int size; /* File size */
double rrate; /* Replay rate */
double time_factor; /* Time conversion constant */
int c4rate; /* C4 replay rate */
int volbase; /* Volume base */
int gvolbase; /* Global volume base */
int gvol; /* Global volume */
const int *vol_table; /* Volume translation table */
int quirk; /* player quirks */
#define READ_EVENT_MOD 0
#define READ_EVENT_FT2 1
#define READ_EVENT_ST3 2
#define READ_EVENT_IT 3
#define READ_EVENT_MED 4
int read_event_type;
#define PERIOD_AMIGA 0
#define PERIOD_MODRNG 1
#define PERIOD_LINEAR 2
#define PERIOD_CSPD 3
int period_type;
int smpctl; /* sample control flags */
int defpan; /* default pan setting */
struct ord_data xxo_info[XMP_MAX_MOD_LENGTH];
int num_sequences;
struct xmp_sequence seq_data[MAX_SEQUENCES];
char *instrument_path;
void *extra; /* format-specific extra fields */
uint8 **scan_cnt; /* scan counters */
struct extra_sample_data *xtra;
struct midi_macro_data *midi;
};
struct pattern_loop {
int start;
int count;
};
struct flow_control {
int pbreak;
int jump;
int delay;
int jumpline;
int loop_chn;
#ifndef LIBXMP_CORE_PLAYER
int jump_in_pat;
#endif
struct pattern_loop *loop;
int num_rows;
int end_point;
#define ROWDELAY_ON (1 << 0)
#define ROWDELAY_FIRST_FRAME (1 << 1)
int rowdelay; /* For IT pattern row delay */
int rowdelay_set;
};
struct virt_channel {
int count;
int map;
};
struct scan_data {
int time; /* replay time in ms */
int row;
int ord;
int num;
};
struct player_data {
int ord;
int pos;
int row;
int frame;
int speed;
int bpm;
int mode;
int player_flags;
int flags;
double current_time;
double frame_time;
int loop_count;
int sequence;
unsigned char sequence_control[XMP_MAX_MOD_LENGTH];
int smix_vol; /* SFX volume */
int master_vol; /* Music volume */
int gvol;
struct flow_control flow;
struct scan_data *scan;
struct channel_data *xc_data;
int channel_vol[XMP_MAX_CHANNELS];
char channel_mute[XMP_MAX_CHANNELS];
struct virt_control {
int num_tracks; /* Number of tracks */
int virt_channels; /* Number of virtual channels */
int virt_used; /* Number of voices currently in use */
int maxvoc; /* Number of sound card voices */
struct virt_channel *virt_channel;
struct mixer_voice *voice_array;
} virt;
struct xmp_event inject_event[XMP_MAX_CHANNELS];
struct {
int consumed;
int in_size;
char *in_buffer;
} buffer_data;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed; /* For IceTracker speed effect */
#endif
int filter; /* Amiga led filter */
};
struct mixer_data {
int freq; /* sampling rate */
int format; /* sample format */
int amplify; /* amplification multiplier */
int mix; /* percentage of channel separation */
int interp; /* interpolation type */
int dsp; /* dsp effect flags */
char *buffer; /* output buffer */
int32 *buf32; /* temporary buffer for 32 bit samples */
int numvoc; /* default softmixer voices number */
int ticksize;
int dtright; /* anticlick control, right channel */
int dtleft; /* anticlick control, left channel */
int bidir_adjust; /* adjustment for IT bidirectional loops */
double pbase; /* period base */
};
struct context_data {
struct player_data p;
struct mixer_data s;
struct module_data m;
struct smix_data smix;
int state;
};
/* Prototypes */
char *libxmp_adjust_string (char *);
int libxmp_prepare_scan (struct context_data *);
void libxmp_free_scan (struct context_data *);
int libxmp_scan_sequences (struct context_data *);
int libxmp_get_sequence (struct context_data *, int);
int libxmp_set_player_mode (struct context_data *);
void libxmp_reset_flow (struct context_data *);
int8 read8s (FILE *, int *err);
uint8 read8 (FILE *, int *err);
uint16 read16l (FILE *, int *err);
uint16 read16b (FILE *, int *err);
uint32 read24l (FILE *, int *err);
uint32 read24b (FILE *, int *err);
uint32 read32l (FILE *, int *err);
uint32 read32b (FILE *, int *err);
static inline void write8 (FILE *f, uint8 b) {
fputc(b, f);
}
void write16l (FILE *, uint16);
void write16b (FILE *, uint16);
void write32l (FILE *, uint32);
void write32b (FILE *, uint32);
uint16 readmem16l (const uint8 *);
uint16 readmem16b (const uint8 *);
uint32 readmem24l (const uint8 *);
uint32 readmem24b (const uint8 *);
uint32 readmem32l (const uint8 *);
uint32 readmem32b (const uint8 *);
struct xmp_instrument *libxmp_get_instrument(struct context_data *, int);
struct xmp_sample *libxmp_get_sample(struct context_data *, int);
char *libxmp_strdup(const char *);
int libxmp_get_filetype (const char *);
#endif /* LIBXMP_COMMON_H */

590
libxmp/src/control.c Normal file
View File

@ -0,0 +1,590 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "format.h"
#include "virtual.h"
#include "mixer.h"
const char *xmp_version LIBXMP_EXPORT_VAR = XMP_VERSION;
const unsigned int xmp_vercode LIBXMP_EXPORT_VAR = XMP_VERCODE;
xmp_context xmp_create_context(void)
{
struct context_data *ctx;
ctx = (struct context_data *) calloc(1, sizeof(struct context_data));
if (ctx == NULL) {
return NULL;
}
ctx->state = XMP_STATE_UNLOADED;
ctx->m.defpan = 100;
ctx->s.numvoc = SMIX_NUMVOC;
return (xmp_context)ctx;
}
void xmp_free_context(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
free(m->instrument_path);
free(opaque);
}
static void set_position(struct context_data *ctx, int pos, int dir)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct flow_control *f = &p->flow;
int seq;
int has_marker;
/* If dir is 0, we can jump to a different sequence */
if (dir == 0) {
seq = libxmp_get_sequence(ctx, pos);
} else {
seq = p->sequence;
}
if (seq == 0xff) {
return;
}
has_marker = HAS_QUIRK(QUIRK_MARKER);
if (seq >= 0) {
int start = m->seq_data[seq].entry_point;
p->sequence = seq;
if (pos >= 0) {
int pat;
while (has_marker && mod->xxo[pos] == 0xfe) {
if (dir < 0) {
if (pos > start) {
pos--;
}
} else {
pos++;
}
}
pat = mod->xxo[pos];
if (pat < mod->pat) {
if (has_marker && pat == 0xff) {
return;
}
if (pos > p->scan[seq].ord) {
f->end_point = 0;
} else {
f->num_rows = mod->xxp[pat]->rows;
f->end_point = p->scan[seq].num;
f->jumpline = 0;
}
}
}
if (pos < mod->len) {
if (pos == 0) {
p->pos = -1;
} else {
p->pos = pos;
}
/* Clear flow vars to prevent old pattern jumps and
* other junk from executing in the new position. */
libxmp_reset_flow(ctx);
}
}
}
int xmp_next_position(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (p->pos < m->mod.len)
set_position(ctx, p->pos + 1, 1);
return p->pos;
}
int xmp_prev_position(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (p->pos == m->seq_data[p->sequence].entry_point) {
set_position(ctx, -1, -1);
} else if (p->pos > m->seq_data[p->sequence].entry_point) {
set_position(ctx, p->pos - 1, -1);
}
return p->pos < 0 ? 0 : p->pos;
}
int xmp_set_position(xmp_context opaque, int pos)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (pos >= m->mod.len)
return -XMP_ERROR_INVALID;
set_position(ctx, pos, 0);
return p->pos;
}
int xmp_set_row(xmp_context opaque, int row)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct flow_control *f = &p->flow;
int pos = p->pos;
int pattern;
if (pos < 0 || pos >= mod->len) {
pos = 0;
}
pattern = mod->xxo[pos];
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (pattern >= mod->pat || row >= mod->xxp[pattern]->rows)
return -XMP_ERROR_INVALID;
/* See set_position. */
if (p->pos < 0)
p->pos = 0;
p->ord = p->pos;
p->row = row;
p->frame = -1;
f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows;
return row;
}
void xmp_stop_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
p->pos = -2;
}
void xmp_restart_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
p->loop_count = 0;
p->pos = -1;
}
int xmp_seek_time(xmp_context opaque, int time)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i, t;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
for (i = m->mod.len - 1; i >= 0; i--) {
int pat = m->mod.xxo[i];
if (pat >= m->mod.pat) {
continue;
}
if (libxmp_get_sequence(ctx, i) != p->sequence) {
continue;
}
t = m->xxo_info[i].time;
if (time >= t) {
set_position(ctx, i, 1);
break;
}
}
if (i < 0) {
xmp_set_position(opaque, 0);
}
return p->pos < 0 ? 0 : p->pos;
}
int xmp_channel_mute(xmp_context opaque, int chn, int status)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
int ret;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
return -XMP_ERROR_INVALID;
}
ret = p->channel_mute[chn];
if (status >= 2) {
p->channel_mute[chn] = !p->channel_mute[chn];
} else if (status >= 0) {
p->channel_mute[chn] = status;
}
return ret;
}
int xmp_channel_vol(xmp_context opaque, int chn, int vol)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
int ret;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
return -XMP_ERROR_INVALID;
}
ret = p->channel_vol[chn];
if (vol >= 0 && vol <= 100) {
p->channel_vol[chn] = vol;
}
return ret;
}
#ifdef USE_VERSIONED_SYMBOLS
LIBXMP_BEGIN_DECLS /* no name-mangling */
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v40__(xmp_context, int, int) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.0");
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v41__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.1");
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v43__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@XMP_4.3");
LIBXMP_EXPORT_VERSIONED extern int xmp_set_player_v44__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_set_player@@XMP_4.4");
#ifndef HAVE_ATTRIBUTE_SYMVER
asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0");
asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1");
asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3");
asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4");
#endif
LIBXMP_END_DECLS
#define xmp_set_player__ xmp_set_player_v40__
#else
#define xmp_set_player__ xmp_set_player
#endif
int xmp_set_player__(xmp_context opaque, int parm, int val)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ret = -XMP_ERROR_INVALID;
if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) {
/* these should be set before loading the module */
if (ctx->state >= XMP_STATE_LOADED) {
return -XMP_ERROR_STATE;
}
} else if (parm == XMP_PLAYER_VOICES) {
/* these should be set before start playing */
if (ctx->state >= XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
} else if (ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
switch (parm) {
case XMP_PLAYER_AMP:
if (val >= 0 && val <= 3) {
s->amplify = val;
ret = 0;
}
break;
case XMP_PLAYER_MIX:
if (val >= -100 && val <= 100) {
s->mix = val;
ret = 0;
}
break;
case XMP_PLAYER_INTERP:
if (val >= XMP_INTERP_NEAREST && val <= XMP_INTERP_SPLINE) {
s->interp = val;
ret = 0;
}
break;
case XMP_PLAYER_DSP:
s->dsp = val;
ret = 0;
break;
case XMP_PLAYER_FLAGS: {
p->player_flags = val;
ret = 0;
break; }
/* 4.1 */
case XMP_PLAYER_CFLAGS: {
int vblank = p->flags & XMP_FLAGS_VBLANK;
p->flags = val;
if (vblank != (p->flags & XMP_FLAGS_VBLANK))
libxmp_scan_sequences(ctx);
ret = 0;
break; }
case XMP_PLAYER_SMPCTL:
m->smpctl = val;
ret = 0;
break;
case XMP_PLAYER_VOLUME:
if (val >= 0 && val <= 200) {
p->master_vol = val;
ret = 0;
}
break;
case XMP_PLAYER_SMIX_VOLUME:
if (val >= 0 && val <= 200) {
p->smix_vol = val;
ret = 0;
}
break;
/* 4.3 */
case XMP_PLAYER_DEFPAN:
if (val >= 0 && val <= 100) {
m->defpan = val;
ret = 0;
}
break;
/* 4.4 */
case XMP_PLAYER_MODE:
p->mode = val;
libxmp_set_player_mode(ctx);
libxmp_scan_sequences(ctx);
ret = 0;
break;
case XMP_PLAYER_VOICES:
s->numvoc = val;
break;
}
return ret;
}
#ifdef USE_VERSIONED_SYMBOLS
LIBXMP_BEGIN_DECLS /* no name-mangling */
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v40__(xmp_context, int) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.0");
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v41__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.1");
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v42__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.2");
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v43__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@XMP_4.3");
LIBXMP_EXPORT_VERSIONED extern int xmp_get_player_v44__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__"))) LIBXMP_ATTRIB_SYMVER("xmp_get_player@@XMP_4.4");
#ifndef HAVE_ATTRIBUTE_SYMVER
asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0");
asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1");
asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2");
asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3");
asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4");
#endif
LIBXMP_END_DECLS
#define xmp_get_player__ xmp_get_player_v40__
#else
#define xmp_get_player__ xmp_get_player
#endif
int xmp_get_player__(xmp_context opaque, int parm)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ret = -XMP_ERROR_INVALID;
if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) {
// can read these at any time
} else if (parm != XMP_PLAYER_STATE && ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
switch (parm) {
case XMP_PLAYER_AMP:
ret = s->amplify;
break;
case XMP_PLAYER_MIX:
ret = s->mix;
break;
case XMP_PLAYER_INTERP:
ret = s->interp;
break;
case XMP_PLAYER_DSP:
ret = s->dsp;
break;
case XMP_PLAYER_FLAGS:
ret = p->player_flags;
break;
/* 4.1 */
case XMP_PLAYER_CFLAGS:
ret = p->flags;
break;
case XMP_PLAYER_SMPCTL:
ret = m->smpctl;
break;
case XMP_PLAYER_VOLUME:
ret = p->master_vol;
break;
case XMP_PLAYER_SMIX_VOLUME:
ret = p->smix_vol;
break;
/* 4.2 */
case XMP_PLAYER_STATE:
ret = ctx->state;
break;
/* 4.3 */
case XMP_PLAYER_DEFPAN:
ret = m->defpan;
break;
/* 4.4 */
case XMP_PLAYER_MODE:
ret = p->mode;
break;
case XMP_PLAYER_MIXER_TYPE:
ret = XMP_MIXER_STANDARD;
if (p->flags & XMP_FLAGS_A500) {
if (IS_AMIGA_MOD()) {
#ifdef LIBXMP_PAULA_SIMULATOR
if (p->filter) {
ret = XMP_MIXER_A500F;
} else {
ret = XMP_MIXER_A500;
}
#endif
}
}
break;
case XMP_PLAYER_VOICES:
ret = s->numvoc;
break;
}
return ret;
}
const char *const *xmp_get_format_list(void)
{
return format_list();
}
void xmp_inject_event(xmp_context opaque, int channel, struct xmp_event *e)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
memcpy(&p->inject_event[channel], e, sizeof(struct xmp_event));
p->inject_event[channel]._flag = 1;
}
int xmp_set_instrument_path(xmp_context opaque, const char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
if (m->instrument_path != NULL)
free(m->instrument_path);
m->instrument_path = libxmp_strdup(path);
if (m->instrument_path == NULL) {
return -XMP_ERROR_SYSTEM;
}
return 0;
}
int xmp_set_tempo_factor(xmp_context opaque, double val)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ticksize;
if (val <= 0.0) {
return -1;
}
val *= 10;
ticksize = s->freq * val * m->rrate / p->bpm / 1000 * sizeof(int);
if (ticksize > XMP_MAX_FRAMESIZE) {
return -1;
}
m->time_factor = val;
return 0;
}

254
libxmp/src/dataio.c Normal file
View File

@ -0,0 +1,254 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include "common.h"
#define read_byte(x) do { \
(x) = fgetc(f); \
if ((x) < 0) goto error; \
} while (0)
#define set_error(x) do { \
if (err != NULL) *err = (x); \
} while (0)
uint8 read8(FILE *f, int *err)
{
int a;
read_byte(a);
set_error(0);
return a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xff;
}
int8 read8s(FILE *f, int *err)
{
int a;
read_byte(a);
set_error(0);
return (int8)a;
error:
set_error(ferror(f) ? errno : EOF);
return 0;
}
uint16 read16l(FILE *f, int *err)
{
int a, b;
read_byte(a);
read_byte(b);
set_error(0);
return ((uint16)b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffff;
}
uint16 read16b(FILE *f, int *err)
{
int a, b;
read_byte(a);
read_byte(b);
set_error(0);
return (a << 8) | b;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffff;
}
uint32 read24l(FILE *f, int *err)
{
int a, b, c;
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (c << 16) | (b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint32 read24b(FILE *f, int *err)
{
int a, b, c;
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (a << 16) | (b << 8) | c;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint32 read32l(FILE *f, int *err)
{
int a, b, c, d;
read_byte(a);
read_byte(b);
read_byte(c);
read_byte(d);
set_error(0);
return (d << 24) | (c << 16) | (b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint32 read32b(FILE *f, int *err)
{
int a, b, c, d;
read_byte(a);
read_byte(b);
read_byte(c);
read_byte(d);
set_error(0);
return (a << 24) | (b << 16) | (c << 8) | d;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint16 readmem16l(const uint8 *m)
{
uint32 a, b;
a = m[0];
b = m[1];
return (b << 8) | a;
}
uint16 readmem16b(const uint8 *m)
{
uint32 a, b;
a = m[0];
b = m[1];
return (a << 8) | b;
}
uint32 readmem24l(const uint8 *m)
{
uint32 a, b, c;
a = m[0];
b = m[1];
c = m[2];
return (c << 16) | (b << 8) | a;
}
uint32 readmem24b(const uint8 *m)
{
uint32 a, b, c;
a = m[0];
b = m[1];
c = m[2];
return (a << 16) | (b << 8) | c;
}
uint32 readmem32l(const uint8 *m)
{
uint32 a, b, c, d;
a = m[0];
b = m[1];
c = m[2];
d = m[3];
return (d << 24) | (c << 16) | (b << 8) | a;
}
uint32 readmem32b(const uint8 *m)
{
uint32 a, b, c, d;
a = m[0];
b = m[1];
c = m[2];
d = m[3];
return (a << 24) | (b << 16) | (c << 8) | d;
}
#ifndef LIBXMP_CORE_PLAYER
void write16l(FILE *f, uint16 w)
{
write8(f, w & 0x00ff);
write8(f, (w & 0xff00) >> 8);
}
void write16b(FILE *f, uint16 w)
{
write8(f, (w & 0xff00) >> 8);
write8(f, w & 0x00ff);
}
void write32l(FILE *f, uint32 w)
{
write8(f, w & 0x000000ff);
write8(f, (w & 0x0000ff00) >> 8);
write8(f, (w & 0x00ff0000) >> 16);
write8(f, (w & 0xff000000) >> 24);
}
void write32b(FILE *f, uint32 w)
{
write8(f, (w & 0xff000000) >> 24);
write8(f, (w & 0x00ff0000) >> 16);
write8(f, (w & 0x0000ff00) >> 8);
write8(f, w & 0x000000ff);
}
#endif

View File

@ -0,0 +1,24 @@
DEPACKERS_OBJS = depacker.o ppdepack.o unsqsh.o mmcmp.o s404_dec.o \
arc.o arcfs.o arc_unpack.o lzx.o lzx_unpack.o \
muse.o miniz_tinfl.o miniz_zip.o \
unzip.o gunzip.o uncompress.o bunzip2.o unlha.o \
unxz.o xz_dec_lzma2.o xz_dec_stream.o \
crc32.o xfnmatch.o ptpopen.o xfd.o xfd_link.o
DEPACKERS_DFILES = Makefile $(DEPACKERS_OBJS:.o=.c) depacker.h \
miniz.h miniz_zip.h arc_unpack.h lzx_unpack.h \
xz_lzma2.h README.unxz xz.h xz_private.h \
xz_stream.h xz_config.h crc32.h xfnmatch.h ptpopen.h
DEPACKERS_PATH = src/depackers
DEPACKER_OBJS = $(addprefix $(DEPACKERS_PATH)/,$(DEPACKERS_OBJS))
default-depackers::
$(MAKE) -C ..
dist-depackers::
mkdir -p $(DIST)/$(DEPACKERS_PATH)
cp -RPp $(addprefix $(DEPACKERS_PATH)/,$(DEPACKERS_DFILES)) $(DIST)/$(DEPACKERS_PATH)

View File

@ -0,0 +1,127 @@
XZ Embedded
===========
XZ Embedded is a relatively small, limited implementation of the .xz
file format. Currently only decoding is implemented.
XZ Embedded was written for use in the Linux kernel, but the code can
be easily used in other environments too, including regular userspace
applications.
This README contains information that is useful only when the copy
of XZ Embedded isn't part of the Linux kernel tree. You should also
read linux/Documentation/xz.txt even if you aren't using XZ Embedded
as part of Linux; information in that file is not repeated in this
README.
Compiling the Linux kernel module
The xz_dec module depends on crc32 module, so make sure that you have
it enabled (CONFIG_CRC32).
Building the xz_dec and xz_dec_test modules without support for BCJ
filters:
cd linux/lib/xz
make -C /path/to/kernel/source \
KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m
Building the xz_dec and xz_dec_test modules with support for BCJ
filters:
cd linux/lib/xz
make -C /path/to/kernel/source \
KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \
CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \
CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \
CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y
If you want only one or a few of the BCJ filters, omit the appropriate
variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support
code shared between all BCJ filters.
Most people don't need the xz_dec_test module. You can skip building
it by omitting CONFIG_XZ_DEC_TEST=m from the make command line.
Compiler requirements
XZ Embedded should compile as either GNU-C89 (used in the Linux
kernel) or with any C99 compiler. Getting the code to compile with
non-GNU C89 compiler or a C++ compiler should be quite easy as
long as there is a data type for unsigned 64-bit integer (or the
code is modified not to support large files, which needs some more
care than just using 32-bit integer instead of 64-bit).
If you use GCC, try to use a recent version. For example, on x86-32,
xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
compiled with GCC 4.3.3.
Embedding into userspace applications
To embed the XZ decoder, copy the following files into a single
directory in your source code tree:
linux/include/linux/xz.h
linux/lib/xz/xz_crc32.c
linux/lib/xz/xz_dec_lzma2.c
linux/lib/xz/xz_dec_stream.c
linux/lib/xz/xz_lzma2.h
linux/lib/xz/xz_private.h
linux/lib/xz/xz_stream.h
userspace/xz_config.h
Alternatively, xz.h may be placed into a different directory but then
that directory must be in the compiler include path when compiling
the .c files.
Your code should use only the functions declared in xz.h. The rest of
the .h files are meant only for internal use in XZ Embedded.
You may want to modify xz_config.h to be more suitable for your build
environment. Probably you should at least skim through it even if the
default file works as is.
BCJ filter support
If you want support for one or more BCJ filters, you need to copy also
linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate
#defines in xz_config.h or in compiler flags. You don't need these
#defines in the code that just uses XZ Embedded via xz.h, but having
them always #defined doesn't hurt either.
#define Instruction set BCJ filter endianness
XZ_DEC_X86 x86-32 or x86-64 Little endian only
XZ_DEC_POWERPC PowerPC Big endian only
XZ_DEC_IA64 Itanium (IA-64) Big or little endian
XZ_DEC_ARM ARM Little endian only
XZ_DEC_ARMTHUMB ARM-Thumb Little endian only
XZ_DEC_SPARC SPARC Big or little endian
While some architectures are (partially) bi-endian, the endianness
setting doesn't change the endianness of the instructions on all
architectures. That's why Itanium and SPARC filters work for both big
and little endian executables (Itanium has little endian instructions
and SPARC has big endian instructions).
There currently is no filter for little endian PowerPC or big endian
ARM or ARM-Thumb. Implementing filters for them can be considered if
there is a need for such filters in real-world applications.
Notes about shared libraries
If you are including XZ Embedded into a shared library, you very
probably should rename the xz_* functions to prevent symbol
conflicts in case your library is linked against some other library
or application that also has XZ Embedded in it (which may even be
a different version of XZ Embedded). TODO: Provide an easy way
to do this.
Please don't create a shared library of XZ Embedded itself unless
it is fine to rebuild everything depending on that shared library
everytime you upgrade to a newer version of XZ Embedded. There are
no API or ABI stability guarantees between different versions of
XZ Embedded.

379
libxmp/src/depackers/arc.c Normal file
View File

@ -0,0 +1,379 @@
/* Extended Module Player
* Copyright (C) 2021-2022 Alice Rowan <petrifiedrowan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Simple single-file unpacker for ARC/Spark archives.
* Report bugs to libxmp or to here: https://github.com/AliceLR/megazeuxtests
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arc_unpack.h"
#include "depacker.h"
#include "crc32.h"
/* Arbitrary maximum allowed output filesize. */
#define ARC_MAX_OUTPUT LIBXMP_DEPACK_LIMIT
/* #define ARC_DEBUG */
#define ARC_HEADER_SIZE 29
#define SPARK_HEADER_EXTRA 12
#define ARC_END_OF_ARCHIVE 0
#define ARC_6_DIR 30
#define ARC_6_END_OF_DIR 31
#ifdef ARC_DEBUG
#define debug(...) do{ fprintf(stderr, "" __VA_ARGS__); fflush(stderr); }while(0)
#endif
static arc_uint16 arc_crc16(arc_uint8 *buf, size_t len)
{
return libxmp_crc16_IBM(buf, len, 0);
}
static arc_uint16 arc_mem_u16(arc_uint8 *buf)
{
return (buf[1] << 8) | buf[0];
}
static arc_uint32 arc_mem_u32(arc_uint8 *buf)
{
return (buf[3] << 24UL) | (buf[2] << 16UL) | (buf[1] << 8UL) | buf[0];
}
struct arc_entry
{
/* 0 arc_uint8 magic; */ /* 0x1a */
/* 1 */ arc_uint8 method;
/* 2 */ char filename[13];
/* 15 */ arc_uint32 compressed_size;
/* 19 arc_uint16 dos_date; */ /* Same as ZIP. */
/* 21 arc_uint16 dos_time; */ /* Same as ZIP. */
/* 23 */ arc_uint16 crc16;
/* 25 */ arc_uint32 uncompressed_size; /* Note: method 1 omits this field. */
/* 29 */
/* Spark only. */
/* load_address and exec_address encode the filetype and RISC OS timestamp
* if the top 12 bits of load_address are 0xFFF. */
/* 29 */ arc_uint32 load_address;
/* 33 arc_uint32 exec_address; */
/* 37 arc_uint32 attributes; */
/* 41 */
};
static inline int is_arc_archive(unsigned char *buf)
{
int i;
/* Test magic. */
if(buf[0] != 0x1a)
return 0;
/* Test filename for garbage and missing terminator. */
for(i = 0; i < 13; i++)
{
if(buf[i + 2] == '\0')
break;
if(buf[i + 2] < 32 || buf[i + 2] == 0x7f)
return 0;
}
if(i >= 13)
return 0;
/* Test type. Not guaranteed to be a complete list. */
switch(buf[1])
{
/* ARC types. */
case ARC_END_OF_ARCHIVE:
case ARC_M_UNPACKED_OLD:
case ARC_M_UNPACKED:
case ARC_M_PACKED:
case ARC_M_SQUEEZED:
case ARC_M_CRUNCHED_5:
case ARC_M_CRUNCHED_6:
case ARC_M_CRUNCHED_7:
case ARC_M_CRUNCHED:
case ARC_M_SQUASHED:
case ARC_M_TRIMMED: /* Also PAK crushed */
case 11: /* PAK distilled */
case 20: /* archive info */
case 21: /* extended file info */
case 22: /* OS-specific info */
case ARC_6_DIR:
case ARC_6_END_OF_DIR:
return 1;
}
switch((int)buf[1] - 0x80)
{
/* Spark types. */
case ARC_END_OF_ARCHIVE:
case ARC_M_UNPACKED_OLD:
case ARC_M_UNPACKED:
case ARC_M_PACKED:
case ARC_M_SQUEEZED:
case ARC_M_CRUNCHED_5:
case ARC_M_CRUNCHED_6:
case ARC_M_CRUNCHED_7:
case ARC_M_CRUNCHED:
case ARC_M_SQUASHED:
case ARC_M_COMPRESSED:
return 1;
}
return 0;
}
static int is_packed(int method)
{
method &= 0x7f;
if(method == ARC_M_UNPACKED || method == ARC_M_UNPACKED_OLD)
return 0;
return 1;
}
static int is_spark(int method)
{
return method & 0x80;
}
static int is_directory(struct arc_entry *e)
{
/* ARC 6 directories have a dedicated type. */
if(e->method == ARC_6_DIR)
return 1;
/* Spark directories are never packed and have the Spark type bit set. */
if(e->method != (0x80 | (int)ARC_M_UNPACKED))
return 0;
/* Spark: top 12 bits must be 0xfff and filetype must be 0xddc (RISC OS archive). */
if(e->load_address >> 8 != 0xfffddcUL)
return 0;
return 1;
}
static size_t arc_header_length(int method)
{
size_t len = ARC_HEADER_SIZE;
/* End-of-archive and end-of-directory should be only 2 bytes long.
* Spark subdirectories end with end-of-archive, not end-of-directory. */
if((method & 0x7f) == ARC_END_OF_ARCHIVE || method == ARC_6_END_OF_DIR)
return 2;
if((method & 0x7f) == ARC_M_UNPACKED_OLD)
len -= 4;
if(is_spark(method))
len += SPARK_HEADER_EXTRA;
return len;
}
static int arc_read_entry(struct arc_entry *e, HIO_HANDLE *f)
{
arc_uint8 buf[ARC_HEADER_SIZE + SPARK_HEADER_EXTRA];
size_t header_len;
if(hio_read(buf, 1, 2, f) < 2 || buf[0] != 0x1a)
return -1;
e->method = buf[1];
header_len = arc_header_length(e->method);
if(header_len <= 2)
return 0;
if(hio_read(buf + 2, 1, header_len - 2, f) < header_len - 2)
return -1;
memcpy(e->filename, buf + 2, 12);
e->filename[12] = '\0';
e->compressed_size = arc_mem_u32(buf + 15);
e->crc16 = arc_mem_u16(buf + 23);
if(!is_packed(e->method))
e->uncompressed_size = e->compressed_size;
else
e->uncompressed_size = arc_mem_u32(buf + 25);
if(is_spark(e->method))
{
/* Spark stores extra RISC OS attribute information. */
size_t offset = header_len - SPARK_HEADER_EXTRA;
e->load_address = arc_mem_u32(buf + offset);
}
return 0;
}
static int arc_read(unsigned char **dest, size_t *dest_len, HIO_HANDLE *f, unsigned long file_len)
{
struct arc_entry e;
unsigned char *in;
unsigned char *out;
const char *err;
size_t out_len;
int level = 0;
arc_uint16 out_crc16;
while(1)
{
if(arc_read_entry(&e, f) < 0)
{
#ifdef ARC_DEBUG
debug("failed to read ARC entry\n");
#endif
return -1;
}
if((e.method & 0x7f) == ARC_END_OF_ARCHIVE || e.method == ARC_6_END_OF_DIR)
{
if(level > 0)
{
/* Valid directories can be continued out of directly into the following
* parent directory files. Note: manually nested archives where the inner
* archive has trailing data may end up erroring due to this simple handling. */
#ifdef ARC_DEBUG
debug("exiting directory\n");
#endif
level--;
continue;
}
return -1;
}
/* Special: both ARC 6 and Spark directories are stored as nested archives.
* The contents of these can just be read as if they're part of the parent. */
if(is_directory(&e))
{
#ifdef ARC_DEBUG
debug("entering directory: %s\n", e.filename);
#endif
level++;
continue;
}
/* Skip unknown types, junk compressed sizes, and unsupported uncompressed sizes. */
if(arc_method_is_supported(e.method) < 0 ||
e.compressed_size > file_len ||
e.uncompressed_size > ARC_MAX_OUTPUT ||
libxmp_exclude_match(e.filename))
{
#ifdef ARC_DEBUG
debug("skipping: method=%d compr=%zu uncompr=%zu\n",
e.method, (size_t)e.compressed_size, (size_t)e.uncompressed_size);
#endif
if(hio_seek(f, e.compressed_size, SEEK_CUR) < 0)
return -1;
continue;
}
#ifdef ARC_DEBUG
debug("file: %s\n", e.filename);
#endif
/* Attempt to unpack. */
in = (unsigned char *)malloc(e.compressed_size);
if(!in)
return -1;
if(hio_read(in, 1, e.compressed_size, f) < e.compressed_size)
{
free(in);
return -1;
}
if(is_packed(e.method))
{
out = (unsigned char *)malloc(e.uncompressed_size);
out_len = e.uncompressed_size;
if(!out)
{
free(in);
return -1;
}
err = arc_unpack(out, out_len, in, e.compressed_size, e.method, 0);
if(err != NULL)
{
#ifdef ARC_DEBUG
debug("error unpacking: %s\n", err);
#endif
free(in);
free(out);
return -1;
}
free(in);
}
else
{
out = in;
out_len = e.compressed_size;
}
out_crc16 = arc_crc16(out, out_len);
if(e.crc16 != out_crc16)
{
#ifdef ARC_DEBUG
debug("crc16 mismatch: expected %zu, got %zu\n",
(size_t)e.crc16, (size_t)out_crc16);
#endif
free(out);
return -1;
}
*dest = out;
*dest_len = out_len;
return 0;
}
}
static int arc_test(unsigned char *data)
{
return is_arc_archive(data);
}
static int arc_decrunch(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
unsigned char *outbuf;
size_t size;
int ret = arc_read(&outbuf, &size, in, inlen);
if(ret < 0)
return -1;
*out = outbuf;
*outlen = size;
return 0;
}
struct depacker libxmp_depacker_arc =
{
arc_test,
NULL,
arc_decrunch
};

View File

@ -0,0 +1,913 @@
/* Extended Module Player
* Copyright (C) 2021-2022 Alice Rowan <petrifiedrowan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Report bugs to libxmp or to here: https://github.com/AliceLR/megazeuxtests
*/
#include "arc_unpack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* #define ARC_DEBUG */
/* ARC method 0x08: read maximum code width from stream, but ignore it. */
#define ARC_IGNORE_CODE_IN_STREAM 0x7ffe
/* Spark method 0xff: read maximum code width from stream. */
#define ARC_MAX_CODE_IN_STREAM 0x7fff
#define ARC_NO_CODE 0xffffffffUL
#define ARC_RESET_CODE 256
#define ARC_BUFFER_SIZE 8192 /* Buffer size for multi-stage compression. */
struct arc_code
{
arc_uint16 prev;
arc_uint16 length;
arc_uint8 value;
};
struct arc_lookup
{
arc_uint16 value;
arc_uint8 length;
};
struct arc_huffman_index
{
arc_int16 value[2];
};
struct arc_data
{
/* RLE90. */
size_t rle_in;
size_t rle_out;
int in_rle_code;
int last_byte;
/* LZW and huffman. */
arc_uint32 codes_buffered[8];
unsigned buffered_pos;
unsigned buffered_width;
size_t lzw_bits_in;
size_t lzw_in;
size_t lzw_out;
unsigned lzw_eof;
unsigned max_code;
unsigned first_code;
unsigned next_code;
unsigned current_width;
unsigned init_width;
unsigned max_width;
unsigned continue_left;
unsigned continue_code;
arc_uint32 last_code;
unsigned kwkwk;
unsigned last_first_value;
unsigned char *window;
struct arc_code *tree;
struct arc_lookup *huffman_lookup;
struct arc_huffman_index *huffman_tree;
unsigned num_huffman;
};
static int arc_unpack_init(struct arc_data *arc, int init_width, int max_width, int is_dynamic)
{
arc->rle_out = 0;
arc->rle_in = 0;
arc->in_rle_code = 0;
arc->last_byte = 0;
arc->buffered_pos = 0;
arc->buffered_width = 0;
arc->lzw_bits_in = 0;
arc->lzw_in = 0;
arc->lzw_out = 0;
arc->lzw_eof = 0;
arc->max_code = (1 << max_width);
arc->first_code = is_dynamic ? 257 : 256;
arc->current_width = init_width;
arc->init_width = init_width;
arc->max_width = max_width;
arc->continue_left = 0;
arc->continue_code = 0;
arc->last_code = ARC_NO_CODE;
arc->last_first_value = 0;
arc->kwkwk = 0;
arc->window = NULL;
arc->tree = NULL;
arc->huffman_lookup = NULL;
arc->huffman_tree = NULL;
arc->num_huffman = 0;
if(max_width)
{
size_t i;
if(max_width < 9 || max_width > 16)
return -1;
arc->tree = (struct arc_code *)calloc(1 << max_width, sizeof(struct arc_code));
if(!arc->tree)
return -1;
for(i = 0; i < 256; i++)
{
struct arc_code *c = &(arc->tree[i]);
c->prev = (arc_uint16)ARC_NO_CODE;
c->length = 1;
c->value = i;
}
arc->next_code = arc->first_code;
}
return 0;
}
static int arc_unpack_window(struct arc_data *arc, size_t window_size)
{
arc->window = (unsigned char *)malloc(window_size);
if(!arc->window)
return -1;
return 0;
}
static void arc_unpack_free(struct arc_data *arc)
{
free(arc->window);
free(arc->tree);
free(arc->huffman_lookup);
free(arc->huffman_tree);
}
static arc_uint32 arc_get_bytes(const unsigned char *pos, int num)
{
switch(num)
{
case 0:
return 0;
case 1:
return pos[0];
case 2:
return pos[0] | (pos[1] << 8UL);
case 3:
return pos[0] | (pos[1] << 8UL) | (pos[2] << 16UL);
default:
return pos[0] | (pos[1] << 8UL) | (pos[2] << 16UL) | (pos[3] << 24UL);
}
}
static arc_int32 arc_read_bits(struct arc_data * ARC_RESTRICT arc,
const unsigned char *src, size_t src_len, unsigned int num_bits)
{
arc_uint32 ret;
if(arc->lzw_bits_in + num_bits > (src_len << 3))
{
arc->lzw_bits_in = src_len << 3;
arc->lzw_in = src_len;
return -1;
}
ret = arc_get_bytes(src + arc->lzw_in, src_len - arc->lzw_in);
ret = (ret >> (arc->lzw_bits_in & 7)) & (0xffffUL << num_bits >> 16);
arc->lzw_bits_in += num_bits;
arc->lzw_in = arc->lzw_bits_in >> 3;
return ret;
}
static arc_uint32 arc_next_code(struct arc_data * ARC_RESTRICT arc,
const unsigned char *src, size_t src_len)
{
/**
* Codes are read 8 at a time in the original ARC/ArcFS/Spark software,
* presumably to simplify file IO. This buffer needs to be simulated.
*
* When the code width changes, the extra buffered codes are discarded.
* Despite this, the final number of codes won't always be a multiple of 8.
*/
if(arc->buffered_pos >= 8 || arc->buffered_width != arc->current_width)
{
size_t i;
for(i = 0; i < 8; i++)
{
arc_int32 value = arc_read_bits(arc, src, src_len, arc->current_width);
if(value < 0)
break;
arc->codes_buffered[i] = value;
}
for(; i < 8; i++)
arc->codes_buffered[i] = ARC_NO_CODE;
arc->buffered_pos = 0;
arc->buffered_width = arc->current_width;
}
return arc->codes_buffered[arc->buffered_pos++];
}
static void arc_unlzw_add(struct arc_data *arc)
{
if(arc->last_code != ARC_NO_CODE && arc->next_code < arc->max_code)
{
arc_uint32 len = arc->tree[arc->last_code].length;
struct arc_code *e;
e = &(arc->tree[arc->next_code++]);
e->prev = arc->last_code;
e->length = len ? len + 1 : 0;
e->value = arc->last_first_value;
/* Automatically expand width. */
if(arc->next_code >= (1U << arc->current_width) && arc->current_width < arc->max_width)
{
arc->current_width++;
#ifdef ARC_DEBUG
fprintf(stderr, "width expanded to %u\n", arc->current_width);
#endif
}
}
}
static int arc_unlzw_get_length(const struct arc_data *arc,
const struct arc_code *e)
{
unsigned length = 1;
int code;
if(e->length)
return e->length;
do
{
if(length >= arc->max_code)
return 0;
length++;
code = e->prev;
e = &(arc->tree[code]);
}
while(code >= 256);
return length;
}
static int arc_unlzw_block(struct arc_data * ARC_RESTRICT arc,
unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len)
{
unsigned char *pos;
struct arc_code *e;
arc_uint16 start_code;
arc_uint32 code;
int len;
int set_last_first;
#ifdef ARC_DEBUG
int num_debug = 0;
#endif
while(arc->lzw_out < dest_len)
{
/* Interrupted while writing out code? Resume output... */
if(arc->continue_code)
{
code = arc->continue_code;
set_last_first = 0;
goto continue_code;
}
code = arc_next_code(arc, src, src_len);
if(code >= arc->max_code)
{
arc->lzw_eof = 1;
break;
}
#ifdef ARC_DEBUG
fprintf(stderr, "%04x ", code);
num_debug++;
if(!(num_debug & 15))
fprintf(stderr, "\n");
#endif
if(code == ARC_RESET_CODE && arc->first_code == 257)
{
size_t i;
/* Reset width for dynamic modes 8, 9, and 255. */
#ifdef ARC_DEBUG
fprintf(stderr, "reset at size = %u codes\n", arc->next_code);
#endif
arc->next_code = arc->first_code;
arc->current_width = arc->init_width;
arc->last_code = ARC_NO_CODE;
for(i = 256; i < arc->max_code; i++)
arc->tree[i].length = 0;
continue;
}
/* Add next code first to avoid KwKwK problem. */
if((unsigned)code == arc->next_code)
{
arc_unlzw_add(arc);
arc->kwkwk = 1;
}
/* Emit code. */
set_last_first = 1;
continue_code:
start_code = code;
e = &(arc->tree[code]);
if(!arc->continue_code)
{
len = arc_unlzw_get_length(arc, e);
if(!len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "failed to get length for %04xh (code count is %04xh)\n",
code, arc->next_code);
#endif
return -1;
}
}
else
len = arc->continue_left;
if((unsigned)len > dest_len - arc->lzw_out)
{
/* Calculate arc->continue_left, skip arc->continue_left,
* emit remaining len from end of dest. */
arc_int32 num_emit = dest_len - arc->lzw_out;
arc->continue_left = len - num_emit;
arc->continue_code = code;
for(; len > num_emit; len--)
e = &(arc->tree[e->prev]);
}
else
arc->continue_code = 0;
pos = dest + arc->lzw_out + len - 1;
arc->lzw_out += len;
for(; len > 0; len--)
{
code = e->value;
*(pos--) = code;
e = &(arc->tree[e->prev]);
}
/* Only set this if this is the tail end of the chain,
* i.e., the first section written. */
if(set_last_first)
arc->last_first_value = code;
if(arc->continue_code)
return 0;
if(!arc->kwkwk)
arc_unlzw_add(arc);
arc->last_code = start_code;
arc->kwkwk = 0;
}
return 0;
}
static int arc_unrle90_block(struct arc_data * ARC_RESTRICT arc,
unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len)
{
size_t start;
size_t len;
size_t i;
for(i = 0; i < src_len;)
{
if(arc->in_rle_code)
{
arc->in_rle_code = 0;
if(i >= src_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "end of input stream mid-code @ %zu\n", i);
#endif
return -1;
}
if(src[i] == 0)
{
if(arc->rle_out >= dest_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "end of output stream @ %zu emitting 0x90\n", i);
#endif
return -1;
}
#ifdef ARC_DEBUG
fprintf(stderr, "@ %zu: literal 0x90\n", i);
#endif
dest[arc->rle_out++] = 0x90;
arc->last_byte = 0x90;
}
else
{
len = src[i] - 1;
if(arc->rle_out + len > dest_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "end of output stream @ %zu: run of %02xh times %zu\n",
i, arc->last_byte, len);
#endif
return -1;
}
#ifdef ARC_DEBUG
fprintf(stderr, "@ %zu: run of %02xh times %zu\n", i, arc->last_byte, len);
#endif
memset(dest + arc->rle_out, arc->last_byte, len);
arc->rle_out += len;
}
i++;
}
start = i;
while(i < src_len && src[i] != 0x90)
i++;
if(i > start)
{
len = i - start;
if(len + arc->rle_out > dest_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "end of output_stream @ %zu: block of length %zu\n",
i, len);
#endif
/* In some uncommon cases, ArcFS seems to output extra data beyond the
* expected end of the file when unpacking crunched files. In the few
* that have CRCs, ignoring the extra data still passes the check. */
len = dest_len - arc->rle_out;
if(!len)
break;
}
#ifdef ARC_DEBUG
fprintf(stderr, "@ %zu: block of length %zu\n", i, len);
#endif
memcpy(dest + arc->rle_out, src + start, len);
arc->rle_out += len;
arc->last_byte = src[i - 1];
}
if(i < src_len && src[i] == 0x90)
{
arc->in_rle_code = 1;
i++;
}
}
arc->rle_in += i;
return 0;
}
static int arc_unpack_rle90(unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len)
{
struct arc_data arc;
if(arc_unpack_init(&arc, 0, 0, 0) != 0)
return -1;
if(arc_unrle90_block(&arc, dest, dest_len, src, src_len) != 0)
{
#ifdef ARC_DEBUG
fprintf(stderr, "arc_unrle90_block failed\n");
#endif
goto err;
}
if(arc.rle_out != dest_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "out %zu != buffer size %zu\n", arc.rle_out, dest_len);
#endif
goto err;
}
arc_unpack_free(&arc);
return 0;
err:
arc_unpack_free(&arc);
return -1;
}
static int arc_unpack_lzw(unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len, int init_width, int max_width)
{
struct arc_data arc;
int is_dynamic = (init_width != max_width);
if(max_width == ARC_MAX_CODE_IN_STREAM)
{
if(src_len < 2)
return -1;
max_width = src[0];
src++;
src_len--;
if(max_width < 9 || max_width > 16)
return -1;
}
if(arc_unpack_init(&arc, init_width, max_width, is_dynamic) != 0)
return -1;
if(arc_unlzw_block(&arc, dest, dest_len, src, src_len))
{
#ifdef ARC_DEBUG
fprintf(stderr, "arc_unlzw_block failed (%zu in, %zu out)\n",
arc.lzw_in, arc.lzw_out);
#endif
goto err;
}
if(arc.lzw_out != dest_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "out %zu != buffer size %zu\n", arc.lzw_out, dest_len);
#endif
goto err;
}
arc_unpack_free(&arc);
return 0;
err:
arc_unpack_free(&arc);
return -1;
}
static int arc_unpack_lzw_rle90(unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len, int init_width, int max_width)
{
struct arc_data arc;
int is_dynamic = (init_width != max_width);
/* This is only used for Spark method 0xff, which doesn't use RLE. */
if(max_width == ARC_MAX_CODE_IN_STREAM)
return -1;
if(max_width == ARC_IGNORE_CODE_IN_STREAM)
{
if(src_len < 2)
return -1;
src++;
src_len--;
max_width = 12;
}
if(max_width < 9 || max_width > 16)
return -1;
if(arc_unpack_init(&arc, init_width, max_width, is_dynamic) != 0)
return -1;
if(arc_unpack_window(&arc, ARC_BUFFER_SIZE) != 0)
goto err;
while(arc.lzw_eof == 0)
{
arc.lzw_out = 0;
if(arc_unlzw_block(&arc, arc.window, ARC_BUFFER_SIZE, src, src_len))
{
#ifdef ARC_DEBUG
fprintf(stderr, "arc_unlzw_block failed "
"(%zu in, %zu out in buffer, %zu out in stream)\n",
arc.lzw_in, arc.lzw_out, arc.rle_out);
#endif
goto err;
}
if(arc_unrle90_block(&arc, dest, dest_len, arc.window, arc.lzw_out))
{
#ifdef ARC_DEBUG
fprintf(stderr, "arc_unrle90_block failed (%zu in, %zu out)\n",
arc.lzw_in, arc.rle_out);
#endif
goto err;
}
}
if(arc.rle_out != dest_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "out %zu != buffer size %zu\n", arc.rle_out, dest_len);
#endif
goto err;
}
arc_unpack_free(&arc);
return 0;
err:
arc_unpack_free(&arc);
return -1;
}
/**
* Huffman decoding based on this blog post by Phaeron.
* https://www.virtualdub.org/blog2/entry_345.html
*/
#define LOOKUP_BITS 11
#define LOOKUP_MASK ((1 << LOOKUP_BITS) - 1)
#define HUFFMAN_TREE_MAX 256
static int arc_huffman_check_tree(const struct arc_huffman_index *tree)
{
/* Make sure the tree isn't garbage... */
const struct arc_huffman_index *e;
arc_uint8 visited[HUFFMAN_TREE_MAX];
arc_uint8 stack[HUFFMAN_TREE_MAX];
int stack_pos = 1;
size_t i;
memset(visited, 0, sizeof(visited));
stack[0] = 0;
while(stack_pos > 0)
{
i = stack[--stack_pos];
e = &(tree[i]);
visited[i] = 1;
if(e->value[0] >= 0)
{
if(visited[e->value[0]])
return -1;
stack[stack_pos++] = e->value[0];
}
if(e->value[1] >= 0)
{
if(visited[e->value[1]])
return -1;
stack[stack_pos++] = e->value[1];
}
}
return 0;
}
static int arc_huffman_init(struct arc_data * ARC_RESTRICT arc,
const unsigned char *src, size_t src_len)
{
size_t table_size = 1 << LOOKUP_BITS;
size_t iter;
size_t i;
size_t j;
if(src_len < 2)
return -1;
arc->num_huffman = src[0] | (src[1] << 8);
if(!arc->num_huffman || arc->num_huffman > HUFFMAN_TREE_MAX)
return -1;
arc->lzw_in = 2UL + 4UL * arc->num_huffman;
arc->lzw_bits_in = (arc->lzw_in << 3);
if(arc->lzw_in > src_len)
return -1;
/* Precompute huffman tree and lookup table. */
arc->huffman_lookup = (struct arc_lookup *)calloc(table_size, sizeof(struct arc_lookup));
arc->huffman_tree = (struct arc_huffman_index *)malloc(arc->num_huffman * sizeof(struct arc_huffman_index));
for(i = 0; i < arc->num_huffman; i++)
{
struct arc_huffman_index *e = &(arc->huffman_tree[i]);
e->value[0] = src[i * 4 + 2] | (src[i * 4 + 3] << 8);
e->value[1] = src[i * 4 + 4] | (src[i * 4 + 5] << 8);
if(e->value[0] >= (int)arc->num_huffman || e->value[1] >= (int)arc->num_huffman)
return -1;
}
if(arc_huffman_check_tree(arc->huffman_tree) < 0)
return -1;
for(i = 0; i < table_size; i++)
{
int index = 0;
int value = i;
int bits;
if(arc->huffman_lookup[i].length)
continue;
for(bits = 0; index >= 0 && bits < LOOKUP_BITS; bits++)
{
index = arc->huffman_tree[index].value[value & 1];
value >>= 1;
}
if(index >= 0)
{
arc->huffman_lookup[i].value = index;
continue;
}
iter = 1 << bits;
for(j = i; j < table_size; j += iter)
{
arc->huffman_lookup[j].value = ~index;
arc->huffman_lookup[j].length = bits;
}
}
return 0;
}
static int arc_huffman_read_bits(struct arc_data * ARC_RESTRICT arc,
const unsigned char *src, size_t src_len)
{
struct arc_huffman_index *tree = arc->huffman_tree;
struct arc_lookup *e;
size_t peek;
size_t bits_end;
int index;
if(arc->lzw_in >= src_len)
return -1;
/* Optimize short values with precomputed table. */
peek = arc_get_bytes(src + arc->lzw_in, src_len - arc->lzw_in) >> (arc->lzw_bits_in & 7);
e = &(arc->huffman_lookup[peek & LOOKUP_MASK]);
if(e->length)
{
arc->lzw_bits_in += e->length;
arc->lzw_in = arc->lzw_bits_in >> 3;
return e->value;
}
/* The table also allows skipping the first few bits of long codes. */
bits_end = (src_len << 3);
arc->lzw_bits_in += LOOKUP_BITS;
index = e->value;
while(index >= 0 && arc->lzw_bits_in < bits_end)
{
/* Force unsigned here to avoid potential sign extensions. */
unsigned bit = (unsigned)src[arc->lzw_bits_in >> 3] >> (arc->lzw_bits_in & 7);
arc->lzw_bits_in++;
index = tree[index].value[bit & 1];
}
arc->lzw_in = arc->lzw_bits_in >> 3;
/* This translates truncated code indices to negative
* values (i.e. failure), no check required. */
return ~index;
}
static int arc_unhuffman_block(struct arc_data * ARC_RESTRICT arc,
unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len)
{
while(arc->lzw_out < dest_len)
{
int value = arc_huffman_read_bits(arc, src, src_len);
if(value >= 256)
{
/* End of stream code. */
arc->lzw_in = src_len;
arc->lzw_eof = 1;
return 0;
}
if(value < 0)
return -1;
dest[arc->lzw_out++] = value;
}
return 0;
}
static int arc_unpack_huffman_rle90(unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len)
{
struct arc_data arc;
if(arc_unpack_init(&arc, 0, 0, 0) != 0)
return -1;
if(arc_unpack_window(&arc, ARC_BUFFER_SIZE) != 0)
goto err;
if(arc_huffman_init(&arc, src, src_len) != 0)
goto err;
while(arc.lzw_eof == 0)
{
arc.lzw_out = 0;
if(arc_unhuffman_block(&arc, arc.window, ARC_BUFFER_SIZE, src, src_len))
{
#ifdef ARC_DEBUG
fprintf(stderr, "arc_unhuffman_block failed "
"(%zu in, %zu out in buffer, %zu out in stream)\n",
arc.lzw_in, arc.lzw_out, arc.rle_out);
#endif
goto err;
}
if(arc_unrle90_block(&arc, dest, dest_len, arc.window, arc.lzw_out))
{
#ifdef ARC_DEBUG
fprintf(stderr, "arc_unrle90_block failed (%zu in, %zu out)\n",
arc.lzw_in, arc.rle_out);
#endif
goto err;
}
}
if(arc.rle_out != dest_len)
{
#ifdef ARC_DEBUG
fprintf(stderr, "out %zu != buffer size %zu\n", arc.rle_out, dest_len);
#endif
goto err;
}
arc_unpack_free(&arc);
return 0;
err:
arc_unpack_free(&arc);
return -1;
}
const char *arc_unpack(unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len, int method, int max_width)
{
switch(method & 0x7f)
{
case ARC_M_UNPACKED_OLD:
case ARC_M_UNPACKED:
/* Handle these somewhere that doesn't require an extra buffer. */
return "not packed";
case ARC_M_PACKED: /* RLE90 */
if(arc_unpack_rle90(dest, dest_len, src, src_len) < 0)
return "failed unpack";
break;
case ARC_M_SQUEEZED: /* RLE90 + Huffman coding */
if(arc_unpack_huffman_rle90(dest, dest_len, src, src_len) < 0)
return "failed unsqueeze";
break;
case ARC_M_CRUNCHED: /* RLE90 + LZW 9-12 bit dynamic */
if(max_width > 16)
return "invalid uncrunch width";
if(max_width <= 0)
max_width = ARC_IGNORE_CODE_IN_STREAM;
if(arc_unpack_lzw_rle90(dest, dest_len, src, src_len, 9, max_width))
return "failed uncrunch";
break;
case ARC_M_SQUASHED: /* LZW 9-13 bit dynamic */
if(arc_unpack_lzw(dest, dest_len, src, src_len, 9, 13))
return "failed unsquash";
break;
case ARC_M_COMPRESSED: /* LZW 9-16 bit dynamic */
if(max_width > 16)
return "invalid uncompress width";
if(max_width <= 0)
max_width = ARC_MAX_CODE_IN_STREAM;
if(arc_unpack_lzw(dest, dest_len, src, src_len, 9, max_width))
return "failed uncompress";
break;
default:
return "unsupported method";
}
return NULL;
}

View File

@ -0,0 +1,123 @@
/* Extended Module Player
* Copyright (C) 2021-2022 Alice Rowan <petrifiedrowan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Unpacker for ARC/ArcFS/Spark compressed streams.
* Report bugs to libxmp or to here: https://github.com/AliceLR/megazeuxtests
*/
#ifndef LIBXMP_ARC_UNPACK_H
#define LIBXMP_ARC_UNPACK_H
#ifdef __cplusplus
extern "C" {
#endif
/* libxmp hacks */
#include "../common.h"
typedef int8 arc_int8;
typedef int16 arc_int16;
typedef int32 arc_int32;
typedef uint8 arc_uint8;
typedef uint16 arc_uint16;
typedef uint32 arc_uint32;
#define ARC_RESTRICT LIBXMP_RESTRICT
#define arc_unpack libxmp_arc_unpack
/* end libxmp hacks */
enum arc_method
{
ARC_M_UNPACKED_OLD = 0x01,
ARC_M_UNPACKED = 0x02,
ARC_M_PACKED = 0x03, /* RLE90 */
ARC_M_SQUEEZED = 0x04, /* RLE90 + Huffman coding */
ARC_M_CRUNCHED_5 = 0x05, /* LZW 12-bit static (old hash) */
ARC_M_CRUNCHED_6 = 0x06, /* RLE90 + LZW 12-bit static (old hash) */
ARC_M_CRUNCHED_7 = 0x07, /* RLE90 + LZW 12-bit static (new hash) */
ARC_M_CRUNCHED = 0x08, /* RLE90 + LZW 9-12 bit dynamic */
ARC_M_SQUASHED = 0x09, /* LZW 9-13 bit dynamic (PK extension)*/
ARC_M_TRIMMED = 0x0a, /* RLE90 + LZH with adaptive Huffman coding */
ARC_M_COMPRESSED = 0x7f, /* LZW 9-16 bit dynamic (Spark extension) */
ARC_M_MAX
};
/**
* Determine if a given ARC/ArcFS/Spark method is supported.
*
* Almost all methods found in ArcFS and Spark archives in practice are
* supported. The rare methods 5-7 are not supported. Method 10 was added
* in later versions of ARC and is not supported here. Other higher method
* values are used to encode archive info and other things that can be
* safely ignored.
*
* @param method compression method to test. All but the lowest seven bits
* will be masked away from this value.
*
* @return 0 if a method is supported, otherwise -1.
*/
static inline int arc_method_is_supported(int method)
{
switch(method & 0x7f)
{
case ARC_M_UNPACKED_OLD:
case ARC_M_UNPACKED:
case ARC_M_PACKED:
case ARC_M_SQUEEZED:
case ARC_M_CRUNCHED:
case ARC_M_SQUASHED:
case ARC_M_COMPRESSED:
return 0;
}
return -1;
}
/**
* Unpack a buffer containing an ARC/ArcFS/Spark compressed stream
* into an uncompressed representation of the stream. The unpacked methods
* should be handled separately from this function since they don't need
* a second output buffer for the uncompressed data.
*
* @param dest destination buffer for the uncompressed stream.
* @param dest_len destination buffer size.
* @param src buffer containing the compressed stream.
* @param src_len size of the compressed stream.
* @param method ARC/ArcFS/Spark compression method. All but the lowest
* seven bits will be masked away from this value.
* @param max_width Specifies the maximum bit width for the crunched and
* compressed (Spark) methods. This value is stored in the
* compressed stream in the ARC/Spark formats but is NOT
* stored in the compressed stream in the ArcFS format.
* If <=0, the value is read from the stream instead.
* For all other methods, this field is ignored.
*
* @return `NULL` on success, otherwise a static const string
* containing a short error message.
*/
const char *arc_unpack(unsigned char * ARC_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len, int method, int max_width);
#ifdef __cplusplus
}
#endif
#endif /* LIBXMP_ARC_UNPACK_H */

View File

@ -0,0 +1,339 @@
/* Extended Module Player
* Copyright (C) 2021-2022 Alice Rowan <petrifiedrowan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Simple single-file unpacker for ArcFS archives.
* Report bugs to libxmp or to here: https://github.com/AliceLR/megazeuxtests
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arc_unpack.h"
#include "depacker.h"
#include "crc32.h"
/* Arbitrary maximum allowed output filesize. */
#define ARCFS_MAX_OUTPUT LIBXMP_DEPACK_LIMIT
/* #define ARCFS_DEBUG */
#define ARCFS_HEADER_SIZE 96
#define ARCFS_ENTRY_SIZE 36
#define ARCFS_END_OF_DIR 0
#define ARCFS_DELETED 1
#ifdef ARCFS_DEBUG
#define debug(...) do{ fprintf(stderr, "" __VA_ARGS__); fflush(stderr); }while(0)
#endif
static arc_uint16 arc_crc16(arc_uint8 *buf, size_t len)
{
return libxmp_crc16_IBM(buf, len, 0);
}
static arc_uint16 arc_mem_u16(arc_uint8 *buf)
{
return (buf[1] << 8) | buf[0];
}
static arc_uint32 arc_mem_u32(arc_uint8 *buf)
{
return (buf[3] << 24UL) | (buf[2] << 16UL) | (buf[1] << 8UL) | buf[0];
}
struct arcfs_data
{
/* 0 char magic[8]; */
/* 8 */ arc_uint32 entries_length;
/* 12 */ arc_uint32 data_offset;
/* 16 */ arc_uint32 min_read_version;
/* 20 */ arc_uint32 min_write_version;
/* 24 */ arc_uint32 format_version;
/* 28 Filler. */
/* 96 */
};
struct arcfs_entry
{
/* Unhandled fields:
* - Permissions are stored in the low byte of attributes.
* - A 40-bit timestamp is usually stored in load_offset/exec_offset.
* The timestamp counts 10ms increments from epoch 1900-01-01.
* This is supposed to be stored when the top 12 bits of load_offset are 0xFFF.
* - Likewise, when the top 12 bits of load_offset are 0xFF, bits 8 through 19
* in load_offset are supposed to be the RISC OS filetype.
*/
/* 0 */ arc_uint8 method;
/* 1 */ char filename[12];
/* 12 */ arc_uint32 uncompressed_size;
/* 16 arc_uint32 load_offset; */ /* Low byte -> high byte of the 40-bit timestamp. */
/* 20 arc_uint32 exec_offset; */ /* Low portion of the 40-bit timestamp. */
/* 24 arc_uint32 attributes; */
/* 28 */ arc_uint32 compressed_size;
/* 32 arc_uint32 info; */
/* 36 */
/* Unpacked fields */
arc_uint16 crc16;
arc_uint8 compression_bits;
arc_uint8 is_directory;
arc_uint32 value_offset;
};
static int arcfs_check_magic(const unsigned char *buf)
{
return memcmp(buf, "Archive\x00", 8) ? -1 : 0;
}
static int arcfs_read_header(struct arcfs_data *data, HIO_HANDLE *f)
{
arc_uint8 buffer[ARCFS_HEADER_SIZE];
if(hio_read(buffer, 1, ARCFS_HEADER_SIZE, f) < ARCFS_HEADER_SIZE)
{
#ifdef ARCFS_DEBUG
debug("short read in header\n");
#endif
return -1;
}
if(arcfs_check_magic(buffer) < 0)
{
#ifdef ARCFS_DEBUG
debug("bad header magic: %8.8s\n", (char *)buffer);
#endif
return -1;
}
data->entries_length = arc_mem_u32(buffer + 8);
data->data_offset = arc_mem_u32(buffer + 12);
data->min_read_version = arc_mem_u32(buffer + 16);
data->min_write_version = arc_mem_u32(buffer + 20);
data->format_version = arc_mem_u32(buffer + 24);
if(data->entries_length % ARCFS_ENTRY_SIZE != 0)
{
#ifdef ARCFS_DEBUG
debug("bad entries length: %zu\n", (size_t)data->entries_length);
#endif
return -1;
}
if(data->data_offset < ARCFS_HEADER_SIZE ||
data->data_offset - ARCFS_HEADER_SIZE < data->entries_length)
{
#ifdef ARCFS_DEBUG
debug("bad data offset: %zu\n", (size_t)data->data_offset);
#endif
return -1;
}
/* These seem to be the highest versions that exist. */
if(data->min_read_version > 260 || data->min_write_version > 260 || data->format_version > 0x0a)
{
#ifdef ARCFS_DEBUG
debug("bad versions: %zu %zu %zu\n", (size_t)data->min_read_version,
(size_t)data->min_write_version, (size_t)data->format_version);
#endif
return -1;
}
return 0;
}
static int arcfs_read_entry(struct arcfs_entry *e, HIO_HANDLE *f)
{
arc_uint8 buffer[ARCFS_ENTRY_SIZE];
if(hio_read(buffer, 1, ARCFS_ENTRY_SIZE, f) < ARCFS_ENTRY_SIZE)
return -1;
e->method = buffer[0] & 0x7f;
if(e->method == ARCFS_END_OF_DIR)
return 0;
memcpy(e->filename, buffer + 1, 11);
e->filename[11] = '\0';
e->uncompressed_size = arc_mem_u32(buffer + 12);
e->compression_bits = buffer[25]; /* attributes */
e->crc16 = arc_mem_u16(buffer + 26); /* attributes */
e->compressed_size = arc_mem_u32(buffer + 28);
e->value_offset = arc_mem_u32(buffer + 32) & 0x7fffffffUL; /* info */
e->is_directory = buffer[35] >> 7; /* info */
return 0;
}
static int arcfs_read(unsigned char **dest, size_t *dest_len, HIO_HANDLE *f, unsigned long file_len)
{
struct arcfs_data data;
struct arcfs_entry e;
unsigned char *in;
unsigned char *out;
const char *err;
size_t out_len;
size_t offset;
size_t i;
if(arcfs_read_header(&data, f) < 0)
return -1;
if(data.data_offset > file_len)
return -1;
for(i = 0; i < data.entries_length; i += ARCFS_ENTRY_SIZE)
{
if(arcfs_read_entry(&e, f) < 0)
{
#ifdef ARCFS_DEBUG
debug("error reading entry %zu\n", (size_t)data.entries_length / ARCFS_ENTRY_SIZE);
#endif
return -1;
}
#ifdef ARCFS_DEBUG
debug("checking file: %s\n", e.filename);
#endif
/* Ignore directories, end of directory markers, deleted files. */
if(e.method == ARCFS_END_OF_DIR || e.method == ARCFS_DELETED || e.is_directory)
continue;
if(e.method == ARC_M_UNPACKED)
e.compressed_size = e.uncompressed_size;
/* Ignore junk offset/size. */
if(e.value_offset >= file_len - data.data_offset)
continue;
offset = data.data_offset + e.value_offset;
if(e.compressed_size > file_len - offset)
continue;
/* Ignore sizes over the allowed limit. */
if(e.uncompressed_size > ARCFS_MAX_OUTPUT)
continue;
/* Ignore unsupported methods. */
if(arc_method_is_supported(e.method) < 0)
continue;
if(libxmp_exclude_match(e.filename))
continue;
/* Read file. */
#ifdef ARCFS_DEBUG
debug("unpacking file: %s\n", e.filename);
#endif
if(hio_seek(f, offset, SEEK_SET) < 0)
return -1;
in = (unsigned char *)malloc(e.compressed_size);
if(!in)
return -1;
if(hio_read(in, 1, e.compressed_size, f) < e.compressed_size)
{
free(in);
return -1;
}
if(e.method != ARC_M_UNPACKED)
{
out = (unsigned char *)malloc(e.uncompressed_size);
out_len = e.uncompressed_size;
if(!out)
{
free(in);
return -1;
}
err = arc_unpack(out, out_len, in, e.compressed_size, e.method, e.compression_bits);
if(err != NULL)
{
#ifdef ARCFS_DEBUG
debug("error unpacking: %s\n", err);
#endif
free(in);
free(out);
return -1;
}
free(in);
}
else
{
out = in;
out_len = e.uncompressed_size;
}
/* ArcFS CRC may sometimes just be 0, in which case, ignore it. */
if(e.crc16)
{
arc_uint16 out_crc16 = arc_crc16(out, out_len);
if(e.crc16 != out_crc16)
{
#ifdef ARCFS_DEBUG
debug("crc16 mismatch: expected %u, got %u\n", e.crc16, out_crc16);
#endif
free(out);
return -1;
}
}
*dest = out;
*dest_len = out_len;
return 0;
}
return -1;
}
static int arcfs_test(unsigned char *data)
{
return arcfs_check_magic(data) == 0;
}
static int arcfs_decrunch(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
unsigned char *outbuf;
size_t size;
int ret = arcfs_read(&outbuf, &size, in, inlen);
if(ret < 0)
return -1;
*out = outbuf;
*outlen = size;
return 0;
}
struct depacker libxmp_depacker_arcfs =
{
arcfs_test,
NULL,
arcfs_decrunch
};

View File

@ -0,0 +1,741 @@
/* bzcat.c - bzip2 decompression
*
* Copyright 2003, 2007 Rob Landley <rob@landley.net>
*
* Based on a close reading (but not the actual code) of the original bzip2
* decompression code by Julian R Seward (jseward@acm.org), which also
* acknowledges contributions by Mike Burrows, David Wheeler, Peter Fenwick,
* Alistair Moffat, Radford Neal, Ian H. Witten, Robert Sedgewick, and
* Jon L. Bentley.
*
* This is 0BSD-licensed code from https://github.com/landley/toybox:
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <setjmp.h>
#include "../common.h"
#include "depacker.h"
#include "crc32.h"
#define THREADS 1
// Constants for huffman coding
#define MAX_GROUPS 6
#define GROUP_SIZE 50 /* 64 would have been more efficient */
#define MAX_HUFCODE_BITS 20 /* Longest huffman code allowed */
#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */
#define SYMBOL_RUNA 0
#define SYMBOL_RUNB 1
// Other housekeeping constants
#define IOBUF_SIZE 4096
// Status return values
#define RETVAL_LAST_BLOCK (-100)
#define RETVAL_NOT_BZIP_DATA (-1)
#define RETVAL_DATA_ERROR (-2)
#define RETVAL_OBSOLETE_INPUT (-3)
// libxmp additions:
#define RETVAL_OUT_OF_MEMORY (-4)
#define RETVAL_UNEXPECTED_INPUT_EOF (-5)
#define RETVAL_UNEXPECTED_OUTPUT_EOF (-6)
// This is what we know about each huffman coding group
struct group_data {
// limit and base are 1-indexed. index 0 is never used but increasing
// the length by 1 simplifies the code and isn't that much of a waste.
int limit[1+MAX_HUFCODE_BITS+1], base[1+MAX_HUFCODE_BITS+1], permute[MAX_SYMBOLS];
char minLen, maxLen;
};
// Data for burrows wheeler transform
struct bwdata {
unsigned int origPtr;
int byteCount[256];
// State saved when interrupting output
int writePos, writeRun, writeCount, writeCurrent;
unsigned int dataCRC, headerCRC;
unsigned int *dbuf;
};
// Structure holding all the housekeeping data, including IO buffers and
// memory that persists between calls to bunzip
struct bunzip_data {
// Input stream, input buffer, input bit buffer
HIO_HANDLE *in_fd; /* libxmp: int -> HIO_HANDLE * */
int inbufCount, inbufPos;
unsigned char *inbuf; /* libxmp: char -> unsigned char */
unsigned int inbufBitCount, inbufBits;
// Output buffer
char outbuf[IOBUF_SIZE];
int outbufPos;
unsigned int totalCRC;
// First pass decompression data (Huffman and MTF decoding)
char selectors[32768]; // nSelectors=15 bits
struct group_data groups[MAX_GROUPS]; // huffman coding tables
int symTotal, groupCount, nSelectors;
unsigned char symToByte[256], mtfSymbol[256];
// The CRC values stored in the block header and calculated from the data
unsigned int crc32Table[256];
// Second pass decompression data (burrows-wheeler transform)
unsigned int dbufSize;
struct bwdata bwdata[THREADS];
/* libxmp: For I/O error handling */
jmp_buf jmpbuf;
};
// libxmp addition
struct bunzip_output {
unsigned char *buf;
size_t buf_size;
size_t buf_alloc;
};
static void crc_init(unsigned *crc_table, int little_endian)
{
unsigned int i;
// Init the CRC32 table (big endian)
for (i=0; i<256; i++) {
unsigned int j, c = little_endian ? i : i<<24;
for (j=8; j; j--)
if (little_endian) c = (c&1) ? (c>>1)^0xEDB88320 : c>>1;
else c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1);
crc_table[i] = c;
}
}
// Return the next nnn bits of input. All reads from the compressed input
// are done through this function. All reads are big endian.
static unsigned int get_bits(struct bunzip_data *bd, char bits_wanted)
{
unsigned int bits = 0;
// If we need to get more data from the byte buffer, do so. (Loop getting
// one byte at a time to enforce endianness and avoid unaligned access.)
while (bd->inbufBitCount < bits_wanted) {
// If we need to read more data from file into byte buffer, do so
if (bd->inbufPos == bd->inbufCount) {
if (0 >= (bd->inbufCount = hio_read(bd->inbuf, 1, IOBUF_SIZE, bd->in_fd))) /* libxmp: read -> hio_read */
longjmp(bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF); /* libxmp: error_exit -> longjmp */
bd->inbufPos = 0;
}
// Avoid 32-bit overflow (dump bit buffer to top of output)
if (bd->inbufBitCount>=24) {
bits = bd->inbufBits&((1u<<bd->inbufBitCount)-1);
bits_wanted -= bd->inbufBitCount;
bits <<= bits_wanted;
bd->inbufBitCount = 0;
}
// Grab next 8 bits of input from buffer.
bd->inbufBits = (bd->inbufBits<<8) | bd->inbuf[bd->inbufPos++];
bd->inbufBitCount += 8;
}
// Calculate result
bd->inbufBitCount -= bits_wanted;
bits |= (bd->inbufBits>>bd->inbufBitCount) & ((1<<bits_wanted)-1);
return bits;
}
/* Read block header at start of a new compressed data block. Consists of:
*
* 48 bits : Block signature, either pi (data block) or e (EOF block).
* 32 bits : bw->headerCRC
* 1 bit : obsolete feature flag.
* 24 bits : origPtr (Burrows-wheeler unwind index, only 20 bits ever used)
* 16 bits : Mapping table index.
*[16 bits]: symToByte[symTotal] (Mapping table. For each bit set in mapping
* table index above, read another 16 bits of mapping table data.
* If correspondig bit is unset, all bits in that mapping table
* section are 0.)
* 3 bits : groupCount (how many huffman tables used to encode, anywhere
* from 2 to MAX_GROUPS)
* variable: hufGroup[groupCount] (MTF encoded huffman table data.)
*/
static int read_block_header(struct bunzip_data *bd, struct bwdata *bw)
{
struct group_data *hufGroup;
int hh, ii, jj, kk, symCount;
unsigned char uc;
// Read in header signature and CRC (which is stored big endian)
ii = get_bits(bd, 24);
jj = get_bits(bd, 24);
bw->headerCRC = get_bits(bd,32);
// Is this the EOF block with CRC for whole file? (Constant is "e")
if (ii==0x177245 && jj==0x385090) return RETVAL_LAST_BLOCK;
// Is this a valid data block? (Constant is "pi".)
if (ii!=0x314159 || jj!=0x265359) return RETVAL_NOT_BZIP_DATA;
// We can add support for blockRandomised if anybody complains.
if (get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT;
if ((bw->origPtr = get_bits(bd,24)) > bd->dbufSize) return RETVAL_DATA_ERROR;
// mapping table: if some byte values are never used (encoding things
// like ascii text), the compression code removes the gaps to have fewer
// symbols to deal with, and writes a sparse bitfield indicating which
// values were present. We make a translation table to convert the symbols
// back to the corresponding bytes.
hh = get_bits(bd, 16);
bd->symTotal = 0;
for (ii=0; ii<16; ii++) {
if (hh & (1 << (15 - ii))) {
kk = get_bits(bd, 16);
for (jj=0; jj<16; jj++)
if (kk & (1 << (15 - jj)))
bd->symToByte[bd->symTotal++] = (16 * ii) + jj;
}
}
// How many different huffman coding groups does this block use?
bd->groupCount = get_bits(bd,3);
if (bd->groupCount<2 || bd->groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR;
// nSelectors: Every GROUP_SIZE many symbols we switch huffman coding
// tables. Each group has a selector, which is an index into the huffman
// coding table arrays.
//
// Read in the group selector array, which is stored as MTF encoded
// bit runs. (MTF = Move To Front. Every time a symbol occurs it's moved
// to the front of the table, so it has a shorter encoding next time.)
if (!(bd->nSelectors = get_bits(bd, 15))) return RETVAL_DATA_ERROR;
for (ii=0; ii<bd->groupCount; ii++) bd->mtfSymbol[ii] = ii;
for (ii=0; ii<bd->nSelectors; ii++) {
// Get next value
for(jj=0;get_bits(bd,1);jj++)
if (jj>=bd->groupCount) return RETVAL_DATA_ERROR;
// Decode MTF to get the next selector, and move it to the front.
uc = bd->mtfSymbol[jj];
memmove(bd->mtfSymbol+1, bd->mtfSymbol, jj);
bd->mtfSymbol[0] = bd->selectors[ii] = uc;
}
// Read the huffman coding tables for each group, which code for symTotal
// literal symbols, plus two run symbols (RUNA, RUNB)
symCount = bd->symTotal+2;
if (symCount < 1) return RETVAL_DATA_ERROR; /* libxmp: fix broken warning */
for (jj=0; jj<bd->groupCount; jj++) {
unsigned char length[MAX_SYMBOLS];
unsigned temp[MAX_HUFCODE_BITS+1];
int minLen, maxLen, pp;
// Read lengths
hh = get_bits(bd, 5);
for (ii = 0; ii < symCount; ii++) {
for(;;) {
// !hh || hh > MAX_HUFCODE_BITS in one test.
if (MAX_HUFCODE_BITS-1 < (unsigned)hh-1) return RETVAL_DATA_ERROR;
// Grab 2 bits instead of 1 (slightly smaller/faster). Stop if
// first bit is 0, otherwise second bit says whether to
// increment or decrement.
kk = get_bits(bd, 2);
if (kk & 2) hh += 1 - ((kk&1)<<1);
else {
bd->inbufBitCount++;
break;
}
}
length[ii] = hh;
}
// Find largest and smallest lengths in this group
minLen = maxLen = length[0];
for (ii = 1; ii < symCount; ii++) {
if(length[ii] > maxLen) maxLen = length[ii];
else if(length[ii] < minLen) minLen = length[ii];
}
/* Calculate permute[], base[], and limit[] tables from length[].
*
* permute[] is the lookup table for converting huffman coded symbols
* into decoded symbols. It contains symbol values sorted by length.
*
* base[] is the amount to subtract from the value of a huffman symbol
* of a given length when using permute[].
*
* limit[] indicates the largest numerical value a symbol with a given
* number of bits can have. It lets us know when to stop reading.
*
* To use these, keep reading bits until value <= limit[bitcount] or
* you've read over 20 bits (error). Then the decoded symbol
* equals permute[hufcode_value - base[hufcode_bitcount]].
*/
hufGroup = bd->groups+jj;
hufGroup->minLen = minLen;
hufGroup->maxLen = maxLen;
// zero temp[] and limit[], and calculate permute[]
pp = 0;
for (ii = minLen; ii <= maxLen; ii++) {
temp[ii] = hufGroup->limit[ii] = 0;
for (hh = 0; hh < symCount; hh++)
if (length[hh] == ii) hufGroup->permute[pp++] = hh;
}
// Count symbols coded for at each bit length
for (ii = 0; ii < symCount; ii++) temp[length[ii]]++;
/* Calculate limit[] (the largest symbol-coding value at each bit
* length, which is (previous limit<<1)+symbols at this level), and
* base[] (number of symbols to ignore at each bit length, which is
* limit minus the cumulative count of symbols coded for already). */
pp = hh = 0;
for (ii = minLen; ii < maxLen; ii++) {
pp += temp[ii];
hufGroup->limit[ii] = pp-1;
pp <<= 1;
hufGroup->base[ii+1] = pp-(hh+=temp[ii]);
}
hufGroup->limit[maxLen] = pp+temp[maxLen]-1;
hufGroup->limit[maxLen+1] = INT_MAX;
hufGroup->base[minLen] = 0;
}
return 0;
}
/* First pass, read block's symbols into dbuf[dbufCount].
*
* This undoes three types of compression: huffman coding, run length encoding,
* and move to front encoding. We have to undo all those to know when we've
* read enough input.
*/
static int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
{
struct group_data *hufGroup;
int ii, jj, kk, runPos, dbufCount, symCount, selector, nextSym,
*byteCount;
unsigned hh, *dbuf = bw->dbuf;
unsigned char uc;
// We've finished reading and digesting the block header. Now read this
// block's huffman coded symbols from the file and undo the huffman coding
// and run length encoding, saving the result into dbuf[dbufCount++] = uc
// Initialize symbol occurrence counters and symbol mtf table
byteCount = bw->byteCount;
for(ii=0; ii<256; ii++) {
byteCount[ii] = 0;
bd->mtfSymbol[ii] = ii;
}
// Loop through compressed symbols. This is the first "tight inner loop"
// that needs to be micro-optimized for speed. (This one fills out dbuf[]
// linearly, staying in cache more, so isn't as limited by DRAM access.)
runPos = dbufCount = symCount = selector = 0;
// Some unnecessary initializations to shut gcc up.
hufGroup = 0;
hh = 0;
for (;;) {
// Have we reached the end of this huffman group?
if (!(symCount--)) {
// Determine which huffman coding group to use.
symCount = GROUP_SIZE-1;
if (selector >= bd->nSelectors) return RETVAL_DATA_ERROR;
hufGroup = bd->groups + bd->selectors[selector++];
}
// Read next huffman-coded symbol (into jj).
ii = hufGroup->minLen;
jj = get_bits(bd, ii);
while (jj > hufGroup->limit[ii]) {
// if (ii > hufGroup->maxLen) return RETVAL_DATA_ERROR;
ii++;
// Unroll get_bits() to avoid a function call when the data's in
// the buffer already.
kk = bd->inbufBitCount
? (bd->inbufBits >> --(bd->inbufBitCount)) & 1 : get_bits(bd, 1);
jj = (jj << 1) | kk;
}
// Huffman decode jj into nextSym (with bounds checking)
jj-=hufGroup->base[ii];
if (ii > hufGroup->maxLen || (unsigned)jj >= MAX_SYMBOLS)
return RETVAL_DATA_ERROR;
nextSym = hufGroup->permute[jj];
// If this is a repeated run, loop collecting data
if ((unsigned)nextSym <= SYMBOL_RUNB) {
// If this is the start of a new run, zero out counter
if(!runPos) {
runPos = 1;
hh = 0;
}
/* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
each bit position, add 1 or 2 instead. For example,
1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
You can make any bit pattern that way using 1 less symbol than
the basic or 0/1 method (except all bits 0, which would use no
symbols, but a run of length 0 doesn't mean anything in this
context). Thus space is saved. */
hh += (runPos << nextSym); // +runPos if RUNA; +2*runPos if RUNB
runPos <<= 1;
continue;
}
/* When we hit the first non-run symbol after a run, we now know
how many times to repeat the last literal, so append that many
copies to our buffer of decoded symbols (dbuf) now. (The last
literal used is the one at the head of the mtfSymbol array.) */
if (runPos) {
runPos = 0;
// Check for integer overflow
if (hh>bd->dbufSize || dbufCount+hh>bd->dbufSize)
return RETVAL_DATA_ERROR;
uc = bd->symToByte[bd->mtfSymbol[0]];
byteCount[uc] += hh;
while (hh--) dbuf[dbufCount++] = uc;
}
// Is this the terminating symbol?
if (nextSym>bd->symTotal) break;
/* At this point, the symbol we just decoded indicates a new literal
character. Subtract one to get the position in the MTF array
at which this literal is currently to be found. (Note that the
result can't be -1 or 0, because 0 and 1 are RUNA and RUNB.
Another instance of the first symbol in the mtf array, position 0,
would have been handled as part of a run.) */
if (dbufCount>=bd->dbufSize) return RETVAL_DATA_ERROR;
ii = nextSym - 1;
uc = bd->mtfSymbol[ii];
// On my laptop, unrolling this memmove() into a loop shaves 3.5% off
// the total running time.
while(ii--) bd->mtfSymbol[ii+1] = bd->mtfSymbol[ii];
bd->mtfSymbol[0] = uc;
uc = bd->symToByte[uc];
// We have our literal byte. Save it into dbuf.
byteCount[uc]++;
dbuf[dbufCount++] = (unsigned int)uc;
}
// Now we know what dbufCount is, do a better sanity check on origPtr.
if (bw->origPtr >= (bw->writeCount = dbufCount)) return RETVAL_DATA_ERROR;
return 0;
}
static size_t next_power_of_two_32(size_t i)
{
i |= i >> 16UL;
i |= i >> 8UL;
i |= i >> 4UL;
i |= i >> 2UL;
i |= i >> 1UL;
return i + 1;
}
// Flush output buffer to disk
// libxmp: changed to output to memory instead of a file
static int flush_bunzip_outbuf(struct bunzip_data *bd, struct bunzip_output *out_fd)
{
if (bd->outbufPos) {
unsigned char *buf = out_fd->buf;
if (bd->outbufPos > out_fd->buf_alloc - out_fd->buf_size) {
size_t new_size = next_power_of_two_32(out_fd->buf_size + bd->outbufPos);
if (new_size <= out_fd->buf_alloc || new_size > LIBXMP_DEPACK_LIMIT)
return RETVAL_UNEXPECTED_OUTPUT_EOF;
buf = (unsigned char *)realloc(buf, new_size);
if (!buf)
return RETVAL_UNEXPECTED_OUTPUT_EOF;
out_fd->buf = buf;
out_fd->buf_alloc = new_size;
}
memcpy(buf + out_fd->buf_size, bd->outbuf, bd->outbufPos);
out_fd->buf_size += bd->outbufPos;
bd->outbufPos = 0;
}
return 0;
}
static void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
{
int ii, jj;
unsigned int *dbuf = bw->dbuf;
int *byteCount = bw->byteCount;
// Turn byteCount into cumulative occurrence counts of 0 to n-1.
jj = 0;
for (ii=0; ii<256; ii++) {
int kk = jj + byteCount[ii];
byteCount[ii] = jj;
jj = kk;
}
// Use occurrence counts to quickly figure out what order dbuf would be in
// if we sorted it.
for (ii=0; ii < bw->writeCount; ii++) {
unsigned char uc = dbuf[ii];
dbuf[byteCount[uc]] |= (ii << 8);
byteCount[uc]++;
}
// blockRandomised support would go here.
// Using ii as position, jj as previous character, hh as current character,
// and uc as run count.
bw->dataCRC = 0xffffffffL;
/* Decode first byte by hand to initialize "previous" byte. Note that it
doesn't get output, and if the first three characters are identical
it doesn't qualify as a run (hence uc=255, which will either wrap
to 1 or get reset). */
if (bw->writeCount) {
bw->writePos = dbuf[bw->origPtr];
bw->writeCurrent = (unsigned char)bw->writePos;
bw->writePos >>= 8;
bw->writeRun = -1;
}
}
// Decompress a block of text to intermediate buffer
static int read_bunzip_data(struct bunzip_data *bd)
{
int rc = read_block_header(bd, bd->bwdata);
if (!rc) rc=read_huffman_data(bd, bd->bwdata);
// First thing that can be done by a background thread.
burrows_wheeler_prep(bd, bd->bwdata);
return rc;
}
// Undo burrows-wheeler transform on intermediate buffer to produce output.
// If !len, write up to len bytes of data to buf. Otherwise write to out_fd.
// Returns len ? bytes written : 0. Notice all errors are negative #'s.
//
// Burrows-wheeler transform is described at:
// http://dogma.net/markn/articles/bwt/bwt.htm
// http://marknelson.us/1996/09/01/bwt/
/* libxmp: int -> struct bunzip_output * */
static int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw,
struct bunzip_output *out_fd, char *outbuf, int len)
{
unsigned int *dbuf = bw->dbuf;
int count, pos, current, run, copies, outbyte, previous, gotcount = 0;
/* libxmp: Reset longjmp I/O error handling */
int ret = setjmp(bd->jmpbuf);
if (ret) return ret;
for (;;) {
// If last read was short due to end of file, return last block now
if (bw->writeCount < 0) return bw->writeCount;
// If we need to refill dbuf, do it.
if (!bw->writeCount) {
int i = read_bunzip_data(bd);
if (i) {
if (i == RETVAL_LAST_BLOCK) {
bw->writeCount = i;
return gotcount;
} else return i;
}
}
// loop generating output
count = bw->writeCount;
pos = bw->writePos;
current = bw->writeCurrent;
run = bw->writeRun;
while (count) {
// If somebody (like tar) wants a certain number of bytes of
// data from memory instead of written to a file, humor them.
if (len && bd->outbufPos >= len) goto dataus_interruptus;
count--;
// Follow sequence vector to undo Burrows-Wheeler transform.
previous = current;
pos = dbuf[pos];
current = pos&0xff;
pos >>= 8;
// Whenever we see 3 consecutive copies of the same byte,
// the 4th is a repeat count
if (run++ == 3) {
copies = current;
outbyte = previous;
current = -1;
} else {
copies = 1;
outbyte = current;
}
// Output bytes to buffer, flushing to file if necessary
while (copies--) {
if (bd->outbufPos == IOBUF_SIZE) {
int i = flush_bunzip_outbuf(bd, out_fd); /* libxmp: error checking */
if (i) return i;
}
bd->outbuf[bd->outbufPos++] = outbyte;
bw->dataCRC = (bw->dataCRC << 8)
^ bd->crc32Table[(bw->dataCRC >> 24) ^ outbyte];
}
if (current != previous) run=0;
}
// decompression of this block completed successfully
bw->dataCRC = ~(bw->dataCRC);
bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ bw->dataCRC;
// if this block had a crc error, force file level crc error.
if (bw->dataCRC != bw->headerCRC) {
bd->totalCRC = bw->headerCRC+1;
return RETVAL_LAST_BLOCK;
}
dataus_interruptus:
bw->writeCount = count;
if (len) {
gotcount += bd->outbufPos;
memcpy(outbuf, bd->outbuf, len);
// If we got enough data, checkpoint loop state and return
if ((len -= bd->outbufPos)<1) {
bd->outbufPos -= len;
if (bd->outbufPos) memmove(bd->outbuf, bd->outbuf+len, bd->outbufPos);
bw->writePos = pos;
bw->writeCurrent = current;
bw->writeRun = run;
return gotcount;
}
}
}
}
// Allocate the structure, read file header. If !len, src_fd contains
// filehandle to read from. Else inbuf contains data.
/* libxmp: int -> HIO_HANDLE *, char -> unsigned char */
static int start_bunzip(struct bunzip_data **bdp, HIO_HANDLE *src_fd, unsigned char *inbuf,
int len)
{
struct bunzip_data *bd;
unsigned int i;
// Figure out how much data to allocate.
i = sizeof(struct bunzip_data);
if (!len) i += IOBUF_SIZE;
// Allocate bunzip_data. Most fields initialize to zero.
bd = *bdp = (struct bunzip_data *)calloc(1, i); /* libxmp: xzalloc -> calloc + error checking */
if (!bd) return RETVAL_OUT_OF_MEMORY;
if (len) {
bd->inbuf = inbuf;
bd->inbufCount = len;
bd->in_fd = NULL;
} else {
bd->inbuf = (unsigned char *)(bd+1); /* libxmp: char -> unsigned char */
bd->in_fd = src_fd;
}
crc_init(bd->crc32Table, 0);
/* libxmp: Setup for I/O error handling via longjmp */
i = setjmp(bd->jmpbuf);
if (i) return i;
// Ensure that file starts with "BZh".
for (i=0;i<3;i++) if (get_bits(bd,8)!="BZh"[i]) return RETVAL_NOT_BZIP_DATA;
// Next byte ascii '1'-'9', indicates block size in units of 100k of
// uncompressed data. Allocate intermediate buffer for block.
i = get_bits(bd, 8);
if (i<'1' || i>'9') return RETVAL_NOT_BZIP_DATA;
bd->dbufSize = 100000*(i-'0')*THREADS;
for (i=0; i<THREADS; i++) {
bd->bwdata[i].dbuf = (unsigned int *)malloc(bd->dbufSize * sizeof(int)); /* libxmp: xmalloc -> malloc + error checking */
if (!bd->bwdata[i].dbuf) return RETVAL_OUT_OF_MEMORY;
}
return 0;
}
static int test_bzip2(unsigned char *b)
{
return b[0] == 'B' && b[1] == 'Z' && b[2] == 'h';
}
static int decrunch_bzip2(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
struct bunzip_data *bd;
struct bunzip_output output;
int i, j;
output.buf = NULL;
output.buf_size = 0;
output.buf_alloc = 0;
if (!(i = start_bunzip(&bd, in, 0, 0))) {
i = write_bunzip_data(bd, bd->bwdata, &output, 0, 0);
if (i==RETVAL_LAST_BLOCK) {
if (bd->bwdata[0].headerCRC==bd->totalCRC) i = 0;
else i = RETVAL_DATA_ERROR;
}
}
if (!i) i = flush_bunzip_outbuf(bd, &output);
for (j=0; j<THREADS; j++) free(bd->bwdata[j].dbuf);
free(bd);
if (i != 0) {
free(output.buf);
return -1;
}
/* Shrink allocation */
if (output.buf_size < output.buf_alloc) {
unsigned char *tmp = (unsigned char *)realloc(output.buf, output.buf_size);
if (tmp)
output.buf = tmp;
}
*out = output.buf;
*outlen = output.buf_size;
return 0;
}
struct depacker libxmp_depacker_bzip2 = {
test_bzip2,
NULL,
decrunch_bzip2
};

View File

@ -0,0 +1,201 @@
/*
* CRC functions for libxmp
* Copyright (C) 2013 Claudio Matsuoka
* Copyright (C) 2022 Alice Rowan
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#include "crc32.h"
#define CRC(table) do{ crc = table[*buf++ ^ (crc & 0xff)] ^ (crc >> 8); }while(0)
static const uint32 crc32_A_table[256] = {
0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
0x2d02ef8dUL
};
static const uint16 crc16_IBM_table[256] = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};
uint32 libxmp_crc32_A_no_inv(const uint8 *buf, size_t size, uint32 crc)
{
/* This loop gives a very marginal performance improvement. Other
* styles of unrolling/type punning are roughly the equivalent. */
while (size >= 4) {
CRC(crc32_A_table);
CRC(crc32_A_table);
CRC(crc32_A_table);
CRC(crc32_A_table);
size -= 4;
}
while (size--) {
CRC(crc32_A_table);
}
return crc;
}
/* Used by ZIP, gzip, XZ, LZX, probably others... */
uint32 libxmp_crc32_A(const uint8 *buf, size_t size, uint32 crc)
{
return ~libxmp_crc32_A_no_inv(buf, size, ~crc);
}
/* Used by ARC/ArcFS/Spark, LHA. */
uint16 libxmp_crc16_IBM(const uint8 *buf, size_t size, uint16 crc)
{
while (size >= 4) {
CRC(crc16_IBM_table);
CRC(crc16_IBM_table);
CRC(crc16_IBM_table);
CRC(crc16_IBM_table);
size -= 4;
}
while (size--) {
CRC(crc16_IBM_table);
}
return crc;
}
#if 0
#include <stdio.h>
/* Table generation code based on the algorithm provided on Wikipedia.
*
* https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks#Generating_the_tables
*/
int main()
{
/* CRC-16-IBM
uint16 table[256] = { 0 };
uint16 poly = 0xa001;
uint16 crc = 0x0001;
*/
uint32 table[256] = { 0 };
uint32 poly = 0xEDB88320;
uint32 crc = 0x00000001;
for (int i = 128; i >= 1; i >>= 1) {
if (crc & 0x01)
crc = (crc >> 1) ^ poly;
else
crc >>= 1;
for (int j = 0; j < 256; j += i * 2)
table[i + j] = crc ^ table[j];
}
printf("static const uint32 crc32_A_table[256] = {\n");
for (int i = 0, k = 5; i < 256; i += k) {
printf("\t");
for (int j = 0; j < k && i + j < 256; j++)
printf("0x%08xUL, ", table[i + j]);
printf("\n");
}
printf("};\n");
return 0;
}
#endif

View File

@ -0,0 +1,15 @@
#ifndef LIBXMP_CRC_H
#define LIBXMP_CRC_H
#include "../common.h"
LIBXMP_BEGIN_DECLS
uint32 libxmp_crc32_A (const uint8 *, size_t, uint32);
uint32 libxmp_crc32_A_no_inv (const uint8 *, size_t, uint32);
uint16 libxmp_crc16_IBM (const uint8 *, size_t, uint16);
LIBXMP_END_DECLS
#endif

View File

@ -0,0 +1,420 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include "../common.h"
#include "depacker.h"
#include "../hio.h"
#include "../tempfile.h"
#include "xfnmatch.h"
#ifdef _WIN32
/* Note: The _popen function returns an invalid file opaque, if
* used in a Windows program, that will cause the program to hang
* indefinitely. _popen works properly in a Console application.
* To create a Windows application that redirects input and output,
* read the section "Creating a Child Process with Redirected Input
* and Output" in the Win32 SDK. -- Mirko
*
* This popen reimplementation uses CreateProcess instead and should be safe.
*/
#include "ptpopen.h"
#ifndef HAVE_POPEN
#define HAVE_POPEN 1
#endif
#elif defined(__WATCOMC__)
#define popen _popen
#define pclose _pclose
#define HAVE_POPEN 1
#endif
#define BUFLEN 16384
static struct depacker *depacker_list[] = {
#if defined(LIBXMP_AMIGA) && defined(HAVE_PROTO_XFDMASTER_H)
&libxmp_depacker_xfd,
#endif
&libxmp_depacker_zip,
&libxmp_depacker_lha,
&libxmp_depacker_gzip,
&libxmp_depacker_bzip2,
&libxmp_depacker_xz,
&libxmp_depacker_compress,
&libxmp_depacker_pp,
&libxmp_depacker_sqsh,
&libxmp_depacker_arc,
&libxmp_depacker_arcfs,
&libxmp_depacker_mmcmp,
&libxmp_depacker_muse,
&libxmp_depacker_lzx,
&libxmp_depacker_s404,
NULL
};
#if defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_EXECVP) && \
defined(HAVE_DUP2) && defined(HAVE_WAIT)
#define DECRUNCH_USE_FORK
#elif defined(HAVE_POPEN) && \
(defined(_WIN32) || defined(__OS2__) || defined(__EMX__) || defined(__DJGPP__) || defined(__riscos__))
#define DECRUNCH_USE_POPEN
#else
static int execute_command(const char * const cmd[], FILE *t) {
return -1;
}
#endif
#ifdef DECRUNCH_USE_POPEN
/* TODO: this may not be safe outside of _WIN32 (which uses CreateProcess). */
static int execute_command(const char * const cmd[], FILE *t)
{
#ifdef _WIN32
struct pt_popen_data *popen_data;
#endif
char line[1024], buf[BUFLEN];
FILE *p;
int pos;
int n;
/* Collapse command array into a command line for popen. */
for (n = 0, pos = 0; cmd[n]; n++) {
int written = snprintf(line + pos, sizeof(line) - pos, n ? "\"%s\" " : "%s ", cmd[n]);
pos += written;
if (pos >= sizeof(line)) {
D_(D_CRIT "popen command line exceeded buffer size");
return -1;
}
}
line[sizeof(line) - 1] = '\0';
D_(D_INFO "popen(%s)", line);
#ifdef _WIN32
p = pt_popen(line, "rb", &popen_data);
#else
p = popen(line, "rb");
#endif
if (p == NULL) {
D_(D_CRIT "failed popen");
return -1;
}
while ((n = fread(buf, 1, BUFLEN, p)) > 0) {
fwrite(buf, 1, n, t);
}
#ifdef _WIN32
pt_pclose(p, &popen_data);
#else
pclose(p);
#endif
return 0;
}
#endif /* USE_PTPOPEN */
#ifdef DECRUNCH_USE_FORK
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
static int execute_command(const char * const cmd[], FILE *t)
{
/* Use pipe/fork/execvp to avoid shell injection vulnerabilities. */
char buf[BUFLEN];
FILE *p;
int n;
int fds[2];
pid_t pid;
int status;
D_(D_INFO "fork/execvp(%s...)", cmd[0]);
if (pipe(fds) < 0) {
D_(D_CRIT "failed pipe");
return -1;
}
if ((pid = fork()) < 0) {
D_(D_CRIT "failed fork");
close(fds[0]);
close(fds[1]);
return -1;
}
if (pid == 0) {
dup2(fds[1], STDOUT_FILENO);
close(fds[0]);
close(fds[1]);
/* argv param isn't const char * const * for some reason but
* exec* only copies the provided arguments. */
execvp(cmd[0], (char * const *)cmd);
exit(errno);
}
close(fds[1]);
wait(&status);
if (!WIFEXITED(status)) {
D_(D_CRIT "process failed (wstatus = %d)", status);
close(fds[0]);
return -1;
}
if (WEXITSTATUS(status)) {
D_(D_CRIT "process exited with status %d", WEXITSTATUS(status));
close(fds[0]);
return -1;
}
if ((p = fdopen(fds[0], "rb")) == NULL) {
D_(D_CRIT "failed fdopen");
close(fds[0]);
return -1;
}
while ((n = fread(buf, 1, BUFLEN, p)) > 0) {
fwrite(buf, 1, n, t);
}
fclose(p);
return 0;
}
#endif /* USE_FORK */
static int decrunch_command(HIO_HANDLE **h, const char * const cmd[], char **temp)
{
#if defined __ANDROID__ || defined __native_client__
/* Don't use external helpers in android */
return 0;
#else
HIO_HANDLE *tmp;
FILE *t;
D_(D_WARN "Depacking file... ");
if ((t = make_temp_file(temp)) == NULL) {
goto err;
}
/* Depack file */
D_(D_INFO "External depacker: %s", cmd[0]);
if (execute_command(cmd, t) < 0) {
D_(D_CRIT "failed");
goto err2;
}
D_(D_INFO "done");
if (fseek(t, 0, SEEK_SET) < 0) {
D_(D_CRIT "fseek error");
goto err2;
}
if ((tmp = hio_open_file2(t)) == NULL)
return -1; /* call closes on failure. */
hio_close(*h);
*h = tmp;
return 0;
err2:
fclose(t);
err:
return -1;
#endif
}
static int decrunch_internal_tempfile(HIO_HANDLE **h, struct depacker *depacker, char **temp)
{
HIO_HANDLE *tmp;
FILE *t;
D_(D_WARN "Depacking file... ");
if ((t = make_temp_file(temp)) == NULL) {
goto err;
}
/* Depack file */
D_(D_INFO "Internal depacker");
if (depacker->depack(*h, t, hio_size(*h)) < 0) {
D_(D_CRIT "failed");
goto err2;
}
D_(D_INFO "done");
if (fseek(t, 0, SEEK_SET) < 0) {
D_(D_CRIT "fseek error");
goto err2;
}
if ((tmp = hio_open_file2(t)) == NULL)
return -1; /* call closes on failure. */
hio_close(*h);
*h = tmp;
return 0;
err2:
fclose(t);
err:
return -1;
}
static int decrunch_internal_memory(HIO_HANDLE **h, struct depacker *depacker)
{
HIO_HANDLE *tmp;
void *out;
long outlen;
D_(D_WARN "Depacking file... ");
/* Depack file */
D_(D_INFO "Internal depacker");
if (depacker->depack_mem(*h, &out, hio_size(*h), &outlen) < 0) {
D_(D_CRIT "failed");
return -1;
}
D_(D_INFO "done");
if ((tmp = hio_open_mem(out, outlen, 1)) == NULL) {
free(out);
return -1;
}
hio_close(*h);
*h = tmp;
return 0;
}
int libxmp_decrunch(HIO_HANDLE **h, const char *filename, char **temp)
{
unsigned char b[1024];
const char *cmd[32];
int headersize;
int i;
struct depacker *depacker = NULL;
cmd[0] = NULL;
*temp = NULL;
headersize = hio_read(b, 1, 1024, *h);
if (headersize < 100) { /* minimum valid file size */
return 0;
}
/* Check built-in depackers */
for (i = 0; depacker_list[i] != NULL; i++) {
if (depacker_list[i]->test(b)) {
depacker = depacker_list[i];
D_(D_INFO "Use depacker %d", i);
break;
}
}
/* Check external commands */
if (depacker == NULL) {
if (b[0] == 'M' && b[1] == 'O' && b[2] == '3') {
/* MO3 */
D_(D_INFO "mo3");
i = 0;
cmd[i++] = "unmo3";
cmd[i++] = "-s";
cmd[i++] = filename;
cmd[i++] = "STDOUT";
cmd[i++] = NULL;
} else if (memcmp(b, "Rar", 3) == 0) {
/* rar */
D_(D_INFO "rar");
i = 0;
cmd[i++] = "unrar";
cmd[i++] = "p";
cmd[i++] = "-inul";
cmd[i++] = "-xreadme";
cmd[i++] = "-x*.diz";
cmd[i++] = "-x*.nfo";
cmd[i++] = "-x*.txt";
cmd[i++] = "-x*.exe";
cmd[i++] = "-x*.com";
cmd[i++] = filename;
cmd[i++] = NULL;
}
}
if (hio_seek(*h, 0, SEEK_SET) < 0) {
return -1;
}
/* Depack file */
if (cmd[0]) {
/* When the filename is unknown (because it is a stream) don't use
* external helpers
*/
if (filename == NULL) {
return 0;
}
return decrunch_command(h, cmd, temp);
} else if (depacker && depacker->depack) {
return decrunch_internal_tempfile(h, depacker, temp);
} else if (depacker && depacker->depack_mem) {
return decrunch_internal_memory(h, depacker);
} else {
D_(D_INFO "Not packed");
return 0;
}
}
/*
* Check whether the given string matches one of the blacklisted glob
* patterns. Used to filter file names stored in archive files.
*/
int libxmp_exclude_match(const char *name)
{
int i;
static const char *const exclude[] = {
"README", "readme", "ReadMe",
"ReadMe!", "readMe!", "!ReadMe!",
"*.DIZ", "*.diz",
"*.NFO", "*.nfo",
"*.DOC", "*.Doc", "*.doc",
"*.INFO", "*.info", "*.Info",
"*.TXT", "*.txt",
"*.EXE", "*.exe",
"*.COM", "*.com",
"*.README", "*.readme", "*.Readme", "*.ReadMe",
/* Found in Spark archives. */
"\\?From", "From\\?", "InfoText",
NULL
};
for (i = 0; exclude[i] != NULL; i++) {
if (fnmatch(exclude[i], name, 0) == 0) {
return 1;
}
}
return 0;
}

View File

@ -0,0 +1,47 @@
#ifndef LIBXMP_DEPACKER_H
#define LIBXMP_DEPACKER_H
#include "../common.h"
#include "../hio.h"
/* Output file size limit for files unpacked from unarchivers into RAM. Most
* general archive compression formats can't nicely bound the output size
* from their input filesize, and a cap is needed for a few reasons:
*
* - Linux is too dumb for its own good and its malloc/realloc will return
* pointers to RAM that doesn't exist instead of NULL. When these are used,
* it will kill the application instead of allowing it to fail gracefully.
* - libFuzzer and the clang sanitizers have malloc/realloc interceptors that
* terminate with an error instead of returning NULL.
*
* Depackers that have better ways of bounding the output size can ignore this.
* This value is fairly arbitrary and can be changed if needed.
*/
#define LIBXMP_DEPACK_LIMIT (512 << 20)
extern struct depacker libxmp_depacker_zip;
extern struct depacker libxmp_depacker_lha;
extern struct depacker libxmp_depacker_gzip;
extern struct depacker libxmp_depacker_bzip2;
extern struct depacker libxmp_depacker_xz;
extern struct depacker libxmp_depacker_compress;
extern struct depacker libxmp_depacker_pp;
extern struct depacker libxmp_depacker_sqsh;
extern struct depacker libxmp_depacker_arc;
extern struct depacker libxmp_depacker_arcfs;
extern struct depacker libxmp_depacker_mmcmp;
extern struct depacker libxmp_depacker_muse;
extern struct depacker libxmp_depacker_lzx;
extern struct depacker libxmp_depacker_s404;
extern struct depacker libxmp_depacker_xfd;
struct depacker {
int (*test)(unsigned char *);
int (*depack)(HIO_HANDLE *, FILE *, long);
int (*depack_mem)(HIO_HANDLE *, void **, long, long *);
};
int libxmp_decrunch (HIO_HANDLE **h, const char *filename, char **temp);
int libxmp_exclude_match (const char *);
#endif /* LIBXMP_DEPACKER_H */

View File

@ -0,0 +1,164 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* This file is part of the Extended Module Player and is distributed
* under the terms of the GNU Lesser General Public License. See COPYING.LIB
* for more information.
*/
#include "../common.h"
#include "depacker.h"
#include "crc32.h"
#include "miniz.h"
/* See RFC1952 for further information */
/* The flag byte is divided into individual bits as follows:
bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
bit 4 FCOMMENT
bit 5 reserved
bit 6 reserved
bit 7 reserved
*/
#define FLAG_FTEXT (1 << 0)
#define FLAG_FHCRC (1 << 1)
#define FLAG_FEXTRA (1 << 2)
#define FLAG_FNAME (1 << 3)
#define FLAG_FCOMMENT (1 << 4)
struct member {
uint8 id1;
uint8 id2;
uint8 cm;
uint8 flg;
uint32 mtime;
uint8 xfl;
uint8 os;
uint32 crc32;
uint32 size;
};
static int test_gzip(unsigned char *b)
{
return b[0] == 31 && b[1] == 139;
}
static int decrunch_gzip(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
struct member member;
int val, c;
size_t in_buf_size;
void *pCmp_data, *pOut_buf;
size_t pOut_len;
uint32 crc_in, crc;
long start;
member.id1 = hio_read8(in);
member.id2 = hio_read8(in);
member.cm = hio_read8(in);
member.flg = hio_read8(in);
member.mtime = hio_read32l(in);
member.xfl = hio_read8(in);
member.os = hio_read8(in);
if (member.cm != 0x08) {
D_(D_CRIT "Unsuported compression method: %x", member.cm);
return -1;
}
if (member.flg & FLAG_FEXTRA) {
int xlen = hio_read16l(in);
if (hio_seek(in, xlen, SEEK_CUR) < 0) {
D_(D_CRIT "hio_seek() failed");
return -1;
}
}
if (member.flg & FLAG_FNAME) {
do {
c = hio_read8(in);
if (hio_error(in)) {
D_(D_CRIT "hio_read8() failed");
return -1;
}
} while (c != 0);
}
if (member.flg & FLAG_FCOMMENT) {
do {
c = hio_read8(in);
if (hio_error(in)) {
D_(D_CRIT "hio_read8() failed");
return -1;
}
} while (c != 0);
}
if (member.flg & FLAG_FHCRC) {
hio_read16l(in);
}
start = hio_tell(in);
if (hio_error(in) || start < 0 || inlen < start || inlen - start < 8) {
D_(D_CRIT "input file is truncated or is missing gzip footer");
return -1;
}
in_buf_size = inlen - start - 8;
pCmp_data = (uint8 *)malloc(in_buf_size);
if (!pCmp_data)
{
D_(D_CRIT "Out of memory");
return -1;
}
if (hio_read(pCmp_data, 1, in_buf_size, in) != in_buf_size)
{
D_(D_CRIT "Failed reading input file");
free(pCmp_data);
return -1;
}
pOut_buf = tinfl_decompress_mem_to_heap(pCmp_data, in_buf_size, &pOut_len, 0);
if (!pOut_buf) {
D_(D_CRIT "tinfl_decompress_mem_to_heap() failed");
free(pCmp_data);
return -1;
}
free(pCmp_data);
crc_in = hio_read32l(in);
crc = libxmp_crc32_A((uint8 *)pOut_buf, pOut_len, 0UL);
if (crc_in != crc) {
D_(D_CRIT "CRC-32 mismatch: expected %08zx, got %08zx",
(size_t)crc_in, (size_t)crc);
free(pOut_buf);
return -1;
}
/* Check file size */
val = hio_read32l(in);
if (val != pOut_len) {
D_(D_CRIT "Invalid file size");
free(pOut_buf);
return -1;
}
*out = pOut_buf;
*outlen = pOut_len;
return 0;
}
struct depacker libxmp_depacker_gzip = {
test_gzip,
NULL,
decrunch_gzip
};

445
libxmp/src/depackers/lzx.c Normal file
View File

@ -0,0 +1,445 @@
/* Extended Module Player
* Copyright (C) 2022 Alice Rowan <petrifiedrowan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Simple single-file unpacker for Amiga LZX archives.
* Report bugs to libxmp or to here: https://github.com/AliceLR/megazeuxtests
*
* Most format info was reverse engineered with a hex editor, with some
* minor details filled in from the comments in unlzx.c (unknown license).
* Usage of unlzx.c is directly stated and is probably non-copyrightable.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "crc32.h"
#include "depacker.h"
#include "lzx_unpack.h"
/* #define LZX_DEBUG */
/* Arbitrary output maximum file length. */
#define LZX_OUTPUT_MAX LIBXMP_DEPACK_LIMIT
#define LZX_HEADER_SIZE 10
#define LZX_ENTRY_SIZE 31
#define LZX_FLAG_MERGED 1
#define LZX_NO_SELECTION ((size_t)-1)
#ifdef LZX_DEBUG
#define debug(...) do{ fprintf(stderr, "" __VA_ARGS__); fflush(stderr); }while(0)
#endif
static lzx_uint32 lzx_crc32(lzx_uint32 crc, const lzx_uint8 *buf, size_t len)
{
return libxmp_crc32_A(buf, len, crc);
}
static inline lzx_uint32 lzx_mem_u32(const lzx_uint8 *buf)
{
return (buf[3] << 24UL) | (buf[2] << 16UL) | (buf[1] << 8UL) | buf[0];
}
enum lzx_merge_state
{
NO_MERGE,
IN_MERGE,
FINAL_MERGE_ENTRY
};
struct lzx_data
{
/* 0 */ char magic[3]; /* "LZX" */
/* 3 lzx_uint8 unknown0; */ /* Claimed to be flags by unlzx.c */
/* 4 lzx_uint8 lzx_version; */ /* 0x0 for <=1.20R, 0xc for >=1.21 */
/* 5 lzx_uint8 unknown1; */
/* 6 lzx_uint8 format_version;*/ /* 0xa */
/* 7 lzx_uint8 flags; */
/* 8 lzx_uint8 unknown2[2]; */
/* 10 */
/* Most of the above info is guessed due to lack of documentation.
*
* The non-zero header bytes seem to be tied to the version used.
* Byte 6 is always 0x0a, and is maybe intended to be the format version.
* Byte 4 is always 0x0c for versions >=1.21 and may be intended to be the
* LZX archiver version (0xc -> 1.2, similar to 0xa -> 1.0 for the format).
* Byte 7 is used for flags. 1=damage protection, 2=locked. 4=unknown
* is always set for versions >=1.21. None of these flags are documented.
*/
/* Data for the current merge record to allow reading in one pass.
* A merged record starts with an entry with a 0 compressed size and the
* merged flag set, and ends when a compressed size is encountered. */
enum lzx_merge_state merge_state;
int merge_invalid;
size_t merge_total_size;
size_t selected_offset;
size_t selected_size;
lzx_uint32 selected_crc32;
};
struct lzx_entry
{
/* 0 lzx_uint8 attributes; */
/* 1 lzx_uint8 unknown0; */
/* 2 */ lzx_uint32 uncompressed_size;
/* 6 */ lzx_uint32 compressed_size;
/* 10 lzx_uint8 machine_type; */ /* unlzx.c */
/* 11 */ lzx_uint8 method; /* unlzx.c */
/* 12 */ lzx_uint8 flags; /* unlzx.c */
/* 13 lzx_uint8 unknown1; */
/* 14 */ lzx_uint8 comment_length; /* unlzx.c; = m */
/* 15 */ lzx_uint8 extract_version; /* unlzx.c; should be 0x0A? */
/* 16 lzx_uint16 unknown2; */
/* 18 lzx_uint32 datestamp; */ /* unlzx.c */
/* 22 */ lzx_uint32 crc32; /* unlzx.c */
/* 26 */ lzx_uint32 header_crc32; /* unlzx.c */
/* 30 */ lzx_uint8 filename_length; /* = n */
/* 31 */ char filename[256];
/* 31 + n + m */
lzx_uint32 computed_header_crc32;
/* Date packing (quoted directly from unlzx.c):
*
* "UBYTE packed[4]; bit 0 is MSB, 31 is LSB
* bit # 0-4=Day 5-8=Month 9-14=Year 15-19=Hour 20-25=Minute 26-31=Second"
*
* Year interpretation is non-intuitive due to bugs in the original LZX, but
* Classic Workbench bundles Dr.Titus' fixed LZX, which interprets years as:
*
* 001000b to 011101b -> 1978 to 1999 Original range
* 111010b to 111111b -> 2000 to 2005 Original-compatible Y2K bug range
* 011110b to 111001b -> 2006 to 2033 Dr.Titus extension
* 000000b to 000111b -> 2034 to 2041 Dr.Titus extension (reserved values)
*
* The buggy original range is probably caused by ([2 digit year] - 70) & 63.
*/
};
static int lzx_read_header(struct lzx_data *lzx, HIO_HANDLE *f)
{
unsigned char buf[LZX_HEADER_SIZE];
if(hio_read(buf, 1, LZX_HEADER_SIZE, f) < LZX_HEADER_SIZE)
return -1;
if(memcmp(buf, "LZX", 3))
return -1;
memset(lzx, 0, sizeof(struct lzx_data));
lzx->selected_offset = LZX_NO_SELECTION;
return 0;
}
static int lzx_read_entry(struct lzx_entry *e, HIO_HANDLE *f)
{
unsigned char buf[256];
lzx_uint32 crc;
/* unlzx.c claims there's a method 32 for EOF, but nothing like this
* has shown up. Most LZX archives just end after the last file. */
if(hio_read(buf, 1, LZX_ENTRY_SIZE, f) < LZX_ENTRY_SIZE)
return -1;
e->uncompressed_size = lzx_mem_u32(buf + 2);
e->compressed_size = lzx_mem_u32(buf + 6);
e->method = buf[11];
e->flags = buf[12];
e->comment_length = buf[14];
e->extract_version = buf[15];
e->crc32 = lzx_mem_u32(buf + 22);
e->header_crc32 = lzx_mem_u32(buf + 26);
e->filename_length = buf[30];
/* The header CRC is taken with its field 0-initialized. (unlzx.c) */
memset(buf + 26, 0, 4);
crc = lzx_crc32(0, buf, LZX_ENTRY_SIZE);
if(e->filename_length)
{
if(hio_read(e->filename, 1, e->filename_length, f) < e->filename_length)
return -1;
crc = lzx_crc32(crc, (lzx_uint8 *)e->filename, e->filename_length);
}
e->filename[e->filename_length] = '\0';
/* Mostly assuming this part because the example files don't have it. */
if(e->comment_length)
{
if(hio_read(buf, 1, e->comment_length, f) < e->comment_length)
return -1;
crc = lzx_crc32(crc, buf, e->comment_length);
}
e->computed_header_crc32 = crc;
return 0;
}
static void lzx_reset_merge(struct lzx_data *lzx)
{
lzx->merge_state = NO_MERGE;
lzx->merge_invalid = 0;
lzx->merge_total_size = 0;
lzx->selected_offset = LZX_NO_SELECTION;
}
static int lzx_has_selected_file(struct lzx_data *lzx)
{
return lzx->selected_offset != LZX_NO_SELECTION;
}
static void lzx_select_file(struct lzx_data *lzx, const struct lzx_entry *e)
{
if(!lzx_has_selected_file(lzx))
{
/* For multiple file output, use a queue here instead... */
lzx->selected_offset = lzx->merge_total_size;
lzx->selected_size = e->uncompressed_size;
lzx->selected_crc32 = e->crc32;
#ifdef LZX_DEBUG
debug("selecting file '%s'\n", e->filename);
debug(" offset: %zu size: %zu crc: %08zx\n",
lzx->selected_offset, lzx->selected_size, (size_t)lzx->selected_crc32);
#endif
}
}
static int lzx_check_entry(struct lzx_data *lzx, const struct lzx_entry *e,
size_t file_len)
{
int selectable = 1;
#ifdef LZX_DEBUG
debug("checking file '%s'\n", e->filename);
#endif
/* Filter unsupported or junk files. */
if(e->header_crc32 != e->computed_header_crc32 ||
e->compressed_size >= file_len ||
e->uncompressed_size > LZX_OUTPUT_MAX ||
e->extract_version > 0x0a ||
lzx_method_is_supported(e->method) < 0 ||
libxmp_exclude_match(e->filename))
{
#ifdef LZX_DEBUG
if(e->header_crc32 != e->computed_header_crc32)
{
debug("skipping file: header CRC-32 mismatch (got 0x%08zx, expected 0x%08zx)\n",
(size_t)e->computed_header_crc32, (size_t)e->header_crc32);
}
else
{
debug("skipping file: unsupported file (u:%zu c:%zu ver:%u method:%u flag:%u)\n",
(size_t)e->uncompressed_size, (size_t)e->compressed_size, e->extract_version,
e->method, e->flags);
}
#endif
lzx->merge_invalid = 1;
selectable = 0;
}
/* Not invalid, but not useful (and bad for some realloc implementations). */
if(e->uncompressed_size == 0)
selectable = 0;
if(e->flags & LZX_FLAG_MERGED)
{
if(lzx->merge_state != IN_MERGE)
{
lzx_reset_merge(lzx);
lzx->merge_state = IN_MERGE;
}
/* Check overflow for 32-bit systems and other unsupported things. */
if(lzx->merge_invalid ||
e->method != LZX_M_PACKED ||
lzx->merge_total_size + e->uncompressed_size < lzx->merge_total_size ||
lzx->merge_total_size + e->uncompressed_size > LZX_OUTPUT_MAX)
{
lzx->merge_invalid = 1;
selectable = 0;
}
if(selectable)
lzx_select_file(lzx, e);
lzx->merge_total_size += e->uncompressed_size;
if(e->compressed_size)
{
lzx->merge_state = FINAL_MERGE_ENTRY;
if(lzx_has_selected_file(lzx) && !lzx->merge_invalid)
return 0;
}
/* Continue until a usable entry with compressed data is found. */
return -1;
}
/* Not merged */
lzx_reset_merge(lzx);
if(selectable)
{
lzx_select_file(lzx, e);
lzx->merge_total_size += e->uncompressed_size;
return 0;
}
return -1;
}
static int lzx_read(unsigned char **dest, size_t *dest_len, HIO_HANDLE *f,
unsigned long file_len)
{
struct lzx_data lzx;
struct lzx_entry e;
unsigned char *out;
unsigned char *in;
size_t out_len;
lzx_uint32 out_crc32;
int err;
if(lzx_read_header(&lzx, f) < 0)
return -1;
while(1)
{
if(lzx_read_entry(&e, f) < 0)
{
#ifdef LZX_DEBUG
debug("failed to read entry\n");
#endif
return -1;
}
if(lzx_check_entry(&lzx, &e, file_len) < 0)
{
if(e.compressed_size && hio_seek(f, e.compressed_size, SEEK_CUR) < 0)
return -1;
continue;
}
#ifdef LZX_DEBUG
debug("extracting file '%s'\n", e.filename);
#endif
/* Extract */
in = (unsigned char *)malloc(e.compressed_size);
if(in == NULL)
return -1;
if(hio_read(in, 1, e.compressed_size, f) < e.compressed_size)
{
free(in);
return -1;
}
if(e.method != LZX_M_UNPACKED)
{
out = (unsigned char *)malloc(lzx.merge_total_size);
out_len = lzx.merge_total_size;
if(out == NULL)
{
free(in);
return -1;
}
err = lzx_unpack(out, out_len, in, e.compressed_size, e.method);
free(in);
if(err)
{
#ifdef LZX_DEBUG
debug("unpack failed\n");
#endif
free(out);
return -1;
}
}
else
{
out = in;
out_len = e.compressed_size;
}
/* Select a file from a merge (if needed). */
if(lzx.selected_size < out_len)
{
unsigned char *t;
#ifdef LZX_DEBUG
debug("using data pos:%zu len:%zu in merge of length %zu\n",
lzx.selected_offset, lzx.selected_size, lzx.merge_total_size);
#endif
if(lzx.selected_offset && lzx.selected_offset <= out_len - lzx.selected_size)
memmove(out, out + lzx.selected_offset, lzx.selected_size);
out_len = lzx.selected_size;
t = (unsigned char *)realloc(out, out_len);
if(t != NULL)
out = t;
}
out_crc32 = lzx_crc32(0, out, out_len);
if(out_crc32 != lzx.selected_crc32)
{
#ifdef LZX_DEBUG
debug("file CRC-32 mismatch (got 0x%08zx, expected 0x%08zx)\n",
(size_t)out_crc32, (size_t)lzx.selected_crc32);
#endif
free(out);
return -1;
}
*dest = out;
*dest_len = out_len;
return 0;
}
}
static int test_lzx(unsigned char *b)
{
return !memcmp(b, "LZX", 3);
}
static int decrunch_lzx(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
unsigned char *dest;
size_t dest_len;
if(lzx_read(&dest, &dest_len, in, inlen) < 0)
return -1;
*out = dest;
*outlen = dest_len;
return 0;
}
struct depacker libxmp_depacker_lzx =
{
test_lzx,
NULL,
decrunch_lzx
};

View File

@ -0,0 +1,751 @@
/* Extended Module Player
* Copyright (C) 2022 Alice Rowan <petrifiedrowan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Unpacker for Amiga LZX compressed streams.
* Report bugs to libxmp or to here: https://github.com/AliceLR/megazeuxtests
*
* Based primarily on the LZX compression documentation from MSDN, with
* further reference and corrections based on temisu's Ancient decompressor:
*
* https://docs.microsoft.com/en-us/previous-versions/bb417343(v=msdn.10)?redirectedfrom=MSDN#microsoft-lzx-data-compression-format
* https://github.com/temisu/ancient/blob/master/src/LZXDecompressor.cpp
*
* The following changes are required from the MSDN documentation for this
* to work correctly:
*
* * CAB LZX adds a x86 bytecode preprocessing header not relevant here.
*
* * CAB LZX changed the block type values:
* 1 is verbatim in CAB LZX, but may or may not be used in Amiga LZX.
* 2 is verbatim in classic LZX, but is aligned offsets in CAB LZX.
* 3 is aligned offsets in classic LZX, but is uncompressed in CAB LZX.
* Type 1 is supported by some Amiga LZX depackers but I haven't seen it.
* In these depackers it's treated as verbatim with no stored tree.
*
* * The bitstream description is wrong for classic LZX. Amiga LZX reads
* big endian 16-bit codes into a little endian bitstream, but CAB LZX
* appears to have been updated to do the opposite.
*
* * Amiga LZX uses a fixed 64k window and 512 distance+length codes. It
* does not have a separate lengths tree. The distance slot is determined
* by (symbol - 256) & 0x1f and the length slot is determined by
* (symbol - 256) >> 5. Both use the same set of slots, which are the same
* as the first 32 CAB LZX position slots. Amiga LZX only has one stored
* previous distance rather than CAB LZX's three.
*
* * The documentation states block lengths are a 24-bit field but fails to
* clarify that they're read in three 8-bit chunks big endian style. This
* is corrected in the LZX DELTA specification.
*
* * The aligned offset tree header documentation is wrong, even for CAB:
* in CAB LZX, the aligned offset tree is after the block length, but in
* Amiga LZX, it's BEFORE the block length.
*
* * The code tree width delta algorithm is incorrectly documented as
* (prev_len[x] + code) % 17 instead of (prev_len[x] - code + 17) % 17.
* This is corrected in the LZX DELTA specification. The Amiga LZX delta
* RLE codes also have separate behavior for the two main tree blocks.
*
* * In CAB LZX the aligned offsets tree is only used for >3 bit distances,
* but Amiga LZX also uses it for 3 bit distances.
*/
#include "lzx_unpack.h"
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/* #define LZX_DEBUG */
#define LZX_LOOKUP_BITS 12
#define LZX_LOOKUP_MASK ((1 << LZX_LOOKUP_BITS) - 1)
#define LZX_NUM_CHARS 256
#define LZX_MAX_CODES (LZX_NUM_CHARS + 512)
#define LZX_MAX_ALIGNED 8
#define LZX_MAX_PRETREE 20
#define LZX_MAX_BINS 17
#define LZX_CODE_BINS 17
#define LZX_ALIGNED_BINS 8
#define LZX_PRETREE_BINS 16
/* This is 2 in CAB LZX, but Amiga LZX seems to rely on 3 instead. */
#define LZX_MIN_MATCH 3
#ifdef LZX_DEBUG
#include <assert.h>
#include <stdio.h>
#define debug(...) do{ fprintf(stderr, "" __VA_ARGS__); fflush(stderr); }while(0)
#endif
/* Bit buffer should be able to hold at least 32-bits, but 64 is better.
* There are edge cases where size_t is 16-bits but they aren't relevant. */
typedef size_t buffertype;
#if defined(_WIN64) || LONG_MAX > 0x7fffffffL
#define LZX_BUFFERTYPE_IS_64
#endif
/* Position slot base positions table from MSDN documentation. */
static const unsigned lzx_slot_base[32] =
{
0, 1, 2, 3, 4, 6, 8, 12,
16, 24, 32, 48, 64, 96, 128, 192,
256, 384, 512, 768, 1024, 1536, 2048, 3072,
4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152
};
/* Position slot footer bits table from MSDN documentation. */
static const unsigned lzx_slot_bits[32] =
{
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14
};
static const lzx_uint8 lzx_reverse8[] =
{
0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
};
static lzx_uint16 lzx_reverse16(lzx_uint16 v)
{
return (lzx_reverse8[v & 0xff] << 8) | lzx_reverse8[v >> 8];
}
static lzx_uint16 lzx_mem_u16be(const unsigned char *buf)
{
return (buf[0] << 8) | buf[1];
}
enum lzx_block_type
{
LZX_B_VERBATIM_NO_TREE = 1,
LZX_B_VERBATIM = 2,
LZX_B_ALIGNED = 3,
};
struct lzx_lookup
{
lzx_uint16 value;
lzx_uint8 length;
};
struct lzx_bin
{
lzx_uint16 offset; /* Translate code to its position in the values list. */
lzx_uint16 last; /* Position after last valid position in this bin. */
};
struct lzx_tree
{
lzx_uint16 *values;
struct lzx_lookup *lookup;
unsigned num_values;
unsigned num_bins;
unsigned min_bin;
struct lzx_bin bins[LZX_MAX_BINS];
};
struct lzx_data
{
size_t in;
size_t out;
unsigned eof;
buffertype buffer;
unsigned buffer_left;
struct lzx_tree codes;
struct lzx_tree aligned;
struct lzx_tree pretree;
lzx_uint16 code_values[LZX_MAX_CODES];
lzx_uint16 aligned_values[LZX_MAX_ALIGNED];
lzx_uint16 pretree_values[LZX_MAX_PRETREE];
/* LZX stores delta widths for codes between blocks. */
lzx_uint8 code_widths[LZX_MAX_CODES];
};
static struct lzx_data *lzx_unpack_init()
{
struct lzx_data *lzx = (struct lzx_data *)calloc(1, sizeof(struct lzx_data));
if(!lzx)
return NULL;
lzx->codes.values = lzx->code_values;
lzx->aligned.values = lzx->aligned_values;
lzx->pretree.values = lzx->pretree_values;
lzx->codes.lookup = (struct lzx_lookup *)calloc((1 << LZX_LOOKUP_BITS), sizeof(struct lzx_lookup));
if(!lzx->codes.lookup)
{
free(lzx);
return NULL;
}
return lzx;
}
static void lzx_unpack_free(struct lzx_data *lzx)
{
free(lzx->codes.lookup);
free(lzx);
}
/* Amiga LZX uses an LSB ordered (right shift) bitstream, but
* rather than appending bytes, it appends 16-bit big endian words.
*/
static void lzx_word_in(struct lzx_data * LZX_RESTRICT lzx,
const unsigned char *src, size_t src_len)
{
if(src_len - lzx->in >= 2)
{
lzx->buffer |= (buffertype)lzx_mem_u16be(src + lzx->in) << lzx->buffer_left;
lzx->buffer_left += 16;
lzx->in += 2;
}
}
/* Not guaranteed to return the requested number of bits! */
static unsigned lzx_peek_bits(struct lzx_data * LZX_RESTRICT lzx,
const unsigned char *src, size_t src_len, unsigned num)
{
static const lzx_uint16 BIT_MASKS[17] =
{
0,
0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff,
0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
#ifdef LZX_DEBUG
/* It is currently impossible for >16 to reach here but
* this assert might be useful for debug. */
assert(num <= 16);
#endif
if(lzx->buffer_left < num)
{
/* Minor optimization for 64-bit builds:
* buffer_left < 16, so 3 words can be read into the buffer. */
#ifdef LZX_BUFFERTYPE_IS_64
lzx_word_in(lzx, src, src_len);
lzx_word_in(lzx, src, src_len);
#endif
lzx_word_in(lzx, src, src_len);
}
return lzx->buffer & BIT_MASKS[num];
}
/* Bounds check and discard bits from lzx_peek_bits. */
static int lzx_skip_bits(struct lzx_data * LZX_RESTRICT lzx,
unsigned num)
{
if(lzx->buffer_left < num)
{
lzx->eof = 1;
return -1;
}
lzx->buffer >>= num;
lzx->buffer_left -= num;
return 0;
}
/* Read and remove bits from the bitstream (effectively peek + skip). */
static lzx_int32 lzx_get_bits(struct lzx_data * LZX_RESTRICT lzx,
const unsigned char *src, size_t src_len, unsigned num)
{
unsigned peek = lzx_peek_bits(lzx, src, src_len, num);
if(lzx_skip_bits(lzx, num) < 0)
return -1;
return peek;
}
/*
* Huffman decoder.
*
* Since LZX uses canonical Huffman, the Huffman tree can be optimized out
* entirely. All that is required is a set of bins for all of the bit widths
* and a list of values in the order they appear in the tree, from left to
* right. To get the list index, subtract bin.offset from a code. If the
* index is less than bin.last, it is a valid code for that width.
*
* A lookup table can be used on top of this as with usual Huffman trees.
*/
static int lzx_get_huffman(struct lzx_data * LZX_RESTRICT lzx,
const struct lzx_tree *tree, const unsigned char *src, size_t src_len)
{
unsigned pos = tree->min_bin;
unsigned peek;
peek = lzx_peek_bits(lzx, src, src_len, 16);
if(tree->lookup)
{
struct lzx_lookup e = tree->lookup[peek & LZX_LOOKUP_MASK];
if(e.length)
{
if(lzx_skip_bits(lzx, e.length) < 0)
return -1;
return e.value;
}
pos = LZX_LOOKUP_BITS + 1;
}
/* Fast canonical Huffman needs MSB ordered codes, but LZX is LSB ordered. */
peek = lzx_reverse16(peek);
for(; pos < tree->num_bins; pos++)
{
unsigned code = peek >> (16 - pos);
code -= tree->bins[pos].offset;
if(code < tree->bins[pos].last)
{
if(lzx_skip_bits(lzx, pos) < 0)
return -1;
return tree->values[code];
}
}
return -1;
}
static int lzx_prepare_huffman(struct lzx_tree * LZX_RESTRICT tree,
const lzx_uint16 *counts, const lzx_uint8 *widths, unsigned max_codes,
unsigned max_bins)
{
unsigned offsets[LZX_CODE_BINS];
unsigned pos = 0;
unsigned first = 0;
unsigned i;
tree->num_values = 0;
tree->num_bins = 0;
tree->min_bin = 0;
for(i = 1; i < max_bins; i++)
{
offsets[i] = pos;
pos += counts[i];
if(counts[i])
{
if(!tree->min_bin)
tree->min_bin = i;
tree->num_bins = i + 1;
tree->num_values = pos;
}
tree->bins[i].offset = first - offsets[i];
tree->bins[i].last = pos;
first = (first + counts[i]) << 1;
#ifdef LZX_DEBUG
if(tree->min_bin)
debug("bin %u: %04x %u\n", i, tree->bins[i].offset, tree->bins[i].last);
#endif
}
/* The "first" value after completing a valid Huffman tree should be the
* maximum number of codes that can be held by a [max_bins]-bit tree.
* If it isn't, the Huffman tree is under/over-specified. */
#ifdef LZX_DEBUG
debug("Huffman tree: sum=%u expected=%u\n", first, 1 << max_bins);
#endif
if(first != 1U << max_bins)
return -1;
for(i = 0; i < max_codes; i++)
{
if(widths[i] > 0)
{
unsigned offset = offsets[widths[i]]++;
tree->values[offset] = i;
}
}
#ifdef LZX_DEBUG
if(max_codes <= 20)
for(i = 0; i < tree->num_values; i++)
debug("code %u: %u\n", i, tree->values[i]);
#endif
return 0;
}
static void lzx_prepare_lookup(struct lzx_tree * LZX_RESTRICT tree,
const lzx_uint16 *counts)
{
struct lzx_lookup *dest = tree->lookup;
struct lzx_lookup e;
unsigned bin = tree->min_bin;
unsigned j = 0;
unsigned i;
unsigned code;
unsigned iter;
if(!tree->lookup)
return;
memset(dest, 0, (1 << LZX_LOOKUP_BITS) * sizeof(struct lzx_lookup));
for(i = 0, j = 0; i < tree->num_values; i++, j++)
{
while(j >= counts[bin])
{
bin++;
j = 0;
if(bin >= tree->num_bins || bin > LZX_LOOKUP_BITS)
return;
}
e.value = tree->values[i];
e.length = bin;
/* LZX uses an LSB ordered stream but canonical Huffman codes are MSB,
* so they need to be bit reversed to get a table matching the stream. */
code = i + tree->bins[bin].offset;
code = lzx_reverse16(code) >> (16 - bin);
iter = 1 << bin;
for(; code < (1 << LZX_LOOKUP_BITS); code += iter)
dest[code] = e;
}
}
static int lzx_read_aligned(struct lzx_data * LZX_RESTRICT lzx,
const unsigned char *src, size_t src_len)
{
struct lzx_tree *tree = &(lzx->aligned);
lzx_uint8 widths[LZX_MAX_ALIGNED];
lzx_uint16 counts[LZX_ALIGNED_BINS];
unsigned i;
memset(counts, 0, sizeof(counts));
#ifdef LZX_DEBUG
debug("aligned offsets\n");
#endif
for(i = 0; i < LZX_MAX_ALIGNED; i++)
{
lzx_int32 w = lzx_get_bits(lzx, src, src_len, 3);
if(w < 0)
return -1;
widths[i] = w;
counts[w]++;
}
return lzx_prepare_huffman(tree, counts, widths, LZX_MAX_ALIGNED, LZX_ALIGNED_BINS);
}
static int lzx_read_pretree(struct lzx_data * LZX_RESTRICT lzx,
const unsigned char *src, size_t src_len)
{
struct lzx_tree *tree = &(lzx->pretree);
lzx_uint8 widths[LZX_MAX_PRETREE];
lzx_uint16 counts[LZX_PRETREE_BINS];
unsigned i;
memset(counts, 0, sizeof(counts));
#ifdef LZX_DEBUG
debug("pretree\n");
#endif
for(i = 0; i < LZX_MAX_PRETREE; i++)
{
lzx_int32 w = lzx_get_bits(lzx, src, src_len, 4);
if(w < 0)
return -1;
widths[i] = w;
counts[w]++;
}
return lzx_prepare_huffman(tree, counts, widths, LZX_MAX_PRETREE, LZX_PRETREE_BINS);
}
static int lzx_read_delta(struct lzx_data *lzx,
lzx_uint16 * LZX_RESTRICT counts, lzx_uint8 * LZX_RESTRICT widths,
int i, int max, const unsigned char *src, size_t src_len)
{
/* In Amiga LZX (but not CAB LZX) the RLE bit reads and repeat count
* values vary depending on which section of the tree is being read.
* The changes for this were found by experimenting with LZX files and
* then confirming against other Amiga LZX decompressors. */
int is_dists = (i >= LZX_NUM_CHARS);
#ifdef LZX_DEBUG
debug("code deltas %d through %d\n", i, max);
#endif
while(i < max)
{
lzx_int32 w = lzx_get_huffman(lzx, &(lzx->pretree), src, src_len);
lzx_int32 bits;
lzx_int32 num;
if(w < 0 || w >= 20)
return -1;
switch(w)
{
default:
widths[i] = (widths[i] + 17 - w) % 17;
counts[widths[i]]++;
i++;
break;
case 17: /* Short run of 0. */
bits = lzx_get_bits(lzx, src, src_len, 4);
num = bits + 4 - is_dists;
if(bits < 0 || num > max - i)
return -1;
memset(widths + i, 0, num);
counts[0] += num;
i += num;
break;
case 18: /* Long run of 0. */
bits = lzx_get_bits(lzx, src, src_len, 5 + is_dists);
num = bits + 20 - is_dists;
if(bits < 0 || num > max - i)
return -1;
memset(widths + i, 0, num);
counts[0] += num;
i += num;
break;
case 19: /* Short run of same value. */
bits = lzx_get_bits(lzx, src, src_len, 1);
num = bits + 4 - is_dists;
if(bits < 0 || num > max - i)
return -1;
w = lzx_get_huffman(lzx, &(lzx->pretree), src, src_len);
if(w < 0 || w > 16)
return -1;
w = (widths[i] + 17 - w) % 17;
memset(widths + i, w, num);
counts[w] += num;
i += num;
break;
}
}
return 0;
}
static int lzx_read_codes(struct lzx_data * LZX_RESTRICT lzx,
const unsigned char *src, size_t src_len)
{
struct lzx_tree *tree = &(lzx->codes);
lzx_uint8 *widths = lzx->code_widths;
lzx_uint16 counts[LZX_CODE_BINS];
memset(counts, 0, sizeof(counts));
/* Read pretree and first 256 codes. */
if(lzx_read_pretree(lzx, src, src_len) < 0)
return -1;
if(lzx_read_delta(lzx, counts, widths, 0, LZX_NUM_CHARS, src, src_len) < 0)
return -1;
/* Read pretree and distance codes. */
if(lzx_read_pretree(lzx, src, src_len) < 0)
return -1;
if(lzx_read_delta(lzx, counts, widths, LZX_NUM_CHARS, LZX_MAX_CODES, src, src_len) < 0)
return -1;
if(lzx_prepare_huffman(tree, counts, widths, LZX_MAX_CODES, LZX_CODE_BINS) < 0)
return -1;
lzx_prepare_lookup(tree, counts);
return 0;
}
/*
* LZX unpacking.
*/
static void lzx_copy_dictionary(struct lzx_data * LZX_RESTRICT lzx,
unsigned char * LZX_RESTRICT dest, ptrdiff_t distance, size_t length)
{
ptrdiff_t offset = (ptrdiff_t)lzx->out - distance;
unsigned char *pos;
unsigned char *end;
/* LZX can emit these for starting runs of 0. */
if(offset < 0)
{
size_t count = -offset;
if(count > length)
count = length;
memset(dest + lzx->out, 0, count);
lzx->out += count;
length -= count;
offset = 0;
}
pos = dest + offset;
end = pos + length;
dest += lzx->out;
lzx->out += length;
while(pos < end)
*(dest++) = *(pos++);
}
int lzx_unpack(unsigned char * LZX_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len, int method)
{
struct lzx_data *lzx;
size_t bytes_out;
unsigned prev_distance = 1;
/* Only one supported compression method. */
if(method != LZX_M_PACKED)
return -1;
lzx = lzx_unpack_init();
if(!lzx)
return -1;
while(lzx->out < dest_len)
{
unsigned block_type = lzx_get_bits(lzx, src, src_len, 3);
#ifdef LZX_DEBUG
debug("\nblock type:%u\n", block_type);
#endif
if(block_type < LZX_B_VERBATIM_NO_TREE || block_type > LZX_B_ALIGNED)
goto err;
if(block_type == LZX_B_ALIGNED)
if(lzx_read_aligned(lzx, src, src_len) < 0)
goto err;
bytes_out = lzx_get_bits(lzx, src, src_len, 8) << 16;
bytes_out |= lzx_get_bits(lzx, src, src_len, 8) << 8;
bytes_out |= lzx_get_bits(lzx, src, src_len, 8);
if(lzx->eof || bytes_out > dest_len - lzx->out)
goto err;
#ifdef LZX_DEBUG
debug("uncompr.size:%zu (%06zx)\n", bytes_out, bytes_out);
#endif
if(block_type == LZX_B_VERBATIM || block_type == LZX_B_ALIGNED)
{
if(lzx_read_codes(lzx, src, src_len) < 0)
goto err;
}
while(bytes_out)
{
int slot, bits;
unsigned distance, length;
int code = lzx_get_huffman(lzx, &(lzx->codes), src, src_len);
if(code < 0)
{
#ifdef LZX_DEBUG
debug("failed to read code (in:%zu out:%zu)\n", lzx->in, lzx->out);
#endif
goto err;
}
if(code < LZX_NUM_CHARS)
{
#ifdef LZX_DEBUG
debug("b: %02x\n", code);
#endif
dest[lzx->out++] = code;
bytes_out--;
continue;
}
slot = (code - LZX_NUM_CHARS) & 0x1f;
distance = lzx_slot_base[slot];
bits = lzx_slot_bits[slot];
if(bits)
{
if(block_type == LZX_B_ALIGNED && bits >= 3)
{
distance += lzx_get_bits(lzx, src, src_len, bits - 3) << 3;
distance += lzx_get_huffman(lzx, &(lzx->aligned), src, src_len);
}
else
distance += lzx_get_bits(lzx, src, src_len, bits);
}
else
if(!distance)
distance = prev_distance;
prev_distance = distance;
slot = (code - LZX_NUM_CHARS) >> 5;
length = lzx_slot_base[slot] + LZX_MIN_MATCH;
bits = lzx_slot_bits[slot];
if(bits)
length += lzx_get_bits(lzx, src, src_len, bits);
if(lzx->eof || length > bytes_out)
{
#ifdef LZX_DEBUG
debug("invalid length %d (in:%zu out:%zu)\n", length, lzx->in, lzx->out);
#endif
goto err;
}
#ifdef LZX_DEBUG
debug("d: pos=%zu dist=%u length %u\n", lzx->out, distance, length);
#endif
lzx_copy_dictionary(lzx, dest, distance, length);
bytes_out -= length;
}
}
lzx_unpack_free(lzx);
return 0;
err:
lzx_unpack_free(lzx);
return -1;
}

View File

@ -0,0 +1,91 @@
/* Extended Module Player
* Copyright (C) 2022 Alice Rowan <petrifiedrowan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Unpacker for Amiga LZX compressed streams.
* Report bugs to libxmp or to here: https://github.com/AliceLR/megazeuxtests
*/
#ifndef LIBXMP_LZX_UNPACK_H
#define LIBXMP_LZX_UNPACK_H
#ifdef __cplusplus
extern "C" {
#endif
/* libxmp hacks */
#include "../common.h"
typedef uint8 lzx_uint8;
typedef uint16 lzx_uint16;
typedef uint32 lzx_uint32;
typedef int32 lzx_int32;
#define LZX_RESTRICT LIBXMP_RESTRICT
/* end libxmp hacks */
enum lzx_method
{
LZX_M_UNPACKED = 0,
LZX_M_PACKED = 2,
LZX_M_MAX
};
/**
* Determine if a given LZX method is supported.
*
* @param method compression method to test.
*
* @return 0 if a method is supported, otherwise -1.
*/
static inline int lzx_method_is_supported(int method)
{
switch(method)
{
case LZX_M_UNPACKED:
case LZX_M_PACKED:
return 0;
}
return -1;
}
/**
* Unpack a buffer containing an LZX compressed stream into an uncompressed
* representation of the stream. The unpacked method should be handled
* separately from this function since it doesn't need a second output buffer
* for the uncompressed data.
*
* @param dest destination buffer for the uncompressed stream.
* @param dest_len destination buffer size.
* @param src buffer containing the compressed stream.
* @param src_len size of the compressed stream.
* @param method LZX compression method (should be LZX_M_PACKED).
*
* @return 0 on success, otherwise -1.
*/
int lzx_unpack(unsigned char * LZX_RESTRICT dest, size_t dest_len,
const unsigned char *src, size_t src_len, int method);
#ifdef __cplusplus
}
#endif
#endif /* LIBXMP_LZX_UNPACK_H */

View File

@ -0,0 +1,503 @@
#ifndef LIBXMP_MINIZ_H
#define LIBXMP_MINIZ_H 1
#ifndef MINIZ_EXPORT
#define MINIZ_EXPORT
#endif
/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
See "unlicense" statement at the end of this file.
Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
* Low-level Deflate/Inflate implementation notes:
Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
approximately as well as zlib.
Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
block large enough to hold the entire file.
The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
* zlib-style API notes:
miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
zlib replacement in many apps:
The z_stream struct, optional memory allocation callbacks
deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
inflateInit/inflateInit2/inflate/inflateReset/inflateEnd
compress, compress2, compressBound, uncompress
CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
Supports raw deflate streams or standard zlib streams with adler-32 checking.
Limitations:
The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
there are no guarantees that miniz.c pulls this off perfectly.
* PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
Alex Evans. Supports 1-4 bytes/pixel images.
* ZIP archive API notes:
The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
existing archives, create new archives, append new files to existing archives, or clone archive data from
one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
or you can specify custom file read/write callbacks.
- Archive reading: Just call this function to read a single file from a disk archive:
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
size_t *pSize, mz_uint zip_flags);
For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
- Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
The locate operation can optionally check file comments too, which (as one example) can be used to identify
multiple versions of the same file in an archive. This function uses a simple linear search through the central
directory, so it's not very fast.
Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
retrieve detailed info on each file by calling mz_zip_reader_file_stat().
- Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
to disk and builds an exact image of the central directory in memory. The central directory image is written
all at once at the end of the archive file when the archive is finalized.
The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
which can be useful when the archive will be read from optical media. Also, the writer supports placing
arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
readable by any ZIP tool.
- Archive appending: The simple way to add a single file to an archive is to call this function:
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
The archive will be created if it doesn't already exist, otherwise it'll be appended to.
Note the appending is done in-place and is not an atomic operation, so if something goes wrong
during the operation it's possible the archive could be left without a central directory (although the local
file headers and file data will be fine, so the archive will be recoverable).
For more complex archive modification scenarios:
1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
you're done. This is safe but requires a bunch of temporary disk space or heap memory.
2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
append new files as needed, then finalize the archive which will write an updated central directory to the
original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
- ZIP archive support limitations:
No spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
Requires streams capable of seeking.
* This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
* Important: For best perf. be sure to customize the below macros for your target platform:
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#define MINIZ_LITTLE_ENDIAN 1
#define MINIZ_HAS_64BIT_REGISTERS 1
* On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
(i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
*/
#if 1 /* LIBXMP-SPECIFIC CONFIG: */
#ifndef DEBUG /* not a debug build */
#ifndef NDEBUG
#define NDEBUG /* disable assert()s */
#endif
#endif
/* Defines to completely disable specific portions of miniz.c:
If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */
/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */
#define MINIZ_NO_STDIO
/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */
/* get/set file times, and the C run-time funcs that get/set times won't be called. */
/* The current downside is the times written to your archives will be from 1979. */
#define MINIZ_NO_TIME
/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */
#define MINIZ_NO_DEFLATE_APIS
/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */
/*#define MINIZ_NO_INFLATE_APIS */
/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */
/*#define MINIZ_NO_ARCHIVE_APIS */
/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */
#define MINIZ_NO_ARCHIVE_WRITING_APIS
/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */
#define MINIZ_NO_ZLIB_APIS
/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */
/*#define MINIZ_NO_MALLOC */
#ifndef WORDS_BIGENDIAN
#define MINIZ_LITTLE_ENDIAN 1
#else
#define MINIZ_LITTLE_ENDIAN 0
#endif
/* prefix the public functions with 'libxmp_' : */
#define tinfl_decompress libxmp_tinfl_decompress
#define tinfl_decompress_mem_to_heap libxmp_tinfl_decompress_mem_to_heap
#define tinfl_decompress_mem_to_mem libxmp_tinfl_decompress_mem_to_mem
#define tinfl_decompress_mem_to_callback libxmp_tinfl_decompress_mem_to_callback
#define tinfl_decompressor_alloc libxmp_tinfl_decompressor_alloc
#define tinfl_decompressor_free libxmp_tinfl_decompressor_free
#endif /* LIBXMP-SPECIFIC */
#ifdef MINIZ_NO_INFLATE_APIS
#define MINIZ_NO_ARCHIVE_APIS
#endif
#ifdef MINIZ_NO_DEFLATE_APIS
#define MINIZ_NO_ARCHIVE_WRITING_APIS
#endif
#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */
#define MINIZ_NO_TIME
#endif
#include <stddef.h>
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */
#define MINIZ_X86_OR_X64_CPU 1
#else
#define MINIZ_X86_OR_X64_CPU 0
#endif
/* Set MINIZ_LITTLE_ENDIAN only if not set */
#if !defined(MINIZ_LITTLE_ENDIAN)
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */
#define MINIZ_LITTLE_ENDIAN 1
#else
#define MINIZ_LITTLE_ENDIAN 0
#endif
#else
#if MINIZ_X86_OR_X64_CPU
#define MINIZ_LITTLE_ENDIAN 1
#else
#define MINIZ_LITTLE_ENDIAN 0
#endif
#endif
#endif
#if defined(__has_feature)
#if __has_feature(undefined_behavior_sanitizer)
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
#endif
#endif
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
#if MINIZ_X86_OR_X64_CPU
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#define MINIZ_UNALIGNED_USE_MEMCPY
#else
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
#endif
#endif
#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */
#define MINIZ_HAS_64BIT_REGISTERS 1
#else
#define MINIZ_HAS_64BIT_REGISTERS 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------- zlib-style API Definitions. */
/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */
typedef unsigned long mz_ulong;
/* Method */
#define MZ_DEFLATED 8
/* Heap allocation callbacks.
Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */
typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
typedef void (*mz_free_func)(void *opaque, void *address);
typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
#define MZ_VERSION "10.2.0"
#define MZ_VERNUM 0xA100
#define MZ_VER_MAJOR 10
#define MZ_VER_MINOR 2
#define MZ_VER_REVISION 0
#define MZ_VER_SUBREVISION 0
#ifdef __cplusplus
}
#endif
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/* ------------------- Types and macros */
#if 1 /* libxmp-specific typedefs: */
#include "../common.h"
#include "depacker.h"
typedef uint8 mz_uint8;
typedef int16 mz_int16;
typedef uint16 mz_uint16;
typedef uint32 mz_uint32;
typedef int64 mz_int64;
typedef uint64 mz_uint64;
#else
#include <stdint.h>
typedef unsigned char mz_uint8;
typedef signed short mz_int16;
typedef unsigned short mz_uint16;
typedef unsigned int mz_uint32;
typedef int64_t mz_int64;
typedef uint64_t mz_uint64;
#endif
typedef unsigned int mz_uint;
typedef int mz_bool;
#define MZ_FALSE (0)
#define MZ_TRUE (1)
/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */
#ifdef _MSC_VER
#define MZ_MACRO_END while (0, 0)
#else
#define MZ_MACRO_END while (0)
#endif
#define MZ_FILE void *
#define MZ_ASSERT(x) assert(x)
/*#define MZ_MALLOC(x) malloc(x)*/
#define MZ_FREE(x) free(x)
/*#define MZ_REALLOC(p, x) realloc(p, x)*/
/* tinfl doesn't have a nicer way of limiting the output buffer size. */
#define MZ_REALLOC(p,sz) (((sz) <= LIBXMP_DEPACK_LIMIT) ? realloc((p),(sz)) : NULL)
#define MZ_MALLOC(sz) (((sz) <= LIBXMP_DEPACK_LIMIT) ? malloc((sz)) : NULL)
#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj))
#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj))
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
#else
#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif
#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U))
#ifdef _MSC_VER
#define MZ_FORCEINLINE __forceinline
#elif (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2))) || defined(__clang__)
#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__))
#else
#define MZ_FORCEINLINE inline
#endif
#define MZ_UINT16_MAX (0xFFFFU)
#define MZ_UINT32_MAX (0xFFFFFFFFU)
typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1];
typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1];
typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
/* ------------------- Low-level Decompression API Definitions */
#ifndef MINIZ_NO_INFLATE_APIS
#ifdef __cplusplus
extern "C" {
#endif
/* Decompression flags used by tinfl_decompress(). */
/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */
/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */
/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */
/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
TINFL_FLAG_HAS_MORE_INPUT = 2,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
TINFL_FLAG_COMPUTE_ADLER32 = 8
};
/* High level decompression functions: */
/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */
/* On entry: */
/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */
/* On return: */
/* Function returns a pointer to the decompressed data, or NULL on failure. */
/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */
/* The caller must call mz_free() on the returned block when it's no longer needed. */
MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */
/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */
/* Returns 1 on success or 0 on failure. */
typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
struct tinfl_decompressor_tag;
typedef struct tinfl_decompressor_tag tinfl_decompressor;
#ifndef MINIZ_NO_MALLOC
/* Allocate the tinfl_decompressor structure in C so that */
/* non-C language bindings to tinfl_ API don't need to worry about */
/* structure size and allocation mechanism. */
MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void);
MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp);
#endif
/* Max size of LZ dictionary. */
#define TINFL_LZ_DICT_SIZE 32768
/* Return status. */
typedef enum {
/* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */
/* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */
/* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */
TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4,
/* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */
TINFL_STATUS_BAD_PARAM = -3,
/* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */
TINFL_STATUS_ADLER32_MISMATCH = -2,
/* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */
TINFL_STATUS_FAILED = -1,
/* Any status code less than TINFL_STATUS_DONE must indicate a failure. */
/* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */
/* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */
TINFL_STATUS_DONE = 0,
/* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */
/* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */
/* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */
TINFL_STATUS_NEEDS_MORE_INPUT = 1,
/* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */
/* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */
/* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */
/* so I may need to add some code to address this. */
TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;
/* Initializes the decompressor to its initial state. */
#define tinfl_init(r) \
do \
{ \
(r)->m_state = 0; \
} \
MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32
/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */
/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */
MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
/* Internal/private bits follow. */
enum
{
TINFL_MAX_HUFF_TABLES = 3,
TINFL_MAX_HUFF_SYMBOLS_0 = 288,
TINFL_MAX_HUFF_SYMBOLS_1 = 32,
TINFL_MAX_HUFF_SYMBOLS_2 = 19,
TINFL_FAST_LOOKUP_BITS = 10,
TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};
#if MINIZ_HAS_64BIT_REGISTERS
#define TINFL_USE_64BIT_BITBUF 1
#else
#define TINFL_USE_64BIT_BITBUF 0
#endif
#if TINFL_USE_64BIT_BITBUF
typedef mz_uint64 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (64)
#else
typedef mz_uint32 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (32)
#endif
struct tinfl_decompressor_tag
{
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
tinfl_bit_buf_t m_bit_buf;
size_t m_dist_from_out_buf_start;
mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE];
mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2];
mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2];
mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0];
mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1];
mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2];
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};
#ifdef __cplusplus
}
#endif
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
#endif /* LIBXMP_MINIZ_H */

View File

@ -0,0 +1,777 @@
/**************************************************************************
*
* Copyright 2013-2014 RAD Game Tools and Valve Software
* Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
**************************************************************************/
#include "miniz.h"
#ifndef MINIZ_NO_INFLATE_APIS
#ifdef __cplusplus
extern "C" {
#endif
/* ------------------- Low-level Decompression (completely independent from all compression API's) */
#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)
#define TINFL_CR_BEGIN \
switch (r->m_state) \
{ \
case 0:
#define TINFL_CR_RETURN(state_index, result) \
do \
{ \
status = result; \
r->m_state = state_index; \
goto common_exit; \
case state_index:; \
} \
MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) \
do \
{ \
for (;;) \
{ \
TINFL_CR_RETURN(state_index, result); \
} \
} \
MZ_MACRO_END
#define TINFL_CR_FINISH }
#define TINFL_GET_BYTE(state_index, c) \
do \
{ \
while (pIn_buf_cur >= pIn_buf_end) \
{ \
TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \
} \
c = *pIn_buf_cur++; \
} \
MZ_MACRO_END
#define TINFL_NEED_BITS(state_index, n) \
do \
{ \
mz_uint c; \
TINFL_GET_BYTE(state_index, c); \
bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \
num_bits += 8; \
} while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n) \
do \
{ \
if (num_bits < (mz_uint)(n)) \
{ \
TINFL_NEED_BITS(state_index, n); \
} \
bit_buf >>= (n); \
num_bits -= (n); \
} \
MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n) \
do \
{ \
if (num_bits < (mz_uint)(n)) \
{ \
TINFL_NEED_BITS(state_index, n); \
} \
b = bit_buf & ((1 << (n)) - 1); \
bit_buf >>= (n); \
num_bits -= (n); \
} \
MZ_MACRO_END
/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */
/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */
/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */
/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */
#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \
do \
{ \
temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
if (temp >= 0) \
{ \
code_len = temp >> 9; \
if ((code_len) && (num_bits >= code_len)) \
break; \
} \
else if (num_bits > TINFL_FAST_LOOKUP_BITS) \
{ \
code_len = TINFL_FAST_LOOKUP_BITS; \
do \
{ \
temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \
} while ((temp < 0) && (num_bits >= (code_len + 1))); \
if (temp >= 0) \
break; \
} \
TINFL_GET_BYTE(state_index, c); \
bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \
num_bits += 8; \
} while (num_bits < 15);
/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */
/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */
/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */
/* The slow path is only executed at the very end of the input buffer. */
/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */
/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */
#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \
do \
{ \
int temp; \
mz_uint code_len, c; \
if (num_bits < 15) \
{ \
if ((pIn_buf_end - pIn_buf_cur) < 2) \
{ \
TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \
} \
else \
{ \
bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \
pIn_buf_cur += 2; \
num_bits += 16; \
} \
} \
if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
code_len = temp >> 9, temp &= 511; \
else \
{ \
code_len = TINFL_FAST_LOOKUP_BITS; \
do \
{ \
temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \
} while (temp < 0); \
} \
sym = temp; \
bit_buf >>= code_len; \
num_bits -= code_len; \
code_len_hack = code_len; /* FIXME: workaround for miniz/#229 */ \
} \
MZ_MACRO_END
static void tinfl_clear_tree(tinfl_decompressor *r)
{
if (r->m_type == 0)
MZ_CLEAR_ARR(r->m_tree_0);
else if (r->m_type == 1)
MZ_CLEAR_ARR(r->m_tree_1);
else
MZ_CLEAR_ARR(r->m_tree_2);
}
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };
static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };
static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 };
mz_int16 *pTrees[3];
mz_uint8 *pCode_sizes[3];
tinfl_status status = TINFL_STATUS_FAILED;
mz_uint32 num_bits, dist, counter, num_extra;
tinfl_bit_buf_t bit_buf;
const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL;
size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
/* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */
if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start))
{
*pIn_buf_size = *pOut_buf_size = 0;
return TINFL_STATUS_BAD_PARAM;
}
pTrees[0] = r->m_tree_0;
pTrees[1] = r->m_tree_1;
pTrees[2] = r->m_tree_2;
pCode_sizes[0] = r->m_code_size_0;
pCode_sizes[1] = r->m_code_size_1;
pCode_sizes[2] = r->m_code_size_2;
num_bits = r->m_num_bits;
bit_buf = r->m_bit_buf;
dist = r->m_dist;
counter = r->m_counter;
num_extra = r->m_num_extra;
dist_from_out_buf_start = r->m_dist_from_out_buf_start;
TINFL_CR_BEGIN
bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0;
r->m_z_adler32 = r->m_check_adler32 = 1;
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_GET_BYTE(1, r->m_zhdr0);
TINFL_GET_BYTE(2, r->m_zhdr1);
counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
if (counter)
{
TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED);
}
}
do
{
TINFL_GET_BITS(3, r->m_final, 3);
r->m_type = r->m_final >> 1;
if (r->m_type == 0)
{
TINFL_SKIP_BITS(5, num_bits & 7);
for (counter = 0; counter < 4; ++counter)
{
if (num_bits)
TINFL_GET_BITS(6, r->m_raw_header[counter], 8);
else
TINFL_GET_BYTE(7, r->m_raw_header[counter]);
}
if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8))))
{
TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED);
}
while ((counter) && (num_bits))
{
TINFL_GET_BITS(51, dist, 8);
while (pOut_buf_cur >= pOut_buf_end)
{
TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT);
}
*pOut_buf_cur++ = (mz_uint8)dist;
counter--;
}
while (counter)
{
size_t n;
while (pOut_buf_cur >= pOut_buf_end)
{
TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT);
}
while (pIn_buf_cur >= pIn_buf_end)
{
TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS);
}
n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n);
pIn_buf_cur += n;
pOut_buf_cur += n;
counter -= (mz_uint)n;
}
}
else if (r->m_type == 3)
{
TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
}
else
{
mz_uint code_len_hack; /* FIXME: workaround for miniz/#229 */
if (r->m_type == 1)
{
mz_uint8 *p = r->m_code_size_0;
mz_uint i;
r->m_table_sizes[0] = 288;
r->m_table_sizes[1] = 32;
TINFL_MEMSET(r->m_code_size_1, 5, 32);
for (i = 0; i <= 143; ++i)
*p++ = 8;
for (; i <= 255; ++i)
*p++ = 9;
for (; i <= 279; ++i)
*p++ = 7;
for (; i <= 287; ++i)
*p++ = 8;
}
else
{
for (counter = 0; counter < 3; counter++)
{
TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]);
r->m_table_sizes[counter] += s_min_table_sizes[counter];
}
MZ_CLEAR_ARR(r->m_code_size_2);
for (counter = 0; counter < r->m_table_sizes[2]; counter++)
{
mz_uint s;
TINFL_GET_BITS(14, s, 3);
r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s;
}
r->m_table_sizes[2] = 19;
}
for (; (int)r->m_type >= 0; r->m_type--)
{
int tree_next, tree_cur;
mz_int16 *pLookUp;
mz_int16 *pTree;
mz_uint8 *pCode_size;
mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16];
pLookUp = r->m_look_up[r->m_type];
pTree = pTrees[r->m_type];
pCode_size = pCode_sizes[r->m_type];
MZ_CLEAR_ARR(total_syms);
TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0]));
tinfl_clear_tree(r);
for (i = 0; i < r->m_table_sizes[r->m_type]; ++i)
total_syms[pCode_size[i]]++;
used_syms = 0, total = 0;
next_code[0] = next_code[1] = 0;
for (i = 1; i <= 15; ++i)
{
used_syms += total_syms[i];
next_code[i + 1] = (total = ((total + total_syms[i]) << 1));
}
if ((65536 != total) && (used_syms > 1))
{
TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
}
for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
{
mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index];
if (!code_size)
continue;
cur_code = next_code[code_size]++;
for (l = code_size; l > 0; l--, cur_code >>= 1)
rev_code = (rev_code << 1) | (cur_code & 1);
if (code_size <= TINFL_FAST_LOOKUP_BITS)
{
mz_int16 k = (mz_int16)((code_size << 9) | sym_index);
while (rev_code < TINFL_FAST_LOOKUP_SIZE)
{
pLookUp[rev_code] = k;
rev_code += (1 << code_size);
}
continue;
}
if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)]))
{
pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next;
tree_cur = tree_next;
tree_next -= 2;
}
rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
{
tree_cur -= ((rev_code >>= 1) & 1);
if (!pTree[-tree_cur - 1])
{
pTree[-tree_cur - 1] = (mz_int16)tree_next;
tree_cur = tree_next;
tree_next -= 2;
}
else
tree_cur = pTree[-tree_cur - 1];
}
tree_cur -= ((rev_code >>= 1) & 1);
pTree[-tree_cur - 1] = (mz_int16)sym_index;
}
if (r->m_type == 2)
{
for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);)
{
mz_uint s;
TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2);
if (dist < 16)
{
r->m_len_codes[counter++] = (mz_uint8)dist;
continue;
}
if ((dist == 16) && (!counter))
{
TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
}
num_extra = "\02\03\07"[dist - 16];
TINFL_GET_BITS(18, s, num_extra);
s += "\03\03\013"[dist - 16];
TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s);
counter += s;
}
if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
{
TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
}
TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]);
TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
}
}
for (;;)
{
mz_uint8 *pSrc;
for (;;)
{
if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
{
TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0);
/* FIXME: workaround for miniz/#229 */
if (!code_len_hack)
TINFL_CR_RETURN_FOREVER(101, TINFL_STATUS_FAILED);
if (counter >= 256)
break;
while (pOut_buf_cur >= pOut_buf_end)
{
TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT);
}
*pOut_buf_cur++ = (mz_uint8)counter;
}
else
{
int sym2;
mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
if (num_bits < 30)
{
bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits);
pIn_buf_cur += 4;
num_bits += 32;
}
#else
if (num_bits < 15)
{
bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
pIn_buf_cur += 2;
num_bits += 16;
}
#endif
if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS;
do
{
sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
} while (sym2 < 0);
}
/* FIXME: workaround for miniz/#229 */
if (!code_len)
TINFL_CR_RETURN_FOREVER(100, TINFL_STATUS_FAILED);
counter = sym2;
bit_buf >>= code_len;
num_bits -= code_len;
if (counter & 256)
break;
#if !TINFL_USE_64BIT_BITBUF
if (num_bits < 15)
{
bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);
pIn_buf_cur += 2;
num_bits += 16;
}
#endif
if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS;
do
{
sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
} while (sym2 < 0);
}
bit_buf >>= code_len;
num_bits -= code_len;
pOut_buf_cur[0] = (mz_uint8)counter;
if (sym2 & 256)
{
pOut_buf_cur++;
counter = sym2;
break;
}
pOut_buf_cur[1] = (mz_uint8)sym2;
pOut_buf_cur += 2;
}
}
if ((counter &= 511) == 256)
break;
num_extra = s_length_extra[counter - 257];
counter = s_length_base[counter - 257];
if (num_extra)
{
mz_uint extra_bits;
TINFL_GET_BITS(25, extra_bits, num_extra);
counter += extra_bits;
}
TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1);
num_extra = s_dist_extra[dist];
dist = s_dist_base[dist];
if (num_extra)
{
mz_uint extra_bits;
TINFL_GET_BITS(27, extra_bits, num_extra);
dist += extra_bits;
}
dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
{
TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
}
pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
{
while (counter--)
{
while (pOut_buf_cur >= pOut_buf_end)
{
TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT);
}
*pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
}
continue;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
else if ((counter >= 9) && (counter <= dist))
{
const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
do
{
#ifdef MINIZ_UNALIGNED_USE_MEMCPY
memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2);
#else
((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
#endif
pOut_buf_cur += 8;
} while ((pSrc += 8) < pSrc_end);
if ((counter &= 7) < 3)
{
if (counter)
{
pOut_buf_cur[0] = pSrc[0];
if (counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
continue;
}
}
#endif
while(counter>2)
{
pOut_buf_cur[0] = pSrc[0];
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur[2] = pSrc[2];
pOut_buf_cur += 3;
pSrc += 3;
counter -= 3;
}
if (counter > 0)
{
pOut_buf_cur[0] = pSrc[0];
if (counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
}
}
} while (!(r->m_final & 1));
/* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
/* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */
TINFL_SKIP_BITS(32, num_bits & 7);
while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
{
--pIn_buf_cur;
num_bits -= 8;
}
bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits);
MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
for (counter = 0; counter < 4; ++counter)
{
mz_uint s;
if (num_bits)
TINFL_GET_BITS(41, s, 8);
else
TINFL_GET_BYTE(42, s);
r->m_z_adler32 = (r->m_z_adler32 << 8) | s;
}
}
TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
TINFL_CR_FINISH
common_exit:
/* As long as we aren't telling the caller that we NEED more input to make forward progress: */
/* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */
/* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */
if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS))
{
while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))
{
--pIn_buf_cur;
num_bits -= 8;
}
}
r->m_num_bits = num_bits;
r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits);
r->m_dist = dist;
r->m_counter = counter;
r->m_num_extra = num_extra;
r->m_dist_from_out_buf_start = dist_from_out_buf_start;
*pIn_buf_size = pIn_buf_cur - pIn_buf_next;
*pOut_buf_size = pOut_buf_cur - pOut_buf_next;
if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
{
const mz_uint8 *ptr = pOut_buf_next;
size_t buf_len = *pOut_buf_size;
mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16;
size_t block_len = buf_len % 5552;
while (buf_len)
{
for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
{
s1 += ptr[0], s2 += s1;
s1 += ptr[1], s2 += s1;
s1 += ptr[2], s2 += s1;
s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1;
s1 += ptr[5], s2 += s1;
s1 += ptr[6], s2 += s1;
s1 += ptr[7], s2 += s1;
}
for (; i < block_len; ++i)
s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U;
buf_len -= block_len;
block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1;
if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))
status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
}
/* Higher level helper functions. */
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
tinfl_decompressor decomp;
void *pBuf = NULL, *pNew_buf;
size_t src_buf_ofs = 0, out_buf_capacity = 0;
*pOut_len = 0;
tinfl_init(&decomp);
for (;;)
{
size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size,
(flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
{
MZ_FREE(pBuf);
*pOut_len = 0;
return NULL;
}
src_buf_ofs += src_buf_size;
*pOut_len += dst_buf_size;
if (status == TINFL_STATUS_DONE)
break;
new_out_buf_capacity = out_buf_capacity * 2;
if (new_out_buf_capacity < 128)
new_out_buf_capacity = 128;
pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
if (!pNew_buf)
{
MZ_FREE(pBuf);
*pOut_len = 0;
return NULL;
}
pBuf = pNew_buf;
out_buf_capacity = new_out_buf_capacity;
}
return pBuf;
}
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
tinfl_decompressor decomp;
tinfl_status status;
tinfl_init(&decomp);
status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
}
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
int result = 0;
tinfl_decompressor decomp;
mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE);
size_t in_buf_ofs = 0, dict_ofs = 0;
if (!pDict)
return TINFL_STATUS_FAILED;
memset(pDict,0,TINFL_LZ_DICT_SIZE);
tinfl_init(&decomp);
for (;;)
{
size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
(flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
in_buf_ofs += in_buf_size;
if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
break;
if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
{
result = (status == TINFL_STATUS_DONE);
break;
}
dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
}
MZ_FREE(pDict);
*pIn_buf_size = in_buf_ofs;
return result;
}
#ifndef MINIZ_NO_MALLOC
tinfl_decompressor *tinfl_decompressor_alloc(void)
{
tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor));
if (pDecomp)
tinfl_init(pDecomp);
return pDecomp;
}
void tinfl_decompressor_free(tinfl_decompressor *pDecomp)
{
MZ_FREE(pDecomp);
}
#endif
#ifdef __cplusplus
}
#endif
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,249 @@
#ifndef MINIZ_ZIP_H
#define MINIZ_ZIP_H
#include "miniz.h"
#if 1 /* LIBXMP-SPECIFIC : */
/* change namespace from mz_ to libxmp_ for public functions: */
#define mz_zip_reader_init libxmp_zip_reader_init
#define mz_zip_reader_end libxmp_zip_reader_end
#define mz_zip_get_error_string libxmp_zip_get_error_string
#define mz_zip_reader_is_file_a_directory libxmp_zip_reader_is_file_a_directory
#define mz_zip_reader_is_file_encrypted libxmp_zip_reader_is_file_encrypted
#define mz_zip_reader_is_file_supported libxmp_zip_reader_is_file_supported
#define mz_zip_reader_get_filename libxmp_zip_reader_get_filename
#define mz_zip_reader_locate_file libxmp_zip_reader_locate_file
#define mz_zip_reader_locate_file_v2 libxmp_zip_reader_locate_file_v2
#define mz_zip_reader_file_stat libxmp_zip_reader_file_stat
#define mz_zip_reader_extract_to_mem libxmp_zip_reader_extract_to_mem
#define mz_zip_reader_extract_to_mem_no_alloc libxmp_zip_reader_extract_to_mem_no_alloc
#define mz_zip_reader_extract_to_heap libxmp_zip_reader_extract_to_heap
#define mz_zip_reader_extract_to_callback libxmp_zip_reader_extract_to_callback
#endif /* LIBXMP-SPECIFIC */
/* ------------------- ZIP archive reading/writing */
#ifndef MINIZ_NO_ARCHIVE_APIS
#ifdef __cplusplus
extern "C" {
#endif
enum
{
/* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */
MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024,
MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512,
MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512
};
typedef struct
{
/* Central directory file index. */
mz_uint32 m_file_index;
/* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */
mz_uint64 m_central_dir_ofs;
/* These fields are copied directly from the zip's central dir. */
mz_uint16 m_version_made_by;
mz_uint16 m_version_needed;
mz_uint16 m_bit_flag;
mz_uint16 m_method;
/* CRC-32 of uncompressed data. */
mz_uint32 m_crc32;
/* File's compressed size. */
mz_uint64 m_comp_size;
/* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */
mz_uint64 m_uncomp_size;
/* Zip internal and external file attributes. */
mz_uint16 m_internal_attr;
mz_uint32 m_external_attr;
/* Entry's local header file offset in bytes. */
mz_uint64 m_local_header_ofs;
/* Size of comment in bytes. */
mz_uint32 m_comment_size;
/* MZ_TRUE if the entry appears to be a directory. */
mz_bool m_is_directory;
/* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */
mz_bool m_is_encrypted;
/* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */
mz_bool m_is_supported;
/* Filename. If string ends in '/' it's a subdirectory entry. */
/* Guaranteed to be zero terminated, may be truncated to fit. */
char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
/* Comment field. */
/* Guaranteed to be zero terminated, may be truncated to fit. */
char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
} mz_zip_archive_file_stat;
typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);
struct mz_zip_internal_state_tag;
typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
typedef enum {
MZ_ZIP_MODE_INVALID = 0,
MZ_ZIP_MODE_READING = 1,
MZ_ZIP_MODE_WRITING = 2,
MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
} mz_zip_mode;
typedef enum {
MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,
MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,
MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,
MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800,
MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */
MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */
MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */
MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000,
MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000,
/*After adding a compressed file, seek back
to local file header and set the correct sizes*/
MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000
} mz_zip_flags;
typedef enum {
MZ_ZIP_TYPE_INVALID = 0,
MZ_ZIP_TYPE_USER,
MZ_ZIP_TYPE_MEMORY,
MZ_ZIP_TYPE_HEAP,
MZ_ZIP_TYPE_FILE,
MZ_ZIP_TYPE_CFILE,
MZ_ZIP_TOTAL_TYPES
} mz_zip_type;
/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */
typedef enum {
MZ_ZIP_NO_ERROR = 0,
MZ_ZIP_UNDEFINED_ERROR,
MZ_ZIP_TOO_MANY_FILES,
MZ_ZIP_FILE_TOO_LARGE,
MZ_ZIP_UNSUPPORTED_METHOD,
MZ_ZIP_UNSUPPORTED_ENCRYPTION,
MZ_ZIP_UNSUPPORTED_FEATURE,
MZ_ZIP_FAILED_FINDING_CENTRAL_DIR,
MZ_ZIP_NOT_AN_ARCHIVE,
MZ_ZIP_INVALID_HEADER_OR_CORRUPTED,
MZ_ZIP_UNSUPPORTED_MULTIDISK,
MZ_ZIP_DECOMPRESSION_FAILED,
MZ_ZIP_COMPRESSION_FAILED,
MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE,
MZ_ZIP_CRC_CHECK_FAILED,
MZ_ZIP_UNSUPPORTED_CDIR_SIZE,
MZ_ZIP_ALLOC_FAILED,
MZ_ZIP_FILE_OPEN_FAILED,
MZ_ZIP_FILE_CREATE_FAILED,
MZ_ZIP_FILE_WRITE_FAILED,
MZ_ZIP_FILE_READ_FAILED,
MZ_ZIP_FILE_CLOSE_FAILED,
MZ_ZIP_FILE_SEEK_FAILED,
MZ_ZIP_FILE_STAT_FAILED,
MZ_ZIP_INVALID_PARAMETER,
MZ_ZIP_INVALID_FILENAME,
MZ_ZIP_BUF_TOO_SMALL,
MZ_ZIP_INTERNAL_ERROR,
MZ_ZIP_FILE_NOT_FOUND,
MZ_ZIP_ARCHIVE_TOO_LARGE,
MZ_ZIP_VALIDATION_FAILED,
MZ_ZIP_WRITE_CALLBACK_FAILED,
MZ_ZIP_TOTAL_ERRORS
} mz_zip_error;
typedef struct
{
mz_uint64 m_archive_size;
mz_uint64 m_central_directory_file_ofs;
/* We only support up to UINT32_MAX files in zip64 mode. */
mz_uint32 m_total_files;
mz_zip_mode m_zip_mode;
mz_zip_type m_zip_type;
mz_zip_error m_last_error;
mz_uint64 m_file_offset_alignment;
mz_alloc_func m_pAlloc;
mz_free_func m_pFree;
mz_realloc_func m_pRealloc;
void *m_pAlloc_opaque;
mz_file_read_func m_pRead;
mz_file_write_func m_pWrite;
mz_file_needs_keepalive m_pNeeds_keepalive;
void *m_pIO_opaque;
mz_zip_internal_state *m_pState;
} mz_zip_archive;
/* -------- ZIP reading */
/* Inits a ZIP archive reader. */
/* These functions read and validate the archive's central directory. */
MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags);
/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */
MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
/* -------- ZIP reading or writing */
#ifdef DEBUG /* libxmp uses this only in debug mode */
MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err);
#endif
/* MZ_TRUE if the archive file entry is a directory entry. */
MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
/* MZ_TRUE if the file is encrypted/strong encrypted. */
MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */
MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index);
/* Retrieves the filename of an archive file entry. */
/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */
MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
/* Attempts to locates a file in the archive's central directory. */
/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */
/* Returns -1 if the file cannot be found. */
MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index);
/* Returns detailed information about an archive file entry. */
MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
/* Extracts a archive file to a memory buffer. */
MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
/* Extracts a archive file to a dynamically allocated heap buffer. */
/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */
/* Returns NULL and sets the last error on failure. */
MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
/* Extracts a archive file using a callback function to output the file's data. */
MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
#ifdef __cplusplus
}
#endif
#endif /* MINIZ_NO_ARCHIVE_APIS */
#endif /* MINIZ_ZIP_H */

View File

@ -0,0 +1,467 @@
/*
* Based on the public domain version by Olivier Lapicque
* Rewritten for libxmp by Claudio Matsuoka
*
* Copyright (C) 2012 Claudio Matsuoka
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#include "depacker.h"
#define MMCMP_COMP 0x0001
#define MMCMP_DELTA 0x0002
#define MMCMP_16BIT 0x0004
#define MMCMP_STEREO 0x0100
#define MMCMP_ABS16 0x0200
#define MMCMP_ENDIAN 0x0400
struct header {
int version;
int nblocks;
int filesize;
int blktable;
int glb_comp;
int fmt_comp;
};
struct block {
int unpk_size;
int pk_size;
int xor_chk;
int sub_blk;
int flags;
int tt_entries;
int num_bits;
};
struct sub_block {
int unpk_pos;
int unpk_size;
};
static const uint32 cmd_8bits[8] = {
0x01, 0x03, 0x07, 0x0f, 0x1e, 0x3c, 0x78, 0xf8
};
static const uint32 fetch_8bit[8] = {
3, 3, 3, 3, 2, 1, 0, 0
};
static const uint32 cmd_16bit[16] = {
0x0001, 0x0003, 0x0007, 0x000f, 0x001e, 0x003c, 0x0078, 0x00f0,
0x01f0, 0x03f0, 0x07f0, 0x0ff0, 0x1ff0, 0x3ff0, 0x7ff0, 0xfff0
};
static const uint32 fetch_16bit[16] = {
4, 4, 4, 4, 3, 2, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
struct bit_buffer {
uint32 count;
uint32 buffer;
};
static uint32 get_bits(HIO_HANDLE *f, int n, struct bit_buffer *bb)
{
uint32 bits;
if (n == 0) {
return 0;
}
while (bb->count < 24) {
bb->buffer |= hio_read8(f) << bb->count;
bb->count += 8;
}
bits = bb->buffer & ((1 << n) - 1);
bb->buffer >>= n;
bb->count -= n;
return bits;
}
struct mem_buffer {
uint8 *buf;
size_t size;
size_t pos;
};
static int mem_seek(struct mem_buffer *out, int pos_set)
{
if (pos_set >= out->size)
return -1;
out->pos = pos_set;
return 0;
}
static int mem_write8(uint8 value, struct mem_buffer *out)
{
if (out->pos >= out->size)
return -1;
out->buf[out->pos++] = value;
return 0;
}
static int mem_write16l(uint16 value, struct mem_buffer *out)
{
/* Some MMCMP blocks seem to rely on writing half words. This
* theoretically could occur at the end of the file, so write each
* byte separately. */
if (mem_write8(value & 0xff, out) ||
mem_write8(value >> 8, out))
return -1;
return 0;
}
static int block_copy(struct block *block, struct sub_block *sub,
HIO_HANDLE *in, struct mem_buffer *out)
{
int i;
for (i = 0; i < block->sub_blk; i++, sub++) {
if (sub->unpk_pos >= out->size ||
sub->unpk_size > out->size - sub->unpk_pos)
return -1;
if (hio_read(out->buf + sub->unpk_pos, 1, sub->unpk_size, in) < sub->unpk_size)
return -1;
}
return 0;
}
static int block_unpack_16bit(struct block *block, struct sub_block *sub,
HIO_HANDLE *in, struct mem_buffer *out)
{
struct bit_buffer bb;
uint32 pos = 0;
uint32 numbits = block->num_bits;
uint32 j, oldval = 0;
bb.count = 0;
bb.buffer = 0;
if (mem_seek(out, sub->unpk_pos) < 0) {
return -1;
}
if (hio_seek(in, block->tt_entries, SEEK_CUR) < 0) {
return -1;
}
for (j = 0; j < block->sub_blk; ) {
uint32 size = sub[j].unpk_size;
uint32 newval = 0x10000;
uint32 d = get_bits(in, numbits + 1, &bb);
if (d >= cmd_16bit[numbits]) {
uint32 fetch = fetch_16bit[numbits];
uint32 newbits = get_bits(in, fetch, &bb) +
((d - cmd_16bit[numbits]) << fetch);
if (newbits != numbits) {
numbits = newbits & 0x0f;
} else {
if ((d = get_bits(in, 4, &bb)) == 0x0f) {
if (get_bits(in, 1, &bb))
break;
newval = 0xffff;
} else {
newval = 0xfff0 + d;
}
}
} else {
newval = d;
}
if (newval < 0x10000) {
if (newval & 1) {
newval = (uint32)(-(int32)((newval + 1) >> 1));
} else {
newval = (uint32)(newval >> 1);
}
if (block->flags & MMCMP_DELTA) {
newval += oldval;
oldval = newval;
} else if (!(block->flags & MMCMP_ABS16)) {
newval ^= 0x8000;
}
pos += 2;
mem_write16l((uint16)newval, out);
}
if (pos >= size) {
if (++j >= block->sub_blk)
break;
pos = 0;
if (mem_seek(out, sub[j].unpk_pos) < 0) {
return -1;
}
}
}
return 0;
}
static int block_unpack_8bit(struct block *block, struct sub_block *sub,
HIO_HANDLE *in, struct mem_buffer *out)
{
struct bit_buffer bb;
uint32 pos = 0;
uint32 numbits = block->num_bits;
uint32 j, oldval = 0;
uint8 ptable[0x100];
long seekpos = hio_tell(in) + block->tt_entries;
/* The way the original libmodplug depacker is written allows values
* to be read from the compressed data. It's impossible to tell if this
* was intentional or yet another bug. Nothing seems to rely on it. */
memset(ptable, 0, sizeof(ptable));
if (hio_read(ptable, 1, 0x100, in) < block->tt_entries) {
return -1;
}
bb.count = 0;
bb.buffer = 0;
if (mem_seek(out, sub->unpk_pos) < 0) {
return -1;
}
if (hio_seek(in, seekpos, SEEK_SET) < 0) {
return -1;
}
for (j = 0; j < block->sub_blk; ) {
uint32 size = sub[j].unpk_size;
uint32 newval = 0x100;
uint32 d = get_bits(in, numbits+1, &bb);
if (d >= cmd_8bits[numbits]) {
uint32 fetch = fetch_8bit[numbits];
uint32 newbits = get_bits(in, fetch, &bb) +
((d - cmd_8bits[numbits]) << fetch);
if (newbits != numbits) {
numbits = newbits & 0x07;
} else {
if ((d = get_bits(in, 3, &bb)) == 7) {
if (get_bits(in, 1, &bb))
break;
newval = 0xff;
} else {
newval = 0xf8 + d;
}
}
} else {
newval = d;
}
if (newval < 0x100) {
int n = ptable[newval];
if (block->flags & MMCMP_DELTA) {
n += oldval;
oldval = n;
}
pos++;
mem_write8((uint8)n, out);
}
if (pos >= size) {
if (++j >= block->sub_blk)
break;
pos = 0;
if (mem_seek(out, sub[j].unpk_pos) < 0) {
return -1;
}
}
}
return 0;
}
static int test_mmcmp(unsigned char *b)
{
return memcmp(b, "ziRCONia", 8) == 0;
}
static int decrunch_mmcmp(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
struct header h;
struct mem_buffer outbuf;
uint32 *table;
uint32 i, j;
/* Read file header */
if (hio_read32l(in) != 0x4352697A) /* ziRC */
goto err;
if (hio_read32l(in) != 0x61694e4f) /* ONia */
goto err;
if (hio_read16l(in) != 14) /* header size */
goto err;
/* Read header */
h.version = hio_read16l(in);
if (hio_error(in) != 0) goto err;
h.nblocks = hio_read16l(in);
if (hio_error(in) != 0) goto err;
h.filesize = hio_read32l(in);
if (hio_error(in) != 0) goto err;
h.blktable = hio_read32l(in);
if (hio_error(in) != 0) goto err;
h.glb_comp = hio_read8(in);
if (hio_error(in) != 0) goto err;
h.fmt_comp = hio_read8(in);
if (hio_error(in) != 0) goto err;
if (h.nblocks == 0 || h.filesize < 16 || h.filesize > LIBXMP_DEPACK_LIMIT)
goto err;
/* Block table */
if (hio_seek(in, h.blktable, SEEK_SET) < 0) {
goto err;
}
table = (uint32 *) malloc(h.nblocks * 4);
if (table == NULL) {
goto err;
}
outbuf.buf = (uint8 *) calloc(1, h.filesize);
if (outbuf.buf == NULL) {
goto err2;
}
outbuf.pos = 0;
outbuf.size = h.filesize;
for (i = 0; i < h.nblocks; i++) {
table[i] = hio_read32l(in);
if (hio_error(in) != 0) goto err2;
}
for (i = 0; i < h.nblocks; i++) {
struct block block;
struct sub_block *sub_block;
uint8 buf[20];
if (hio_seek(in, table[i], SEEK_SET) < 0) {
goto err2;
}
if (hio_read(buf, 1, 20, in) != 20) {
goto err2;
}
block.unpk_size = readmem32l(buf);
block.pk_size = readmem32l(buf + 4);
block.xor_chk = readmem32l(buf + 8);
block.sub_blk = readmem16l(buf + 12);
block.flags = readmem16l(buf + 14);
block.tt_entries = readmem16l(buf + 16);
block.num_bits = readmem16l(buf + 18);
/* Sanity check */
if (block.unpk_size <= 0 || block.pk_size <= 0)
goto err2;
if (block.tt_entries < 0 || block.pk_size <= block.tt_entries)
goto err2;
if (block.sub_blk <= 0)
goto err2;
if (block.flags & MMCMP_COMP) {
if (block.flags & MMCMP_16BIT) {
if (block.num_bits >= 16) {
goto err2;
}
} else {
if (block.num_bits >= 8) {
goto err2;
}
}
}
sub_block = (struct sub_block *) malloc(block.sub_blk * sizeof (struct sub_block));
if (sub_block == NULL)
goto err2;
for (j = 0; j < block.sub_blk; j++) {
if (hio_read(buf, 1, 8, in) != 8) {
free(sub_block);
goto err2;
}
sub_block[j].unpk_pos = readmem32l(buf);
sub_block[j].unpk_size = readmem32l(buf + 4);
/* Sanity check */
if (sub_block[j].unpk_pos < 0 ||
sub_block[j].unpk_size < 0) {
free(sub_block);
goto err2;
}
}
if (~block.flags & MMCMP_COMP) {
/* Data is not packed */
if (block_copy(&block, sub_block, in, &outbuf) < 0) {
free(sub_block);
goto err2;
}
} else if (block.flags & MMCMP_16BIT) {
/* Data is 16-bit packed */
if (block_unpack_16bit(&block, sub_block, in, &outbuf) < 0) {
free(sub_block);
goto err2;
}
} else {
/* Data is 8-bit packed */
if (block_unpack_8bit(&block, sub_block, in, &outbuf) < 0) {
free(sub_block);
goto err2;
}
}
free(sub_block);
}
*out = outbuf.buf;
*outlen = h.filesize;
free(table);
return 0;
err2:
free(outbuf.buf);
free(table);
err:
return -1;
}
struct depacker libxmp_depacker_mmcmp = {
test_mmcmp,
NULL,
decrunch_mmcmp
};

View File

@ -0,0 +1,82 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#include "depacker.h"
#include "miniz.h"
static int test_muse(unsigned char *b)
{
if (memcmp(b, "MUSE", 4) == 0) {
uint32 r = readmem32b(b + 4);
/* MOD2J2B uses 0xdeadbabe */
if (r == 0xdeadbeaf || r == 0xdeadbabe) {
return 1;
}
}
return 0;
}
static int decrunch_muse(HIO_HANDLE *f, void **out, long inlen, long *outlen)
{
size_t in_buf_size = inlen - 24;
void *pCmp_data, *pOut_buf;
size_t pOut_len;
if (hio_seek(f, 24, SEEK_SET) < 0) {
D_(D_CRIT "hio_seek() failed");
return -1;
}
pCmp_data = (uint8 *)malloc(in_buf_size);
if (!pCmp_data) {
D_(D_CRIT "Out of memory");
return -1;
}
if (hio_read(pCmp_data, 1, in_buf_size, f) != in_buf_size) {
D_(D_CRIT "Failed reading input file");
free(pCmp_data);
return -1;
}
pOut_buf = tinfl_decompress_mem_to_heap(pCmp_data, in_buf_size, &pOut_len, TINFL_FLAG_PARSE_ZLIB_HEADER);
if (!pOut_buf) {
D_(D_CRIT "tinfl_decompress_mem_to_heap() failed");
free(pCmp_data);
return -1;
}
free(pCmp_data);
*out = pOut_buf;
*outlen = pOut_len;
return 0;
}
struct depacker libxmp_depacker_muse = {
test_muse,
NULL,
decrunch_muse
};

View File

@ -0,0 +1,236 @@
/* PowerPacker decrunch
* Based on code by Stuart Caie <kyzer@4u.net>
* This software is in the Public Domain
*/
/* Code from Heikki Orsila's amigadepack 0.02 to replace previous
* PowerPack depacker with license issues.
*
* Modified for xmp by Claudio Matsuoka, 08/2007
* - merged mld's checks from the old depack sources. Original credits:
* - corrupt file and data detection
* (thanks to Don Adan and Dirk Stoecker for help and infos)
* - implemeted "efficiency" checks
* - further detection based on code by Georg Hoermann
*
* Modified for xmp by Claudio Matsuoka, 05/2013
* - decryption code removed
*/
#include "../common.h"
#include "depacker.h"
/* #define val(p) ((p)[0]<<16 | (p)[1] << 8 | (p)[2]) */
#define PP_READ_BITS(nbits, var) do { \
bit_cnt = (nbits); \
while (bits_left < bit_cnt) { \
if (buf_src < src) return 0; /* out of source bits */ \
bit_buffer |= (*--buf_src << bits_left); \
bits_left += 8; \
} \
(var) = 0; \
bits_left -= bit_cnt; \
while (bit_cnt--) { \
(var) = ((var) << 1) | (bit_buffer & 1); \
bit_buffer >>= 1; \
} \
} while(0)
#define PP_BYTE_OUT(byte) do { \
if (out <= dest) return 0; /* output overflow */ \
*--out = (byte); \
written++; \
} while (0)
static int ppDecrunch(uint8 *src, uint8 *dest, uint8 *offset_lens,
uint32 src_len, uint32 dest_len, uint8 skip_bits)
{
uint8 *buf_src, *out, *dest_end, bits_left = 0, bit_cnt;
uint32 bit_buffer = 0, x, todo, offbits, offset, written=0;
if (src == NULL || dest == NULL || offset_lens == NULL) return 0;
if (skip_bits > 32) return 0;
/* set up input and output pointers */
buf_src = src + src_len;
out = dest_end = dest + dest_len;
/* skip the first few bits */
PP_READ_BITS(skip_bits, x);
/* while there are input bits left */
while (written < dest_len) {
PP_READ_BITS(1, x);
if (x == 0) {
/* 1bit==0: literal, then match. 1bit==1: just match */
todo = 1; do { PP_READ_BITS(2, x); todo += x; } while (x == 3);
while (todo--) { PP_READ_BITS(8, x); PP_BYTE_OUT(x); }
/* should we end decoding on a literal, break out of the main loop */
if (written == dest_len) break;
}
/* match: read 2 bits for initial offset bitlength / match length */
PP_READ_BITS(2, x);
offbits = offset_lens[x];
todo = x+2;
if (x == 3) {
PP_READ_BITS(1, x);
if (x==0) offbits = 7;
PP_READ_BITS(offbits, offset);
do { PP_READ_BITS(3, x); todo += x; } while (x == 7);
}
else {
PP_READ_BITS(offbits, offset);
}
if ((out + offset) >= dest_end) return 0; /* match overflow */
while (todo--) { x = out[offset]; PP_BYTE_OUT(x); }
}
/* all output bytes written without error */
return 1;
/* return (src == buf_src) ? 1 : 0; */
}
static int ppdepack(uint8 *data, size_t len, void **output, long *outlen)
{
/* PP FORMAT:
* 1 longword identifier 'PP20' or 'PX20'
* [1 word checksum (if 'PX20') $ssss]
* 1 longword efficiency $eeeeeeee
* X longwords crunched file $cccccccc,$cccccccc,...
* 1 longword decrunch info 'decrlen' << 8 | '8 bits other info'
*/
/* uint8 *crypted; */
if (len < 16) {
/*fprintf(stderr, "File is too short to be a PP file (%u bytes)\n", len);*/
return -1;
}
if (data[0]=='P' && data[1]=='P' && data[2]=='2' && data[3]=='0') {
if (len & 0x03) {
/*fprintf(stderr, "File length is not a multiple of 4\n");*/
return -1;
}
/*crypted = 0;*/
}
#if 0
else if (data[0]=='P' && data[1]=='X' && data[2]=='2' && data[3]=='0') {
if ((len-2) & 0x03) {
/*fprintf(stderr, "(file length - 2) is not a multiple of 4\n");*/
return -1;
}
crypted = 1;
}
#endif
else {
/*fprintf(stderr, "File does not have the PP signature\n");*/
return -1;
}
*outlen = readmem24b(data + len - 4);
/* fprintf(stderr, "decrunched length = %u bytes\n", *outlen); */
*output = (uint8 *) malloc(*outlen);
if (*output == NULL) {
/*fprintf(stderr, "out of memory!\n");*/
return -1;
}
/* if (crypted == 0) { */
/*fprintf(stderr, "not encrypted, decrunching anyway\n"); */
if (ppDecrunch(&data[8], (uint8 *) *output, &data[4], len-12, *outlen, data[len-1])) {
/* fprintf(stderr, "Decrunch successful! "); */
return 0;
}
/**/
free(*output);
*output = NULL;
*outlen = 0;
return -1;
}
static int test_pp(unsigned char *b)
{
return memcmp(b, "PP20", 4) == 0;
}
static int decrunch_pp(HIO_HANDLE *f, void **out, long inlen, long *outlen)
{
uint8 *packed;
int unplen;
/* Amiga longwords are only on even addresses.
* The pp20 data format has the length stored in a longword
* after the packed data, so I guess a file that is not even
* is probl not a valid pp20 file. Thanks for Don Adan for
* reminding me on this! - mld
*/
if ((inlen != (inlen / 2) * 2)) {
/*fprintf(stderr, "filesize not even\n");*/
goto err;
}
packed = (uint8 *) malloc(inlen);
if (packed == NULL) {
/*fprintf(stderr, "can't allocate memory for packed data\n");*/
goto err;
}
if (hio_read(packed, 1, inlen, f) != inlen) {
goto err1;
}
/* Hmmh... original pp20 only support efficiency from 9 9 9 9 up to 9 10 12 13, afaik
* but the xfd detection code says this... *sigh*
*
* move.l 4(a0),d0
* cmp.b #9,d0
* blo.b .Exit
* and.l #$f0f0f0f0,d0
* bne.s .Exit
*/
if (((packed[4] < 9) || (packed[5] < 9) || (packed[6] < 9) || (packed[7] < 9))) {
/*fprintf(stderr, "invalid efficiency\n");*/
goto err1;
}
if (((readmem24b(packed +4) * 256 + packed[7]) & 0xf0f0f0f0) != 0 ) {
/*fprintf(stderr, "invalid efficiency(?)\n");*/
goto err1;
}
unplen = readmem24b(packed + inlen - 4);
if (!unplen) {
/*fprintf(stderr, "not a powerpacked file\n");*/
goto err1;
}
if (ppdepack (packed, inlen, out, outlen) == -1) {
/*fprintf(stderr, "error while decrunching data...");*/
goto err1;
}
free (packed);
return 0;
err1:
free(packed);
err:
return -1;
}
struct depacker libxmp_depacker_pp = {
test_pp,
NULL,
decrunch_pp
};

View File

@ -0,0 +1,237 @@
/*
* pt_popen/pt_pclose functions
* Written somewhere in the 90s by Kurt Keller
* Comments translated by Steve Donovan
* Modified for use in xmp by Mirko Buffoni and Claudio Matsuoka
* Reentrancy patch added for xmp by Alice Rowan
*/
/*
* This piece of code is in the public domain. I do not claim any rights
* on it. Do whatever you want to do with it and I hope it will be still
* useful. -- Kurt Keller, Aug 2013
*/
#ifdef _WIN32
#include "ptpopen.h"
/*
> Hello,
> I am currently porting a UNIX program to WINDOWS.
> Most difficulty time I have is to find the popen()-like function under
> WINDOWS. Any help and hints would be greatly appreciated.
>
> Thanks in advance
> Tianlin Wang
This is what I use instead of popen(): (Sorry for the comments in german ;-))
It is not an **EXACT** replacement for popen() but it is OK for me.
Kurt.
---------------------------------------------------
Tel.: (49)7150/393394 Parity Software GmbH
Fax.: (49)7150/393351 Stuttgarter Strasse 42/3
E-Mail: kk@parity-soft.de D-71701 Schwieberdingen
Web: www.parity-soft.de
---------------------------------------------------
*/
/*----------------------------------------------------------------------------
Globals for the Routines pt_popen() / pt_pclose()
----------------------------------------------------------------------------*/
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <errno.h>
#if defined(_MSC_VER) && (_MSC_VER < 1300)
typedef LONG LONG_PTR;
#endif
struct pt_popen_data
{
HANDLE pipein[2];
HANDLE pipeout[2];
HANDLE pipeerr[2];
char popenmode;
BOOL is_open;
};
static struct pt_popen_data static_data;
static int my_pipe(HANDLE *readwrite)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa); /* Length in bytes */
sa.bInheritHandle = 1; /* the child must inherit these handles */
sa.lpSecurityDescriptor = NULL;
if (! CreatePipe (&readwrite[0],&readwrite[1],&sa,1 << 13))
{
errno = EMFILE;
return -1;
}
return 0;
}
/*----------------------------------------------------------------------------
Replacement for 'popen()' under WIN32.
NOTE: if cmd contains '2>&1', we connect the standard error file handle
to the standard output file handle.
NOTE: a pointer to allocate a pt_popen_data struct to may be provided. If
this pointer is NULL, a static (non-reentrant) struct will be used instead.
----------------------------------------------------------------------------*/
FILE * pt_popen(const char *cmd, const char *mode, struct pt_popen_data **data)
{
FILE *fptr = (FILE *)0;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
int success, umlenkung;
struct pt_popen_data *my_data = &static_data;
BOOL user_data = FALSE;
if (data) {
my_data = (struct pt_popen_data *) malloc(sizeof(struct pt_popen_data));
if (!my_data)
return NULL;
user_data = TRUE;
} else if (static_data.is_open) {
return NULL;
}
my_data->pipein[0] = INVALID_HANDLE_VALUE;
my_data->pipein[1] = INVALID_HANDLE_VALUE;
my_data->pipeout[0] = INVALID_HANDLE_VALUE;
my_data->pipeout[1] = INVALID_HANDLE_VALUE;
my_data->pipeerr[0] = INVALID_HANDLE_VALUE;
my_data->pipeerr[1] = INVALID_HANDLE_VALUE;
my_data->is_open = TRUE;
if (!mode || !*mode)
goto finito;
my_data->popenmode = *mode;
if (my_data->popenmode != 'r' && my_data->popenmode != 'w')
goto finito;
/*
* Shall we redirect stderr to stdout ? */
umlenkung = strstr("2>&1",(char *)cmd) != 0;
/*
* Create the Pipes... */
if (my_pipe(my_data->pipein) == -1 ||
my_pipe(my_data->pipeout) == -1)
goto finito;
if (!umlenkung && my_pipe(my_data->pipeerr) == -1)
goto finito;
/*
* Now create the child process */
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdInput = my_data->pipein[0];
siStartInfo.hStdOutput = my_data->pipeout[1];
if (umlenkung)
siStartInfo.hStdError = my_data->pipeout[1];
else
siStartInfo.hStdError = my_data->pipeerr[1];
siStartInfo.dwFlags = STARTF_USESTDHANDLES;
success = CreateProcess(NULL,
(LPTSTR)cmd, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
DETACHED_PROCESS, // creation flags: without window (?)
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (!success)
goto finito;
/*
* These handles listen to the child process */
CloseHandle(my_data->pipein[0]); my_data->pipein[0] = INVALID_HANDLE_VALUE;
CloseHandle(my_data->pipeout[1]); my_data->pipeout[1] = INVALID_HANDLE_VALUE;
CloseHandle(my_data->pipeerr[1]); my_data->pipeerr[1] = INVALID_HANDLE_VALUE;
if (my_data->popenmode == 'r')
fptr = _fdopen(_open_osfhandle((LONG_PTR)my_data->pipeout[0],_O_BINARY),"r");
else
fptr = _fdopen(_open_osfhandle((LONG_PTR)my_data->pipein[1],_O_BINARY),"w");
finito:
if (!fptr)
{
if (my_data->pipein[0] != INVALID_HANDLE_VALUE)
CloseHandle(my_data->pipein[0]);
if (my_data->pipein[1] != INVALID_HANDLE_VALUE)
CloseHandle(my_data->pipein[1]);
if (my_data->pipeout[0] != INVALID_HANDLE_VALUE)
CloseHandle(my_data->pipeout[0]);
if (my_data->pipeout[1] != INVALID_HANDLE_VALUE)
CloseHandle(my_data->pipeout[1]);
if (my_data->pipeerr[0] != INVALID_HANDLE_VALUE)
CloseHandle(my_data->pipeerr[0]);
if (my_data->pipeerr[1] != INVALID_HANDLE_VALUE)
CloseHandle(my_data->pipeerr[1]);
my_data->is_open = FALSE;
if (user_data)
{
free(my_data);
my_data = NULL;
}
}
if (user_data)
*data = my_data;
return fptr;
}
/*----------------------------------------------------------------------------
Replacement for 'pclose()' under WIN32
----------------------------------------------------------------------------*/
int pt_pclose(FILE *fle, struct pt_popen_data **data)
{
struct pt_popen_data *my_data = &static_data;
BOOL free_data = FALSE;
if (data)
{
if (!*data)
return -1;
my_data = *data;
free_data = TRUE;
}
if (fle && my_data->is_open)
{
(void)fclose(fle);
CloseHandle(my_data->pipeerr[0]);
if (my_data->popenmode == 'r')
CloseHandle(my_data->pipein[1]);
else
CloseHandle(my_data->pipeout[0]);
if (free_data)
{
free(my_data);
*data = NULL;
}
return 0;
}
return -1;
}
#endif /* WIN32 */

View File

@ -0,0 +1,28 @@
#ifndef _PT_POPEN_H
#define _PT_POPEN_H 1
#ifdef _WIN32
#include <stdio.h>
#undef popen
#define popen(cmd, mode) pt_popen(cmd, mode, NULL)
#undef pclose
#define pclose(f) pt_pclose(f, NULL)
#ifdef __cplusplus
extern "C" {
#endif
struct pt_popen_data;
FILE * pt_popen(const char *cmd, const char *mode, struct pt_popen_data **data);
int pt_pclose(FILE *fle, struct pt_popen_data **data);
#ifdef __cplusplus
}
#endif
#endif /* _WIN32 */
#endif /* _PT_POPEN_H */

View File

@ -0,0 +1,432 @@
/*
StoneCracker S404 algorithm data decompression routine
(c) 2006 Jouni 'Mr.Spiv' Korhonen. The code is in public domain.
from shd:
Some portability notes. We are using int32_t as a file size, and that fits
all Amiga file sizes. size_t is of course the right choice.
Warning: Code is not re-entrant.
modified for xmp by Claudio Matsuoka, Jan 2010
(couldn't keep stdint types, some platforms we build on didn't like them)
*/
/*#include <assert.h>*/
#include "../common.h"
#include "depacker.h"
struct bitstream {
/* bit buffer for rolling data bit by bit from the compressed file */
uint32 word;
/* bits left in the bit buffer */
int left;
/* compressed data source */
uint16 *src;
uint8 *orgsrc;
};
static int initGetb(struct bitstream *bs, uint8 *src, uint32 src_length)
{
int eff;
bs->src = (uint16 *) (src + src_length);
bs->orgsrc = src;
bs->left = readmem16b((uint8 *)bs->src); /* bit counter */
/*if (bs->left & (~0xf))
fprintf(stderr, "Workarounded an ancient stc bug\n");*/
/* mask off any corrupt bits */
bs->left &= 0x000f;
bs->src--;
/* get the first 16-bits of the compressed stream */
bs->word = readmem16b((uint8 *)bs->src);
bs->src--;
eff = readmem16b((uint8 *)bs->src); /* efficiency */
bs->src--;
return eff;
}
/* get nbits from the compressed stream */
static int getb(struct bitstream *bs, int nbits)
{
bs->word &= 0x0000ffff;
/* If not enough bits in the bit buffer, get more */
if (bs->left < nbits) {
bs->word <<= bs->left;
/* assert((bs->word & 0x0000ffffU) == 0); */
/* Check that we don't go out of bounds */
/*assert((uint8 *)bs->src >= bs->orgsrc);*/
if (bs->orgsrc > (uint8 *)bs->src) {
return -1;
}
bs->word |= readmem16b((uint8 *)bs->src);
bs->src--;
nbits -= bs->left;
bs->left = 16; /* 16 unused (and some used) bits left in the word */
}
/* Shift nbits off the word and return them */
bs->left -= nbits;
bs->word <<= nbits;
return bs->word >> 16;
}
/* Returns bytes still to read.. or < 0 if error. */
static int checkS404File(uint32 *buf,
int32 *oLen, int32 *pLen, int32 *sLen )
{
if (memcmp(buf, "S404", 4) != 0)
return -1;
*sLen = readmem32b((uint8 *)&buf[1]); /* Security length */
if (*sLen < 0)
return -1;
*oLen = readmem32b((uint8 *)&buf[2]); /* Depacked length */
if (*oLen <= 0)
return -1;
*pLen = readmem32b((uint8 *)&buf[3]); /* Packed length */
if (*pLen <= 6)
return -1;
return 0;
}
static int decompressS404(uint8 *src, uint8 *orgdst,
int32 dst_length, int32 src_length)
{
uint16 w;
int32 eff;
int32 n;
uint8 *dst;
int32 oLen = dst_length;
struct bitstream bs;
int x;
dst = orgdst + oLen;
eff = initGetb(&bs, src, src_length);
/* Sanity check--prevent invalid shift exponents. */
if (eff < 6 || eff >= 32)
return -1;
/*printf("_bl: %02X, _bb: %04X, eff: %d\n",_bl,_bb, eff);*/
while (oLen > 0) {
x = getb(&bs, 9);
/* Sanity check */
if (x < 0) {
return -1;
}
w = x;
/*printf("oLen: %d _bl: %02X, _bb: %04X, w: %04X\n",oLen,_bl,_bb,w);*/
if (w < 0x100) {
/*assert(dst > orgdst);*/
if (orgdst >= dst) {
return -1;
}
*--dst = w;
/*printf("0+[8] -> %02X\n",w);*/
oLen--;
} else if (w == 0x13e || w == 0x13f) {
w <<= 4;
x = getb(&bs, 4);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
n = (w & 0x1f) + 14;
oLen -= n;
while (n-- > 0) {
x = getb(&bs, 8);
/* Sanity check */
if (x < 0) {
return -1;
}
w = x;
/*printf("1+001+1111+[4] -> [8] -> %02X\n",w);*/
/*assert(dst > orgdst);*/
if (orgdst >= dst) {
return -1;
}
*--dst = w;
}
} else {
if (w >= 0x180) {
/* copy 2-3 */
n = w & 0x40 ? 3 : 2;
if (w & 0x20) {
/* dist 545 -> */
w = (w & 0x1f) << (eff - 5);
x = getb(&bs, eff - 5);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
w += 544;
/* printf("1+1+[1]+1+[%d] -> ", eff); */
} else if (w & 0x30) {
// dist 1 -> 32
w = (w & 0x0f) << 1;
x = getb(&bs, 1);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
/* printf("1+1+[1]+01+[5] %d %02X %d %04X-> ",n,w, _bl, _bb); */
} else {
/* dist 33 -> 544 */
w = (w & 0x0f) << 5;
x = getb(&bs, 5);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
w += 32;
/* printf("1+1+[1]+00+[9] -> "); */
}
} else if (w >= 0x140) {
/* copy 4-7 */
n = ((w & 0x30) >> 4) + 4;
if (w & 0x08) {
/* dist 545 -> */
w = (w & 0x07) << (eff - 3);
x = getb(&bs, eff - 3);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
w += 544;
/* printf("1+01+[2]+1+[%d] -> ", eff); */
} else if (w & 0x0c) {
/* dist 1 -> 32 */
w = (w & 0x03) << 3;
x = getb(&bs, 3);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
/* printf("1+01+[2]+01+[5] -> "); */
} else {
/* dist 33 -> 544 */
w = (w & 0x03) << 7;
x = getb(&bs, 7);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
w += 32;
/* printf("1+01+[2]+00+[9] -> "); */
}
} else if (w >= 0x120) {
/* copy 8-22 */
n = ((w & 0x1e) >> 1) + 8;
if (w & 0x01) {
/* dist 545 -> */
x = getb(&bs, eff);
/* Sanity check */
if (x < 0) {
return -1;
}
w = x;
w += 544;
/* printf("1+001+[4]+1+[%d] -> ", eff); */
} else {
x = getb(&bs, 6);
/* Sanity check */
if (x < 0) {
return -1;
}
w = x;
if (w & 0x20) {
/* dist 1 -> 32 */
w &= 0x1f;
/* printf("1+001+[4]+001+[5] -> "); */
} else {
/* dist 33 -> 544 */
w <<= 4;
x = getb(&bs, 4);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
w += 32;
/* printf("1+001+[4]+00+[9] -> "); */
}
}
} else {
w = (w & 0x1f) << 3;
x = getb(&bs, 3);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
n = 23;
while (w == 0xff) {
n += w;
x = getb(&bs, 8);
/* Sanity check */
if (x < 0) {
return -1;
}
w = x;
}
n += w;
x = getb(&bs, 7);
w = x;
if (w & 0x40) {
/* dist 545 -> */
w = (w & 0x3f) << (eff - 6);
x = getb(&bs, eff - 6);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
w += 544;
} else if (w & 0x20) {
/* dist 1 -> 32 */
w &= 0x1f;
/* printf("1+000+[8]+01+[5] -> "); */
} else {
/* dist 33 -> 544; */
w <<= 4;
x = getb(&bs, 4);
/* Sanity check */
if (x < 0) {
return -1;
}
w |= x;
w += 32;
/* printf("1+000+[8]+00+[9] -> "); */
}
}
/* printf("<%d,%d>\n",n,w+1); fflush(stdout); */
oLen -= n;
while (n-- > 0) {
/* printf("Copying: %02X\n",dst[w]); */
dst--;
if (dst < orgdst || (dst + w + 1) >= (orgdst + dst_length))
return -1;
*dst = dst[w + 1];
}
}
}
return 0;
}
static int test_s404(unsigned char *b)
{
return memcmp(b, "S404", 4) == 0;
}
static int decrunch_s404(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
int32 oLen, sLen, pLen;
uint8 *dst = NULL;
uint8 *buf, *src;
if (inlen <= 16)
return -1;
src = buf = (uint8 *) malloc(inlen);
if (src == NULL)
return -1;
if (hio_read(buf, 1, inlen, in) != inlen) {
goto error;
}
if (checkS404File((uint32 *) src, &oLen, &pLen, &sLen)) {
/*fprintf(stderr,"S404 Error: checkS404File() failed..\n");*/
goto error;
}
/* Sanity check */
if (pLen > inlen - 18) {
goto error;
}
/**
* Best case ratio of S404 sliding window:
*
* 2-3: 9b + (>=1b) -> 2-3B -> 24:10
* 4-7: 9b + (>=3b) -> 4-7B -> 56:12
* 8:22: 9b + (>=6b) -> 8-22B -> 176:15
* 23+: 9b + 3b + 8b * floor((n-23)/255) + 7b + (>=0b) -> n B -> ~255:1
*/
if (pLen < (oLen / 255)) {
goto error;
}
if ((dst = (uint8 *)malloc(oLen)) == NULL) {
/*fprintf(stderr,"S404 Error: malloc(%d) failed..\n", oLen);*/
goto error;
}
/* src + 16 skips S404 header */
if (decompressS404(src + 16, dst, oLen, pLen) < 0) {
goto error1;
}
free(src);
*out = dst;
*outlen = oLen;
return 0;
error1:
free(dst);
error:
free(src);
return -1;
}
struct depacker libxmp_depacker_s404 = {
test_s404,
NULL,
decrunch_s404
};

View File

@ -0,0 +1,289 @@
/* public domain decompress code */
#include "depacker.h"
#define MAGIC_1 31 /* First byte of compressed file */
#define MAGIC_2 157 /* Second byte of compressed file */
#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits */
/* Masks 0x20 and 0x40 are free. */
#define BITS 16
#define HSIZE 69001 /* 95% occupancy */
#define FIRST 257 /* first free entry */
#define CLEAR 256 /* table clear output code */
#define INIT_BITS 9 /* initial number of bits/code */
#define BLOCK_MODE 0x80 /* Block compresssion if table is full and */
#define MAXCODE(n) (1L << (n))
/* compression rate is dropping flush tables */
#define IBUFSIZ BUFSIZ /* Default input buffer size */
#define OBUFSIZ BUFSIZ /* Default output buffer size */
#define input(b,o,c,n,m) do { \
char_type *p = &(b)[(o)>>3]; \
(c) = ((((long)(p[0]))|((long)(p[1])<<8)| \
((long)(p[2])<<16))>>((o)&0x7))&(m); \
(o) += (n); \
} while (0)
typedef unsigned char char_type;
typedef long int code_int;
typedef long int count_int;
typedef long int cmp_code_int;
#define htabof(i) htab[i]
#define codetabof(i) codetab[i]
#define tab_prefixof(i) codetabof(i)
#define tab_suffixof(i) ((char_type *)(htab))[i]
#define de_stack ((char_type *)&(htab[HSIZE-1]))
#define clear_htab() memset(htab, -1, sizeof(htab))
#define clear_tab_prefixof() memset(codetab, 0, 256);
static int test_compress(unsigned char *b)
{
return b[0] == 31 && b[1] == 157;
}
/*
* Decompress stdin to stdout. This routine adapts to the codes in the
* file building the "string" table on-the-fly; requiring no table to
* be stored in the compressed file. The tables used herein are shared
* with those of the compress() routine. See the definitions above.
*/
static int decrunch_compress(HIO_HANDLE * in, void ** out, long inlen, long * outlen)
{
char_type *stackp;
code_int code;
int finchar;
code_int oldcode;
code_int incode;
int inbits;
int posbits;
int outpos;
int outsize;
int insize;
int bitmask;
code_int free_ent;
code_int maxcode;
code_int maxmaxcode;
int n_bits;
int rsize;
int maxbits;
int block_mode;
int i;
/*long bytes_in;*/ /* Total number of byte from input */
/*long bytes_out;*/ /* Total number of byte to output */
char_type inbuf[IBUFSIZ + 64]; /* Input buffer */
char_type *outbuf; /* Output buffer */
char_type *tmp;
count_int htab[HSIZE];
unsigned short codetab[HSIZE];
insize = 0;
rsize = hio_read(inbuf, 1, IBUFSIZ, in);
insize += rsize;
if (insize < 3 || inbuf[0] != MAGIC_1 || inbuf[1] != MAGIC_2) {
return -1;
}
maxbits = inbuf[2] & BIT_MASK;
block_mode = inbuf[2] & BLOCK_MODE;
maxmaxcode = MAXCODE(maxbits);
if (maxbits < INIT_BITS || maxbits > BITS) {
/*fprintf(stderr,
"%s: compressed with %d bits, can only handle %d bits\n",
(*ifname != '\0' ? ifname : "stdin"), maxbits, BITS);
exit_code = 4; */
return -1;
}
/*bytes_in = insize;*/
maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
bitmask = (1 << n_bits) - 1;
oldcode = -1;
finchar = 0;
outpos = 0;
outsize = OBUFSIZ;
posbits = 3 << 3;
free_ent = ((block_mode) ? FIRST : 256);
clear_tab_prefixof(); /* As above, initialize the first
256 entries in the table. */
for (code = 255; code >= 0; --code)
tab_suffixof(code) = (char_type) code;
outbuf = (char_type *) malloc(outsize + 2048);
if (!outbuf) {
return -1;
}
do {
resetbuf:;
{
int idx;
int e;
int o;
o = posbits >> 3;
e = o <= insize ? insize - o : 0;
for (idx = 0; idx < e; ++idx)
inbuf[idx] = inbuf[idx + o];
insize = e;
posbits = 0;
}
if (insize < sizeof(inbuf) - IBUFSIZ) {
if ((rsize = hio_read(inbuf + insize, 1, IBUFSIZ, in)) < 0) {
free(outbuf);
return -1;
}
insize += rsize;
}
inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 :
(insize << 3) - (n_bits - 1));
while (inbits > posbits) {
if (free_ent > maxcode) {
posbits = ((posbits - 1) + ((n_bits << 3) -
(posbits - 1 +
(n_bits << 3)) %
(n_bits << 3)));
++n_bits;
if (n_bits == maxbits)
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits) - 1;
bitmask = (1 << n_bits) - 1;
goto resetbuf;
}
input(inbuf, posbits, code, n_bits, bitmask);
if (oldcode == -1) {
if (code >= 256) {
/*
fprintf(stderr, "oldcode:-1 code:%i\n",
(int)(code));
fprintf(stderr, "uncompress: corrupt input\n");
*/
/* abort_compress(); */
free(outbuf);
return -1;
}
outbuf[outpos++] = (char_type)(finchar = (int)(oldcode = code));
continue;
}
if (code == CLEAR && block_mode) {
clear_tab_prefixof();
free_ent = FIRST - 1;
posbits = ((posbits - 1) + ((n_bits << 3) -
(posbits - 1 + (n_bits << 3)) %
(n_bits << 3)));
maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
bitmask = (1 << n_bits) - 1;
goto resetbuf;
}
incode = code;
stackp = de_stack;
if (code >= free_ent) { /* Special case for KwKwK string. */
if (code > free_ent) {
/*char_type *p;
posbits -= n_bits;
p = &inbuf[posbits >> 3];
fprintf(stderr,
"insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)\n",
insize, posbits, p[-1], p[0],
p[1], p[2], p[3],
(posbits & 07));
fprintf(stderr,
"uncompress: corrupt input\n");
*/
/* abort_compress(); */
free(outbuf);
return -1;
}
*--stackp = (char_type) finchar;
code = oldcode;
}
while ((cmp_code_int) code >= (cmp_code_int) 256) { /* Generate output characters in reverse order */
*--stackp = tab_suffixof(code);
code = tab_prefixof(code);
}
*--stackp = (char_type) (finchar = tab_suffixof(code));
/* And put them out in forward order */
if (outpos + (i = (de_stack - stackp)) >= outsize) {
do {
if (i > outsize - outpos)
i = outsize - outpos;
if (i > 0) {
memcpy(outbuf + outpos, stackp, i);
outpos += i;
}
if (outpos >= outsize) {
outsize += OBUFSIZ;
tmp = (char_type *) realloc(outbuf, outsize + 2048);
if (!tmp) {
free(outbuf);
return -1;
}
outbuf = tmp;
}
stackp += i;
}
while ((i = (de_stack - stackp)) > 0);
} else {
memcpy(outbuf + outpos, stackp, i);
outpos += i;
}
if ((code = free_ent) < maxmaxcode) { /* Generate the new entry. */
tab_prefixof(code) = (unsigned short)oldcode;
tab_suffixof(code) = (char_type) finchar;
free_ent = code + 1;
}
oldcode = incode; /* Remember previous code. */
}
/* bytes_in += rsize; */
}
while (rsize > 0);
if ((tmp = (char_type *) realloc(outbuf, outpos)) != NULL)
outbuf = tmp;
*out = outbuf;
*outlen = outpos;
return 0;
}
struct depacker libxmp_depacker_compress = {
test_compress,
NULL,
decrunch_compress
};

1871
libxmp/src/depackers/unlha.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,435 @@
/*
* XPK-SQSH depacker
* Algorithm from the portable decruncher by Bert Jahn (24.12.97)
* Checksum added by Sipos Attila <h430827@stud.u-szeged.hu>
* Rewritten for libxmp by Claudio Matsuoka
*
* Copyright (C) 2013 Claudio Matsuoka
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#include "depacker.h"
struct io {
uint8 *src;
uint8 *dest;
int offs;
int srclen;
};
static uint8 ctable[] = {
2, 3, 4, 5, 6, 7, 8, 0,
3, 2, 4, 5, 6, 7, 8, 0,
4, 3, 5, 2, 6, 7, 8, 0,
5, 4, 6, 2, 3, 7, 8, 0,
6, 5, 7, 2, 3, 4, 8, 0,
7, 6, 8, 2, 3, 4, 5, 0,
8, 7, 6, 2, 3, 4, 5, 0
};
static uint16 xchecksum(uint8 *ptr, uint32 count)
{
uint32 sum = 0;
while (count-- > 0) {
sum ^= readmem32b(ptr);
ptr += 4;
}
return (uint16) (sum ^ (sum >> 16));
}
static int has_bits(struct io *io, int count)
{
return (count <= io->srclen - io->offs);
}
static int get_bits(struct io *io, int count)
{
int r;
if (!has_bits(io, count)) {
return -1;
}
r = readmem24b(io->src + (io->offs >> 3));
r <<= io->offs % 8;
r &= 0xffffff;
r >>= 24 - count;
io->offs += count;
return r;
}
static int get_bits_final(struct io *io, int count)
{
/* Note: has_bits check should be done separately since
* this can return negative values.
*/
int r = readmem24b(io->src + (io->offs >> 3));
r <<= (io->offs % 8) + 8;
r >>= 32 - count;
io->offs += count;
return r;
}
static int copy_data(struct io *io, int d1, int *data, uint8 *dest_start, uint8 *dest_end)
{
uint8 *copy_src;
int r, dest_offset, count, copy_len;
if (get_bits(io, 1) == 0) {
copy_len = get_bits(io, 1) + 2;
} else if (get_bits(io, 1) == 0) {
copy_len = get_bits(io, 1) + 4;
} else if (get_bits(io, 1) == 0) {
copy_len = get_bits(io, 1) + 6;
} else if (get_bits(io, 1) == 0) {
copy_len = get_bits(io, 3) + 8;
} else {
copy_len = get_bits(io, 5) + 16;
}
r = get_bits(io, 1);
if (copy_len < 0 || r < 0) {
return -1;
}
if (r == 0) {
r = get_bits(io, 1);
if (r < 0) {
return -1;
}
if (r == 0) {
count = 8;
dest_offset = 0;
} else {
count = 14;
dest_offset = -0x1100;
}
} else {
count = 12;
dest_offset = -0x100;
}
copy_len -= 3;
if (copy_len >= 0) {
if (copy_len != 0) {
d1--;
}
d1--;
if (d1 < 0) {
d1 = 0;
}
}
copy_len += 2;
r = get_bits(io, count);
if (r < 0) {
return -1;
}
copy_src = io->dest + dest_offset - r - 1;
/* Sanity check */
if (copy_src < dest_start || copy_src + copy_len >= dest_end) {
return -1;
}
do {
//printf("dest=%p src=%p end=%p\n", io->dest, copy_src, dest_end);
*io->dest++ = *copy_src++;
} while (copy_len--);
*data = *(--copy_src);
return d1;
}
static int unsqsh_block(struct io *io, uint8 *dest_start, uint8 *dest_end)
{
int r, d1, d2, data, unpack_len, count, old_count;
d1 = d2 = data = old_count = 0;
io->offs = 0;
data = *(io->src++);
*(io->dest++) = data;
do {
r = get_bits(io, 1);
if (r < 0)
return -1;
if (d1 < 8) {
if (r) {
d1 = copy_data(io, d1, &data, dest_start, dest_end);
if (d1 < 0)
return -1;
d2 -= d2 >> 3;
continue;
}
unpack_len = 0;
count = 8;
} else {
if (r) {
count = 8;
if (count == old_count) {
if (d2 >= 20) {
unpack_len = 1;
d2 += 8;
} else {
unpack_len = 0;
}
} else {
count = old_count;
unpack_len = 4;
d2 += 8;
}
} else {
r = get_bits(io, 1);
if (r < 0)
return -1;
if (r == 0) {
d1 = copy_data(io, d1, &data, dest_start, dest_end);
if (d1 < 0)
return -1;
d2 -= d2 >> 3;
continue;
}
r = get_bits(io, 1);
if (r < 0)
return -1;
if (r == 0) {
count = 2;
} else {
r = get_bits(io, 1);
if (r < 0)
return -1;
if (r) {
io->offs--;
count = get_bits(io, 3);
if (count < 0)
return -1;
} else {
count = 3;
}
}
count = ctable[8 * old_count + count - 17];
if (count != 8) {
unpack_len = 4;
d2 += 8;
} else {
if (d2 >= 20) {
unpack_len = 1;
d2 += 8;
} else {
unpack_len = 0;
}
}
}
}
if (!has_bits(io, count * (unpack_len + 2))) {
return -1;
}
do {
data -= get_bits_final(io, count);
*io->dest++ = data;
} while (unpack_len--);
if (d1 != 31) {
d1++;
}
old_count = count;
d2 -= d2 >> 3;
} while (io->dest < dest_end);
return 0;
}
static int unsqsh(uint8 *src, int srclen, uint8 *dest, int destlen)
{
int len = destlen;
int decrunched = 0;
int type;
int sum, packed_size, unpacked_size;
int lchk;
uint8 *c, *dest_start, *dest_end;
uint8 bc[3];
struct io io;
io.src = src;
io.dest = dest;
dest_start = io.dest;
c = src + 20;
while (len) {
/* Sanity check */
if (c + 8 > src + srclen) {
return -1;
}
type = *c++;
c++; /* hchk */
sum = readmem16b(c);
c += 2; /* checksum */
packed_size = readmem16b(c); /* packed */
c += 2;
unpacked_size = readmem16b(c); /* unpacked */
c += 2;
/* Sanity check */
if (packed_size <= 0 || unpacked_size <= 0) {
return -1;
}
if (c + packed_size + 3 > src + srclen) {
return -1;
}
io.src = c + 2;
io.srclen = packed_size << 3;
memcpy(bc, c + packed_size, 3);
memset(c + packed_size, 0, 3);
lchk = xchecksum(c, (packed_size + 3) >> 2);
memcpy(c + packed_size, bc, 3);
if (lchk != sum) {
return decrunched;
}
if (type == 0) {
/* verbatim block */
decrunched += packed_size;
if (decrunched > destlen) {
return -1;
}
memcpy(io.dest, c, packed_size);
io.dest += packed_size;
c += packed_size;
len -= packed_size;
continue;
}
if (type != 1) {
/* unknown type */
return decrunched;
}
len -= unpacked_size;
decrunched += unpacked_size;
/* Sanity check */
if (decrunched > destlen) {
return -1;
}
packed_size = (packed_size + 3) & 0xfffc;
c += packed_size;
dest_end = io.dest + unpacked_size;
if (unsqsh_block(&io, dest_start, dest_end) < 0) {
return -1;
}
io.dest = dest_end;
}
return decrunched;
}
static int test_sqsh(unsigned char *b)
{
return memcmp(b, "XPKF", 4) == 0 && memcmp(b + 8, "SQSH", 4) == 0;
}
static int decrunch_sqsh(HIO_HANDLE * f, void ** outbuf, long inlen, long * outlen)
{
unsigned char *src, *dest;
int srclen, destlen;
if (hio_read32b(f) != 0x58504b46) /* XPKF */
goto err;
srclen = hio_read32b(f);
/* Sanity check */
if (srclen <= 8 || srclen > 0x100000)
goto err;
if (hio_read32b(f) != 0x53515348) /* SQSH */
goto err;
destlen = hio_read32b(f);
if (destlen < 0 || destlen > 0x100000)
goto err;
if ((src = (unsigned char *)calloc(1, srclen + 3)) == NULL)
goto err;
if ((dest = (unsigned char *)malloc(destlen + 100)) == NULL)
goto err2;
if (hio_read(src, srclen - 8, 1, f) != 1)
goto err3;
if (unsqsh(src, srclen, dest, destlen) != destlen)
goto err3;
free(src);
*outbuf = dest;
*outlen = destlen;
return 0;
err3:
free(dest);
err2:
free(src);
err:
return -1;
}
struct depacker libxmp_depacker_sqsh = {
test_sqsh,
NULL,
decrunch_sqsh
};

107
libxmp/src/depackers/unxz.c Normal file
View File

@ -0,0 +1,107 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "depacker.h"
#include "xz.h"
#include "crc32.h"
#define XZ_MAX_OUTPUT LIBXMP_DEPACK_LIMIT
#define XZ_MAX_DICT (16 << 20)
#define XZ_BUFFER_SIZE 4096
static const uint8 XZ_MAGIC[] = { 0xfd, '7', 'z', 'X', 'Z', 0x00 };
static int test_xz(unsigned char *b)
{
return !memcmp(b, XZ_MAGIC, sizeof(XZ_MAGIC));
}
static int decrunch_xz(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
struct xz_dec *xz;
struct xz_buf buf;
enum xz_ret ret = XZ_OK;
uint8 *inbuf = NULL;
uint8 *tmp;
xz = xz_dec_init(XZ_DYNALLOC, XZ_MAX_DICT);
if (xz == NULL)
return -1;
if ((buf.out = (uint8 *) malloc(XZ_BUFFER_SIZE)) == NULL)
goto err;
if ((inbuf = (uint8 *) malloc(XZ_BUFFER_SIZE)) == NULL)
goto err;
buf.in = inbuf;
buf.in_pos = 0;
buf.in_size = 0;
buf.out_pos = 0;
buf.out_size = XZ_BUFFER_SIZE;
while (ret != XZ_STREAM_END) {
if (buf.out_pos == buf.out_size) {
/* Allocate more output space. */
buf.out_size <<= 1;
if (buf.out_size > XZ_MAX_OUTPUT)
goto err;
if ((tmp = (uint8 *) realloc(buf.out, buf.out_size)) == NULL)
goto err;
buf.out = tmp;
}
else if (buf.in_pos == buf.in_size) {
/* Read input. */
buf.in_pos = 0;
buf.in_size = hio_read(inbuf, 1, XZ_BUFFER_SIZE, in);
if (buf.in_size == 0)
goto err;
}
ret = xz_dec_run(xz, &buf);
if (ret != XZ_OK && ret != XZ_STREAM_END && ret != XZ_UNSUPPORTED_CHECK)
goto err;
}
xz_dec_end(xz);
if ((tmp = (uint8 *) realloc(buf.out, buf.out_pos)) != NULL)
buf.out = tmp;
*out = buf.out;
*outlen = buf.out_pos;
free(inbuf);
return 0;
err:
xz_dec_end(xz);
free(buf.out);
free(inbuf);
return -1;
}
struct depacker libxmp_depacker_xz = {
test_xz,
NULL,
decrunch_xz
};

View File

@ -0,0 +1,101 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#include "depacker.h"
#include "miniz_zip.h"
static int test_zip(unsigned char *b)
{
return b[0] == 'P' && b[1] == 'K' &&
((b[2] == 3 && b[3] == 4) || (b[2] == '0' && b[3] == '0' &&
b[4] == 'P' && b[5] == 'K' && b[6] == 3 && b[7] == 4));
}
#ifndef MINIZ_NO_ARCHIVE_APIS
static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 ofs, void *pBuf, size_t n)
{
if (hio_seek((HIO_HANDLE *)pOpaque, (long)ofs, SEEK_SET))
return 0;
return hio_read(pBuf, 1, n, (HIO_HANDLE *)pOpaque);
}
#endif
static int decrunch_zip(HIO_HANDLE *in, void **out, long inlen, long *outlen)
{
#ifndef MINIZ_NO_ARCHIVE_APIS
mz_zip_archive archive;
char filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
mz_uint32 i;
void *pBuf;
size_t pSize;
memset(&archive, 0, sizeof(archive));
archive.m_pRead = mz_zip_file_read_func;
archive.m_pIO_opaque = in;
if (!mz_zip_reader_init(&archive, inlen, 0)) {
D_(D_CRIT "Failed to open archive: %s", mz_zip_get_error_string(archive.m_last_error));
return -1;
}
for (i = 0; i < archive.m_total_files; i++) {
if (mz_zip_reader_get_filename(&archive, i, filename, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE) == 0) {
D_(D_WARN "Could not get file name: %s", mz_zip_get_error_string(archive.m_last_error));
continue;
}
if (mz_zip_reader_is_file_a_directory(&archive, i)) {
D_(D_INFO "Skipping directory %s", filename);
continue;
}
if (!mz_zip_reader_is_file_supported(&archive, i)) {
D_(D_INFO "Skipping unsupported file %s", filename);
continue;
}
if (libxmp_exclude_match(filename)) {
D_(D_INFO "Skipping file %s", filename);
continue;
}
pBuf = mz_zip_reader_extract_to_heap(&archive, i, &pSize, 0);
if (!pBuf) {
D_(D_CRIT "Failed to extract %s: %s", filename, mz_zip_get_error_string(archive.m_last_error));
break;
}
mz_zip_reader_end(&archive);
*out = pBuf;
*outlen = pSize;
return 0;
}
mz_zip_reader_end(&archive);
#endif
return -1;
}
struct depacker libxmp_depacker_zip = {
test_zip,
NULL,
decrunch_zip
};

117
libxmp/src/depackers/xfd.c Normal file
View File

@ -0,0 +1,117 @@
/* xfdmaster.library decruncher for XMP
* Copyright (C) 2007 Chris Young
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#if defined(LIBXMP_AMIGA) && defined(HAVE_PROTO_XFDMASTER_H)
#define __USE_INLINE__
#include <proto/exec.h>
#include <proto/xfdmaster.h>
#include <exec/types.h>
#include "depacker.h"
static int _test_xfd(unsigned char *buffer, int length)
{
int ret = 0;
struct xfdBufferInfo *xfdobj;
xfdobj = (struct xfdBufferInfo *) xfdAllocObject(XFDOBJ_BUFFERINFO);
if(xfdobj)
{
xfdobj->xfdbi_SourceBuffer = buffer;
xfdobj->xfdbi_SourceBufLen = length;
xfdobj->xfdbi_Flags = XFDFB_RECOGTARGETLEN | XFDFB_RECOGEXTERN;
if(xfdRecogBuffer(xfdobj))
{
ret = (xfdobj->xfdbi_PackerName != NULL);
}
xfdFreeObject((APTR)xfdobj);
}
return(ret);
}
static int test_xfd(unsigned char *b)
{
if (!xfdMasterBase) return 0;
return _test_xfd(b, 1024);
}
static int decrunch_xfd(HIO_HANDLE *f, void **outbuf, long inlen, long *outlen)
{
struct xfdBufferInfo *xfdobj;
uint8 *packed;
void *unpacked;
int ret = -1;
if (xfdMasterBase == NULL)
return -1;
packed = (uint8 *) AllocVec(inlen,MEMF_CLEAR);
if (!packed) return -1;
hio_read(packed,inlen,1,f);
xfdobj = (struct xfdBufferInfo *) xfdAllocObject(XFDOBJ_BUFFERINFO);
if(xfdobj)
{
xfdobj->xfdbi_SourceBufLen = inlen;
xfdobj->xfdbi_SourceBuffer = packed;
xfdobj->xfdbi_Flags = XFDFF_RECOGEXTERN | XFDFF_RECOGTARGETLEN;
/* xfdobj->xfdbi_PackerFlags = XFDPFF_RECOGLEN; */
if(xfdRecogBuffer(xfdobj))
{
xfdobj->xfdbi_TargetBufMemType = MEMF_ANY;
if(xfdDecrunchBuffer(xfdobj))
{
unpacked = malloc(xfdobj->xfdbi_TargetBufSaveLen);
if (unpacked) {
memcpy(unpacked, xfdobj->xfdbi_TargetBuffer, xfdobj->xfdbi_TargetBufSaveLen);
*outbuf = unpacked;
*outlen = xfdobj->xfdbi_TargetBufSaveLen;
ret=0;
}
else
{
ret=-1;
}
FreeMem(xfdobj->xfdbi_TargetBuffer,xfdobj->xfdbi_TargetBufLen);
}
else
{
ret=-1;
}
}
xfdFreeObject((APTR)xfdobj);
}
FreeVec(packed);
return(ret);
}
struct depacker libxmp_depacker_xfd = {
test_xfd,
NULL,
decrunch_xfd
};
#endif /* AMIGA */

View File

@ -0,0 +1,88 @@
/* xfdmaster.library decruncher for XMP
* Copyright (C) 2007 Chris Young
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#if defined(LIBXMP_AMIGA) && defined(HAVE_PROTO_XFDMASTER_H)
#ifdef __amigaos4__
#define __USE_INLINE__
#endif
#include <proto/exec.h>
#include <proto/xfdmaster.h>
#include <exec/types.h>
#if defined(__amigaos4__) || defined(__MORPHOS__)
struct Library *xfdMasterBase;
#else
struct xfdMasterBase *xfdMasterBase;
#endif
#ifdef __amigaos4__
struct xfdMasterIFace *IxfdMaster;
/*struct ExecIFace *IExec;*/
#endif
#ifdef __GNUC__
void INIT_8_open_xfd(void) __attribute__ ((constructor));
void EXIT_8_close_xfd(void) __attribute__ ((destructor));
#endif
#ifdef __VBCC__
#define INIT_8_open_xfd _INIT_8_open_xfd
#define EXIT_8_close_xfd _EXIT_8_close_xfd
#endif
void EXIT_8_close_xfd(void) {
#ifdef __amigaos4__
if (IxfdMaster) {
DropInterface((struct Interface *) IxfdMaster);
IxfdMaster = NULL;
}
#endif
if (xfdMasterBase) {
CloseLibrary((struct Library *) xfdMasterBase);
xfdMasterBase = NULL;
}
}
void INIT_8_open_xfd(void) {
#ifdef __amigaos4__
/*IExec = (struct ExecIFace *)(*(struct ExecBase **)4)->MainInterface;*/
#endif
#if defined(__amigaos4__) || defined(__MORPHOS__)
xfdMasterBase = OpenLibrary("xfdmaster.library",38);
#else
xfdMasterBase = (struct xfdMasterBase *) OpenLibrary("xfdmaster.library",38);
#endif
if (!xfdMasterBase) return;
#ifdef __amigaos4__
IxfdMaster = (struct xfdMasterIFace *) GetInterface(xfdMasterBase,"main",1,NULL);
if (!IxfdMaster) {
CloseLibrary(xfdMasterBase);
xfdMasterBase = NULL;
}
#endif
}
#endif /* AMIGA */

View File

@ -0,0 +1,230 @@
/* $OpenBSD: fnmatch.c,v 1.13 2006/03/31 05:34:14 deraadt Exp $ */
#ifndef HAVE_FNMATCH
/*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Guido van Rossum.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
* Compares a filename or pathname to a pattern.
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "xfnmatch.h"
#define EOS '\0'
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
/* Limit of recursion during matching attempts. */
#define __FNM_MAX_RECUR 64
static int rangematch(const char *, char, int, char **);
static int __fnmatch(const char *, const char *, int, int);
int
fnmatch(const char *pattern, const char *string, int flags)
{
int e;
e = __fnmatch(pattern, string, flags, __FNM_MAX_RECUR);
if (e == -1)
e = FNM_NOMATCH;
return (e);
}
static int
__fnmatch(const char *pattern, const char *string, int flags, int recur)
{
const char *stringstart;
char *newp;
char c, test;
int e;
if (recur-- == 0)
return (-1);
for (stringstart = string;;)
switch (c = *pattern++) {
case EOS:
if ((flags & FNM_LEADING_DIR) && *string == '/')
return (0);
return (*string == EOS ? 0 : FNM_NOMATCH);
case '?':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
++string;
break;
case '*':
c = *pattern;
/* Collapse multiple stars. */
while (c == '*')
c = *++pattern;
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
/* Optimize for pattern with * at end or before /. */
if (c == EOS) {
if (flags & FNM_PATHNAME)
return ((flags & FNM_LEADING_DIR) ||
strchr(string, '/') == NULL ?
0 : FNM_NOMATCH);
else
return (0);
} else if (c == '/' && (flags & FNM_PATHNAME)) {
if ((string = strchr(string, '/')) == NULL)
return (FNM_NOMATCH);
break;
}
/* General case, use recursion. */
while ((test = *string) != EOS) {
e = __fnmatch(pattern, string,
flags & ~FNM_PERIOD, recur);
if (e != FNM_NOMATCH)
return (e);
if (test == '/' && (flags & FNM_PATHNAME))
break;
++string;
}
return (FNM_NOMATCH);
case '[':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
switch (rangematch(pattern, *string, flags, &newp)) {
case RANGE_ERROR:
/* not a good range, treat as normal text */
goto normal;
case RANGE_MATCH:
pattern = newp;
break;
case RANGE_NOMATCH:
return (FNM_NOMATCH);
}
++string;
break;
case '\\':
if (!(flags & FNM_NOESCAPE)) {
if ((c = *pattern++) == EOS) {
c = '\\';
--pattern;
}
}
/* FALLTHROUGH */
default:
normal:
if (c != *string && !((flags & FNM_CASEFOLD) &&
(tolower((unsigned char)c) ==
tolower((unsigned char)*string))))
return (FNM_NOMATCH);
++string;
break;
}
/* NOTREACHED */
}
static int
rangematch(const char *pattern, char test, int flags, char **newp)
{
int negate, ok;
char c, c2;
/*
* A bracket expression starting with an unquoted circumflex
* character produces unspecified results (IEEE 1003.2-1992,
* 3.13.2). This implementation treats it like '!', for
* consistency with the regular expression syntax.
* J.T. Conklin (conklin@ngai.kaleida.com)
*/
if ((negate = (*pattern == '!' || *pattern == '^')))
++pattern;
if (flags & FNM_CASEFOLD)
test = (char)tolower((unsigned char)test);
/*
* A right bracket shall lose its special meaning and represent
* itself in a bracket expression if it occurs first in the list.
* -- POSIX.2 2.8.3.2
*/
ok = 0;
c = *pattern++;
do {
if (c == '\\' && !(flags & FNM_NOESCAPE))
c = *pattern++;
if (c == EOS)
return (RANGE_ERROR);
if (c == '/' && (flags & FNM_PATHNAME))
return (RANGE_NOMATCH);
if ((flags & FNM_CASEFOLD))
c = (char)tolower((unsigned char)c);
if (*pattern == '-'
&& (c2 = *(pattern+1)) != EOS && c2 != ']') {
pattern += 2;
if (c2 == '\\' && !(flags & FNM_NOESCAPE))
c2 = *pattern++;
if (c2 == EOS)
return (RANGE_ERROR);
if (flags & FNM_CASEFOLD)
c2 = (char)tolower((unsigned char)c2);
if (c <= test && test <= c2)
ok = 1;
} else if (c == test)
ok = 1;
} while ((c = *pattern++) != ']');
*newp = (char *)pattern;
return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
}
#endif

View File

@ -0,0 +1,65 @@
/* $OpenBSD: fnmatch.h,v 1.8 2005/12/13 00:35:22 millert Exp $ */
/* $NetBSD: fnmatch.h,v 1.5 1994/10/26 00:55:53 cgd Exp $ */
#ifdef HAVE_FNMATCH
#include <fnmatch.h>
#else
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
*/
#ifndef _FNMATCH_H_
#define _FNMATCH_H_
#define FNM_NOMATCH 1 /* Match failed. */
#define FNM_NOSYS 2 /* Function not supported (unused). */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
#define FNM_IGNORECASE FNM_CASEFOLD
#define FNM_FILE_NAME FNM_PATHNAME
#if defined(__cplusplus)
extern "C" {
#endif
int fnmatch(const char *, const char *, int);
#if defined(__cplusplus)
}
#endif
#endif /* !_FNMATCH_H_ */
#endif /* HAVE_FNMATCH */

293
libxmp/src/depackers/xz.h Normal file
View File

@ -0,0 +1,293 @@
/*
* XZ decompressor
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
#ifndef XZ_H
#define XZ_H
#include "../common.h"
#define xz_false 0
#define xz_true 1
typedef int xz_bool;
#ifdef __cplusplus
extern "C" {
#endif
/* In Linux, this is used to make extern functions static when needed. */
#ifndef XZ_EXTERN
# define XZ_EXTERN /*extern*/
#endif
/**
* enum xz_mode - Operation mode
*
* @XZ_SINGLE: Single-call mode. This uses less RAM than
* multi-call modes, because the LZMA2
* dictionary doesn't need to be allocated as
* part of the decoder state. All required data
* structures are allocated at initialization,
* so xz_dec_run() cannot return XZ_MEM_ERROR.
* @XZ_PREALLOC: Multi-call mode with preallocated LZMA2
* dictionary buffer. All data structures are
* allocated at initialization, so xz_dec_run()
* cannot return XZ_MEM_ERROR.
* @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is
* allocated once the required size has been
* parsed from the stream headers. If the
* allocation fails, xz_dec_run() will return
* XZ_MEM_ERROR.
*
* It is possible to enable support only for a subset of the above
* modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
* or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
* with support for all operation modes, but the preboot code may
* be built with fewer features to minimize code size.
*/
enum xz_mode {
XZ_SINGLE,
XZ_PREALLOC,
XZ_DYNALLOC
};
/**
* enum xz_ret - Return codes
* @XZ_OK: Everything is OK so far. More input or more
* output space is required to continue. This
* return code is possible only in multi-call mode
* (XZ_PREALLOC or XZ_DYNALLOC).
* @XZ_STREAM_END: Operation finished successfully.
* @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding
* is still possible in multi-call mode by simply
* calling xz_dec_run() again.
* Note that this return value is used only if
* XZ_DEC_ANY_CHECK was defined at build time,
* which is not used in the kernel. Unsupported
* check types return XZ_OPTIONS_ERROR if
* XZ_DEC_ANY_CHECK was not defined at build time.
* @XZ_MEM_ERROR: Allocating memory failed. This return code is
* possible only if the decoder was initialized
* with XZ_DYNALLOC. The amount of memory that was
* tried to be allocated was no more than the
* dict_max argument given to xz_dec_init().
* @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than
* allowed by the dict_max argument given to
* xz_dec_init(). This return value is possible
* only in multi-call mode (XZ_PREALLOC or
* XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
* ignores the dict_max argument.
* @XZ_FORMAT_ERROR: File format was not recognized (wrong magic
* bytes).
* @XZ_OPTIONS_ERROR: This implementation doesn't support the requested
* compression options. In the decoder this means
* that the header CRC32 matches, but the header
* itself specifies something that we don't support.
* @XZ_DATA_ERROR: Compressed data is corrupt.
* @XZ_BUF_ERROR: Cannot make any progress. Details are slightly
* different between multi-call and single-call
* mode; more information below.
*
* In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
* to XZ code cannot consume any input and cannot produce any new output.
* This happens when there is no new input available, or the output buffer
* is full while at least one output byte is still pending. Assuming your
* code is not buggy, you can get this error only when decoding a compressed
* stream that is truncated or otherwise corrupt.
*
* In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
* is too small or the compressed input is corrupt in a way that makes the
* decoder produce more output than the caller expected. When it is
* (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
* is used instead of XZ_BUF_ERROR.
*/
enum xz_ret {
XZ_OK,
XZ_STREAM_END,
XZ_UNSUPPORTED_CHECK,
XZ_MEM_ERROR,
XZ_MEMLIMIT_ERROR,
XZ_FORMAT_ERROR,
XZ_OPTIONS_ERROR,
XZ_DATA_ERROR,
XZ_BUF_ERROR
};
/**
* struct xz_buf - Passing input and output buffers to XZ code
* @in: Beginning of the input buffer. This may be NULL if and only
* if in_pos is equal to in_size.
* @in_pos: Current position in the input buffer. This must not exceed
* in_size.
* @in_size: Size of the input buffer
* @out: Beginning of the output buffer. This may be NULL if and only
* if out_pos is equal to out_size.
* @out_pos: Current position in the output buffer. This must not exceed
* out_size.
* @out_size: Size of the output buffer
*
* Only the contents of the output buffer from out[out_pos] onward, and
* the variables in_pos and out_pos are modified by the XZ code.
*/
struct xz_buf {
const uint8 *in;
size_t in_pos;
size_t in_size;
uint8 *out;
size_t out_pos;
size_t out_size;
};
/**
* struct xz_dec - Opaque type to hold the XZ decoder state
*/
struct xz_dec;
/**
* xz_dec_init() - Allocate and initialize a XZ decoder state
* @mode: Operation mode
* @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for
* multi-call decoding. This is ignored in single-call mode
* (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
* or 2^n + 2^(n-1) bytes (the latter sizes are less common
* in practice), so other values for dict_max don't make sense.
* In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
* 512 KiB, and 1 MiB are probably the only reasonable values,
* except for kernel and initramfs images where a bigger
* dictionary can be fine and useful.
*
* Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
* once. The caller must provide enough output space or the decoding will
* fail. The output space is used as the dictionary buffer, which is why
* there is no need to allocate the dictionary as part of the decoder's
* internal state.
*
* Because the output buffer is used as the workspace, streams encoded using
* a big dictionary are not a problem in single-call mode. It is enough that
* the output buffer is big enough to hold the actual uncompressed data; it
* can be smaller than the dictionary size stored in the stream headers.
*
* Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
* of memory is preallocated for the LZMA2 dictionary. This way there is no
* risk that xz_dec_run() could run out of memory, since xz_dec_run() will
* never allocate any memory. Instead, if the preallocated dictionary is too
* small for decoding the given input stream, xz_dec_run() will return
* XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
* decoded to avoid allocating excessive amount of memory for the dictionary.
*
* Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
* dict_max specifies the maximum allowed dictionary size that xz_dec_run()
* may allocate once it has parsed the dictionary size from the stream
* headers. This way excessive allocations can be avoided while still
* limiting the maximum memory usage to a sane value to prevent running the
* system out of memory when decompressing streams from untrusted sources.
*
* On success, xz_dec_init() returns a pointer to struct xz_dec, which is
* ready to be used with xz_dec_run(). If memory allocation fails,
* xz_dec_init() returns NULL.
*/
XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32 dict_max);
/**
* xz_dec_run() - Run the XZ decoder
* @s: Decoder state allocated using xz_dec_init()
* @b: Input and output buffers
*
* The possible return values depend on build options and operation mode.
* See enum xz_ret for details.
*
* Note that if an error occurs in single-call mode (return value is not
* XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
* contents of the output buffer from b->out[b->out_pos] onward are
* undefined. This is true even after XZ_BUF_ERROR, because with some filter
* chains, there may be a second pass over the output buffer, and this pass
* cannot be properly done if the output buffer is truncated. Thus, you
* cannot give the single-call decoder a too small buffer and then expect to
* get that amount valid data from the beginning of the stream. You must use
* the multi-call decoder if you don't want to uncompress the whole stream.
*/
XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
/**
* xz_dec_reset() - Reset an already allocated decoder state
* @s: Decoder state allocated using xz_dec_init()
*
* This function can be used to reset the multi-call decoder state without
* freeing and reallocating memory with xz_dec_end() and xz_dec_init().
*
* In single-call mode, xz_dec_reset() is always called in the beginning of
* xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
* multi-call mode.
*/
XZ_EXTERN void xz_dec_reset(struct xz_dec *s);
/**
* xz_dec_end() - Free the memory allocated for the decoder state
* @s: Decoder state allocated using xz_dec_init(). If s is NULL,
* this function does nothing.
*/
XZ_EXTERN void xz_dec_end(struct xz_dec *s);
/*
* Standalone build (userspace build or in-kernel build for boot time use)
* needs a CRC32 implementation. For normal in-kernel use, kernel's own
* CRC32 module is used instead, and users of this module don't need to
* care about the functions below.
*/
#ifndef XZ_INTERNAL_CRC32
# define XZ_INTERNAL_CRC32 1
#endif
/*
* If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64
* implementation is needed too.
*/
#ifndef XZ_USE_CRC64
# undef XZ_INTERNAL_CRC64
# define XZ_INTERNAL_CRC64 0
#endif
#ifndef XZ_INTERNAL_CRC64
# define XZ_INTERNAL_CRC64 1
#endif
#if XZ_INTERNAL_CRC32
/*
* This must be called before any other xz_* function to initialize
* the CRC32 lookup table.
*/
XZ_EXTERN void xz_crc32_init(void);
/*
* Update CRC32 value using the polynomial from IEEE-802.3. To start a new
* calculation, the third argument must be zero. To continue the calculation,
* the previously returned value is passed as the third argument.
*/
XZ_EXTERN uint32 xz_crc32(const uint8 *buf, size_t size, uint32 crc);
#endif
#if XZ_INTERNAL_CRC64
/*
* This must be called before any other xz_* function (except xz_crc32_init())
* to initialize the CRC64 lookup table.
*/
XZ_EXTERN void xz_crc64_init(void);
/*
* Update CRC64 value using the polynomial from ECMA-182. To start a new
* calculation, the third argument must be zero. To continue the calculation,
* the previously returned value is passed as the third argument.
*/
XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,111 @@
/*
* Private includes and definitions for userspace use of XZ Embedded
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
#ifndef XZ_CONFIG_H
#define XZ_CONFIG_H
/* Uncomment to enable CRC64 support. */
/* #define XZ_USE_CRC64 */
/* Uncomment as needed to enable BCJ filter decoders. */
/* #define XZ_DEC_X86 */
/* #define XZ_DEC_POWERPC */
/* #define XZ_DEC_IA64 */
/* #define XZ_DEC_ARM */
/* #define XZ_DEC_ARMTHUMB */
/* #define XZ_DEC_SPARC */
#define XZ_DEC_ANY_CHECK 1
#include "xz.h"
#define GFP_KERNEL (0)
#define kmalloc(size, flags) malloc(size)
#define kfree(ptr) free(ptr)
#define vmalloc(size) malloc(size)
#define vfree(ptr) free(ptr)
#define memeq(a, b, size) (memcmp(a, b, size) == 0)
#define memzero(buf, size) memset(buf, 0, size)
#ifndef min
# define min(x, y) ((x) < (y) ? (x) : (y))
#endif
#define min_t(type, x, y) min(x, y)
/*
* Some functions have been marked with __always_inline to keep the
* performance reasonable even when the compiler is optimizing for
* small code size. You may be able to save a few bytes by #defining
* __always_inline to plain inline, but don't complain if the code
* becomes slow.
*
* NOTE: System headers on GNU/Linux may #define this macro already,
* so if you want to change it, you need to #undef it first.
*/
#ifndef __always_inline
# ifdef __GNUC__
# define __always_inline \
inline __attribute__((__always_inline__))
# else
# define __always_inline inline
# endif
#endif
/* Inline functions to access unaligned unsigned 32-bit integers */
#ifndef get_unaligned_le32
static inline uint32 get_unaligned_le32(const uint8 *buf)
{
return (uint32)buf[0]
| ((uint32)buf[1] << 8)
| ((uint32)buf[2] << 16)
| ((uint32)buf[3] << 24);
}
#endif
#ifndef get_unaligned_be32
static inline uint32 get_unaligned_be32(const uint8 *buf)
{
return (uint32)(buf[0] << 24)
| ((uint32)buf[1] << 16)
| ((uint32)buf[2] << 8)
| (uint32)buf[3];
}
#endif
#ifndef put_unaligned_le32
static inline void put_unaligned_le32(uint32 val, uint8 *buf)
{
buf[0] = (uint8)val;
buf[1] = (uint8)(val >> 8);
buf[2] = (uint8)(val >> 16);
buf[3] = (uint8)(val >> 24);
}
#endif
#ifndef put_unaligned_be32
static inline void put_unaligned_be32(uint32 val, uint8 *buf)
{
buf[0] = (uint8)(val >> 24);
buf[1] = (uint8)(val >> 16);
buf[2] = (uint8)(val >> 8);
buf[3] = (uint8)val;
}
#endif
/*
* Use get_unaligned_le32() also for aligned access for simplicity. On
* little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
* could save a few bytes in code size.
*/
#ifndef get_le32
# define get_le32 get_unaligned_le32
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,880 @@
/*
* .xz Stream decoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
#include "xz_private.h"
#include "xz_stream.h"
#include "crc32.h"
#define xz_crc32 libxmp_crc32_A
#ifdef XZ_USE_CRC64
# define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)
#else
# define IS_CRC64(check_type) xz_false
#endif
/* Hash used to validate the Index field */
struct xz_dec_hash {
vli_type unpadded;
vli_type uncompressed;
uint32 crc32;
};
enum dec_sequence_main {
SEQ_STREAM_HEADER,
SEQ_BLOCK_START,
SEQ_BLOCK_HEADER,
SEQ_BLOCK_UNCOMPRESS,
SEQ_BLOCK_PADDING,
SEQ_BLOCK_CHECK,
SEQ_INDEX,
SEQ_INDEX_PADDING,
SEQ_INDEX_CRC32,
SEQ_STREAM_FOOTER
};
enum dec_sequence_index {
SEQ_INDEX_COUNT,
SEQ_INDEX_UNPADDED,
SEQ_INDEX_UNCOMPRESSED
};
#ifndef __cplusplus
typedef enum xz_check xz_check_t;
#else
typedef int xz_check_t;
#endif
struct xz_dec {
/* Position in dec_main() */
enum dec_sequence_main sequence;
/* Position in variable-length integers and Check fields */
uint32 pos;
/* Variable-length integer decoded by dec_vli() */
vli_type vli;
/* Saved in_pos and out_pos */
size_t in_start;
size_t out_start;
#ifdef XZ_USE_CRC64
/* CRC32 or CRC64 value in Block or CRC32 value in Index */
uint64 crc;
#else
/* CRC32 value in Block or Index */
uint32 crc;
#endif
/* Type of the integrity check calculated from uncompressed data */
xz_check_t check_type;
/* Operation mode */
enum xz_mode mode;
/*
* True if the next call to xz_dec_run() is allowed to return
* XZ_BUF_ERROR.
*/
xz_bool allow_buf_error;
/* Information stored in Block Header */
struct {
/*
* Value stored in the Compressed Size field, or
* VLI_UNKNOWN if Compressed Size is not present.
*/
vli_type compressed;
/*
* Value stored in the Uncompressed Size field, or
* VLI_UNKNOWN if Uncompressed Size is not present.
*/
vli_type uncompressed;
/* Size of the Block Header field */
uint32 size;
} block_header;
/* Information collected when decoding Blocks */
struct {
/* Observed compressed size of the current Block */
vli_type compressed;
/* Observed uncompressed size of the current Block */
vli_type uncompressed;
/* Number of Blocks decoded so far */
vli_type count;
/*
* Hash calculated from the Block sizes. This is used to
* validate the Index field.
*/
struct xz_dec_hash hash;
} block;
/* Variables needed when verifying the Index field */
struct {
/* Position in dec_index() */
enum dec_sequence_index sequence;
/* Size of the Index in bytes */
vli_type size;
/* Number of Records (matches block.count in valid files) */
vli_type count;
/*
* Hash calculated from the Records (matches block.hash in
* valid files).
*/
struct xz_dec_hash hash;
} index;
/*
* Temporary buffer needed to hold Stream Header, Block Header,
* and Stream Footer. The Block Header is the biggest (1 KiB)
* so we reserve space according to that. buf[] has to be aligned
* to a multiple of four bytes; the size_t variables before it
* should guarantee this.
*/
struct {
size_t pos;
size_t size;
uint8 buf[1024];
} temp;
struct xz_dec_lzma2 *lzma2;
#ifdef XZ_DEC_BCJ
struct xz_dec_bcj *bcj;
xz_bool bcj_active;
#endif
};
#ifdef XZ_DEC_ANY_CHECK
/* Sizes of the Check field with different Check IDs */
static const uint8 check_sizes[16] = {
0,
4, 4, 4,
8, 8, 8,
16, 16, 16,
32, 32, 32,
64, 64, 64
};
#endif
/*
* Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
* must have set s->temp.pos to indicate how much data we are supposed
* to copy into s->temp.buf. Return true once s->temp.pos has reached
* s->temp.size.
*/
static xz_bool fill_temp(struct xz_dec *s, struct xz_buf *b)
{
size_t copy_size = min_t(size_t,
b->in_size - b->in_pos, s->temp.size - s->temp.pos);
memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
b->in_pos += copy_size;
s->temp.pos += copy_size;
if (s->temp.pos == s->temp.size) {
s->temp.pos = 0;
return xz_true;
}
return xz_false;
}
/* Decode a variable-length integer (little-endian base-128 encoding) */
static enum xz_ret dec_vli(struct xz_dec *s, const uint8 *in,
size_t *in_pos, size_t in_size)
{
uint8 byte;
if (s->pos == 0)
s->vli = 0;
while (*in_pos < in_size) {
byte = in[*in_pos];
++*in_pos;
s->vli |= (vli_type)(byte & 0x7F) << s->pos;
if ((byte & 0x80) == 0) {
/* Don't allow non-minimal encodings. */
if (byte == 0 && s->pos != 0)
return XZ_DATA_ERROR;
s->pos = 0;
return XZ_STREAM_END;
}
s->pos += 7;
if (s->pos == 7 * VLI_BYTES_MAX)
return XZ_DATA_ERROR;
}
return XZ_OK;
}
/*
* Decode the Compressed Data field from a Block. Update and validate
* the observed compressed and uncompressed sizes of the Block so that
* they don't exceed the values possibly stored in the Block Header
* (validation assumes that no integer overflow occurs, since vli_type
* is normally uint64_t). Update the CRC32 or CRC64 value if presence of
* the CRC32 or CRC64 field was indicated in Stream Header.
*
* Once the decoding is finished, validate that the observed sizes match
* the sizes possibly stored in the Block Header. Update the hash and
* Block count, which are later used to validate the Index field.
*/
static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
{
enum xz_ret ret;
s->in_start = b->in_pos;
s->out_start = b->out_pos;
#ifdef XZ_DEC_BCJ
if (s->bcj_active)
ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
else
#endif
ret = xz_dec_lzma2_run(s->lzma2, b);
s->block.compressed += b->in_pos - s->in_start;
s->block.uncompressed += b->out_pos - s->out_start;
/*
* There is no need to separately check for VLI_UNKNOWN, since
* the observed sizes are always smaller than VLI_UNKNOWN.
*/
if (s->block.compressed > s->block_header.compressed
|| s->block.uncompressed
> s->block_header.uncompressed)
return XZ_DATA_ERROR;
if (s->check_type == XZ_CHECK_CRC32)
s->crc = xz_crc32(b->out + s->out_start,
b->out_pos - s->out_start, s->crc);
#ifdef XZ_USE_CRC64
else if (s->check_type == XZ_CHECK_CRC64)
s->crc = xz_crc64(b->out + s->out_start,
b->out_pos - s->out_start, s->crc);
#endif
if (ret == XZ_STREAM_END) {
if (s->block_header.compressed != VLI_UNKNOWN
&& s->block_header.compressed
!= s->block.compressed)
return XZ_DATA_ERROR;
if (s->block_header.uncompressed != VLI_UNKNOWN
&& s->block_header.uncompressed
!= s->block.uncompressed)
return XZ_DATA_ERROR;
s->block.hash.unpadded += s->block_header.size
+ s->block.compressed;
#ifdef XZ_DEC_ANY_CHECK
s->block.hash.unpadded += check_sizes[s->check_type];
#else
if (s->check_type == XZ_CHECK_CRC32)
s->block.hash.unpadded += 4;
#ifdef XZ_USE_CRC64
else if (s->check_type == XZ_CHECK_CRC64)
s->block.hash.unpadded += 8;
#endif
#endif
s->block.hash.uncompressed += s->block.uncompressed;
s->block.hash.crc32 = xz_crc32(
(const uint8 *)&s->block.hash,
sizeof(s->block.hash), s->block.hash.crc32);
++s->block.count;
}
return ret;
}
/* Update the Index size and the CRC32 value. */
static void index_update(struct xz_dec *s, const struct xz_buf *b)
{
size_t in_used = b->in_pos - s->in_start;
s->index.size += in_used;
s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);
}
/*
* Decode the Number of Records, Unpadded Size, and Uncompressed Size
* fields from the Index field. That is, Index Padding and CRC32 are not
* decoded by this function.
*
* This can return XZ_OK (more input needed), XZ_STREAM_END (everything
* successfully decoded), or XZ_DATA_ERROR (input is corrupt).
*/
static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
{
enum xz_ret ret;
do {
ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
if (ret != XZ_STREAM_END) {
index_update(s, b);
return ret;
}
switch (s->index.sequence) {
case SEQ_INDEX_COUNT:
s->index.count = s->vli;
/*
* Validate that the Number of Records field
* indicates the same number of Records as
* there were Blocks in the Stream.
*/
if (s->index.count != s->block.count)
return XZ_DATA_ERROR;
s->index.sequence = SEQ_INDEX_UNPADDED;
break;
case SEQ_INDEX_UNPADDED:
s->index.hash.unpadded += s->vli;
s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
break;
case SEQ_INDEX_UNCOMPRESSED:
s->index.hash.uncompressed += s->vli;
s->index.hash.crc32 = xz_crc32(
(const uint8 *)&s->index.hash,
sizeof(s->index.hash),
s->index.hash.crc32);
--s->index.count;
s->index.sequence = SEQ_INDEX_UNPADDED;
break;
}
} while (s->index.count > 0);
return XZ_STREAM_END;
}
/*
* Validate that the next four or eight input bytes match the value
* of s->crc. s->pos must be zero when starting to validate the first byte.
* The "bits" argument allows using the same code for both CRC32 and CRC64.
*/
static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b,
uint32 bits)
{
do {
if (b->in_pos == b->in_size)
return XZ_OK;
if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])
return XZ_DATA_ERROR;
s->pos += 8;
} while (s->pos < bits);
s->crc = 0;
s->pos = 0;
return XZ_STREAM_END;
}
#ifdef XZ_DEC_ANY_CHECK
/*
* Skip over the Check field when the Check ID is not supported.
* Returns true once the whole Check field has been skipped over.
*/
static xz_bool check_skip(struct xz_dec *s, struct xz_buf *b)
{
while (s->pos < check_sizes[s->check_type]) {
if (b->in_pos == b->in_size)
return xz_false;
++b->in_pos;
++s->pos;
}
s->pos = 0;
return xz_true;
}
#endif
/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
static enum xz_ret dec_stream_header(struct xz_dec *s)
{
if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
return XZ_FORMAT_ERROR;
if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
!= get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
return XZ_DATA_ERROR;
if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
return XZ_OPTIONS_ERROR;
/*
* Of integrity checks, we support none (Check ID = 0),
* CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).
* However, if XZ_DEC_ANY_CHECK is defined, we will accept other
* check types too, but then the check won't be verified and
* a warning (XZ_UNSUPPORTED_CHECK) will be given.
*/
s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
#ifdef XZ_DEC_ANY_CHECK
if (s->check_type > XZ_CHECK_MAX)
return XZ_OPTIONS_ERROR;
if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
return XZ_UNSUPPORTED_CHECK;
#else
if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
return XZ_OPTIONS_ERROR;
#endif
return XZ_OK;
}
/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
static enum xz_ret dec_stream_footer(struct xz_dec *s)
{
if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
return XZ_DATA_ERROR;
if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
return XZ_DATA_ERROR;
/*
* Validate Backward Size. Note that we never added the size of the
* Index CRC32 field to s->index.size, thus we use s->index.size / 4
* instead of s->index.size / 4 - 1.
*/
if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
return XZ_DATA_ERROR;
if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
return XZ_DATA_ERROR;
/*
* Use XZ_STREAM_END instead of XZ_OK to be more convenient
* for the caller.
*/
return XZ_STREAM_END;
}
/* Decode the Block Header and initialize the filter chain. */
static enum xz_ret dec_block_header(struct xz_dec *s)
{
enum xz_ret ret;
/*
* Validate the CRC32. We know that the temp buffer is at least
* eight bytes so this is safe.
*/
s->temp.size -= 4;
if (xz_crc32(s->temp.buf, s->temp.size, 0)
!= get_le32(s->temp.buf + s->temp.size))
return XZ_DATA_ERROR;
s->temp.pos = 2;
/*
* Catch unsupported Block Flags. We support only one or two filters
* in the chain, so we catch that with the same test.
*/
#ifdef XZ_DEC_BCJ
if (s->temp.buf[1] & 0x3E)
#else
if (s->temp.buf[1] & 0x3F)
#endif
return XZ_OPTIONS_ERROR;
/* Compressed Size */
if (s->temp.buf[1] & 0x40) {
if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
!= XZ_STREAM_END)
return XZ_DATA_ERROR;
s->block_header.compressed = s->vli;
} else {
s->block_header.compressed = VLI_UNKNOWN;
}
/* Uncompressed Size */
if (s->temp.buf[1] & 0x80) {
if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
!= XZ_STREAM_END)
return XZ_DATA_ERROR;
s->block_header.uncompressed = s->vli;
} else {
s->block_header.uncompressed = VLI_UNKNOWN;
}
#ifdef XZ_DEC_BCJ
/* If there are two filters, the first one must be a BCJ filter. */
s->bcj_active = s->temp.buf[1] & 0x01;
if (s->bcj_active) {
if (s->temp.size - s->temp.pos < 2)
return XZ_OPTIONS_ERROR;
ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
if (ret != XZ_OK)
return ret;
/*
* We don't support custom start offset,
* so Size of Properties must be zero.
*/
if (s->temp.buf[s->temp.pos++] != 0x00)
return XZ_OPTIONS_ERROR;
}
#endif
/* Valid Filter Flags always take at least two bytes. */
if (s->temp.size - s->temp.pos < 2)
return XZ_DATA_ERROR;
/* Filter ID = LZMA2 */
if (s->temp.buf[s->temp.pos++] != 0x21)
return XZ_OPTIONS_ERROR;
/* Size of Properties = 1-byte Filter Properties */
if (s->temp.buf[s->temp.pos++] != 0x01)
return XZ_OPTIONS_ERROR;
/* Filter Properties contains LZMA2 dictionary size. */
if (s->temp.size - s->temp.pos < 1)
return XZ_DATA_ERROR;
ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
if (ret != XZ_OK)
return ret;
/* The rest must be Header Padding. */
while (s->temp.pos < s->temp.size)
if (s->temp.buf[s->temp.pos++] != 0x00)
return XZ_OPTIONS_ERROR;
s->temp.pos = 0;
s->block.compressed = 0;
s->block.uncompressed = 0;
return XZ_OK;
}
static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
{
enum xz_ret ret;
/*
* Store the start position for the case when we are in the middle
* of the Index field.
*/
s->in_start = b->in_pos;
while (xz_true) {
switch (s->sequence) {
case SEQ_STREAM_HEADER:
/*
* Stream Header is copied to s->temp, and then
* decoded from there. This way if the caller
* gives us only little input at a time, we can
* still keep the Stream Header decoding code
* simple. Similar approach is used in many places
* in this file.
*/
if (!fill_temp(s, b))
return XZ_OK;
/*
* If dec_stream_header() returns
* XZ_UNSUPPORTED_CHECK, it is still possible
* to continue decoding if working in multi-call
* mode. Thus, update s->sequence before calling
* dec_stream_header().
*/
s->sequence = SEQ_BLOCK_START;
ret = dec_stream_header(s);
if (ret != XZ_OK)
return ret;
/* Fall through */
case SEQ_BLOCK_START:
/* We need one byte of input to continue. */
if (b->in_pos == b->in_size)
return XZ_OK;
/* See if this is the beginning of the Index field. */
if (b->in[b->in_pos] == 0) {
s->in_start = b->in_pos++;
s->sequence = SEQ_INDEX;
break;
}
/*
* Calculate the size of the Block Header and
* prepare to decode it.
*/
s->block_header.size
= ((uint32)b->in[b->in_pos] + 1) * 4;
s->temp.size = s->block_header.size;
s->temp.pos = 0;
s->sequence = SEQ_BLOCK_HEADER;
/* Fall through */
case SEQ_BLOCK_HEADER:
if (!fill_temp(s, b))
return XZ_OK;
ret = dec_block_header(s);
if (ret != XZ_OK)
return ret;
s->sequence = SEQ_BLOCK_UNCOMPRESS;
/* Fall through */
case SEQ_BLOCK_UNCOMPRESS:
ret = dec_block(s, b);
if (ret != XZ_STREAM_END)
return ret;
s->sequence = SEQ_BLOCK_PADDING;
/* Fall through */
case SEQ_BLOCK_PADDING:
/*
* Size of Compressed Data + Block Padding
* must be a multiple of four. We don't need
* s->block.compressed for anything else
* anymore, so we use it here to test the size
* of the Block Padding field.
*/
while (s->block.compressed & 3) {
if (b->in_pos == b->in_size)
return XZ_OK;
if (b->in[b->in_pos++] != 0)
return XZ_DATA_ERROR;
++s->block.compressed;
}
s->sequence = SEQ_BLOCK_CHECK;
/* Fall through */
case SEQ_BLOCK_CHECK:
if (s->check_type == XZ_CHECK_CRC32) {
ret = crc_validate(s, b, 32);
if (ret != XZ_STREAM_END)
return ret;
}
#ifdef XZ_USE_CRC64
else if (s->check_type == XZ_CHECK_CRC64) {
ret = crc_validate(s, b, 64);
if (ret != XZ_STREAM_END)
return ret;
}
#endif
#ifdef XZ_DEC_ANY_CHECK
else if (!check_skip(s, b)) {
return XZ_OK;
}
#endif
s->sequence = SEQ_BLOCK_START;
break;
case SEQ_INDEX:
ret = dec_index(s, b);
if (ret != XZ_STREAM_END)
return ret;
s->sequence = SEQ_INDEX_PADDING;
/* Fall through */
case SEQ_INDEX_PADDING:
while ((s->index.size + (b->in_pos - s->in_start))
& 3) {
if (b->in_pos == b->in_size) {
index_update(s, b);
return XZ_OK;
}
if (b->in[b->in_pos++] != 0)
return XZ_DATA_ERROR;
}
/* Finish the CRC32 value and Index size. */
index_update(s, b);
/* Compare the hashes to validate the Index field. */
if (!memeq(&s->block.hash, &s->index.hash,
sizeof(s->block.hash)))
return XZ_DATA_ERROR;
s->sequence = SEQ_INDEX_CRC32;
/* Fall through */
case SEQ_INDEX_CRC32:
ret = crc_validate(s, b, 32);
if (ret != XZ_STREAM_END)
return ret;
s->temp.size = STREAM_HEADER_SIZE;
s->sequence = SEQ_STREAM_FOOTER;
/* Fall through */
case SEQ_STREAM_FOOTER:
if (!fill_temp(s, b))
return XZ_OK;
return dec_stream_footer(s);
}
}
/* Never reached */
}
/*
* xz_dec_run() is a wrapper for dec_main() to handle some special cases in
* multi-call and single-call decoding.
*
* In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
* are not going to make any progress anymore. This is to prevent the caller
* from calling us infinitely when the input file is truncated or otherwise
* corrupt. Since zlib-style API allows that the caller fills the input buffer
* only when the decoder doesn't produce any new output, we have to be careful
* to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
* after the second consecutive call to xz_dec_run() that makes no progress.
*
* In single-call mode, if we couldn't decode everything and no error
* occurred, either the input is truncated or the output buffer is too small.
* Since we know that the last input byte never produces any output, we know
* that if all the input was consumed and decoding wasn't finished, the file
* must be corrupt. Otherwise the output buffer has to be too small or the
* file is corrupt in a way that decoding it produces too big output.
*
* If single-call decoding fails, we reset b->in_pos and b->out_pos back to
* their original values. This is because with some filter chains there won't
* be any valid uncompressed data in the output buffer unless the decoding
* actually succeeds (that's the price to pay of using the output buffer as
* the workspace).
*/
XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
{
size_t in_start;
size_t out_start;
enum xz_ret ret;
if (DEC_IS_SINGLE(s->mode))
xz_dec_reset(s);
in_start = b->in_pos;
out_start = b->out_pos;
ret = dec_main(s, b);
if (DEC_IS_SINGLE(s->mode)) {
if (ret == XZ_OK)
ret = b->in_pos == b->in_size
? XZ_DATA_ERROR : XZ_BUF_ERROR;
if (ret != XZ_STREAM_END) {
b->in_pos = in_start;
b->out_pos = out_start;
}
} else if (ret == XZ_OK && in_start == b->in_pos
&& out_start == b->out_pos) {
if (s->allow_buf_error)
ret = XZ_BUF_ERROR;
s->allow_buf_error = xz_true;
} else {
s->allow_buf_error = xz_false;
}
return ret;
}
XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32 dict_max)
{
struct xz_dec *s = (struct xz_dec *) kmalloc(sizeof(*s), GFP_KERNEL);
if (s == NULL)
return NULL;
s->mode = mode;
#ifdef XZ_DEC_BCJ
s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
if (s->bcj == NULL)
goto error_bcj;
#endif
s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
if (s->lzma2 == NULL)
goto error_lzma2;
xz_dec_reset(s);
return s;
error_lzma2:
#ifdef XZ_DEC_BCJ
xz_dec_bcj_end(s->bcj);
error_bcj:
#endif
kfree(s);
return NULL;
}
XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
{
s->sequence = SEQ_STREAM_HEADER;
s->allow_buf_error = xz_false;
s->pos = 0;
s->crc = 0;
memzero(&s->block, sizeof(s->block));
memzero(&s->index, sizeof(s->index));
s->temp.pos = 0;
s->temp.size = STREAM_HEADER_SIZE;
}
XZ_EXTERN void xz_dec_end(struct xz_dec *s)
{
if (s != NULL) {
xz_dec_lzma2_end(s->lzma2);
#ifdef XZ_DEC_BCJ
xz_dec_bcj_end(s->bcj);
#endif
kfree(s);
}
}

View File

@ -0,0 +1,209 @@
/*
* LZMA2 definitions
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
#ifndef XZ_LZMA2_H
#define XZ_LZMA2_H
/* Range coder constants */
#define RC_SHIFT_BITS 8
#define RC_TOP_BITS 24
#define RC_TOP_VALUE (1 << RC_TOP_BITS)
#define RC_BIT_MODEL_TOTAL_BITS 11
#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
#define RC_MOVE_BITS 5
/*
* Maximum number of position states. A position state is the lowest pb
* number of bits of the current uncompressed offset. In some places there
* are different sets of probabilities for different position states.
*/
#define POS_STATES_MAX (1 << 4)
/*
* This enum is used to track which LZMA symbols have occurred most recently
* and in which order. This information is used to predict the next symbol.
*
* Symbols:
* - Literal: One 8-bit byte
* - Match: Repeat a chunk of data at some distance
* - Long repeat: Multi-byte match at a recently seen distance
* - Short repeat: One-byte repeat at a recently seen distance
*
* The symbol names are in from STATE_oldest_older_previous. REP means
* either short or long repeated match, and NONLIT means any non-literal.
*/
enum lzma_state {
STATE_LIT_LIT,
STATE_MATCH_LIT_LIT,
STATE_REP_LIT_LIT,
STATE_SHORTREP_LIT_LIT,
STATE_MATCH_LIT,
STATE_REP_LIT,
STATE_SHORTREP_LIT,
STATE_LIT_MATCH,
STATE_LIT_LONGREP,
STATE_LIT_SHORTREP,
STATE_NONLIT_MATCH,
STATE_NONLIT_REP
};
#ifndef __cplusplus
typedef enum lzma_state lzma_state_t;
#else
typedef int lzma_state_t;
#endif
/* Total number of states */
#define STATES 12
/* The lowest 7 states indicate that the previous state was a literal. */
#define LIT_STATES 7
/* Indicate that the latest symbol was a literal. */
static inline void lzma_state_literal(lzma_state_t *state)
{
if (*state <= STATE_SHORTREP_LIT_LIT)
*state = STATE_LIT_LIT;
else if (*state <= STATE_LIT_SHORTREP)
*state -= 3;
else
*state -= 6;
}
/* Indicate that the latest symbol was a match. */
static inline void lzma_state_match(lzma_state_t *state)
{
*state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
}
/* Indicate that the latest state was a long repeated match. */
static inline void lzma_state_long_rep(lzma_state_t *state)
{
*state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
}
/* Indicate that the latest symbol was a short match. */
static inline void lzma_state_short_rep(lzma_state_t *state)
{
*state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
}
/* Test if the previous symbol was a literal. */
static inline xz_bool lzma_state_is_literal(lzma_state_t state)
{
return state < LIT_STATES;
}
/* Each literal coder is divided in three sections:
* - 0x001-0x0FF: Without match byte
* - 0x101-0x1FF: With match byte; match bit is 0
* - 0x201-0x2FF: With match byte; match bit is 1
*
* Match byte is used when the previous LZMA symbol was something else than
* a literal (that is, it was some kind of match).
*/
#define LITERAL_CODER_SIZE 0x300
/* Maximum number of literal coders */
#define LITERAL_CODERS_MAX (1 << 4)
/* Minimum length of a match is two bytes. */
#define MATCH_LEN_MIN 2
/* Match length is encoded with 4, 5, or 10 bits.
*
* Length Bits
* 2-9 4 = Choice=0 + 3 bits
* 10-17 5 = Choice=1 + Choice2=0 + 3 bits
* 18-273 10 = Choice=1 + Choice2=1 + 8 bits
*/
#define LEN_LOW_BITS 3
#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
#define LEN_MID_BITS 3
#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
#define LEN_HIGH_BITS 8
#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
/*
* Maximum length of a match is 273 which is a result of the encoding
* described above.
*/
#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
/*
* Different sets of probabilities are used for match distances that have
* very short match length: Lengths of 2, 3, and 4 bytes have a separate
* set of probabilities for each length. The matches with longer length
* use a shared set of probabilities.
*/
#define DIST_STATES 4
/*
* Get the index of the appropriate probability array for decoding
* the distance slot.
*/
static inline uint32 lzma_get_dist_state(uint32 len)
{
return len < DIST_STATES + MATCH_LEN_MIN
? len - MATCH_LEN_MIN : DIST_STATES - 1;
}
/*
* The highest two bits of a 32-bit match distance are encoded using six bits.
* This six-bit value is called a distance slot. This way encoding a 32-bit
* value takes 6-36 bits, larger values taking more bits.
*/
#define DIST_SLOT_BITS 6
#define DIST_SLOTS (1 << DIST_SLOT_BITS)
/* Match distances up to 127 are fully encoded using probabilities. Since
* the highest two bits (distance slot) are always encoded using six bits,
* the distances 0-3 don't need any additional bits to encode, since the
* distance slot itself is the same as the actual distance. DIST_MODEL_START
* indicates the first distance slot where at least one additional bit is
* needed.
*/
#define DIST_MODEL_START 4
/*
* Match distances greater than 127 are encoded in three pieces:
* - distance slot: the highest two bits
* - direct bits: 2-26 bits below the highest two bits
* - alignment bits: four lowest bits
*
* Direct bits don't use any probabilities.
*
* The distance slot value of 14 is for distances 128-191.
*/
#define DIST_MODEL_END 14
/* Distance slots that indicate a distance <= 127. */
#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
/*
* For match distances greater than 127, only the highest two bits and the
* lowest four bits (alignment) is encoded using probabilities.
*/
#define ALIGN_BITS 4
#define ALIGN_SIZE (1 << ALIGN_BITS)
#define ALIGN_MASK (ALIGN_SIZE - 1)
/* Total number of all probability variables */
#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
/*
* LZMA remembers the four most recent match distances. Reusing these
* distances tends to take less space than re-encoding the actual
* distance value.
*/
#define REPS 4
#endif

View File

@ -0,0 +1,118 @@
/*
* Private includes and definitions
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
#ifndef XZ_PRIVATE_H
#define XZ_PRIVATE_H
# include "xz_config.h"
/* If no specific decoding mode is requested, enable support for all modes. */
#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
&& !defined(XZ_DEC_DYNALLOC)
# define XZ_DEC_SINGLE
# define XZ_DEC_PREALLOC
# define XZ_DEC_DYNALLOC
#endif
/*
* The DEC_IS_foo(mode) macros are used in "if" statements. If only some
* of the supported modes are enabled, these macros will evaluate to true or
* false at compile time and thus allow the compiler to omit unneeded code.
*/
#ifdef XZ_DEC_SINGLE
# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
#else
# define DEC_IS_SINGLE(mode) (xz_false)
#endif
#ifdef XZ_DEC_PREALLOC
# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
#else
# define DEC_IS_PREALLOC(mode) (xz_false)
#endif
#ifdef XZ_DEC_DYNALLOC
# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
#else
# define DEC_IS_DYNALLOC(mode) (xz_false)
#endif
#if !defined(XZ_DEC_SINGLE)
# define DEC_IS_MULTI(mode) (xz_true)
#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
#else
# define DEC_IS_MULTI(mode) (xz_false)
#endif
/*
* If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
* XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
*/
#ifndef XZ_DEC_BCJ
# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
|| defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
|| defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
|| defined(XZ_DEC_SPARC)
# define XZ_DEC_BCJ
# endif
#endif
/*
* Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
* before calling xz_dec_lzma2_run().
*/
XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
uint32 dict_max);
/*
* Decode the LZMA2 properties (one byte) and reset the decoder. Return
* XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
* big enough, and XZ_OPTIONS_ERROR if props indicates something that this
* decoder doesn't support.
*/
XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
uint8 props);
/* Decode raw LZMA2 stream from b->in to b->out. */
XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
struct xz_buf *b);
/* Free the memory allocated for the LZMA2 decoder. */
XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
#ifdef XZ_DEC_BCJ
/*
* Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
* calling xz_dec_bcj_run().
*/
XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(xz_bool single_call);
/*
* Decode the Filter ID of a BCJ filter. This implementation doesn't
* support custom start offsets, so no decoding of Filter Properties
* is needed. Returns XZ_OK if the given Filter ID is supported.
* Otherwise XZ_OPTIONS_ERROR is returned.
*/
XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8 id);
/*
* Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
* a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
* must be called directly.
*/
XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
struct xz_dec_lzma2 *lzma2,
struct xz_buf *b);
/* Free the memory allocated for the BCJ filters. */
#define xz_dec_bcj_end(s) kfree(s)
#endif
#endif

View File

@ -0,0 +1,55 @@
/*
* Definitions for handling the .xz file format
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
#ifndef XZ_STREAM_H
#define XZ_STREAM_H
/*
* See the .xz file format specification at
* https://tukaani.org/xz/xz-file-format.txt
* to understand the container format.
*/
#define STREAM_HEADER_SIZE 12
#define HEADER_MAGIC "\3757zXZ"
#define HEADER_MAGIC_SIZE 6
#define FOOTER_MAGIC "YZ"
#define FOOTER_MAGIC_SIZE 2
/*
* Variable-length integer can hold a 63-bit unsigned integer or a special
* value indicating that the value is unknown.
*
* Experimental: vli_type can be defined to uint32_t to save a few bytes
* in code size (no effect on speed). Doing so limits the uncompressed and
* compressed size of the file to less than 256 MiB and may also weaken
* error detection slightly.
*/
typedef uint64 vli_type;
#define VLI_MAX ((vli_type)-1 / 2)
#define VLI_UNKNOWN ((vli_type)-1)
/* Maximum encoded size of a VLI */
#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
/* Integrity Check types */
enum xz_check {
XZ_CHECK_NONE = 0,
XZ_CHECK_CRC32 = 1,
XZ_CHECK_CRC64 = 4,
XZ_CHECK_SHA256 = 10
};
/* Maximum possible Check ID */
#define XZ_CHECK_MAX 15
#endif

1133
libxmp/src/effects.c Normal file

File diff suppressed because it is too large Load Diff

163
libxmp/src/effects.h Normal file
View File

@ -0,0 +1,163 @@
#ifndef LIBXMP_EFFECTS_H
#define LIBXMP_EFFECTS_H
/* Protracker effects */
#define FX_ARPEGGIO 0x00
#define FX_PORTA_UP 0x01
#define FX_PORTA_DN 0x02
#define FX_TONEPORTA 0x03
#define FX_VIBRATO 0x04
#define FX_TONE_VSLIDE 0x05
#define FX_VIBRA_VSLIDE 0x06
#define FX_TREMOLO 0x07
#define FX_OFFSET 0x09
#define FX_VOLSLIDE 0x0a
#define FX_JUMP 0x0b
#define FX_VOLSET 0x0c
#define FX_BREAK 0x0d
#define FX_EXTENDED 0x0e
#define FX_SPEED 0x0f
/* Fast tracker effects */
#define FX_SETPAN 0x08
/* Fast Tracker II effects */
#define FX_GLOBALVOL 0x10
#define FX_GVOL_SLIDE 0x11
#define FX_KEYOFF 0x14
#define FX_ENVPOS 0x15
#define FX_PANSLIDE 0x19
#define FX_MULTI_RETRIG 0x1b
#define FX_TREMOR 0x1d
#define FX_XF_PORTA 0x21
/* Protracker extended effects */
#define EX_FILTER 0x00
#define EX_F_PORTA_UP 0x01
#define EX_F_PORTA_DN 0x02
#define EX_GLISS 0x03
#define EX_VIBRATO_WF 0x04
#define EX_FINETUNE 0x05
#define EX_PATTERN_LOOP 0x06
#define EX_TREMOLO_WF 0x07
#define EX_SETPAN 0x08
#define EX_RETRIG 0x09
#define EX_F_VSLIDE_UP 0x0a
#define EX_F_VSLIDE_DN 0x0b
#define EX_CUT 0x0c
#define EX_DELAY 0x0d
#define EX_PATT_DELAY 0x0e
#define EX_INVLOOP 0x0f
#ifndef LIBXMP_CORE_PLAYER
/* Oktalyzer effects */
#define FX_OKT_ARP3 0x70
#define FX_OKT_ARP4 0x71
#define FX_OKT_ARP5 0x72
#define FX_NSLIDE2_DN 0x73
#define FX_NSLIDE2_UP 0x74
#define FX_F_NSLIDE_DN 0x75
#define FX_F_NSLIDE_UP 0x76
/* Persistent effects -- for FNK */
#define FX_PER_PORTA_DN 0x78
#define FX_PER_PORTA_UP 0x79
#define FX_PER_TPORTA 0x7a
#define FX_PER_VIBRATO 0x7b
#define FX_PER_VSLD_UP 0x7c
#define FX_PER_VSLD_DN 0x7d
#define FX_SPEED_CP 0x7e
#define FX_PER_CANCEL 0x7f
/* 669 frequency based effects */
#define FX_669_PORTA_UP 0x60
#define FX_669_PORTA_DN 0x61
#define FX_669_TPORTA 0x62
#define FX_669_FINETUNE 0x63
#define FX_669_VIBRATO 0x64
/* FAR effects */
#define FX_FAR_PORTA_UP 0x65 /* FAR pitch offset up */
#define FX_FAR_PORTA_DN 0x66 /* FAR pitch offset down */
#define FX_FAR_TPORTA 0x67 /* FAR persistent tone portamento */
#define FX_FAR_TEMPO 0x68 /* FAR coarse tempo and tempo mode */
#define FX_FAR_F_TEMPO 0x69 /* FAR fine tempo slide up/down */
#define FX_FAR_VIBDEPTH 0x6a /* FAR set vibrato depth */
#define FX_FAR_VIBRATO 0x6b /* FAR persistent vibrato */
#define FX_FAR_SLIDEVOL 0x6c /* FAR persistent slide-to-volume */
#define FX_FAR_RETRIG 0x6d /* FAR retrigger */
#define FX_FAR_DELAY 0x6e /* FAR note offset */
/* Other frequency based effects (ULT, etc) */
#define FX_ULT_TPORTA 0x6f
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
/* IT effects */
#define FX_TRK_VOL 0x80
#define FX_TRK_VSLIDE 0x81
#define FX_TRK_FVSLIDE 0x82
#define FX_IT_INSTFUNC 0x83
#define FX_FLT_CUTOFF 0x84
#define FX_FLT_RESN 0x85
#define FX_IT_BPM 0x87
#define FX_IT_ROWDELAY 0x88
#define FX_IT_PANSLIDE 0x89
#define FX_PANBRELLO 0x8a
#define FX_PANBRELLO_WF 0x8b
#define FX_HIOFFSET 0x8c
#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */
#define FX_MACRO_SET 0xbd /* Set active IT parametered MIDI macro */
#define FX_MACRO 0xbe /* Execute IT MIDI macro */
#define FX_MACROSMOOTH 0xbf /* Execute IT MIDI macro slide */
#endif
#ifndef LIBXMP_CORE_PLAYER
/* MED effects */
#define FX_HOLD_DECAY 0x90
#define FX_SETPITCH 0x91
#define FX_VIBRATO2 0x92
/* PTM effects */
#define FX_NSLIDE_DN 0x9c /* IMF/PTM note slide down */
#define FX_NSLIDE_UP 0x9d /* IMF/PTM note slide up */
#define FX_NSLIDE_R_UP 0x9e /* PTM note slide down with retrigger */
#define FX_NSLIDE_R_DN 0x9f /* PTM note slide up with retrigger */
/* Extra effects */
#define FX_VOLSLIDE_UP 0xa0 /* SFX, MDL */
#define FX_VOLSLIDE_DN 0xa1
#define FX_F_VSLIDE 0xa5 /* IMF/MDL */
#define FX_CHORUS 0xa9 /* IMF */
#define FX_ICE_SPEED 0xa2
#define FX_REVERB 0xaa /* IMF */
#define FX_MED_HOLD 0xb1 /* MMD hold/decay */
#define FX_MEGAARP 0xb2 /* Smaksak effect 7: MegaArp */
#define FX_VOL_ADD 0xb6 /* SFX change volume up */
#define FX_VOL_SUB 0xb7 /* SFX change volume down */
#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */
#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */
#define FX_LINE_JUMP 0xba /* Archimedes jump to line in current order */
#endif
#define FX_SURROUND 0x8d /* S3M/IT */
#define FX_REVERSE 0x8f /* XM/IT/others: play forward/reverse */
#define FX_S3M_SPEED 0xa3 /* S3M */
#define FX_VOLSLIDE_2 0xa4
#define FX_FINETUNE 0xa6
#define FX_S3M_BPM 0xab /* S3M */
#define FX_FINE_VIBRATO 0xac /* S3M/PTM/IMF/LIQ */
#define FX_F_VSLIDE_UP 0xad /* MMD */
#define FX_F_VSLIDE_DN 0xae /* MMD */
#define FX_F_PORTA_UP 0xaf /* MMD */
#define FX_F_PORTA_DN 0xb0 /* MMD */
#define FX_PATT_DELAY 0xb3 /* MMD */
#define FX_S3M_ARPEGGIO 0xb4
#define FX_PANSL_NOMEM 0xb5 /* XM volume column */
#define FX_VSLIDE_UP_2 0xc0 /* IT volume column volume slide */
#define FX_VSLIDE_DN_2 0xc1
#define FX_F_VSLIDE_UP_2 0xc2
#define FX_F_VSLIDE_DN_2 0xc3
#endif /* LIBXMP_EFFECTS_H */

163
libxmp/src/extras.c Normal file
View File

@ -0,0 +1,163 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "common.h"
#include "player.h"
#include "extras.h"
#include "med_extras.h"
#include "hmn_extras.h"
#include "far_extras.h"
/*
* Module extras
*/
void libxmp_release_module_extras(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
if (HAS_MED_MODULE_EXTRAS(*m))
libxmp_med_release_module_extras(m);
else if (HAS_HMN_MODULE_EXTRAS(*m))
libxmp_hmn_release_module_extras(m);
else if (HAS_FAR_MODULE_EXTRAS(*m))
libxmp_far_release_module_extras(m);
}
/*
* Channel extras
*/
int libxmp_new_channel_extras(struct context_data *ctx, struct channel_data *xc)
{
struct module_data *m = &ctx->m;
if (HAS_MED_MODULE_EXTRAS(*m)) {
if (libxmp_med_new_channel_extras(xc) < 0)
return -1;
} else if (HAS_HMN_MODULE_EXTRAS(*m)) {
if (libxmp_hmn_new_channel_extras(xc) < 0)
return -1;
} else if (HAS_FAR_MODULE_EXTRAS(*m)) {
if (libxmp_far_new_channel_extras(xc) < 0)
return -1;
}
return 0;
}
void libxmp_release_channel_extras(struct context_data *ctx, struct channel_data *xc)
{
struct module_data *m = &ctx->m;
if (HAS_MED_CHANNEL_EXTRAS(*m))
libxmp_med_release_channel_extras(xc);
else if (HAS_HMN_CHANNEL_EXTRAS(*m))
libxmp_hmn_release_channel_extras(xc);
else if (HAS_FAR_CHANNEL_EXTRAS(*m))
libxmp_far_release_channel_extras(xc);
}
void libxmp_reset_channel_extras(struct context_data *ctx, struct channel_data *xc)
{
struct module_data *m = &ctx->m;
if (HAS_MED_CHANNEL_EXTRAS(*m))
libxmp_med_reset_channel_extras(xc);
else if (HAS_HMN_CHANNEL_EXTRAS(*m))
libxmp_hmn_reset_channel_extras(xc);
else if (HAS_FAR_CHANNEL_EXTRAS(*m))
libxmp_far_reset_channel_extras(xc);
}
/*
* Player extras
*/
void libxmp_play_extras(struct context_data *ctx, struct channel_data *xc, int chn)
{
struct module_data *m = &ctx->m;
if (HAS_FAR_CHANNEL_EXTRAS(*xc))
libxmp_far_play_extras(ctx, xc, chn);
if (xc->ins >= m->mod.ins) /* SFX instruments have no extras */
return;
if (HAS_MED_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins]))
libxmp_med_play_extras(ctx, xc, chn);
else if (HAS_HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins]))
libxmp_hmn_play_extras(ctx, xc, chn);
}
int libxmp_extras_get_volume(struct context_data *ctx, struct channel_data *xc)
{
struct module_data *m = &ctx->m;
int vol;
if (xc->ins >= m->mod.ins)
vol = xc->volume;
else if (HAS_MED_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins]))
vol = MED_CHANNEL_EXTRAS(*xc)->volume * xc->volume / 64;
else if (HAS_HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins]))
vol = HMN_CHANNEL_EXTRAS(*xc)->volume * xc->volume / 64;
else
vol = xc->volume;
return vol;
}
int libxmp_extras_get_period(struct context_data *ctx, struct channel_data *xc)
{
int period;
if (HAS_MED_CHANNEL_EXTRAS(*xc))
period = libxmp_med_change_period(ctx, xc);
else period = 0;
return period;
}
int libxmp_extras_get_linear_bend(struct context_data *ctx, struct channel_data *xc)
{
int linear_bend;
if (HAS_MED_CHANNEL_EXTRAS(*xc))
linear_bend = libxmp_med_linear_bend(ctx, xc);
else if (HAS_HMN_CHANNEL_EXTRAS(*xc))
linear_bend = libxmp_hmn_linear_bend(ctx, xc);
else
linear_bend = 0;
return linear_bend;
}
void libxmp_extras_process_fx(struct context_data *ctx, struct channel_data *xc,
int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum)
{
if (HAS_MED_CHANNEL_EXTRAS(*xc))
libxmp_med_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum);
else if (HAS_HMN_CHANNEL_EXTRAS(*xc))
libxmp_hmn_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum);
else if (HAS_FAR_CHANNEL_EXTRAS(*xc))
libxmp_far_extras_process_fx(ctx, xc, chn, note, fxt, fxp, fnum);
}

18
libxmp/src/extras.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef LIBXMP_EXTRAS_H
#define LIBXMP_EXTRAS_H
void libxmp_release_module_extras(struct context_data *);
int libxmp_new_channel_extras(struct context_data *, struct channel_data *);
void libxmp_release_channel_extras(struct context_data *, struct channel_data *);
void libxmp_reset_channel_extras(struct context_data *, struct channel_data *);
void libxmp_play_extras(struct context_data *, struct channel_data *, int);
int libxmp_extras_get_volume(struct context_data *, struct channel_data *);
int libxmp_extras_get_period(struct context_data *, struct channel_data *);
int libxmp_extras_get_linear_bend(struct context_data *, struct channel_data *);
void libxmp_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int);
/* FIXME */
void libxmp_med_hold_hack(struct context_data *ctx, int, int, int);
#endif

405
libxmp/src/far_extras.c Normal file
View File

@ -0,0 +1,405 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "common.h"
#include "player.h"
#include "lfo.h"
#include "effects.h"
#include "period.h"
#include "far_extras.h"
#define FAR_GUS_CHANNELS 17
#define FAR_OLD_TEMPO_SHIFT 2 /* Power of multiplier for old tempo mode. */
/**
* The time factor needed to directly use FAR tempos is a little unintuitive.
*
* Generally: FAR tries to run 32/[coarse tempo] rows per second, which
* (usually, but not always) are subdivided into 4 "ticks". To achieve
* this, it measures tempos in the number of ticks that should play per second
* (see far_tempos below). Fine tempo is added or subtracted from this number.
* To time these ticks, FAR uses the programmable interval timer (PIT) to run a
* player interrupt.
*
* libxmp effectively uses a calculation of 10.0 * 0.25 / BPM to get the tick
* duration in seconds. A base time factor of 4.0 makes this 1 / BPM, turning
* BPM into the ticks/sec measure that FAR uses. This isn't completely
* accurate to FAR, though.
*
* The x86 PIT runs at a rate of 1193182 Hz, but FAR does something strange
* when calculating PIT divisors and uses a constant of 1197255 Hz instead.
* This means FAR tempo is slightly slower by a factor of around:
*
* floor(1197255 / 32) / floor(1193182 / 32) ~= 1.003439
*
* This still isn't perfect, but it gets the playback rate fairly close.
*/
/* tempo[0] = 256; tempo[i] = floor(128 / i). */
static const int far_tempos[16] =
{
256, 128, 64, 42, 32, 25, 21, 18, 16, 14, 12, 11, 10, 9, 9, 8
};
/**
* FAR tempo has some unusual requirements that don't really match any other
* format:
*
* 1) The coarse tempo is roughly equivalent to speed, but a value of 0 is
* supported, and FAR doesn't actually have a concept of ticks: it translates
* this value to tempo.
*
* 2) There is some very bizarre clamping behavior involving fine tempo slides
* that needs to be emulated.
*
* 3) Tempos can range from 1 to 356(!). FAR uses a fixed row subdivision size
* of 16, so just shift the tempo by 4 and hope libxmp doesn't change it.
*
* 4) There are two tempo modes, and they can be switched between arbitrarily...
*/
int libxmp_far_translate_tempo(int mode, int fine_change, int coarse,
int *fine, int *_speed, int *_bpm)
{
int speed, bpm;
if (coarse < 0 || coarse > 15 || mode < 0 || mode > 1)
return -1;
/* Compatibility for FAR's broken fine tempo "clamping". */
if (fine_change < 0 && far_tempos[coarse] + *fine <= 0) {
*fine = 0;
} else if (fine_change > 0 && far_tempos[coarse] + *fine >= 100) {
*fine = 100;
}
if (mode == 1) {
/* "New" FAR tempo
* Note that negative values are possible in Farandole Composer
* via changing fine tempo and then slowing coarse tempo.
* These result in very slow final tempos due to signed to
* unsigned conversion. Zero should just be ignored entirely. */
int tempo = far_tempos[coarse] + *fine;
uint32 divisor;
if (tempo == 0)
return -1;
divisor = 1197255 / tempo;
/* Coincidentally(?), the "new" FAR tempo algorithm actually
* prevents the BPM from dropping too far under XMP_MIN_BPM,
* which is what libxmp needs anyway. */
speed = 0;
while (divisor > 0xffff) {
divisor >>= 1;
tempo <<= 1;
speed++;
}
if (speed >= 2)
speed++;
speed += 3;
/* Add an extra tick because the FAR replayer checks the tick
* remaining count before decrementing it but after handling
* each tick, i.e. a count of "3" executes 4 ticks. */
speed++;
bpm = tempo;
} else {
/* "Old" FAR tempo
* This runs into the XMP_MIN_BPM limit, but nothing uses it anyway.
* Old tempo mode in the original FAR replayer has 32 ticks,
* but ignores all except every 8th. */
speed = 4 << FAR_OLD_TEMPO_SHIFT;
bpm = (far_tempos[coarse] + *fine * 2) << FAR_OLD_TEMPO_SHIFT;
}
if (bpm < XMP_MIN_BPM)
bpm = XMP_MIN_BPM;
*_speed = speed;
*_bpm = bpm;
return 0;
}
static void libxmp_far_update_tempo(struct context_data *ctx, int fine_change)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct far_module_extras *me = (struct far_module_extras *)m->extra;
int speed, bpm;
if (libxmp_far_translate_tempo(me->tempo_mode, fine_change,
me->coarse_tempo, &me->fine_tempo, &speed, &bpm) == 0) {
p->speed = speed;
p->bpm = bpm;
p->frame_time = m->time_factor * m->rrate / p->bpm;
}
}
static void libxmp_far_update_vibrato(struct lfo *lfo, int rate, int depth)
{
libxmp_lfo_set_depth(lfo, libxmp_gus_frequency_steps(depth << 1, FAR_GUS_CHANNELS));
libxmp_lfo_set_rate(lfo, rate * 3);
}
/* Convoluted algorithm for delay times for retrigger and note offset effects. */
static int libxmp_far_retrigger_delay(struct far_module_extras *me, int param)
{
int delay;
if (me->coarse_tempo < 0 || me->coarse_tempo > 15 || param < 1)
return -1;
delay = (far_tempos[me->coarse_tempo] + me->fine_tempo) / param;
if (me->tempo_mode) {
/* Effects divide by 4, timer increments by 2 (round up). */
return ((delay >> 2) + 1) >> 1;
} else {
/* Effects divide by 2, timer increments by 2 (round up).
* Old tempo mode handles every 8th tick (<< FAR_OLD_TEMPO_SHIFT).
* Delay values >4 result in no retrigger. */
delay = (((delay >> 1) + 1) >> 1) << FAR_OLD_TEMPO_SHIFT;
if (delay >= 16)
return -1;
if (delay < (1 << FAR_OLD_TEMPO_SHIFT))
return (1 << FAR_OLD_TEMPO_SHIFT);
return delay;
}
}
void libxmp_far_play_extras(struct context_data *ctx, struct channel_data *xc, int chn)
{
struct far_module_extras *me = FAR_MODULE_EXTRAS(ctx->m);
struct far_channel_extras *ce = FAR_CHANNEL_EXTRAS(*xc);
/* FAR vibrato depth is global, even though rate isn't. This might have
* been changed by a different channel, so make sure it's applied. */
if (TEST(VIBRATO) || TEST_PER(VIBRATO))
libxmp_far_update_vibrato(&xc->vibrato.lfo, ce->vib_rate, me->vib_depth);
}
int libxmp_far_new_channel_extras(struct channel_data *xc)
{
xc->extra = calloc(1, sizeof(struct far_channel_extras));
if (xc->extra == NULL)
return -1;
FAR_CHANNEL_EXTRAS(*xc)->magic = FAR_EXTRAS_MAGIC;
return 0;
}
void libxmp_far_reset_channel_extras(struct channel_data *xc)
{
memset((char *)xc->extra + 4, 0, sizeof(struct far_channel_extras) - 4);
}
void libxmp_far_release_channel_extras(struct channel_data *xc)
{
free(xc->extra);
xc->extra = NULL;
}
int libxmp_far_new_module_extras(struct module_data *m)
{
m->extra = calloc(1, sizeof(struct far_module_extras));
if (m->extra == NULL)
return -1;
FAR_MODULE_EXTRAS(*m)->magic = FAR_EXTRAS_MAGIC;
FAR_MODULE_EXTRAS(*m)->vib_depth = 4;
return 0;
}
void libxmp_far_release_module_extras(struct module_data *m)
{
free(m->extra);
m->extra = NULL;
}
void libxmp_far_extras_process_fx(struct context_data *ctx, struct channel_data *xc,
int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum)
{
struct xmp_module *mod = &ctx->m.mod;
struct far_module_extras *me = FAR_MODULE_EXTRAS(ctx->m);
struct far_channel_extras *ce = FAR_CHANNEL_EXTRAS(*xc);
int update_tempo = 0;
int update_vibrato = 0;
int fine_change = 0;
int delay, target, tempo;
int32 diff, step;
/* Tempo effects and vibrato are multiplexed to reduce the effects count.
*
* Misc. notes: FAR pitch offset effects can overflow/underflow GUS
* frequency, which isn't supported by libxmp (Haj/before.far).
*/
switch (fxt) {
case FX_FAR_PORTA_UP: /* FAR pitch offset up */
SET(FINE_BEND);
RESET_PER(TONEPORTA);
xc->freq.fslide = libxmp_gus_frequency_steps(fxp << 2, FAR_GUS_CHANNELS);
break;
case FX_FAR_PORTA_DN: /* FAR pitch offset down */
SET(FINE_BEND);
RESET_PER(TONEPORTA);
xc->freq.fslide = -libxmp_gus_frequency_steps(fxp << 2, FAR_GUS_CHANNELS);
break;
/* Despite some claims, this effect scales with tempo and only
* corresponds to (param) rows at tempo 4. See FORMATS.DOC.
*/
case FX_FAR_TPORTA: /* FAR persistent tone portamento */
if (!IS_VALID_INSTRUMENT(xc->ins))
break;
tempo = far_tempos[me->coarse_tempo] + me->fine_tempo;
SET_PER(TONEPORTA);
if (IS_VALID_NOTE(note - 1)) {
xc->porta.target = libxmp_note_to_period(ctx, note - 1, xc->finetune, xc->per_adj);
}
xc->porta.dir = xc->period < xc->porta.target ? 1 : -1;
/* Parameter of 0 is equivalent to 1. */
if (fxp < 1)
fxp = 1;
/* Tempos <=0 cause crashes and other weird behavior
* here in Farandole Composer, don't emulate that. */
if (tempo < 1)
tempo = 1;
diff = xc->porta.target - xc->period;
step = (diff > 0 ? diff : -diff) * 8 / (tempo * fxp);
xc->porta.slide = (step > 0) ? step : 1;
break;
/* Despite some claims, this effect scales with tempo and only
* corresponds to (param/2) rows at tempo 4. See FORMATS.DOC.
*/
case FX_FAR_SLIDEVOL: /* FAR persistent slide-to-volume */
tempo = far_tempos[me->coarse_tempo] + me->fine_tempo;
target = MSN(fxp) << 4;
fxp = LSN(fxp);
/* Parameter of 0 is equivalent to 1. */
if (fxp < 1)
fxp = 1;
/* Tempos <=0 cause crashes and other weird behavior
* here in Farandole Composer, don't emulate that. */
if (tempo < 1)
tempo = 1;
diff = target - xc->volume;
step = diff * 16 / (tempo * fxp);
if (step == 0)
step = (diff > 0) ? 1 : -1;
SET_PER(VOL_SLIDE);
xc->vol.slide = step;
xc->vol.target = target + 1;
break;
case FX_FAR_VIBDEPTH: /* FAR set vibrato depth */
me->vib_depth = LSN(fxp);
update_vibrato = 1;
break;
case FX_FAR_VIBRATO: /* FAR vibrato and sustained vibrato */
if (ce->vib_sustain == 0) {
/* With sustain, regular vibrato only sets the rate. */
ce->vib_sustain = MSN(fxp);
if (ce->vib_sustain == 0)
SET(VIBRATO);
}
ce->vib_rate = LSN(fxp);
update_vibrato = 1;
break;
/* Retrigger note param times at intervals that roughly evently
* divide the row. A param of 0 crashes Farandole Composer.
*/
case FX_FAR_RETRIG: /* FAR retrigger */
delay = libxmp_far_retrigger_delay(me, fxp);
if (note && fxp > 1 && delay >= 0 && delay <= ctx->p.speed) {
SET(RETRIG);
xc->retrig.val = delay ? delay : 1;
xc->retrig.count = delay + 1;
xc->retrig.type = 0;
xc->retrig.limit = fxp - 1;
}
break;
/* A better effect name would probably be "retrigger once".
* The description/intent seems to be that this is a delay
* effect, but an initial note always plays as well. The second
* note always plays on the (param)th tick due to player quirks,
* but it's supposed to be derived similar to retrigger.
* A param of zero works like effect 4F (bug?).
*/
case FX_FAR_DELAY: /* FAR note offset */
if (note) {
delay = me->tempo_mode ? fxp : fxp << FAR_OLD_TEMPO_SHIFT;
SET(RETRIG);
xc->retrig.val = delay ? delay : 1;
xc->retrig.count = delay + 1;
xc->retrig.type = 0;
xc->retrig.limit = fxp ? 1 : 0;
}
break;
case FX_FAR_TEMPO: /* FAR coarse tempo and tempo mode */
if (MSN(fxp)) {
me->tempo_mode = MSN(fxp) - 1;
} else {
me->coarse_tempo = LSN(fxp);
}
update_tempo = 1;
break;
case FX_FAR_F_TEMPO: /* FAR fine tempo slide up/down */
if (MSN(fxp)) {
me->fine_tempo += MSN(fxp);
fine_change = MSN(fxp);
} else if (LSN(fxp)) {
me->fine_tempo -= LSN(fxp);
fine_change = -LSN(fxp);
} else {
me->fine_tempo = 0;
}
update_tempo = 1;
break;
}
if (update_vibrato) {
if (ce->vib_rate != 0) {
if (ce->vib_sustain)
SET_PER(VIBRATO);
} else {
RESET_PER(VIBRATO);
ce->vib_sustain = 0;
}
libxmp_far_update_vibrato(&xc->vibrato.lfo, ce->vib_rate, me->vib_depth);
}
if (update_tempo)
libxmp_far_update_tempo(ctx, fine_change);
}

55
libxmp/src/far_extras.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef XMP_FAR_EXTRAS_H
#define XMP_FAR_EXTRAS_H
#include "common.h"
#define FAR_EXTRAS_MAGIC 0x7b12a83f
/*
struct far_instrument_extras {
uint32 magic;
};
*/
struct far_channel_extras {
uint32 magic;
int vib_sustain; /* Is vibrato persistent? */
int vib_rate; /* Vibrato rate. */
};
struct far_module_extras {
uint32 magic;
int coarse_tempo;
int fine_tempo;
int tempo_mode;
int vib_depth; /* Vibrato depth for all channels. */
};
/*
#define FAR_INSTRUMENT_EXTRAS(x) ((struct far_instrument_extras *)(x).extra)
#define HAS_FAR_INSTRUMENT_EXTRAS(x) \
(FAR_INSTRUMENT_EXTRAS(x) != NULL && \
FAR_INSTRUMENT_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC)
*/
#define FAR_CHANNEL_EXTRAS(x) ((struct far_channel_extras *)(x).extra)
#define HAS_FAR_CHANNEL_EXTRAS(x) \
(FAR_CHANNEL_EXTRAS(x) != NULL && \
FAR_CHANNEL_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC)
#define FAR_MODULE_EXTRAS(x) ((struct far_module_extras *)(x).extra)
#define HAS_FAR_MODULE_EXTRAS(x) \
(FAR_MODULE_EXTRAS(x) != NULL && \
FAR_MODULE_EXTRAS(x)->magic == FAR_EXTRAS_MAGIC)
int libxmp_far_translate_tempo(int, int, int, int *, int *, int *);
void libxmp_far_play_extras(struct context_data *, struct channel_data *, int);
int libxmp_far_linear_bend(struct context_data *, struct channel_data *);
int libxmp_far_new_channel_extras(struct channel_data *);
void libxmp_far_reset_channel_extras(struct channel_data *);
void libxmp_far_release_channel_extras(struct channel_data *);
int libxmp_far_new_module_extras(struct module_data *);
void libxmp_far_release_module_extras(struct module_data *);
void libxmp_far_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int);
#endif

145
libxmp/src/filetype.c Normal file
View File

@ -0,0 +1,145 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "common.h"
#include <errno.h>
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
int libxmp_get_filetype (const char *path)
{
DWORD result = GetFileAttributesA(path);
if (result == (DWORD)(-1)) {
errno = ENOENT;
return XMP_FILETYPE_NONE;
}
return (result & FILE_ATTRIBUTE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE;
}
#elif defined(__OS2__) || defined(__EMX__)
#define INCL_DOSFILEMGR
#include <os2.h>
int libxmp_get_filetype (const char *path)
{
FILESTATUS3 fs;
if (DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) != 0) {
errno = ENOENT;
return XMP_FILETYPE_NONE;
}
return (fs.attrFile & FILE_DIRECTORY) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE;
}
#elif defined(__DJGPP__)
#include <dos.h>
#include <io.h>
int libxmp_get_filetype (const char *path)
{
int attr = _chmod(path, 0);
/* Root directories on some non-local drives (e.g. CD-ROM), as well as
* devices may fail _chmod, but we are not interested in such cases. */
if (attr < 0) return XMP_FILETYPE_NONE;
/* we shouldn't hit _A_VOLID ! */
return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE;
}
#elif defined(__WATCOMC__) && defined(_DOS)
#include <dos.h>
#include <direct.h>
int libxmp_get_filetype (const char *path)
{
unsigned int attr;
if (_dos_getfileattr(path, &attr)) return XMP_FILETYPE_NONE;
return (attr & (_A_SUBDIR|_A_VOLID)) ? XMP_FILETYPE_DIR : XMP_FILETYPE_FILE;
}
#elif defined(__amigaos4__)
#define __USE_INLINE__
#include <proto/dos.h>
int libxmp_get_filetype (const char *path)
{
int typ = XMP_FILETYPE_NONE;
struct ExamineData *data = ExamineObjectTags(EX_StringNameInput, path, TAG_END);
if (data) {
if (EXD_IS_FILE(data)) {
typ = XMP_FILETYPE_FILE;
} else
if (EXD_IS_DIRECTORY(data)) {
typ = XMP_FILETYPE_DIR;
}
FreeDosObject(DOS_EXAMINEDATA, data);
}
if (typ == XMP_FILETYPE_NONE) errno = ENOENT;
return typ;
}
#elif defined(LIBXMP_AMIGA)
#include <proto/dos.h>
int libxmp_get_filetype (const char *path)
{
int typ = XMP_FILETYPE_NONE;
BPTR lock = Lock((const STRPTR)path, ACCESS_READ);
if (lock) {
struct FileInfoBlock *fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB,NULL);
if (fib) {
if (Examine(lock, fib)) {
typ = (fib->fib_DirEntryType < 0) ? XMP_FILETYPE_FILE : XMP_FILETYPE_DIR;
}
FreeDosObject(DOS_FIB, fib);
}
UnLock(lock);
}
if (typ == XMP_FILETYPE_NONE) errno = ENOENT;
return typ;
}
#else /* unix (ish): */
#include <sys/types.h>
#include <sys/stat.h>
int libxmp_get_filetype (const char *path)
{
struct stat st;
memset(&st, 0, sizeof(st)); /* silence sanitizers.. */
if (stat(path, &st) < 0) return XMP_FILETYPE_NONE;
if (S_ISDIR(st.st_mode)) return XMP_FILETYPE_DIR;
if (S_ISREG(st.st_mode)) return XMP_FILETYPE_FILE;
return XMP_FILETYPE_NONE;
}
#endif

109
libxmp/src/filter.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Based on the public domain version by Olivier Lapicque
* Rewritten for libxmp by Claudio Matsuoka
*
* Copyright (C) 2012 Claudio Matsuoka
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBXMP_CORE_DISABLE_IT
#include <math.h>
#include "xmp.h"
#include "common.h"
#include "mixer.h"
/* LUT for 2 * damping factor */
static const float resonance_table[128] = {
1.0000000000000000f, 0.9786446094512940f, 0.9577452540397644f, 0.9372922182083130f,
0.9172759056091309f, 0.8976871371269226f, 0.8785166740417481f, 0.8597555756568909f,
0.8413951396942139f, 0.8234267830848694f, 0.8058421611785889f, 0.7886331081390381f,
0.7717915177345276f, 0.7553095817565918f, 0.7391796708106995f, 0.7233941555023193f,
0.7079457640647888f, 0.6928272843360901f, 0.6780316829681397f, 0.6635520458221436f,
0.6493816375732422f, 0.6355138421058655f, 0.6219421625137329f, 0.6086603403091431f,
0.5956621170043945f, 0.5829415321350098f, 0.5704925656318665f, 0.5583094954490662f,
0.5463865399360657f, 0.5347182154655457f, 0.5232990980148315f, 0.5121238231658936f,
0.5011872053146362f, 0.4904841780662537f, 0.4800096750259399f, 0.4697588682174683f,
0.4597269892692566f, 0.4499093294143677f, 0.4403013288974762f, 0.4308985173702240f,
0.4216965138912201f, 0.4126909971237183f, 0.4038778245449066f, 0.3952528536319733f,
0.3868120610713959f, 0.3785515129566193f, 0.3704673945903778f, 0.3625559210777283f,
0.3548133969306946f, 0.3472362160682678f, 0.3398208320140839f, 0.3325638175010681f,
0.3254617750644684f, 0.3185114264488220f, 0.3117094635963440f, 0.3050527870655060f,
0.2985382676124573f, 0.2921628654003143f, 0.2859236001968384f, 0.2798175811767578f,
0.2738419771194458f, 0.2679939568042755f, 0.2622708380222321f, 0.2566699385643005f,
0.2511886358261108f, 0.2458244115114212f, 0.2405747324228287f, 0.2354371547698975f,
0.2304092943668366f, 0.2254888117313385f, 0.2206734120845795f, 0.2159608304500580f,
0.2113489061594009f, 0.2068354636430740f, 0.2024184018373489f, 0.1980956792831421f,
0.1938652694225311f, 0.1897251904010773f, 0.1856735348701477f, 0.1817083954811096f,
0.1778279393911362f, 0.1740303486585617f, 0.1703138649463654f, 0.1666767448186874f,
0.1631172895431519f, 0.1596338599920273f, 0.1562248021364212f, 0.1528885662555695f,
0.1496235728263855f, 0.1464282870292664f, 0.1433012634515762f, 0.1402409970760346f,
0.1372461020946503f, 0.1343151479959488f, 0.1314467936754227f, 0.1286396980285645f,
0.1258925348520279f, 0.1232040524482727f, 0.1205729842185974f, 0.1179980933666229f,
0.1154781952500343f, 0.1130121126770973f, 0.1105986908078194f, 0.1082368120551109f,
0.1059253737330437f, 0.1036632955074310f, 0.1014495193958283f, 0.0992830246686935f,
0.0971627980470657f, 0.0950878411531448f, 0.0930572077631950f, 0.0910699293017387f,
0.0891250967979431f, 0.0872217938303947f, 0.0853591337800026f, 0.0835362523794174f,
0.0817523002624512f, 0.0800064504146576f, 0.0782978758215904f, 0.0766257941722870f,
0.0749894231557846f, 0.0733879879117012f, 0.0718207582831383f, 0.0702869966626167f,
0.0687859877943993f, 0.0673170387744904f, 0.0658794566988945f, 0.0644725710153580f,
};
#if !defined(HAVE_POWF) || defined(__DJGPP__) || defined(__WATCOMC__)
/* Watcom doesn't have powf. DJGPP have a C-only implementation in libm. */
#undef powf
#define powf(f1_,f2_) (float)pow((f1_),(f2_))
#endif
/*
* Simple 2-poles resonant filter
*/
#define FREQ_PARAM_MULT (128.0f / (24.0f * 256.0f))
void libxmp_filter_setup(int srate, int cutoff, int res, int *a0, int *b0, int *b1)
{
float fc, fs = (float)srate;
float fg, fb0, fb1;
float r, d, e;
/* [0-255] => [100Hz-8000Hz] */
CLAMP(cutoff, 0, 255);
CLAMP(res, 0, 255);
fc = 110.0f * powf(2.0f, (float)cutoff * FREQ_PARAM_MULT + 0.25f);
if (fc > fs / 2.0f) {
fc = fs / 2.0f;
}
r = fs / (2.0 * 3.14159265358979f * fc);
d = resonance_table[res >> 1] * (r + 1.0) - 1.0;
e = r * r;
fg = 1.0 / (1.0 + d + e);
fb0 = (d + e + e) / (1.0 + d + e);
fb1 = -e / (1.0 + d + e);
*a0 = (int)(fg * (1 << FILTER_SHIFT));
*b0 = (int)(fb0 * (1 << FILTER_SHIFT));
*b1 = (int)(fb1 * (1 << FILTER_SHIFT));
}
#endif

124
libxmp/src/format.c Normal file
View File

@ -0,0 +1,124 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "format.h"
#ifndef LIBXMP_NO_PROWIZARD
#include "loaders/prowizard/prowiz.h"
#endif
const struct format_loader *const format_loaders[NUM_FORMATS + 2] = {
&libxmp_loader_xm,
&libxmp_loader_mod,
&libxmp_loader_flt,
&libxmp_loader_st,
&libxmp_loader_it,
&libxmp_loader_s3m,
&libxmp_loader_stm,
&libxmp_loader_stx,
&libxmp_loader_mtm,
&libxmp_loader_ice,
&libxmp_loader_imf,
&libxmp_loader_ptm,
&libxmp_loader_mdl,
&libxmp_loader_ult,
&libxmp_loader_liq,
&libxmp_loader_no,
&libxmp_loader_masi,
&libxmp_loader_gal5,
&libxmp_loader_gal4,
&libxmp_loader_psm,
&libxmp_loader_amf,
&libxmp_loader_asylum,
&libxmp_loader_gdm,
&libxmp_loader_mmd1,
&libxmp_loader_mmd3,
&libxmp_loader_med2,
&libxmp_loader_med3,
&libxmp_loader_med4,
/* &libxmp_loader_dmf, */
&libxmp_loader_chip,
&libxmp_loader_rtm,
&libxmp_loader_pt3,
/* &libxmp_loader_tcb, */
&libxmp_loader_dt,
/* &libxmp_loader_gtk, */
/* &libxmp_loader_dtt, */
&libxmp_loader_mgt,
&libxmp_loader_arch,
&libxmp_loader_sym,
&libxmp_loader_digi,
&libxmp_loader_dbm,
&libxmp_loader_emod,
&libxmp_loader_okt,
&libxmp_loader_sfx,
&libxmp_loader_far,
&libxmp_loader_umx,
&libxmp_loader_hmn,
&libxmp_loader_stim,
&libxmp_loader_coco,
/* &libxmp_loader_mtp, */
&libxmp_loader_ims,
&libxmp_loader_669,
&libxmp_loader_fnk,
/* &libxmp_loader_amd, */
/* &libxmp_loader_rad, */
/* &libxmp_loader_hsc, */
&libxmp_loader_mfp,
&libxmp_loader_abk,
/* &libxmp_loader_alm, */
/* &libxmp_loader_polly, */
/* &libxmp_loader_stc, */
#ifndef LIBXMP_NO_PROWIZARD
&libxmp_loader_pw,
#else
NULL,
#endif
NULL /* list teminator */
};
static const char *_farray[NUM_FORMATS + NUM_PW_FORMATS + 1] = { NULL };
const char *const *format_list(void)
{
int count, i;
if (_farray[0] == NULL) {
for (count = i = 0; format_loaders[i] != NULL; i++) {
#ifndef LIBXMP_NO_PROWIZARD
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
int j;
for (j = 0; pw_formats[j] != NULL; j++) {
_farray[count++] = pw_formats[j]->name;
}
continue;
}
#endif
_farray[count++] = format_loaders[i]->name;
}
_farray[count] = NULL;
}
return _farray;
}

95
libxmp/src/format.h Normal file
View File

@ -0,0 +1,95 @@
#ifndef LIBXMP_FORMAT_H
#define LIBXMP_FORMAT_H
#include "common.h"
#include "hio.h"
struct format_loader {
const char *name;
int (*test)(HIO_HANDLE *, char *, const int);
int (*loader)(struct module_data *, HIO_HANDLE *, const int);
};
extern const struct format_loader *const format_loaders[];
const char *const *format_list(void);
extern const struct format_loader libxmp_loader_xm;
extern const struct format_loader libxmp_loader_mod;
extern const struct format_loader libxmp_loader_it;
extern const struct format_loader libxmp_loader_s3m;
#ifndef LIBXMP_CORE_PLAYER
extern const struct format_loader libxmp_loader_flt;
extern const struct format_loader libxmp_loader_st;
extern const struct format_loader libxmp_loader_stm;
extern const struct format_loader libxmp_loader_stx;
extern const struct format_loader libxmp_loader_mtm;
extern const struct format_loader libxmp_loader_ice;
extern const struct format_loader libxmp_loader_imf;
extern const struct format_loader libxmp_loader_ptm;
extern const struct format_loader libxmp_loader_mdl;
extern const struct format_loader libxmp_loader_ult;
extern const struct format_loader libxmp_loader_liq;
extern const struct format_loader libxmp_loader_no;
extern const struct format_loader libxmp_loader_masi;
extern const struct format_loader libxmp_loader_gal5;
extern const struct format_loader libxmp_loader_gal4;
extern const struct format_loader libxmp_loader_psm;
extern const struct format_loader libxmp_loader_amf;
extern const struct format_loader libxmp_loader_asylum;
extern const struct format_loader libxmp_loader_gdm;
extern const struct format_loader libxmp_loader_mmd1;
extern const struct format_loader libxmp_loader_mmd3;
extern const struct format_loader libxmp_loader_med2;
extern const struct format_loader libxmp_loader_med3;
extern const struct format_loader libxmp_loader_med4;
extern const struct format_loader libxmp_loader_rtm;
extern const struct format_loader libxmp_loader_pt3;
extern const struct format_loader libxmp_loader_dt;
extern const struct format_loader libxmp_loader_mgt;
extern const struct format_loader libxmp_loader_arch;
extern const struct format_loader libxmp_loader_sym;
extern const struct format_loader libxmp_loader_digi;
extern const struct format_loader libxmp_loader_dbm;
extern const struct format_loader libxmp_loader_emod;
extern const struct format_loader libxmp_loader_okt;
extern const struct format_loader libxmp_loader_sfx;
extern const struct format_loader libxmp_loader_far;
extern const struct format_loader libxmp_loader_umx;
extern const struct format_loader libxmp_loader_stim;
extern const struct format_loader libxmp_loader_coco;
extern const struct format_loader libxmp_loader_ims;
extern const struct format_loader libxmp_loader_669;
extern const struct format_loader libxmp_loader_fnk;
extern const struct format_loader libxmp_loader_mfp;
extern const struct format_loader libxmp_loader_pw;
extern const struct format_loader libxmp_loader_hmn;
extern const struct format_loader libxmp_loader_chip;
extern const struct format_loader libxmp_loader_abk;
#if 0 /* broken / unused, yet. */
extern const struct format_loader libxmp_loader_dmf;
extern const struct format_loader libxmp_loader_tcb;
extern const struct format_loader libxmp_loader_gtk;
extern const struct format_loader libxmp_loader_dtt;
extern const struct format_loader libxmp_loader_mtp;
extern const struct format_loader libxmp_loader_amd;
extern const struct format_loader libxmp_loader_rad;
extern const struct format_loader libxmp_loader_hsc;
extern const struct format_loader libxmp_loader_alm;
extern const struct format_loader libxmp_loader_polly;
extern const struct format_loader libxmp_loader_stc;
#endif
#define NUM_FORMATS 50
#define NUM_PW_FORMATS 43
#ifndef LIBXMP_NO_PROWIZARD
extern const struct pw_format *const pw_formats[];
int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
#endif
#endif /* LIBXMP_CORE_PLAYER */
#endif /* LIBXMP_FORMAT_H */

493
libxmp/src/hio.c Normal file
View File

@ -0,0 +1,493 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include "common.h"
#include "hio.h"
#include "callbackio.h"
#include "mdataio.h"
static long get_size(FILE *f)
{
long size, pos;
pos = ftell(f);
if (pos >= 0) {
if (fseek(f, 0, SEEK_END) < 0) {
return -1;
}
size = ftell(f);
if (fseek(f, pos, SEEK_SET) < 0) {
return -1;
}
return size;
} else {
return pos;
}
}
int8 hio_read8s(HIO_HANDLE *h)
{
int err;
int8 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read8s(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread8s(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread8s(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint8 hio_read8(HIO_HANDLE *h)
{
int err;
uint8 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read8(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread8(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread8(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint16 hio_read16l(HIO_HANDLE *h)
{
int err;
uint16 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read16l(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread16l(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread16l(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint16 hio_read16b(HIO_HANDLE *h)
{
int err;
uint16 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read16b(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread16b(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread16b(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read24l(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read24l(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread24l(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread24l(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read24b(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read24b(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread24b(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread24b(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read32l(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read32l(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread32l(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread32l(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
uint32 hio_read32b(HIO_HANDLE *h)
{
int err;
uint32 ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read32b(h->handle.file, &err);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread32b(h->handle.mem, &err);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread32b(h->handle.cbfile, &err);
break;
default:
return 0;
}
if (err != 0) {
h->error = err;
}
return ret;
}
size_t hio_read(void *buf, size_t size, size_t num, HIO_HANDLE *h)
{
size_t ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = fread(buf, size, num, h->handle.file);
if (ret != num) {
if (ferror(h->handle.file)) {
h->error = errno;
} else {
h->error = feof(h->handle.file) ? EOF : -2;
}
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread(buf, size, num, h->handle.mem);
if (ret != num) {
h->error = EOF;
}
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbread(buf, size, num, h->handle.cbfile);
if (ret != num) {
h->error = EOF;
}
break;
}
return ret;
}
int hio_seek(HIO_HANDLE *h, long offset, int whence)
{
int ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = fseek(h->handle.file, offset, whence);
if (ret < 0) {
h->error = errno;
}
else if (h->error == EOF) {
h->error = 0;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mseek(h->handle.mem, offset, whence);
if (ret < 0) {
h->error = EINVAL;
}
else if (h->error == EOF) {
h->error = 0;
}
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbseek(h->handle.cbfile, offset, whence);
if (ret < 0) {
h->error = EINVAL;
}
else if (h->error == EOF) {
h->error = 0;
}
break;
}
return ret;
}
long hio_tell(HIO_HANDLE *h)
{
long ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = ftell(h->handle.file);
if (ret < 0) {
h->error = errno;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mtell(h->handle.mem);
if (ret < 0) {
/* should _not_ happen! */
h->error = EINVAL;
}
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbtell(h->handle.cbfile);
if (ret < 0) {
h->error = EINVAL;
}
break;
}
return ret;
}
int hio_eof(HIO_HANDLE *h)
{
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
return feof(h->handle.file);
case HIO_HANDLE_TYPE_MEMORY:
return meof(h->handle.mem);
case HIO_HANDLE_TYPE_CBFILE:
return cbeof(h->handle.cbfile);
}
return EOF;
}
int hio_error(HIO_HANDLE *h)
{
int error = h->error;
h->error = 0;
return error;
}
HIO_HANDLE *hio_open(const char *path, const char *mode)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL)
goto err;
h->type = HIO_HANDLE_TYPE_FILE;
h->handle.file = fopen(path, mode);
if (h->handle.file == NULL)
goto err2;
h->size = get_size(h->handle.file);
if (h->size < 0)
goto err3;
return h;
err3:
fclose(h->handle.file);
err2:
free(h);
err:
return NULL;
}
HIO_HANDLE *hio_open_mem(const void *ptr, long size, int free_after_use)
{
HIO_HANDLE *h;
if (size <= 0) return NULL;
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL)
return NULL;
h->type = HIO_HANDLE_TYPE_MEMORY;
h->handle.mem = mopen(ptr, size, free_after_use);
h->size = size;
if (!h->handle.mem) {
free(h);
h = NULL;
}
return h;
}
HIO_HANDLE *hio_open_file(FILE *f)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL)
return NULL;
h->noclose = 1;
h->type = HIO_HANDLE_TYPE_FILE;
h->handle.file = f;
h->size = get_size(f);
if (h->size < 0) {
free(h);
return NULL;
}
return h;
}
HIO_HANDLE *hio_open_file2(FILE *f)
{
HIO_HANDLE *h = hio_open_file(f);
if (h != NULL) {
h->noclose = 0;
}
else {
fclose(f);
}
return h;
}
HIO_HANDLE *hio_open_callbacks(void *priv, struct xmp_callbacks callbacks)
{
HIO_HANDLE *h;
CBFILE *f = cbopen(priv, callbacks);
if (!f)
return NULL;
h = (HIO_HANDLE *) calloc(1, sizeof(HIO_HANDLE));
if (h == NULL) {
cbclose(f);
return NULL;
}
h->type = HIO_HANDLE_TYPE_CBFILE;
h->handle.cbfile = f;
h->size = cbfilelength(f);
if (h->size < 0) {
cbclose(f);
free(h);
return NULL;
}
return h;
}
int hio_close(HIO_HANDLE *h)
{
int ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = (h->noclose)? 0 : fclose(h->handle.file);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mclose(h->handle.mem);
break;
case HIO_HANDLE_TYPE_CBFILE:
ret = cbclose(h->handle.cbfile);
break;
}
free(h);
return ret;
}
long hio_size(HIO_HANDLE *h)
{
return h->size;
}

48
libxmp/src/hio.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef XMP_HIO_H
#define XMP_HIO_H
#include "callbackio.h"
#include "memio.h"
#define HIO_HANDLE_TYPE(x) ((x)->type)
enum hio_type {
HIO_HANDLE_TYPE_FILE,
HIO_HANDLE_TYPE_MEMORY,
HIO_HANDLE_TYPE_CBFILE
};
typedef struct {
enum hio_type type;
long size;
union {
FILE *file;
MFILE *mem;
CBFILE *cbfile;
} handle;
int error;
int noclose;
} HIO_HANDLE;
int8 hio_read8s (HIO_HANDLE *);
uint8 hio_read8 (HIO_HANDLE *);
uint16 hio_read16l (HIO_HANDLE *);
uint16 hio_read16b (HIO_HANDLE *);
uint32 hio_read24l (HIO_HANDLE *);
uint32 hio_read24b (HIO_HANDLE *);
uint32 hio_read32l (HIO_HANDLE *);
uint32 hio_read32b (HIO_HANDLE *);
size_t hio_read (void *, size_t, size_t, HIO_HANDLE *);
int hio_seek (HIO_HANDLE *, long, int);
long hio_tell (HIO_HANDLE *);
int hio_eof (HIO_HANDLE *);
int hio_error (HIO_HANDLE *);
HIO_HANDLE *hio_open (const char *, const char *);
HIO_HANDLE *hio_open_mem (const void *, long, int);
HIO_HANDLE *hio_open_file (FILE *);
HIO_HANDLE *hio_open_file2 (FILE *);/* allows fclose()ing the file by libxmp */
HIO_HANDLE *hio_open_callbacks (void *, struct xmp_callbacks);
int hio_close (HIO_HANDLE *);
long hio_size (HIO_HANDLE *);
#endif

141
libxmp/src/hmn_extras.c Normal file
View File

@ -0,0 +1,141 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "common.h"
#include "player.h"
#include "virtual.h"
#include "effects.h"
#include "hmn_extras.h"
static uint8 megaarp[16][16] = {
{ 0, 3, 7, 12, 15, 12, 7, 3, 0, 3, 7, 12, 15, 12, 7, 3 },
{ 0, 4, 7, 12, 16, 12, 7, 4, 0, 4, 7, 12, 16, 12, 7, 4 },
{ 0, 3, 8, 12, 15, 12, 8, 3, 0, 3, 8, 12, 15, 12, 8, 3 },
{ 0, 4, 8, 12, 16, 12, 8, 4, 0, 4, 8, 12, 16, 12, 8, 4 },
{ 0, 5, 8, 12, 17, 12, 8, 5, 0, 5, 8, 12, 17, 12, 8, 5 },
{ 0, 5, 9, 12, 17, 12, 9, 5, 0, 5, 9, 12, 17, 12, 9, 5 },
{ 12, 0, 7, 0, 3, 0, 7, 0, 12, 0, 7, 0, 3, 0, 7, 0 },
{ 12, 0, 7, 0, 4, 0, 7, 0, 12, 0, 7, 0, 4, 0, 7, 0 },
{ 0, 3, 7, 3, 7, 12, 7, 12, 15, 12, 7, 12, 7, 3, 7, 3 },
{ 0, 4, 7, 4, 7, 12, 7, 12, 16, 12, 7, 12, 7, 4, 7, 4 },
{ 31, 27, 24, 19, 15, 12, 7, 3, 0, 3, 7, 12, 15, 19, 24, 27 },
{ 31, 28, 24, 19, 16, 12, 7, 4, 0, 4, 7, 12, 16, 19, 24, 28 },
{ 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12 },
{ 0, 12, 24, 12, 0, 12, 24, 12, 0, 12, 24, 12, 0, 12, 24, 12 },
{ 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3 },
{ 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4 }
};
int libxmp_hmn_linear_bend(struct context_data *ctx, struct channel_data *xc)
{
return 0;
}
void libxmp_hmn_play_extras(struct context_data *ctx, struct channel_data *xc, int chn)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct hmn_channel_extras *ce = (struct hmn_channel_extras *)xc->extra;
struct xmp_instrument *xxi;
int pos, waveform, volume;
if (p->frame == 0 && TEST(NEW_NOTE|NEW_INS)) {
ce->datapos = 0;
}
xxi = &m->mod.xxi[xc->ins];
pos = ce->datapos;
waveform = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->data[pos];
volume = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->progvolume[pos] & 0x7f;
if (waveform < xxi->nsm && xxi->sub[waveform].sid != xc->smp) {
xc->smp = xxi->sub[waveform].sid;
libxmp_virt_setsmp(ctx, chn, xc->smp);
}
pos++;
if (pos > HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->dataloopend)
pos = HMN_INSTRUMENT_EXTRAS(m->mod.xxi[xc->ins])->dataloopstart;
ce->datapos = pos;
ce->volume = volume;
}
int libxmp_hmn_new_instrument_extras(struct xmp_instrument *xxi)
{
xxi->extra = calloc(1, sizeof(struct hmn_instrument_extras));
if (xxi->extra == NULL)
return -1;
HMN_INSTRUMENT_EXTRAS((*xxi))->magic = HMN_EXTRAS_MAGIC;
return 0;
}
int libxmp_hmn_new_channel_extras(struct channel_data *xc)
{
xc->extra = calloc(1, sizeof(struct hmn_channel_extras));
if (xc->extra == NULL)
return -1;
HMN_CHANNEL_EXTRAS((*xc))->magic = HMN_EXTRAS_MAGIC;
return 0;
}
void libxmp_hmn_reset_channel_extras(struct channel_data *xc)
{
memset((char *)xc->extra + 4, 0, sizeof(struct hmn_channel_extras) - 4);
}
void libxmp_hmn_release_channel_extras(struct channel_data *xc)
{
free(xc->extra);
xc->extra = NULL;
}
int libxmp_hmn_new_module_extras(struct module_data *m)
{
m->extra = calloc(1, sizeof(struct hmn_module_extras));
if (m->extra == NULL)
return -1;
HMN_MODULE_EXTRAS((*m))->magic = HMN_EXTRAS_MAGIC;
return 0;
}
void libxmp_hmn_release_module_extras(struct module_data *m)
{
free(m->extra);
m->extra = NULL;
}
void libxmp_hmn_extras_process_fx(struct context_data *ctx, struct channel_data *xc,
int chn, uint8 note, uint8 fxt, uint8 fxp, int fnum)
{
switch (fxt) {
case FX_MEGAARP:
/* Not sure if this is correct... */
fxp = LSN(fxp);
memcpy(xc->arpeggio.val, megaarp[fxp], 16);
xc->arpeggio.size = 16;
break;
}
}

51
libxmp/src/hmn_extras.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef XMP_HMN_EXTRAS_H
#define XMP_HMN_EXTRAS_H
#define HMN_EXTRAS_MAGIC 0x041bc81a
struct hmn_instrument_extras {
uint32 magic;
int dataloopstart;
int dataloopend;
uint8 data[64];
uint8 progvolume[64];
};
struct hmn_channel_extras {
uint32 magic;
int datapos; /* HMN waveform table pointer */
int volume; /* HMN synth volume */
};
struct hmn_module_extras {
uint32 magic;
};
#define HMN_INSTRUMENT_EXTRAS(x) ((struct hmn_instrument_extras *)(x).extra)
#define HAS_HMN_INSTRUMENT_EXTRAS(x) \
(HMN_INSTRUMENT_EXTRAS(x) != NULL && \
HMN_INSTRUMENT_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC)
#define HMN_CHANNEL_EXTRAS(x) ((struct hmn_channel_extras *)(x).extra)
#define HAS_HMN_CHANNEL_EXTRAS(x) \
(HMN_CHANNEL_EXTRAS(x) != NULL && \
HMN_CHANNEL_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC)
#define HMN_MODULE_EXTRAS(x) ((struct hmn_module_extras *)(x).extra)
#define HAS_HMN_MODULE_EXTRAS(x) \
(HMN_MODULE_EXTRAS(x) != NULL && \
HMN_MODULE_EXTRAS(x)->magic == HMN_EXTRAS_MAGIC)
void libxmp_hmn_play_extras(struct context_data *, struct channel_data *, int);
void libxmp_hmn_set_arpeggio(struct channel_data *, int);
int libxmp_hmn_linear_bend(struct context_data *, struct channel_data *);
int libxmp_hmn_new_instrument_extras(struct xmp_instrument *);
int libxmp_hmn_new_channel_extras(struct channel_data *);
void libxmp_hmn_reset_channel_extras(struct channel_data *);
void libxmp_hmn_release_channel_extras(struct channel_data *);
int libxmp_hmn_new_module_extras(struct module_data *);
void libxmp_hmn_release_module_extras(struct module_data *);
void libxmp_hmn_extras_process_fx(struct context_data *, struct channel_data *, int, uint8, uint8, uint8, int);
#endif

163
libxmp/src/lfo.c Normal file
View File

@ -0,0 +1,163 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "lfo.h"
#define WAVEFORM_SIZE 64
static const int sine_wave[WAVEFORM_SIZE] = {
0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224,
235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197,
180, 161, 141, 120, 97, 74, 49, 24, 0, -24, -49, -74,
-97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253,
-255,-253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120,
-97, -74, -49, -24
};
/* LFO */
static int get_lfo_mod(struct lfo *lfo)
{
int val;
if (lfo->rate == 0)
return 0;
switch (lfo->type) {
case 0: /* sine */
val = sine_wave[lfo->phase];
break;
case 1: /* ramp down */
val = 255 - (lfo->phase << 3);
break;
case 2: /* square */
val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : -255;
break;
case 3: /* random */
val = ((rand() & 0x1ff) - 256);
break;
#ifndef LIBXMP_CORE_PLAYER
case 669: /* 669 vibrato */
val = lfo->phase & 1;
break;
#endif
default:
return 0;
}
return val * lfo->depth;
}
static int get_lfo_st3(struct lfo *lfo)
{
if (lfo->rate == 0) {
return 0;
}
/* S3M square */
if (lfo->type == 2) {
int val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : 0;
return val * lfo->depth;
}
return get_lfo_mod(lfo);
}
/* From OpenMPT VibratoWaveforms.xm:
* "Generally the vibrato and tremolo tables are identical to those that
* ProTracker uses, but the vibratos ramp down table is upside down."
*/
static int get_lfo_ft2(struct lfo *lfo)
{
if (lfo->rate == 0)
return 0;
/* FT2 ramp */
if (lfo->type == 1) {
int phase = (lfo->phase + (WAVEFORM_SIZE >> 1)) % WAVEFORM_SIZE;
int val = (phase << 3) - 255;
return val * lfo->depth;
}
return get_lfo_mod(lfo);
}
#ifndef LIBXMP_CORE_DISABLE_IT
static int get_lfo_it(struct lfo *lfo)
{
if (lfo->rate == 0)
return 0;
return get_lfo_st3(lfo);
}
#endif
int libxmp_lfo_get(struct context_data *ctx, struct lfo *lfo, int is_vibrato)
{
struct module_data *m = &ctx->m;
switch (m->read_event_type) {
case READ_EVENT_ST3:
return get_lfo_st3(lfo);
case READ_EVENT_FT2:
if (is_vibrato) {
return get_lfo_ft2(lfo);
} else {
return get_lfo_mod(lfo);
}
#ifndef LIBXMP_CORE_DISABLE_IT
case READ_EVENT_IT:
return get_lfo_it(lfo);
#endif
default:
return get_lfo_mod(lfo);
}
}
void libxmp_lfo_update(struct lfo *lfo)
{
lfo->phase += lfo->rate;
lfo->phase %= WAVEFORM_SIZE;
}
void libxmp_lfo_set_phase(struct lfo *lfo, int phase)
{
lfo->phase = phase;
}
void libxmp_lfo_set_depth(struct lfo *lfo, int depth)
{
lfo->depth = depth;
}
void libxmp_lfo_set_rate(struct lfo *lfo, int rate)
{
lfo->rate = rate;
}
void libxmp_lfo_set_waveform(struct lfo *lfo, int type)
{
lfo->type = type;
}

20
libxmp/src/lfo.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef LIBXMP_LFO_H
#define LIBXMP_LFO_H
#include "common.h"
struct lfo {
int type;
int rate;
int depth;
int phase;
};
int libxmp_lfo_get(struct context_data *, struct lfo *, int);
void libxmp_lfo_update(struct lfo *);
void libxmp_lfo_set_phase(struct lfo *, int);
void libxmp_lfo_set_depth(struct lfo *, int);
void libxmp_lfo_set_rate(struct lfo *, int);
void libxmp_lfo_set_waveform(struct lfo *, int);
#endif

141
libxmp/src/list.h Normal file
View File

@ -0,0 +1,141 @@
#ifndef LIBXMP_LIST_H
#define LIBXMP_LIST_H
#include <stddef.h> /* offsetof */
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *_new,
struct list_head * prev,
struct list_head * next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
/**
* list_add - add a new entry
* @_new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @_new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev,
struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(struct list_head *head)
{
return head->next == head;
}
/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
struct list_head *first = list->next;
if (first != list) {
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#endif /* LIBXMP_LIST_H */

582
libxmp/src/load.c Normal file
View File

@ -0,0 +1,582 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include "format.h"
#include "list.h"
#include "hio.h"
#include "tempfile.h"
#include "loaders/loader.h"
#ifndef LIBXMP_NO_DEPACKERS
#include "depackers/depacker.h"
#endif
#ifndef LIBXMP_CORE_PLAYER
#include "md5.h"
#include "extras.h"
#endif
void libxmp_load_prologue(struct context_data *);
void libxmp_load_epilogue(struct context_data *);
int libxmp_prepare_scan(struct context_data *);
#ifndef LIBXMP_CORE_PLAYER
#define BUFLEN 16384
#endif
#ifndef LIBXMP_CORE_PLAYER
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
{
unsigned char buf[BUFLEN];
MD5_CTX ctx;
int bytes_read;
hio_seek(f, 0, SEEK_SET);
MD5Init(&ctx);
while ((bytes_read = hio_read(buf, 1, BUFLEN, f)) > 0) {
MD5Update(&ctx, buf, bytes_read);
}
MD5Final(digest, &ctx);
}
static char *get_dirname(const char *name)
{
char *dirname;
const char *p;
ptrdiff_t len;
if ((p = strrchr(name, '/')) != NULL) {
len = p - name + 1;
dirname = (char *) malloc(len + 1);
if (dirname != NULL) {
memcpy(dirname, name, len);
dirname[len] = 0;
}
} else {
dirname = libxmp_strdup("");
}
return dirname;
}
static char *get_basename(const char *name)
{
const char *p;
char *basename;
if ((p = strrchr(name, '/')) != NULL) {
basename = libxmp_strdup(p + 1);
} else {
basename = libxmp_strdup(name);
}
return basename;
}
#endif /* LIBXMP_CORE_PLAYER */
static int test_module(struct xmp_test_info *info, HIO_HANDLE *h)
{
char buf[XMP_NAME_SIZE];
int i;
if (info != NULL) {
*info->name = 0; /* reset name prior to testing */
*info->type = 0; /* reset type prior to testing */
}
for (i = 0; format_loaders[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET);
if (format_loaders[i]->test(h, buf, 0) == 0) {
int is_prowizard = 0;
#ifndef LIBXMP_NO_PROWIZARD
if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
hio_seek(h, 0, SEEK_SET);
pw_test_format(h, buf, 0, info);
is_prowizard = 1;
}
#endif
if (info != NULL && !is_prowizard) {
strncpy(info->name, buf, XMP_NAME_SIZE - 1);
info->name[XMP_NAME_SIZE - 1] = '\0';
strncpy(info->type, format_loaders[i]->name,
XMP_NAME_SIZE - 1);
info->type[XMP_NAME_SIZE - 1] = '\0';
}
return 0;
}
}
return -XMP_ERROR_FORMAT;
}
int xmp_test_module(const char *path, struct xmp_test_info *info)
{
HIO_HANDLE *h;
#ifndef LIBXMP_NO_DEPACKERS
char *temp = NULL;
#endif
int ret;
ret = libxmp_get_filetype(path);
if (ret == XMP_FILETYPE_NONE) {
return -XMP_ERROR_SYSTEM;
}
if (ret & XMP_FILETYPE_DIR) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
if ((h = hio_open(path, "rb")) == NULL)
return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, path, &temp) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
#endif
ret = test_module(info, h);
#ifndef LIBXMP_NO_DEPACKERS
err:
hio_close(h);
unlink_temp_file(temp);
#else
hio_close(h);
#endif
return ret;
}
int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info *info)
{
HIO_HANDLE *h;
int ret;
if (size <= 0) {
return -XMP_ERROR_INVALID;
}
if ((h = hio_open_mem(mem, size, 0)) == NULL)
return -XMP_ERROR_SYSTEM;
ret = test_module(info, h);
hio_close(h);
return ret;
}
int xmp_test_module_from_file(void *file, struct xmp_test_info *info)
{
HIO_HANDLE *h;
int ret;
#ifndef LIBXMP_NO_DEPACKERS
char *temp = NULL;
#endif
if ((h = hio_open_file((FILE *)file)) == NULL)
return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_NO_DEPACKERS
if (libxmp_decrunch(&h, NULL, &temp) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
#endif
ret = test_module(info, h);
#ifndef LIBXMP_NO_DEPACKERS
err:
hio_close(h);
unlink_temp_file(temp);
#else
hio_close(h);
#endif
return ret;
}
int xmp_test_module_from_callbacks(void *priv, struct xmp_callbacks callbacks,
struct xmp_test_info *info)
{
HIO_HANDLE *h;
int ret;
if ((h = hio_open_callbacks(priv, callbacks)) == NULL)
return -XMP_ERROR_SYSTEM;
ret = test_module(info, h);
hio_close(h);
return ret;
}
static int load_module(xmp_context opaque, HIO_HANDLE *h)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, j, ret;
int test_result, load_result;
libxmp_load_prologue(ctx);
D_(D_WARN "load");
test_result = load_result = -1;
for (i = 0; format_loaders[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET);
D_(D_WARN "test %s", format_loaders[i]->name);
test_result = format_loaders[i]->test(h, NULL, 0);
if (test_result == 0) {
hio_seek(h, 0, SEEK_SET);
D_(D_WARN "load format: %s", format_loaders[i]->name);
load_result = format_loaders[i]->loader(m, h, 0);
break;
}
}
if (test_result < 0) {
xmp_release_module(opaque);
return -XMP_ERROR_FORMAT;
}
if (load_result < 0) {
goto err_load;
}
/* Sanity check: number of channels, module length */
if (mod->chn > XMP_MAX_CHANNELS || mod->len > XMP_MAX_MOD_LENGTH) {
goto err_load;
}
/* Sanity check: channel pan */
for (i = 0; i < mod->chn; i++) {
if (mod->xxc[i].vol < 0 || mod->xxc[i].vol > 0xff) {
goto err_load;
}
if (mod->xxc[i].pan < 0 || mod->xxc[i].pan > 0xff) {
goto err_load;
}
}
/* Sanity check: patterns */
if (mod->xxp == NULL) {
goto err_load;
}
for (i = 0; i < mod->pat; i++) {
if (mod->xxp[i] == NULL) {
goto err_load;
}
for (j = 0; j < mod->chn; j++) {
int t = mod->xxp[i]->index[j];
if (t < 0 || t >= mod->trk || mod->xxt[t] == NULL) {
goto err_load;
}
}
}
libxmp_adjust_string(mod->name);
for (i = 0; i < mod->ins; i++) {
libxmp_adjust_string(mod->xxi[i].name);
}
for (i = 0; i < mod->smp; i++) {
libxmp_adjust_string(mod->xxs[i].name);
}
#ifndef LIBXMP_CORE_PLAYER
if (test_result == 0 && load_result == 0)
set_md5sum(h, m->md5);
#endif
libxmp_load_epilogue(ctx);
ret = libxmp_prepare_scan(ctx);
if (ret < 0) {
xmp_release_module(opaque);
return ret;
}
ret = libxmp_scan_sequences(ctx);
if (ret < 0) {
xmp_release_module(opaque);
return -XMP_ERROR_LOAD;
}
ctx->state = XMP_STATE_LOADED;
return 0;
err_load:
xmp_release_module(opaque);
return -XMP_ERROR_LOAD;
}
int xmp_load_module(xmp_context opaque, const char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
#ifndef LIBXMP_CORE_PLAYER
struct module_data *m = &ctx->m;
#endif
#ifndef LIBXMP_NO_DEPACKERS
char *temp_name;
#endif
HIO_HANDLE *h;
int ret;
D_(D_WARN "path = %s", path);
ret = libxmp_get_filetype(path);
if (ret == XMP_FILETYPE_NONE) {
return -XMP_ERROR_SYSTEM;
}
if (ret & XMP_FILETYPE_DIR) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
if ((h = hio_open(path, "rb")) == NULL) {
return -XMP_ERROR_SYSTEM;
}
#ifndef LIBXMP_NO_DEPACKERS
D_(D_INFO "decrunch");
if (libxmp_decrunch(&h, path, &temp_name) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
#endif
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
#ifndef LIBXMP_CORE_PLAYER
m->dirname = get_dirname(path);
if (m->dirname == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err;
}
m->basename = get_basename(path);
if (m->basename == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err;
}
m->filename = path; /* For ALM, SSMT, etc */
m->size = hio_size(h);
#else
ctx->m.filename = NULL;
ctx->m.dirname = NULL;
ctx->m.basename = NULL;
#endif
ret = load_module(opaque, h);
hio_close(h);
#ifndef LIBXMP_NO_DEPACKERS
unlink_temp_file(temp_name);
#endif
return ret;
#ifndef LIBXMP_CORE_PLAYER
err:
hio_close(h);
#ifndef LIBXMP_NO_DEPACKERS
unlink_temp_file(temp_name);
#endif
return ret;
#endif
}
int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
int ret;
if (size <= 0) {
return -XMP_ERROR_INVALID;
}
if ((h = hio_open_mem(mem, size, 0)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = size;
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
int xmp_load_module_from_file(xmp_context opaque, void *file, long size)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
int ret;
if ((h = hio_open_file((FILE *)file)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = hio_size(h);
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
int xmp_load_module_from_callbacks(xmp_context opaque, void *priv,
struct xmp_callbacks callbacks)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
int ret;
if ((h = hio_open_callbacks(priv, callbacks)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = hio_size(h);
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
void xmp_release_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i;
/* can't test this here, we must call release_module to clean up
* load errors
if (ctx->state < XMP_STATE_LOADED)
return;
*/
if (ctx->state > XMP_STATE_LOADED)
xmp_end_player(opaque);
ctx->state = XMP_STATE_UNLOADED;
D_(D_INFO "Freeing memory");
#ifndef LIBXMP_CORE_PLAYER
libxmp_release_module_extras(ctx);
#endif
if (mod->xxt != NULL) {
for (i = 0; i < mod->trk; i++) {
free(mod->xxt[i]);
}
free(mod->xxt);
mod->xxt = NULL;
}
if (mod->xxp != NULL) {
for (i = 0; i < mod->pat; i++) {
free(mod->xxp[i]);
}
free(mod->xxp);
mod->xxp = NULL;
}
if (mod->xxi != NULL) {
for (i = 0; i < mod->ins; i++) {
free(mod->xxi[i].sub);
free(mod->xxi[i].extra);
}
free(mod->xxi);
mod->xxi = NULL;
}
if (mod->xxs != NULL) {
for (i = 0; i < mod->smp; i++) {
libxmp_free_sample(&mod->xxs[i]);
}
free(mod->xxs);
mod->xxs = NULL;
}
free(m->xtra);
free(m->midi);
m->xtra = NULL;
m->midi = NULL;
libxmp_free_scan(ctx);
free(m->comment);
m->comment = NULL;
D_("free dirname/basename");
free(m->dirname);
free(m->basename);
m->basename = NULL;
m->dirname = NULL;
}
void xmp_scan_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
if (ctx->state < XMP_STATE_LOADED)
return;
libxmp_scan_sequences(ctx);
}

433
libxmp/src/load_helpers.c Normal file
View File

@ -0,0 +1,433 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <ctype.h>
#include "common.h"
#include "loaders/loader.h"
#ifndef LIBXMP_CORE_PLAYER
/*
* Handle special "module quirks" that can't be detected automatically
* such as Protracker 2.x compatibility, vblank timing, etc.
*/
struct module_quirk {
uint8 md5[16];
int flags;
int mode;
};
const struct module_quirk mq[] = {
/* "No Mercy" by Alf/VTL (added by Martin Willers) */
{
{ 0x36, 0x6e, 0xc0, 0xfa, 0x96, 0x2a, 0xeb, 0xee,
0x03, 0x4a, 0xa2, 0xdb, 0xaa, 0x49, 0xaa, 0xea },
0, XMP_MODE_PROTRACKER
},
/* mod.souvenir of china */
{
{ 0x93, 0xf1, 0x46, 0xae, 0xb7, 0x58, 0xc3, 0x9d,
0x8b, 0x5f, 0xbc, 0x98, 0xbf, 0x23, 0x7a, 0x43 },
XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
},
/* "siedler ii" (added by Daniel Åkerud) */
{
{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
{
{ 0xe9, 0x98, 0x01, 0x2c, 0x70, 0x0e, 0xb4, 0x3a,
0xf0, 0x32, 0x17, 0x11, 0x30, 0x58, 0x29, 0xb2 },
0, XMP_MODE_NOISETRACKER
},
#if 0
/* -- Already covered by Noisetracker fingerprinting -- */
/* Another version of Klisje paa klisje sent by Steve Fernandez */
{
{ 0x12, 0x19, 0x1c, 0x90, 0x41, 0xe3, 0xfd, 0x70,
0xb7, 0xe6, 0xb3, 0x94, 0x8b, 0x21, 0x07, 0x63 },
XMP_FLAGS_VBLANK
},
#endif
/* "((((( nebulos )))))" sent by Tero Auvinen (AMP version) */
{
{ 0x51, 0x6e, 0x8d, 0xcc, 0x35, 0x7d, 0x50, 0xde,
0xa9, 0x85, 0xbe, 0xbf, 0x90, 0x2e, 0x42, 0xdc },
0, XMP_MODE_NOISETRACKER
},
/* Purple Motion's Sundance.mod, Music Channel BBS edit */
{
{ 0x5d, 0x3e, 0x1e, 0x08, 0x28, 0x52, 0x12, 0xc7,
0x17, 0x64, 0x95, 0x75, 0x98, 0xe6, 0x95, 0xc1 },
0, XMP_MODE_ST3
},
/* Asle's Ode to Protracker */
{
{ 0x97, 0xa3, 0x7d, 0x30, 0xd7, 0xae, 0x6d, 0x50,
0xc9, 0x62, 0xe9, 0xd8, 0x87, 0x1b, 0x7e, 0x8a },
0, XMP_MODE_PROTRACKER
},
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
0, 0
}
};
static void module_quirks(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i;
for (i = 0; mq[i].flags != 0 || mq[i].mode != 0; i++) {
if (!memcmp(m->md5, mq[i].md5, 16)) {
p->flags |= mq[i].flags;
p->mode = mq[i].mode;
}
}
}
#endif /* LIBXMP_CORE_PLAYER */
char *libxmp_adjust_string(char *s)
{
int i;
for (i = 0; i < strlen(s); i++) {
if (!isprint((unsigned char)s[i]) || ((uint8) s[i] > 127))
s[i] = ' ';
}
while (*s && (s[strlen(s) - 1] == ' ')) {
s[strlen(s) - 1] = 0;
}
return s;
}
static void check_envelope(struct xmp_envelope *env)
{
/* Disable envelope if invalid number of points */
if (env->npt <= 0 || env->npt > XMP_MAX_ENV_POINTS) {
env->flg &= ~XMP_ENVELOPE_ON;
}
/* Disable envelope loop if invalid loop parameters */
if (env->lps >= env->npt || env->lpe >= env->npt) {
env->flg &= ~XMP_ENVELOPE_LOOP;
}
/* Disable envelope sustain if invalid sustain */
if (env->sus >= env->npt || env->sue >= env->npt) {
env->flg &= ~XMP_ENVELOPE_SUS;
}
}
static void clamp_volume_envelope(struct module_data *m, struct xmp_envelope *env)
{
/* Clamp broken values in the volume envelope to the expected range. */
if (env->flg & XMP_ENVELOPE_ON) {
int i;
for (i = 0; i < env->npt; i++) {
int16 *data = &env->data[i * 2 + 1];
CLAMP(*data, 0, m->volbase);
}
}
}
void libxmp_load_prologue(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
int i;
/* Reset variables */
memset(&m->mod, 0, sizeof (struct xmp_module));
m->rrate = PAL_RATE;
m->c4rate = C4_PAL_RATE;
m->volbase = 0x40;
m->gvol = m->gvolbase = 0x40;
m->vol_table = NULL;
m->quirk = 0;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA;
m->comment = NULL;
m->scan_cnt = NULL;
m->midi = NULL;
/* Set defaults */
m->mod.pat = 0;
m->mod.trk = 0;
m->mod.chn = 4;
m->mod.ins = 0;
m->mod.smp = 0;
m->mod.spd = 6;
m->mod.bpm = 125;
m->mod.len = 0;
m->mod.rst = 0;
#ifndef LIBXMP_CORE_PLAYER
m->extra = NULL;
#endif
m->time_factor = DEFAULT_TIME_FACTOR;
for (i = 0; i < 64; i++) {
int pan = (((i + 1) / 2) % 2) * 0xff;
m->mod.xxc[i].pan = 0x80 + (pan - 0x80) * m->defpan / 100;
m->mod.xxc[i].vol = 0x40;
m->mod.xxc[i].flg = 0;
}
}
void libxmp_load_epilogue(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, j;
mod->gvl = m->gvol;
/* Sanity check for module parameters */
CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH);
CLAMP(mod->pat, 0, 257); /* some formats have an extra pattern */
CLAMP(mod->ins, 0, 255);
CLAMP(mod->smp, 0, MAX_SAMPLES);
CLAMP(mod->chn, 0, XMP_MAX_CHANNELS);
/* Fix cases where the restart value is invalid e.g. kc_fall8.xm
* from http://aminet.net/mods/mvp/mvp_0002.lha (reported by
* Ralf Hoffmann <ralf@boomerangsworld.de>)
*/
if (mod->rst >= mod->len) {
mod->rst = 0;
}
/* Sanity check for tempo and BPM */
if (mod->spd <= 0 || mod->spd > 255) {
mod->spd = 6;
}
CLAMP(mod->bpm, XMP_MIN_BPM, 255);
/* Set appropriate values for instrument volumes and subinstrument
* global volumes when QUIRK_INSVOL is not set, to keep volume values
* consistent if the user inspects struct xmp_module. We can later
* set volumes in the loaders and eliminate the quirk.
*/
for (i = 0; i < mod->ins; i++) {
if (~m->quirk & QUIRK_INSVOL) {
mod->xxi[i].vol = m->volbase;
}
for (j = 0; j < mod->xxi[i].nsm; j++) {
if (~m->quirk & QUIRK_INSVOL) {
mod->xxi[i].sub[j].gvl = m->volbase;
}
}
}
/* Sanity check for envelopes
*/
for (i = 0; i < mod->ins; i++) {
check_envelope(&mod->xxi[i].aei);
check_envelope(&mod->xxi[i].fei);
check_envelope(&mod->xxi[i].pei);
clamp_volume_envelope(m, &mod->xxi[i].aei);
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* TODO: there's no unintrusive and clean way to get this struct into
* libxmp_load_sample currently, so bound these fields here for now. */
for (i = 0; i < mod->smp; i++) {
struct xmp_sample *xxs = &mod->xxs[i];
struct extra_sample_data *xtra = &m->xtra[i];
if (xtra->sus < 0) {
xtra->sus = 0;
}
if (xtra->sue > xxs->len) {
xtra->sue = xxs->len;
}
if (xtra->sus >= xxs->len || xtra->sus >= xtra->sue) {
xtra->sus = xtra->sue = 0;
xxs->flg &= ~(XMP_SAMPLE_SLOOP | XMP_SAMPLE_SLOOP_BIDIR);
}
}
#endif
p->filter = 0;
p->mode = XMP_MODE_AUTO;
p->flags = p->player_flags;
#ifndef LIBXMP_CORE_PLAYER
module_quirks(ctx);
#endif
libxmp_set_player_mode(ctx);
}
int libxmp_prepare_scan(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, ord;
if (mod->xxp == NULL || mod->xxt == NULL)
return -XMP_ERROR_LOAD;
ord = 0;
while (ord < mod->len && mod->xxo[ord] >= mod->pat) {
ord++;
}
if (ord >= mod->len) {
mod->len = 0;
return 0;
}
m->scan_cnt = (uint8 **) calloc(mod->len, sizeof(uint8 *));
if (m->scan_cnt == NULL)
return -XMP_ERROR_SYSTEM;
for (i = 0; i < mod->len; i++) {
int pat_idx = mod->xxo[i];
struct xmp_pattern *pat;
/* Add pattern if referenced in orders */
if (pat_idx < mod->pat && !mod->xxp[pat_idx]) {
if (libxmp_alloc_pattern(mod, pat_idx) < 0) {
return -XMP_ERROR_SYSTEM;
}
}
pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx];
m->scan_cnt[i] = (uint8 *) calloc(1, (pat && pat->rows)? pat->rows : 1);
if (m->scan_cnt[i] == NULL)
return -XMP_ERROR_SYSTEM;
}
return 0;
}
void libxmp_free_scan(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i;
if (m->scan_cnt) {
for (i = 0; i < mod->len; i++)
free(m->scan_cnt[i]);
free(m->scan_cnt);
m->scan_cnt = NULL;
}
free(p->scan);
p->scan = NULL;
}
/* Process player personality flags */
int libxmp_set_player_mode(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int q;
switch (p->mode) {
case XMP_MODE_AUTO:
break;
case XMP_MODE_MOD:
m->c4rate = C4_PAL_RATE;
m->quirk = 0;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA;
break;
case XMP_MODE_NOISETRACKER:
m->c4rate = C4_PAL_RATE;
m->quirk = QUIRK_NOBPM;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_MODRNG;
break;
case XMP_MODE_PROTRACKER:
m->c4rate = C4_PAL_RATE;
m->quirk = QUIRK_PROTRACK;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_MODRNG;
break;
case XMP_MODE_S3M:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | q;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_ST3:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_ST3GUS:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
m->quirk &= ~QUIRK_RSTCHN;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_XM:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_FT2;
m->read_event_type = READ_EVENT_FT2;
break;
case XMP_MODE_FT2:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_FT2 | QUIRK_FT2BUGS;
m->read_event_type = READ_EVENT_FT2;
break;
case XMP_MODE_IT:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
m->read_event_type = READ_EVENT_IT;
break;
case XMP_MODE_ITSMP:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN);
m->read_event_type = READ_EVENT_IT;
break;
default:
return -1;
}
return 0;
}

View File

@ -0,0 +1,262 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
static int c669_test (HIO_HANDLE *, char *, const int);
static int c669_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_669 = {
"Composer 669",
c669_test,
c669_load
};
static int c669_test(HIO_HANDLE *f, char *t, const int start)
{
uint16 id;
id = hio_read16b(f);
if (id != 0x6966 && id != 0x4a4e)
return -1;
hio_seek(f, 110, SEEK_SET);
if (hio_read8(f) > 64)
return -1;
if (hio_read8(f) > 128)
return -1;
hio_seek(f, 240, SEEK_SET);
if (hio_read8(f) != 0xff)
return -1;
hio_seek(f, start + 2, SEEK_SET);
libxmp_read_title(f, t, 36);
return 0;
}
struct c669_file_header {
uint8 marker[2]; /* 'if'=standard, 'JN'=extended */
uint8 message[108]; /* Song message */
uint8 nos; /* Number of samples (0-64) */
uint8 nop; /* Number of patterns (0-128) */
uint8 loop; /* Loop order number */
uint8 order[128]; /* Order list */
uint8 speed[128]; /* Tempo list for patterns */
uint8 pbrk[128]; /* Break list for patterns */
};
struct c669_instrument_header {
uint8 name[13]; /* ASCIIZ instrument name */
uint32 length; /* Instrument length */
uint32 loop_start; /* Instrument loop start */
uint32 loopend; /* Instrument loop end */
};
#define NONE 0xff
/* Effects bug fixed by Miod Vallat <miodrag@multimania.com> */
static const uint8 fx[6] = {
FX_669_PORTA_UP,
FX_669_PORTA_DN,
FX_669_TPORTA,
FX_669_FINETUNE,
FX_669_VIBRATO,
FX_SPEED_CP
};
static int c669_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct c669_file_header sfh;
struct c669_instrument_header sih;
uint8 ev[3];
LOAD_INIT();
hio_read(sfh.marker, 2, 1, f); /* 'if'=standard, 'JN'=extended */
hio_read(sfh.message, 108, 1, f); /* Song message */
sfh.nos = hio_read8(f); /* Number of samples (0-64) */
sfh.nop = hio_read8(f); /* Number of patterns (0-128) */
/* Sanity check */
if (sfh.nos > 64 || sfh.nop > 128)
return -1;
sfh.loop = hio_read8(f); /* Loop order number */
if (hio_read(sfh.order, 1, 128, f) != 128) /* Order list */
return -1;
if (hio_read(sfh.speed, 1, 128, f) != 128) /* Tempo list for patterns */
return -1;
if (hio_read(sfh.pbrk, 1, 128, f) != 128) /* Break list for patterns */
return -1;
mod->chn = 8;
mod->ins = sfh.nos;
mod->pat = sfh.nop;
mod->trk = mod->chn * mod->pat;
for (i = 0; i < 128; i++) {
if (sfh.order[i] > sfh.nop)
break;
}
mod->len = i;
memcpy (mod->xxo, sfh.order, mod->len);
mod->spd = 6;
mod->bpm = 78;
mod->smp = mod->ins;
m->period_type = PERIOD_CSPD;
m->c4rate = C4_NTSC_RATE;
libxmp_copy_adjust(mod->name, sfh.message, 36);
libxmp_set_type(m, strncmp((char *)sfh.marker, "if", 2) ?
"UNIS 669" : "Composer 669");
MODULE_INFO();
m->comment = (char *) malloc(109);
memcpy(m->comment, sfh.message, 108);
m->comment[108] = 0;
/* Read and convert instruments and samples */
if (libxmp_init_instrument(m) < 0)
return -1;
D_(D_INFO "Instruments: %d", mod->pat);
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
sub = &xxi->sub[0];
hio_read (sih.name, 13, 1, f); /* ASCIIZ instrument name */
sih.length = hio_read32l(f); /* Instrument size */
sih.loop_start = hio_read32l(f); /* Instrument loop start */
sih.loopend = hio_read32l(f); /* Instrument loop end */
/* Sanity check */
if (sih.length > MAX_SAMPLE_SIZE)
return -1;
xxs->len = sih.length;
xxs->lps = sih.loop_start;
xxs->lpe = sih.loopend >= 0xfffff ? 0 : sih.loopend;
xxs->flg = xxs->lpe ? XMP_SAMPLE_LOOP : 0; /* 1 == Forward loop */
sub->vol = 0x40;
sub->pan = 0x80;
sub->sid = i;
if (xxs->len > 0)
xxi->nsm = 1;
libxmp_instrument_name(mod, i, sih.name, 13);
D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c", i,
xxi->name, xxs->len, xxs->lps, xxs->lpe,
xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ');
}
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
int pbrk;
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
event = &EVENT(i, 0, 0);
event->f2t = FX_SPEED_CP;
event->f2p = sfh.speed[i];
pbrk = sfh.pbrk[i];
if (pbrk >= 64)
return -1;
event = &EVENT(i, 1, pbrk);
event->f2t = FX_BREAK;
event->f2p = 0;
for (j = 0; j < 64 * 8; j++) {
event = &EVENT(i, j % 8, j / 8);
if(hio_read(ev, 1, 3, f) < 3) {
D_(D_CRIT "read error at pat %d", i);
return -1;
}
if ((ev[0] & 0xfe) != 0xfe) {
event->note = 1 + 36 + (ev[0] >> 2);
event->ins = 1 + MSN(ev[1]) + ((ev[0] & 0x03) << 4);
}
if (ev[0] != 0xff)
event->vol = (LSN(ev[1]) << 2) + 1;
if (ev[2] != 0xff) {
if (MSN(ev[2]) >= ARRAY_SIZE(fx))
continue;
event->fxt = fx[MSN(ev[2])];
event->fxp = LSN(ev[2]);
if (event->fxt == FX_SPEED_CP) {
event->f2t = FX_PER_CANCEL;
}
}
}
}
/* Read samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (mod->xxs[i].len <= 2)
continue;
if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0)
return -1;
}
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = DEFPAN((i % 2) * 0xff);
}
m->quirk |= QUIRK_PBALL|QUIRK_PERPAT;
return 0;
}

View File

@ -0,0 +1,655 @@
/* Extended Module Player
* AMOS/STOS Music Bank Loader
* Copyright (C) 2014 Stephen J Leary and Claudio Matsuoka
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "../effects.h"
#include "../period.h"
#define AMOS_BANK 0x416d426b
#define AMOS_MUSIC_TYPE 0x0003
#define AMOS_MAIN_HEADER 0x14L
#define AMOS_STRING_LEN 0x10
#define AMOS_BASE_FREQ 8192
#define AMOS_ABK_CHANNELS 4
#define ABK_HEADER_SECTION_COUNT 3
static int abk_test (HIO_HANDLE *, char *, const int);
static int abk_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_abk =
{
"AMOS Music Bank",
abk_test,
abk_load
};
/**
* @class abk_header
* @brief represents the main ABK header.
*/
struct abk_header
{
uint32 instruments_offset;
uint32 songs_offset;
uint32 patterns_offset;
};
/**
* @class abk_song
* @brief represents a song in an ABK module.
*/
struct abk_song
{
uint32 playlist_offset[AMOS_ABK_CHANNELS];
uint16 tempo;
char song_name[AMOS_STRING_LEN];
};
/**
* @class abk_playlist
* @brief represents an ABK playlist.
*/
struct abk_playlist
{
uint16 length;
uint16 *pattern;
};
/**
* @class abk_instrument
* @brief represents an ABK instrument.
*/
struct abk_instrument
{
uint32 sample_offset;
uint32 sample_length;
uint32 repeat_offset;
uint16 repeat_end;
uint16 sample_volume;
char sample_name[AMOS_STRING_LEN];
};
/**
* @brief read the ABK playlist out from the file stream. This method malloc's some memory for the playlist
* and can realloc if the playlist is very long.
* @param f the stream to read from
* @param playlist_offset the offset to the playlist sections.
* @param playlist this structure is populated with the result.
*/
static void read_abk_playlist(HIO_HANDLE *f, uint32 playlist_offset, struct abk_playlist *playlist)
{
uint16 playdata;
int arraysize;
arraysize = 64;
/* clear the length */
playlist->length = 0;
/* move to the start of the songs data section. */
hio_seek(f, playlist_offset, SEEK_SET);
playlist->pattern = (uint16 *) malloc(arraysize * sizeof(uint16));
playdata = hio_read16b(f);
while((playdata != 0xFFFF) && (playdata != 0xFFFE))
{
/* i hate doing a realloc in a loop
but i'd rather not traverse the list twice.*/
if (playlist->length >= arraysize)
{
arraysize *= 2;
playlist->pattern = (uint16 *) realloc(playlist->pattern , arraysize * sizeof(uint16));
}
playlist->pattern[playlist->length++] = playdata;
playdata = hio_read16b(f);
};
}
static int read_abk_song(HIO_HANDLE *f, struct abk_song *song, uint32 songs_section_offset)
{
int i;
uint32 song_section;
/* move to the start of the songs data section */
hio_seek(f, songs_section_offset, SEEK_SET);
if (hio_read16b(f) != 1)
{
/* we only support a single song.
* in a an abk file for now */
return -1;
}
song_section = hio_read32b(f);
if (hio_seek(f, songs_section_offset + song_section, SEEK_SET) < 0) {
return -1;
}
for (i=0; i<AMOS_ABK_CHANNELS; i++)
{
song->playlist_offset[i] = hio_read16b(f) + songs_section_offset + song_section;
}
song->tempo = hio_read16b(f);
/* unused. just progress the file pointer forward */
(void) hio_read16b(f);
if (hio_read(song->song_name, 1, AMOS_STRING_LEN, f) != AMOS_STRING_LEN) {
return -1;
}
return 0;
}
/**
* @brief reads an ABK pattern into a xmp_event structure.
* @param f stream to read data from.
* @param events events object to populate.
* @param pattern_offset_abs the absolute file offset to the start of the patter to read.
* @return returns the size of the pattern.
*/
static int read_abk_pattern(HIO_HANDLE *f, struct xmp_event *events, uint32 pattern_offset_abs)
{
uint8 position;
uint8 command;
uint8 param;
uint8 inst = 0;
uint8 jumped = 0;
uint8 per_command = 0;
uint8 per_param = 0;
uint16 delay;
uint16 patdata;
int storepos;
if ((storepos = hio_tell(f)) < 0) {
return -1;
}
/* count how many abk positions are used in this pattern */
position = 0;
hio_seek(f, pattern_offset_abs, SEEK_SET);
/* read the first bit of pattern data */
patdata = hio_read16b(f);
while ((patdata != 0x8000) && (jumped == 0))
{
if (patdata == 0x9100)
{
break;
}
if (patdata & 0x8000)
{
command = (patdata >> 8) & 0x7F;
param = patdata & 0x007F;
if (command != 0x03 && command != 0x09 && command != 0x0b && command != 0x0c && command != 0x0d && command < 0x10) {
per_command = 0;
per_param = 0;
}
switch (command)
{
case 0x01: /* portamento up */
case 0x0e:
events[position].fxt = FX_PORTA_UP;
events[position].fxp = param;
break;
case 0x02: /* portamento down */
case 0x0f:
events[position].fxt = FX_PORTA_DN;
events[position].fxp = param;
break;
case 0x03: /* set volume */
events[position].fxt = FX_VOLSET;
events[position].fxp = param;
break;
case 0x04: /* stop effect */
break;
case 0x05: /* repeat */
events[position].fxt = FX_EXTENDED;
if (param == 0) {
events[position].fxp = 0x50;
} else {
events[position].fxp = 0x60 | (param & 0x0f);
}
break;
case 0x06: /* lowpass filter off */
events[position].fxt = FX_EXTENDED;
events[position].fxp = 0x00;
break;
case 0x07: /* lowpass filter on */
events[position].fxt = FX_EXTENDED;
events[position].fxp = 0x01;
break;
case 0x08: /* set tempo */
if (param > 0) {
events[position].fxt = FX_SPEED;
events[position].fxp = 100/param;
}
break;
case 0x09: /* set instrument */
inst = param + 1;
break;
case 0x0a: /* arpeggio */
per_command = FX_ARPEGGIO;
per_param = param;
break;
case 0x0b: /* tone portamento */
per_command = FX_TONEPORTA;
per_param = param;
break;
case 0x0c: /* vibrato */
per_command = FX_VIBRATO;
per_param = param;
break;
case 0x0d: /* volume slide */
if (param != 0) {
per_command = FX_VOLSLIDE;
per_param = param;
} else {
per_command = 0;
per_param = 0;
}
break;
case 0x10: /* delay */
if (per_command != 0 || per_param != 0) {
int i;
for (i = 0; i < param && position < 64; i++) {
events[position].fxt = per_command;
events[position].fxp = per_param;
position++;
}
} else {
position += param;
}
if (position >= 64) {
jumped = 1;
}
break;
case 0x11: /* position jump */
events[position].fxt = FX_JUMP;
events[position].fxp = param;
/* break out of the loop because we've jumped.*/
jumped = 1;
break;
default:
#if 0
/* write out an error for any unprocessed commands.*/
D_(D_WARN "ABK UNPROCESSED COMMAND: %x,%x\n", command, param);
break;
#else
return -1;
#endif
}
}
else
{
if (patdata & 0x4000)
{
/* old note format.*/
/* old note format is 2 x 2 byte words with bit 14 set on the first word */
/* WORD 1: 0x4XDD | X = dont care, D = delay to apply after note. (Usually in 7FDD form).
* WORD 2: 0xXPPP | PPP = Amiga Period */
delay = patdata & 0xff;
patdata = hio_read16b(f);
if (patdata == 0 && delay == 0)
{
/* a zero note, with zero delay ends the pattern */
break;
}
if (patdata != 0)
{
/* convert the note from amiga period format to xmp's internal format.*/
events[position].note = libxmp_period_to_note(patdata & 0x0fff);
events[position].ins = inst;
}
/* now add on the delay */
position += delay;
if (position >= 64) {
break;
}
}
else /* new note format */
{
/* convert the note from amiga period format to xmp's internal format.*/
events[position].note = libxmp_period_to_note(patdata & 0x0fff);
events[position].ins = inst;
}
}
/* read the data for the next pass round the loop */
patdata = hio_read16b(f);
/* check for an EOF while reading */
if (hio_eof(f))
{
break;
}
}
if (position < 64) {
events[position - 1].f2t = FX_BREAK;
}
hio_seek(f, storepos, SEEK_SET);
return position;
}
static struct abk_instrument* read_abk_insts(HIO_HANDLE *f, uint32 inst_section_size, int count)
{
uint16 i;
struct abk_instrument *inst;
if (count < 1)
return NULL;
inst = (struct abk_instrument*) malloc(count * sizeof(struct abk_instrument));
memset(inst, 0, count * sizeof(struct abk_instrument));
for (i = 0; i < count; i++)
{
uint32 sampleLength;
inst[i].sample_offset = hio_read32b(f);
inst[i].repeat_offset = hio_read32b(f);
inst[i].sample_length = hio_read16b(f);
inst[i].repeat_end = hio_read16b(f);
inst[i].sample_volume = hio_read16b(f);
sampleLength = hio_read16b(f);
/* detect a potential bug where the sample length is not specified (and we might already know the length) */
if (sampleLength > 4)
{
inst[i].sample_length = sampleLength;
}
if (hio_read(inst[i].sample_name, 1, 16, f) != 16) {
free(inst);
return NULL;
}
}
return inst;
}
static int abk_test(HIO_HANDLE *f, char *t, const int start)
{
struct abk_song song;
char music[8];
uint32 song_section_offset;
if (hio_read32b(f) != AMOS_BANK)
{
return -1;
}
if (hio_read16b(f) != AMOS_MUSIC_TYPE)
{
return -1;
}
/* skip over length and chip/fastmem.*/
hio_seek(f, 6, SEEK_CUR);
if (hio_read(music, 1, 8, f) != 8) /* get the "Music " */
return -1;
if (memcmp(music, "Music ", 8))
{
return -1;
}
/* Attempt to read title. */
hio_read32b(f); /* instruments_offset */
song_section_offset = hio_read32b(f);
if (t != NULL && read_abk_song(f, &song, AMOS_MAIN_HEADER + song_section_offset) == 0)
{
libxmp_copy_adjust(t, (uint8 *)song.song_name, AMOS_STRING_LEN);
}
return 0;
}
static int abk_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
int i,j,k;
uint16 pattern;
uint32 first_sample_offset;
uint32 inst_section_size;
struct xmp_module *mod = &m->mod;
struct abk_header main_header;
struct abk_instrument *ci;
struct abk_song song;
struct abk_playlist playlist;
hio_seek(f, AMOS_MAIN_HEADER, SEEK_SET);
main_header.instruments_offset = hio_read32b(f);
main_header.songs_offset = hio_read32b(f);
main_header.patterns_offset = hio_read32b(f);
/* Sanity check */
if (main_header.instruments_offset > 0x00100000 ||
main_header.songs_offset > 0x00100000 ||
main_header.patterns_offset > 0x00100000) {
return -1;
}
inst_section_size = main_header.instruments_offset;
D_(D_INFO "Sample Bytes: %d", inst_section_size);
LOAD_INIT();
libxmp_set_type(m, "AMOS Music Bank");
if (read_abk_song(f, &song, AMOS_MAIN_HEADER + main_header.songs_offset) < 0)
{
return -1;
}
libxmp_copy_adjust(mod->name, (uint8*) song.song_name, AMOS_STRING_LEN);
MODULE_INFO();
hio_seek(f, AMOS_MAIN_HEADER + main_header.patterns_offset, SEEK_SET);
mod->chn = AMOS_ABK_CHANNELS;
mod->pat = hio_read16b(f);
/* Sanity check */
if (mod->pat > 256) {
return -1;
}
mod->trk = mod->chn * mod->pat;
/* move to the start of the instruments section. */
hio_seek(f, AMOS_MAIN_HEADER + main_header.instruments_offset, SEEK_SET);
mod->ins = hio_read16b(f);
/* Sanity check */
if (mod->ins > 255) {
return -1;
}
mod->smp = mod->ins;
/* Read and convert instruments and samples */
if (libxmp_init_instrument(m) < 0)
{
return -1;
}
D_(D_INFO "Instruments: %d", mod->ins);
/* read all the instruments in */
ci = read_abk_insts(f, inst_section_size, mod->ins);
if (ci == NULL) {
return -1;
}
/* store the location of the first sample so we can read them later. */
first_sample_offset = AMOS_MAIN_HEADER + main_header.instruments_offset + ci[0].sample_offset;
for (i = 0; i < mod->ins; i++)
{
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
{
free(ci);
return -1;
}
mod->xxs[i].len = ci[i].sample_length << 1;
if (mod->xxs[i].len > 0)
{
mod->xxi[i].nsm = 1;
}
/* the repeating stuff. */
if (ci[i].repeat_offset > ci[i].sample_offset)
{
mod->xxs[i].lps = (ci[i].repeat_offset - ci[i].sample_offset) << 1;
}
else
{
mod->xxs[i].lps = 0;
}
mod->xxs[i].lpe = ci[i].repeat_end;
if (mod->xxs[i].lpe > 2) {
mod->xxs[i].lpe <<= 1;
mod->xxs[i].flg = XMP_SAMPLE_LOOP;
}
/*printf("%02x lps=%04x lpe=%04x\n", i, mod->xxs[i].lps, mod->xxs[i].lpe);*/
mod->xxi[i].sub[0].vol = ci[i].sample_volume;
mod->xxi[i].sub[0].pan = 0x80;
mod->xxi[i].sub[0].sid = i;
libxmp_instrument_name(mod, i, (uint8*)ci[i].sample_name, 16);
D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c", i,
mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ');
}
free(ci);
if (libxmp_init_pattern(mod) < 0)
{
return -1;
}
/* figure out the playlist order.
* TODO: if the 4 channels arent in the same order then
* we need to fail here. */
read_abk_playlist(f, song.playlist_offset[0], &playlist);
/* move to the start of the instruments section */
/* then convert the patterns one at a time. there is a pattern for each channel.*/
hio_seek(f, AMOS_MAIN_HEADER + main_header.patterns_offset + 2, SEEK_SET);
mod->len = 0;
i = 0;
for (j = 0; j < mod->pat; j++)
{
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
{
free(playlist.pattern);
return -1;
}
for (k = 0; k < mod->chn; k++)
{
pattern = hio_read16b(f);
if (read_abk_pattern(f, mod->xxt[(i*mod->chn)+k]->event, AMOS_MAIN_HEADER + main_header.patterns_offset + pattern) < 0) {
free(playlist.pattern);
return -1;
}
}
i++;
}
/* Sanity check */
if (playlist.length > 256) {
free(playlist.pattern);
return -1;
}
mod->len = playlist.length;
/* now push all the patterns into the module and set the length */
for (i = 0; i < playlist.length; i++)
{
mod->xxo[i] = playlist.pattern[i];
}
/* free up some memory here */
free(playlist.pattern);
D_(D_INFO "Stored patterns: %d", mod->pat);
D_(D_INFO "Stored tracks: %d", mod->trk);
/* Read samples */
hio_seek(f, first_sample_offset, SEEK_SET);
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++)
{
if (mod->xxs[i].len <= 2)
continue;
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
{
return -1;
}
}
return 0;
}

View File

@ -0,0 +1,187 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* ALM (Aley's Module) is a module format used on 8bit computers. It was
* designed to be usable on Sam Coupe (CPU Z80 6MHz) and PC XT. The ALM file
* format is very simple and it have no special effects, so every computer
* can play the ALMs.
*
* Note: xmp's module loading mechanism was not designed to load samples
* from different files. Using *module into a global variable is a hack.
*/
#include "loader.h"
static int alm_test (HIO_HANDLE *, char *, const int);
static int alm_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_alm = {
"Aley Keptr (ALM)",
alm_test,
alm_load
};
static int alm_test(HIO_HANDLE *f, char *t, const int start)
{
char buf[7];
if (HIO_HANDLE_TYPE(f) != HIO_HANDLE_TYPE_FILE)
return -1;
if (hio_read(buf, 1, 7, f) < 7)
return -1;
if (memcmp(buf, "ALEYMOD", 7) && memcmp(buf, "ALEY MO", 7))
return -1;
libxmp_read_title(f, t, 0);
return 0;
}
struct alm_file_header {
uint8 id[7]; /* "ALEY MO" or "ALEYMOD" */
uint8 speed; /* Only in versions 1.1 and 1.2 */
uint8 length; /* Length of module */
uint8 restart; /* Restart position */
uint8 order[128]; /* Pattern sequence */
};
#define NAME_SIZE 255
static int alm_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct alm_file_header afh;
struct xmp_event *event;
uint8 b;
char *basename;
char filename[NAME_SIZE];
char modulename[NAME_SIZE];
LOAD_INIT();
hio_read(&afh.id, 7, 1, f);
if (!strncmp((char *)afh.id, "ALEYMOD", 7)) /* Version 1.0 */
mod->spd = afh.speed / 2;
strncpy(modulename, m->filename, NAME_SIZE);
basename = strtok (modulename, ".");
afh.speed = hio_read8(f);
afh.length = hio_read8(f);
afh.restart = hio_read8(f);
hio_read(&afh.order, 128, 1, f);
mod->len = afh.length;
mod->rst = afh.restart;
memcpy (mod->xxo, afh.order, mod->len);
for (mod->pat = i = 0; i < mod->len; i++)
if (mod->pat < afh.order[i])
mod->pat = afh.order[i];
mod->pat++;
mod->ins = 31;
mod->trk = mod->pat * mod->chn;
mod->smp = mod->ins;
m->c4rate = C4_NTSC_RATE;
libxmp_set_type(m, "Aley's Module");
MODULE_INFO();
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
for (j = 0; j < 64 * mod->chn; j++) {
event = &EVENT (i, j % mod->chn, j / mod->chn);
b = hio_read8(f);
if (b)
event->note = (b == 37) ? 0x61 : b + 48;
event->ins = hio_read8(f);
}
}
if (libxmp_init_instrument(m) < 0)
return -1;
/* Read and convert instruments and samples */
D_(D_INFO "Loading samples: %d", mod->ins);
for (i = 0; i < mod->ins; i++) {
HIO_HANDLE *s;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
mod->xxi[i].sub = calloc(sizeof (struct xmp_subinstrument), 1);
snprintf(filename, NAME_SIZE, "%s.%d", basename, i + 1);
s = hio_open(filename, "rb");
if (s == NULL)
continue;
mod->xxi[i].nsm = 1;
b = hio_read8(s); /* Get first octet */
mod->xxs[i].len = hio_size(s) - 5 * !b;
if (!b) { /* Instrument with header */
mod->xxs[i].lps = hio_read16l(f);
mod->xxs[i].lpe = hio_read16l(f);
mod->xxs[i].flg = mod->xxs[i].lpe > mod->xxs[i].lps ? XMP_SAMPLE_LOOP : 0;
} else {
hio_seek(s, 0, SEEK_SET);
}
mod->xxi[i].sub[0].pan = 0x80;
mod->xxi[i].sub[0].vol = 0x40;
mod->xxi[i].sub[0].sid = i;
D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c V%02x", i,
filename, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', mod->xxi[i].sub[0].vol);
if (libxmp_load_sample(m, s, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0)
return -1;
hio_close(s);
}
/* ALM is LRLR, not LRRL */
for (i = 0; i < mod->chn; i++)
mod->xxc[i].pan = DEFPAN((i % 2) * 0xff);
return 0;
}

View File

@ -0,0 +1,592 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* AMF loader written based on the format specs by Miodrag Vallat with
* fixes by Andre Timmermans.
*
* The AMF format is the internal format used by DSMI, the DOS Sound and Music
* Interface, which is the engine of DMP. As DMP was able to play more and more
* module formats, the format evolved to support more features. There were 5
* official formats, numbered from 10 (AMF 1.0) to 14 (AMF 1.4).
*/
#include "loader.h"
#include "../period.h"
static int amf_test(HIO_HANDLE *, char *, const int);
static int amf_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_amf = {
"DSMI Advanced Module Format",
amf_test,
amf_load
};
static int amf_test(HIO_HANDLE * f, char *t, const int start)
{
char buf[4];
int ver;
if (hio_read(buf, 1, 3, f) < 3)
return -1;
if (buf[0] != 'A' || buf[1] != 'M' || buf[2] != 'F')
return -1;
ver = hio_read8(f);
if ((ver != 0x01 && ver < 0x08) || ver > 0x0e)
return -1;
libxmp_read_title(f, t, 32);
return 0;
}
static int amf_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
uint8 buf[1024];
int *trkmap, newtrk;
int no_loopend = 0;
int ver;
LOAD_INIT();
hio_read(buf, 1, 3, f);
ver = hio_read8(f);
if (hio_read(buf, 1, 32, f) != 32)
return -1;
memcpy(mod->name, buf, 32);
mod->name[32] = '\0';
libxmp_set_type(m, "DSMI %d.%d AMF", ver / 10, ver % 10);
mod->ins = hio_read8(f);
mod->len = hio_read8(f);
mod->trk = hio_read16l(f);
mod->chn = 4;
if (ver >= 0x09) {
mod->chn = hio_read8(f);
}
/* Sanity check */
if (mod->ins == 0 || mod->len == 0 || mod->trk == 0
|| mod->chn == 0 || mod->chn > XMP_MAX_CHANNELS) {
return -1;
}
mod->smp = mod->ins;
mod->pat = mod->len;
if (ver == 0x09 || ver == 0x0a)
hio_read(buf, 1, 16, f); /* channel remap table */
if (ver >= 0x0d) {
if (hio_read(buf, 1, 32, f) != 32) /* panning table */
return -1;
for (i = 0; i < 32; i++) {
mod->xxc->pan = 0x80 + 2 * (int8)buf[i];
}
mod->bpm = hio_read8(f);
mod->spd = hio_read8(f);
} else if (ver >= 0x0b) {
hio_read(buf, 1, 16, f);
}
m->c4rate = C4_NTSC_RATE;
MODULE_INFO();
/* Orders */
/*
* Andre Timmermans <andre.timmermans@atos.net> says:
*
* Order table: track numbers in this table are not explained,
* but as you noticed you have to perform -1 to obtain the index
* in the track table. For value 0, found in some files, I think
* it means an empty track.
*
* 2021 note: this is misleading. Do not subtract 1 from the logical
* track values found in the order table; load the mapping table to
* index 1 instead.
*/
for (i = 0; i < mod->len; i++)
mod->xxo[i] = i;
D_(D_INFO "Stored patterns: %d", mod->pat);
mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *));
if (mod->xxp == NULL)
return -1;
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern(mod, i) < 0)
return -1;
mod->xxp[i]->rows = ver >= 0x0e ? hio_read16l(f) : 64;
if (mod->xxp[i]->rows > 256)
return -1;
for (j = 0; j < mod->chn; j++) {
uint16 t = hio_read16l(f);
mod->xxp[i]->index[j] = t;
}
}
/* Instruments */
if (libxmp_init_instrument(m) < 0)
return -1;
/* Probe for 2-byte loop start 1.0 format
* in facing_n.amf and sweetdrm.amf have only the sample
* loop start specified in 2 bytes
*
* These modules are an early variant of the AMF 1.0 format. Since
* normal AMF 1.0 files have 32-bit lengths/loop start/loop end,
* this is possibly caused by these fields having been expanded for
* the 1.0 format, but M2AMF 1.3 writing instrument structs with
* the old length (which would explain the missing 6 bytes).
*/
if (ver == 0x0a) {
uint8 b;
uint32 len, val;
long pos = hio_tell(f);
if (pos < 0) {
return -1;
}
for (i = 0; i < mod->ins; i++) {
b = hio_read8(f);
if (b != 0 && b != 1) {
no_loopend = 1;
break;
}
hio_seek(f, 32 + 13, SEEK_CUR);
if (hio_read32l(f) > (uint32)mod->ins) { /* check index */
no_loopend = 1;
break;
}
len = hio_read32l(f);
if (len > 0x100000) { /* check len */
no_loopend = 1;
break;
}
if (hio_read16l(f) == 0x0000) { /* check c2spd */
no_loopend = 1;
break;
}
if (hio_read8(f) > 0x40) { /* check volume */
no_loopend = 1;
break;
}
val = hio_read32l(f); /* check loop start */
if (val > len) {
no_loopend = 1;
break;
}
val = hio_read32l(f); /* check loop end */
if (val > len) {
no_loopend = 1;
break;
}
}
hio_seek(f, pos, SEEK_SET);
}
if (no_loopend) {
D_(D_INFO "Detected AMF 1.0 truncated instruments.");
}
for (i = 0; i < mod->ins; i++) {
int c2spd;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
hio_read8(f);
hio_read(buf, 1, 32, f);
libxmp_instrument_name(mod, i, buf, 32);
hio_read(buf, 1, 13, f); /* sample name */
hio_read32l(f); /* sample index */
mod->xxi[i].nsm = 1;
mod->xxi[i].sub[0].sid = i;
mod->xxi[i].sub[0].pan = 0x80;
if (ver >= 0x0a) {
mod->xxs[i].len = hio_read32l(f);
} else {
mod->xxs[i].len = hio_read16l(f);
}
c2spd = hio_read16l(f);
libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin);
mod->xxi[i].sub[0].vol = hio_read8(f);
/*
* Andre Timmermans <andre.timmermans@atos.net> says:
*
* [Miodrag Vallat's] doc tells that in version 1.0 only
* sample loop start is present (2 bytes) but the files I
* have tells both start and end are present (2*4 bytes).
* Maybe it should be read as version < 1.0.
*
* CM: confirmed with Maelcum's "The tribal zone"
*/
if (no_loopend != 0) {
mod->xxs[i].lps = hio_read16l(f);
mod->xxs[i].lpe = mod->xxs[i].len;
} else if (ver >= 0x0a) {
mod->xxs[i].lps = hio_read32l(f);
mod->xxs[i].lpe = hio_read32l(f);
} else {
/* Non-looping samples are stored with lpe=-1, not 0. */
mod->xxs[i].lps = hio_read16l(f);
mod->xxs[i].lpe = hio_read16l(f);
if (mod->xxs[i].lpe == 0xffff)
mod->xxs[i].lpe = 0;
}
if (no_loopend != 0) {
mod->xxs[i].flg = mod->xxs[i].lps > 0 ? XMP_SAMPLE_LOOP : 0;
} else {
mod->xxs[i].flg = mod->xxs[i].lpe > mod->xxs[i].lps ?
XMP_SAMPLE_LOOP : 0;
}
D_(D_INFO "[%2X] %-32.32s %05x %05x %05x %c V%02x %5d",
i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps,
mod->xxs[i].lpe, mod->xxs[i].flg & XMP_SAMPLE_LOOP ?
'L' : ' ', mod->xxi[i].sub[0].vol, c2spd);
}
if (hio_error(f))
return -1;
/* Tracks */
/* Index 0 is a blank track that isn't stored in the file. To keep
* things simple, load the mapping table to index 1 so the table
* index is the same as the logical track value. Older versions
* attempted to remap it to index 0 and subtract 1 from the index,
* breaking modules that directly reference the empty track in the
* order table (see "cosmos st.amf").
*/
trkmap = (int *) calloc(mod->trk + 1, sizeof(int));
if (trkmap == NULL)
return -1;
newtrk = 0;
for (i = 1; i <= mod->trk; i++) { /* read track table */
uint16 t;
t = hio_read16l(f);
trkmap[i] = t;
if (t > newtrk) newtrk = t;
}
for (i = 0; i < mod->pat; i++) { /* read track table */
for (j = 0; j < mod->chn; j++) {
uint16 k = mod->xxp[i]->index[j];
/* Use empty track if an invalid track is requested
* (such as in Lasse Makkonen "faster and louder")
*/
if (k > mod->trk)
k = 0;
mod->xxp[i]->index[j] = trkmap[k];
}
}
mod->trk = newtrk + 1; /* + empty track */
free(trkmap);
if (hio_error(f))
return -1;
D_(D_INFO "Stored tracks: %d", mod->trk - 1);
mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *));
if (mod->xxt == NULL)
return -1;
/* Alloc track 0 as empty track */
if (libxmp_alloc_track(mod, 0, 64) < 0)
return -1;
/* Alloc rest of the tracks */
for (i = 1; i < mod->trk; i++) {
uint8 t1, t2, t3;
int size;
if (libxmp_alloc_track(mod, i, 64) < 0) /* FIXME! */
return -1;
/* Previous versions loaded this as a 24-bit value, but it's
* just a word. The purpose of the third byte is unknown, and
* DSMI just ignores it.
*/
size = hio_read16l(f);
hio_read8(f);
if (hio_error(f))
return -1;
/* Version 0.1 AMFs do not count the end-of-track marker in
* the event count, so add 1. This hasn't been verified yet. */
if (ver == 0x01 && size != 0)
size++;
for (j = 0; j < size; j++) {
t1 = hio_read8(f); /* row */
t2 = hio_read8(f); /* type */
t3 = hio_read8(f); /* parameter */
if (t1 == 0xff && t2 == 0xff && t3 == 0xff)
break;
/* If an event is encountered past the end of the
* track, treat it the same as the track end. This is
* encountered in "Avoid.amf".
*/
if (t1 >= mod->xxt[i]->rows) {
if (hio_seek(f, (size - j - 1) * 3, SEEK_CUR))
return -1;
break;
}
event = &mod->xxt[i]->event[t1];
if (t2 < 0x7f) { /* note */
if (t2 > 0)
event->note = t2 + 1;
/* A volume value of 0xff indicates that
* the old volume should be reused. Prior
* libxmp versions also forgot to add 1 here.
*/
event->vol = (t3 != 0xff) ? (t3 + 1) : 0;
} else if (t2 == 0x7f) { /* note retrigger */
/* AMF.TXT claims that this duplicates the
* previous event, which is a lie. M2AMF emits
* this for MODs when an instrument change
* occurs with no note, indicating it should
* retrigger (like in PT 2.3). Ignore this.
*
* See: "aladdin - aladd.pc.amf", "eye.amf".
*/
} else if (t2 == 0x80) { /* instrument */
event->ins = t3 + 1;
} else { /* effects */
uint8 fxp, fxt;
fxp = fxt = 0;
switch (t2) {
case 0x81:
fxt = FX_S3M_SPEED;
fxp = t3;
break;
case 0x82:
if ((int8)t3 > 0) {
fxt = FX_VOLSLIDE;
fxp = t3 << 4;
} else {
fxt = FX_VOLSLIDE;
fxp = -(int8)t3 & 0x0f;
}
break;
case 0x83:
/* See volume notes above. Previous
* releases forgot to add 1 here. */
event->vol = (t3 + 1);
break;
case 0x84:
/* AT: Not explained for 0x84, pitch
* slide, value 0x00 corresponds to
* S3M E00 and 0x80 stands for S3M F00
* (I checked with M2AMF)
*/
if ((int8)t3 >= 0) {
fxt = FX_PORTA_DN;
fxp = t3;
} else if (t3 == 0x80) {
fxt = FX_PORTA_UP;
fxp = 0;
} else {
fxt = FX_PORTA_UP;
fxp = -(int8)t3;
}
break;
case 0x85:
/* porta abs -- unknown */
break;
case 0x86:
fxt = FX_TONEPORTA;
fxp = t3;
break;
/* AT: M2AMF maps both tremolo and tremor to
* 0x87. Since tremor is only found in certain
* formats, maybe it would be better to
* consider it is a tremolo.
*/
case 0x87:
fxt = FX_TREMOLO;
fxp = t3;
break;
case 0x88:
fxt = FX_ARPEGGIO;
fxp = t3;
break;
case 0x89:
fxt = FX_VIBRATO;
fxp = t3;
break;
case 0x8a:
if ((int8)t3 > 0) {
fxt = FX_TONE_VSLIDE;
fxp = t3 << 4;
} else {
fxt = FX_TONE_VSLIDE;
fxp = -(int8)t3 & 0x0f;
}
break;
case 0x8b:
if ((int8)t3 > 0) {
fxt = FX_VIBRA_VSLIDE;
fxp = t3 << 4;
} else {
fxt = FX_VIBRA_VSLIDE;
fxp = -(int8)t3 & 0x0f;
}
break;
case 0x8c:
fxt = FX_BREAK;
fxp = t3;
break;
case 0x8d:
fxt = FX_JUMP;
fxp = t3;
break;
case 0x8e:
/* sync -- unknown */
break;
case 0x8f:
fxt = FX_EXTENDED;
fxp = (EX_RETRIG << 4) | (t3 & 0x0f);
break;
case 0x90:
fxt = FX_OFFSET;
fxp = t3;
break;
case 0x91:
if ((int8)t3 > 0) {
fxt = FX_EXTENDED;
fxp = (EX_F_VSLIDE_UP << 4) |
(t3 & 0x0f);
} else {
fxt = FX_EXTENDED;
fxp = (EX_F_VSLIDE_DN << 4) |
(t3 & 0x0f);
}
break;
case 0x92:
if ((int8)t3 > 0) {
fxt = FX_PORTA_DN;
fxp = 0xf0 | (fxp & 0x0f);
} else {
fxt = FX_PORTA_UP;
fxp = 0xf0 | (fxp & 0x0f);
}
break;
case 0x93:
fxt = FX_EXTENDED;
fxp = (EX_DELAY << 4) | (t3 & 0x0f);
break;
case 0x94:
fxt = FX_EXTENDED;
fxp = (EX_CUT << 4) | (t3 & 0x0f);
break;
case 0x95:
fxt = FX_SPEED;
if (t3 < 0x21)
t3 = 0x21;
fxp = t3;
break;
case 0x96:
if ((int8)t3 > 0) {
fxt = FX_PORTA_DN;
fxp = 0xe0 | (fxp & 0x0f);
} else {
fxt = FX_PORTA_UP;
fxp = 0xe0 | (fxp & 0x0f);
}
break;
case 0x97:
/* Same as S3M pan, but param is offset by -0x40. */
if (t3 == 0x64) { /* 0xA4 - 0x40 */
fxt = FX_SURROUND;
fxp = 1;
} else if (t3 >= 0xC0 || t3 <= 0x40) {
int pan = ((int8)t3 << 1) + 0x80;
fxt = FX_SETPAN;
fxp = MIN(0xff, pan);
}
break;
}
event->fxt = fxt;
event->fxp = fxp;
}
}
}
/* Samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0)
return -1;
}
m->quirk |= QUIRK_FINEFX;
return 0;
}

View File

@ -0,0 +1,510 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "iff.h"
#define MAGIC_MUSX MAGIC4('M','U','S','X')
#define MAGIC_MNAM MAGIC4('M','N','A','M')
#define MAGIC_SNAM MAGIC4('S','N','A','M')
#define MAGIC_SVOL MAGIC4('S','V','O','L')
#define MAGIC_SLEN MAGIC4('S','L','E','N')
#define MAGIC_ROFS MAGIC4('R','O','F','S')
#define MAGIC_RLEN MAGIC4('R','L','E','N')
#define MAGIC_SDAT MAGIC4('S','D','A','T')
static int arch_test (HIO_HANDLE *, char *, const int);
static int arch_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_arch = {
"Archimedes Tracker",
arch_test,
arch_load
};
/*
* Linear (0 to 0x40) to logarithmic volume conversion.
* This is only used for the Protracker-compatible "linear volume" effect in
* Andy Southgate's StasisMod. In this implementation linear and logarithmic
* volumes can be freely intermixed.
*/
static const uint8 lin_table[65]={
0x00, 0x48, 0x64, 0x74, 0x82, 0x8a, 0x92, 0x9a,
0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xea, 0xbe,
0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0,
0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0,
0xe2, 0xe2, 0xe4, 0xe4, 0xe6, 0xe6, 0xe8, 0xe8,
0xea, 0xea, 0xec, 0xec, 0xee, 0xee, 0xf0, 0xf0,
0xf2, 0xf2, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8,
0xfa, 0xfa, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe
};
#if 0
static uint8 convert_vol(uint8 vol) {
/* return pow(2,6.0-(255.0-vol)/32)+.5; */
return vol_table[vol];
}
#endif
static int arch_test(HIO_HANDLE *f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_MUSX) {
return -1;
}
hio_read32l(f);
while (!hio_eof(f)) {
uint32 id = hio_read32b(f);
uint32 len = hio_read32l(f);
/* Sanity check */
if (len > 0x100000) {
return -1;
}
if (id == MAGIC_MNAM) {
libxmp_read_title(f, t, 32);
return 0;
}
hio_seek(f, len, SEEK_CUR);
}
libxmp_read_title(f, t, 0);
return 0;
}
struct local_data {
int year, month, day;
int pflag, sflag, max_ins, max_pat;
int has_mvox;
int has_pnum;
uint8 ster[8], rows[64];
};
static void fix_effect(struct xmp_event *e)
{
#if 0
/* for debugging */
printf ("%c%02x ", e->fxt["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"], e->fxp);
#endif
switch (e->fxt) {
case 0x00: /* 00 xy Normal play or Arpeggio */
e->fxt = FX_ARPEGGIO;
/* x: first halfnote to add
y: second halftone to subtract */
break;
case 0x01: /* 01 xx Slide Up */
e->fxt = FX_PORTA_UP;
break;
case 0x02: /* 02 xx Slide Down */
e->fxt = FX_PORTA_DN;
break;
case 0x03: /* 03 xx Tone Portamento */
e->fxt = FX_TONEPORTA;
break;
case 0x0b: /* 0B xx Break Pattern */
e->fxt = FX_BREAK;
break;
case 0x0c:
/* Set linear volume */
if (e->fxp <= 64) {
e->fxt = FX_VOLSET;
e->fxp = lin_table[e->fxp];
} else {
e->fxp = e->fxt = 0;
}
break;
case 0x0e: /* 0E xy Set Stereo */
case 0x19: /* StasisMod's non-standard set panning effect */
/* y: stereo position (1-7,ignored). 1=left 4=center 7=right */
if (e->fxp>0 && e->fxp<8) {
e->fxt = FX_SETPAN;
e->fxp = 42*e->fxp-40;
} else
e->fxt = e->fxp = 0;
break;
case 0x10: /* 10 xx Volume Slide Up */
e->fxt = FX_VOLSLIDE_UP;
break;
case 0x11: /* 11 xx Volume Slide Down */
e->fxt = FX_VOLSLIDE_DN;
break;
case 0x13: /* 13 xx Position Jump */
e->fxt = FX_JUMP;
break;
case 0x15: /* 15 xy Line Jump. (not in manual) */
/* Jump to line 10*x+y in same pattern. (10*x+y>63 ignored) */
if (MSN(e->fxp) * 10 + LSN(e->fxp) < 64) {
e->fxt = FX_LINE_JUMP;
e->fxp = MSN(e->fxp) * 10 + LSN(e->fxp);
} else {
e->fxt = e->fxp = 0;
}
break;
case 0x1c: /* 1C xy Set Speed */
e->fxt = FX_SPEED;
break;
case 0x1f: /* 1F xx Set Volume */
e->fxt = FX_VOLSET;
/* all volumes are logarithmic */
/* e->fxp = convert_vol (e->fxp); */
break;
default:
e->fxt = e->fxp = 0;
}
}
static int get_tinf(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct local_data *data = (struct local_data *)parm;
int x;
x = hio_read8(f);
data->year = ((x & 0xf0) >> 4) * 10 + (x & 0x0f);
x = hio_read8(f);
data->year += ((x & 0xf0) >> 4) * 1000 + (x & 0x0f) * 100;
x = hio_read8(f);
data->month = ((x & 0xf0) >> 4) * 10 + (x & 0x0f);
x = hio_read8(f);
data->day = ((x & 0xf0) >> 4) * 10 + (x & 0x0f);
return 0;
}
static int get_mvox(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
uint32 chn;
chn = hio_read32l(f);
/* Sanity check */
if (chn < 1 || chn > 8 || data->has_mvox) {
return -1;
}
mod->chn = chn;
data->has_mvox = 1;
return 0;
}
static int get_ster(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i;
if (hio_read(data->ster, 1, 8, f) != 8) {
return -1;
}
for (i = 0; i < mod->chn; i++) {
if (data->ster[i] > 0 && data->ster[i] < 8) {
mod->xxc[i].pan = 42 * data->ster[i] - 40;
}
}
return 0;
}
static int get_mnam(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
if (hio_read(mod->name, 1, 32, f) != 32)
return -1;
return 0;
}
static int get_anam(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
/*hio_read(m->author, 1, 32, f); */
return 0;
}
static int get_mlen(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
uint32 len;
len = hio_read32l(f);
/* Sanity check */
if (len > 0xff)
return -1;
mod->len = len;
return 0;
}
static int get_pnum(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
uint32 pat;
pat = hio_read32l(f);
/* Sanity check */
if (pat < 1 || pat > 64 || data->has_pnum)
return -1;
mod->pat = pat;
data->has_pnum = 1;
return 0;
}
static int get_plen(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct local_data *data = (struct local_data *)parm;
if (hio_read(data->rows, 1, 64, f) != 64)
return -1;
return 0;
}
static int get_sequ(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
hio_read(mod->xxo, 1, 128, f);
libxmp_set_type(m, "Archimedes Tracker");
MODULE_INFO();
return 0;
}
static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i, j, k;
struct xmp_event *event;
/* Sanity check */
if (!data->has_mvox || !data->has_pnum) {
return -1;
}
if (!data->pflag) {
D_(D_INFO "Stored patterns: %d", mod->pat);
data->pflag = 1;
data->max_pat = 0;
mod->trk = mod->pat * mod->chn;
if (libxmp_init_pattern(mod) < 0)
return -1;
}
/* Sanity check */
if (data->max_pat >= mod->pat || data->max_pat >= 64)
return -1;
i = data->max_pat;
if (libxmp_alloc_pattern_tracks(mod, i, data->rows[i]) < 0)
return -1;
for (j = 0; j < data->rows[i]; j++) {
for (k = 0; k < mod->chn; k++) {
event = &EVENT(i, k, j);
event->fxp = hio_read8(f);
event->fxt = hio_read8(f);
event->ins = hio_read8(f);
event->note = hio_read8(f);
if (event->note)
event->note += 48;
fix_effect(event);
}
}
data->max_pat++;
return 0;
}
static int get_samp(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i;
if (!data->sflag) {
mod->smp = mod->ins = 36;
if (libxmp_init_instrument(m) < 0)
return -1;
D_(D_INFO "Instruments: %d", mod->ins);
data->sflag = 1;
data->max_ins = 0;
}
/* FIXME: More than 36 sample slots used. Unfortunately we
* have no way to handle this without two passes, and it's
* officially supposed to be 36, so ignore the rest.
*/
if (data->max_ins >= 36)
return 0;
i = data->max_ins;
mod->xxi[i].nsm = 1;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
if (hio_read32b(f) != MAGIC_SNAM) /* SNAM */
return -1;
{
/* should usually be 0x14 but zero is not unknown */
int name_len = hio_read32l(f);
/* Sanity check */
if (name_len < 0 || name_len > 32)
return -1;
hio_read(mod->xxi[i].name, 1, name_len, f);
}
if (hio_read32b(f) != MAGIC_SVOL) /* SVOL */
return -1;
hio_read32l(f);
/* mod->xxi[i].sub[0].vol = convert_vol(hio_read32l(f)); */
mod->xxi[i].sub[0].vol = hio_read32l(f) & 0xff;
if (hio_read32b(f) != MAGIC_SLEN) /* SLEN */
return -1;
hio_read32l(f);
mod->xxs[i].len = hio_read32l(f);
if (hio_read32b(f) != MAGIC_ROFS) /* ROFS */
return -1;
hio_read32l(f);
mod->xxs[i].lps = hio_read32l(f);
if (hio_read32b(f) != MAGIC_RLEN) /* RLEN */
return -1;
hio_read32l(f);
mod->xxs[i].lpe = hio_read32l(f);
if (hio_read32b(f) != MAGIC_SDAT) /* SDAT */
return -1;
hio_read32l(f);
hio_read32l(f); /* 0x00000000 */
mod->xxi[i].sub[0].sid = i;
mod->xxi[i].sub[0].pan = 0x80;
m->vol_table = libxmp_arch_vol_table;
m->volbase = 0xff;
if (mod->xxs[i].lpe > 2) {
mod->xxs[i].flg = XMP_SAMPLE_LOOP;
mod->xxs[i].lpe = mod->xxs[i].lps + mod->xxs[i].lpe;
} else if (mod->xxs[i].lpe == 2 && mod->xxs[i].lps > 0) {
/* non-zero repeat offset and repeat length of 2
* means loop to end of sample */
mod->xxs[i].flg = XMP_SAMPLE_LOOP;
mod->xxs[i].lpe = mod->xxs[i].len;
}
if (libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, &mod->xxs[i], NULL) < 0)
return -1;
D_(D_INFO "[%2X] %-20.20s %05x %05x %05x %c V%02x",
i, mod->xxi[i].name,
mod->xxs[i].len,
mod->xxs[i].lps,
mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol);
data->max_ins++;
return 0;
}
static int arch_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
iff_handle handle;
int i;
struct local_data data;
LOAD_INIT();
hio_read32b(f); /* MUSX */
hio_read32b(f);
memset(&data, 0, sizeof(struct local_data));
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
/* IFF chunk IDs */
libxmp_iff_register(handle, "TINF", get_tinf);
libxmp_iff_register(handle, "MVOX", get_mvox);
libxmp_iff_register(handle, "STER", get_ster);
libxmp_iff_register(handle, "MNAM", get_mnam);
libxmp_iff_register(handle, "ANAM", get_anam);
libxmp_iff_register(handle, "MLEN", get_mlen);
libxmp_iff_register(handle, "PNUM", get_pnum);
libxmp_iff_register(handle, "PLEN", get_plen);
libxmp_iff_register(handle, "SEQU", get_sequ);
libxmp_iff_register(handle, "PATT", get_patt);
libxmp_iff_register(handle, "SAMP", get_samp);
libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN);
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
libxmp_iff_release(handle);
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff);
}
return 0;
}

104
libxmp/src/loaders/asif.c Normal file
View File

@ -0,0 +1,104 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "asif.h"
#define MAGIC_FORM MAGIC4('F','O','R','M')
#define MAGIC_ASIF MAGIC4('A','S','I','F')
#define MAGIC_NAME MAGIC4('N','A','M','E')
#define MAGIC_INST MAGIC4('I','N','S','T')
#define MAGIC_WAVE MAGIC4('W','A','V','E')
int asif_load(struct module_data *m, HIO_HANDLE *f, int i)
{
struct xmp_module *mod = &m->mod;
int size, pos;
uint32 id;
int chunk;
int j;
if (f == NULL)
return -1;
if (hio_read32b(f) != MAGIC_FORM)
return -1;
/*size =*/ hio_read32b(f);
if (hio_read32b(f) != MAGIC_ASIF)
return -1;
for (chunk = 0; chunk < 2; ) {
id = hio_read32b(f);
size = hio_read32b(f);
pos = hio_tell(f) + size;
switch (id) {
case MAGIC_WAVE:
//printf("wave chunk\n");
hio_seek(f, hio_read8(f), SEEK_CUR); /* skip name */
mod->xxs[i].len = hio_read16l(f) + 1;
size = hio_read16l(f); /* NumSamples */
for (j = 0; j < size; j++) {
hio_read16l(f); /* Location */
mod->xxs[j].len = 256 * hio_read16l(f);
hio_read16l(f); /* OrigFreq */
hio_read16l(f); /* SampRate */
}
if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS,
&mod->xxs[i], NULL) < 0) {
return -1;
}
chunk++;
break;
case MAGIC_INST:
//printf("inst chunk\n");
hio_seek(f, hio_read8(f), SEEK_CUR); /* skip name */
hio_read16l(f); /* SampNum */
hio_seek(f, 24, SEEK_CUR); /* skip envelope */
hio_read8(f); /* ReleaseSegment */
hio_read8(f); /* PriorityIncrement */
hio_read8(f); /* PitchBendRange */
hio_read8(f); /* VibratoDepth */
hio_read8(f); /* VibratoSpeed */
hio_read8(f); /* UpdateRate */
mod->xxi[i].nsm = 1;
mod->xxi[i].sub[0].vol = 0x40;
mod->xxi[i].sub[0].pan = 0x80;
mod->xxi[i].sub[0].sid = i;
chunk++;
}
hio_seek(f, pos, SEEK_SET);
}
return 0;
}

View File

@ -0,0 +1,9 @@
#ifndef XMP_ASIF_H
#define XMP_ASIF_H
#include "../common.h"
#include "../hio.h"
int asif_load(struct module_data *, HIO_HANDLE *, int);
#endif

View File

@ -0,0 +1,185 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Based on AMF->MOD converter written by Mr. P / Powersource, 1995
*/
#include "loader.h"
#include "../period.h"
static int asylum_test(HIO_HANDLE *, char *, const int);
static int asylum_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_asylum = {
"Asylum Music Format v1.0",
asylum_test,
asylum_load
};
static int asylum_test(HIO_HANDLE *f, char *t, const int start)
{
char buf[32];
if (hio_read(buf, 1, 32, f) < 32)
return -1;
if (memcmp(buf, "ASYLUM Music Format V1.0\0\0\0\0\0\0\0\0", 32))
return -1;
libxmp_read_title(f, t, 0);
return 0;
}
static int asylum_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
uint8 buf[2048];
int i, j;
LOAD_INIT();
hio_seek(f, 32, SEEK_CUR); /* skip magic */
mod->spd = hio_read8(f); /* initial speed */
mod->bpm = hio_read8(f); /* initial BPM */
mod->ins = hio_read8(f); /* number of instruments */
mod->pat = hio_read8(f); /* number of patterns */
mod->len = hio_read8(f); /* module length */
mod->rst = hio_read8(f); /* restart byte */
/* Sanity check - this format only stores 64 sample structures. */
if (mod->ins > 64) {
D_(D_CRIT "invalid sample count %d", mod->ins);
return -1;
}
hio_read(mod->xxo, 1, mod->len, f); /* read orders */
hio_seek(f, start + 294, SEEK_SET);
mod->chn = 8;
mod->smp = mod->ins;
mod->trk = mod->pat * mod->chn;
snprintf(mod->type, XMP_NAME_SIZE, "Asylum Music Format v1.0");
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
/* Read and convert instruments and samples */
for (i = 0; i < mod->ins; i++) {
uint8 insbuf[37];
if (libxmp_alloc_subinstrument(mod, i, 1) < 0) {
return -1;
}
if (hio_read(insbuf, 1, 37, f) != 37) {
return -1;
}
libxmp_instrument_name(mod, i, insbuf, 22);
mod->xxi[i].sub[0].fin = (int8)(insbuf[22] << 4);
mod->xxi[i].sub[0].vol = insbuf[23];
mod->xxi[i].sub[0].xpo = (int8)insbuf[24];
mod->xxi[i].sub[0].pan = 0x80;
mod->xxi[i].sub[0].sid = i;
mod->xxs[i].len = readmem32l(insbuf + 25);
mod->xxs[i].lps = readmem32l(insbuf + 29);
mod->xxs[i].lpe = mod->xxs[i].lps + readmem32l(insbuf + 33);
/* Sanity check - ASYLUM modules are converted from MODs. */
if ((uint32)mod->xxs[i].len >= 0x20000) {
D_(D_CRIT "invalid sample %d length %d", i, mod->xxs[i].len);
return -1;
}
mod->xxs[i].flg = mod->xxs[i].lpe > 2 ? XMP_SAMPLE_LOOP : 0;
D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %d", i,
mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps,
mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin);
}
hio_seek(f, 37 * (64 - mod->ins), SEEK_CUR);
D_(D_INFO "Module length: %d", mod->len);
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
uint8 *pos;
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
if (hio_read(buf, 1, 2048, f) < 2048) {
D_(D_CRIT "read error at pattern %d", i);
return -1;
}
pos = buf;
for (j = 0; j < 64 * 8; j++) {
uint8 note;
event = &EVENT(i, j % 8, j / 8);
memset(event, 0, sizeof(struct xmp_event));
note = *pos++;
if (note != 0) {
event->note = note + 13;
}
event->ins = *pos++;
event->fxt = *pos++;
event->fxp = *pos++;
/* TODO: m07.amf and m12.amf from Crusader: No Remorse
* use 0x1b for what looks *plausibly* like retrigger.
* No other ASYLUM modules use effects over 16. */
if (event->fxt >= 0x10 && event->fxt != FX_MULTI_RETRIG)
event->fxt = event->fxp = 0;
}
}
/* Read samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (mod->xxs[i].len > 1) {
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
mod->xxi[i].nsm = 1;
}
}
return 0;
}

View File

@ -0,0 +1,203 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "mod.h"
#include "../period.h"
static int chip_test(HIO_HANDLE *, char *, const int);
static int chip_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_chip = {
"Chiptracker",
chip_test,
chip_load
};
static int chip_test(HIO_HANDLE *f, char *t, const int start)
{
char buf[4];
hio_seek(f, start + 952, SEEK_SET);
if (hio_read(buf, 1, 4, f) < 4)
return -1;
/* Also RASP? */
if (memcmp(buf, "KRIS", 4) != 0)
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 20);
return 0;
}
static int chip_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct mod_header mh;
uint8 *tidx;
int i, j, tnum;
LOAD_INIT();
tidx = (uint8 *) calloc(1, 1024);
if (tidx == NULL) {
goto err;
}
hio_read(mh.name, 20, 1, f);
hio_read16b(f);
for (i = 0; i < 31; i++) {
hio_read(mh.ins[i].name, 22, 1, f);
mh.ins[i].size = hio_read16b(f);
mh.ins[i].finetune = hio_read8(f);
mh.ins[i].volume = hio_read8(f);
mh.ins[i].loop_start = hio_read16b(f);
mh.ins[i].loop_size = hio_read16b(f);
}
hio_read(mh.magic, 4, 1, f);
mh.len = hio_read8(f);
/* Sanity check */
if (mh.len > 128) {
goto err2;
}
mh.restart = hio_read8(f);
hio_read(tidx, 1024, 1, f);
hio_read16b(f);
mod->chn = 4;
mod->ins = 31;
mod->smp = mod->ins;
mod->len = mh.len;
mod->pat = mh.len;
mod->rst = mh.restart;
tnum = 0;
for (i = 0; i < mod->len; i++) {
mod->xxo[i] = i;
for (j = 0; j < 4; j++) {
int t = tidx[2 * (4 * i + j)];
if (t > tnum)
tnum = t;
}
}
mod->trk = tnum + 1;
strncpy(mod->name, (char *)mh.name, 20);
libxmp_set_type(m, "Chiptracker");
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
goto err2;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
goto err2;
sub = &xxi->sub[0];
xxs->len = 2 * mh.ins[i].size;
xxs->lps = mh.ins[i].loop_start;
xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size;
xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0;
sub->fin = (int8) (mh.ins[i].finetune << 4);
sub->vol = mh.ins[i].volume;
sub->pan = 0x80;
sub->sid = i;
if (xxs->len > 0)
xxi->nsm = 1;
libxmp_instrument_name(mod, i, mh.ins[i].name, 22);
}
if (libxmp_init_pattern(mod) < 0)
goto err2;
for (i = 0; i < mod->len; i++) {
if (libxmp_alloc_pattern(mod, i) < 0)
goto err2;
mod->xxp[i]->rows = 64;
for (j = 0; j < 4; j++) {
int t = tidx[2 * (4 * i + j)];
mod->xxp[i]->index[j] = t;
}
}
/* Load and convert tracks */
D_(D_INFO "Stored tracks: %d", mod->trk);
for (i = 0; i < mod->trk; i++) {
if (libxmp_alloc_track(mod, i, 64) < 0)
goto err2;
for (j = 0; j < 64; j++) {
struct xmp_event *event = &mod->xxt[i]->event[j];
uint8 e[4];
if (hio_read(e, 1, 4, f) < 4) {
D_(D_CRIT "read error in track %d", i);
goto err2;
}
if (e[0] && e[0] != 0xa8)
event->note = 13 + e[0] / 2;
event->ins = e[1];
event->fxt = e[2] & 0x0f;
event->fxp = e[3];
}
}
m->period_type = PERIOD_MODRNG;
/* Load samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
if (mod->xxs[i].len == 0)
continue;
if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, &mod->xxs[i], NULL) < 0)
goto err2;
}
free(tidx);
return 0;
err2:
free(tidx);
err:
return -1;
}

View File

@ -0,0 +1,318 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
static int coco_test (HIO_HANDLE *, char *, const int);
static int coco_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_coco = {
"Coconizer",
coco_test,
coco_load
};
static int check_cr(uint8 *s, int n)
{
while (n--) {
if (*s++ == 0x0d)
return 0;
}
return -1;
}
static int coco_test(HIO_HANDLE *f, char *t, const int start)
{
uint8 x, buf[20];
uint32 y;
int n, i;
x = hio_read8(f);
/* check number of channels */
if (x != 0x84 && x != 0x88)
return -1;
if (hio_read(buf, 1, 20, f) != 20) /* read title */
return -1;
if (check_cr(buf, 20) != 0)
return -1;
n = hio_read8(f); /* instruments */
if (n <= 0 || n > 100)
return -1;
hio_read8(f); /* sequences */
hio_read8(f); /* patterns */
y = hio_read32l(f);
if (y < 64 || y > 0x00100000) /* offset of sequence table */
return -1;
y = hio_read32l(f); /* offset of patterns */
if (y < 64 || y > 0x00100000)
return -1;
for (i = 0; i < n; i++) {
int ofs = hio_read32l(f);
int len = hio_read32l(f);
int vol = hio_read32l(f);
int lps = hio_read32l(f);
int lsz = hio_read32l(f);
if (ofs < 64 || ofs > 0x00100000)
return -1;
if (vol < 0 || vol > 0xff)
return -1;
if (len < 0 || lps < 0 || lsz < 0)
return -1;
if (len > 0x00100000 || lps > 0x00100000 || lsz > 0x00100000)
return -1;
if (lps > 0 && lps + lsz - 1 > len)
return -1;
hio_read(buf, 1, 11, f);
hio_read8(f); /* unused */
}
hio_seek(f, start + 1, SEEK_SET);
libxmp_read_title(f, t, 20);
#if 0
for (i = 0; i < 20; i++) {
if (t[i] == 0x0d)
t[i] = 0;
}
#endif
return 0;
}
static void fix_effect(struct xmp_event *e)
{
switch (e->fxt) {
case 0x00: /* 00 xy Normal play or Arpeggio */
e->fxt = FX_ARPEGGIO;
/* x: first halfnote to add
y: second halftone to subtract */
break;
case 0x01: /* 01 xx Slide Pitch Up (until Amis Max), Frequency+InfoByte*64*/
case 0x05: /* 05 xx Slide Pitch Up (no limit), Frequency+InfoByte*16 */
e->fxt = FX_PORTA_UP;
break;
case 0x02: /* 02 xx Slide Pitch Down (until Amis Min), Frequency-InfoByte*64*/
case 0x06: /* 06 xx Slide Pitch Down (0 limit), Frequency-InfoByte*16 */
e->fxt = FX_PORTA_DN;
break;
case 0x03: /* 03 xx Fine Volume Up */
e->fxt = FX_F_VSLIDE_UP;
break;
case 0x04: /* 04 xx Fine Volume Down */
e->fxt = FX_F_VSLIDE_DN;
break;
case 0x07: /* 07 xy Set Stereo Position */
/* y: stereo position (1-7,ignored). 1=left 4=center 7=right */
if (e->fxp>0 && e->fxp<8) {
e->fxt = FX_SETPAN;
e->fxp = 42*e->fxp-40;
} else
e->fxt = e->fxp = 0;
break;
case 0x08: /* 08 xx Start Auto Fine Volume Up */
case 0x09: /* 09 xx Start Auto Fine Volume Down */
case 0x0a: /* 0A xx Start Auto Pitch Up */
case 0x0b: /* 0B xx Start Auto Pitch Down */
e->fxt = e->fxp = 0; /* FIXME */
break;
case 0x0c: /* 0C xx Set Volume */
e->fxt = FX_VOLSET;
e->fxp = 0xff - e->fxp;
break;
case 0x0d: /* 0D xy Pattern Break */
e->fxt = FX_BREAK;
break;
case 0x0e: /* 0E xx Position Jump */
e->fxt = FX_JUMP;
break;
case 0x0f: /* 0F xx Set Speed */
e->fxt = FX_SPEED;
break;
case 0x10: /* 10 xx Unused */
e->fxt = e->fxp = 0;
break;
case 0x11: /* 11 xx Fine Slide Pitch Up */
case 0x12: /* 12 xx Fine Slide Pitch Down */
e->fxt = e->fxp = 0; /* FIXME */
break;
case 0x13: /* 13 xx Volume Up */
e->fxt = FX_VOLSLIDE_UP;
break;
case 0x14: /* 14 xx Volume Down */
e->fxt = FX_VOLSLIDE_DN;
break;
default:
e->fxt = e->fxp = 0;
}
}
static int coco_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
int i, j, k;
int seq_ptr, pat_ptr, smp_ptr[100];
LOAD_INIT();
mod->chn = hio_read8(f) & 0x3f;
libxmp_read_title(f, mod->name, 20);
for (i = 0; i < 20; i++) {
if (mod->name[i] == 0x0d)
mod->name[i] = 0;
}
libxmp_set_type(m, "Coconizer");
mod->ins = mod->smp = hio_read8(f);
mod->len = hio_read8(f);
mod->pat = hio_read8(f);
mod->trk = mod->pat * mod->chn;
seq_ptr = hio_read32l(f);
pat_ptr = hio_read32l(f);
if (hio_error(f)) {
return -1;
}
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
m->vol_table = libxmp_arch_vol_table;
m->volbase = 0xff;
for (i = 0; i < mod->ins; i++) {
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
smp_ptr[i] = hio_read32l(f);
mod->xxs[i].len = hio_read32l(f);
mod->xxi[i].sub[0].vol = 0xff - hio_read32l(f);
mod->xxi[i].sub[0].pan = 0x80;
mod->xxs[i].lps = hio_read32l(f);
mod->xxs[i].lpe = mod->xxs[i].lps + hio_read32l(f);
if (mod->xxs[i].lpe)
mod->xxs[i].lpe -= 1;
mod->xxs[i].flg = mod->xxs[i].lps > 0 ? XMP_SAMPLE_LOOP : 0;
hio_read(mod->xxi[i].name, 1, 11, f);
for (j = 0; j < 11; j++) {
if (mod->xxi[i].name[j] == 0x0d)
mod->xxi[i].name[j] = 0;
}
hio_read8(f); /* unused */
mod->xxi[i].sub[0].sid = i;
if (mod->xxs[i].len > 0)
mod->xxi[i].nsm = 1;
if (hio_error(f)) {
return -1;
}
D_(D_INFO "[%2X] %-10.10s %05x %05x %05x %c V%02x",
i, mod->xxi[i].name,
mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol);
}
/* Sequence */
hio_seek(f, start + seq_ptr, SEEK_SET);
for (i = 0; ; i++) {
uint8 x = hio_read8(f);
if (x == 0xff)
break;
if (i < mod->len)
mod->xxo[i] = x;
}
/* Patterns */
if (libxmp_init_pattern(mod) < 0)
return -1;
D_(D_INFO "Stored patterns: %d", mod->pat);
if (hio_seek(f, start + pat_ptr, SEEK_SET) < 0)
return -1;
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
for (j = 0; j < 64; j++) {
for (k = 0; k < mod->chn; k++) {
event = &EVENT(i, k, j);
event->fxp = hio_read8(f);
event->fxt = hio_read8(f);
event->ins = hio_read8(f);
event->note = hio_read8(f);
if (event->note)
event->note += 12;
if (hio_error(f)) {
return -1;
}
fix_effect(event);
}
}
}
/* Read samples */
D_(D_INFO "Stored samples : %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (mod->xxi[i].nsm == 0)
continue;
hio_seek(f, start + smp_ptr[i], SEEK_SET);
if (libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, &mod->xxs[i], NULL) < 0)
return -1;
}
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = DEFPAN((((i + 3) / 2) % 2) * 0xff);
}
return 0;
}

543
libxmp/src/loaders/common.c Normal file
View File

@ -0,0 +1,543 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <ctype.h>
#if defined(HAVE_DIRENT)
#include <sys/types.h>
#include <dirent.h>
#endif
#include "../common.h"
#include "xmp.h"
#include "../period.h"
#include "loader.h"
int libxmp_init_instrument(struct module_data *m)
{
struct xmp_module *mod = &m->mod;
if (mod->ins > 0) {
mod->xxi = (struct xmp_instrument *) calloc(mod->ins, sizeof(struct xmp_instrument));
if (mod->xxi == NULL)
return -1;
}
if (mod->smp > 0) {
int i;
/* Sanity check */
if (mod->smp > MAX_SAMPLES) {
D_(D_CRIT "sample count %d exceeds maximum (%d)",
mod->smp, MAX_SAMPLES);
return -1;
}
mod->xxs = (struct xmp_sample *) calloc(mod->smp, sizeof(struct xmp_sample));
if (mod->xxs == NULL)
return -1;
m->xtra = (struct extra_sample_data *) calloc(mod->smp, sizeof(struct extra_sample_data));
if (m->xtra == NULL)
return -1;
for (i = 0; i < mod->smp; i++) {
m->xtra[i].c5spd = m->c4rate;
}
}
return 0;
}
/* Sample number adjustment (originally by Vitamin/CAIG).
* Only use this AFTER a previous usage of libxmp_init_instrument,
* and don't use this to free samples that have already been loaded. */
int libxmp_realloc_samples(struct module_data *m, int new_size)
{
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs;
struct extra_sample_data *xtra;
/* Sanity check */
if (new_size < 0)
return -1;
if (new_size == 0) {
/* Don't rely on implementation-defined realloc(x,0) behavior. */
mod->smp = 0;
free(mod->xxs);
mod->xxs = NULL;
free(m->xtra);
m->xtra = NULL;
return 0;
}
xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * new_size);
if (xxs == NULL)
return -1;
mod->xxs = xxs;
xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * new_size);
if (xtra == NULL)
return -1;
m->xtra = xtra;
if (new_size > mod->smp) {
int clear_size = new_size - mod->smp;
int i;
memset(xxs + mod->smp, 0, sizeof(struct xmp_sample) * clear_size);
memset(xtra + mod->smp, 0, sizeof(struct extra_sample_data) * clear_size);
for (i = mod->smp; i < new_size; i++) {
m->xtra[i].c5spd = m->c4rate;
}
}
mod->smp = new_size;
return 0;
}
int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
{
if (num == 0)
return 0;
mod->xxi[i].sub = (struct xmp_subinstrument *) calloc(num, sizeof(struct xmp_subinstrument));
if (mod->xxi[i].sub == NULL)
return -1;
return 0;
}
int libxmp_init_pattern(struct xmp_module *mod)
{
mod->xxt = (struct xmp_track **) calloc(mod->trk, sizeof(struct xmp_track *));
if (mod->xxt == NULL)
return -1;
mod->xxp = (struct xmp_pattern **) calloc(mod->pat, sizeof(struct xmp_pattern *));
if (mod->xxp == NULL)
return -1;
return 0;
}
int libxmp_alloc_pattern(struct xmp_module *mod, int num)
{
/* Sanity check */
if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL)
return -1;
mod->xxp[num] = (struct xmp_pattern *) calloc(1, sizeof(struct xmp_pattern) +
sizeof(int) * (mod->chn - 1));
if (mod->xxp[num] == NULL)
return -1;
return 0;
}
int libxmp_alloc_track(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0)
return -1;
mod->xxt[num] = (struct xmp_track *) calloc(1, sizeof(struct xmp_track) +
sizeof(struct xmp_event) * (rows - 1));
if (mod->xxt[num] == NULL)
return -1;
mod->xxt[num]->rows = rows;
return 0;
}
int libxmp_alloc_tracks_in_pattern(struct xmp_module *mod, int num)
{
int i;
D_(D_INFO "Alloc %d tracks of %d rows", mod->chn, mod->xxp[num]->rows);
for (i = 0; i < mod->chn; i++) {
int t = num * mod->chn + i;
int rows = mod->xxp[num]->rows;
if (libxmp_alloc_track(mod, t, rows) < 0)
return -1;
mod->xxp[num]->index[i] = t;
}
return 0;
}
int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (rows <= 0 || rows > 256)
return -1;
if (libxmp_alloc_pattern(mod, num) < 0)
return -1;
mod->xxp[num]->rows = rows;
if (libxmp_alloc_tracks_in_pattern(mod, num) < 0)
return -1;
return 0;
}
#ifndef LIBXMP_CORE_PLAYER
/* Some formats explicitly allow more than 256 rows (e.g. OctaMED). This function
* allows those formats to work without disrupting the sanity check for other formats.
*/
int libxmp_alloc_pattern_tracks_long(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (rows <= 0 || rows > 32768)
return -1;
if (libxmp_alloc_pattern(mod, num) < 0)
return -1;
mod->xxp[num]->rows = rows;
if (libxmp_alloc_tracks_in_pattern(mod, num) < 0)
return -1;
return 0;
}
#endif
char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n)
{
CLAMP(n, 0, 31);
return libxmp_copy_adjust(mod->xxi[i].name, r, n);
}
char *libxmp_copy_adjust(char *s, uint8 *r, int n)
{
int i;
memset(s, 0, n + 1);
strncpy(s, (char *)r, n);
for (i = 0; s[i] && i < n; i++) {
if (!isprint((unsigned char)s[i]) || ((uint8)s[i] > 127))
s[i] = '.';
}
while (*s && (s[strlen(s) - 1] == ' '))
s[strlen(s) - 1] = 0;
return s;
}
void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
{
uint8 buf[XMP_NAME_SIZE];
if (t == NULL || s < 0)
return;
if (s >= XMP_NAME_SIZE)
s = XMP_NAME_SIZE -1;
memset(t, 0, s + 1);
s = hio_read(buf, 1, s, f);
buf[s] = 0;
libxmp_copy_adjust(t, buf, s);
}
#ifndef LIBXMP_CORE_PLAYER
int libxmp_test_name(uint8 *s, int n)
{
int i;
for (i = 0; i < n; i++) {
if (s[i] > 0x7f)
return -1;
/* ACS_Team2.mod has a backspace in instrument name */
if (s[i] > 0 && s[i] < 32 && s[i] != 0x08)
return -1;
}
return 0;
}
int libxmp_copy_name_for_fopen(char *dest, const char *name, int n)
{
int converted_colon = 0;
int i;
/* libxmp_copy_adjust, but make sure the filename won't do anything
* malicious when given to fopen. This should only be used on song files.
*/
if (!strcmp(name, ".") || strstr(name, "..") ||
name[0] == '\\' || name[0] == '/' || name[0] == ':')
return -1;
for (i = 0; i < n - 1; i++) {
uint8 t = name[i];
if (!t)
break;
/* Reject non-ASCII symbols as they have poorly defined behavior.
*/
if (t < 32 || t >= 0x7f)
return -1;
/* Reject anything resembling a Windows-style root path. Allow
* converting a single : to / so things like ST-01:samplename
* work. (Leave the : as-is on Amiga.)
*/
if (i > 0 && t == ':' && !converted_colon) {
uint8 t2 = name[i + 1];
if (!t2 || t2 == '/' || t2 == '\\')
return -1;
converted_colon = 1;
#ifndef LIBXMP_AMIGA
dest[i] = '/';
continue;
#endif
}
if (t == '\\') {
dest[i] = '/';
continue;
}
dest[i] = t;
}
dest[i] = '\0';
return 0;
}
/*
* Honor Noisetracker effects:
*
* 0 - arpeggio
* 1 - portamento up
* 2 - portamento down
* 3 - Tone-portamento
* 4 - Vibrato
* A - Slide volume
* B - Position jump
* C - Set volume
* D - Pattern break
* E - Set filter (keep the led off, please!)
* F - Set speed (now up to $1F)
*
* Pex Tufvesson's notes from http://www.livet.se/mahoney/:
*
* Note that some of the modules will have bugs in the playback with all
* known PC module players. This is due to that in many demos where I synced
* events in the demo with the music, I used commands that these newer PC
* module players erroneously interpret as "newer-version-trackers commands".
* Which they aren't.
*/
void libxmp_decode_noisetracker_event(struct xmp_event *event, const uint8 *mod_event)
{
int fxt;
memset(event, 0, sizeof (struct xmp_event));
event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]);
event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2]));
fxt = LSN(mod_event[2]);
if (fxt <= 0x06 || (fxt >= 0x0a && fxt != 0x0e)) {
event->fxt = fxt;
event->fxp = mod_event[3];
}
libxmp_disable_continue_fx(event);
}
#endif
void libxmp_decode_protracker_event(struct xmp_event *event, const uint8 *mod_event)
{
int fxt = LSN(mod_event[2]);
memset(event, 0, sizeof (struct xmp_event));
event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]);
event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2]));
if (fxt != 0x08) {
event->fxt = fxt;
event->fxp = mod_event[3];
}
libxmp_disable_continue_fx(event);
}
void libxmp_disable_continue_fx(struct xmp_event *event)
{
if (event->fxp == 0) {
switch (event->fxt) {
case 0x05:
event->fxt = 0x03;
break;
case 0x06:
event->fxt = 0x04;
break;
case 0x01:
case 0x02:
case 0x0a:
event->fxt = 0x00;
}
} else if (event->fxt == 0x0e) {
if (event->fxp == 0xa0 || event->fxp == 0xb0) {
event->fxt = event->fxp = 0;
}
}
}
#ifndef LIBXMP_CORE_PLAYER
/* libxmp_check_filename_case(): */
/* Given a directory, see if file exists there, ignoring case */
#if defined(_WIN32) || defined(__DJGPP__) || \
defined(__OS2__) || defined(__EMX__) || \
defined(_DOS) || defined(LIBXMP_AMIGA) || \
defined(__riscos__) || \
/* case-insensitive file system: directly probe the file */\
\
!defined(HAVE_DIRENT) /* or, target does not have dirent. */
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
char path[XMP_MAXPATH];
snprintf(path, sizeof(path), "%s/%s", dir, name);
if (! (libxmp_get_filetype(path) & XMP_FILETYPE_FILE))
return 0;
strncpy(new_name, name, size);
return 1;
}
#else /* target has dirent */
int libxmp_check_filename_case(const char *dir, const char *name, char *new_name, int size)
{
int found = 0;
DIR *dirp;
struct dirent *d;
dirp = opendir(dir);
if (dirp == NULL)
return 0;
while ((d = readdir(dirp)) != NULL) {
if (!strcasecmp(d->d_name, name)) {
found = 1;
strncpy(new_name, d->d_name, size);
break;
}
}
closedir(dirp);
return found;
}
#endif
void libxmp_get_instrument_path(struct module_data *m, char *path, int size)
{
if (m->instrument_path) {
strncpy(path, m->instrument_path, size);
} else if (getenv("XMP_INSTRUMENT_PATH")) {
strncpy(path, getenv("XMP_INSTRUMENT_PATH"), size);
} else {
strncpy(path, ".", size);
}
}
#endif /* LIBXMP_CORE_PLAYER */
void libxmp_set_type(struct module_data *m, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(m->mod.type, XMP_NAME_SIZE, fmt, ap);
va_end(ap);
}
#ifndef LIBXMP_CORE_PLAYER
static int schism_tracker_date(int year, int month, int day)
{
int mm = (month + 9) % 12;
int yy = year - mm / 10;
yy = yy * 365 + (yy / 4) - (yy / 100) + (yy / 400);
mm = (mm * 306 + 5) / 10;
return yy + mm + (day - 1);
}
/* Generate a Schism Tracker version string.
* Schism Tracker versions are stored as follows:
*
* s_ver <= 0x50: 0.s_ver
* s_ver > 0x50, < 0xfff: days from epoch=(s_ver - 0x50)
* s_ver = 0xfff: days from epoch=l_ver
*/
void libxmp_schism_tracker_string(char *buf, size_t size, int s_ver, int l_ver)
{
if (s_ver >= 0x50) {
/* time_t epoch_sec = 1256947200; */
int64 t = schism_tracker_date(2009, 10, 31);
int year, month, day, dayofyear;
if (s_ver == 0xfff) {
t += l_ver;
} else
t += s_ver - 0x50;
/* Date algorithm reimplemented from OpenMPT.
*/
year = (int)((t * 10000L + 14780) / 3652425);
dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400));
if (dayofyear < 0) {
year--;
dayofyear = t - (365L * year + (year / 4) - (year / 100) + (year / 400));
}
month = (100 * dayofyear + 52) / 3060;
day = dayofyear - (month * 306 + 5) / 10 + 1;
year += (month + 2) / 12;
month = (month + 2) % 12 + 1;
snprintf(buf, size, "Schism Tracker %04d-%02d-%02d",
year, month, day);
} else {
snprintf(buf, size, "Schism Tracker 0.%x", s_ver);
}
}
#endif
char *libxmp_strdup(const char *src)
{
size_t len = strlen(src) + 1;
char *buf = (char *) malloc(len);
if (buf) {
memcpy(buf, src, len);
}
return buf;
}

View File

@ -0,0 +1,582 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Based on DigiBooster_E.guide from the DigiBoosterPro 2.20 package.
* DigiBooster Pro written by Tomasz & Waldemar Piasta
*/
#include "loader.h"
#include "iff.h"
#include "../period.h"
#define MAGIC_DBM0 MAGIC4('D','B','M','0')
static int dbm_test(HIO_HANDLE *, char *, const int);
static int dbm_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_dbm = {
"DigiBooster Pro",
dbm_test,
dbm_load
};
static int dbm_test(HIO_HANDLE * f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_DBM0)
return -1;
hio_seek(f, 12, SEEK_CUR);
libxmp_read_title(f, t, 44);
return 0;
}
struct local_data {
int have_info;
int have_song;
int have_patt;
int have_smpl;
int have_inst;
int have_venv;
int have_penv;
int maj_version;
int min_version;
};
struct dbm_envelope {
int ins;
int flg;
int npt;
int sus;
int lps;
int lpe;
int sus2;
struct dbm_envelope_node {
uint16 position;
int16 value;
} nodes[32];
};
static void dbm_translate_effect(struct xmp_event *event, uint8 *fxt, uint8 *fxp)
{
switch (*fxt) {
case 0x0e:
switch (MSN(*fxp)) {
case 0x3: /* Play from backward */
/* TODO: this is supposed to play the sample in
* reverse only once, then forward. */
if (event->note) {
*fxt = FX_REVERSE;
*fxp = 1;
} else {
*fxt = *fxp = 0;
}
break;
case 0x4: /* Turn off sound in channel */
*fxt = FX_EXTENDED;
*fxp = (EX_CUT << 4);
break;
case 0x5: /* Turn on/off channel */
/* In DigiBooster Pro, this is tied to
* the channel mute toggle in the UI. */
*fxt = FX_TRK_VOL;
*fxp = *fxp ? 0x40 : 0x00;
break;
}
break;
case 0x1c: /* Set Real BPM */
*fxt = FX_S3M_BPM;
break;
default:
if (*fxt > 0x1c)
*fxt = *fxp = 0;
}
}
static int get_info(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int val;
/* Sanity check */
if (data->have_info || size < 10) {
return -1;
}
data->have_info = 1;
val = hio_read16b(f);
if (val < 0 || val > 255) {
D_(D_CRIT "Invalid number of instruments: %d", val);
goto err;
}
mod->ins = val;
val = hio_read16b(f);
if (val < 0) {
D_(D_CRIT "Invalid number of samples: %d", val);
goto err2;
}
mod->smp = val;
hio_read16b(f); /* Songs */
val = hio_read16b(f);
if (val < 0 || val > 256) {
D_(D_CRIT "Invalid number of patterns: %d", val);
goto err3;
}
mod->pat = val;
val = hio_read16b(f);
if (val < 0 || val > XMP_MAX_CHANNELS) {
D_(D_CRIT "Invalid number of channels: %d", val);
goto err4;
}
mod->chn = val;
mod->trk = mod->pat * mod->chn;
if (libxmp_init_instrument(m) < 0)
return -1;
return 0;
err4:
mod->pat = 0;
err3:
mod->smp = 0;
err2:
mod->ins = 0;
err:
return -1;
}
static int get_song(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i;
char buffer[50];
/* Sanity check */
if (data->have_song || size < 46) {
return 0;
}
data->have_song = 1;
hio_read(buffer, 44, 1, f);
D_(D_INFO "Song name: %.44s", buffer);
mod->len = hio_read16b(f);
D_(D_INFO "Song length: %d patterns", mod->len);
/* Sanity check */
if (mod->len > 256) {
return -1;
}
for (i = 0; i < mod->len; i++)
mod->xxo[i] = hio_read16b(f);
return 0;
}
static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i;
int c2spd, flags, snum;
uint8 buffer[50];
/* Sanity check */
if (data->have_inst || size < 50 * mod->ins) {
return -1;
}
data->have_inst = 1;
D_(D_INFO "Instruments: %d", mod->ins);
for (i = 0; i < mod->ins; i++) {
mod->xxi[i].nsm = 1;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
if (hio_read(buffer, 30, 1, f) == 0)
return -1;
libxmp_instrument_name(mod, i, buffer, 30);
snum = hio_read16b(f);
if (snum == 0 || snum > mod->smp) {
/* Skip remaining data for this instrument. */
hio_seek(f, 18, SEEK_CUR);
continue;
}
mod->xxi[i].sub[0].sid = --snum;
mod->xxi[i].sub[0].vol = hio_read16b(f);
c2spd = hio_read32b(f);
mod->xxs[snum].lps = hio_read32b(f);
mod->xxs[snum].lpe = mod->xxs[snum].lps + hio_read32b(f);
mod->xxi[i].sub[0].pan = 0x80 + (int16)hio_read16b(f);
if (mod->xxi[i].sub[0].pan > 0xff)
mod->xxi[i].sub[0].pan = 0xff;
flags = hio_read16b(f);
mod->xxs[snum].flg = flags & 0x03 ? XMP_SAMPLE_LOOP : 0;
mod->xxs[snum].flg |= flags & 0x02 ? XMP_SAMPLE_LOOP_BIDIR : 0;
libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin);
D_(D_INFO "[%2X] %-30.30s #%02X V%02x P%02x %5d",
i, mod->xxi[i].name, snum,
mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].pan, c2spd);
}
return 0;
}
static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i, c, r, n, sz;
struct xmp_event *event, dummy;
uint8 x;
/* Sanity check */
if (data->have_patt || !data->have_info) {
return -1;
}
data->have_patt = 1;
if (libxmp_init_pattern(mod) < 0)
return -1;
D_(D_INFO "Stored patterns: %d ", mod->pat);
/*
* Note: channel and flag bytes are inverted in the format
* description document
*/
for (i = 0; i < mod->pat; i++) {
int rows = hio_read16b(f);
if (hio_error(f))
return -1;
if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0)
return -1;
sz = hio_read32b(f);
//printf("rows = %d, size = %d\n", mod->xxp[i]->rows, sz);
r = 0;
/*c = -1;*/
while (sz > 0) {
//printf(" offset=%x, sz = %d, ", hio_tell(f), sz);
c = hio_read8(f);
if (hio_error(f))
return -1;
if (--sz <= 0) break;
//printf("c = %02x\n", c);
if (c == 0) {
r++;
continue;
}
c--;
n = hio_read8(f);
if (--sz <= 0) break;
//printf(" n = %d\n", n);
if (c >= mod->chn || r >= mod->xxp[i]->rows) {
event = &dummy;
} else {
event = &EVENT(i, c, r);
}
memset(event, 0, sizeof (struct xmp_event));
if (n & 0x01) {
x = hio_read8(f);
event->note = 13 + MSN(x) * 12 + LSN(x);
if (--sz <= 0) break;
}
if (n & 0x02) {
event->ins = hio_read8(f);
if (--sz <= 0) break;
}
if (n & 0x04) {
event->fxt = hio_read8(f);
if (--sz <= 0) break;
}
if (n & 0x08) {
event->fxp = hio_read8(f);
if (--sz <= 0) break;
}
if (n & 0x10) {
event->f2t = hio_read8(f);
if (--sz <= 0) break;
}
if (n & 0x20) {
event->f2p = hio_read8(f);
if (--sz <= 0) break;
}
dbm_translate_effect(event, &event->fxt, &event->fxp);
dbm_translate_effect(event, &event->f2t, &event->f2p);
}
}
return 0;
}
static int get_smpl(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i, flags;
/* Sanity check */
if (data->have_smpl || !data->have_info) {
return -1;
}
data->have_smpl = 1;
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
flags = hio_read32b(f);
mod->xxs[i].len = hio_read32b(f);
if (flags & 0x02) {
mod->xxs[i].flg |= XMP_SAMPLE_16BIT;
}
if (flags & 0x04) { /* Skip 32-bit samples */
mod->xxs[i].len <<= 2;
hio_seek(f, mod->xxs[i].len, SEEK_CUR);
continue;
}
if (libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND, &mod->xxs[i], NULL) < 0)
return -1;
if (mod->xxs[i].len == 0)
continue;
D_(D_INFO "[%2X] %08x %05x%c%05x %05x %c",
i, flags, mod->xxs[i].len,
mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ',
mod->xxs[i].lps, mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ?
(mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' : 'L') : ' ');
}
return 0;
}
static int read_envelope(struct xmp_module *mod, struct dbm_envelope *env, HIO_HANDLE *f)
{
int i;
env->ins = (int)hio_read16b(f) - 1;
env->flg = hio_read8(f) & 0x7;
env->npt = (int)hio_read8(f) + 1; /* DBM counts sections, not points. */
env->sus = hio_read8(f);
env->lps = hio_read8(f);
env->lpe = hio_read8(f);
env->sus2 = hio_read8(f);
/* The format document claims there should be a reserved byte here but
* no DigiBooster Pro module actually has this. The revised document
* on the DigiBooster 3 website is corrected.
*/
/* Sanity check */
if (env->ins < 0 || env->ins >= mod->ins || env->npt > 32 ||
env->sus >= 32 || env->lps >= 32 || env->lpe >= 32)
return -1;
for (i = 0; i < 32; i++) {
env->nodes[i].position = hio_read16b(f);
env->nodes[i].value = (int16)hio_read16b(f);
}
if (hio_error(f))
return -1;
return 0;
}
static int get_venv(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
struct dbm_envelope env;
int i, j, nenv, ins;
/* Sanity check */
if (data->have_venv || !data->have_info) {
return -1;
}
data->have_venv = 1;
nenv = hio_read16b(f);
D_(D_INFO "Vol envelopes : %d ", nenv);
for (i = 0; i < nenv; i++) {
if (read_envelope(mod, &env, f) != 0)
return -1;
ins = env.ins;
mod->xxi[ins].aei.flg = env.flg;
mod->xxi[ins].aei.npt = env.npt;
mod->xxi[ins].aei.sus = env.sus;
mod->xxi[ins].aei.lps = env.lps;
mod->xxi[ins].aei.lpe = env.lpe;
for (j = 0; j < 32; j++) {
mod->xxi[ins].aei.data[j * 2 + 0] = env.nodes[j].position;
mod->xxi[ins].aei.data[j * 2 + 1] = env.nodes[j].value;
}
}
return 0;
}
static int get_penv(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
struct dbm_envelope env;
int i, j, nenv, ins;
/* Sanity check */
if (data->have_penv || !data->have_info) {
return -1;
}
data->have_penv = 1;
nenv = hio_read16b(f);
D_(D_INFO "Pan envelopes : %d ", nenv);
for (i = 0; i < nenv; i++) {
if (read_envelope(mod, &env, f) != 0)
return -1;
ins = env.ins;
mod->xxi[ins].pei.flg = env.flg;
mod->xxi[ins].pei.npt = env.npt;
mod->xxi[ins].pei.sus = env.sus;
mod->xxi[ins].pei.lps = env.lps;
mod->xxi[ins].pei.lpe = env.lpe;
for (j = 0; j < 32; j++) {
/* DigiBooster Pro 2 stores the pan value between 0 and 64.
* DigiBooster 3 stores it from -128 to 128 (Krashan - M2.dbm).
*/
if (data->maj_version >= 3) {
env.nodes[j].value = env.nodes[j].value / 4 + 32;
}
mod->xxi[ins].pei.data[j * 2 + 0] = env.nodes[j].position;
mod->xxi[ins].pei.data[j * 2 + 1] = env.nodes[j].value;
}
}
return 0;
}
static int dbm_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
iff_handle handle;
char name[XMP_NAME_SIZE];
uint16 version;
int i, ret;
struct local_data data;
LOAD_INIT();
hio_read32b(f); /* DBM0 */
memset(&data, 0, sizeof(struct local_data));
version = hio_read16b(f);
data.maj_version = version >> 8;
data.min_version = version & 0xFF;
hio_seek(f, 10, SEEK_CUR);
if (hio_read(name, 1, 44, f) < 44)
return -1;
name[44] = '\0';
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
m->c4rate = C4_NTSC_RATE;
m->quirk |= QUIRK_FINEFX;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "INFO", get_info);
ret |= libxmp_iff_register(handle, "SONG", get_song);
ret |= libxmp_iff_register(handle, "INST", get_inst);
ret |= libxmp_iff_register(handle, "PATT", get_patt);
ret |= libxmp_iff_register(handle, "SMPL", get_smpl);
ret |= libxmp_iff_register(handle, "VENV", get_venv);
ret |= libxmp_iff_register(handle, "PENV", get_penv);
if (ret != 0)
return -1;
strncpy(mod->name, name, XMP_NAME_SIZE);
snprintf(mod->type, XMP_NAME_SIZE, "DigiBooster Pro %d.%02x DBM0",
data.maj_version, data.min_version);
MODULE_INFO();
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
libxmp_iff_release(handle);
for (i = 0; i < mod->chn; i++)
mod->xxc[i].pan = 0x80;
return 0;
}

View File

@ -0,0 +1,252 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Based on the DIGI Booster player v1.6 by Tap (Tomasz Piasta), with the
* help of Louise Heimann <louise.heimann@softhome.net>. The following
* DIGI Booster effects are _NOT_ recognized by this player:
*
* 8xx robot
* e00 filter off
* e01 filter on
* e30 backwd play sample
* e31 backwd play sample+loop
* e50 channel off
* e51 channel on
* e8x sample offset 2
* e9x retrace
*/
#include "loader.h"
static int digi_test (HIO_HANDLE *, char *, const int);
static int digi_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_digi = {
"DIGI Booster",
digi_test,
digi_load
};
static int digi_test(HIO_HANDLE *f, char *t, const int start)
{
char buf[20];
if (hio_read(buf, 1, 20, f) < 20)
return -1;
if (memcmp(buf, "DIGI Booster module", 19))
return -1;
hio_seek(f, 156, SEEK_CUR);
hio_seek(f, 3 * 4 * 32, SEEK_CUR);
hio_seek(f, 2 * 1 * 32, SEEK_CUR);
libxmp_read_title(f, t, 32);
return 0;
}
struct digi_header {
uint8 id[20]; /* ID: "DIGI Booster module\0" */
uint8 vstr[4]; /* Version string: "Vx.y" */
uint8 ver; /* Version hi-nibble.lo-nibble */
uint8 chn; /* Number of channels */
uint8 pack; /* PackEnable */
uint8 unknown[19]; /* ?! */
uint8 pat; /* Number of patterns */
uint8 len; /* Song length */
uint8 ord[128]; /* Orders */
uint32 slen[31]; /* Sample length for 31 samples */
uint32 sloop[31]; /* Sample loop start for 31 samples */
uint32 sllen[31]; /* Sample loop length for 31 samples */
uint8 vol[31]; /* Instrument volumes */
int8 fin[31]; /* Finetunes */
uint8 title[32]; /* Song name */
uint8 insname[31][30]; /* Instrument names */
};
static int digi_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event = 0;
struct digi_header dh;
uint8 digi_event[4], chn_table[64];
uint16 w;
int i, j, k, c;
LOAD_INIT();
hio_read(dh.id, 20, 1, f);
hio_read(dh.vstr, 4, 1, f);
dh.ver = hio_read8(f);
dh.chn = hio_read8(f);
dh.pack = hio_read8(f);
hio_read(dh.unknown, 19, 1, f);
dh.pat = hio_read8(f);
dh.len = hio_read8(f);
/* Sanity check */
if (dh.len > 127) {
return -1;
}
hio_read(dh.ord, 128, 1, f);
for (i = 0; i < 31; i++)
dh.slen[i] = hio_read32b(f);
for (i = 0; i < 31; i++)
dh.sloop[i] = hio_read32b(f);
for (i = 0; i < 31; i++)
dh.sllen[i] = hio_read32b(f);
for (i = 0; i < 31; i++)
dh.vol[i] = hio_read8(f);
for (i = 0; i < 31; i++)
dh.fin[i] = hio_read8s(f);
if (hio_read(dh.title, 1, 32, f) < 32) {
D_(D_CRIT "read error at title");
return -1;
}
for (i = 0; i < 31; i++) {
if (hio_read(dh.insname[i], 1, 30, f) < 30) {
D_(D_CRIT "read error at instrument name %d", i);
return -1;
}
}
mod->ins = 31;
mod->smp = mod->ins;
mod->pat = dh.pat + 1;
mod->chn = dh.chn;
mod->trk = mod->pat * mod->chn;
mod->len = dh.len + 1;
m->period_type = PERIOD_MODRNG;
libxmp_copy_adjust(mod->name, dh.title, 32);
libxmp_set_type(m, "DIGI Booster %-4.4s", dh.vstr);
MODULE_INFO();
for (i = 0; i < mod->len; i++)
mod->xxo[i] = dh.ord[i];
if (libxmp_init_instrument(m) < 0)
return -1;
/* Read and convert instruments and samples */
for (i = 0; i < mod->ins; i++) {
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
mod->xxs[i].len = dh.slen[i];
mod->xxs[i].lps = dh.sloop[i];
mod->xxs[i].lpe = dh.sloop[i] + dh.sllen[i];
mod->xxs[i].flg = mod->xxs[i].lpe > 0 ? XMP_SAMPLE_LOOP : 0;
mod->xxi[i].sub[0].vol = dh.vol[i];
mod->xxi[i].sub[0].fin = dh.fin[i];
mod->xxi[i].sub[0].pan = 0x80;
mod->xxi[i].sub[0].sid = i;
if (mod->xxs[i].len > 0)
mod->xxi[i].nsm = 1;
libxmp_instrument_name(mod, i, dh.insname[i], 30);
D_(D_INFO "[%2X] %-30.30s %04x %04x %04x %c V%02x", i,
mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', mod->xxi[i].sub[0].vol);
}
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
if (dh.pack) {
w = (hio_read16b(f) - 64) >> 2;
if (hio_read(chn_table, 1, 64, f) < 64) {
D_(D_CRIT "read error at channel table %d", i);
return -1;
}
} else {
w = 64 * mod->chn;
memset(chn_table, 0xff, sizeof(chn_table));
}
for (j = 0; j < 64; j++) {
for (c = 0, k = 0x80; c < mod->chn; c++, k >>= 1) {
if (chn_table[j] & k) {
if (hio_read(digi_event, 1, 4, f) < 4) {
D_(D_CRIT "read error at pat %d", i);
return -1;
}
event = &EVENT (i, c, j);
libxmp_decode_protracker_event(event, digi_event);
switch (event->fxt) {
case 0x08: /* Robot */
event->fxt = event->fxp = 0;
break;
case 0x0e:
switch (MSN (event->fxp)) {
case 0x00:
case 0x03:
case 0x08:
case 0x09:
event->fxt = event->fxp = 0;
break;
case 0x04:
event->fxt = 0x0c;
event->fxp = 0x00;
break;
}
}
w--;
}
}
}
if (w) {
D_(D_CRIT "Corrupted file (w = %d)", w);
}
}
/* Read samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
}
return 0;
}

View File

@ -0,0 +1,465 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Public domain DMF sample decompressor by Olivier Lapicque
*/
#include "loader.h"
#include "iff.h"
#include "../period.h"
#define MAGIC_DDMF MAGIC4('D','D','M','F')
static int dmf_test(HIO_HANDLE *, char *, const int);
static int dmf_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_dmf = {
"X-Tracker",
dmf_test,
dmf_load
};
static int dmf_test(HIO_HANDLE * f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_DDMF)
return -1;
hio_seek(f, 9, SEEK_CUR);
libxmp_read_title(f, t, 30);
return 0;
}
struct local_data {
int ver;
uint8 packtype[256];
};
struct hnode {
short int left, right;
uint8 value;
};
struct htree {
uint8 *ibuf, *ibufmax;
uint32 bitbuf;
int bitnum;
int lastnode, nodecount;
struct hnode nodes[256];
};
static uint8 read_bits(struct htree *tree, int nbits)
{
uint8 x = 0, bitv = 1;
while (nbits--) {
if (tree->bitnum) {
tree->bitnum--;
} else {
tree->bitbuf = (tree->ibuf < tree->ibufmax) ?
*(tree->ibuf++) : 0;
tree->bitnum = 7;
}
if (tree->bitbuf & 1) x |= bitv;
bitv <<= 1;
tree->bitbuf >>= 1;
}
return x;
}
/* tree: [8-bit value][12-bit index][12-bit index] = 32-bit */
static void new_node(struct htree *tree)
{
uint8 isleft, isright;
int actnode;
actnode = tree->nodecount;
if (actnode > 255)
return;
tree->nodes[actnode].value = read_bits(tree, 7);
isleft = read_bits(tree, 1);
isright = read_bits(tree, 1);
actnode = tree->lastnode;
if (actnode > 255)
return;
tree->nodecount++;
tree->lastnode = tree->nodecount;
if (isleft) {
tree->nodes[actnode].left = tree->lastnode;
new_node(tree);
} else {
tree->nodes[actnode].left = -1;
}
tree->lastnode = tree->nodecount;
if (isright) {
tree->nodes[actnode].right = tree->lastnode;
new_node(tree);
} else {
tree->nodes[actnode].right = -1;
}
}
static int unpack(uint8 *psample, uint8 *ibuf, uint8 *ibufmax, uint32 maxlen)
{
struct htree tree;
int i, actnode;
uint8 value, sign, delta = 0;
memset(&tree, 0, sizeof(tree));
tree.ibuf = ibuf;
tree.ibufmax = ibufmax;
new_node(&tree);
value = 0;
for (i = 0; i < maxlen; i++) {
actnode = 0;
sign = read_bits(&tree, 1);
do {
if (read_bits(&tree, 1))
actnode = tree.nodes[actnode].right;
else
actnode = tree.nodes[actnode].left;
if (actnode > 255) break;
delta = tree.nodes[actnode].value;
if ((tree.ibuf >= tree.ibufmax) && (!tree.bitnum)) break;
} while ((tree.nodes[actnode].left >= 0) &&
(tree.nodes[actnode].right >= 0));
if (sign)
delta ^= 0xff;
value += delta;
psample[i] = i ? value : 0;
}
return tree.ibuf - ibuf;
}
/*
* IFF chunk handlers
*/
static int get_sequ(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i;
hio_read16l(f); /* sequencer loop start */
hio_read16l(f); /* sequencer loop end */
mod->len = (size - 4) / 2;
if (mod->len > 255)
mod->len = 255;
for (i = 0; i < mod->len; i++)
mod->xxo[i] = hio_read16l(f);
return 0;
}
static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i, j, r, chn;
int patrows;
int patsize;
int info, counter, data;
int track_counter[32];
struct xmp_event *event;
mod->pat = hio_read16l(f);
mod->chn = hio_read8(f);
mod->trk = mod->chn * mod->pat;
if (libxmp_init_pattern(mod) < 0)
return -1;
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
chn = hio_read8(f);
hio_read8(f); /* beat */
patrows = hio_read16l(f);
/* Sanity check */
if (patrows > 512)
return -1;
if (libxmp_alloc_pattern_tracks_long(mod, i, patrows) < 0)
return -1;
patsize = hio_read32l(f);
for (j = 0; j < chn; j++)
track_counter[j] = 0;
for (counter = r = 0; r < mod->xxp[i]->rows; r++) {
if (counter == 0) {
/* global track */
info = hio_read8(f);
counter = info & 0x80 ? hio_read8(f) : 0;
data = info & 0x3f ? hio_read8(f) : 0;
} else {
counter--;
}
for (j = 0; j < chn; j++) {
int b, fxt, fxp;
event = &EVENT(i, j, r);
if (track_counter[j] == 0) {
b = hio_read8(f);
if (b & 0x80)
track_counter[j] = hio_read8(f);
if (b & 0x40)
event->ins = hio_read8(f);
if (b & 0x20)
event->note = 24 + hio_read8(f);
if (b & 0x10)
event->vol = hio_read8(f);
if (b & 0x08) { /* instrument effect */
fxt = hio_read8(f);
fxp = hio_read8(f);
}
if (b & 0x04) { /* note effect */
fxt = hio_read8(f);
fxp = hio_read8(f);
}
if (b & 0x02) { /* volume effect */
fxt = hio_read8(f);
fxp = hio_read8(f);
switch (fxt) {
case 0x02:
event->fxt = FX_VOLSLIDE_DN;
event->fxp = fxp;
break;
}
}
} else {
track_counter[j]--;
}
}
}
}
return 0;
}
static int get_smpi(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i, namelen, c3spd, flag;
uint8 name[30];
mod->ins = mod->smp = hio_read8(f);
if (libxmp_init_instrument(m) < 0)
return -1;
D_(D_INFO "Instruments: %d", mod->ins);
for (i = 0; i < mod->ins; i++) {
int x;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
namelen = hio_read8(f);
x = namelen - hio_read(name, 1, namelen > 30 ? 30 : namelen, f);
libxmp_instrument_name(mod, i, name, namelen);
name[namelen] = 0;
while (x--)
hio_read8(f);
mod->xxs[i].len = hio_read32l(f);
mod->xxs[i].lps = hio_read32l(f);
mod->xxs[i].lpe = hio_read32l(f);
mod->xxi[i].nsm = !!mod->xxs[i].len;
c3spd = hio_read16l(f);
libxmp_c2spd_to_note(c3spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin);
mod->xxi[i].sub[0].vol = hio_read8(f);
mod->xxi[i].sub[0].pan = 0x80;
mod->xxi[i].sub[0].sid = i;
flag = hio_read8(f);
mod->xxs[i].flg = flag & 0x01 ? XMP_SAMPLE_LOOP : 0;
if (data->ver >= 8)
hio_seek(f, 8, SEEK_CUR); /* library name */
hio_read16l(f); /* reserved -- specs say 1 byte only*/
hio_read32l(f); /* sampledata crc32 */
data->packtype[i] = (flag & 0x0c) >> 2;
D_(D_INFO "[%2X] %-30.30s %05x %05x %05x %c P%c %5d V%02x",
i, name, mod->xxs[i].len, mod->xxs[i].lps & 0xfffff,
mod->xxs[i].lpe & 0xfffff,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
'0' + data->packtype[i],
c3spd, mod->xxi[i].sub[0].vol);
}
return 0;
}
struct dynamic_buffer
{
uint32 size;
uint8* data;
};
static int dynamic_buffer_alloc(struct dynamic_buffer* buf, uint32 size)
{
uint8* data;
if (buf->size >= size)
return 0;
if (!buf->data)
data = malloc(size);
else
data = realloc(buf->data, size);
if (data) {
buf->data = data;
buf->size = size;
return 0;
} else {
return -1;
}
}
static void dynamic_buffer_free(struct dynamic_buffer* buf)
{
free(buf->data);
}
static int get_smpd(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i;
struct dynamic_buffer sbuf = {0}, ibuf = {0};
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
uint32 samplesize = mod->xxs[i].len;
uint32 datasize = hio_read32l(f);
if (datasize == 0)
continue;
switch (data->packtype[i]) {
case 0:
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
goto error;
break;
case 1:
if (dynamic_buffer_alloc(&ibuf, datasize) < 0)
goto error;
if (hio_read(ibuf.data, 1, datasize, f) != datasize)
goto error;
if (dynamic_buffer_alloc(&sbuf, samplesize) < 0)
goto error;
unpack(sbuf.data, ibuf.data, ibuf.data + datasize, samplesize);
if (libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD,
&mod->xxs[i], (char *)sbuf.data) < 0)
goto error;
break;
default:
hio_seek(f, datasize, SEEK_CUR);
}
}
dynamic_buffer_free(&ibuf);
dynamic_buffer_free(&sbuf);
return 0;
error:
dynamic_buffer_free(&ibuf);
dynamic_buffer_free(&sbuf);
return -1;
}
static int dmf_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
iff_handle handle;
uint8 date[3];
char tracker_name[10];
struct local_data data;
int ret;
LOAD_INIT();
hio_read32b(f); /* DDMF */
data.ver = hio_read8(f);
hio_read(tracker_name, 8, 1, f);
tracker_name[8] = 0;
snprintf(mod->type, XMP_NAME_SIZE, "%s DMF v%d",
tracker_name, data.ver);
tracker_name[8] = 0;
hio_read(mod->name, 30, 1, f);
hio_seek(f, 20, SEEK_CUR);
hio_read(date, 3, 1, f);
m->c4rate = C4_NTSC_RATE;
MODULE_INFO();
D_(D_INFO "Creation date: %02d/%02d/%04d", date[0],
date[1], 1900 + date[2]);
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "SEQU", get_sequ);
ret |= libxmp_iff_register(handle, "PATT", get_patt);
ret |= libxmp_iff_register(handle, "SMPI", get_smpi);
ret |= libxmp_iff_register(handle, "SMPD", get_smpd);
if (ret != 0)
return -1;
libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN);
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
m->volbase = 0xff;
libxmp_iff_release(handle);
return 0;
}

View File

@ -0,0 +1,354 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "iff.h"
#include "../period.h"
#define MAGIC_D_T_ MAGIC4('D','.','T','.')
static int dt_test(HIO_HANDLE *, char *, const int);
static int dt_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_dt = {
"Digital Tracker",
dt_test,
dt_load
};
static int dt_test(HIO_HANDLE *f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_D_T_)
return -1;
hio_read32b(f); /* chunk size */
hio_read16b(f); /* type */
hio_read16b(f); /* 0xff then mono */
hio_read16b(f); /* reserved */
hio_read16b(f); /* tempo */
hio_read16b(f); /* bpm */
hio_read32b(f); /* undocumented */
libxmp_read_title(f, t, 32);
return 0;
}
struct local_data {
int pflag, sflag;
int realpat;
int last_pat;
int insnum;
};
static int get_d_t_(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int b;
hio_read16b(f); /* type */
hio_read16b(f); /* 0xff then mono */
hio_read16b(f); /* reserved */
mod->spd = hio_read16b(f);
if ((b = hio_read16b(f)) > 0) /* RAMBO.DTM has bpm 0 */
mod->bpm = b;
hio_read32b(f); /* undocumented */
hio_read(mod->name, 32, 1, f);
libxmp_set_type(m, "Digital Tracker DTM");
MODULE_INFO();
return 0;
}
static int get_s_q_(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i, maxpat;
/* Sanity check */
if (mod->pat != 0) {
return -1;
}
mod->len = hio_read16b(f);
mod->rst = hio_read16b(f);
/* Sanity check */
if (mod->len > 256 || mod->rst > 255) {
return -1;
}
hio_read32b(f); /* reserved */
for (maxpat = i = 0; i < 128; i++) {
mod->xxo[i] = hio_read8(f);
if (mod->xxo[i] > maxpat)
maxpat = mod->xxo[i];
}
mod->pat = maxpat + 1;
return 0;
}
static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
/* Sanity check */
if (data->pflag) {
return -1;
}
mod->chn = hio_read16b(f);
data->realpat = hio_read16b(f);
mod->trk = mod->chn * mod->pat;
/* Sanity check */
if (mod->chn > XMP_MAX_CHANNELS) {
return -1;
}
return 0;
}
static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i, c2spd;
uint8 name[30];
/* Sanity check */
if (mod->ins != 0) {
return -1;
}
mod->ins = mod->smp = hio_read16b(f);
/* Sanity check */
if (mod->ins > MAX_INSTRUMENTS) {
return -1;
}
D_(D_INFO "Instruments : %d ", mod->ins);
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
uint32 repstart, replen;
int fine, flag;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
hio_read32b(f); /* reserved */
mod->xxs[i].len = hio_read32b(f);
mod->xxi[i].nsm = !!mod->xxs[i].len;
fine = hio_read8s(f); /* finetune */
mod->xxi[i].sub[0].vol = hio_read8(f);
mod->xxi[i].sub[0].pan = 0x80;
repstart = hio_read32b(f);
replen = hio_read32b(f);
mod->xxs[i].lps = repstart;
mod->xxs[i].lpe = repstart + replen - 1;
mod->xxs[i].flg = replen > 2 ? XMP_SAMPLE_LOOP : 0;
if (hio_read(name, 22, 1, f) == 0)
return -1;
libxmp_instrument_name(mod, i, name, 22);
flag = hio_read16b(f); /* bit 0-7:resol 8:stereo */
if ((flag & 0xff) > 8) {
mod->xxs[i].flg |= XMP_SAMPLE_16BIT;
mod->xxs[i].len >>= 1;
mod->xxs[i].lps >>= 1;
mod->xxs[i].lpe >>= 1;
}
hio_read32b(f); /* midi note (0x00300000) */
c2spd = hio_read32b(f); /* frequency */
libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin);
/* It's strange that we have both c2spd and finetune */
mod->xxi[i].sub[0].fin += fine;
mod->xxi[i].sub[0].sid = i;
D_(D_INFO "[%2X] %-22.22s %05x%c%05x %05x %c%c %2db V%02x F%+03d %5d",
i, mod->xxi[i].name,
mod->xxs[i].len,
mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ',
repstart,
replen,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
flag & 0x100 ? 'S' : ' ',
flag & 0xff,
mod->xxi[i].sub[0].vol,
fine,
c2spd);
}
return 0;
}
static int get_dapt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int pat, i, j, k;
struct xmp_event *event;
int rows;
if (!data->pflag) {
D_(D_INFO "Stored patterns: %d", mod->pat);
data->pflag = 1;
data->last_pat = 0;
if (libxmp_init_pattern(mod) < 0)
return -1;
}
hio_read32b(f); /* 0xffffffff */
pat = hio_read16b(f);
rows = hio_read16b(f);
/* Sanity check */
if (pat < 0 || pat >= mod->pat || rows < 0 || rows > 256) {
return -1;
}
if (pat < data->last_pat) {
return -1;
}
for (i = data->last_pat; i <= pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0)
return -1;
}
data->last_pat = pat + 1;
for (j = 0; j < rows; j++) {
for (k = 0; k < mod->chn; k++) {
uint8 a, b, c, d;
event = &EVENT(pat, k, j);
a = hio_read8(f);
b = hio_read8(f);
c = hio_read8(f);
d = hio_read8(f);
if (a) {
a--;
event->note = 12 * (a >> 4) + (a & 0x0f) + 12;
}
event->vol = (b & 0xfc) >> 2;
event->ins = ((b & 0x03) << 4) + (c >> 4);
event->fxt = c & 0xf;
event->fxp = d;
}
}
return 0;
}
static int get_dait(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
if (!data->sflag) {
D_(D_INFO "Stored samples : %d ", mod->smp);
data->sflag = 1;
data->insnum = 0;
}
if (size > 2) {
int ret;
/* Sanity check */
if (data->insnum >= mod->ins) {
return -1;
}
ret = libxmp_load_sample(m, f, SAMPLE_FLAG_BIGEND,
&mod->xxs[mod->xxi[data->insnum].sub[0].sid], NULL);
if (ret < 0)
return -1;
}
data->insnum++;
return 0;
}
static int dt_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
iff_handle handle;
struct local_data data;
struct xmp_module *mod = &m->mod;
int ret, i;
LOAD_INIT();
memset(&data, 0, sizeof (struct local_data));
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
m->c4rate = C4_NTSC_RATE;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "D.T.", get_d_t_);
ret |= libxmp_iff_register(handle, "S.Q.", get_s_q_);
ret |= libxmp_iff_register(handle, "PATT", get_patt);
ret |= libxmp_iff_register(handle, "INST", get_inst);
ret |= libxmp_iff_register(handle, "DAPT", get_dapt);
ret |= libxmp_iff_register(handle, "DAIT", get_dait);
if (ret != 0)
return -1;
/* Load IFF chunks */
ret = libxmp_iff_load(handle, m, f , &data);
libxmp_iff_release(handle);
if (ret < 0)
return -1;
/* alloc remaining patterns */
if (mod->xxp != NULL) {
for (i = data.last_pat; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) {
return -1;
}
}
}
return 0;
}

View File

@ -0,0 +1,185 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#define MAGIC_DskT MAGIC4('D','s','k','T')
#define MAGIC_DskS MAGIC4('D','s','k','S')
static int dtt_test(HIO_HANDLE *, char *, const int);
static int dtt_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_dtt = {
"Desktop Tracker",
dtt_test,
dtt_load
};
static int dtt_test(HIO_HANDLE *f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_DskT)
return -1;
libxmp_read_title(f, t, 64);
return 0;
}
static int dtt_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
int i, j, k;
int n;
uint8 buf[100];
uint32 flags;
uint32 pofs[256];
uint8 plen[256];
int sdata[64];
LOAD_INIT();
hio_read32b(f);
libxmp_set_type(m, "Desktop Tracker");
hio_read(buf, 1, 64, f);
strncpy(mod->name, (char *)buf, XMP_NAME_SIZE);
hio_read(buf, 1, 64, f);
/* strncpy(m->author, (char *)buf, XMP_NAME_SIZE); */
flags = hio_read32l(f);
mod->chn = hio_read32l(f);
mod->len = hio_read32l(f);
hio_read(buf, 1, 8, f);
mod->spd = hio_read32l(f);
mod->rst = hio_read32l(f);
mod->pat = hio_read32l(f);
mod->ins = mod->smp = hio_read32l(f);
mod->trk = mod->pat * mod->chn;
hio_read(mod->xxo, 1, (mod->len + 3) & ~3L, f);
m->c4rate = C4_NTSC_RATE;
MODULE_INFO();
for (i = 0; i < mod->pat; i++) {
int x = hio_read32l(f);
if (i < 256)
pofs[i] = x;
}
n = (mod->pat + 3) & ~3L;
for (i = 0; i < n; i++) {
int x = hio_read8(f);
if (i < 256)
plen[i] = x;
}
if (libxmp_init_instrument(m) < 0)
return -1;
/* Read instrument names */
for (i = 0; i < mod->ins; i++) {
int c2spd, looplen;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
hio_read8(f); /* note */
mod->xxi[i].sub[0].vol = hio_read8(f) >> 1;
mod->xxi[i].sub[0].pan = 0x80;
hio_read16l(f); /* not used */
c2spd = hio_read32l(f); /* period? */
hio_read32l(f); /* sustain start */
hio_read32l(f); /* sustain length */
mod->xxs[i].lps = hio_read32l(f);
looplen = hio_read32l(f);
mod->xxs[i].flg = looplen > 0 ? XMP_SAMPLE_LOOP : 0;
mod->xxs[i].lpe = mod->xxs[i].lps + looplen;
mod->xxs[i].len = hio_read32l(f);
hio_read(buf, 1, 32, f);
libxmp_instrument_name(mod, i, (uint8 *)buf, 32);
sdata[i] = hio_read32l(f);
mod->xxi[i].nsm = !!(mod->xxs[i].len);
mod->xxi[i].sub[0].sid = i;
D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x %d\n",
i, mod->xxi[i].name, mod->xxs[i].len,
mod->xxs[i].lps, mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol, c2spd);
}
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, plen[i]) < 0)
return -1;
hio_seek(f, start + pofs[i], SEEK_SET);
for (j = 0; j < mod->xxp[i]->rows; j++) {
for (k = 0; k < mod->chn; k++) {
uint32 x;
event = &EVENT (i, k, j);
x = hio_read32l(f);
event->ins = (x & 0x0000003f);
event->note = (x & 0x00000fc0) >> 6;
event->fxt = (x & 0x0001f000) >> 12;
if (event->note)
event->note += 48;
/* sorry, we only have room for two effects */
if (x & (0x1f << 17)) {
event->f2p = (x & 0x003e0000) >> 17;
x = hio_read32l(f);
event->fxp = (x & 0x000000ff);
event->f2p = (x & 0x0000ff00) >> 8;
} else {
event->fxp = (x & 0xfc000000) >> 18;
}
}
}
}
/* Read samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
hio_seek(f, start + sdata[i], SEEK_SET);
if (libxmp_load_sample(m, f, SAMPLE_FLAG_VIDC, &mod->xxs[i], NULL) < 0)
return -1;
}
return 0;
}

View File

@ -0,0 +1,262 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "iff.h"
#define MAGIC_FORM MAGIC4('F','O','R','M')
#define MAGIC_EMOD MAGIC4('E','M','O','D')
#define MAGIC_EMIC MAGIC4('E','M','I','C')
static int emod_test(HIO_HANDLE *, char *, const int);
static int emod_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_emod = {
"Quadra Composer",
emod_test,
emod_load
};
static int emod_test(HIO_HANDLE * f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_FORM)
return -1;
hio_read32b(f);
if (hio_read32b(f) != MAGIC_EMOD)
return -1;
if (hio_read32b(f) == MAGIC_EMIC) {
hio_read32b(f); /* skip size */
hio_read16b(f); /* skip version */
libxmp_read_title(f, t, 20);
} else {
libxmp_read_title(f, t, 0);
}
return 0;
}
struct local_data {
int has_emic;
int has_patt;
int has_8smp;
};
static int get_emic(struct module_data *m, int size, HIO_HANDLE * f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i, ver;
uint8 reorder[256];
/* Sanity check */
if (data->has_emic) {
return -1;
}
data->has_emic = 1;
ver = hio_read16b(f);
hio_read(mod->name, 1, 20, f);
hio_seek(f, 20, SEEK_CUR);
mod->bpm = hio_read8(f);
mod->ins = hio_read8(f);
mod->smp = mod->ins;
m->period_type = PERIOD_MODRNG;
snprintf(mod->type, XMP_NAME_SIZE, "Quadra Composer EMOD v%d", ver);
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
uint8 name[20];
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
sub = &xxi->sub[0];
hio_read8(f); /* num */
sub->vol = hio_read8(f);
xxs->len = 2 * hio_read16b(f);
if (hio_read(name, 1, 20, f) < 20)
return -1;
libxmp_instrument_name(mod, i, name, 20);
xxs->flg = hio_read8(f) & 1 ? XMP_SAMPLE_LOOP : 0;
sub->fin = hio_read8s(f) << 4;
xxs->lps = 2 * hio_read16b(f);
xxs->lpe = xxs->lps + 2 * hio_read16b(f);
hio_read32b(f); /* ptr */
xxi->nsm = 1;
sub->pan = 0x80;
sub->sid = i;
D_(D_INFO "[%2X] %-20.20s %05x %05x %05x %c V%02x %+d",
i, xxi->name, xxs->len, xxs->lps, xxs->lpe,
xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
sub->vol, sub->fin >> 4);
}
hio_read8(f); /* pad */
mod->pat = hio_read8(f);
mod->trk = mod->pat * mod->chn;
if (libxmp_init_pattern(mod) < 0)
return -1;
memset(reorder, 0, sizeof(reorder));
for (i = 0; i < mod->pat; i++) {
reorder[hio_read8(f)] = i;
if (libxmp_alloc_pattern_tracks(mod, i, hio_read8(f) + 1) < 0)
return -1;
hio_seek(f, 20, SEEK_CUR); /* skip name */
hio_read32b(f); /* ptr */
}
mod->len = hio_read8(f);
D_(D_INFO "Module length: %d", mod->len);
for (i = 0; i < mod->len; i++)
mod->xxo[i] = reorder[hio_read8(f)];
return 0;
}
static int get_patt(struct module_data *m, int size, HIO_HANDLE * f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
struct xmp_event *event;
int i, j, k;
uint8 x;
/* Sanity check */
if (data->has_patt || !data->has_emic) {
return -1;
}
data->has_patt = 1;
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
for (j = 0; j < mod->xxp[i]->rows; j++) {
for (k = 0; k < mod->chn; k++) {
event = &EVENT(i, k, j);
event->ins = hio_read8(f);
event->note = hio_read8(f) + 1;
if (event->note != 0)
event->note += 48;
event->fxt = hio_read8(f) & 0x0f;
event->fxp = hio_read8(f);
/* Fix effects */
switch (event->fxt) {
case 0x04:
x = event->fxp;
event->fxp =
(x & 0xf0) | ((x << 1) & 0x0f);
break;
case 0x09:
event->fxt <<= 1;
break;
case 0x0b:
x = event->fxt;
event->fxt = 16 * (x / 10) + x % 10;
break;
}
}
}
}
return 0;
}
static int get_8smp(struct module_data *m, int size, HIO_HANDLE * f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i;
/* Sanity check */
if (data->has_8smp || !data->has_emic) {
return -1;
}
data->has_8smp = 1;
D_(D_INFO "Stored samples : %d ", mod->smp);
for (i = 0; i < mod->smp; i++) {
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
}
return 0;
}
static int emod_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
iff_handle handle;
struct local_data data;
int ret;
LOAD_INIT();
memset(&data, 0, sizeof(struct local_data));
hio_read32b(f); /* FORM */
hio_read32b(f);
hio_read32b(f); /* EMOD */
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "EMIC", get_emic);
ret |= libxmp_iff_register(handle, "PATT", get_patt);
ret |= libxmp_iff_register(handle, "8SMP", get_8smp);
if (ret != 0)
return -1;
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
libxmp_iff_release(handle);
return 0;
}

View File

@ -0,0 +1,472 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Based on the Farandole Composer format specifications by Daniel Potter.
*
* "(...) this format is for EDITING purposes (storing EVERYTHING you're
* working on) so it may include information not completely neccessary."
*/
#include "loader.h"
#include "../far_extras.h"
struct far_header {
uint32 magic; /* File magic: 'FAR\xfe' */
uint8 name[40]; /* Song name */
uint8 crlf[3]; /* 0x0d 0x0a 0x1A */
uint16 headersize; /* Remaining header size in bytes */
uint8 version; /* Version MSN=major, LSN=minor */
uint8 ch_on[16]; /* Channel on/off switches */
uint8 rsvd1[9]; /* Current editing values */
uint8 tempo; /* Default tempo */
uint8 pan[16]; /* Channel pan definitions */
uint8 rsvd2[4]; /* Grid, mode (for editor) */
uint16 textlen; /* Length of embedded text */
};
struct far_header2 {
uint8 order[256]; /* Orders */
uint8 patterns; /* Number of stored patterns (?) */
uint8 songlen; /* Song length in patterns */
uint8 restart; /* Restart pos */
uint16 patsize[256]; /* Size of each pattern in bytes */
};
struct far_instrument {
uint8 name[32]; /* Instrument name */
uint32 length; /* Length of sample (up to 64Kb) */
uint8 finetune; /* Finetune (unsuported) */
uint8 volume; /* Volume (unsuported?) */
uint32 loop_start; /* Loop start */
uint32 loopend; /* Loop end */
uint8 sampletype; /* 1=16 bit sample */
uint8 loopmode;
};
struct far_event {
uint8 note;
uint8 instrument;
uint8 volume; /* In reverse nibble order? */
uint8 effect;
};
#define MAGIC_FAR MAGIC4('F','A','R',0xfe)
static int far_test (HIO_HANDLE *, char *, const int);
static int far_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_far = {
"Farandole Composer",
far_test,
far_load
};
static int far_test(HIO_HANDLE *f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_FAR)
return -1;
libxmp_read_title(f, t, 40);
return 0;
}
static void far_translate_effect(struct xmp_event *event, int fx, int param, int vol)
{
switch (fx) {
case 0x0: /* 0x0? Global funct */
switch (param) {
case 0x1: /* 0x01 Ramp delay on */
case 0x2: /* 0x02 Ramp delay off */
/* These control volume ramping and can be ignored. */
break;
case 0x3: /* 0x03 Fulfill loop */
/* This is intended to be sustain release, but the
* effect is buggy and just cuts most of the time. */
event->fxt = FX_KEYOFF;
break;
case 0x4: /* 0x04 Old FAR tempo */
event->fxt = FX_FAR_TEMPO;
event->fxp = 0x10;
break;
case 0x5: /* 0x05 New FAR tempo */
event->fxt = FX_FAR_TEMPO;
event->fxp = 0x20;
break;
}
break;
case 0x1: /* 0x1? Pitch offset up */
event->fxt = FX_FAR_PORTA_UP;
event->fxp = param;
break;
case 0x2: /* 0x2? Pitch offset down */
event->fxt = FX_FAR_PORTA_DN;
event->fxp = param;
break;
case 0x3: /* 0x3? Note-port */
event->fxt = FX_FAR_TPORTA;
event->fxp = param;
break;
case 0x4: /* 0x4? Retrigger */
event->fxt = FX_FAR_RETRIG;
event->fxp = param;
break;
case 0x5: /* 0x5? Set Vibrato depth */
event->fxt = FX_FAR_VIBDEPTH;
event->fxp = param;
break;
case 0x6: /* 0x6? Vibrato note */
event->fxt = FX_FAR_VIBRATO;
event->fxp = param;
break;
case 0x7: /* 0x7? Vol Sld Up */
event->fxt = FX_F_VSLIDE_UP;
event->fxp = (param << 4);
break;
case 0x8: /* 0x8? Vol Sld Dn */
event->fxt = FX_F_VSLIDE_DN;
event->fxp = (param << 4);
break;
case 0x9: /* 0x9? Sustained vibrato */
event->fxt = FX_FAR_VIBRATO;
event->fxp = 0x10 /* Vibrato sustain flag */ | param;
break;
case 0xa: /* 0xa? Slide-to-vol */
if (vol >= 0x01 && vol <= 0x10) {
event->fxt = FX_FAR_SLIDEVOL;
event->fxp = ((vol - 1) << 4) | param;
event->vol = 0;
}
break;
case 0xb: /* 0xb? Balance */
event->fxt = FX_SETPAN;
event->fxp = (param << 4) | param;
break;
case 0xc: /* 0xc? Note Offset */
event->fxt = FX_FAR_DELAY;
event->fxp = param;
break;
case 0xd: /* 0xd? Fine tempo down */
event->fxt = FX_FAR_F_TEMPO;
event->fxp = param;
break;
case 0xe: /* 0xe? Fine tempo up */
event->fxt = FX_FAR_F_TEMPO;
event->fxp = param << 4;
break;
case 0xf: /* 0xf? Set tempo */
event->fxt = FX_FAR_TEMPO;
event->fxp = param;
break;
}
}
#define COMMENT_MAXLINES 44
static void far_read_text(char *dest, size_t textlen, HIO_HANDLE *f)
{
/* FAR module text uses 132-char lines with no line breaks... */
size_t end, lastchar, i;
if (textlen > COMMENT_MAXLINES * 132)
textlen = COMMENT_MAXLINES * 132;
while (textlen) {
end = MIN(textlen, 132);
textlen -= end;
end = hio_read(dest, 1, end, f);
lastchar = 0;
for (i = 0; i < end; i++) {
/* Nulls in the text area are equivalent to spaces. */
if (dest[i] == '\0')
dest[i] = ' ';
else if (dest[i] != ' ')
lastchar = i;
}
dest += lastchar + 1;
*dest++ = '\n';
}
*dest = '\0';
}
static int far_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct far_module_extras *me;
int i, j, k;
struct xmp_event *event;
struct far_header ffh;
struct far_header2 ffh2;
struct far_instrument fih;
uint8 *patbuf = NULL;
uint8 sample_map[8];
LOAD_INIT();
hio_read32b(f); /* File magic: 'FAR\xfe' */
hio_read(ffh.name, 40, 1, f); /* Song name */
hio_read(ffh.crlf, 3, 1, f); /* 0x0d 0x0a 0x1A */
ffh.headersize = hio_read16l(f); /* Remaining header size in bytes */
ffh.version = hio_read8(f); /* Version MSN=major, LSN=minor */
hio_read(ffh.ch_on, 16, 1, f); /* Channel on/off switches */
hio_seek(f, 9, SEEK_CUR); /* Current editing values */
ffh.tempo = hio_read8(f); /* Default tempo */
hio_read(ffh.pan, 16, 1, f); /* Channel pan definitions */
hio_read32l(f); /* Grid, mode (for editor) */
ffh.textlen = hio_read16l(f); /* Length of embedded text */
/* Sanity check */
if (ffh.tempo >= 16) {
return -1;
}
if ((m->comment = (char *)malloc(ffh.textlen + COMMENT_MAXLINES + 1)) != NULL) {
far_read_text(m->comment, ffh.textlen, f);
} else {
hio_seek(f, ffh.textlen, SEEK_CUR); /* Skip song text */
}
hio_read(ffh2.order, 256, 1, f); /* Orders */
ffh2.patterns = hio_read8(f); /* Number of stored patterns (?) */
ffh2.songlen = hio_read8(f); /* Song length in patterns */
ffh2.restart = hio_read8(f); /* Restart pos */
for (i = 0; i < 256; i++) {
ffh2.patsize[i] = hio_read16l(f); /* Size of each pattern in bytes */
}
if (hio_error(f)) {
return -1;
}
/* Skip unsupported header extension if it exists. The documentation claims
* this field is the "remaining" header size, but it's the total size. */
if (ffh.headersize > 869 + ffh.textlen) {
if (hio_seek(f, ffh.headersize, SEEK_SET))
return -1;
}
mod->chn = 16;
/*mod->pat=ffh2.patterns; (Error in specs? --claudio) */
mod->len = ffh2.songlen;
mod->rst = ffh2.restart;
memcpy (mod->xxo, ffh2.order, mod->len);
for (mod->pat = i = 0; i < 256; i++) {
if (ffh2.patsize[i])
mod->pat = i + 1;
}
/* Make sure referenced zero-sized patterns are also counted. */
for (i = 0; i < mod->len; i++) {
if (mod->pat <= mod->xxo[i])
mod->pat = mod->xxo[i] + 1;
}
mod->trk = mod->chn * mod->pat;
if (libxmp_far_new_module_extras(m) != 0)
return -1;
me = FAR_MODULE_EXTRAS(*m);
me->coarse_tempo = ffh.tempo;
me->fine_tempo = 0;
me->tempo_mode = 1;
m->time_factor = FAR_TIME_FACTOR;
libxmp_far_translate_tempo(1, 0, me->coarse_tempo, &me->fine_tempo, &mod->spd, &mod->bpm);
m->period_type = PERIOD_CSPD;
m->c4rate = C4_NTSC_RATE;
m->quirk |= QUIRK_VSALL | QUIRK_PBALL | QUIRK_VIBALL;
strncpy(mod->name, (char *)ffh.name, 40);
libxmp_set_type(m, "Farandole Composer %d.%d", MSN(ffh.version), LSN(ffh.version));
MODULE_INFO();
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Comment bytes : %d", ffh.textlen);
D_(D_INFO "Stored patterns: %d", mod->pat);
if ((patbuf = (uint8 *)malloc(256 * 16 * 4)) == NULL)
return -1;
for (i = 0; i < mod->pat; i++) {
uint8 brk, note, ins, vol, fxb;
uint8 *pos;
int rows;
if (libxmp_alloc_pattern(mod, i) < 0)
goto err;
if (!ffh2.patsize[i])
continue;
rows = (ffh2.patsize[i] - 2) / 64;
/* Sanity check */
if (rows <= 0 || rows > 256) {
goto err;
}
mod->xxp[i]->rows = rows;
if (libxmp_alloc_tracks_in_pattern(mod, i) < 0)
goto err;
brk = hio_read8(f) + 1;
hio_read8(f);
if (hio_read(patbuf, rows * 64, 1, f) < 1) {
D_(D_CRIT "read error at pat %d", i);
goto err;
}
pos = patbuf;
for (j = 0; j < mod->xxp[i]->rows; j++) {
for (k = 0; k < mod->chn; k++) {
event = &EVENT(i, k, j);
if (k == 0 && j == brk)
event->f2t = FX_BREAK;
note = *pos++;
ins = *pos++;
vol = *pos++;
fxb = *pos++;
if (note)
event->note = note + 48;
if (event->note || ins)
event->ins = ins + 1;
if (vol >= 0x01 && vol <= 0x10)
event->vol = (vol - 1) * 16 + 1;
far_translate_effect(event, MSN(fxb), LSN(fxb), vol);
}
}
}
free(patbuf);
/* Allocate tracks for any patterns referenced with a size of 0. These
* use the configured pattern break position, which is 64 by default. */
for (i = 0; i < mod->len; i++) {
int pat = mod->xxo[i];
if (mod->xxp[pat]->rows == 0) {
mod->xxp[pat]->rows = 64;
if (libxmp_alloc_tracks_in_pattern(mod, pat) < 0)
return -1;
}
}
mod->ins = -1;
if (hio_read(sample_map, 1, 8, f) < 8) {
D_(D_CRIT "read error at sample map");
return -1;
}
for (i = 0; i < 64; i++) {
if (sample_map[i / 8] & (1 << (i % 8)))
mod->ins = i;
}
mod->ins++;
mod->smp = mod->ins;
if (libxmp_init_instrument(m) < 0)
return -1;
/* Read and convert instruments and samples */
for (i = 0; i < mod->ins; i++) {
if (!(sample_map[i / 8] & (1 << (i % 8))))
continue;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
hio_read(fih.name, 32, 1, f); /* Instrument name */
fih.length = hio_read32l(f); /* Length of sample (up to 64Kb) */
fih.finetune = hio_read8(f); /* Finetune (unsuported) */
fih.volume = hio_read8(f); /* Volume (unsuported?) */
fih.loop_start = hio_read32l(f);/* Loop start */
fih.loopend = hio_read32l(f); /* Loop end */
fih.sampletype = hio_read8(f); /* 1=16 bit sample */
fih.loopmode = hio_read8(f);
/* Sanity check */
if (fih.length > 0x10000 || fih.loop_start > 0x10000 ||
fih.loopend > 0x10000) {
return -1;
}
mod->xxs[i].len = fih.length;
mod->xxs[i].lps = fih.loop_start;
mod->xxs[i].lpe = fih.loopend;
mod->xxs[i].flg = 0;
if (mod->xxs[i].len > 0)
mod->xxi[i].nsm = 1;
if (fih.sampletype != 0) {
mod->xxs[i].flg |= XMP_SAMPLE_16BIT;
mod->xxs[i].len >>= 1;
mod->xxs[i].lps >>= 1;
mod->xxs[i].lpe >>= 1;
}
mod->xxs[i].flg |= fih.loopmode ? XMP_SAMPLE_LOOP : 0;
mod->xxi[i].sub[0].vol = 0xff; /* fih.volume; */
mod->xxi[i].sub[0].sid = i;
libxmp_instrument_name(mod, i, fih.name, 32);
D_(D_INFO "[%2X] %-32.32s %04x %04x %04x %c V%02x",
i, mod->xxi[i].name, mod->xxs[i].len, mod->xxs[i].lps,
mod->xxs[i].lpe, fih.loopmode ? 'L' : ' ', mod->xxi[i].sub[0].vol);
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
}
/* Panning map */
for (i = 0; i < 16; i++) {
if (ffh.ch_on[i] == 0)
mod->xxc[i].flg |= XMP_CHANNEL_MUTE;
if (ffh.pan[i] < 0x10)
mod->xxc[i].pan = (ffh.pan[i] << 4) | ffh.pan[i];
}
m->volbase = 0xf0;
return 0;
err:
free(patbuf);
return -1;
}

View File

@ -0,0 +1,491 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "mod.h"
#include "../period.h"
static int flt_test(HIO_HANDLE *, char *, const int);
static int flt_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_flt = {
"Startrekker",
flt_test,
flt_load
};
static int flt_test(HIO_HANDLE * f, char *t, const int start)
{
char buf[4];
hio_seek(f, start + 1080, SEEK_SET);
if (hio_read(buf, 1, 4, f) < 4)
return -1;
/* Also RASP? */
if (memcmp(buf, "FLT", 3) && memcmp(buf, "EXO", 3))
return -1;
if (buf[3] != '4' && buf[3] != '8' && buf[3] != 'M')
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 20);
return 0;
}
/* Waveforms from the Startrekker 1.2 AM synth replayer code */
static const int8 am_waveform[3][32] = {
{ 0, 25, 49, 71, 90, 106, 117, 125, /* Sine */
127, 125, 117, 106, 90, 71, 49, 25,
0, -25, -49, -71, -90, -106, -117, -125,
-127, -125, -117, -106, -90, -71, -49, -25
},
{ -128, -120, -112, -104, -96, -88, -80, -72, /* Ramp */
-64, -56, -48, -40, -32, -24, -16, -8,
0, 8, 16, 24, 32, 40, 48, 56,
64, 72, 80, 88, 96, 104, 112, 120
},
{ -128, -128, -128, -128, -128, -128, -128, -128, /* Square */
-128, -128, -128, -128, -128, -128, -128, -128,
127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127
}
};
struct am_instrument {
int16 l0; /* start amplitude */
int16 a1l; /* attack level */
int16 a1s; /* attack speed */
int16 a2l; /* secondary attack level */
int16 a2s; /* secondary attack speed */
int16 sl; /* sustain level */
int16 ds; /* decay speed */
int16 st; /* sustain time */
int16 rs; /* release speed */
int16 wf; /* waveform */
int16 p_fall; /* ? */
int16 v_amp; /* vibrato amplitude */
int16 v_spd; /* vibrato speed */
int16 fq; /* base frequency */
};
static int is_am_instrument(HIO_HANDLE *nt, int i)
{
char buf[2];
int16 wf;
hio_seek(nt, 144 + i * 120, SEEK_SET);
hio_read(buf, 1, 2, nt);
if (memcmp(buf, "AM", 2))
return 0;
hio_seek(nt, 24, SEEK_CUR);
wf = hio_read16b(nt);
if (hio_error(nt) || wf < 0 || wf > 3)
return 0;
return 1;
}
static int read_am_instrument(struct module_data *m, HIO_HANDLE *nt, int i)
{
struct xmp_module *mod = &m->mod;
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_envelope *vol_env = &xxi->aei;
struct xmp_envelope *freq_env = &xxi->fei;
struct am_instrument am;
char *wave;
int a, b;
int8 am_noise[1024];
hio_seek(nt, 144 + i * 120 + 2 + 4, SEEK_SET);
am.l0 = hio_read16b(nt);
am.a1l = hio_read16b(nt);
am.a1s = hio_read16b(nt);
am.a2l = hio_read16b(nt);
am.a2s = hio_read16b(nt);
am.sl = hio_read16b(nt);
am.ds = hio_read16b(nt);
am.st = hio_read16b(nt);
hio_read16b(nt);
am.rs = hio_read16b(nt);
am.wf = hio_read16b(nt);
am.p_fall = -(int16) hio_read16b(nt);
am.v_amp = hio_read16b(nt);
am.v_spd = hio_read16b(nt);
am.fq = hio_read16b(nt);
if (hio_error(nt)) {
return -1;
}
#if 0
printf
("L0=%d A1L=%d A1S=%d A2L=%d A2S=%d SL=%d DS=%d ST=%d RS=%d WF=%d\n",
am.l0, am.a1l, am.a1s, am.a2l, am.a2s, am.sl, am.ds, am.st, am.rs,
am.wf);
#endif
if (am.wf < 3) {
xxs->len = 32;
xxs->lps = 0;
xxs->lpe = 32;
wave = (char *)&am_waveform[am.wf][0];
} else {
int j;
xxs->len = 1024;
xxs->lps = 0;
xxs->lpe = 1024;
for (j = 0; j < 1024; j++)
am_noise[j] = rand() % 256;
wave = (char *)&am_noise[0];
}
xxs->flg = XMP_SAMPLE_LOOP;
xxi->sub[0].vol = 0x40; /* prelude.mod has 0 in instrument */
xxi->nsm = 1;
xxi->sub[0].xpo = -12 * am.fq;
xxi->sub[0].vwf = 0;
xxi->sub[0].vde = am.v_amp << 2;
xxi->sub[0].vra = am.v_spd;
/*
* AM synth envelope parameters based on the Startrekker 1.2 docs
*
* L0 Start amplitude for the envelope
* A1L Attack level
* A1S The speed that the amplitude changes to the attack level, $1
* is slow and $40 is fast.
* A2L Secondary attack level, for those who likes envelopes...
* A2S Secondary attack speed.
* DS The speed that the amplitude decays down to the:
* SL Sustain level. There is remains for the time set by the
* ST Sustain time.
* RS Release speed. The speed that the amplitude falls from ST to 0.
*/
if (am.a1s == 0)
am.a1s = 1;
if (am.a2s == 0)
am.a2s = 1;
if (am.ds == 0)
am.ds = 1;
if (am.rs == 0)
am.rs = 1;
vol_env->npt = 6;
vol_env->flg = XMP_ENVELOPE_ON;
vol_env->data[0] = 0;
vol_env->data[1] = am.l0 / 4;
/*
* Startrekker increments/decrements the envelope by the stage speed
* until it reaches the next stage level.
*
* ^
* |
* 100 +.........o
* | /:
* A2L +.......o : x = 256 * (A2L - A1L) / (256 - A1L)
* | /: :
* | / : :
* A1L +....o..:.:
* | : : :
* | :x : :
* +----+--+-+----->
* | |
* |256/|
* A2S
*/
if (am.a1l > am.l0) {
a = am.a1l - am.l0;
b = 256 - am.l0;
} else {
a = am.l0 - am.a1l;
b = am.l0;
}
if (b == 0)
b = 1;
vol_env->data[2] = vol_env->data[0] + (256 * a) / (am.a1s * b);
vol_env->data[3] = am.a1l / 4;
if (am.a2l > am.a1l) {
a = am.a2l - am.a1l;
b = 256 - am.a1l;
} else {
a = am.a1l - am.a2l;
b = am.a1l;
}
if (b == 0)
b = 1;
vol_env->data[4] = vol_env->data[2] + (256 * a) / (am.a2s * b);
vol_env->data[5] = am.a2l / 4;
if (am.sl > am.a2l) {
a = am.sl - am.a2l;
b = 256 - am.a2l;
} else {
a = am.a2l - am.sl;
b = am.a2l;
}
if (b == 0)
b = 1;
vol_env->data[6] = vol_env->data[4] + (256 * a) / (am.ds * b);
vol_env->data[7] = am.sl / 4;
vol_env->data[8] = vol_env->data[6] + am.st;
vol_env->data[9] = am.sl / 4;
vol_env->data[10] = vol_env->data[8] + (256 / am.rs);
vol_env->data[11] = 0;
/*
* Implement P.FALL using pitch envelope
*/
if (am.p_fall) {
freq_env->npt = 2;
freq_env->flg = XMP_ENVELOPE_ON;
freq_env->data[0] = 0;
freq_env->data[1] = 0;
freq_env->data[2] = 1024 / abs(am.p_fall);
freq_env->data[3] = 10 * (am.p_fall < 0 ? -256 : 256);
}
if (libxmp_load_sample(m, NULL, SAMPLE_FLAG_NOLOAD, xxs, wave))
return -1;
return 0;
}
static int flt_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct mod_header mh;
uint8 mod_event[4];
const char *tracker;
char filename[1024];
char buf[16];
HIO_HANDLE *nt;
int am_synth;
LOAD_INIT();
/* See if we have the synth parameters file */
am_synth = 0;
snprintf(filename, 1024, "%s%s.NT", m->dirname, m->basename);
if ((nt = hio_open(filename, "rb")) == NULL) {
snprintf(filename, 1024, "%s%s.nt", m->dirname, m->basename);
if ((nt = hio_open(filename, "rb")) == NULL) {
snprintf(filename, 1024, "%s%s.AS", m->dirname,
m->basename);
if ((nt = hio_open(filename, "rb")) == NULL) {
snprintf(filename, 1024, "%s%s.as", m->dirname,
m->basename);
nt = hio_open(filename, "rb");
}
}
}
tracker = "Startrekker";
if (nt) {
if (hio_read(buf, 1, 16, nt) != 16) {
goto err;
}
if (memcmp(buf, "ST1.2 ModuleINFO", 16) == 0) {
am_synth = 1;
tracker = "Startrekker 1.2";
} else if (memcmp(buf, "ST1.3 ModuleINFO", 16) == 0) {
am_synth = 1;
tracker = "Startrekker 1.3";
} else if (memcmp(buf, "AudioSculpture10", 16) == 0) {
am_synth = 1;
tracker = "AudioSculpture 1.0";
}
}
hio_read(mh.name, 20, 1, f);
for (i = 0; i < 31; i++) {
hio_read(mh.ins[i].name, 22, 1, f);
mh.ins[i].size = hio_read16b(f);
mh.ins[i].finetune = hio_read8(f);
mh.ins[i].volume = hio_read8(f);
mh.ins[i].loop_start = hio_read16b(f);
mh.ins[i].loop_size = hio_read16b(f);
}
mh.len = hio_read8(f);
mh.restart = hio_read8(f);
hio_read(mh.order, 128, 1, f);
hio_read(mh.magic, 4, 1, f);
if (mh.magic[3] == '4') {
mod->chn = 4;
} else {
mod->chn = 8;
}
mod->ins = 31;
mod->smp = mod->ins;
mod->len = mh.len;
mod->rst = mh.restart;
memcpy(mod->xxo, mh.order, 128);
for (i = 0; i < 128; i++) {
if (mod->chn > 4)
mod->xxo[i] >>= 1;
if (mod->xxo[i] > mod->pat)
mod->pat = mod->xxo[i];
}
mod->pat++;
mod->trk = mod->chn * mod->pat;
strncpy(mod->name, (char *)mh.name, 20);
libxmp_set_type(m, "%s %4.4s", tracker, mh.magic);
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
goto err;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
goto err;
sub = &xxi->sub[0];
xxs->len = 2 * mh.ins[i].size;
xxs->lps = 2 * mh.ins[i].loop_start;
xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size;
xxs->flg = mh.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0;
sub->fin = (int8) (mh.ins[i].finetune << 4);
sub->vol = mh.ins[i].volume;
sub->pan = 0x80;
sub->sid = i;
xxi->rls = 0xfff;
if (xxs->len > 0)
xxi->nsm = 1;
libxmp_instrument_name(mod, i, mh.ins[i].name, 22);
}
if (libxmp_init_pattern(mod) < 0)
goto err;
/* Load and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
/* "The format you are looking for is FLT8, and the ONLY two
* differences are: It says FLT8 instead of FLT4 or M.K., AND, the
* patterns are PAIRED. I thought this was the easiest 8 track
* format possible, since it can be loaded in a normal 4 channel
* tracker if you should want to rip sounds or patterns. So, in a
* 8 track FLT8 module, patterns 00 and 01 is "really" pattern 00.
* Patterns 02 and 03 together is "really" pattern 01. Thats it.
* Oh well, I didnt have the time to implement all effect commands
* either, so some FLT8 modules would play back badly (I think
* especially the portamento command uses a different "scale" than
* the normal portamento command, that would be hard to patch).
*/
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
goto err;
for (j = 0; j < (64 * 4); j++) {
event = &EVENT(i, j % 4, j / 4);
if (hio_read(mod_event, 1, 4, f) < 4) {
D_(D_CRIT "read error at pat %d", i);
goto err;
}
libxmp_decode_noisetracker_event(event, mod_event);
}
if (mod->chn > 4) {
for (j = 0; j < (64 * 4); j++) {
event = &EVENT(i, (j % 4) + 4, j / 4);
if (hio_read(mod_event, 1, 4, f) < 4) {
D_(D_CRIT "read error at pat %d", i);
goto err;
}
libxmp_decode_noisetracker_event(event, mod_event);
/* no macros */
if (event->fxt == 0x0e)
event->fxt = event->fxp = 0;
}
}
}
/* no such limit for synth instruments
* mod->flg |= XXM_FLG_MODRNG;
*/
/* Load samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
if (mod->xxs[i].len == 0) {
if (am_synth && is_am_instrument(nt, i)) {
if (read_am_instrument(m, nt, i) < 0) {
D_(D_CRIT "Missing nt file");
goto err;
}
}
continue;
}
if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP, &mod->xxs[i], NULL) <
0) {
goto err;
}
}
if (nt) {
hio_close(nt);
}
return 0;
err:
if (nt) {
hio_close(nt);
}
return -1;
}

View File

@ -0,0 +1,342 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#define MAGIC_Funk MAGIC4('F','u','n','k')
static int fnk_test (HIO_HANDLE *, char *, const int);
static int fnk_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_fnk = {
"Funktracker",
fnk_test,
fnk_load
};
static int fnk_test(HIO_HANDLE *f, char *t, const int start)
{
uint8 a, b;
int size;
if (hio_read32b(f) != MAGIC_Funk)
return -1;
hio_read8(f);
a = hio_read8(f);
b = hio_read8(f);
hio_read8(f);
if ((a >> 1) < 10) /* creation year (-1980) */
return -1;
if (MSN(b) > 7 || LSN(b) > 9) /* CPU and card */
return -1;
size = hio_read32l(f);
if (size < 1024)
return -1;
if (hio_size(f) != size)
return -1;
libxmp_read_title(f, t, 0);
return 0;
}
struct fnk_instrument {
uint8 name[19]; /* ASCIIZ instrument name */
uint32 loop_start; /* Instrument loop start */
uint32 length; /* Instrument length */
uint8 volume; /* Volume (0-255) */
uint8 pan; /* Pan (0-255) */
uint8 shifter; /* Portamento and offset shift */
uint8 waveform; /* Vibrato and tremolo waveforms */
uint8 retrig; /* Retrig and arpeggio speed */
};
struct fnk_header {
uint8 marker[4]; /* 'Funk' */
uint8 info[4]; /* */
uint32 filesize; /* File size */
uint8 fmt[4]; /* F2xx, Fkxx or Fvxx */
uint8 loop; /* Loop order number */
uint8 order[256]; /* Order list */
uint8 pbrk[128]; /* Break list for patterns */
struct fnk_instrument fih[64]; /* Instruments */
};
static void fnk_translate_event(struct xmp_event *event, const uint8 ev[3],
const struct fnk_header *ffh)
{
switch (ev[0] >> 2) {
case 0x3f:
case 0x3e:
case 0x3d:
break;
default:
event->note = 37 + (ev[0] >> 2);
event->ins = 1 + MSN(ev[1]) + ((ev[0] & 0x03) << 4);
event->vol = ffh->fih[event->ins - 1].volume;
}
switch (LSN(ev[1])) {
case 0x00:
event->fxt = FX_PER_PORTA_UP;
event->fxp = ev[2];
break;
case 0x01:
event->fxt = FX_PER_PORTA_DN;
event->fxp = ev[2];
break;
case 0x02:
event->fxt = FX_PER_TPORTA;
event->fxp = ev[2];
break;
case 0x03:
event->fxt = FX_PER_VIBRATO;
event->fxp = ev[2];
break;
case 0x06:
event->fxt = FX_PER_VSLD_UP;
event->fxp = ev[2] << 1;
break;
case 0x07:
event->fxt = FX_PER_VSLD_DN;
event->fxp = ev[2] << 1;
break;
case 0x0b:
event->fxt = FX_ARPEGGIO;
event->fxp = ev[2];
break;
case 0x0d:
event->fxt = FX_VOLSET;
event->fxp = ev[2];
break;
case 0x0e:
if (ev[2] == 0x0a || ev[2] == 0x0b || ev[2] == 0x0c) {
event->fxt = FX_PER_CANCEL;
break;
}
switch (MSN(ev[2])) {
case 0x1:
event->fxt = FX_EXTENDED;
event->fxp = (EX_CUT << 4) | LSN(ev[2]);
break;
case 0x2:
event->fxt = FX_EXTENDED;
event->fxp = (EX_DELAY << 4) | LSN(ev[2]);
break;
case 0xd:
event->fxt = FX_EXTENDED;
event->fxp = (EX_RETRIG << 4) | LSN(ev[2]);
break;
case 0xe:
event->fxt = FX_SETPAN;
event->fxp = 8 + (LSN(ev[2]) << 4);
break;
case 0xf:
event->fxt = FX_SPEED;
event->fxp = LSN(ev[2]);
break;
}
}
}
static int fnk_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j, k;
/* int day, month, year; */
struct xmp_event *event;
struct fnk_header ffh;
uint8 ev[3];
LOAD_INIT();
hio_read(ffh.marker, 4, 1, f);
hio_read(ffh.info, 4, 1, f);
ffh.filesize = hio_read32l(f);
hio_read(ffh.fmt, 4, 1, f);
ffh.loop = hio_read8(f);
hio_read(ffh.order, 256, 1, f);
hio_read(ffh.pbrk, 128, 1, f);
for (i = 0; i < 128; i++) {
if (ffh.pbrk[i] >= 64) {
return -1;
}
}
for (i = 0; i < 64; i++) {
hio_read(ffh.fih[i].name, 19, 1, f);
ffh.fih[i].loop_start = hio_read32l(f);
ffh.fih[i].length = hio_read32l(f);
ffh.fih[i].volume = hio_read8(f);
ffh.fih[i].pan = hio_read8(f);
ffh.fih[i].shifter = hio_read8(f);
ffh.fih[i].waveform = hio_read8(f);
ffh.fih[i].retrig = hio_read8(f);
/* Sanity check */
if (ffh.fih[i].length >= ffh.filesize) {
return -1;
}
}
/* day = ffh.info[0] & 0x1f;
month = ((ffh.info[1] & 0x01) << 3) | ((ffh.info[0] & 0xe0) >> 5);
year = 1980 + ((ffh.info[1] & 0xfe) >> 1); */
mod->smp = mod->ins = 64;
for (i = 0; i < 256 && ffh.order[i] != 0xff; i++) {
if (ffh.order[i] > mod->pat)
mod->pat = ffh.order[i];
}
mod->pat++;
/* Sanity check */
if (mod->pat > 128) {
return -1;
}
mod->len = i;
memcpy (mod->xxo, ffh.order, mod->len);
mod->spd = 4;
mod->bpm = 125;
mod->chn = 0;
/*
* If an R1 fmt (funktype = Fk** or Fv**), then ignore byte 3. It's
* unreliable. It used to store the (GUS) sample memory requirement.
*/
if (ffh.fmt[0] == 'F' && ffh.fmt[1] == '2') {
if (((int8)ffh.info[3] >> 1) & 0x40)
mod->bpm -= (ffh.info[3] >> 1) & 0x3f;
else
mod->bpm += (ffh.info[3] >> 1) & 0x3f;
libxmp_set_type(m, "FunktrackerGOLD");
} else if (ffh.fmt[0] == 'F' && (ffh.fmt[1] == 'v' || ffh.fmt[1] == 'k')) {
libxmp_set_type(m, "Funktracker");
} else {
mod->chn = 8;
libxmp_set_type(m, "Funktracker DOS32");
}
if (mod->chn == 0) {
mod->chn = (ffh.fmt[2] < '0') || (ffh.fmt[2] > '9') ||
(ffh.fmt[3] < '0') || (ffh.fmt[3] > '9') ? 8 :
(ffh.fmt[2] - '0') * 10 + ffh.fmt[3] - '0';
/* Sanity check */
if (mod->chn <= 0 || mod->chn > XMP_MAX_CHANNELS)
return -1;
}
mod->bpm = 4 * mod->bpm / 5;
mod->trk = mod->chn * mod->pat;
/* FNK allows mode per instrument but we don't, so use linear for all */
m->period_type = PERIOD_LINEAR;
MODULE_INFO();
/* D_(D_INFO "Creation date: %02d/%02d/%04d", day, month, year); */
if (libxmp_init_instrument(m) < 0)
return -1;
/* Convert instruments */
for (i = 0; i < mod->ins; i++) {
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
mod->xxs[i].len = ffh.fih[i].length;
mod->xxs[i].lps = ffh.fih[i].loop_start;
if (mod->xxs[i].lps == -1)
mod->xxs[i].lps = 0;
mod->xxs[i].lpe = ffh.fih[i].length;
mod->xxs[i].flg = ffh.fih[i].loop_start != -1 ? XMP_SAMPLE_LOOP : 0;
mod->xxi[i].sub[0].vol = ffh.fih[i].volume;
mod->xxi[i].sub[0].pan = ffh.fih[i].pan;
mod->xxi[i].sub[0].sid = i;
if (mod->xxs[i].len > 0)
mod->xxi[i].nsm = 1;
libxmp_instrument_name(mod, i, ffh.fih[i].name, 19);
D_(D_INFO "[%2X] %-20.20s %04x %04x %04x %c V%02x P%02x", i,
mod->xxi[i].name,
mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].pan);
}
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
EVENT(i, 0, ffh.pbrk[i]).f2t = FX_BREAK;
for (j = 0; j < 64; j++) {
for(k = 0; k < mod->chn; k++) {
event = &EVENT(i, k, j);
if (hio_read(ev, 1, 3, f) < 3)
return -1;
fnk_translate_event(event, ev, &ffh);
}
}
}
/* Read samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (mod->xxs[i].len <= 2)
continue;
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
}
for (i = 0; i < mod->chn; i++)
mod->xxc[i].pan = 0x80;
m->volbase = 0xff;
m->quirk = QUIRK_VSALL;
return 0;
}

View File

@ -0,0 +1,481 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "iff.h"
#include "../period.h"
/* Galaxy Music System 4.0 module file loader
*
* Based on modules converted using mod2j2b.exe
*/
static int gal4_test(HIO_HANDLE *, char *, const int);
static int gal4_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_gal4 = {
"Galaxy Music System 4.0",
gal4_test,
gal4_load
};
static int gal4_test(HIO_HANDLE *f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC4('R', 'I', 'F', 'F'))
return -1;
hio_read32b(f);
if (hio_read32b(f) != MAGIC4('A', 'M', 'F', 'F'))
return -1;
if (hio_read32b(f) != MAGIC4('M', 'A', 'I', 'N'))
return -1;
hio_read32b(f); /* skip size */
libxmp_read_title(f, t, 64);
return 0;
}
struct local_data {
int snum;
};
static int get_main(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
char buf[64];
int flags;
if (hio_read(buf, 1, 64, f) < 64)
return -1;
strncpy(mod->name, buf, 63); /* ensure string terminator */
mod->name[63] = '\0';
libxmp_set_type(m, "Galaxy Music System 4.0");
flags = hio_read8(f);
if (~flags & 0x01)
m->period_type = PERIOD_LINEAR;
mod->chn = hio_read8(f);
mod->spd = hio_read8(f);
mod->bpm = hio_read8(f);
hio_read16l(f); /* unknown - 0x01c5 */
hio_read16l(f); /* unknown - 0xff00 */
hio_read8(f); /* unknown - 0x80 */
/* Sanity check */
if (mod->chn > 32) {
return -1;
}
return 0;
}
static int get_ordr(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i;
mod->len = hio_read8(f) + 1;
if (hio_error(f)) {
return -1;
}
for (i = 0; i < mod->len; i++) {
mod->xxo[i] = hio_read8(f);
}
return 0;
}
static int get_patt_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i;
i = hio_read8(f) + 1; /* pattern number */
if (i > mod->pat)
mod->pat = i;
return 0;
}
static int get_inst_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i;
hio_read8(f); /* 00 */
i = hio_read8(f) + 1; /* instrument number */
/* Sanity check */
if (i > MAX_INSTRUMENTS)
return -1;
if (i > mod->ins)
mod->ins = i;
hio_seek(f, 28, SEEK_CUR); /* skip name */
mod->smp += hio_read8(f);
return 0;
}
static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event, dummy;
int i, len, chan;
int rows, r;
uint8 flag;
i = hio_read8(f); /* pattern number */
len = hio_read32l(f);
/* Sanity check */
if (i >= mod->pat || len <= 0 || mod->xxp[i]) {
return -1;
}
rows = hio_read8(f) + 1;
if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0)
return -1;
for (r = 0; r < rows; ) {
if ((flag = hio_read8(f)) == 0) {
r++;
continue;
}
if (hio_error(f)) {
return -1;
}
chan = flag & 0x1f;
event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy;
if (flag & 0x80) {
uint8 fxp = hio_read8(f);
uint8 fxt = hio_read8(f);
switch (fxt) {
case 0x14: /* speed */
fxt = FX_S3M_SPEED;
break;
default:
if (fxt > 0x0f) {
D_(D_CRIT "p%d r%d c%d unknown effect %02x %02x", i, r, chan, fxt, fxp);
fxt = fxp = 0;
}
}
event->fxt = fxt;
event->fxp = fxp;
}
if (flag & 0x40) {
event->ins = hio_read8(f);
event->note = hio_read8(f);
if (event->note == 128) {
event->note = XMP_KEY_OFF;
}
}
if (flag & 0x20) {
event->vol = 1 + hio_read8(f) / 2;
}
}
return 9;
}
static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
int i, j;
int srate, finetune, flags;
int val, vwf, vra, vde, vsw /*, fade*/;
uint8 buf[30];
hio_read8(f); /* 00 */
i = hio_read8(f); /* instrument number */
/* Sanity check */
if (i >= mod->ins || mod->xxi[i].nsm) {
return -1;
}
hio_read(mod->xxi[i].name, 1, 28, f);
mod->xxi[i].nsm = hio_read8(f);
for (j = 0; j < 108; j++) {
mod->xxi[i].map[j].ins = hio_read8(f);
}
hio_seek(f, 11, SEEK_CUR); /* unknown */
vwf = hio_read8(f); /* vibrato waveform */
vsw = hio_read8(f); /* vibrato sweep */
hio_read8(f); /* unknown */
hio_read8(f); /* unknown */
vde = hio_read8(f); /* vibrato depth */
vra = hio_read16l(f) / 16; /* vibrato speed */
hio_read8(f); /* unknown */
val = hio_read8(f); /* PV envelopes flags */
if (LSN(val) & 0x01)
mod->xxi[i].aei.flg |= XMP_ENVELOPE_ON;
if (LSN(val) & 0x02)
mod->xxi[i].aei.flg |= XMP_ENVELOPE_SUS;
if (LSN(val) & 0x04)
mod->xxi[i].aei.flg |= XMP_ENVELOPE_LOOP;
if (MSN(val) & 0x01)
mod->xxi[i].pei.flg |= XMP_ENVELOPE_ON;
if (MSN(val) & 0x02)
mod->xxi[i].pei.flg |= XMP_ENVELOPE_SUS;
if (MSN(val) & 0x04)
mod->xxi[i].pei.flg |= XMP_ENVELOPE_LOOP;
val = hio_read8(f); /* PV envelopes points */
mod->xxi[i].aei.npt = LSN(val) + 1;
mod->xxi[i].pei.npt = MSN(val) + 1;
val = hio_read8(f); /* PV envelopes sustain point */
mod->xxi[i].aei.sus = LSN(val);
mod->xxi[i].pei.sus = MSN(val);
val = hio_read8(f); /* PV envelopes loop start */
mod->xxi[i].aei.lps = LSN(val);
mod->xxi[i].pei.lps = MSN(val);
hio_read8(f); /* PV envelopes loop end */
mod->xxi[i].aei.lpe = LSN(val);
mod->xxi[i].pei.lpe = MSN(val);
if (mod->xxi[i].aei.npt <= 0 || mod->xxi[i].aei.npt > MIN(10, XMP_MAX_ENV_POINTS))
mod->xxi[i].aei.flg &= ~XMP_ENVELOPE_ON;
if (mod->xxi[i].pei.npt <= 0 || mod->xxi[i].pei.npt > MIN(10, XMP_MAX_ENV_POINTS))
mod->xxi[i].pei.flg &= ~XMP_ENVELOPE_ON;
if (hio_read(buf, 1, 30, f) < 30) { /* volume envelope points */
D_(D_CRIT "read error at vol env %d", i);
return -1;
}
for (j = 0; j < mod->xxi[i].aei.npt; j++) {
if (j >= 10) {
break;
}
mod->xxi[i].aei.data[j * 2] = readmem16l(buf + j * 3) / 16;
mod->xxi[i].aei.data[j * 2 + 1] = buf[j * 3 + 2];
}
if (hio_read(buf, 1, 30, f) < 30) { /* pan envelope points */
D_(D_CRIT "read error at pan env %d", i);
return -1;
}
for (j = 0; j < mod->xxi[i].pei.npt; j++) {
if (j >= 10) {
break;
}
mod->xxi[i].pei.data[j * 2] = readmem16l(buf + j * 3) / 16;
mod->xxi[i].pei.data[j * 2 + 1] = buf[j * 3 + 2];
}
/*fade =*/ hio_read8(f); /* fadeout - 0x80->0x02 0x310->0x0c */
hio_read8(f); /* unknown */
D_(D_INFO "[%2X] %-28.28s %2d ", i, mod->xxi[i].name, mod->xxi[i].nsm);
if (mod->xxi[i].nsm == 0)
return 0;
if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0)
return -1;
for (j = 0; j < mod->xxi[i].nsm; j++, data->snum++) {
hio_read32b(f); /* SAMP */
hio_read32b(f); /* size */
hio_read(mod->xxs[data->snum].name, 1, 28, f);
mod->xxi[i].sub[j].pan = hio_read8(f) * 4;
if (mod->xxi[i].sub[j].pan == 0) /* not sure about this */
mod->xxi[i].sub[j].pan = 0x80;
mod->xxi[i].sub[j].vol = hio_read8(f);
flags = hio_read8(f);
hio_read8(f); /* unknown - 0x80 */
mod->xxi[i].sub[j].vwf = vwf;
mod->xxi[i].sub[j].vde = vde;
mod->xxi[i].sub[j].vra = vra;
mod->xxi[i].sub[j].vsw = vsw;
mod->xxi[i].sub[j].sid = data->snum;
mod->xxs[data->snum].len = hio_read32l(f);
mod->xxs[data->snum].lps = hio_read32l(f);
mod->xxs[data->snum].lpe = hio_read32l(f);
mod->xxs[data->snum].flg = 0;
if (flags & 0x04)
mod->xxs[data->snum].flg |= XMP_SAMPLE_16BIT;
if (flags & 0x08)
mod->xxs[data->snum].flg |= XMP_SAMPLE_LOOP;
if (flags & 0x10)
mod->xxs[data->snum].flg |= XMP_SAMPLE_LOOP_BIDIR;
/* if (flags & 0x80)
mod->xxs[data->snum].flg |= ? */
srate = hio_read32l(f);
finetune = 0;
libxmp_c2spd_to_note(srate, &mod->xxi[i].sub[j].xpo, &mod->xxi[i].sub[j].fin);
mod->xxi[i].sub[j].fin += finetune;
hio_read32l(f); /* 0x00000000 */
hio_read32l(f); /* unknown */
D_(D_INFO " %X: %05x%c%05x %05x %c V%02x P%02x %5d",
j, mod->xxs[data->snum].len,
mod->xxs[data->snum].flg & XMP_SAMPLE_16BIT ? '+' : ' ',
mod->xxs[data->snum].lps,
mod->xxs[data->snum].lpe,
mod->xxs[data->snum].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' :
mod->xxs[data->snum].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[j].vol,
mod->xxi[i].sub[j].pan,
srate);
if (mod->xxs[data->snum].len > 1) {
int snum = data->snum;
if (libxmp_load_sample(m, f, 0, &mod->xxs[snum], NULL) < 0)
return -1;
}
}
return 0;
}
static int gal4_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
iff_handle handle;
int i, ret, offset;
struct local_data data;
LOAD_INIT();
hio_read32b(f); /* Skip RIFF */
hio_read32b(f); /* Skip size */
hio_read32b(f); /* Skip AM */
offset = hio_tell(f);
mod->smp = mod->ins = 0;
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
m->c4rate = C4_NTSC_RATE;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "MAIN", get_main);
ret |= libxmp_iff_register(handle, "ORDR", get_ordr);
ret |= libxmp_iff_register(handle, "PATT", get_patt_cnt);
ret |= libxmp_iff_register(handle, "INST", get_inst_cnt);
if (ret != 0)
return -1;
libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN);
libxmp_iff_set_quirk(handle, IFF_CHUNK_TRUNC4);
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
libxmp_iff_release(handle);
mod->trk = mod->pat * mod->chn;
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
if (libxmp_init_pattern(mod) < 0)
return -1;
D_(D_INFO "Stored patterns: %d\n", mod->pat);
D_(D_INFO "Stored samples : %d ", mod->smp);
hio_seek(f, start + offset, SEEK_SET);
data.snum = 0;
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "PATT", get_patt);
ret |= libxmp_iff_register(handle, "INST", get_inst);
if (ret != 0)
return -1;
libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN);
libxmp_iff_set_quirk(handle, IFF_CHUNK_TRUNC4);
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
libxmp_iff_release(handle);
/* Alloc missing patterns */
for (i = 0; i < mod->pat; i++) {
if (mod->xxp[i] == NULL) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) {
return -1;
}
}
}
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = 0x80;
}
m->quirk |= QUIRKS_FT2;
m->read_event_type = READ_EVENT_FT2;
return 0;
}

View File

@ -0,0 +1,404 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "iff.h"
#include "../period.h"
/* Galaxy Music System 5.0 module file loader
*
* Based on the format description by Dr.Eggman
* (http://www.jazz2online.com/J2Ov2/articles/view.php?articleID=288)
* and Jazz Jackrabbit modules by Alexander Brandon from Lori Central
* (http://www.loricentral.com/jj2music.html)
*/
static int gal5_test(HIO_HANDLE *, char *, const int);
static int gal5_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_gal5 = {
"Galaxy Music System 5.0 (J2B)",
gal5_test,
gal5_load
};
struct local_data {
uint8 chn_pan[64];
};
static int gal5_test(HIO_HANDLE *f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC4('R', 'I', 'F', 'F'))
return -1;
hio_read32b(f);
if (hio_read32b(f) != MAGIC4('A', 'M', ' ', ' '))
return -1;
if (hio_read32b(f) != MAGIC4('I', 'N', 'I', 'T'))
return -1;
hio_read32b(f); /* skip size */
libxmp_read_title(f, t, 64);
return 0;
}
static int get_init(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct local_data *data = (struct local_data *)parm;
char buf[64];
int flags;
if (hio_read(buf, 1, 64, f) < 64)
return -1;
strncpy(mod->name, buf, 63); /* ensure string terminator */
mod->name[63] = '\0';
libxmp_set_type(m, "Galaxy Music System 5.0");
flags = hio_read8(f); /* bit 0: Amiga period */
if (~flags & 0x01)
m->period_type = PERIOD_LINEAR;
mod->chn = hio_read8(f);
mod->spd = hio_read8(f);
mod->bpm = hio_read8(f);
hio_read16l(f); /* unknown - 0x01c5 */
hio_read16l(f); /* unknown - 0xff00 */
hio_read8(f); /* unknown - 0x80 */
if (hio_read(data->chn_pan, 1, 64, f) != 64) {
D_(D_CRIT "error reading INIT");
return -1;
}
/* Sanity check */
if (mod->chn > XMP_MAX_CHANNELS)
return -1;
return 0;
}
static int get_ordr(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i;
mod->len = hio_read8(f) + 1;
/* Don't follow Dr.Eggman's specs here */
for (i = 0; i < mod->len; i++)
mod->xxo[i] = hio_read8(f);
return 0;
}
static int get_patt_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i;
i = hio_read8(f) + 1; /* pattern number */
if (i > mod->pat)
mod->pat = i;
return 0;
}
static int get_inst_cnt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i;
hio_read32b(f); /* 42 01 00 00 */
hio_read8(f); /* 00 */
i = hio_read8(f) + 1; /* instrument number */
/* Sanity check */
if (i > MAX_INSTRUMENTS)
return -1;
if (i > mod->ins)
mod->ins = i;
return 0;
}
static int get_patt(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event, dummy;
int i, len, chan;
int rows, r;
uint8 flag;
i = hio_read8(f); /* pattern number */
len = hio_read32l(f);
rows = hio_read8(f) + 1;
/* Sanity check - don't allow duplicate patterns. */
if (len < 0 || mod->xxp[i] != NULL)
return -1;
if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0)
return -1;
for (r = 0; r < rows; ) {
if ((flag = hio_read8(f)) == 0) {
r++;
continue;
}
if (hio_error(f)) {
return -1;
}
chan = flag & 0x1f;
event = chan < mod->chn ? &EVENT(i, chan, r) : &dummy;
if (flag & 0x80) {
uint8 fxp = hio_read8(f);
uint8 fxt = hio_read8(f);
switch (fxt) {
case 0x14: /* speed */
fxt = FX_S3M_SPEED;
break;
default:
if (fxt > 0x0f) {
D_(D_CRIT "p%d r%d c%d unknown effect %02x %02x", i, r, chan, fxt, fxp);
fxt = fxp = 0;
}
}
event->fxt = fxt;
event->fxp = fxp;
}
if (flag & 0x40) {
event->ins = hio_read8(f);
event->note = hio_read8(f);
if (event->note == 128) {
event->note = XMP_KEY_OFF;
}
}
if (flag & 0x20) {
event->vol = 1 + hio_read8(f) / 2;
}
}
return 0;
}
static int get_inst(struct module_data *m, int size, HIO_HANDLE *f, void *parm)
{
struct xmp_module *mod = &m->mod;
int i, srate, finetune, flags;
int has_unsigned_sample;
hio_read32b(f); /* 42 01 00 00 */
hio_read8(f); /* 00 */
i = hio_read8(f); /* instrument number */
/* Sanity check - don't allow duplicate instruments. */
if (mod->xxi[i].nsm != 0)
return -1;
hio_read(mod->xxi[i].name, 1, 28, f);
hio_seek(f, 290, SEEK_CUR); /* Sample/note map, envelopes */
mod->xxi[i].nsm = hio_read16l(f);
D_(D_INFO "[%2X] %-28.28s %2d ", i, mod->xxi[i].name, mod->xxi[i].nsm);
if (mod->xxi[i].nsm == 0)
return 0;
if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0)
return -1;
/* FIXME: Currently reading only the first sample */
hio_read32b(f); /* RIFF */
hio_read32b(f); /* size */
hio_read32b(f); /* AS */
hio_read32b(f); /* SAMP */
hio_read32b(f); /* size */
hio_read32b(f); /* unknown - usually 0x40000000 */
hio_read(mod->xxs[i].name, 1, 28, f);
hio_read32b(f); /* unknown - 0x0000 */
hio_read8(f); /* unknown - 0x00 */
mod->xxi[i].sub[0].sid = i;
mod->xxi[i].vol = hio_read8(f);
mod->xxi[i].sub[0].pan = 0x80;
mod->xxi[i].sub[0].vol = (hio_read16l(f) + 1) / 512;
flags = hio_read16l(f);
hio_read16l(f); /* unknown - 0x0080 */
mod->xxs[i].len = hio_read32l(f);
mod->xxs[i].lps = hio_read32l(f);
mod->xxs[i].lpe = hio_read32l(f);
mod->xxs[i].flg = 0;
has_unsigned_sample = 0;
if (flags & 0x04)
mod->xxs[i].flg |= XMP_SAMPLE_16BIT;
if (flags & 0x08)
mod->xxs[i].flg |= XMP_SAMPLE_LOOP;
if (flags & 0x10)
mod->xxs[i].flg |= XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR;
if (~flags & 0x80)
has_unsigned_sample = 1;
srate = hio_read32l(f);
finetune = 0;
libxmp_c2spd_to_note(srate, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin);
mod->xxi[i].sub[0].fin += finetune;
hio_read32l(f); /* 0x00000000 */
hio_read32l(f); /* unknown */
D_(D_INFO " %x: %05x%c%05x %05x %c V%02x %04x %5d",
0, mod->xxs[i].len,
mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ',
mod->xxs[i].lps,
mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' :
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol, flags, srate);
if (mod->xxs[i].len > 1) {
if (libxmp_load_sample(m, f, has_unsigned_sample ?
SAMPLE_FLAG_UNS : 0, &mod->xxs[i], NULL) < 0)
return -1;
}
return 0;
}
static int gal5_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
iff_handle handle;
int i, ret, offset;
struct local_data data;
LOAD_INIT();
hio_read32b(f); /* Skip RIFF */
hio_read32b(f); /* Skip size */
hio_read32b(f); /* Skip AM */
offset = hio_tell(f);
mod->smp = mod->ins = 0;
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
m->c4rate = C4_NTSC_RATE;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "INIT", get_init); /* Galaxy 5.0 */
ret |= libxmp_iff_register(handle, "ORDR", get_ordr);
ret |= libxmp_iff_register(handle, "PATT", get_patt_cnt);
ret |= libxmp_iff_register(handle, "INST", get_inst_cnt);
if (ret != 0)
return -1;
libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN);
libxmp_iff_set_quirk(handle, IFF_SKIP_EMBEDDED);
libxmp_iff_set_quirk(handle, IFF_CHUNK_ALIGN2);
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
libxmp_iff_release(handle);
mod->trk = mod->pat * mod->chn;
mod->smp = mod->ins;
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
if (libxmp_init_pattern(mod) < 0)
return -1;
D_(D_INFO "Stored patterns: %d", mod->pat);
D_(D_INFO "Stored samples: %d ", mod->smp);
hio_seek(f, start + offset, SEEK_SET);
handle = libxmp_iff_new();
if (handle == NULL)
return -1;
/* IFF chunk IDs */
ret = libxmp_iff_register(handle, "PATT", get_patt);
ret |= libxmp_iff_register(handle, "INST", get_inst);
if (ret != 0)
return -1;
libxmp_iff_set_quirk(handle, IFF_LITTLE_ENDIAN);
libxmp_iff_set_quirk(handle, IFF_SKIP_EMBEDDED);
libxmp_iff_set_quirk(handle, IFF_CHUNK_ALIGN2);
/* Load IFF chunks */
if (libxmp_iff_load(handle, m, f, &data) < 0) {
libxmp_iff_release(handle);
return -1;
}
libxmp_iff_release(handle);
/* Alloc missing patterns */
for (i = 0; i < mod->pat; i++) {
if (mod->xxp[i] == NULL) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0) {
return -1;
}
}
}
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = data.chn_pan[i] * 2;
}
m->quirk |= QUIRKS_FT2;
m->read_event_type = READ_EVENT_FT2;
return 0;
}

View File

@ -0,0 +1,449 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Based on the GDM (General Digital Music) version 1.0 File Format
* Specification - Revision 2 by MenTaLguY
*/
#include "loader.h"
#include "../period.h"
#define MAGIC_GDM MAGIC4('G','D','M',0xfe)
#define MAGIC_GMFS MAGIC4('G','M','F','S')
static int gdm_test(HIO_HANDLE *, char *, const int);
static int gdm_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_gdm = {
"General Digital Music",
gdm_test,
gdm_load
};
static int gdm_test(HIO_HANDLE *f, char *t, const int start)
{
if (hio_read32b(f) != MAGIC_GDM)
return -1;
hio_seek(f, start + 0x47, SEEK_SET);
if (hio_read32b(f) != MAGIC_GMFS)
return -1;
hio_seek(f, start + 4, SEEK_SET);
libxmp_read_title(f, t, 32);
return 0;
}
void fix_effect(uint8 *fxt, uint8 *fxp)
{
int h, l;
switch (*fxt) {
case 0x00: /* no effect */
*fxp = 0;
break;
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07: /* same as protracker */
break;
case 0x08:
*fxt = FX_TREMOR;
break;
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d: /* same as protracker */
break;
case 0x0e:
/* Convert some extended effects to their S3M equivalents. This is
* necessary because the continue effects were left as the original
* effect (e.g. FX_VOLSLIDE for the fine volume slides) by 2GDM!
* Otherwise, these should be the same as protracker.
*/
h = MSN(*fxp);
l = LSN(*fxp);
switch(h) {
case EX_F_PORTA_UP:
*fxt = FX_PORTA_UP;
*fxp = l | 0xF0;
break;
case EX_F_PORTA_DN:
*fxt = FX_PORTA_DN;
*fxp = l | 0xF0;
break;
case 0x8: /* extra fine portamento up */
*fxt = FX_PORTA_UP;
*fxp = l | 0xE0;
break;
case 0x9: /* extra fine portamento down */
*fxt = FX_PORTA_DN;
*fxp = l | 0xE0;
break;
case EX_F_VSLIDE_UP:
/* Don't convert 0 as it would turn into volume slide down... */
if (l) {
*fxt = FX_VOLSLIDE;
*fxp = (l << 4) | 0xF;
}
break;
case EX_F_VSLIDE_DN:
/* Don't convert 0 as it would turn into volume slide up... */
if (l) {
*fxt = FX_VOLSLIDE;
*fxp = l | 0xF0;
}
break;
}
break;
case 0x0f: /* set speed */
*fxt = FX_S3M_SPEED;
break;
case 0x10: /* arpeggio */
*fxt = FX_S3M_ARPEGGIO;
break;
case 0x11: /* set internal flag */
*fxt = *fxp = 0;
break;
case 0x12:
*fxt = FX_MULTI_RETRIG;
break;
case 0x13:
*fxt = FX_GLOBALVOL;
break;
case 0x14:
*fxt = FX_FINE_VIBRATO;
break;
case 0x1e: /* special misc */
switch (MSN(*fxp)) {
case 0x0: /* sample control */
if (LSN(*fxp) == 1) { /* enable surround */
/* This is the only sample control effect
* that 2GDM emits. BWSB ignores it,
* but supporting it is harmless. */
*fxt = FX_SURROUND;
*fxp = 1;
} else {
*fxt = *fxp = 0;
}
break;
case 0x8: /* set pan position */
*fxt = FX_EXTENDED;
break;
default:
*fxt = *fxp = 0;
break;
}
break;
case 0x1f:
*fxt = FX_S3M_BPM;
break;
default:
*fxt = *fxp = 0;
}
}
static int gdm_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
int vermaj, vermin, tvmaj, tvmin, tracker;
int /*origfmt,*/ ord_ofs, pat_ofs, ins_ofs, smp_ofs;
uint8 buffer[32], panmap[32];
int i;
LOAD_INIT();
hio_read32b(f); /* skip magic */
hio_read(mod->name, 1, 32, f);
hio_seek(f, 32, SEEK_CUR); /* skip author */
hio_seek(f, 7, SEEK_CUR);
vermaj = hio_read8(f);
vermin = hio_read8(f);
tracker = hio_read16l(f);
tvmaj = hio_read8(f);
tvmin = hio_read8(f);
if (tracker == 0) {
libxmp_set_type(m, "GDM %d.%02d (2GDM %d.%02d)",
vermaj, vermin, tvmaj, tvmin);
} else {
libxmp_set_type(m, "GDM %d.%02d (unknown tracker %d.%02d)",
vermaj, vermin, tvmaj, tvmin);
}
if (hio_read(panmap, 32, 1, f) == 0) {
D_(D_CRIT "error reading header");
return -1;
}
for (i = 0; i < 32; i++) {
if (panmap[i] == 255) {
panmap[i] = 8;
mod->xxc[i].vol = 0;
mod->xxc[i].flg |= XMP_CHANNEL_MUTE;
} else if (panmap[i] == 16) {
panmap[i] = 8;
}
mod->xxc[i].pan = 0x80 + (panmap[i] - 8) * 16;
}
mod->gvl = hio_read8(f);
mod->spd = hio_read8(f);
mod->bpm = hio_read8(f);
/*origfmt =*/ hio_read16l(f);
ord_ofs = hio_read32l(f);
mod->len = hio_read8(f) + 1;
pat_ofs = hio_read32l(f);
mod->pat = hio_read8(f) + 1;
ins_ofs = hio_read32l(f);
smp_ofs = hio_read32l(f);
mod->ins = mod->smp = hio_read8(f) + 1;
/* Sanity check */
if (mod->ins > MAX_INSTRUMENTS)
return -1;
m->c4rate = C4_NTSC_RATE;
MODULE_INFO();
hio_seek(f, start + ord_ofs, SEEK_SET);
for (i = 0; i < mod->len; i++)
mod->xxo[i] = hio_read8(f);
/* Read instrument data */
hio_seek(f, start + ins_ofs, SEEK_SET);
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
int flg, c4spd, vol, pan;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
if (hio_read(buffer, 1, 32, f) != 32)
return -1;
libxmp_instrument_name(mod, i, buffer, 32);
hio_seek(f, 12, SEEK_CUR); /* skip filename */
hio_read8(f); /* skip EMS handle */
mod->xxs[i].len = hio_read32l(f);
mod->xxs[i].lps = hio_read32l(f);
mod->xxs[i].lpe = hio_read32l(f);
flg = hio_read8(f);
c4spd = hio_read16l(f);
vol = hio_read8(f);
pan = hio_read8(f);
mod->xxi[i].sub[0].vol = vol > 0x40 ? 0x40 : vol;
mod->xxi[i].sub[0].pan = pan > 15 ? 0x80 : 0x80 + (pan - 8) * 16;
libxmp_c2spd_to_note(c4spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin);
mod->xxi[i].sub[0].sid = i;
mod->xxs[i].flg = 0;
if (mod->xxs[i].len > 0)
mod->xxi[i].nsm = 1;
if (flg & 0x01) {
mod->xxs[i].flg |= XMP_SAMPLE_LOOP;
}
if (flg & 0x02) {
mod->xxs[i].flg |= XMP_SAMPLE_16BIT;
mod->xxs[i].len >>= 1;
mod->xxs[i].lps >>= 1;
mod->xxs[i].lpe >>= 1;
}
D_(D_INFO "[%2X] %-32.32s %05x%c%05x %05x %c V%02x P%02x %5d",
i, mod->xxi[i].name,
mod->xxs[i].len,
mod->xxs[i].flg & XMP_SAMPLE_16BIT ? '+' : ' ',
mod->xxs[i].lps,
mod->xxs[i].lpe,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol,
mod->xxi[i].sub[0].pan,
c4spd);
}
/* Read and convert patterns */
hio_seek(f, start + pat_ofs, SEEK_SET);
/* Effects in muted channels are processed, so scan patterns first to
* see the real number of channels
*/
mod->chn = 0;
for (i = 0; i < mod->pat; i++) {
int len, c, r, k;
len = hio_read16l(f);
len -= 2;
for (r = 0; len > 0; ) {
c = hio_read8(f);
if (hio_error(f))
return -1;
len--;
if (c == 0) {
r++;
/* Sanity check */
if (len == 0) {
if (r > 64)
return -1;
} else {
if (r >= 64)
return -1;
}
continue;
}
if (mod->chn <= (c & 0x1f))
mod->chn = (c & 0x1f) + 1;
if (c & 0x20) { /* note and sample follows */
hio_read8(f);
hio_read8(f);
len -= 2;
}
if (c & 0x40) { /* effect(s) follow */
do {
k = hio_read8(f);
if (hio_error(f))
return -1;
len--;
if ((k & 0xc0) != 0xc0) {
hio_read8(f);
len--;
}
} while (k & 0x20);
}
}
}
mod->trk = mod->pat * mod->chn;
if (libxmp_init_pattern(mod) < 0)
return -1;
hio_seek(f, start + pat_ofs, SEEK_SET);
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
int len, c, r, k;
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
len = hio_read16l(f);
len -= 2;
for (r = 0; len > 0; ) {
c = hio_read8(f);
if (hio_error(f))
return -1;
len--;
if (c == 0) {
r++;
continue;
}
/* Sanity check */
if ((c & 0x1f) >= mod->chn || r >= 64) {
return -1;
}
event = &EVENT(i, c & 0x1f, r);
if (c & 0x20) { /* note and sample follows */
k = hio_read8(f);
/* 0 is empty note */
event->note = k ? 12 + 12 * MSN(k & 0x7f) + LSN(k) : 0;
event->ins = hio_read8(f);
len -= 2;
}
if (c & 0x40) { /* effect(s) follow */
do {
k = hio_read8(f);
if (hio_error(f))
return -1;
len--;
switch ((k & 0xc0) >> 6) {
case 0:
event->fxt = k & 0x1f;
event->fxp = hio_read8(f);
len--;
fix_effect(&event->fxt, &event->fxp);
break;
case 1:
event->f2t = k & 0x1f;
event->f2p = hio_read8(f);
len--;
fix_effect(&event->f2t, &event->f2p);
break;
case 2:
hio_read8(f);
len--;
}
} while (k & 0x20);
}
}
}
/* Read samples */
hio_seek(f, start + smp_ofs, SEEK_SET);
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0)
return -1;
}
m->quirk |= QUIRK_ARPMEM | QUIRK_FINEFX;
return 0;
}

View File

@ -0,0 +1,285 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "../period.h"
static int gtk_test(HIO_HANDLE *, char *, const int);
static int gtk_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_gtk = {
"Graoumf Tracker",
gtk_test,
gtk_load
};
static int gtk_test(HIO_HANDLE * f, char *t, const int start)
{
char buf[4];
if (hio_read(buf, 1, 4, f) < 4)
return -1;
if (memcmp(buf, "GTK", 3) || buf[3] > 4)
return -1;
libxmp_read_title(f, t, 32);
return 0;
}
static void translate_effects(struct xmp_event *event)
{
/* Ignore extended effects */
if (event->fxt == 0x0e || event->fxt == 0x0c) {
event->fxt = 0;
event->fxp = 0;
}
/* handle high-numbered effects */
if (event->fxt >= 0x40 && event->fxt <= 0x4f) {
event->fxp = (event->fxt << 4) | (event->fxp >> 4);
event->fxt = FX_SETPAN;
} else if (event->fxt >= 0x80 && event->fxt <= 0x8f) {
event->fxt = FX_MULTI_RETRIG;
} else if (event->fxt >= 0x08) {
switch (event->fxt) {
case 0x08: /* detune */
event->fxt = event->fxp = 0;
break;
case 0x09: /* delay */
event->fxt = FX_EXTENDED;
event->fxp = 0xd0 | (event->fxp & 0x0f);
break;
case 0x0a: /* cut / note release */
event->fxt = FX_EXTENDED;
event->fxp = 0xc0 | (event->fxp & 0x0f);
break;
case 0x0b: /* position jump */
case 0x0d: /* break pattern */
case 0x0f: /* set global speed/tempo */
/* same as ptk */
break;
case 0x0c: /* set vibrato waveform */
event->fxt = FX_EXTENDED;
event->fxp = 0x40 | (event->fxp & 0x0f);
break;
case 0x0e: /* set tremolo waveform */
event->fxt = FX_EXTENDED;
event->fxp = 0x70 | (event->fxp & 0x0f);
break;
case 0x10: /* arpeggio */
event->fxt = FX_ARPEGGIO;
break;
case 0x11: /* fine portamento up */
event->fxt = FX_F_PORTA_UP;
break;
case 0x12: /* fine portamento down */
event->fxt = FX_F_PORTA_DN;
break;
case 0x13: /* roll + volume slide */
event->fxt = FX_MULTI_RETRIG;
break;
case 0x16: /* exp. volume slide up */
case 0x14: /* linear volume slide up */
event->fxt = FX_VOLSLIDE_UP;
break;
case 0x17: /* exp. volume slide down */
case 0x15: /* linear volume slide down */
event->fxt = FX_VOLSLIDE_DN;
break;
case 0x20: /* set volume */
event->fxt = FX_VOLSET;
break;
case 0x21: /* set volume to 0x100 */
event->fxt = FX_VOLSET;
event->fxp = 0xff;
break;
case 0xa4: /* fine volume slide up */
event->fxt = FX_F_VSLIDE;
if (event->fxp > 0x0f)
event->fxp = 0x0f;
event->fxp <<= 4;
break;
case 0xa5: /* fine volume slide down */
event->fxt = FX_F_VSLIDE;
if (event->fxp > 0x0f)
event->fxp = 0x0f;
break;
case 0xa8: /* set number of frames */
event->fxt = FX_S3M_SPEED;
break;
default:
event->fxt = event->fxp = 0;
}
}
}
static int gtk_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
int i, j, k;
uint8 buffer[40];
int rows, bits, c2spd, size;
int ver, patmax;
LOAD_INIT();
hio_read(buffer, 4, 1, f);
ver = buffer[3];
hio_read(mod->name, 32, 1, f);
libxmp_set_type(m, "Graoumf Tracker GTK v%d", ver);
hio_seek(f, 160, SEEK_CUR); /* skip comments */
mod->ins = hio_read16b(f);
mod->smp = mod->ins;
rows = hio_read16b(f);
mod->chn = hio_read16b(f);
mod->len = hio_read16b(f);
mod->rst = hio_read16b(f);
m->volbase = 0x100;
m->c4rate = C4_NTSC_RATE;
MODULE_INFO();
D_(D_INFO "Instruments : %d ", mod->ins);
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
hio_read(buffer, 28, 1, f);
libxmp_instrument_name(mod, i, buffer, 28);
if (ver == 1) {
hio_read32b(f);
mod->xxs[i].len = hio_read32b(f);
mod->xxs[i].lps = hio_read32b(f);
size = hio_read32b(f);
mod->xxs[i].lpe = mod->xxs[i].lps + size - 1;
hio_read16b(f);
hio_read16b(f);
mod->xxi[i].sub[0].vol = 0xff;
mod->xxi[i].sub[0].pan = 0x80;
bits = 1;
c2spd = 8363;
} else {
hio_seek(f, 14, SEEK_CUR);
hio_read16b(f); /* autobal */
bits = hio_read16b(f); /* 1 = 8 bits, 2 = 16 bits */
c2spd = hio_read16b(f);
libxmp_c2spd_to_note(c2spd, &mod->xxi[i].sub[0].xpo, &mod->xxi[i].sub[0].fin);
mod->xxs[i].len = hio_read32b(f);
mod->xxs[i].lps = hio_read32b(f);
size = hio_read32b(f);
mod->xxs[i].lpe = mod->xxs[i].lps + size - 1;
mod->xxi[i].sub[0].vol = hio_read16b(f);
hio_read8(f);
mod->xxi[i].sub[0].fin = hio_read8s(f);
}
if (mod->xxs[i].len > 0)
mod->xxi[i].nsm = 1;
mod->xxi[i].sub[0].sid = i;
mod->xxs[i].flg = size > 2 ? XMP_SAMPLE_LOOP : 0;
if (bits > 1) {
mod->xxs[i].flg |= XMP_SAMPLE_16BIT;
mod->xxs[i].len >>= 1;
mod->xxs[i].lps >>= 1;
mod->xxs[i].lpe >>= 1;
}
D_(D_INFO "[%2X] %-28.28s %05x%c%05x %05x %c "
"V%02x F%+03d %5d", i,
mod->xxi[i].name,
mod->xxs[i].len,
bits > 1 ? '+' : ' ',
mod->xxs[i].lps,
size,
mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
mod->xxi[i].sub[0].vol, mod->xxi[i].sub[0].fin,
c2spd);
}
for (i = 0; i < 256; i++)
mod->xxo[i] = hio_read16b(f);
for (patmax = i = 0; i < mod->len; i++) {
if (mod->xxo[i] > patmax)
patmax = mod->xxo[i];
}
mod->pat = patmax + 1;
mod->trk = mod->pat * mod->chn;
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0)
return -1;
for (j = 0; j < mod->xxp[i]->rows; j++) {
for (k = 0; k < mod->chn; k++) {
event = &EVENT (i, k, j);
event->note = hio_read8(f);
if (event->note) {
event->note += 13;
}
event->ins = hio_read8(f);
event->fxt = hio_read8(f);
event->fxp = hio_read8(f);
if (ver >= 4) {
event->vol = hio_read8(f);
}
translate_effects(event);
}
}
}
/* Read samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (mod->xxs[i].len == 0)
continue;
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
}
return 0;
}

View File

@ -0,0 +1,322 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "loader.h"
#include "mod.h"
#include "../period.h"
#include "../hmn_extras.h"
/*
* From http://www.livet.se/mahoney/:
*
* Most modules from His Master's Noise uses special chip-sounds or
* fine-tuning of samples that never was a part of the standard NoiseTracker
* v2.0 command set. So if you want to listen to them correctly use an Amiga
* emulator and run the demo! DeliPlayer does a good job of playing them
* (there are some occasional error mostly concerning vibrato and portamento
* effects, but I can live with that!), and it can be downloaded from
* http://www.deliplayer.com
*/
/*
* From http://www.cactus.jawnet.pl/attitude/index.php?action=readtext&issue=12&which=12
*
* [Bepp] For your final Amiga release, the music disk His Master's Noise,
* you developed a special version of NoiseTracker. Could you tell us a
* little about this project?
*
* [Mahoney] I wanted to make a music disk with loads of songs, without being
* too repetitive or boring. So all of my "experimental features" that did not
* belong to NoiseTracker v2.0 were put into a separate version that would
* feature wavetable sounds, chord calculations, off-line filter calculations,
* mixing, reversing, sample accurate delays, resampling, fades - calculations
* that would be done on a standard setup of sounds instead of on individual
* modules. This "compression technique" lead to some 100 songs fitting on two
* standard 3.5" disks, written by 22 different composers. I'd say that writing
* a music program does give you loads of talented friends - you should try
* that yourself someday!
*/
/*
* From: Pex Tufvesson
* To: Claudio Matsuoka
* Date: Sat, Jun 1, 2013 at 4:16 AM
* Subject: Re: A question about (very) old stuff
*
* (...)
* If I remember correctly, these chip sounds were done with several short
* waveforms, and an index table that was loopable that would choose which
* waveform to play each frame. And, you didn't have to "draw" every
* waveform in the instrument - you would choose which waveforms to draw
* and the replayer would (at startup) interpolate the waveforms that you
* didn't draw.
*
* In the special noisetracker, you could draw all of these waveforms, draw
* the index table, and the instrument would be stored in one of the
* "patterns" of the song.
*/
static int hmn_test(HIO_HANDLE *, char *, const int);
static int hmn_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_hmn = {
"His Master's Noise",
hmn_test,
hmn_load
};
/* His Master's Noise M&K! will fail in regular Noisetracker loading
* due to invalid finetune values.
*/
#define MAGIC_FEST MAGIC4('F', 'E', 'S', 'T')
#define MAGIC_MK MAGIC4('M', '&', 'K', '!')
static int hmn_test(HIO_HANDLE * f, char *t, const int start)
{
int magic;
hio_seek(f, start + 1080, SEEK_SET);
magic = hio_read32b(f);
if (magic != MAGIC_FEST && magic != MAGIC_MK)
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 20);
return 0;
}
struct mupp {
uint8 prgon;
uint8 pattno;
uint8 dataloopstart;
uint8 dataloopend;
};
static int hmn_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct mod_header mh;
struct mupp mupp[31];
uint8 mod_event[4];
int mupp_index, num_mupp;
LOAD_INIT();
/*
* clr.b $1c(a6) ;prog on/off
* CMP.L #'Mupp',-$16(a3,d4.l)
* bne.s noprgo
* move.l a0,-(a7)
* move.b #1,$1c(a6) ;prog on
* move.l l697,a0
* lea $43c(a0),a0
* moveq #0,d2
* move.b -$16+$4(a3,d4.l),d2 ;pattno
* mulu #$400,d2
* lea (a0,d2.l),a0
* move.l a0,4(a6) ;proginstr data-start
* moveq #0,d2
* MOVE.B $3C0(A0),$12(A6)
* AND.B #$7F,$12(A6)
* move.b $380(a0),d2
* mulu #$20,d2
* lea (a0,d2.w),a0
* move.l a0,$a(a6) ;loopstartmempoi = startmempoi
* move.B $3(a3,d4.l),$13(a6) ;volume
* move.b -$16+$5(a3,d4.l),8(a6) ;dataloopstart
* move.b -$16+$6(a3,d4.l),9(a6) ;dataloopend
* move.w #$10,$e(a6) ;looplen
* move.l (a7)+,a0
* MOVE.W $12(A6),(A2)
* AND.W #$FF,(A2)
* BRA.S L505_LQ
*/
/*
* Wavetable structure is 22 * 32 byte waveforms and 32 byte
* wave control data with looping.
*/
memset(mupp, 0, 31 * sizeof (struct mupp));
hio_read(mh.name, 20, 1, f);
num_mupp = 0;
for (i = 0; i < 31; i++) {
hio_read(mh.ins[i].name, 22, 1, f); /* Instrument name */
if (memcmp(mh.ins[i].name, "Mupp", 4) == 0) {
mupp[i].prgon = 1;
mupp[i].pattno = mh.ins[i].name[4];
mupp[i].dataloopstart = mh.ins[i].name[5];
mupp[i].dataloopend = mh.ins[i].name[6];
num_mupp++;
}
mh.ins[i].size = hio_read16b(f);
mh.ins[i].finetune = hio_read8(f);
mh.ins[i].volume = hio_read8(f);
mh.ins[i].loop_start = hio_read16b(f);
mh.ins[i].loop_size = hio_read16b(f);
}
mh.len = hio_read8(f);
mh.restart = hio_read8(f);
hio_read(mh.order, 128, 1, f);
hio_read(mh.magic, 4, 1, f);
mod->chn = 4;
mod->ins = 31;
mod->smp = mod->ins + 28 * num_mupp;
mod->len = mh.len;
mod->rst = mh.restart;
memcpy(mod->xxo, mh.order, 128);
for (i = 0; i < 128; i++) {
if (mod->xxo[i] > mod->pat)
mod->pat = mod->xxo[i];
}
mod->pat++;
mod->trk = mod->chn * mod->pat;
if (libxmp_hmn_new_module_extras(m) != 0)
return -1;
strncpy(mod->name, (char *)mh.name, 20);
libxmp_set_type(m, "%s (%4.4s)", "His Master's Noise", mh.magic);
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
if (mupp[i].prgon) {
mod->xxi[i].nsm = 28;
snprintf(mod->xxi[i].name, 32,
"Mupp %02x %02x %02x", mupp[i].pattno,
mupp[i].dataloopstart, mupp[i].dataloopend);
if (libxmp_hmn_new_instrument_extras(&mod->xxi[i]) != 0)
return -1;
} else {
mod->xxi[i].nsm = 1;
libxmp_instrument_name(mod, i, mh.ins[i].name, 22);
mod->xxs[i].len = 2 * mh.ins[i].size;
mod->xxs[i].lps = 2 * mh.ins[i].loop_start;
mod->xxs[i].lpe = mod->xxs[i].lps +
2 * mh.ins[i].loop_size;
mod->xxs[i].flg = mh.ins[i].loop_size > 1 ?
XMP_SAMPLE_LOOP : 0;
}
if (libxmp_alloc_subinstrument(mod, i, mod->xxi[i].nsm) < 0)
return -1;
for (j = 0; j < mod->xxi[i].nsm; j++) {
mod->xxi[i].sub[j].fin =
-(int8)(mh.ins[i].finetune << 3);
mod->xxi[i].sub[j].vol = mh.ins[i].volume;
mod->xxi[i].sub[j].pan = 0x80;
mod->xxi[i].sub[j].sid = i;
}
}
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Load and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
for (j = 0; j < (64 * 4); j++) {
event = &EVENT(i, j % 4, j / 4);
if (hio_read(mod_event, 1, 4, f) < 4) {
D_(D_CRIT "read error at pat %d", i);
return -1;
}
libxmp_decode_protracker_event(event, mod_event);
switch (event->fxt) {
case 0x07:
event->fxt = FX_MEGAARP;
break;
case 0x08:
case 0x09:
case 0x0e:
event->fxt = event->fxp = 0;
break;
}
}
}
m->period_type = PERIOD_MODRNG;
/* Load samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < 31; i++) {
if (libxmp_load_sample(m, f, SAMPLE_FLAG_FULLREP,
&mod->xxs[i], NULL) < 0) {
return -1;
}
}
/* Load Mupp samples */
mupp_index = 0;
for (i = 0; i < 31; i ++) {
struct hmn_instrument_extras *extra =
(struct hmn_instrument_extras *)mod->xxi[i].extra;
if (!mupp[i].prgon)
continue;
hio_seek(f, start + 1084 + 1024 * mupp[i].pattno, SEEK_SET);
for (j = 0; j < 28; j++) {
int k = 31 + 28 * mupp_index + j;
mod->xxi[i].sub[j].sid = k;
mod->xxs[k].len = 32;
mod->xxs[k].lps = 0;
mod->xxs[k].lpe = 32;
mod->xxs[k].flg = XMP_SAMPLE_LOOP;
if (libxmp_load_sample(m, f, 0, &mod->xxs[k], NULL) < 0)
return -1;
}
extra->dataloopstart = mupp[i].dataloopstart;
extra->dataloopend = mupp[i].dataloopend;
hio_read(extra->data, 1, 64, f);
hio_read(extra->progvolume, 1, 64, f);
mupp_index++;
}
return 0;
}

View File

@ -0,0 +1,206 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Loader for Soundtracker 2.6/Ice Tracker modules */
#include "loader.h"
#define MAGIC_MTN_ MAGIC4('M','T','N',0)
#define MAGIC_IT10 MAGIC4('I','T','1','0')
static int ice_test(HIO_HANDLE *, char *, const int);
static int ice_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_ice = {
"Soundtracker 2.6/Ice Tracker",
ice_test,
ice_load
};
static int ice_test(HIO_HANDLE * f, char *t, const int start)
{
uint32 magic;
hio_seek(f, start + 1464, SEEK_SET);
magic = hio_read32b(f);
if (magic != MAGIC_MTN_ && magic != MAGIC_IT10)
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 28);
return 0;
}
struct ice_ins {
char name[22]; /* Instrument name */
uint16 len; /* Sample length / 2 */
uint8 finetune; /* Finetune */
uint8 volume; /* Volume (0-63) */
uint16 loop_start; /* Sample loop start in file */
uint16 loop_size; /* Loop size / 2 */
};
struct ice_header {
char title[20];
struct ice_ins ins[31]; /* Instruments */
uint8 len; /* Size of the pattern list */
uint8 trk; /* Number of tracks */
uint8 ord[128][4];
uint32 magic; /* 'MTN\0', 'IT10' */
};
static int ice_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct ice_header ih;
uint8 ev[4];
LOAD_INIT();
hio_read(ih.title, 20, 1, f);
for (i = 0; i < 31; i++) {
hio_read(ih.ins[i].name, 22, 1, f);
ih.ins[i].len = hio_read16b(f);
ih.ins[i].finetune = hio_read8(f);
ih.ins[i].volume = hio_read8(f);
ih.ins[i].loop_start = hio_read16b(f);
ih.ins[i].loop_size = hio_read16b(f);
}
ih.len = hio_read8(f);
ih.trk = hio_read8(f);
hio_read(ih.ord, 128 * 4, 1, f);
ih.magic = hio_read32b(f);
/* Sanity check */
if (ih.len > 128) {
return -1;
}
for (i = 0; i < ih.len; i++) {
for (j = 0; j < 4; j++) {
if (ih.ord[i][j] >= ih.trk)
return -1;
}
}
if (ih.magic == MAGIC_IT10)
libxmp_set_type(m, "Ice Tracker");
else if (ih.magic == MAGIC_MTN_)
libxmp_set_type(m, "Soundtracker 2.6");
else
return -1;
mod->ins = 31;
mod->smp = mod->ins;
mod->pat = ih.len;
mod->len = ih.len;
mod->trk = ih.trk;
strncpy(mod->name, (char *)ih.title, 20);
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi;
struct xmp_sample *xxs;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
xxi = &mod->xxi[i];
xxs = &mod->xxs[i];
xxs->len = 2 * ih.ins[i].len;
xxs->lps = 2 * ih.ins[i].loop_start;
xxs->lpe = xxs->lps + 2 * ih.ins[i].loop_size;
xxs->flg = ih.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0;
xxi->sub[0].vol = ih.ins[i].volume;
/* xxi->sub[0].fin = (int8)(ih.ins[i].finetune << 4); */
xxi->sub[0].pan = 0x80;
xxi->sub[0].sid = i;
if (xxs->len > 0)
xxi->nsm = 1;
D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c %02x %01x",
i, ih.ins[i].name, xxs->len, xxs->lps,
xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
xxi->sub[0].vol, xxi->sub[0].fin >> 4);
}
if (libxmp_init_pattern(mod) < 0)
return -1;
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern(mod, i) < 0)
return -1;
mod->xxp[i]->rows = 64;
for (j = 0; j < mod->chn; j++) {
mod->xxp[i]->index[j] = ih.ord[i][j];
}
mod->xxo[i] = i;
}
D_(D_INFO "Stored tracks: %d", mod->trk);
for (i = 0; i < mod->trk; i++) {
if (libxmp_alloc_track(mod, i, 64) < 0)
return -1;
for (j = 0; j < mod->xxt[i]->rows; j++) {
event = &mod->xxt[i]->event[j];
if (hio_read(ev, 1, 4, f) < 4) {
D_(D_CRIT "read error at track %d", i);
return -1;
}
libxmp_decode_protracker_event(event, ev);
if (event->fxt == FX_SPEED) {
if (MSN(event->fxp) && LSN(event->fxp)) {
event->fxt = FX_ICE_SPEED;
}
}
}
}
m->period_type = PERIOD_MODRNG;
/* Read samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->ins; i++) {
if (mod->xxs[i].len <= 4)
continue;
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
}
return 0;
}

215
libxmp/src/loaders/iff.c Normal file
View File

@ -0,0 +1,215 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#include "../list.h"
#include "iff.h"
#include "loader.h"
struct iff_data {
struct list_head iff_list;
unsigned id_size;
unsigned flags;
};
static int iff_process(iff_handle opaque, struct module_data *m, char *id, long size,
HIO_HANDLE *f, void *parm)
{
struct iff_data *data = (struct iff_data *)opaque;
struct list_head *tmp;
struct iff_info *i;
int pos;
pos = hio_tell(f);
list_for_each(tmp, &data->iff_list) {
i = list_entry(tmp, struct iff_info, list);
if (id && !memcmp(id, i->id, data->id_size)) {
D_(D_WARN "Load IFF chunk %s (%ld) @%d", id, size, pos);
if (size > IFF_MAX_CHUNK_SIZE) {
return -1;
}
if (i->loader(m, size, f, parm) < 0) {
return -1;
}
break;
}
}
if (hio_seek(f, pos + size, SEEK_SET) < 0) {
return -1;
}
return 0;
}
static int iff_chunk(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm)
{
struct iff_data *data = (struct iff_data *)opaque;
unsigned size;
char id[17] = "";
D_(D_INFO "chunk id size: %d", data->id_size);
if (hio_read(id, 1, data->id_size, f) != data->id_size) {
(void)hio_error(f); /* clear error flag */
return 1;
}
D_(D_INFO "chunk id: [%s]", id);
if (data->flags & IFF_SKIP_EMBEDDED) {
/* embedded RIFF hack */
if (!strncmp(id, "RIFF", 4)) {
hio_read32b(f);
hio_read32b(f);
/* read first chunk ID instead */
if (hio_read(id, 1, data->id_size, f) != data->id_size){
return 1;
}
}
}
if (data->flags & IFF_LITTLE_ENDIAN) {
size = hio_read32l(f);
} else {
size = hio_read32b(f);
}
D_(D_INFO "size: %d", size);
if (hio_error(f)) {
return -1;
}
if (data->flags & IFF_CHUNK_ALIGN2) {
/* Sanity check */
if (size > 0xfffffffe) {
return -1;
}
size = (size + 1) & ~1;
}
if (data->flags & IFF_CHUNK_ALIGN4) {
/* Sanity check */
if (size > 0xfffffffc) {
return -1;
}
size = (size + 3) & ~3;
}
/* PT 3.6 hack: this does not seem to ever apply to "PTDT".
* This broke several modules (city lights.pt36, acid phase.pt36) */
if ((data->flags & IFF_FULL_CHUNK_SIZE) && memcmp(id, "PTDT", 4)) {
if (size < data->id_size + 4)
return -1;
size -= data->id_size + 4;
}
return iff_process(opaque, m, id, size, f, parm);
}
iff_handle libxmp_iff_new()
{
struct iff_data *data;
data = (struct iff_data *) malloc(sizeof(struct iff_data));
if (data == NULL) {
return NULL;
}
INIT_LIST_HEAD(&data->iff_list);
data->id_size = 4;
data->flags = 0;
return (iff_handle)data;
}
int libxmp_iff_load(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm)
{
int ret;
while (!hio_eof(f)) {
ret = iff_chunk(opaque, m, f, parm);
if (ret > 0)
break;
if (ret < 0)
return -1;
}
return 0;
}
int libxmp_iff_register(iff_handle opaque, const char *id,
int (*loader)(struct module_data *, int, HIO_HANDLE *, void *))
{
struct iff_data *data = (struct iff_data *)opaque;
struct iff_info *f;
int i = 0;
f = (struct iff_info *) malloc(sizeof(struct iff_info));
if (f == NULL)
return -1;
/* Note: previously was an strncpy */
for (; i < 4 && id && id[i]; i++)
f->id[i] = id[i];
for (; i < 4; i++)
f->id[i] = '\0';
f->loader = loader;
list_add_tail(&f->list, &data->iff_list);
return 0;
}
void libxmp_iff_release(iff_handle opaque)
{
struct iff_data *data = (struct iff_data *)opaque;
struct list_head *tmp;
struct iff_info *i;
/* can't use list_for_each, we free the node before incrementing */
for (tmp = (&data->iff_list)->next; tmp != (&data->iff_list);) {
i = list_entry(tmp, struct iff_info, list);
list_del(&i->list);
tmp = tmp->next;
free(i);
}
free(data);
}
/* Functions to tune IFF mutations */
void libxmp_iff_id_size(iff_handle opaque, int n)
{
struct iff_data *data = (struct iff_data *)opaque;
data->id_size = n;
}
void libxmp_iff_set_quirk(iff_handle opaque, int i)
{
struct iff_data *data = (struct iff_data *)opaque;
data->flags |= i;
}

43
libxmp/src/loaders/iff.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef LIBXMP_IFF_H
#define LIBXMP_IFF_H
#include "../hio.h"
#include "../list.h"
#define IFF_NOBUFFER 0x0001
#define IFF_LITTLE_ENDIAN 0x01
#define IFF_FULL_CHUNK_SIZE 0x02
#define IFF_CHUNK_ALIGN2 0x04
#define IFF_CHUNK_ALIGN4 0x08
#define IFF_SKIP_EMBEDDED 0x10
#define IFF_CHUNK_TRUNC4 0x20
#define IFF_MAX_CHUNK_SIZE 0x800000
typedef void *iff_handle;
struct iff_header {
char form[4]; /* FORM */
int len; /* File length */
char id[4]; /* IFF type identifier */
};
struct iff_info {
char id[4];
int (*loader)(struct module_data *, int, HIO_HANDLE *, void *);
struct list_head list;
};
iff_handle libxmp_iff_new(void);
int libxmp_iff_load(iff_handle, struct module_data *, HIO_HANDLE *, void *);
/* int libxmp_iff_chunk(iff_handle, struct module_data *, HIO_HANDLE *, void *); */
int libxmp_iff_register(iff_handle, const char *,
int (*loader)(struct module_data *, int, HIO_HANDLE *, void *));
void libxmp_iff_id_size(iff_handle, int);
void libxmp_iff_set_quirk(iff_handle, int);
void libxmp_iff_release(iff_handle);
/* int libxmp_iff_process(iff_handle, struct module_data *, char *, long,
HIO_HANDLE *, void *); */
#endif /* LIBXMP_IFF_H */

View File

@ -0,0 +1,541 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Loader for Imago Orpheus modules based on the format description
* written by Lutz Roeder.
*/
#include "loader.h"
#include "../period.h"
#define IMF_EOR 0x00
#define IMF_CH_MASK 0x1f
#define IMF_NI_FOLLOW 0x20
#define IMF_FX_FOLLOWS 0x80
#define IMF_F2_FOLLOWS 0x40
struct imf_channel {
char name[12]; /* Channelname (ASCIIZ-String, max 11 chars) */
uint8 status; /* Channel status */
uint8 pan; /* Pan positions */
uint8 chorus; /* Default chorus */
uint8 reverb; /* Default reverb */
};
struct imf_header {
char name[32]; /* Songname (ASCIIZ-String, max. 31 chars) */
uint16 len; /* Number of orders saved */
uint16 pat; /* Number of patterns saved */
uint16 ins; /* Number of instruments saved */
uint16 flg; /* Module flags */
uint8 unused1[8];
uint8 tpo; /* Default tempo (1..255) */
uint8 bpm; /* Default beats per minute (BPM) (32..255) */
uint8 vol; /* Default mastervolume (0..64) */
uint8 amp; /* Amplification factor (4..127) */
uint8 unused2[8];
uint32 magic; /* 'IM10' */
struct imf_channel chn[32]; /* Channel settings */
uint8 pos[256]; /* Order list */
};
struct imf_env {
uint8 npt; /* Number of envelope points */
uint8 sus; /* Envelope sustain point */
uint8 lps; /* Envelope loop start point */
uint8 lpe; /* Envelope loop end point */
uint8 flg; /* Envelope flags */
uint8 unused[3];
};
struct imf_instrument {
char name[32]; /* Inst. name (ASCIIZ-String, max. 31 chars) */
uint8 map[120]; /* Multisample settings */
uint8 unused[8];
uint16 vol_env[32]; /* Volume envelope settings */
uint16 pan_env[32]; /* Pan envelope settings */
uint16 pitch_env[32]; /* Pitch envelope settings */
struct imf_env env[3];
uint16 fadeout; /* Fadeout rate (0...0FFFH) */
uint16 nsm; /* Number of samples in instrument */
uint32 magic; /* 'II10' */
};
struct imf_sample {
char name[13]; /* Sample filename (12345678.ABC) */
uint8 unused1[3];
uint32 len; /* Length */
uint32 lps; /* Loop start */
uint32 lpe; /* Loop end */
uint32 rate; /* Samplerate */
uint8 vol; /* Default volume (0..64) */
uint8 pan; /* Default pan (00h = Left / 80h = Middle) */
uint8 unused2[14];
uint8 flg; /* Sample flags */
uint8 unused3[5];
uint16 ems; /* Reserved for internal usage */
uint32 dram; /* Reserved for internal usage */
uint32 magic; /* 'IS10' */
};
#define MAGIC_IM10 MAGIC4('I','M','1','0')
#define MAGIC_II10 MAGIC4('I','I','1','0')
static int imf_test (HIO_HANDLE *, char *, const int);
static int imf_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_imf = {
"Imago Orpheus v1.0",
imf_test,
imf_load
};
static int imf_test(HIO_HANDLE *f, char *t, const int start)
{
hio_seek(f, start + 60, SEEK_SET);
if (hio_read32b(f) != MAGIC_IM10)
return -1;
hio_seek(f, start, SEEK_SET);
libxmp_read_title(f, t, 32);
return 0;
}
#define NONE 0xff
#define FX_IMF_FPORTA_UP 0xfe
#define FX_IMF_FPORTA_DN 0xfd
/* Effect conversion table */
static const uint8 fx[36] = {
NONE,
FX_S3M_SPEED,
FX_S3M_BPM,
FX_TONEPORTA,
FX_TONE_VSLIDE,
FX_VIBRATO,
FX_VIBRA_VSLIDE,
FX_FINE_VIBRATO,
FX_TREMOLO,
FX_S3M_ARPEGGIO,
FX_SETPAN,
FX_PANSLIDE,
FX_VOLSET,
FX_VOLSLIDE,
FX_F_VSLIDE,
FX_FINETUNE,
FX_NSLIDE_UP,
FX_NSLIDE_DN,
FX_PORTA_UP,
FX_PORTA_DN,
FX_IMF_FPORTA_UP,
FX_IMF_FPORTA_DN,
FX_FLT_CUTOFF,
FX_FLT_RESN,
FX_OFFSET,
NONE /* fine offset */,
FX_KEYOFF,
FX_MULTI_RETRIG,
FX_TREMOR,
FX_JUMP,
FX_BREAK,
FX_GLOBALVOL,
FX_GVOL_SLIDE,
FX_EXTENDED,
FX_CHORUS,
FX_REVERB
};
/* Effect translation */
static void xlat_fx (int c, uint8 *fxt, uint8 *fxp)
{
uint8 h = MSN (*fxp), l = LSN (*fxp);
if (*fxt >= ARRAY_SIZE(fx)) {
D_(D_WARN "invalid effect %#02x", *fxt);
*fxt = *fxp = 0;
return;
}
switch (*fxt = fx[*fxt]) {
case FX_IMF_FPORTA_UP:
*fxt = FX_PORTA_UP;
if (*fxp < 0x30)
*fxp = LSN (*fxp >> 2) | 0xe0;
else
*fxp = LSN (*fxp >> 4) | 0xf0;
break;
case FX_IMF_FPORTA_DN:
*fxt = FX_PORTA_DN;
if (*fxp < 0x30)
*fxp = LSN (*fxp >> 2) | 0xe0;
else
*fxp = LSN (*fxp >> 4) | 0xf0;
break;
case FX_EXTENDED: /* Extended effects */
switch (h) {
case 0x1: /* Set filter */
case 0x2: /* Undefined */
case 0x4: /* Undefined */
case 0x6: /* Undefined */
case 0x7: /* Undefined */
case 0x9: /* Undefined */
case 0xe: /* Ignore envelope */
case 0xf: /* Invert loop */
*fxp = *fxt = 0;
break;
case 0x3: /* Glissando */
*fxp = l | (EX_GLISS << 4);
break;
case 0x5: /* Vibrato waveform */
*fxp = l | (EX_VIBRATO_WF << 4);
break;
case 0x8: /* Tremolo waveform */
*fxp = l | (EX_TREMOLO_WF << 4);
break;
case 0xa: /* Pattern loop */
*fxp = l | (EX_PATTERN_LOOP << 4);
break;
case 0xb: /* Pattern delay */
*fxp = l | (EX_PATT_DELAY << 4);
break;
case 0xc:
if (l == 0)
*fxt = *fxp = 0;
}
break;
case NONE: /* No effect */
*fxt = *fxp = 0;
break;
}
}
static int imf_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int c, r, i, j;
struct xmp_event *event = 0, dummy;
struct imf_header ih;
struct imf_instrument ii;
struct imf_sample is;
int pat_len, smp_num;
uint8 n, b;
LOAD_INIT();
/* Load and convert header */
hio_read(ih.name, 32, 1, f);
ih.len = hio_read16l(f);
ih.pat = hio_read16l(f);
ih.ins = hio_read16l(f);
ih.flg = hio_read16l(f);
hio_read(ih.unused1, 8, 1, f);
ih.tpo = hio_read8(f);
ih.bpm = hio_read8(f);
ih.vol = hio_read8(f);
ih.amp = hio_read8(f);
hio_read(ih.unused2, 8, 1, f);
ih.magic = hio_read32b(f);
/* Sanity check */
if (ih.len > 256 || ih.pat > 256 || ih.ins > 255) {
return -1;
}
for (i = 0; i < 32; i++) {
hio_read(ih.chn[i].name, 12, 1, f);
ih.chn[i].chorus = hio_read8(f);
ih.chn[i].reverb = hio_read8(f);
ih.chn[i].pan = hio_read8(f);
ih.chn[i].status = hio_read8(f);
}
if (hio_read(ih.pos, 256, 1, f) < 1) {
D_(D_CRIT "read error at order list");
return -1;
}
if (ih.magic != MAGIC_IM10) {
return -1;
}
libxmp_copy_adjust(mod->name, (uint8 *)ih.name, 32);
mod->len = ih.len;
mod->ins = ih.ins;
mod->smp = 1024;
mod->pat = ih.pat;
if (ih.flg & 0x01)
m->period_type = PERIOD_LINEAR;
mod->spd = ih.tpo;
mod->bpm = ih.bpm;
libxmp_set_type(m, "Imago Orpheus 1.0 IMF");
MODULE_INFO();
mod->chn = 0;
for (i = 0; i < 32; i++) {
/* 0=enabled; 1=muted, but still processed; 2=disabled.*/
if (ih.chn[i].status >= 2)
continue;
mod->chn = i + 1;
mod->xxc[i].pan = ih.chn[i].pan;
#if 0
/* FIXME */
mod->xxc[i].cho = ih.chn[i].chorus;
mod->xxc[i].rvb = ih.chn[i].reverb;
mod->xxc[i].flg |= XMP_CHANNEL_FX;
#endif
}
mod->trk = mod->pat * mod->chn;
memcpy(mod->xxo, ih.pos, mod->len);
for (i = 0; i < mod->len; i++) {
if (mod->xxo[i] == 0xff)
mod->xxo[i]--;
}
m->c4rate = C4_NTSC_RATE;
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Read patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
int rows;
pat_len = hio_read16l(f) - 4;
rows = hio_read16l(f);
/* Sanity check */
if (rows > 256) {
return -1;
}
if (libxmp_alloc_pattern_tracks(mod, i, rows) < 0)
return -1;
r = 0;
while (--pat_len >= 0) {
b = hio_read8(f);
if (b == IMF_EOR) {
r++;
continue;
}
/* Sanity check */
if (r >= rows) {
return -1;
}
c = b & IMF_CH_MASK;
event = c >= mod->chn ? &dummy : &EVENT(i, c, r);
if (b & IMF_NI_FOLLOW) {
n = hio_read8(f);
switch (n) {
case 255:
case 160: /* ?! */
n = XMP_KEY_OFF;
break; /* Key off */
default:
n = 13 + 12 * MSN (n) + LSN (n);
}
event->note = n;
event->ins = hio_read8(f);
pat_len -= 2;
}
if (b & IMF_FX_FOLLOWS) {
event->fxt = hio_read8(f);
event->fxp = hio_read8(f);
xlat_fx(c, &event->fxt, &event->fxp);
pat_len -= 2;
}
if (b & IMF_F2_FOLLOWS) {
event->f2t = hio_read8(f);
event->f2p = hio_read8(f);
xlat_fx(c, &event->f2t, &event->f2p);
pat_len -= 2;
}
}
}
if (libxmp_init_instrument(m) < 0)
return -1;
/* Read and convert instruments and samples */
D_(D_INFO "Instruments: %d", mod->ins);
for (smp_num = i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
hio_read(ii.name, 32, 1, f);
ii.name[31] = 0;
hio_read(ii.map, 120, 1, f);
hio_read(ii.unused, 8, 1, f);
for (j = 0; j < 32; j++)
ii.vol_env[j] = hio_read16l(f);
for (j = 0; j < 32; j++)
ii.pan_env[j] = hio_read16l(f);
for (j = 0; j < 32; j++)
ii.pitch_env[j] = hio_read16l(f);
for (j = 0; j < 3; j++) {
ii.env[j].npt = hio_read8(f);
ii.env[j].sus = hio_read8(f);
ii.env[j].lps = hio_read8(f);
ii.env[j].lpe = hio_read8(f);
ii.env[j].flg = hio_read8(f);
hio_read(ii.env[j].unused, 3, 1, f);
}
ii.fadeout = hio_read16l(f);
ii.nsm = hio_read16l(f);
ii.magic = hio_read32b(f);
/* Sanity check */
if (ii.nsm > 255)
return -1;
if (ii.magic != MAGIC_II10)
return -2;
xxi->nsm = ii.nsm;
if (xxi->nsm > 0) {
if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0)
return -1;
}
strncpy((char *)xxi->name, ii.name, 31);
xxi->name[31] = '\0';
for (j = 0; j < 108; j++) {
xxi->map[j + 12].ins = ii.map[j];
}
D_(D_INFO "[%2X] %-31.31s %2d %4x %c", i, ii.name, ii.nsm,
ii.fadeout, ii.env[0].flg & 0x01 ? 'V' : '-');
xxi->aei.npt = ii.env[0].npt;
xxi->aei.sus = ii.env[0].sus;
xxi->aei.lps = ii.env[0].lps;
xxi->aei.lpe = ii.env[0].lpe;
xxi->aei.flg = ii.env[0].flg & 0x01 ? XMP_ENVELOPE_ON : 0;
xxi->aei.flg |= ii.env[0].flg & 0x02 ? XMP_ENVELOPE_SUS : 0;
xxi->aei.flg |= ii.env[0].flg & 0x04 ? XMP_ENVELOPE_LOOP : 0;
/* Sanity check */
if (xxi->aei.npt > 16) {
return -1;
}
for (j = 0; j < xxi->aei.npt; j++) {
xxi->aei.data[j * 2] = ii.vol_env[j * 2];
xxi->aei.data[j * 2 + 1] = ii.vol_env[j * 2 + 1];
}
for (j = 0; j < ii.nsm; j++, smp_num++) {
struct xmp_subinstrument *sub = &xxi->sub[j];
struct xmp_sample *xxs = &mod->xxs[smp_num];
int sid;
hio_read(is.name, 13, 1, f);
hio_read(is.unused1, 3, 1, f);
is.len = hio_read32l(f);
is.lps = hio_read32l(f);
is.lpe = hio_read32l(f);
is.rate = hio_read32l(f);
is.vol = hio_read8(f);
is.pan = hio_read8(f);
hio_read(is.unused2, 14, 1, f);
is.flg = hio_read8(f);
hio_read(is.unused3, 5, 1, f);
is.ems = hio_read16l(f);
is.dram = hio_read32l(f);
is.magic = hio_read32b(f);
/* Sanity check */
if (is.len > 0x100000 || is.lps > 0x100000 || is.lpe > 0x100000)
return -1;
sub->sid = smp_num;
sub->vol = is.vol;
sub->pan = is.pan;
xxs->len = is.len;
xxs->lps = is.lps;
xxs->lpe = is.lpe;
xxs->flg = is.flg & 1 ? XMP_SAMPLE_LOOP : 0;
if (is.flg & 4) {
xxs->flg |= XMP_SAMPLE_16BIT;
xxs->len >>= 1;
xxs->lps >>= 1;
xxs->lpe >>= 1;
}
D_(D_INFO " %02x: %05x %05x %05x %5d",
j, is.len, is.lps, is.lpe, is.rate);
libxmp_c2spd_to_note(is.rate, &sub->xpo, &sub->fin);
if (xxs->len <= 0)
continue;
sid = sub->sid;
if (libxmp_load_sample(m, f, 0, &mod->xxs[sid], NULL) < 0)
return -1;
}
}
mod->smp = smp_num;
mod->xxs = (struct xmp_sample *) realloc(mod->xxs, sizeof(struct xmp_sample) * mod->smp);
if (mod->xxs == NULL) {
return -1;
}
m->xtra = (struct extra_sample_data *) realloc(m->xtra, sizeof(struct extra_sample_data) * mod->smp);
if (m->xtra == NULL) {
return -1;
}
m->c4rate = C4_NTSC_RATE;
m->quirk |= QUIRK_FILTER | QUIRKS_ST3 | QUIRK_ARPMEM;
m->read_event_type = READ_EVENT_ST3;
return 0;
}

View File

@ -0,0 +1,293 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Loader for Images Music System modules based on the EP replayer.
*
* Date: Thu, 19 Apr 2001 19:13:06 +0200
* From: Michael Doering <mldoering@gmx.net>
*
* I just "stumbled" upon something about the Unic.3C format when I was
* testing replayers for the upcoming UADE 0.21 that might be also
* interesting to you for xmp. The "Beastbusters" tune is not a UNIC file :)
* It's actually a different Format, although obviously related, called
* "Images Music System".
*
* I was testing the replayer from the Wanted Team with one of their test
* tunes, among them also the beastbuster music. When I first listened to
* it, I knew I have heard it somewhere, a bit different but it was alike.
* This one had more/richer percussions and there was no strange beep in
* the bg. ;) After some searching on my HD I found it among the xmp test
* tunes as a UNIC file.
*/
#include "loader.h"
#include "../period.h"
struct ims_instrument {
uint8 name[20];
int16 finetune; /* Causes squeaks in beast-busters1! */
uint16 size;
uint8 unknown;
uint8 volume;
uint16 loop_start;
uint16 loop_size;
};
struct ims_header {
uint8 title[20];
struct ims_instrument ins[31];
uint8 len;
uint8 zero;
uint8 orders[128];
uint8 magic[4];
};
static int ims_test (HIO_HANDLE *, char *, const int);
static int ims_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_ims = {
"Images Music System",
ims_test,
ims_load
};
static int ims_test(HIO_HANDLE *f, char *t, const int start)
{
int i;
int smp_size, pat;
struct ims_header ih;
smp_size = 0;
hio_read(ih.title, 20, 1, f);
for (i = 0; i < 31; i++) {
if (hio_read(ih.ins[i].name, 1, 20, f) < 20)
return -1;
ih.ins[i].finetune = (int16)hio_read16b(f);
ih.ins[i].size = hio_read16b(f);
ih.ins[i].unknown = hio_read8(f);
ih.ins[i].volume = hio_read8(f);
ih.ins[i].loop_start = hio_read16b(f);
ih.ins[i].loop_size = hio_read16b(f);
smp_size += ih.ins[i].size * 2;
if (libxmp_test_name(ih.ins[i].name, 20) < 0)
return -1;
if (ih.ins[i].volume > 0x40)
return -1;
if (ih.ins[i].size > 0x8000)
return -1;
if (ih.ins[i].loop_start > ih.ins[i].size)
return -1;
if (ih.ins[i].size && ih.ins[i].loop_size > 2 * ih.ins[i].size)
return -1;
}
if (smp_size < 8)
return -1;
ih.len = hio_read8(f);
ih.zero = hio_read8(f);
hio_read(ih.orders, 128, 1, f);
if (hio_read(ih.magic, 4, 1, f) == 0)
return -1;
if (ih.zero > 1) /* not sure what this is */
return -1;
if (ih.magic[3] != 0x3c)
return -1;
if (ih.len > 0x7f)
return -1;
for (pat = i = 0; i < ih.len; i++)
if (ih.orders[i] > pat)
pat = ih.orders[i];
pat++;
if (pat > 0x7f || ih.len == 0 || ih.len > 0x7f)
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 20);
return 0;
}
static int ims_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct ims_header ih;
uint8 ims_event[3];
int xpo = 21; /* Tuned against UADE */
LOAD_INIT();
mod->chn = 4;
mod->ins = 31;
mod->smp = mod->ins;
hio_read (ih.title, 20, 1, f);
for (i = 0; i < 31; i++) {
hio_read (ih.ins[i].name, 20, 1, f);
ih.ins[i].finetune = (int16)hio_read16b(f);
ih.ins[i].size = hio_read16b(f);
ih.ins[i].unknown = hio_read8(f);
ih.ins[i].volume = hio_read8(f);
ih.ins[i].loop_start = hio_read16b(f);
ih.ins[i].loop_size = hio_read16b(f);
}
ih.len = hio_read8(f);
if (ih.len > 128) {
return -1;
}
ih.zero = hio_read8(f);
hio_read (ih.orders, 128, 1, f);
hio_read (ih.magic, 4, 1, f);
mod->len = ih.len;
memcpy (mod->xxo, ih.orders, mod->len);
for (i = 0; i < mod->len; i++)
if (mod->xxo[i] > mod->pat)
mod->pat = mod->xxo[i];
mod->pat++;
mod->trk = mod->chn * mod->pat;
strncpy(mod->name, (char *)ih.title, 20);
libxmp_set_type(m, "Images Music System");
MODULE_INFO();
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi;
struct xmp_subinstrument *sub;
struct xmp_sample *xxs;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
xxi = &mod->xxi[i];
sub = &xxi->sub[0];
xxs = &mod->xxs[i];
xxs->len = 2 * ih.ins[i].size;
xxs->lps = 2 * ih.ins[i].loop_start;
xxs->lpe = xxs->lps + 2 * ih.ins[i].loop_size;
xxs->flg = ih.ins[i].loop_size > 1 ? XMP_SAMPLE_LOOP : 0;
sub->fin = 0; /* ih.ins[i].finetune; */
sub->vol = ih.ins[i].volume;
sub->pan = 0x80;
sub->sid = i;
//mod->xxi[i].rls = 0xfff;
if (xxs->len > 0) {
xxi->nsm = 1;
}
libxmp_instrument_name(mod, i, ih.ins[i].name, 20);
D_(D_INFO "[%2X] %-20.20s %04x %04x %04x %c V%02x %+d",
i, xxi->name, xxs->len, xxs->lps, xxs->lpe,
ih.ins[i].loop_size > 1 ? 'L' : ' ', sub->vol, sub->fin >> 4);
}
if (libxmp_init_pattern(mod) < 0) {
return -1;
}
/* Load and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
for (j = 0; j < 0x100; j++) {
event = &EVENT (i, j & 0x3, j >> 2);
hio_read(ims_event, 1, 3, f);
/* Event format:
*
* 0000 0000 0000 0000 0000 0000
* |\ / \ / \ / \ /
* | note ins fx parameter
* ins
*
* 0x3f is a blank note.
*/
event->note = ims_event[0] & 0x3f;
if (event->note != 0x00 && event->note != 0x3f)
event->note += xpo + 12;
else
event->note = 0;
event->ins = ((ims_event[0] & 0x40) >> 2) | MSN(ims_event[1]);
event->fxt = LSN(ims_event[1]);
event->fxp = ims_event[2];
libxmp_disable_continue_fx (event);
/* According to Asle:
* ``Just note that pattern break effect command (D**) uses
* HEX value in UNIC format (while it is DEC values in PTK).
* Thus, it has to be converted!''
*
* Is this valid for IMS as well? --claudio
*/
if (event->fxt == 0x0d)
event->fxp = (event->fxp / 10) << 4 | (event->fxp % 10);
}
}
m->period_type = PERIOD_MODRNG;
/* Load samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
if (!mod->xxs[i].len)
continue;
if (libxmp_load_sample(m, f, 0, &mod->xxs[i], NULL) < 0)
return -1;
}
return 0;
}

196
libxmp/src/loaders/it.h Normal file
View File

@ -0,0 +1,196 @@
/* Extended Module Player
* Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBXMP_LOADERS_IT_H
#define LIBXMP_LOADERS_IT_H
#include "loader.h"
/* IT flags */
#define IT_STEREO 0x01
#define IT_VOL_OPT 0x02 /* Not recognized */
#define IT_USE_INST 0x04
#define IT_LINEAR_FREQ 0x08
#define IT_OLD_FX 0x10
#define IT_LINK_GXX 0x20
#define IT_MIDI_WHEEL 0x40
#define IT_MIDI_CONFIG 0x80
/* IT special */
#define IT_HAS_MSG 0x01
#define IT_EDIT_HISTORY 0x02
#define IT_HIGHLIGHTS 0x04
#define IT_SPEC_MIDICFG 0x08
/* IT instrument flags */
#define IT_INST_SAMPLE 0x01
#define IT_INST_16BIT 0x02
#define IT_INST_STEREO 0x04
#define IT_INST_LOOP 0x10
#define IT_INST_SLOOP 0x20
#define IT_INST_BLOOP 0x40
#define IT_INST_BSLOOP 0x80
/* IT sample flags */
#define IT_SMP_SAMPLE 0x01
#define IT_SMP_16BIT 0x02
#define IT_SMP_STEREO 0x04 /* unsupported */
#define IT_SMP_COMP 0x08 /* unsupported */
#define IT_SMP_LOOP 0x10
#define IT_SMP_SLOOP 0x20
#define IT_SMP_BLOOP 0x40
#define IT_SMP_BSLOOP 0x80
/* IT sample conversion flags */
#define IT_CVT_SIGNED 0x01
#define IT_CVT_BIGEND 0x02 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_DIFF 0x04 /* Compressed sample flag */
#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_ADPCM 0xff /* Special: always indicates Modplug ADPCM4 */
/* IT envelope flags */
#define IT_ENV_ON 0x01
#define IT_ENV_LOOP 0x02
#define IT_ENV_SLOOP 0x04
#define IT_ENV_CARRY 0x08
#define IT_ENV_FILTER 0x80
struct it_file_header {
uint32 magic; /* 'IMPM' */
uint8 name[26]; /* ASCIIZ Song name */
uint8 hilite_min; /* Pattern editor highlight */
uint8 hilite_maj; /* Pattern editor highlight */
uint16 ordnum; /* Number of orders (must be even) */
uint16 insnum; /* Number of instruments */
uint16 smpnum; /* Number of samples */
uint16 patnum; /* Number of patterns */
uint16 cwt; /* Tracker ID and version */
uint16 cmwt; /* Format version */
uint16 flags; /* Flags */
uint16 special; /* More flags */
uint8 gv; /* Global volume */
uint8 mv; /* Master volume */
uint8 is; /* Initial speed */
uint8 it; /* Initial tempo */
uint8 sep; /* Panning separation */
uint8 pwd; /* Pitch wheel depth */
uint16 msglen; /* Message length */
uint32 msgofs; /* Message offset */
uint32 rsvd; /* Reserved */
uint8 chpan[64]; /* Channel pan settings */
uint8 chvol[64]; /* Channel volume settings */
};
struct it_instrument1_header {
uint32 magic; /* 'IMPI' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 flags; /* Instrument flags */
uint8 vls; /* Volume loop start */
uint8 vle; /* Volume loop end */
uint8 sls; /* Sustain loop start */
uint8 sle; /* Sustain loop end */
uint16 rsvd1; /* Reserved */
uint16 fadeout; /* Fadeout (release) */
uint8 nna; /* New note action */
uint8 dnc; /* Duplicate note check */
uint16 trkvers; /* Tracker version */
uint8 nos; /* Number of samples */
uint8 rsvd2; /* Reserved */
uint8 name[26]; /* ASCIIZ Instrument name */
uint8 rsvd3[6]; /* Reserved */
uint8 keys[240];
uint8 epoint[200];
uint8 enode[50];
};
struct it_instrument2_header {
uint32 magic; /* 'IMPI' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 nna; /* New Note Action */
uint8 dct; /* Duplicate Check Type */
uint8 dca; /* Duplicate Check Action */
uint16 fadeout;
uint8 pps; /* Pitch-Pan Separation */
uint8 ppc; /* Pitch-Pan Center */
uint8 gbv; /* Global Volume */
uint8 dfp; /* Default pan */
uint8 rv; /* Random volume variation */
uint8 rp; /* Random pan variation */
uint16 trkvers; /* Not used: tracked version */
uint8 nos; /* Not used: number of samples */
uint8 rsvd1; /* Reserved */
uint8 name[26]; /* ASCIIZ Instrument name */
uint8 ifc; /* Initial filter cutoff */
uint8 ifr; /* Initial filter resonance */
uint8 mch; /* MIDI channel */
uint8 mpr; /* MIDI program */
uint16 mbnk; /* MIDI bank */
uint8 keys[240];
};
struct it_envelope_node {
int8 y;
uint16 x;
};
struct it_envelope {
uint8 flg; /* Flags */
uint8 num; /* Number of node points */
uint8 lpb; /* Loop beginning */
uint8 lpe; /* Loop end */
uint8 slb; /* Sustain loop beginning */
uint8 sle; /* Sustain loop end */
struct it_envelope_node node[25];
uint8 unused;
};
struct it_sample_header {
uint32 magic; /* 'IMPS' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 gvl; /* Global volume for instrument */
uint8 flags; /* Sample flags */
uint8 vol; /* Volume */
uint8 name[26]; /* ASCIIZ sample name */
uint8 convert; /* Sample flags */
uint8 dfp; /* Default pan */
uint32 length; /* Length */
uint32 loopbeg; /* Loop begin */
uint32 loopend; /* Loop end */
uint32 c5spd; /* C 5 speed */
uint32 sloopbeg; /* SusLoop begin */
uint32 sloopend; /* SusLoop end */
uint32 sample_ptr; /* Sample pointer */
uint8 vis; /* Vibrato speed */
uint8 vid; /* Vibrato depth */
uint8 vir; /* Vibrato rate */
uint8 vit; /* Vibrato waveform */
};
int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215);
int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215);
#endif /* LIBXMP_LOADERS_IT_H */

1442
libxmp/src/loaders/it_load.c Normal file

File diff suppressed because it is too large Load Diff

243
libxmp/src/loaders/itsex.c Normal file
View File

@ -0,0 +1,243 @@
#ifndef LIBXMP_CORE_DISABLE_IT
/* Public domain IT sample decompressor by Olivier Lapicque */
#include "loader.h"
#include "it.h"
static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n, int *err)
{
uint32 retval = 0;
int i = n;
int bnum = *bitnum;
uint32 bbuf = *bitbuf;
if (n > 0 && n <= 32) {
do {
if (bnum == 0) {
if (hio_eof(ibuf)) {
*err = EOF;
return 0;
}
bbuf = hio_read8(ibuf);
bnum = 8;
}
retval >>= 1;
retval |= bbuf << 31;
bbuf >>= 1;
bnum--;
i--;
} while (i != 0);
i = n;
*bitnum = bnum;
*bitbuf = bbuf;
} else {
/* Invalid shift value. */
*err = -2;
return 0;
}
return (retval >> (32 - i));
}
int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
{
/* uint32 size = 0; */
uint32 block_count = 0;
uint32 bitbuf = 0;
int bitnum = 0;
uint8 left = 0, temp = 0, temp2 = 0;
uint32 d, pos;
int err = 0;
while (len) {
if (!block_count) {
block_count = 0x8000;
/*size =*/ hio_read16l(src);
left = 9;
temp = temp2 = 0;
bitbuf = bitnum = 0;
}
d = block_count;
if (d > len)
d = len;
/* Unpacking */
pos = 0;
do {
uint16 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
if (err != 0)
return -1;
if (left < 7) {
uint32 i = 1 << (left - 1);
uint32 j = bits & 0xffff;
if (i != j)
goto unpack_byte;
bits = (read_bits(src, &bitbuf, &bitnum, 3, &err)
+ 1) & 0xff;
if (err != 0)
return -1;
left = ((uint8)bits < left) ? (uint8)bits :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left < 9) {
uint16 i = (0xff >> (9 - left)) + 4;
uint16 j = i - 8;
if ((bits <= j) || (bits > i))
goto unpack_byte;
bits -= j;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left >= 10)
goto skip_byte;
if (bits >= 256) {
left = (uint8) (bits + 1) & 0xff;
goto next;
}
unpack_byte:
if (left < 8) {
uint8 shift = 8 - left;
signed char c = (signed char)(bits << shift);
c >>= shift;
bits = (uint16) c;
}
bits += temp;
temp = (uint8)bits;
temp2 += temp;
dst[pos] = it215 ? temp2 : temp;
skip_byte:
pos++;
next:
/* if (slen <= 0)
return -1 */;
} while (pos < d);
/* Move On */
block_count -= d;
len -= d;
dst += d;
}
return 0;
}
int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
{
/* uint32 size = 0; */
uint32 block_count = 0;
uint32 bitbuf = 0;
int bitnum = 0;
uint8 left = 0;
int16 temp = 0, temp2 = 0;
uint32 d, pos;
int err = 0;
while (len) {
if (!block_count) {
block_count = 0x4000;
/*size =*/ hio_read16l(src);
left = 17;
temp = temp2 = 0;
bitbuf = bitnum = 0;
}
d = block_count;
if (d > len)
d = len;
/* Unpacking */
pos = 0;
do {
uint32 bits = read_bits(src, &bitbuf, &bitnum, left, &err);
if (err != 0)
return -1;
if (left < 7) {
uint32 i = 1 << (left - 1);
uint32 j = bits;
if (i != j)
goto unpack_byte;
bits = read_bits(src, &bitbuf, &bitnum, 4, &err) + 1;
if (err != 0)
return -1;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left < 17) {
uint32 i = (0xffff >> (17 - left)) + 8;
uint32 j = (i - 16) & 0xffff;
if ((bits <= j) || (bits > (i & 0xffff)))
goto unpack_byte;
bits -= j;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left >= 18)
goto skip_byte;
if (bits >= 0x10000) {
left = (uint8)(bits + 1) & 0xff;
goto next;
}
unpack_byte:
if (left < 16) {
uint8 shift = 16 - left;
int16 c = (int16)(bits << shift);
c >>= shift;
bits = (uint32) c;
}
bits += temp;
temp = (int16)bits;
temp2 += temp;
dst[pos] = (it215) ? temp2 : temp;
skip_byte:
pos++;
next:
/* if (slen <= 0)
return -1 */;
} while (pos < d);
/* Move On */
block_count -= d;
len -= d;
dst += d;
if (len <= 0)
break;
}
return 0;
}
#endif /* LIBXMP_CORE_DISABLE_IT */

Some files were not shown because too many files have changed in this diff Show More