code:
          Scream Tracker 3.01 BETA File Formats And Mixing Info
          =====================================================

This document finally containts the OFFICIAL information on s3m format and
much more. There might be some errors though I've even checked this a few
times, so if something seems weird, don't just blindly believe it but think
first if it could be just a typo or something.

-----------------------------------------------------------------------------
What is the S3M file format?
What is the samplefile format?
What is the adlib instrument format?

        The first table describes the S3M header. All other blocks are
        pointer to by pointers, so in theory they could be anywhere in
        the file. However, the practical standard order is:
        - header
        - instruments in order
        - patterns in order
        - samples in order

        Next the instrument header is described. It is stored to S3M
        for each instrument and also saved to the start of all samples
        saved from ST3. Same header is also used by Advance Digiplayer.

        The third part is the description of the packed pattern format.

                                S3M Module header
          0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
        ���������������������������������������������������������������Ŀ
  0000: � Song name, max 28 chars (end with NUL (0))                    �
        ���������������������������������������������������������������Ĵ
  0010: �                                               �1Ah�Typ� x � x �
        ���������������������������������������������������������������Ĵ
  0020: �OrdNum �InsNum �PatNum � Flags � Cwt/v �  Ffv  �'S'�'C'�'R'�'M'�
        ���������������������������������������������������������������Ĵ
  0030: �g.v�i.s�i.t�m.v� x � x � x � x � x � x � x � x � x � x �Special�
        ���������������������������������������������������������������Ĵ
  0040: �Channel settings for 32 channels, 255=unused,+128=disabled     �
        ���������������������������������������������������������������Ĵ
  0050: �                                                               �
        ���������������������������������������������������������������Ĵ
  0060: �Orders; length=OrdNum (should be even)                         �
        ���������������������������������������������������������������Ĵ
  xxx1: �Parapointers to instruments; length=InsNum*2                   �
        ���������������������������������������������������������������Ĵ
  xxx2: �Parapointers to patterns; length=PatNum*2                      �
        ���������������������������������������������������������������Ĵ
        xxx1=70h+orders
        xxx2=70h+orders+instruments*2

        Parapointers to file offset Y is (Y-Offset of file header)/16.
        You could think of parapointers as segments relative to the
        start of the S3M file.

        Type    = File type: 16=ST3 module
        Ordnum  = Number of orders in file (should be even!)
	Insnum	= Number of instruments in file
	Patnum	= Number of patterns in file
        Cwt/v   = Created with tracker / version: &0xfff=version, >>12=tracker
                        ST3.00:0x1300
                        ST3.01:0x1301
	Ffv	= File format version;
                        1=old version used long ago (samples signed)
                        2=standard (samples unsigned)
        Flags   =  [ These are old flags for Ffv1. Not supported in ST3.01
                   |  +1:st2vibrato
                   |  +2:st2tempo
                   |  +4:amigaslides
                   | +32:enable filter/sfx with sb
                   ]
                    +8: 0vol optimizations
                          Automatically turn off looping notes whose volume
                          is zero for >2 note rows.
                   +16: amiga limits
                          Disallow any notes that go beond the amiga hardware
                          limits (like amiga does). This means that sliding
                          up stops at B#5 etc. Also affects some minor amiga
                          compatibility issues.
                  +128: special custom data in file
        Special = pointer to special custom data (not used by ST3.01)
	g.v	= global volume (see next section)
	m.v	= master volume (see next section) 7 lower bits
                  bit 8: stereo(1) / mono(0)
	i.s	= initial speed (command A)
	i.t    	= initial tempo (command T)

        Channel settings:
          bit 8: channel enabled
        bit 0-7: channel type
                 0..7   : Left Sample Channel 1-8
                 8..15  : Right Sample Channel 1-8
                 16..31 : Adlib channels (9 melody + 5 drums)

        Global volume directly divides the volume numbers used. So
        if the module has a note with volume 48 and master volume
        is 32, the note will be played with volume 24. This affects
        both Gravis & SoundBlasters.

        Master volume only affects the SoundBlaster. It controls
        the amount of sample multiplication (see mixing section
        of this doc). The bigger the value the bigger the output
        volume (and thus quality) will be. However if the value
        is too big, the mixer may have to clip the output to
        fit the 8 bit output stream. The default value works
        pretty well. Note that in stereo, the mastermul is
        internally multiplied by 11/8 inside the player since
        there is generally more room in the output stream.

        Order list lists the order in which to play the patterns. 255=--
        is the end of tune mark and 254=++ is just a marker that is
        skipped.

                        Digiplayer/ST3 samplefileformat
          0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
        ���������������������������������������������������������������Ŀ
  0000: �[T]� Dos filename (12345678.ABC)                   �    MemSeg �
        ���������������������������������������������������������������Ĵ
  0010: �Length �HI:leng�LoopBeg�HI:LBeg�LoopEnd�HI:Lend�Vol� x �[P]�[F]�
        ���������������������������������������������������������������Ĵ
  0020: �C2Spd  �HI:C2sp� x � x � x � x �Int:Gp �Int:512�Int:lastused   �
        ���������������������������������������������������������������Ĵ
  0030: � Sample name, 28 characters max... (incl. NUL)                 �
        ���������������������������������������������������������������Ĵ
  0040: � ...sample name...                             �'S'�'C'�'R'�'S'�
        ���������������������������������������������������������������Ĵ
  xxxx:	sampledata

        Length / LoopBegin / LoopEnd are all 32 bit parameters although
        ST3 only support file sizes up to 64,000 bytes. Files bigger
        than that are clipped to 64,000 bytes when loaded to ST3. NOTE
        that LoopEnd points to one byte AFTER the end of the sample,
        so LoopEnd=100 means that byte 99.9999 (fixed) is the last one
        played.
        C2Spd  = Herz for middle C. ST3 only uses lower 16 bits.
        Vol    = Default volume 0..64
        Memseg = Pointer to sampledata
                 Inside a sample or S3M, MemSeg tells the parapointer to
                 the actual sampledata. In files all 24 bits are used.
                 In memory the value points to the actual sample segment
                 or Fxxx if sample is in EMS under handle xxx. In memory
                 the first memseg byte is overwritten with 0 to create
                 the dos filename terminator nul.
        Int:Gp = Internal: Address of sample in gravis memory /32
                 (only used while module in memory)
        Int:512= Internal: flags for soundblaster loop expansion
                 (only used while module in memory)
        Int:las= Internal: last used position (only works with sb)
                 (only used while module in memory)
        [T]ype   1=Sample, 2=adlib melody, 3+=adlib drum (see below for
                 adlib structure)
	[F]lags, +1=loop on
		 +2=stereo (after Length bytes for LEFT channel,
		 	  another Length bytes for RIGHT channel)
                 +4=16-bit sample (intel LO-HI byteorder)
                 (+2/+4 not supported by ST3.01)
        [P]ack   0=unpacked, 1=DP30ADPCM packing (not used by ST3.01)

                              adlib instrument format
          0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
        ���������������������������������������������������������������Ŀ
  0000: �[T]� Dos filename (12345678.123)                   �00h�00h�00h�
        ���������������������������������������������������������������Ĵ
  0010: �D00�D01�D02�D03�D04�D05�D06�D07�D08�D09�D0A�D0B�Vol�Dsk� x � x �
        ���������������������������������������������������������������Ĵ
  0020: �C2Spd  �HI:C2sp� x � x � x � x � x � x � x � x � x � x � x � x �
        ���������������������������������������������������������������Ĵ
  0030: � Sample name, 28 characters max... (incl. NUL)                 �
        ���������������������������������������������������������������Ĵ
  0040: � ...sample name...                             �'S'�'C'�'R'�'I'�
        ���������������������������������������������������������������Ĵ

        [T]ype  = 2:amel 3:abd 4:asnare 5:atom 6:acym 7:ahihat
        C2Spd   = 'Herz' for middle C. ST3 only uses lower 16 bits.
                  Actually this is a modifier since there is no
                  clear frequency for adlib instruments. It scales
                  the note freq sent to adlib.
        D00..D0B contains the adlib instrument specs packed like this:
        modulator:                                              carrier:
        D00=[freq.muliplier]+[?scale env.]*16+[?sustain]*32+    =D01
                [?pitch vib]*64+[?vol.vib]*128
	D02=[63-volume]+[levelscale&1]*128+[l.s.&2]*64		=D03
	D04=[attack]*16+[decay]					=D05
	D06=[15-sustain]*16+[release]				=D07
	D08=[wave select]					=D09
	D0A=[modulation feedback]*2+[?additive synthesis]
	D0B=unused

                          packed pattern format
          0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
        ���������������������������������������������������������������Ŀ
  0000: �Length � packed data, see below...
        ���������������������������������������������������������������Ĵ

        Length = length of packed pattern

        Unpacked pattern is always 32 channels by 64 rows. Below
        is the unpacked format st uses for reference:
          Unpacked Internal memoryformat for patterns (not used in files):
          NOTE: each channel takes 320 bytes, rows for each channel are
                sequential, so one unpacked pattern takes 10K.
          byte 0 - Note; hi=oct, lo=note, 255=empty note,
                   254=key off (used with adlib, with samples stops smp)
          byte 1 - Instrument      ;0=..
          byte 2 - Volume          ;255=..
          byte 3 - Special command ;255=..
          byte 4 - Command info    ;

        Packed data consits of following entries:
        BYTE:what  0=end of row
                   &31=channel
                   &32=follows;  BYTE:note, BYTE:instrument
                   &64=follows;  BYTE:volume
                   &128=follows; BYTE:command, BYTE:info

        So to unpack, first read one byte. If it's zero, this row is
        done (64 rows in entire pattern). If nonzero, the channel
        this entry belongs to is in BYTE AND 31. Then if bit 32
        is set, read NOTE and INSTRUMENT (2 bytes). Then if bit
        64 is set read VOLUME (1 byte). Then if bit 128 is set
        read COMMAND and INFO (2 bytes).

        For information on commands / how st3 plays them, see the
        manual.

-----------------------------------------------------------------------------
What is the Stmik300old format?
What is the STIMPORT file format?
What is the SIMPLEXFILE format?

        The old stmik 300 (never published because it has no
        usable interface) want's that the samples in the module
        are pre-extended for soundblaster. This means that
        samples have an extra 512 bytes of loop after their
        loopend (or silence if no loop). The save option for
        old stmik does this extension. Otherwise the fileformat
        is the same as for a normal S3M.

        STIMPORT file format is supposed to be an easy way of
        imputting weird data to st3. That is, it should be
        easy to convert something to STIMPORT format. The format
        goes like this:

          0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
        ���������������������������������������������������������������Ŀ
  0000: �'S'�'T'�'I'�'M'�'P'�'O'�'R'�'T'�i.s� x � x � x � x � x � x � x �
        ���������������������������������������������������������������Ĵ
  0010: �Notedata...
        ���������������������������������������������������������������Ĵ
  xxxx: �Instruments...
        ���������������������������������������������������������������Ĵ

        There are no patterns or orders, just a continuos note stream
        of following structures:
          0    1    2    3    4
        ������������������������Ŀ
        �Chan�Note�Inst�Cmnd�Info�
        ��������������������������
        Chan is the channel number for the note. Value 0 stands
        for next row. Value 255 stands for end of note stream.
        Note/Inst/Cmnd/Info are like in the unpacked memory pattern
        (see previous section)

        After the notedata there can be instruments (optional)
        Every instrument consits of the following block:
          0    1    2    3    4
        ����������������������������������������������������������
        �Inst�Loop�LoopBeg  �LoopEnd  �Length   � Sampledata...
        ����������������������������������������������������������
        Inst = number of instrument to load (0=end of instruments)
        Loop = 1=loop enabled
        LoopBeg/LoopEnd/Length = guess
        Sampledata = raw sampledata for Length bytes.

        SIMPLEXFORMAT has been used for some adlib musics. Dunno
        if it's useful to you, but the st3 saves it like this:

        First is stores the 32 first instruments (80 bytes each,
        just like in a S3M). Then it stores the raw notedata
        for the entire song. This means it goes through the
        order list and saves all the patererns in that order
        unpacked. The resulting file will be BIG. The idea
        is that it should be fairly easy to convert to something
        from this.

-----------------------------------------------------------------------------
What is C2SPD?
How to calculate the note frequencies like ST3?
How does ST3 mix depending on master volume?

        Finetuning (C2SPD) is actually the frequency in herz for
        the note C4. Why is it C2SPD? Well, originally in ST2
        the middle note was C2 and the name stuck. Later in ST3
        the middle note was raised to C4 for more octaves... So
        actually C2SPD should be called C4SPD...

        Table for note frequencies used by ST3:

	  note:  C    C#   D    D#   E    F    F#   G    G#   A    A#   B
	period: 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,0960,0907

	middle octave is 4.

			 8363 * 16 * ( period(NOTE) >> octave(NOTE) )
	note_st3period = --------------------------------------------
	 		 middle_c_finetunevalue(INSTRUMENT)

	note_amigaperiod = note_st3period / 4

	note_herz=14317056 / note_st3period

        Note that ST3 uses period values that are 4 times larger than the
        amiga to allow for extra fine slides (which are 4 times finer
        than normal fine slides).

        How ST3 mixes:

        1) volumetable is created in the following way:

        > volumetable[volume][sampledata]=volume*(sampledata-128)/64;

        NOTE: sampledata in memory is unsigned in ST3, so the -128 in the
              formula converts it so that the volumetable output is signed.

        2) postprocessing table is created with this pseudocode:

        > z=mastervol&127;
        > if(z<0x10) z=0x10;
        > c=2048*16/z;
        > a=(2048-c)/2;
        > b=a+c;
        >                     { 0                , if x < a
        > posttable[x+1024] = { (x-a)*256/(b-a)  , if a <= x < b
        >                     { 255              , if x > b

        3) mixing the samples

        output=1024
        for i=0 to number of channels
                output+=volumetable[volume*globalvolume/64][sampledata];
        next
        realoutput=posttable[output]

        This is how the mixing is done in theory. In practice it's a bit
        different for speed reasons, but the result is the same.

-----------------------------------------------------------------------------
That's it. If there are any more questions, that's too bad :-) If you have
problems with the S3M format, try to contact somone who already supports
it (there is quite a lot of support for the S3M already, so that shouldn't
be too hard...) Good luck for reading / writing Scream Tracker files.


code:
Axx Set speed to xx (default 06)
Bxx Jump to order xx
Cxx Break pattern to row xx
D0y.Volume slide down by y
Dx0.Volume slide up by x
DFy.Fine volume slide down by y(!=0,F)
DxF.Fine volume slide up by x(!=0,F)
Exx.Slide down by xx (range 00..DF)
EFx.Fine slide down by x
EEx.Extra fine slide down by x
Fxx.Slide up by xx (range 00..DF)
FFx.Fine slide up by x
FEx.Extra fine slide up by x
Gxx.Tone portamento, speed xx
Hxy.Vibrato, speed x and depth y
Ixy Tremor with ontime x and offtime y
Jxy.Arpeggio; adds halfnotes: x,y,0
Kxy.Dual command: H00 & Dxy
Lxy.Dual command: G00 & Dxy
Mxx --
Nxx --
Oxx Set SampleOffset=xx00h
Pxx --
Qxy.Retrig note (see right)
Rxy.Tremolo with speed x and depth y
Sxy Special commands (see top-right)
Txx Tempo=xx (range 20..FF, def:7dh)
Uxx Fine vibrato (see Hxx)
Vxx Set global volume (0..40)
Wxx --
Xxx unused amiga command 8xx ( Set panning in modern software (X00 left, XFF right) )
Zxx --

S0x Set filter (1/0/F=on/off/for) (ignored)
S1x Set glissando control x=0/1
S2x Set finetune
S3x Set vibrato waveform to x
S4x Set tremolo waveform to x
S5x --
S6x --
S7x --
S8x Set pan position (GRAVIS only in original ST3, ok in modern software)
S9x --
SAx old stereo control (ignored)
SBx Loop (x=cnt)/StartLoop (x=0)
SCx NoteCut in x frames
SDx NoteDelay for x frames
SEx PatternDelay for x notes
SFx FunkRepeat, x=speed (ignored)

Vibrato waveforms (S3x & S4x)
    0:sinewave    1:ramp down
    2:squarewave  3:random
    +4:don't reset waveform

Retrig note infobyte (Qxy)
    y=frames between retrigs
    x=volume change per retrig:
    0:0    4:-8   8:?   C:8
    1:-1   5:-16  9:1   D:16
    2:-2   6:*2/3 A:2   E:*3/2
    3:-4   7:*1/2 B:4   F:*2

repo_name: pgimeno/megazeux

license: mit


code:
##
# gdm2s3m Makefile fragment
##

.PHONY: ${gdm2s3m}_clean

.SUFFIXES: .c

gdm2s3m_src = contrib/gdm2s3m/src
gdm2s3m_obj = contrib/gdm2s3m/src/.build

gdm2s3m_cflags := -W -Wmissing-format-attribute -Wpointer-arith -Wcast-align
gdm2s3m_cflags += -Wwrite-strings -I${gdm2s3m_src}

gdm2s3m_objs = \
    ${gdm2s3m_obj}/error.o ${gdm2s3m_obj}/gdm.o \
    ${gdm2s3m_obj}/s3m.o ${gdm2s3m_obj}/utility.o \
    ${gdm2s3m_obj}/gdm2s3m.o

${gdm2s3m_obj}/%.o: ${gdm2s3m_src}/%.c
	$(if ${V},,@echo "  CC      " $<)
	${CC} -MD ${CFLAGS} ${gdm2s3m_cflags} -c $< -o $@

-include $(gdm2s3m_objs:.o=.d)

${gdm2s3m_objs}: $(filter-out $(wildcard ${gdm2s3m_obj}), ${gdm2s3m_obj})

gdm2s3m_clean:
	$(if ${V},,@echo "  RM      " ${gdm2s3m_obj})
	${RM} -r ${gdm2s3m_obj}


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#include <stdio.h>

#include "error.h"

void error_handle (const char *file, int line, error_code error)
{
  char msg[256];

  switch (error) {
    case FILE_OPEN:
      sprintf (msg, "problem opening file");
      break;

    case FILE_INVALID:
      sprintf (msg, "invalid file format");
      break;

    case OUT_OF_DATA:
      sprintf (msg, "conversion failure");
      break;

    case CONVERT_ERROR:
      sprintf (msg, "convertor ran out of data");
      break;

    default:
      sprintf (msg, "unknown error");
      break;
  }

  /* echo error string to output */
  fprintf (stderr, "%s:%d: %s.\n", file, line, msg);

  /* abnormal quit */
  exit (-1);
}


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __ERROR_H
#define __ERROR_H

#include "types.h"

__G_BEGIN_DECLS

#define error(x) error_handle(__FILE__, __LINE__, x);

/**
 * possible errors
 */

typedef enum {
  FILE_OPEN,
  FILE_INVALID,
  CONVERT_ERROR,
  OUT_OF_DATA
} error_code;

/* function prototypes */
void error_handle (const char *file, int line, error_code error);

__G_END_DECLS

#endif /* __ERROR_H */


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "gdm.h"
#include "error.h"
#include "utility.h"

struct GDM_file *load_gdm (uint8_t *stream, size_t stream_len)
{
  uint8_t *backup = stream, *trace, *trace2, magic[4];
  struct GDM_header *header;
  struct GDM_pattern *pattern;
  struct GDM_samhdr *sample;
  struct GDM_file *gdm;
  uint32_t address;
  int i;

  /* allocate gdm file structure */
  gdm = malloc (sizeof (struct GDM_file));
  memset (gdm, 0, sizeof (struct GDM_file));

  /* level of indirection */
  header = &gdm->header;

  /* extract header */
  check_s_to_a (backup, stream_len, magic, &stream, 4);

  /* check it's a valid file */
  if (strncmp ((char *)magic, GDM_MAGIC, 4) != 0)
    error (FILE_INVALID);

  /* get artist and title */
  check_s_to_a (backup, stream_len, header->title, &stream, 32);
  check_s_to_a (backup, stream_len, header->artist, &stream, 32);

  /* skip junk */
  stream += 3 + 4;

  /* get file version */
  check_s_to_a (backup, stream_len, &header->version, &stream, 2);
  CHECK_ENDIAN_16 (&header->version);

  /* get tracker and version */
  check_s_to_a (backup, stream_len, &header->tracker, &stream, 2);
  check_s_to_a (backup, stream_len, &header->tracker_ver, &stream, 2);
  CHECK_ENDIAN_16 (&header->tracker);
  CHECK_ENDIAN_16 (&header->tracker_ver);

  /* get panning map */
  check_s_to_a (backup, stream_len, &header->panning, &stream, 32);

  /* get global volume, tempo and bpm */
  check_s_to_a (backup, stream_len, &header->global_vol, &stream, 1);
  check_s_to_a (backup, stream_len, &header->bpm, &stream, 1);
  check_s_to_a (backup, stream_len, &header->tempo, &stream, 1);

  /* get original format */
  check_s_to_a (backup, stream_len, &header->origfmt, &stream, 2);
  CHECK_ENDIAN_16 (&header->origfmt);

  /* ORDER HEADER ---------------------------------------------------------- */
  check_s_to_a (backup, stream_len, &address, &stream, 4);
  check_s_to_a (backup, stream_len, &gdm->order_len, &stream, 1);
  CHECK_ENDIAN_32 (&address);

  /* stupid gdm bug */
  gdm->order_len++;

  /* put stream at order offset */
  trace = (uint8_t *) (backup + address);

  /* allocate space for orders */
  gdm->orders.patterns = malloc (gdm->order_len);

  /* copy orders */
  memcpy (gdm->orders.patterns, trace, gdm->order_len);

  /* PATTERN SEGMENT ------------------------------------------------------- */
  check_s_to_a (backup, stream_len, &address, &stream, 4);
  check_s_to_a (backup, stream_len, &gdm->numpatterns, &stream, 1);
  CHECK_ENDIAN_32 (&address);

  /* stupid gdm bug */
  gdm->numpatterns++;

  /* put stream at pattern offset */
  trace = (uint8_t *) (backup + address);

  /* allocate pattern space */
  gdm->patterns = malloc (sizeof (struct GDM_pattern) * gdm->numpatterns);

  /* read in encoded pattern data */
  for (i = 0; i < gdm->numpatterns; i++) {
    pattern = &gdm->patterns[i];

    /* get length of data */
    check_s_to_a (backup, stream_len, &pattern->length, &trace, 2);
    CHECK_ENDIAN_16 (&pattern->length);

    /* remove top two bytes */
    pattern->length -= 2;

    /* allocate data space */
    pattern->data = malloc (pattern->length);

    /* get pattern data */
    memcpy (pattern->data, trace, pattern->length);

    /* move along */
    trace += pattern->length;
  }

  /* SAMPLE HEADERS -------------------------------------------------------- */
  check_s_to_a (backup, stream_len, &address, &stream, 4);
  CHECK_ENDIAN_32 (&address);

  /* move stream along to sample header */
  trace = (uint8_t *) (backup + address);

  /* get sample data segment */
  check_s_to_a (backup, stream_len, &address, &stream, 4);
  check_s_to_a (backup, stream_len, &gdm->numsamples, &stream, 1);
  CHECK_ENDIAN_32 (&address);

  /* stupid gdm bug */
  gdm->numsamples++;

  /* set up initial offset */
  trace2 = (uint8_t *) (backup + address);

  /* allocate sample space */
  gdm->samples = malloc (sizeof (struct GDM_sample) * gdm->numsamples);
  memset (gdm->samples, 0, sizeof (struct GDM_sample) * gdm->numsamples);

  /* read in sample headers and copy sample data */
  for (i = 0; i < gdm->numsamples; i++) {
    sample = &gdm->samples[i].header;

    /* get sample name */
    check_s_to_a (backup, stream_len, sample->name, &trace, 32);

    /* get sample filename */
    check_s_to_a (backup, stream_len, sample->filename, &trace, 12);

    /* skip EMS handle (should be zero) */
    trace++;

    /* get sample data length */
    check_s_to_a (backup, stream_len, &sample->length, &trace, 4);
    CHECK_ENDIAN_32 (&sample->length);

    /* get loop beginning/end */
    check_s_to_a (backup, stream_len, &sample->begin, &trace, 4);
    check_s_to_a (backup, stream_len, &sample->end, &trace, 4);
    CHECK_ENDIAN_32 (&sample->begin);
    CHECK_ENDIAN_32 (&sample->end);

    /* get sample flags */
    check_s_to_a (backup, stream_len, &sample->flags, &trace, 1);

    /* get rate data */
    check_s_to_a (backup, stream_len, &sample->rate, &trace, 2);
    CHECK_ENDIAN_16 (&sample->rate);

    /* get volume */
    check_s_to_a (backup, stream_len, &sample->volume, &trace, 1);

    /* get pan */
    check_s_to_a (backup, stream_len, &sample->pan, &trace, 1);

    /* allocate space for sample data */
    gdm->samples[i].data = malloc (sample->length);

    /* copy sample data */
    memcpy (gdm->samples[i].data, trace2, sample->length);

    /* move sample data pointer along */
    trace2 += sample->length;
  }

  /* gdm structure */
  return gdm;
}

#if 0
void info_gdm (struct GDM_file *gdm)
{
  struct GDM_header *header = &gdm->header;
  struct GDM_samhdr *samhdr;
  int i;

  union version {
    uint16_t v;
    struct {
      uint8_t maj;
      uint8_t min;
    } ver;
  } version;

  /* prepare version dump */
  memcpy (&version, &header->tracker_ver, 2);

  /* basic information */
  PRINT_OUT ("title:\t\t\t%s\n", header->title);
  PRINT_OUT ("artist:\t\t\t%s\n", header->artist);
  PRINT_DEBUG ("version:\t\t%u\n", header->version);
  PRINT_DEBUG ("tracker:\t\t%u\n", header->tracker);
  PRINT_DEBUG ("tracker version:\t%u.%u\n", version.ver.maj, version.ver.min);
  PRINT_OUT ("global vol:\t\t%u\n", header->global_vol);
  PRINT_OUT ("tempo/speed:\t\t%u/%u\n", header->tempo, header->bpm);

  /* dump originating format */
  PRINT_OUT ("original format:\t");

  switch (header->origfmt) {
    case 1: PRINT_OUT ("MOD\n"); break;
    case 2: PRINT_OUT ("MTM\n"); break;
    case 3: PRINT_OUT ("S3M\n"); break;
    case 4: PRINT_OUT ("669\n"); break;
    case 5: PRINT_OUT ("FAR\n"); break;
    case 6: PRINT_OUT ("ULT\n"); break;
    case 7: PRINT_OUT ("STM\n"); break;
    case 8: PRINT_OUT ("MED\n"); break;
  }

#ifdef DEBUG
  /* panning map */
  PRINT_OUT ("\n\t\tL      MM      R  S?:\n");

  for (i = 0; i < 32; i++)
    if (header->panning[i] != 255) {
      /* channel's active */
      PRINT_OUT (" chan %d:\t", i);

      /* padding */
      if (header->panning[i] != 16)
      {
        for (int j = 0; j < header->panning[i]; j++)
          PRINT_OUT (" ");
      }
      else
        PRINT_OUT ("                  ");

      /* and a marker.. */
      PRINT_OUT ("|\n");
    }
#endif

  PRINT_OUT ("\n");

  /* dump sample information */
  for (i = 0; i < gdm->numsamples; i++) {
    samhdr = &gdm->samples[i].header;

    PRINT_OUT ("name:\t\t\t%s\n", samhdr->name);
    PRINT_OUT ("filename:\t\t%s\n", samhdr->filename);
    PRINT_OUT ("data length:\t\t%d\n", samhdr->length);
    PRINT_DEBUG ("loop begin:\t\t%d\n", samhdr->begin);
    PRINT_DEBUG ("loop end:\t\t%d\n", samhdr->end);
    PRINT_DEBUG ("flags:\t\t%d\n", samhdr->flags);
    PRINT_DEBUG ("rate:\t\t%d\n", samhdr->rate);
    PRINT_OUT ("volume:\t\t\t%d\n", samhdr->volume);
    PRINT_OUT ("pan:\t\t\t%d\n", samhdr->pan);
    PRINT_OUT ("\n");
  }

  /* dump order information */
  PRINT_OUT ("%d orders:\n", gdm->order_len);

  for (i = 0; i < gdm->order_len; i++)
    PRINT_OUT ("%d ", gdm->orders.patterns[i]);

  PRINT_OUT ("\n\n");

  /* dump pattern information */
  PRINT_OUT ("%d patterns:\n", gdm->numpatterns);

  for (i = 0; i < gdm->numpatterns; i++)
    PRINT_OUT ("  pattern %d, %d bytes.\n", i, gdm->patterns[i].length);

  PRINT_OUT ("\n");
}
#endif // 0

void free_gdm (struct GDM_file *gdm)
{
  int i;

  /* free up order data */
  free (gdm->orders.patterns);

  /* free up pattern data */
  for (i = 0; i < gdm->numpatterns; i++)
    free (gdm->patterns[i].data);
  free (gdm->patterns);

  /* free up sample data */
  for (i = 0; i < gdm->numsamples; i++)
    free (gdm->samples[i].data);
  free (gdm->samples);

  /* free it */
  free (gdm);
}


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __GDM_H
#define __GDM_H

#include "types.h"

__G_BEGIN_DECLS

/**
 * below are the file structures used by the GDM file format
 */

#define GDM_MAGIC "GDM\xFE"

struct GDM_header {
  uint8_t title[33];
  uint8_t artist[33];
  uint16_t version;        /* GDM format version (always 1.0) */
  uint16_t tracker;        /* tracker used to generate file */
  uint16_t tracker_ver;    /* version of tracker used */
  uint8_t panning[32];     /* panning map, 1 byte per channel */
  uint8_t global_vol;      /* global volume */
  uint8_t tempo;
  uint8_t bpm;
  uint16_t origfmt;        /* original format */
};

struct GDM_order {
  uint8_t *patterns;
};

struct GDM_pattern {
  uint16_t length;
  uint8_t *data;
};

struct GDM_samhdr {
  uint8_t name[33];
  uint8_t filename[13];    /* original filename */
  uint32_t length;         /* length of sample, in bytes */
  uint32_t begin;          /* loop beginning, in samples */
  uint32_t end;            /* loop ending, in samples */
  uint8_t flags;           /* sample type flags */
  uint16_t rate;           /* playback speed in Hz, should be 8363 */
  uint8_t volume;          /* default volume */
  uint8_t pan;             /* default panning */
};

/**
 * end of file structures
 */

struct GDM_file {
  struct GDM_header header;
  struct GDM_order orders;
  struct GDM_pattern *patterns;
  struct GDM_sample *samples;
  uint8_t order_len;
  uint8_t numpatterns;
  uint8_t numsamples;
};

struct GDM_sample {
  struct GDM_samhdr header;  /* file structure */
  uint8_t *data;             /* sample data */
};

/* function prototypes */
struct GDM_file *load_gdm (uint8_t *stream, size_t stream_len);
void info_gdm (struct GDM_file *gdm);
void free_gdm (struct GDM_file *gdm);

__G_END_DECLS

#endif /* __GDM_H */


code:
/**
 *  This software is a "lite" version of gdm2s3m, for Exophase's MegaZeux.
 *
 *  Copyright (C) 2003-2005  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "types.h"
#include "gdm.h"
#include "s3m.h"
#include "error.h"
#include "utility.h"
#include "gdm2s3m.h"

int convert_gdm_s3m (const char *gdmfile, const char *s3mfile)
{
  struct GDM_file *gdm;
  struct S3M_file *s3m;
  size_t filesize;
  uint8_t *stream;
  FILE *handle;

  /* open module */
  if ((handle = fopen (gdmfile, "rb")) == NULL)
    return -1;

  /* get file size */
  fseek (handle, 0, SEEK_END);
  filesize = (uint32_t) ftell (handle);
  fseek (handle, 0, SEEK_SET);

  /* allocate space */
  stream = malloc (filesize);

  /* stream into memory */
  if (fread (stream, 1, filesize, handle) != filesize)
    return -2;

  /* close file */
  fclose (handle);

  /* try to load it */
  gdm = load_gdm (stream, filesize);

  /* free up stream */
  free (stream);

  /* do convert routine */
  s3m = convert_gdm_to_s3m (gdm);

  /* free up gdm */
  free_gdm (gdm);

  /* open up s3m file */
  if ((handle = fopen (s3mfile, "wb")) == NULL)
    return -1;

  /* save s3m file */
  stream = save_s3m (s3m, &filesize);

  /* free up s3m */
  free_s3m (s3m);

  /* write out file */
  if (fwrite (stream, 1, filesize, handle) != filesize)
    return -3;

  /* close file */
  fclose (handle);

  /* free up the stream */
  free (stream);

  /* all ok */
  return 0;
}


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __GDM2S3M_H
#define __GDM2S3M_H

#ifdef __cplusplus
extern "C" {
#endif

/* function prototypes */
int convert_gdm_s3m (const char *gdmfile, const char *s3mfile);

#ifdef __cplusplus
}
#endif

#endif /* __GDM2S3M_H */


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "s3m.h"
#include "gdm2s3m.h"
#include "error.h"
#include "utility.h"

uint8_t *save_s3m (struct S3M_file *s3m, size_t *stream_len)
{
  const uint8_t junk1[4] = { 0x1A, 16, 0, 0 };
  const uint8_t junk2[6] = { 0, 0, 0x17, 0x32, 2, 0 };
  const uint8_t junk3[13] = { 0xb0, 0, 0xfc, 0, 0, 0xe, 0x79,
                              0x10, 0xe4, 0, 0, 0, 0 };
  const uint8_t junk4[1] = { 1 };
  const uint8_t junk5[2] = { 0, 0 };
  const uint8_t junk6[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

  uint8_t gap[16], *gap2, memseg[3], *backup = NULL, *stream = NULL;
  uint32_t offset = 0, spare, segpad, sample_table, pattern_table;
  struct S3M_order *orders = &s3m->orders;
  struct S3M_header *hdr = &s3m->header;
  char s3m_magic[4], sam_magic[4];
  uint16_t segment;
  int i;

  /* copy magics */
  memcpy (s3m_magic, S3M_MAGIC, 4);
  memcpy (sam_magic, S3M_SAMPLE_MAGIC, 4);

  /* zero gap */
  memset (gap, 0, 16);

  /* zero memseg dummy */
  memset (memseg, 0, 3);

  /* initialise stream_len */
  (*stream_len) = 0;

  /* dump song title */
  check_a_to_s (&backup, stream_len, &hdr->title, &stream, 28);

  /* pad with junk */
  check_a_to_s (&backup, stream_len, (uint8_t *)&junk1, &stream, 4);

  /* dump quantities */
  CHECK_ENDIAN_16 (&hdr->numorders);
  CHECK_ENDIAN_16 (&hdr->numinst);
  CHECK_ENDIAN_16 (&hdr->numpatterns);
  check_a_to_s (&backup, stream_len, &hdr->numorders, &stream, 2);
  check_a_to_s (&backup, stream_len, &hdr->numinst, &stream, 2);
  check_a_to_s (&backup, stream_len, &hdr->numpatterns, &stream, 2);
  CHECK_ENDIAN_16 (&hdr->numorders);
  CHECK_ENDIAN_16 (&hdr->numinst);
  CHECK_ENDIAN_16 (&hdr->numpatterns);

  /* pad with more junk */
  check_a_to_s (&backup, stream_len, (uint8_t *)&junk2, &stream, 6);

  /* dump magic bytes */
  check_a_to_s (&backup, stream_len, s3m_magic, &stream, 4);

  /* dump global volume, speed, tempo */
  check_a_to_s (&backup, stream_len, &hdr->global_vol, &stream, 1);
  check_a_to_s (&backup, stream_len, &hdr->speed, &stream, 1);
  check_a_to_s (&backup, stream_len, &hdr->tempo, &stream, 1);

  /* pad with remaining junk */
  check_a_to_s (&backup, stream_len, (uint8_t *)&junk3, &stream, 13);

  /* channel settings */
  check_a_to_s (&backup, stream_len, &hdr->chansett, &stream, 32);

  /* ORDER HEADER ---------------------------------------------------------- */
  check_a_to_s (&backup, stream_len, orders->patterns, &stream,
                  hdr->numorders);

  /* update offset */
  offset = 96 + hdr->numorders;

  /* figure out how much space we've used up */
  sample_table = offset;
  pattern_table = offset + 2 * hdr->numinst;

  /* calculate padding size */
  segpad = hdr->numorders + 2 * hdr->numinst + 2 * hdr->numpatterns;
  segpad += (segpad % 16) ? 16 - segpad % 16 : 0;
  segpad -= hdr->numorders;
  segment = 2 * hdr->numinst + 2 * hdr->numpatterns;

  /* allocate space for sample table, pattern table and padding */
  gap2 = malloc (segpad);
  memset (gap2, 0, segpad);

  /* pad up to segment boundary */
  check_a_to_s (&backup, stream_len, gap2, &stream, segment);

  /* write channel pan values */
  check_a_to_s (&backup, stream_len, &s3m->panmap, &stream, 32);

  /* pad up to segment boundary */
  check_a_to_s (&backup, stream_len, &gap2[segment], &stream,
                 segpad - segment);

  /* free padding */
  free (gap2);

  /* update offset */
  offset += 32 + segpad;

  /* SAMPLE HEADERS -------------------------------------------------------- */

  /* dump sample headers */
  for (i = 0; i < hdr->numinst; i++) {
    struct S3M_samhdr *sample = &s3m->samples[i].header;

    /* write out junk byte */
    check_a_to_s (&backup, stream_len, (uint8_t *)&junk4, &stream, 1);

    /* dos filename */
    check_a_to_s (&backup, stream_len, &sample->filename, &stream, 12);

    /* memseg (dummy) */
    check_a_to_s (&backup, stream_len, memseg, &stream, 3);

    /* sample length */
    CHECK_ENDIAN_32 (&sample->length);
    check_a_to_s (&backup, stream_len, &sample->length, &stream, 4);
    CHECK_ENDIAN_32 (&sample->length);

    /* loop beginning */
    CHECK_ENDIAN_32 (&sample->begin);
    check_a_to_s (&backup, stream_len, &sample->begin, &stream, 4);

    /* loop ending */
    CHECK_ENDIAN_32 (&sample->end);
    check_a_to_s (&backup, stream_len, &sample->end, &stream, 4);

    /* volume */
    check_a_to_s (&backup, stream_len, &sample->volume, &stream, 1);

    /* more junk */
    check_a_to_s (&backup, stream_len, (uint8_t *)&junk5, &stream, 2);

    /* flags */
    check_a_to_s (&backup, stream_len, &sample->flags, &stream, 1);

    /* rate */
    CHECK_ENDIAN_16 (&sample->rate);
    check_a_to_s (&backup, stream_len, &sample->rate, &stream, 2);

    /* last bit of junk */
    check_a_to_s (&backup, stream_len, (uint8_t *)&junk6, &stream, 14);

    /* sample name */
    check_a_to_s (&backup, stream_len, &sample->name, &stream, 28);

    /* sample magic */
    check_a_to_s (&backup, stream_len, sam_magic, &stream, 4);

    /* work out segment */
    segment = (uint16_t)(offset / 16);

    /* update sample parapointer */
    CHECK_ENDIAN_16 (&segment);
    memcpy (&backup[sample_table], &segment, 2);

    /* update sample_table pointer */
    sample_table += 2;

    /* update offset */
    offset += 80;
  }

  /* PATTERN SEGMENT ------------------------------------------------------- */

  /* dump pattern lengths followed by pattern data */
  for (i = 0; i < hdr->numpatterns; i++) {
    struct S3M_pattern *pattern = &s3m->patterns[i];

    /* add on size of length data to length */
    pattern->length += 2;

    /* write out length of pattern */
    CHECK_ENDIAN_16 (&pattern->length);
    check_a_to_s (&backup, stream_len, &pattern->length, &stream, 2);
    CHECK_ENDIAN_16 (&pattern->length);

    /* write out pattern data */
    check_a_to_s (&backup, stream_len, pattern->data, &stream,
                    pattern->length - 2);

    /* work out segment */
    segment = (uint16_t)(offset / 16);

    /* update pattern parapointer */
    CHECK_ENDIAN_16 (&segment);
    memcpy (&backup[pattern_table], &segment, 2);

    /* update pattern_table pointer */
    pattern_table += 2;

    /* update offset */
    offset += pattern->length;

    /* work out padding */
    segpad = (offset % 16) ? 16 - offset % 16 : 0;

    /* write out padding */
    check_a_to_s (&backup, stream_len, gap, &stream, segpad);

    /* update offset */
    offset += segpad;
  }

  /* SAMPLE DATA -------------------------------------------------------- */

  /**
   * we can use the parapointers we've been storing to re-access the samples
   * saved before at the appropriate file offsets. Quite useful.
   */

  sample_table = 96 + hdr->numorders;

  /* dump sample data */
  for (i = 0; i < hdr->numinst; i++) {
    struct S3M_sample *sample = &s3m->samples[i];

    /* write out sample data */
    check_a_to_s (&backup, stream_len, sample->data, &stream,
                   sample->header.length);

    /* extract offset from parapointer table */
    memcpy (&segment, &backup[sample_table], 2);
    CHECK_ENDIAN_16 (&segment);

    /* update sample table pointer */
    sample_table += 2;

    /* real byte offset */
    spare = segment * 16 + 13;

    /* work out segment */
    segment = (uint16_t)(offset / 16);

    /* stick it into that awkward 24bit parapointer */
    CHECK_ENDIAN_16 (&segment);
    memcpy (&backup[spare + 1], &segment, 2);

    /* update offset! */
    offset += sample->header.length;

    /* work out padding */
    segpad = (offset % 16) ? 16 - offset % 16 : 0;

    /* write out padding */
    check_a_to_s (&backup, stream_len, gap, &stream, segpad);

    /* update offset */
    offset += segpad;
  }

  /* return entire stream */
  return backup;
}

/**
 * written by madbrain; contact him if something here isn't right
 */
static void remap_effects (uint8_t gdm_effect, uint8_t gdm_param,
                           uint8_t *dest_effect, uint8_t *dest_param)
{
  uint8_t s3m_effect = 0;
  uint8_t s3m_param = 0;

  switch(gdm_effect)
  {
    case 0: // "No Effect"
      s3m_effect=0;
      s3m_param=gdm_param;
      break;

    case 1: // Portamento up
      s3m_effect='F'-'@';
      s3m_param=gdm_param;
      if(s3m_param>=0xE0)
        s3m_param=0xDF;
      break;

    case 2: // Portamento down
      s3m_effect='E'-'@';
      s3m_param=gdm_param;
      if(s3m_param>=0xE0)
        s3m_param=0xDF;
      break;

    case 3: // Glissando
      s3m_effect='G'-'@';
      s3m_param=gdm_param;
      break;

    case 4: // Vibrato
      s3m_effect='H'-'@';
      s3m_param=gdm_param;
      break;

    case 5: // Portamento + volume slide
      s3m_effect='L'-'@';
      s3m_param=gdm_param;
      break;

    case 6: // Vibrato + volume slide
      s3m_effect='K'-'@';
      s3m_param=gdm_param;
      break;

    case 7: // Tremolo
      s3m_effect='R'-'@';
      s3m_param=gdm_param;
      break;

    case 8: // Tremor/stupid effect nobody uses
      s3m_effect='I'-'@';
      s3m_param=gdm_param;
      break;

    case 9: // Sample offset
      s3m_effect='O'-'@';
      s3m_param=gdm_param;
      break;

    case 0xa: // Volume slide
      s3m_effect='D'-'@';
      s3m_param=gdm_param;
      break;

    case 0xb: // Order jump
      s3m_effect='B'-'@';
      s3m_param=gdm_param;
      break;

    case 0xd: // Pattern break
      s3m_effect='C'-'@';
      s3m_param=gdm_param;
      break;

    case 0xe: // EXTENDED EFFECTS, DO THIS LATER
      switch(gdm_param>>4)
      {
        case 0: // Set filter (AMIGA!)
          s3m_effect='S'-'@';
          s3m_param= 0x00 + (gdm_param & 15);
          break;
        case 1: // Fine slide up
          s3m_effect='F'-'@';
          s3m_param= 0xF0 + (gdm_param & 15);
          break;
        case 2: // Fine slide down
          s3m_effect='E'-'@';
          s3m_param= 0xF0 + (gdm_param & 15);
          break;
        case 3: // Glissando control
          s3m_effect='S'-'@';
          s3m_param= 0x10 + (gdm_param & 15);
          break;
        case 4: // Vibrato waveform
          s3m_effect='S'-'@';
          s3m_param= 0x30 + (gdm_param & 15);
          break;
        case 5: // Set finetune
          s3m_effect='S'-'@';
          s3m_param= 0x20 + (gdm_param & 15);
          break;
        case 6: // Pattern loop
          s3m_effect='S'-'@';
          s3m_param= 0xB0 + (gdm_param & 15);
          break;
        case 7: // Tremolo waveform
          s3m_effect='S'-'@';
          s3m_param= 0x40 + (gdm_param & 15);
          break;
        case 8: // Extra Fine slide up
          s3m_effect='F'-'@';
          s3m_param= 0xE0 + (gdm_param & 15);
          break;
        case 9: // Extra Fine slide down
          s3m_effect='E'-'@';
          s3m_param= 0xE0 + (gdm_param & 15);
          break;
        case 0xA: // Fine volume up
          s3m_effect='D'-'@';
          s3m_param= 0x0F + ((gdm_param & 15) << 4);
          break;
        case 0xB: // Fine volume down
          s3m_effect='D'-'@';
          s3m_param= 0xF0 + (gdm_param & 15);
          break;
        case 0xC: // Cut note after x ticks
          s3m_effect='S'-'@';
          s3m_param= 0xC0 + (gdm_param & 15);
          break;
        case 0xD: // Delay note for x ticks
          s3m_effect='S'-'@';
          s3m_param= 0xD0 + (gdm_param & 15);
          break;
        case 0xE: // Delay whole pattern, in ticks???!?
          s3m_effect='S'-'@';
          s3m_param= 0xE0 + (gdm_param & 15);
          break;
        case 0xF: // Invert loop?!? Funk repeat?!?
          s3m_effect='S'-'@';
          s3m_param= 0xF0 + (gdm_param & 15);
          break;
      }
      break;

    case 0xf: // "Set tempo", apparently sets speed
      s3m_effect='A'-'@';
      s3m_param=gdm_param;
      break;

    case 0x10: // Arpeggio
      s3m_effect='J'-'@';
      s3m_param=gdm_param;
      break;

    case 0x11: // Set internal flag, no idea what this is
      s3m_effect=0; // Convert to no effect
      s3m_param=gdm_param;
      break;

    case 0x12: // Retrigger note
      s3m_effect='Q'-'@';
      s3m_param=gdm_param;
      break;

    case 0x13: // Global volume, 0-63 in both s3m and gdm
      s3m_effect='V'-'@';
      s3m_param=gdm_param;
      break;

    case 0x14: // Fine vibrato, crashes mzx
      s3m_effect='U'-'@';
      s3m_param=gdm_param;
      break;

    case 0x1e: // Special
      if((gdm_param>>4)==8)
      {
        s3m_effect='S'-'@';
        s3m_param= 0x80 + (gdm_param & 15);
      }
      else
      {
        s3m_effect=0;
        s3m_param=gdm_param;
      }
      break;

    case 0x1f: // Set bpm, ie set tempo?!?
      s3m_effect='T'-'@';
      s3m_param=gdm_param;
      break;

    default:
      fprintf (stderr, "gdm2s3m: implemented effect! (contact ajs)\n");
      break;
  }

  *dest_effect = s3m_effect;
  *dest_param = s3m_param;
}

struct S3M_file *convert_gdm_to_s3m (struct GDM_file *gdm)
{
  struct S3M_file *s3m;
  int i, j;

  /* optimal channel settings for ST3 */
  uint8_t chansett[] = { 0,   8,   1,   9,   2,   10,  3,   11,
                         4,   12,  5,   13,  6,   14,  7,   15,
                         255, 255, 255, 255, 255, 255, 255, 255,
                         255, 255, 255, 255, 255, 255, 255, 255 };

  /* allocate s3m struct */
  s3m = malloc (sizeof (struct S3M_file));

  /* clear it */
  memset (s3m, 0, sizeof (struct S3M_file));

  /* copy module title (as much as possible) */
  memcpy (s3m->header.title, gdm->header.title, 27);

  /* pass over order, sample, pattern counts */
  if (gdm->order_len & 1)
    s3m->header.numorders = gdm->order_len;
  else
    s3m->header.numorders = gdm->order_len + 1;
  s3m->header.numinst = gdm->numsamples;
  s3m->header.numpatterns = gdm->numpatterns;

  /* pass over global volume, speed, tempo */
  s3m->header.global_vol = gdm->header.global_vol;
  s3m->header.speed = gdm->header.bpm;
  s3m->header.tempo = gdm->header.tempo;

  /* channel settings have no appreciable function */
  memcpy (s3m->header.chansett, chansett, 32);

  /* handle pan map */
  for (i = 0; i < 32; i++) {
    uint8_t g = gdm->header.panning[i];
    uint8_t *s = &s3m->panmap[i];

    if (g == 16) {     /* surround sound */
      *s = 8 + 0x20;
    }
    else if (g > 16) { /* disabled */
      *s = 0;
    }
    else {             /* normal panning */
      *s = g + 0x20;
    }
  }

  /* allocate order space */
  s3m->orders.patterns = malloc (s3m->header.numorders);
  memset (s3m->orders.patterns, 254, s3m->header.numorders);
  s3m->orders.patterns[s3m->header.numorders - 1] = 255;  // FIXME

  /* copy over order data */
  memcpy (s3m->orders.patterns, gdm->orders.patterns, gdm->order_len);

  /* allocate pattern space */
  s3m->patterns =
    malloc (sizeof (struct S3M_pattern) * s3m->header.numpatterns);

  /* copy over patterns */
  for (i = 0; i < s3m->header.numpatterns; i++) {
    struct S3M_pattern *pattern = &s3m->patterns[i];
    struct GDM_pattern *gpattern = &gdm->patterns[i];
    uint8_t *pdata = NULL, *backup = NULL;
    size_t plen = 0;

    /* big pattern expander and collapser */
    for (j = 0; j < gpattern->length; j++) {
      uint8_t vol = 0, norm = 0, note = 0, effect = 0;
      uint8_t b = 0, n, s, e, ep, se, sep, sv;

      /* it's a zero byte */
      if (gpattern->data[j] != 0) {
        /* copy first 5 bits over */
        b = gpattern->data[j] & 0x1F;

        /* see if we've got a note on */
        if (gpattern->data[j] & 0x20)
          note = 1;

        /* see if we've got an effect */
        if (gpattern->data[j] & 0x40)
          effect = 1;
      }

      /* copy over note data */
      if (note) {
        /* get note and sample */
        n = (gpattern->data[j + 1] & 0x7F) - 1;
        s = gpattern->data[j + 2];

        /* mask in note on */
        b |= 0x20;

        /* increment j */
        j += 2;
      }

      /* copy over effect */
      if (effect) {
        do
        {
          /* get effect number */
          e = gpattern->data[j + 1];

          /* get effect parameter */
          ep = gpattern->data[j + 2];

          /* check for volume event */
          if ((e & 0x1F) == 0xC) {
            /* we can't have more than one volume effect per note */
            if (vol == 1)
              break;
            vol = 1;

            /* mask on that we've got one */
            b |= 0x40;

            /* se for the volume */
            sv = ep;
          } else {
            /* we can't have more than one of these */
            if (norm == 1)
              break;
            norm = 1;

            /* mask on that we've got a real effect */
            b |= 0x80;

            /* convert effect */
            remap_effects (e & 0x1F, ep, &se, &sep);
          }

          /* move along */
          j += 2;
        }
        while (e & 0x20);
      }

      /* copy over */
      check_a_to_s (&backup, &plen, &b, &pdata, 1);

      /* write out note and sample */
      if (note) {
        check_a_to_s (&backup, &plen, &n, &pdata, 1);
        check_a_to_s (&backup, &plen, &s, &pdata, 1);
      }

      /* volume event */
      if (effect && vol) {
        check_a_to_s (&backup, &plen, &sv, &pdata, 1);
      }

      /* normal effect */
      if (effect && norm) {
        check_a_to_s (&backup, &plen, &se, &pdata, 1);
        check_a_to_s (&backup, &plen, &sep, &pdata, 1);
      }
    }

    /* copy over length */
    pattern->length = (uint16_t)plen;

    /* copy over data */
    pattern->data = backup;
  }

  /* allocate sample space */
  s3m->samples = malloc (sizeof (struct S3M_sample) * s3m->header.numinst);

  /* zero it all */
  memset (s3m->samples, 0, sizeof (struct S3M_sample) * s3m->header.numinst);

  /* copy over samples */
  for (i = 0; i < s3m->header.numinst; i++) {
    struct S3M_sample *sample = &s3m->samples[i];
    struct GDM_sample *gsample = &gdm->samples[i];

    /* ------------ HEADER ------------ */

    /* copy filename 8.3 format */
    memcpy (sample->header.filename, gsample->header.filename, 13);

    /* copy sample attributes */
    sample->header.length = gsample->header.length;
    sample->header.begin = gsample->header.begin;
    sample->header.end = gsample->header.end - 1;
    sample->header.volume = gsample->header.volume;
    sample->header.flags = gsample->header.flags & 1;
    sample->header.rate = gsample->header.rate;

    /* 16 bit sample */
    if (gsample->header.flags & 0x2)
      sample->header.flags |= 0x4;

    /* stereo sample */
    if (gsample->header.flags & 0x20)
      sample->header.flags |= 0x2;

    /* sample name (truncated) */
    memcpy (sample->header.name, gsample->header.name, 27);

    /* --------- SAMPLE DATA ---------- */

    /* allocate sample space */
    sample->data = malloc (sample->header.length);

    /* copy sample data */
    memcpy (sample->data, gsample->data, sample->header.length);
  }

  /* return new s3m struct */
  return s3m;
}

void free_s3m (struct S3M_file *s3m)
{
  int i;

  /* free up order data */
  free (s3m->orders.patterns);

  /* free up pattern data */
  for (i = 0; i < s3m->header.numpatterns; i++)
    free (s3m->patterns[i].data);
  free (s3m->patterns);

  /* free up sample data */
  for (i = 0; i < s3m->header.numinst; i++)
    free (s3m->samples[i].data);
  free (s3m->samples);

  /* free it */
  free (s3m);
}


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __S3M_H
#define __S3M_H

#include "types.h"
#include "gdm.h"

__G_BEGIN_DECLS

/**
 * below are the file structures used by the GDM file format
 */

#define S3M_MAGIC         "SCRM"
#define S3M_SAMPLE_MAGIC  "SCRS"

struct S3M_header {
  uint8_t title[28];      /* song title, last byte must be \0 */
  uint16_t numorders;     /* number of orders */
  uint16_t numinst;       /* number of adlib instruments */
  uint16_t numpatterns;   /* number of wav samples */
  uint8_t global_vol;     /* global volume */
  uint8_t speed;
  uint8_t tempo;
  uint8_t chansett[32];   /* channel settings */
};

struct S3M_order {
  uint8_t *patterns;
};

struct S3M_samhdr {
  uint8_t filename[13];    /* original filename */
  uint32_t length;         /* length of sample, in bytes */
  uint32_t begin;          /* loop beginning, in samples */
  uint32_t end;            /* loop ending, in samples */
  uint8_t volume;          /* default volume */
  uint8_t flags;           /* sample type flags */
  uint16_t rate;           /* playback speed in Hz, should be 8363 */
  uint8_t name[28];
};

struct S3M_pattern {
  uint16_t length;
  uint8_t *data;
};

/**
 * end of file structures
 */

struct S3M_file {
  struct S3M_header header;
  struct S3M_order orders;
  struct S3M_sample *samples;
  struct S3M_pattern *patterns;
  uint8_t panmap[32];
};

struct S3M_sample {
  struct S3M_samhdr header;  /* file structure */
  uint8_t *data;             /* sample data */
};

/* function prototypes */
uint8_t *save_s3m (struct S3M_file *s3m, size_t *stream_len);
struct S3M_file *convert_gdm_to_s3m (struct GDM_file *gdm);
void free_s3m (struct S3M_file *s3m);

__G_END_DECLS

#endif /* __S3M_H */


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __TYPES_H
#define __TYPES_H

#ifdef __cplusplus
#define __G_BEGIN_DECLS extern "C" {
#define __G_END_DECLS   }
#else
#define __G_BEGIN_DECLS
#define __G_END_DECLS
#endif /* __cplusplus */

__G_BEGIN_DECLS

#ifndef _MSC_VER

#include <inttypes.h>

#else // _MSC_VER

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

/* MSVC can use windows types from windows.h */
typedef BYTE uint8_t;
typedef USHORT uint16_t;
typedef ULONG uint32_t;

/* MSVC doesn't understand inline */
#define inline

#endif // !_MSC_VER

__G_END_DECLS

#endif /* __TYPES_H */


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "utility.h"
#include "error.h"

static void stream_to_alloc (void *dest, uint8_t **src, uint32_t n)
{
  /* copy count blocks */
  memcpy (dest, *src, n);

  /* increment stream offset */
  *src += n;
}

static void alloc_to_stream (void *src, uint8_t **dest, uint32_t n)
{
  /* copy count blocks */
  memcpy (*dest, src, n);

  /* increment stream offset */
  *dest += n;
}

void check_s_to_a (uint8_t *start, size_t size, void *dest, uint8_t **src,
                   uint32_t n)
{
  const size_t diff = *src - start;

  /* check we've got enough of the stream */
  if (diff + n > size)
    error (OUT_OF_DATA);

  /* otherwise, call on */
  stream_to_alloc (dest, src, n);
}

void check_a_to_s (uint8_t **start, size_t *size, void *src, uint8_t **dest, uint32_t n)
{
  const size_t diff = *dest - *start;

  /* check we've got enough allocated */
  if (diff + n > *size) {
    /* new size */
    *size = diff + n;

    /* increase allocation size */
    *start = realloc (*start, *size);

    /* reassign pointer */
    *dest = (uint8_t *) (*start + diff);
  }

  /* otherwise, call on */
  alloc_to_stream (src, dest, n);
}


code:
/**
 *  Copyright (C) 2003-2004  Alistair John Strachan  (alistair@devzero.co.uk)
 *
 *  This is free software; you can redistribute it and/or modify it under
 *  the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2, or (at your option) any later
 *  version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __UTILITY_H
#define __UTILITY_H

#include "types.h"

__G_BEGIN_DECLS

#if  defined(WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
     defined(__i386__) || defined(__ia64__) || \
    (defined(__alpha__) || defined(__alpha)) || \
     defined(__arm__) || \
    (defined(__mips__) && defined(__MIPSEL__)) || \
     defined(__SYMBIAN32__) || \
     defined(__x86_64__) || \
     defined(__LITTLE_ENDIAN__)

/* little endian systems; no magic necessary */

#define CHECK_ENDIAN_16(x)
#define CHECK_ENDIAN_32(x)

#else // !LITTLE_ENDIAN

/* big endian systems; we need to swap data before storage */

static inline void gdm2s3m_swap16(uint16_t *var)
{
  *var = ((*var << 8) | (*var >> 8));
}

static inline void gdm2s3m_swap32(uint32_t *var)
{
  *var = ((*var << 24) | ((*var << 8) & 0x00FF0000) |
         ((*var >> 8) & 0x0000FF00) | (*var >> 24));
}

#define CHECK_ENDIAN_16(x) gdm2s3m_swap16(x)
#define CHECK_ENDIAN_32(x) gdm2s3m_swap32(x)

#endif // LITTLE_ENDIAN

/**
 * We support MSVC and gcc; MSVC's macro support is awful
 */

#ifdef _MSC_VER
#define PRINT_OUT   printf
#define PRINT_ERR   printf
#define PRINT_DEBUG printf
#else /* _MSC_VER */
#if defined(WIN32) || defined(__WIN32__)
/**
 * An MSYS console flushing bug requires fflush after
 * fprintf (contrary to spec).
 */
#define PRINT_OUT(...) \
  { \
    fprintf(stdout, __VA_ARGS__); \
    fflush(stdout); \
  }
#define PRINT_ERR(...) \
  { \
    fprintf(stderr, __VA_ARGS__); \
    fflush(stderr); \
  }
#else /* __WIN32__ */
/**
 * LINUX, etc. does not require fflush().
 */
#define PRINT_OUT(...) \
  { \
    fprintf(stdout, __VA_ARGS__); \
  }
#define PRINT_ERR(...) \
  { \
    fprintf(stderr, __VA_ARGS__); \
  }
#endif /* __linux__ */

#ifdef DEBUG
#define PRINT_DEBUG(...) \
  { \
    PRINT_OUT("D: " __VA_ARGS__); \
  }
#else
#define PRINT_DEBUG(...)
#endif /* DEBUG */
#endif /* !_MSC_VER */

/* function prototypes */
void check_s_to_a (uint8_t *start, size_t size, void *dest,
                   uint8_t **src, uint32_t n);
void check_a_to_s (uint8_t **start, size_t *size, void *src,
                   uint8_t **dest, uint32_t n);

__G_END_DECLS

#endif /* __UTILITY_H */


code:
##
# icons Makefile fragment
##

.PHONY: icons_clean

.SUFFIXES: .rc

icons_src = contrib/icons
icons_obj = contrib/icons/.build

icons = ${icons_obj}/icons.o

icons_objs = ${icons_obj}/quantump.o

ifneq (${V},1)
WINDRES := @${WINDRES}
endif

${icons_obj}/%.o: ${icons_src}/%.rc
	$(if ${V},,@echo "  WINDRES " $<)
	${WINDRES} -i $< -o $@

ifeq (${EMBED_ICONS},1)
${icons}: ${icons_obj} ${icons_objs}
	$(if ${V},,@echo "  CP      " ${icons_objs} ${icons})
	${CP} ${icons_objs} ${icons}
else
${icons}:
	@echo "--> Embedding of icon branding disabled."
endif

${icons_objs}: $(filter-out $(wildcard ${icons_obj}), ${icons_obj})

icons_clean:
	$(if ${V},,@echo "  RM      " ${icons_obj})
	${RM} -r ${icons_obj}


code: 1 ICON "contrib/icons/ghostblue.ico"


code: 1 ICON "contrib/icons/ghostred.ico"


code: 1 ICON "contrib/icons/quantump.ico"


code:
XMMS plugin:
  Kenton Varda <temporal@gauge3d.org>
  Konstanty Bialkowski <konstanty@ieee.org>

General Maintainence:
  Konstanty Bialkowski <konstanty@ieee.org>

Sound Engine:
  Olivier Lapicque <olivierl@jps.net>

BZip2 support:
  Colin DeVilbiss <crdevilb@mtu.edu>
Spline and Fir resamplers:
  Markus Fick <marf@gmx.net>
Endianness Fixes:
  Adam Goode <adam@evdebs.org>
Endianness Fixes + Implementation of C 24bit,32bit functions:
  Marco Trillo <toad@arsystel.com>
Fixes to AGC/Clipping, Frequency Limit, Other Fixes:
  Alistair John Strachan <s0348365@sms.ed.ac.uk>
AMD64 Fix (long long vs long vs int)
  Tyler Montbriand <tsm@accesscomm.ca>

path: contrib/libmodplug/AUTHORS


code:
changes:
date = 21-apr-2009 [Konstanty Bialkowski/OpenMPT/Novell (Stanislav Brabec)]
->file: src/load_amf.cpp
		where: whole file
		what: added const declarations to read only variables
		what: fixed delete function
->file: src/fastmix.cpp
		where: X86_Convert32To24
		what: conversion to 24bit was incorrect
->file: src/load_mdl.cpp
		where: init of m_lpszSongComments
		what: fixed delete function to be array version (Reported by
		David Binderman / Stanislav Brabec)
->file: src/load_pat.cpp
		where: memcpy to .reserved
		what: changed fixed valid of 36 to sizeof(reserved)
		(Reported by Manfred Tremmel / Stanislav Brabec)
date = 20-apr-2009 [Konstanty Bialkowski]
->file: src/load_meb.cpp
		where: LoadMED
		what: fixed integer boundary condition checking code (fixing
		exploit)
		Details of exploit (and creator of test.s3m for exploit)
		http://www.securityfocus.com/bid/30801/info
		http://www.15897.com/blog/post/QianQianJingTing-mod-buffer-overflow-POC.html
->file: src/load_abc.cpp
		where: TestABC
		what: Made ABC detection code more robust
->file: src/load_abc.cpp, sndfile.cpp, snd_fx.cpp
		where: various
		what: change constant variables to explicitly use const
		definition
		(Thanks to Leandro Nini/Diego "Flameeyes" Pettenò)
->file: src/table.cpp -> src/table.h
		where: rename of file
		what: moved tables to seperate file
		(Thanks to Leandro Nini/Diego "Flameeyes" Pettenò)

date = 15-apr-2009 [Konstanty Bialkowski]
->file: src/load_abc.cpp
		where: TestABC
		what: made sure obviously binary files do not try to get
		loaded as ABC
		where: abc_addchordname
		what: made sure first argument was const char * (not just char
		*)
->file: src/sndfile.cpp
		where: format conversion routines
		what: made sure (*a++) = func(*a), is executed properly, by
		splitting into two lines
-file: src/libmodplug/sndfile.h
		where: IMixPlugin
		what: made sure there is a virtual destructor (to avoid
		warnings)
->file: src/load_ptm.cpp
		where: mixing routine
		what: bswap32 was used on a uint16_t [2] array, and when gcc
		uses -O2 (or greater) optimization, this may not do what is
		intended.
date = 7-apr-2009 [Konstanty Bialkowski/Anthony Ramine]
-> file: src/load_abc.cpp,load_mid.cpp,load_pat.cpp
		where: whole file(s)
		what: removed use of ULONG, and changed to uint32_t
		where: few functions
		what: removed uint, and replaced with uint32_t
date = 7-apr-2009 [Zed Pobre/Debian]
-> file: src/libmodplug.pc
		where: file.
		what: split Libs into Libs.private
date = 2-nov-2006 [Alec Berryman/CVE-2006-4192]
-> file: src/sndfile.cpp
		where: ReadSample
		what: prevent buffer overflow [as reported in CVE-2006-4192]
date = 2-nov-2006 [Konstanty Bialkowski/Macro Trillo]
-> file: configure.in,config.h,load_abc.cpp,fastmix.cpp
		where: Automake 2.60 used
		what: In order to properly use stdint.h we should make use of the new macros for its detection, on systems with automake 2.59 but where stdint.h is available, this can be removed. (Patch by Macro Trillo)
-> file: src/load_amf.cpp, src/fastmix.cpp
		where: Licensing
		what: all old code was relicensed for public domain, and somehow an old version was included with GPL notices
date = 20-jul-2006 [Peter Grootswagers]

-> file: src/load_abc.cpp

		where: instrument loader functions

		what: replaced with correspondig functions in load_pat.cpp

-> file: src/load_mid.cpp

		where: whole source

		what: new loader for midi files

-> file: src/load_pat.cpp

		where: whole source

		what: new loader for GUS instrument patch files (pat)

-> file: src/load_pat.h

		where: whole source

		what: new header declaring reuseable GUS instrument patch functions (pat)

-> file: README

		where: 2. Features

		what: added description of load_mid.cpp and load_pat.cpp

-> file: src/Makefile.am

		where: libmodplug_la_SOURCES

		what: added load_mid.cpp and load_pat.cpp

		where: libmodpluginclude_HEADERS

		what: added load_pat.h

-> file: src/sndfile.cpp

		where: function CSoundFile::Create()

		what: added call to ReadMID and ReadPAT

-> file: src/libmodplug/sndfile.h

		where: #define

		what: added MOD_TYPE_PAT (MOD_TYPE_MID already there...)

		where: class CSoundFile

		what: added public function members ReadMID, TestMID, ReadPat, TestPAT and PATsample

date = 24-jun-2006 [Peter Grootswagers]

-> file: src/load_abc.cpp

		where: whole source

		what: new loader for abc files
-> file: README
		where: 2. Features
		what: added description of load_abc.cpp
-> file: src/Makefile.am
		where: libmodplug_la_SOURCES
		what: added load_abc.cpp
-> file: src/sndfile.cpp
		where: function CSoundFile::Create()
		what: added call to ReadABC
-> file: src/libmodplug/sndfile.h
		where: #define
		what: added MOD_TYPE_ABC
		where: class CSoundFile
		what: added public function members ReadABC and TestABC
date = 20-mar-2006 [Macro Trillo / "Custom libmodplug project"]
-> file : src/load_s3m.cpp
-> file : src/load_far.cpp
		what: fixed endianness
date = 20-mar-2006 [Alistair John Strachan]
....

Many other changes need to be documented here...

GCC3 fixes, GCC4 fixes, More Archive Types.

date = 09-feb-2001 [Markus Fick]
-> file: fastmix.cpp
		where: spline creation, spline macros
		what: added unity gain clamp code, added Quantizer_Bits(shift) preprocessor constants
		where: fir creation, fir macros
		what: - removed x<pi/2 condition in coef creation
		      - added quantizer_bits(shift) preprocessor constants
		      - set default quantizer bits to 15 instead 14 (scale now 32768 instead 16384)
		        there should not occure any overflows during fir response calculation because
		        of the symmetric form of filter and the position of the negative fir coefs
		      - changed final volume calculation for 16bit samples (quality enhancement)

date = 08-feb-2001 [Markus Fick]
-> file: sndmix.cpp
		where: function ReadNote()
		what: modified behaviour of modplug so that interpolation is only deactivated if
		      a) the user selects "no interpolation"
			  b) linear interpolation is set and speed incr. > 0xff00
			 => if spline or fir is active then we use always interpolation

-> file: fastmix.cpp
		where: spline macros
		what: changed spline macros to use precalculated tables (way faster)
		where: file
		what: - implemented spline table precalculator
		      - changed fir precalculator + macros (for higher quality and clearer source)
		      - added some comments and documentation
		comment:
		  - preprocessor constant: SPLINE_FRACBITS
		     ) controls quality/memory usage
			   range is [4..14] inclusive
			   4 = low quality, low memory usage
			   14 = highest quality, highest memory usage (1L<<14)*4*2 bytes
		  - preprocessor constant: WFIR_FRACBITS
		     ) controls quality/memory usage
			   range is [4..12] inclusive
			   4 = low quality, low mu
			   12 = highest quality, highest memory usage ((1L<<(12+1))+1)*8*2 bytes

date = 07-feb-2001 [Markus Fick]
-> file: fastmix.cpp
		where: spline macros
		what: fixed error in coef calculation

date = 07-feb-2001 [Markus Fick]
-> file: sndfile.h
		where: class definition of soundfile
		what: removed InitFIR + DoneFIR function prototypes
-> file: sndfile.cpp
		function:CSoundFile::CSoundFile()
		what: [modify] removed call to CSoundFile::InitFIRMixer( )

		function:CSoundFile::~CSoundFile()
		what: [modify] removed call to CSoundFile::DoneFIRMixer( )
-> file: fastmix.cpp
		where: spline macros
		what: changed formula + added some guard bits to calculation

		where: fir macros + implementation
		what: - moved CSoundfile::FIR funtions to CzFIR (single instance sfir)
		      - changed fir macros to support CzFIR class

date = 06-feb-2001 [Markus Fick]
-> file: fastmix.cpp
         where: macros
		 what: - removed fir filter with coef interpolation
		       - add spline interpolation
			RM: now modplug->select( SPLINE ) selects spline and
			        modplug->select( POLYPHASE ) selects 8tap fir filter

date = 05-feb-2001 [Markus Fick]
-> file: fastmix.cpp
         where: macros + filter order
         what: [modify] changed filter order to 8 instead of 10

-> file: fastmix.cpp
         what: new macros+switch for fir-interpolator with coef interpolation

date = 04-feb-2001 [Markus Fick]

-> file: sndfile.h
	 where: class CSoundFile (bottom)
	 what: [add] methods for FIR mixer support
	             1. int InitFIRInterpolator( );
				 2. int DoneFIRInterpolator( );

-> file: sndfile.cpp
     function:CSoundFile::CSoundFile()
	 what: [modify] add call to CSoundFile::InitFIRMixer( )

	 function:CSoundFile::~CSoundFile()
	 what: [modify] add call to CSoundFile::DoneFIRMixer( )

-> file:  fastmix.cpp
	 new include: <math.h>
		why: need it for fir-coef calculation
     new function: CSoundFile::InitFIRMixer( ) // initializes fir filter lookup (if necessary)
	 new function: CSoundFile::DoneFIRMixer( ) // decrements ReferenceCounter (for static vars) and deinitializes fir struct (if possible).
	 new defs:
		#define FIRCPWBN 10							// log2 of number of precalculated wings (-(1L<<FIRCPWBN)..(1L<<FIRCPWBN))
		#define FIRLOPOSSHIFT (16-(FIRCPWBN+1))		// shift for lopos of sampleposition -> (16 - FIRCPWBN - 1)
		#define FIRLEN 9							// number(-1) of multiplications per sample
		#define FIRCUT 0.90f						// cutoff of filter
		#define MIXNDX_FIRMIXERSRC  0x20			// src-type for firfilter
	 new vars:
		static signed short *cFirLut;				// lulines
		static int bFirInitialized	= 0;			// initialized?
		static int nFirOrder		= FIRLEN;		// order (modplug has 4smps pre/post extension, so limit this to 9)
		static float nFirFC			= FIRCUT;		// cutoff (normalized to pi/2)
		static int nFirCpw			= (1L<<FIRCPWBN);	// number of precalculted filter lines
		static int nFirUsers		= 0;			// reference counter
	 new macros:
		#define SNDMIX_GETMONOVOL8FIRFILTER
		#define SNDMIX_GETMONOVOL16FIRFILTER
		#define SNDMIX_GETSTEREOVOL8FIRFILTER
		#define SNDMIX_GETSTEREOVOL16FIRFILTER
	new mixer interface macros:
		BEGIN_MIX_INTERFACE(Mono8BitFirFilterMix)
		BEGIN_MIX_INTERFACE(Mono16BitFirFilterMix)
		BEGIN_RAMPMIX_INTERFACE(Mono8BitFirFilterRampMix)
		BEGIN_RAMPMIX_INTERFACE(Mono16BitFirFilterRampMix)
		BEGIN_MIX_INTERFACE(FastMono8BitFirFilterMix)
		BEGIN_MIX_INTERFACE(FastMono16BitFirFilterMix)
		BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitFirFilterRampMix)
		BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitFirFilterRampMix)
		BEGIN_MIX_INTERFACE(Stereo8BitFirFilterMix)
		BEGIN_MIX_INTERFACE(Stereo16BitFirFilterMix)
		BEGIN_RAMPMIX_INTERFACE(Stereo8BitFirFilterRampMix)
		BEGIN_RAMPMIX_INTERFACE(Stereo16BitFirFilterRampMix)
		BEGIN_MIX_FLT_INTERFACE(FilterMono8BitFirFilterMix)
		BEGIN_MIX_FLT_INTERFACE(FilterMono16BitFirFilterMix)
		BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitFirFilterRampMix)
		BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitFirFilterRampMix)
		BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitFirFilterMix)
		BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitFirFilterMix)
		BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitFirFilterRampMix)
		BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitFirFilterRampMix)
	modified:
		const LPMIXINTERFACE gpMixFunctionTable[2*2*16] // to hold new fir mixer interface
		const LPMIXINTERFACE gpFastMixFunctionTable[2*2*16] // to hold new fir mixer interface
		functioN: UINT CSoundFile::CreateStereoMix(int count)
			new:
				if (!(pChannel->dwFlags & CHN_NOIDO))
				{
					// use hq-fir mixer?
					if( ((gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE)) == (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE)) ||
						((gdwSoundSetup & (SNDMIX_HQRESAMPLER)) == (SNDMIX_HQRESAMPLER)) )
						nFlags += MIXNDX_FIRMIXERSRC;
					else
						nFlags += MIXNDX_LINEARSRC; // use
				}
			was:
				if (!(pChannel->dwFlags & CHN_NOIDO))
				{
					nFlags += MIXNDX_LINEARSRC; // use
				}

path: contrib/libmodplug/ChangeLog


code:
Konstanty Bialkowski <konstanty@ieee.org> Tue Apr  7 15:00:00 AEST 2009
	New release of small fixes from contributers and downstream distributions. (Package config Libs.private addin (from Debian)), usage of uint32_t vs uint for load_abc.cpp,load_mid.cpp,load_pat.cpp.

Konstanty Bialkowski <konstanty@ieee.org> Mon Mar 20 10:00:00 AEST 2006
	New Release based on fixes contributed since last release.

path: contrib/libmodplug/NEWS


code:
Modplug Plugin for XMMS v2.0 / libmodplug v0.8.5
 Based on the ModPlug sound engine by Olivier Lapicque <olivierl@jps.net>
 XMMS plugin by Kenton Varda <temporal@gauge3d.org>
 Maintainer is now Konstanty Bialkowski <konstanty@ieee.org>

Olivier Lapicque, author of Modplug, which is arguably the best quality
MOD-playing software available, has placed his sound rendering code in the
public domain.  This library and plugin is based on that code.

For more information on libmodplug, the library for decoding mod-like music
formats, see libmodplug/README.

Contents
--------
 1. Requirements
 2. Features
 3. Options
 4. Troubleshooting

---------------
1. Requirements
---------------
- POSIX OS (Linux or other unix*)
- XMMS 1.0.0 or higher (only for modplug-xmms plugin).

* This library is only guaranteed to work on Linux.  I have received
  conflicting reports on whether or not it will work on Solaris x86.
  One person reported that the plugin compiled fine with the
  "-fpermissive" compiler flag, which I have added.  Others had far
  more trouble.  Note that a recent change to the library should allow
  it to work on PPC and other big-endian systems.

* Under linux there is also modplugplay contributed, which allows command
 line playing of mod files under Linux.

-----------
2. Features
-----------
- Plays 22 different mod formats, including:
    MOD, S3M, XM, IT, 669, AMF (both of them), AMS, DBM, DMF, DSM, FAR,
    MDL, MED, MTM, OKT, PTM, STM, ULT, UMX, MT2, PSM
- Plays zip, rar, gzip, and bzip2 compressed mods.  The following
    extensions are recognized:
    zip:  MDZ,  S3Z,  XMZ,  ITZ
    rar:  MDR,  S3R,  XMR,  ITR
    gzip: MDGZ, S3GZ, XMGZ, ITGZ
   You can also load plain old ZIP, RAR, and GZ files.  If ModPlug finds
   a mod in them, it will play it.
   Note: To play these formats, you need to have the associated
    decompression utilities (unzip, gunzip, unrar) installed.
   Note(2): The format of the mod is NOT determined from the extension on
    compressed mods.  For example, if you zipped a UMX mod and gave it the
    extension MDZ, it would work fine.
- plays timidity's GUS patch files (*.pat):
   a multi sample pat file with n samples can be played with a Frere Jacques
   canon with n voices.
- plays all types of MIDI files (*.mid):
   uses the timidity .pat files for samples (when available)
   recognizes environment variables:

     MMPAT_PATH_TO_CFG	set to the directory where the file "timidity.cfg" and
      the subdirectory "instruments" can be found,
       default: "/usr/local/share/timidity".
      MMMID_SPEED for experimenting with the mod speed (1 thru 9)
      MMMID_VERBOSE for feedback on the conversion process
      MMMID_DEBUG for sake of completeness, only useful for maintainers

- plays textfiles written in the ABC music notation (*.abc):
   uses the timidity .pat files for samples (when available)
   recognizes environment variables:

      MMPAT_PATH_TO_CFG	set to the directory where the file "timidity.cfg" and
       the subdirectory "instruments" can be found,
       default: "/usr/local/share/timidity".

      MMABC_NO_RANDOM_PICK when not set and the abc file contains multiple songs
       (X:n) the first song to be played will be picked at random another click
       on the play button advances to the next
       song in the file (or the first when the last song has been
       played), when set it can be 0 (zero) or not numeric
       to let it play all songs in the file, a positive number n to
       let it play the n-th song in the file, a negative number -n to
       let it play the n-th song in the file and advancing to the next
       song when the play button is clicked.

      MMABC_DUMPTRACKS when set it gives diagnostic information on stdout,
       values can be:

        all - every event is printed
        nonotes - only the control events (looping, breaks etc.) are printed
           any other value prints the control events and every note event
           immediately succeeding the control events.
- Slightly better sound quality than Mikmod.  Vastly superior quality
   over Winamp.
- All XMMS calls are supported exept for the band gains on the
   equalizer.  The preamp is supported, but MOD music is not anywhere
   near as cheap to equalize as MP3.  Thus, equalization does is not
   supported in this version.  However, a variable bass boost option
   is available in the configuration dialog (see below).
- Tons of playback options (see below).

----------
3. Options
----------
All of the following items are configurable from the plugin
configuration dialog box.

Sampling rate: Higher is better.  Note that the sound is rendered at a
 higher sampling rate and converted down to increase quality.

Bits per sample: 8-bit or 16-bit sound.  Note that all computations are
 done at 32-bit and converted down to the sampling rate you specify.

Channels: mono/stereo.  Note that all computations are done in stereo.
 If you choose mono, the channels will be mixed.

Resampling: Method used to convert samples to different sampling rates.
 "Nearest" is the fastest setting (but sounds terrible), while
 "8-tap fir" is the best-quality setting.

Noise Reduction: Reduces noise. :)

Fast Playlist Info: When this option is on, names of songs in your
 playlist will load considerably faster, but song lengths will not be
 shown and only MOD, S3M, XM, and IT formats will have their names shown.
 Don't worry, though, because all the data which is skipped will still be
 loaded when you actually play the song.  This should probably always be
 on.

Reverb: A nice reverb effect.  The depth and delay of the reverb can be
 tuned to your liking using the sliders.

Bass boost: Variable bass boost effect.  The "range" slider controls the
 frequency range of the bass boost.  If you increase this value, higher
 frequencies will be boosted, but the overall volume increase will be
 less.  (you can compensate by using the volume slider:)

Surround: Dolby Pro-Logic surround effect.  Depth and delay can be fine
 tuned.

Preamp: A global volume boost.  Note that setting the preamp too high
 will cause clipping (nasty clicks and pops).

Looping: Some mods have loops built-in.  Normally, these loops are
 ignored because otherwise the same mod would play forever.  However,
 you can choose to respect the loops, or even set a number of times to
 follow a loop.

------------------
4. Troubleshooting
------------------

Problem:
 None of the config options seem to have any affect on the sound quality.

Possible cause:
 You did not disable MikMod, so mods are being played by it rather than
 by ModPlug.

Solution:
 Disable MikMod in the XMMS input plugins configuration dialog.

Alternative cause:
 You are not playing a mod-like file.  ModPlug only plays mod-like file
 formats, and has no affect on mp3's or other non-mod formats.  (Note:
 Yes, I have actually had people e-mail me with this problem.)

Solution:
 Download some mods.  You can start at <http://www.traxinspace.org>.

Problem:
 Some of my files load up, but show garbled info in the playlist and/or
 don't play correctly.

Possible cause:
 The mod is in a different format than its file format suggests.
 Modplug-XMMS uses a combination of file extension and contents to figure
 out what format a mod is in, and can be thrown off if a mod is
 incorrectly labeled.

Solution:
 Turning off "fast info" in the configuration may fix the problem.  This
 will cause Modplug-XMMS to detect all basic mod types by content, but
 archive types will still be detected by extension.  If this doesn't
 solve the problem, then you probably have files which are actually
 compressed archives but are not labeled as such.  For example, you may
 have a file "aws_anew.xm" which is actually a ZIP archive.  You will
 have to either unzip these files or rename them to have an extension
 associated with their type.  In the case of a ZIP, you can use any of
 the extensions "ZIP, MDZ, S3Z, XMZ, ITZ".  (Note that these five types
 are all treated exactly the same -- the actual format of the mod is
 detected by contents.)

Problem:
 Everything appears to be working, but no sound is being generated.
 MP3's play just fine.

Possible cause:
 Modplug has a relatively low default volume, and you may just not be
 hearing it.  (Note:  Yes, more that one person has e-mailed me with
 this problem.)

Solution:
 Turn up your volume.  You may wish to do this via the "preamp"
 setting in the ModPlug configuration.  This way, you won't have to
 turn down your volume again when you play an MP3.

Problem:
 You have a mod which is rendered incorrectly by ModPlug-XMMS.

Possible cause:
 This could be our fault. :)

Solution:
 First, test the mod using the Windows version of ModPlug, if you can.
 If it sounds wrong there, then send the mod and a bug report to
 Olivier Lapicque <olivierl@jps.net>.  If the mod plays correctly in
 Windows, however, then the bug is my fault.  In that case, e-mail
 me (Konstanty) <konstanty@ieee.org>. (previously Kenton Varda at
 <temporal@gauge3d.org>).

Problem:
 I have a problem which is not listed here, or an idea for a cool
 feature.

Solution:
 E-mail me (Konstanty) at <konstanty@ieee.org>.  I would be
 happy to hear any suggestions or problems you have.

path: contrib/libmodplug/README


code:
Fix and check endian-ness issues.
Add looping flag to sound file / API function for this.

path: contrib/libmodplug/TODO


code:
##
# libmodplug Makefile fragment
##

.PHONY: ${libmodplug}_clean

.SUFFIXES: .cpp

libmodplug_src = contrib/libmodplug/src
libmodplug_obj = contrib/libmodplug/src/.build

libmodplug_cflags = -DHAVE_INTTYPES_H -I${libmodplug_src} \
                    -I${libmodplug_src}/libmodplug \
                    -include src/platform_endian.h

libmodplug_objs = \
    ${libmodplug_obj}/fastmix.o ${libmodplug_obj}/load_669.o \
    ${libmodplug_obj}/load_amf.o ${libmodplug_obj}/load_dsm.o \
    ${libmodplug_obj}/load_far.o ${libmodplug_obj}/load_it.o \
    ${libmodplug_obj}/load_med.o ${libmodplug_obj}/load_mod.o \
    ${libmodplug_obj}/load_mtm.o ${libmodplug_obj}/load_okt.o \
    ${libmodplug_obj}/load_s3m.o ${libmodplug_obj}/load_stm.o \
    ${libmodplug_obj}/load_ult.o ${libmodplug_obj}/load_wav.o \
    ${libmodplug_obj}/load_xm.o ${libmodplug_obj}/mmcmp.o \
    ${libmodplug_obj}/modplug.o ${libmodplug_obj}/snd_dsp.o \
    ${libmodplug_obj}/sndfile.o ${libmodplug_obj}/snd_flt.o \
    ${libmodplug_obj}/snd_fx.o ${libmodplug_obj}/sndmix.o

${libmodplug_obj}/%.o: ${libmodplug_src}/%.cpp
	$(if ${V},,@echo "  CXX     " $<)
	${CXX} -MD ${CXXFLAGS} ${libmodplug_cflags} -c $< -o $@

-include $(libmodplug_objs:.o=.d)

${libmodplug_objs}: $(filter-out $(wildcard ${libmodplug_obj}), ${libmodplug_obj})

libmodplug_clean:
	$(if ${V},,@echo "  RM      " ${libmodplug_obj})
	${RM} -r ${libmodplug_obj}