shithub: ft²

Download patch

ref: 13b187b846a221708d0d18e4475e230f0b639e2e
parent: 4f7f08a867b7e448ce9580279134f4cf7ff82d03
author: Olav Sørensen <[email protected]>
date: Sun May 23 16:35:57 EDT 2021

Pushed v1.47 code (mega-commit, unfortunately)

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,8 +10,10 @@
     "${ft2-clone_SOURCE_DIR}/src/*.c"
     "${ft2-clone_SOURCE_DIR}/src/gfxdata/*.c"
     "${ft2-clone_SOURCE_DIR}/src/mixer/*.c"
+    "${ft2-clone_SOURCE_DIR}/src/scopes/*.c"
     "${ft2-clone_SOURCE_DIR}/src/modloaders/*.c"
     "${ft2-clone_SOURCE_DIR}/src/smploaders/*.c"
+    "${ft2-clone_SOURCE_DIR}/src/libflac/*.c"
 )
 
 add_executable(ft2-clone ${ft2-clone_SRC})
@@ -26,7 +28,11 @@
 
 target_link_libraries(ft2-clone
     PRIVATE m asound pthread ${SDL2_LIBRARIES})
-target_compile_definitions(ft2-clone PRIVATE __LINUX_ALSA__)
 
+target_compile_definitions(ft2-clone
+    PRIVATE HAS_MIDI
+    PRIVATE __LINUX_ALSA__
+    PRIVATE HAS_LIBFLAC)
+
 install(TARGETS ft2-clone
-    RUNTIME DESTINATION bin )
+    RUNTIME DESTINATION bin)
--- a/LICENSES.txt
+++ b/LICENSES.txt
@@ -3,6 +3,9 @@
 Source code (BSD 3-clause):
 src\LICENSE.txt
 
+libFLAC:
+src\libflac\COPYING.txt
+
 rtmidi:
 src\rtmidi\LICENSE.txt
 
--- /dev/null
+++ b/make-linux-nomidi-noflac.sh
@@ -1,0 +1,10 @@
+#!/bin/bash
+
+rm release/other/ft2-clone &> /dev/null
+echo Compiling \(with no MIDI and no FLAC functionality\), please wait patiently...
+
+gcc -DNDEBUG src/gfxdata/*.c src/mixer/*.c src/scopes/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -lSDL2 -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone
+
+rm src/gfxdata/*.o src/*.o &> /dev/null
+
+echo Done. The executable can be found in \'release/other\' if everything went well.
--- a/make-linux-nomidi.sh
+++ /dev/null
@@ -1,10 +1,0 @@
-#!/bin/bash
-
-rm release/other/ft2-clone &> /dev/null
-echo Compiling \(with no MIDI functionality\), please wait patiently...
-
-gcc -DNDEBUG src/gfxdata/*.c src/mixer/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -lSDL2 -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone
-
-rm src/gfxdata/*.o src/*.o &> /dev/null
-
-echo Done. The executable can be found in \'release/other\' if everything went well.
--- a/make-linux.sh
+++ b/make-linux.sh
@@ -3,7 +3,7 @@
 rm release/other/ft2-clone &> /dev/null
 echo Compiling, please wait patiently...
 
-gcc -DNDEBUG -DHAS_MIDI -D__LINUX_ALSA__ src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -lSDL2 -lpthread -lasound -lstdc++ -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone
+gcc -DNDEBUG -DHAS_MIDI -D__LINUX_ALSA__ -DHAS_LIBFLAC src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/scopes/*.c src/modloaders/*.c src/smploaders/*.c src/libflac/*.c src/*.c -lSDL2 -lpthread -lasound -lstdc++ -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone
 
 rm src/rtmidi/*.o src/gfxdata/*.o src/*.o &> /dev/null
 
--- a/make-macos.sh
+++ b/make-macos.sh
@@ -36,7 +36,7 @@
 #
 function compile() {
     rm $1 &> /dev/null
-    clang $VERBOSE $CFLAGS -F /Library/Frameworks -g0 -DNDEBUG -DHAS_MIDI -D__MACOSX_CORE__ -stdlib=libc++ src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default $LDFLAGS -L /Library/Frameworks -framework SDL2 -framework CoreMidi -framework CoreAudio -framework Cocoa -liconv -lpthread -lm -lstdc++ -o $1
+    clang $VERBOSE $CFLAGS -F /Library/Frameworks -g0 -DNDEBUG -DHAS_MIDI -D__MACOSX_CORE__ -DHAS_LIBFLAC -stdlib=libc++ src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/scopes/*.c src/modloaders/*.c src/smploaders/*.c src/libflac/*.c src/*.c -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default $LDFLAGS -L /Library/Frameworks -framework SDL2 -framework CoreMidi -framework CoreAudio -framework Cocoa -liconv -lpthread -lm -lstdc++ -o $1
     return $?
 }
 
--- a/release/LICENSES.txt
+++ b/release/LICENSES.txt
@@ -29,6 +29,41 @@
 
 
 --------------------------------------------------------------------------------
+----------------------------------- libFLAC ------------------------------------
+--------------------------------------------------------------------------------
+
+Copyright (C) 2000-2009  Josh Coalson
+Copyright (C) 2011-2016  Xiph.Org Foundation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+
+
+--------------------------------------------------------------------------------
 ------------------------------------ rtmidi ------------------------------------
 --------------------------------------------------------------------------------
 
--- a/src/ft2_about.c
+++ b/src/ft2_about.c
@@ -7,7 +7,7 @@
 #include "ft2_video.h"
 #include "ft2_structs.h"
 
-#define NUM_STARS 1750
+#define NUM_STARS 2048
 #define ABOUT_SCREEN_W 626
 #define ABOUT_SCREEN_H 167
 #define ABOUT_LOGO_W 449
@@ -26,10 +26,12 @@
 } matrix_t;
 
 static char *customText1 = "Clone by Olav \"8bitbubsy\" S\025rensen";
-static char customText2[64];
+static char *customText2 = "www.16-bits.org";
+static char customText3[64];
 
-static int16_t customText1X, customText2X, customTextY;
-static int32_t lastStarScreenPos[NUM_STARS], starfieldFade, logoFade;
+static int16_t customText1Y, customText2Y, customText3Y;
+static int16_t customText1X, customText2X, customText3X;
+static int32_t lastStarScreenPos[NUM_STARS], fadeValue;
 static uint32_t randSeed, frameCounter;
 static float f2pi;
 static vector_t starPoints[NUM_STARS], rotation;
@@ -139,9 +141,8 @@
 				d = 255;
 
 			d ^= 255;
-			d = (d * starfieldFade) >> 8;
 
-			int32_t r = d - 66;
+			int32_t r = d - 68;
 			if (r < 0)
 				r = 0;
 
@@ -164,20 +165,20 @@
 	rotateMatrix();
 	starfield();
 
-	rotation.x += 0.00009f;
-	rotation.y += 0.00007f;
-	rotation.z -= 0.00005f;
+	rotation.x -= 0.00011f;
+	rotation.z += 0.00006f;
 
-	// fade in starfield
-	if (starfieldFade < 256)
-		starfieldFade += 4;
-
-	// fade in logo after 1 second
-	if (logoFade < 256 && frameCounter++ >= VBLANK_HZ*1)
+	// fade in stuff after 1/3th of a second
+	if (fadeValue < 256 && ++frameCounter >= (int32_t)((VBLANK_HZ/3.0)+0.5))
 	{
-		blit32Fade(91, 31, bmp.ft2AboutLogo, ABOUT_LOGO_W, ABOUT_LOGO_H, logoFade);
-		textOutFade(customText1X, customTextY, PAL_FORGRND, customText1, logoFade);
-		logoFade += 4;
+		blit32Fade(91, 31, bmp.ft2AboutLogo, ABOUT_LOGO_W, ABOUT_LOGO_H, fadeValue);
+		textOutFade(customText1X, customText1Y, PAL_FORGRND, customText1, fadeValue);
+		textOutFade(customText2X, customText2Y, PAL_FORGRND, customText2, fadeValue);
+		textOutFade(customText3X, customText3Y, PAL_FORGRND, customText3, fadeValue);
+
+		fadeValue += 3;
+		if (fadeValue > 256)
+			fadeValue = 256;
 	}
 }
 
@@ -193,14 +194,17 @@
 
 	showPushButton(PB_EXIT_ABOUT);
 
-	sprintf(customText2, "v%s (%s)", PROG_VER_STR, __DATE__);
+	sprintf(customText3, "v%s (%s)", PROG_VER_STR, __DATE__);
 	customText1X = (SCREEN_W - textWidth(customText1)) >> 1;
 	customText2X = (SCREEN_W-8) - textWidth(customText2);
-	customTextY = 157;
-	textOut(customText2X, customTextY, PAL_FORGRND, customText2);
+	customText3X = (SCREEN_W-8) - textWidth(customText3);
+	customText1Y = 157;
+	customText2Y = 157-12;
+	customText3Y = 157;	
 
 	aboutInit();
-	frameCounter = starfieldFade = logoFade = 0;
+	frameCounter = 0;
+	fadeValue = 0;
 	ui.aboutScreenShown = true;
 }
 
--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -7,7 +7,7 @@
 #include <stdint.h>
 #include "ft2_header.h"
 #include "ft2_config.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_video.h"
 #include "ft2_gui.h"
 #include "ft2_midi.h"
@@ -29,10 +29,10 @@
 
 static int8_t pmpCountDiv, pmpChannels = 2;
 static uint16_t smpBuffSize;
-static int32_t randSeed = INITIAL_DITHER_SEED;
-static uint32_t oldAudioFreq, tickTimeLen, tickTimeLenFrac;
-static double dAudioNormalizeMul, dPrngStateL, dPrngStateR, dPanningTab[256+1];
-static voice_t voice[MAX_VOICES * 2];
+static uint32_t oldAudioFreq, tickTimeLen, tickTimeLenFrac, randSeed = INITIAL_DITHER_SEED;
+static float fAudioNormalizeMul, fPanningTab[256+1];
+static double dAudioNormalizeMul, dPrngStateL, dPrngStateR;
+static voice_t voice[MAX_CHANNELS * 2];
 static void (*sendAudSamplesFunc)(uint8_t *, uint32_t, uint8_t); // "send mixed samples" routines
 
 // globalized
@@ -45,16 +45,13 @@
 
 void resetCachedMixerVars(void)
 {
-	stmTyp *ch = stm;
-	for (int32_t i = 0; i < MAX_VOICES; i++, ch++)
+	channel_t *ch = channel;
+	for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++)
 		ch->oldFinalPeriod = -1;
 
 	voice_t *v = voice;
-	for (int32_t i = 0; i < MAX_VOICES*2; i++, v++)
-	{
-		v->dOldHz = 0.0;
+	for (int32_t i = 0; i < MAX_CHANNELS*2; i++, v++)
 		v->oldDelta = 0;
-	}
 }
 
 void stopVoice(int32_t i)
@@ -63,13 +60,13 @@
 
 	v = &voice[i];
 	memset(v, 0, sizeof (voice_t));
-	v->pan = 128;
+	v->panning = 128;
 
 	// clear "fade out" voice too
 
-	v = &voice[MAX_VOICES + i];
+	v = &voice[MAX_CHANNELS + i];
 	memset(v, 0, sizeof (voice_t));
-	v->pan = 128;
+	v->panning = 128;
 }
 
 bool setNewAudioSettings(void) // only call this from the main input/video thread
@@ -125,8 +122,37 @@
 		dAmp *= 32768.0;
 
 	dAudioNormalizeMul = dAmp;
+	fAudioNormalizeMul = (float)dAmp;
 }
 
+void decreaseMasterVol(void)
+{
+	if (config.masterVol >= 16)
+		config.masterVol -= 16;
+	else
+		config.masterVol = 0;
+
+	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
+
+	// if Config -> I/O Devices is open, update master volume scrollbar
+	if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES)
+		drawScrollBar(SB_MASTERVOL_SCROLL);
+}
+
+void increaseMasterVol(void)
+{
+	if (config.masterVol < (256-16))
+		config.masterVol += 16;
+	else
+		config.masterVol = 256;
+
+	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
+
+	// if Config -> I/O Devices is open, update master volume scrollbar
+	if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES)
+		drawScrollBar(SB_MASTERVOL_SCROLL);
+}
+
 void setNewAudioFreq(uint32_t freq) // for song-to-WAV rendering
 {
 	if (freq == 0)
@@ -150,25 +176,22 @@
 		calcReplayerVars(audio.freq);
 }
 
-void P_SetSpeed(uint16_t bpm)
+void setMixerBPM(int32_t bpm)
 {
-	if (bpm == 0)
+	if (bpm < MIN_BPM || bpm > MAX_BPM)
 		return;
 
-	// non-FT2 check for security
-	if (bpm > MAX_BPM)
-		return;
+	int32_t i = bpm - MIN_BPM;
 
-	audio.dSamplesPerTick = audio.dSamplesPerTickTab[bpm];
-	audio.samplesPerTick = (int32_t)(audio.dSamplesPerTick + 0.5);
+	audio.samplesPerTick64 = audio.samplesPerTick64Tab[i]; // fixed-point
+	audio.samplesPerTick = (audio.samplesPerTick64 + (1LL << 31)) >> 32; // rounded
 
-	// get tick time length for audio/video sync timestamp
-	const uint64_t tickTimeLen64 = audio.tickTimeTab[bpm];
-	tickTimeLen = tickTimeLen64 >> 32;
-	tickTimeLenFrac = tickTimeLen64 & UINT32_MAX;
+	// for audio/video sync timestamp
+	tickTimeLen = audio.tickTimeTab[i];
+	tickTimeLenFrac = audio.tickTimeFracTab[i];
 
-	// used for calculating volume ramp length for "ticks" ramps
-	audio.dRampTickMul = audio.dRampTickMulTab[bpm];
+	// for calculating volume ramp length for tick-length ramps
+	audio.fRampTickMul = audio.fRampTickMulTab[i];
 }
 
 void audioSetVolRamp(bool volRamp)
@@ -189,55 +212,53 @@
 {
 	// same formula as FT2's panning table (with 0.0f..1.0f range)
 	for (int32_t i = 0; i <= 256; i++)
-		dPanningTab[i] = sqrt(i * (1.0 / 256.0));
+		fPanningTab[i] = sqrtf(i / 256.0f);
 }
 
 static void voiceUpdateVolumes(int32_t i, uint8_t status)
 {
-	double dDestVolL, dDestVolR;
-
 	voice_t *v = &voice[i];
 
-	const double dVolL = v->dVol * dPanningTab[256-v->pan];
-	const double dVolR = v->dVol * dPanningTab[    v->pan];
+	const float fVolumeL = v->fVolume * fPanningTab[256-v->panning];
+	const float fVolumeR = v->fVolume * fPanningTab[    v->panning];
 
 	if (!audio.volumeRampingFlag)
 	{
 		// volume ramping is disabled
-		v->dVolL = dVolL;
-		v->dVolR = dVolR;
-		v->volRampSamples = 0;
+		v->fVolumeL = fVolumeL;
+		v->fVolumeR = fVolumeR;
+		v->volumeRampLength = 0;
 		return;
 	}
 
-	v->dDestVolL = dVolL;
-	v->dDestVolR = dVolR;
+	v->fVolumeLTarget = fVolumeL;
+	v->fVolumeRTarget = fVolumeR;
 
-	if (status & IS_NyTon)
+	if (status & IS_Trigger)
 	{
 		// sample is about to start, ramp out/in at the same time
 
 		// setup "fade out" voice (only if current voice volume > 0)
-		if (v->dVolL > 0.0 || v->dVolR > 0.0)
+		if (v->fVolumeL > 0.0f || v->fVolumeR > 0.0f)
 		{
-			voice_t *f = &voice[MAX_VOICES+i];
+			voice_t *f = &voice[MAX_CHANNELS+i];
 
 			*f = *v; // copy voice
 
-			f->volRampSamples = audio.quickVolRampSamples;
+			f->volumeRampLength = audio.quickVolRampSamples;
 
-			dDestVolL = -f->dVolL;
-			dDestVolR = -f->dVolR;
+			const float fVolumeLTarget = -f->fVolumeL;
+			const float fVolumeRTarget = -f->fVolumeR;
 
-			f->dVolDeltaL = dDestVolL * audio.dRampQuickVolMul;
-			f->dVolDeltaR = dDestVolR * audio.dRampQuickVolMul;
+			f->fVolumeLDelta = fVolumeLTarget * audio.fRampQuickVolMul;
+			f->fVolumeRDelta = fVolumeRTarget * audio.fRampQuickVolMul;
 
 			f->isFadeOutVoice = true;
 		}
 
 		// make current voice fade in from zero when it starts
-		v->dVolL = 0.0;
-		v->dVolR = 0.0;
+		v->fVolumeL = 0.0f;
+		v->fVolumeR = 0.0f;
 	}
 
 	// ramp volume changes
@@ -248,57 +269,45 @@
 	*/
 
 	// if destination volume and current volume is the same (and we have no sample trigger), don't do ramp
-	if (dVolL == v->dVolL && dVolR == v->dVolR && !(status & IS_NyTon))
+	if (fVolumeL == v->fVolumeL && fVolumeR == v->fVolumeR && !(status & IS_Trigger))
 	{
 		// there is no volume change
-		v->volRampSamples = 0;
+		v->volumeRampLength = 0;
 	}
 	else
 	{
-		dDestVolL = dVolL - v->dVolL;
-		dDestVolR = dVolR - v->dVolR;
+		const float fVolumeLTarget = fVolumeL - v->fVolumeL;
+		const float fVolumeRTarget = fVolumeR - v->fVolumeR;
 
 		if (status & IS_QuickVol)
 		{
-			v->volRampSamples = audio.quickVolRampSamples;
-			v->dVolDeltaL = dDestVolL * audio.dRampQuickVolMul;
-			v->dVolDeltaR = dDestVolR * audio.dRampQuickVolMul;
+			v->fVolumeLDelta = fVolumeLTarget * audio.fRampQuickVolMul;
+			v->fVolumeRDelta = fVolumeRTarget * audio.fRampQuickVolMul;
+			v->volumeRampLength = audio.quickVolRampSamples;
+
 		}
 		else
 		{
-			v->volRampSamples = audio.samplesPerTick;
-			v->dVolDeltaL = dDestVolL * audio.dRampTickMul;
-			v->dVolDeltaR = dDestVolR * audio.dRampTickMul;
+			v->fVolumeLDelta = fVolumeLTarget * audio.fRampTickMul;
+			v->fVolumeRDelta = fVolumeRTarget * audio.fRampTickMul;
+			v->volumeRampLength = audio.samplesPerTick;
 		}
 	}
 }
 
-static void voiceTrigger(int32_t ch, sampleTyp *s, int32_t position)
+static void voiceTrigger(int32_t ch, sample_t *s, int32_t position)
 {
 	voice_t *v = &voice[ch];
 
-	int32_t length = s->len;
-	int32_t loopStart = s->repS;
-	int32_t loopLength = s->repL;
-	int32_t loopEnd = s->repS + s->repL;
-	uint8_t loopType = s->typ & 3;
-	const bool sampleIs16Bit = (s->typ >> 4) & 1;
+	int32_t length = s->length;
+	int32_t loopStart = s->loopStart;
+	int32_t loopLength = s->loopLength;
+	int32_t loopEnd = s->loopStart + s->loopLength;
+	uint8_t loopType = GET_LOOPTYPE(s->flags);
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 
-	if (sampleIs16Bit)
+	if (s->dataPtr == NULL || length < 1)
 	{
-		assert(!(length & 1));
-		assert(!(loopStart & 1));
-		assert(!(loopLength & 1));
-		assert(!(loopEnd & 1));
-
-		length >>= 1;
-		loopStart >>= 1;
-		loopLength >>= 1;
-		loopEnd >>= 1;
-	}
-
-	if (s->pek == NULL || length < 1)
-	{
 		v->active = false; // shut down voice (illegal parameters)
 		return;
 	}
@@ -306,36 +315,36 @@
 	if (loopLength < 1) // disable loop if loopLength is below 1
 		loopType = 0;
 
-	if (sampleIs16Bit)
+	if (sample16Bit)
 	{
-		v->base16 = (const int16_t *)s->pek;
+		v->base16 = (const int16_t *)s->dataPtr;
 		v->revBase16 = &v->base16[loopStart + loopEnd]; // for pingpong loops
 		v->leftEdgeTaps16 = s->leftEdgeTapSamples16 + SINC_LEFT_TAPS;
 	}
 	else
 	{
-		v->base8 = s->pek;
+		v->base8 = s->dataPtr;
 		v->revBase8 = &v->base8[loopStart + loopEnd]; // for pingpong loops
 		v->leftEdgeTaps8 = s->leftEdgeTapSamples8 + SINC_LEFT_TAPS;
 	}
 
 	v->hasLooped = false; // for sinc interpolation special case
-	v->backwards = false;
+	v->samplingBackwards = false;
 	v->loopType = loopType;
-	v->end = (loopType > 0) ? loopEnd : length;
+	v->sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd;
 	v->loopStart = loopStart;
 	v->loopLength = loopLength;
-	v->pos = position;
-	v->posFrac = 0;
+	v->position = position;
+	v->positionFrac = 0;
 
 	// if position overflows, shut down voice (f.ex. through 9xx command)
-	if (v->pos >= v->end)
+	if (v->position >= v->sampleEnd)
 	{
 		v->active = false;
 		return;
 	}
 
-	v->mixFuncOffset = (sampleIs16Bit * 9) + (audio.interpolationType * 3) + loopType;
+	v->mixFuncOffset = (sample16Bit * 9) + (audio.interpolationType * 3) + loopType;
 	v->active = true;
 }
 
@@ -342,20 +351,20 @@
 void resetRampVolumes(void)
 {
 	voice_t *v = voice;
-	for (int32_t i = 0; i < song.antChn; i++, v++)
+	for (int32_t i = 0; i < song.numChannels; i++, v++)
 	{
-		v->dVolL = v->dDestVolL;
-		v->dVolR = v->dDestVolR;
-		v->volRampSamples = 0;
+		v->fVolumeL = v->fVolumeLTarget;
+		v->fVolumeR = v->fVolumeRTarget;
+		v->volumeRampLength = 0;
 	}
 }
 
 void updateVoices(void)
 {
-	stmTyp *ch = stm;
+	channel_t *ch = channel;
 	voice_t *v = voice;
 
-	for (int32_t i = 0; i < song.antChn; i++, ch++, v++)
+	for (int32_t i = 0; i < song.numChannels; i++, ch++, v++)
 	{
 		const uint8_t status = ch->tmpStatus = ch->status; // (tmpStatus is used for audio/video sync queue)
 		if (status == 0)
@@ -364,10 +373,15 @@
 		ch->status = 0;
 
 		if (status & IS_Vol)
-			v->dVol = ch->dFinalVol;
+		{
+			v->fVolume = ch->fFinalVol;
 
+			const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->fFinalVol) + 0.5f); // rounded
+			v->scopeVolume = (uint8_t)scopeVolume;
+		}
+
 		if (status & IS_Pan)
-			v->pan = ch->finalPan;
+			v->panning = ch->finalPan;
 
 		if (status & (IS_Vol + IS_Pan))
 			voiceUpdateVolumes(i, status);
@@ -381,33 +395,33 @@
 
 				if (ch->finalPeriod == 0) // in FT2, period 0 -> delta 0
 				{
-					v->dOldHz = 0.0;
+					v->scopeDelta = 0;
 					v->oldDelta = 0;
-					v->dSincLUT = gKaiserSinc;
+					v->fSincLUT = fKaiserSinc;
 				}
 				else
 				{
 					const double dHz = dPeriod2Hz(ch->finalPeriod);
-					const uint64_t delta64 = (int64_t)((dHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> rounded 32.32 fixed-point
+					const uintCPUWord_t delta = v->oldDelta = (intCPUWord_t)((dHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded)
 
-					v->dOldHz = dHz;
-					v->oldDelta = delta64;
-
-					// decide which sinc LUT to use according to resampling ratio
-					if (delta64 <= 0x130000000) // 1.1875 (32.32fp)
-						v->dSincLUT = gKaiserSinc;
-					else if (delta64 <= 0x180000000) // 1.5 (32.32fp)
-						v->dSincLUT = gDownSample1;
+					// decide which polyphase sinc LUT to use according to resampling ratio
+					if (delta <= (uintCPUWord_t)(1.1875 * MIXER_FRAC_SCALE))
+						v->fSincLUT = fKaiserSinc;
+					else if (delta <= (uintCPUWord_t)(1.5 * MIXER_FRAC_SCALE))
+						v->fSincLUT = fDownSample1;
 					else
-						v->dSincLUT = gDownSample2;
+						v->fSincLUT = fDownSample2;
+
+					// set scope delta
+					const double dHz2ScopeDeltaMul = SCOPE_FRAC_SCALE / (double)SCOPE_HZ;
+					v->scopeDelta = (intCPUWord_t)((dHz * dHz2ScopeDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded)
 				}
 			}
 
 			v->delta = v->oldDelta;
-			v->dHz = v->dOldHz;
 		}
 
-		if (status & IS_NyTon)
+		if (status & IS_Trigger)
 			voiceTrigger(i, ch->smpPtr, ch->smpStartPos);
 	}
 }
@@ -438,7 +452,8 @@
 	{
 		// left channel - 1-bit triangular dithering
 		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
-		dOut = ((audio.dMixBufferL[i] * dAudioNormalizeMul) + dPrng) - dPrngStateL;
+		dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
+		dOut = (dOut + dPrng) - dPrngStateL;
 		dPrngStateL = dPrng;
 		out32 = (int32_t)dOut;
 		CLAMP16(out32);
@@ -446,11 +461,16 @@
 
 		// right channel - 1-bit triangular dithering
 		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
-		dOut = ((audio.dMixBufferR[i] * dAudioNormalizeMul) + dPrng) - dPrngStateR;
+		dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
+		dOut = (dOut + dPrng) - dPrngStateR;
 		dPrngStateR = dPrng;
 		out32 = (int32_t)dOut;
 		CLAMP16(out32);
 		*streamPointer16++ = (int16_t)out32;
+
+		// clear what we read from the mixing buffer
+		audio.fMixBufferL[i] = 0.0f;
+		audio.fMixBufferR[i] = 0.0f;
 	}
 
 	(void)numAudioChannels;
@@ -465,8 +485,9 @@
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel - 1-bit triangular dithering
-		dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5
-		dOut = ((audio.dMixBufferL[i] * dAudioNormalizeMul) + dPrng) - dPrngStateL;
+		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
+		dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul;
+		dOut = (dOut + dPrng) - dPrngStateL;
 		dPrngStateL = dPrng;
 		out32 = (int32_t)dOut;
 		CLAMP16(out32);
@@ -473,13 +494,18 @@
 		*streamPointer16++ = (int16_t)out32;
 
 		// right channel - 1-bit triangular dithering
-		dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5
-		dOut = ((audio.dMixBufferR[i] * dAudioNormalizeMul) + dPrng) - dPrngStateR;
+		dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5
+		dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul;
+		dOut = (dOut + dPrng) - dPrngStateR;
 		dPrngStateR = dPrng;
 		out32 = (int32_t)dOut;
 		CLAMP16(out32);
 		*streamPointer16++ = (int16_t)out32;
 
+		// clear what we read from the mixing buffer
+		audio.fMixBufferL[i] = 0.0f;
+		audio.fMixBufferR[i] = 0.0f;
+
 		// send zeroes to the rest of the channels
 		for (uint32_t j = 2; j < numAudioChannels; j++)
 			*streamPointer16++ = 0;
@@ -488,20 +514,22 @@
 
 static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	double dOut;
-
-	float *fStreamPointer32 = (float *)stream;
+	float fOut, *fStreamPointer32 = (float *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel
-		dOut = audio.dMixBufferL[i] * dAudioNormalizeMul;
-		dOut = CLAMP(dOut, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dOut;
+		fOut = audio.fMixBufferL[i] * fAudioNormalizeMul;
+		fOut = CLAMP(fOut, -1.0f, 1.0f);
+		*fStreamPointer32++ = fOut;
 
 		// right channel
-		dOut = audio.dMixBufferR[i] * dAudioNormalizeMul;
-		dOut = CLAMP(dOut, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dOut;
+		fOut = audio.fMixBufferR[i] * fAudioNormalizeMul;
+		fOut = CLAMP(fOut, -1.0f, 1.0f);
+		*fStreamPointer32++ = fOut;
+
+		// clear what we read from the mixing buffer
+		audio.fMixBufferL[i] = 0.0f;
+		audio.fMixBufferR[i] = 0.0f;
 	}
 
 	(void)numAudioChannels;
@@ -509,21 +537,23 @@
 
 static void sendSamples32BitMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
 {
-	double dOut;
-
-	float *fStreamPointer32 = (float *)stream;
+	float fOut, *fStreamPointer32 = (float *)stream;
 	for (uint32_t i = 0; i < sampleBlockLength; i++)
 	{
 		// left channel
-		dOut = audio.dMixBufferL[i] * dAudioNormalizeMul;
-		dOut = CLAMP(dOut, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dOut;
+		fOut = audio.fMixBufferL[i] * fAudioNormalizeMul;
+		fOut = CLAMP(fOut, -1.0f, 1.0f);
+		*fStreamPointer32++ = fOut;
 
 		// right channel
-		dOut = audio.dMixBufferR[i] * dAudioNormalizeMul;
-		dOut = CLAMP(dOut, -1.0, 1.0);
-		*fStreamPointer32++ = (float)dOut;
+		fOut = audio.fMixBufferR[i] * fAudioNormalizeMul;
+		fOut = CLAMP(fOut, -1.0f, 1.0f);
+		*fStreamPointer32++ = fOut;
 
+		// clear what we read from the mixing buffer
+		audio.fMixBufferL[i] = 0.0f;
+		audio.fMixBufferR[i] = 0.0f;
+
 		// send zeroes to the rest of the channels
 		for (uint32_t j = 2; j < numAudioChannels; j++)
 			*fStreamPointer32++ = 0.0f;
@@ -533,37 +563,37 @@
 static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix)
 {
 	voice_t *v = voice; // normal voices
-	voice_t *r = &voice[MAX_VOICES]; // volume ramp fadeout-voices
+	voice_t *r = &voice[MAX_CHANNELS]; // volume ramp fadeout-voices
 
-	for (int32_t i = 0; i < song.antChn; i++, v++, r++)
+	for (int32_t i = 0; i < song.numChannels; i++, v++, r++)
 	{
 		if (v->active)
 		{
 			bool centerMixFlag;
 
-			const bool volRampFlag = v->volRampSamples > 0;
+			const bool volRampFlag = (v->volumeRampLength > 0);
 			if (volRampFlag)
 			{
-				centerMixFlag = (v->dDestVolL == v->dDestVolR) && (v->dVolDeltaL == v->dVolDeltaR);
+				centerMixFlag = (v->fVolumeLTarget == v->fVolumeRTarget) && (v->fVolumeLDelta == v->fVolumeRDelta);
 			}
 			else // no volume ramping active
 			{
-				if (v->dVolL == 0.0 && v->dVolR == 0.0)
+				if (v->fVolumeL == 0.0f && v->fVolumeR == 0.0f)
 				{
 					silenceMixRoutine(v, samplesToMix);
 					continue;
 				}
 
-				centerMixFlag = v->dVolL == v->dVolR;
+				centerMixFlag = (v->fVolumeL == v->fVolumeR);
 			}
 
-			mixFuncTab[(centerMixFlag * 36) + (volRampFlag * 18) + v->mixFuncOffset](v, bufferPosition, samplesToMix);
+			mixFuncTab[((int32_t)centerMixFlag * 36) + ((int32_t)volRampFlag * 18) + v->mixFuncOffset](v, bufferPosition, samplesToMix);
 		}
 
 		if (r->active) // volume ramp fadeout-voice
 		{
-			const bool centerMixFlag = (r->dDestVolL == r->dDestVolR) && (r->dVolDeltaL == r->dVolDeltaR);
-			mixFuncTab[(centerMixFlag * 36) + 18 + r->mixFuncOffset](r, bufferPosition, samplesToMix);
+			const bool centerMixFlag = (r->fVolumeLTarget == r->fVolumeRTarget) && (r->fVolumeLDelta == r->fVolumeRDelta);
+			mixFuncTab[((int32_t)centerMixFlag * 36) + 18 + r->mixFuncOffset](r, bufferPosition, samplesToMix);
 		}
 	}
 }
@@ -572,9 +602,6 @@
 void mixReplayerTickToBuffer(uint32_t samplesToMix, uint8_t *stream, uint8_t bitDepth)
 {
 	assert(samplesToMix <= MAX_WAV_RENDER_SAMPLES_PER_TICK);
-	memset(audio.dMixBufferL, 0, samplesToMix * sizeof (double));
-	memset(audio.dMixBufferR, 0, samplesToMix * sizeof (double));
-
 	doChannelMixing(0, samplesToMix);
 
 	// normalize mix buffer and send to audio stream
@@ -857,13 +884,13 @@
 	if (songPlaying)
 	{
 		// push pattern variables to sync queue
-		pattSyncData.timer = song.curReplayerTimer;
-		pattSyncData.patternPos = song.curReplayerPattPos;
-		pattSyncData.pattern = song.curReplayerPattNr;
+		pattSyncData.tick = song.curReplayerTick;
+		pattSyncData.row = song.curReplayerRow;
+		pattSyncData.pattNum = song.curReplayerPattNum;
 		pattSyncData.songPos = song.curReplayerSongPos;
-		pattSyncData.speed = song.speed;
-		pattSyncData.tempo = (uint8_t)song.tempo;
-		pattSyncData.globalVol = (uint8_t)song.globVol;
+		pattSyncData.BPM = song.BPM;
+		pattSyncData.speed = (uint8_t)song.speed;
+		pattSyncData.globalVolume = (uint8_t)song.globalVolume;
 		pattSyncData.timestamp = audio.tickTime64;
 		pattQueuePush(pattSyncData);
 	}
@@ -871,24 +898,24 @@
 	// push channel variables to sync queue
 
 	syncedChannel_t *c = chSyncData.channels;
-	stmTyp *s = stm;
+	channel_t *s = channel;
 	voice_t *v = voice;
 
-	for (int32_t i = 0; i < song.antChn; i++, c++, s++, v++)
+	for (int32_t i = 0; i < song.numChannels; i++, c++, s++, v++)
 	{
-		c->vol = s->finalVol;
-		c->dHz = v->dHz;
-		c->instrNr = s->instrNr;
-		c->sampleNr = s->sampleNr;
+		c->scopeVolume = v->scopeVolume;
+		c->scopeDelta = v->scopeDelta;
+		c->instrNum = s->instrNum;
+		c->smpNum = s->smpNum;
 		c->status = s->tmpStatus;
-		c->smpStartPos = (uint16_t)s->smpStartPos;
+		c->smpStartPos = s->smpStartPos;
 
-		c->pianoNoteNr = 255; // no piano key
+		c->pianoNoteNum = 255; // no piano key
 		if (songPlaying && (c->status & IS_Period) && s->envSustainActive)
 		{
-			const int32_t note = getPianoKey(s->finalPeriod, s->fineTune, s->relTonNr);
+			const int32_t note = getPianoKey(s->finalPeriod, s->finetune, s->relativeNote);
 			if (note >= 0 && note <= 95)
-				c->pianoNoteNr = (uint8_t)note;
+				c->pianoNoteNum = (uint8_t)note;
 		}
 	}
 
@@ -914,8 +941,6 @@
 		return;
 
 	assert(len <= MAX_WAV_RENDER_SAMPLES_PER_TICK);
-	memset(audio.dMixBufferL, 0, len * sizeof (double));
-	memset(audio.dMixBufferR, 0, len * sizeof (double));
 
 	int32_t bufferPosition = 0;
 
@@ -922,10 +947,8 @@
 	int32_t samplesLeft = len;
 	while (samplesLeft > 0)
 	{
-		if (audio.dTickSampleCounter <= 0.0)
+		if (audio.tickSampleCounter64 <= 0) // new replayer tick
 		{
-			// new replayer tick
-
 			replayerBusy = true;
 
 			if (audio.volumeRampingFlag)
@@ -935,12 +958,12 @@
 			updateVoices();
 			fillVisualsSyncBuffer();
 
-			audio.dTickSampleCounter += audio.dSamplesPerTick;
+			audio.tickSampleCounter64 += audio.samplesPerTick64;
 
 			replayerBusy = false;
 		}
 
-		const int32_t remainingTick = (int32_t)ceil(audio.dTickSampleCounter);
+		const int32_t remainingTick = (audio.tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards)
 
 		int32_t samplesToMix = samplesLeft;
 		if (samplesToMix > remainingTick)
@@ -950,7 +973,8 @@
 
 		bufferPosition += samplesToMix;
 		samplesLeft -= samplesToMix;
-		audio.dTickSampleCounter -= samplesToMix;
+		audio.tickSampleCounter64 -= (int64_t)samplesToMix << 32;
+
 	}
 
 	// normalize mix buffer and send to audio stream
@@ -961,37 +985,41 @@
 
 static bool setupAudioBuffers(void)
 {
-	const uint32_t sampleSize = sizeof (double);
+	const uint32_t sampleSize = sizeof (float);
 
-	audio.dMixBufferLUnaligned = (double *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256);
-	audio.dMixBufferRUnaligned = (double *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256);
+	audio.fMixBufferLUnaligned = (float *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256);
+	audio.fMixBufferRUnaligned = (float *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256);
 
-	if (audio.dMixBufferLUnaligned == NULL || audio.dMixBufferRUnaligned == NULL)
+	if (audio.fMixBufferLUnaligned == NULL || audio.fMixBufferRUnaligned == NULL)
 		return false;
 
 	// make aligned main pointers
-	audio.dMixBufferL = (double *)ALIGN_PTR(audio.dMixBufferLUnaligned, 256);
-	audio.dMixBufferR = (double *)ALIGN_PTR(audio.dMixBufferRUnaligned, 256);
+	audio.fMixBufferL = (float *)ALIGN_PTR(audio.fMixBufferLUnaligned, 256);
+	audio.fMixBufferR = (float *)ALIGN_PTR(audio.fMixBufferRUnaligned, 256);
 
+	// clear buffers
+	memset(audio.fMixBufferL, 0, MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize);
+	memset(audio.fMixBufferR, 0, MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize);
+
 	return true;
 }
 
 static void freeAudioBuffers(void)
 {
-	if (audio.dMixBufferLUnaligned != NULL)
+	if (audio.fMixBufferLUnaligned != NULL)
 	{
-		free(audio.dMixBufferLUnaligned);
-		audio.dMixBufferLUnaligned = NULL;
+		free(audio.fMixBufferLUnaligned);
+		audio.fMixBufferLUnaligned = NULL;
 	}
 
-	if (audio.dMixBufferRUnaligned != NULL)
+	if (audio.fMixBufferRUnaligned != NULL)
 	{
-		free(audio.dMixBufferRUnaligned);
-		audio.dMixBufferRUnaligned = NULL;
+		free(audio.fMixBufferRUnaligned);
+		audio.fMixBufferRUnaligned = NULL;
 	}
 
-	audio.dMixBufferL = NULL;
-	audio.dMixBufferR = NULL;
+	audio.fMixBufferL = NULL;
+	audio.fMixBufferR = NULL;
 }
 
 void updateSendAudSamplesRoutine(bool lockMixer)
@@ -1058,7 +1086,7 @@
 	closeAudio();
 
 	if (config.audioFreq < MIN_AUDIO_FREQ || config.audioFreq > MAX_AUDIO_FREQ)
-		config.audioFreq = 48000; // set default rate
+		config.audioFreq = DEFAULT_AUDIO_FREQ;
 
 	// get audio buffer size from config special flags
 
@@ -1105,10 +1133,14 @@
 
 	// test if the received audio rate is compatible
 
+#if CPU_64BIT
 	if (have.freq != 44100 && have.freq != 48000 && have.freq != 96000 && have.freq != 192000)
+#else
+	if (have.freq != 44100 && have.freq != 48000)
+#endif
 	{
 		if (showErrorMsg)
-			showErrorMsgBox("Couldn't open audio device:\nThe program doesn't support an audio output rate of %dHz. Sorry!", have.freq);
+			showErrorMsgBox("Couldn't open audio device:\nThis program doesn't support an audio output rate of %dHz. Sorry!", have.freq);
 
 		closeAudio();
 		return false;
@@ -1162,22 +1194,22 @@
 		showConfigScreen();
 
 	updateWavRendererSettings();
-	setAudioAmp(config.boostLevel, config.masterVol, (config.specialFlags & BITDEPTH_32) ? true : false);
+	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
 
 	// don't call stopVoices() in this routine
-	for (int32_t i = 0; i < MAX_VOICES; i++)
+	for (int32_t i = 0; i < MAX_CHANNELS; i++)
 		stopVoice(i);
 
 	stopAllScopes();
 
-	audio.dTickSampleCounter = 0.0; // zero tick sample counter so that it will instantly initiate a tick
+	audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick
 
 	calcReplayerVars(audio.freq);
 
-	if (song.speed == 0)
-		song.speed = 125;
+	if (song.BPM == 0)
+		song.BPM = 125;
 
-	P_SetSpeed(song.speed); // this is important
+	setMixerBPM(song.BPM); // this is important
 
 	updateSendAudSamplesRoutine(false);
 	audio.resetSyncTickTimeFlag = true;
--- a/src/ft2_audio.h
+++ b/src/ft2_audio.h
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include <SDL2/SDL.h>
 #include "ft2_replayer.h"
+#include "ft2_cpu.h"
 
 enum
 {
@@ -11,13 +12,16 @@
 	FREQ_TABLE_AMIGA = 1,
 };
 
+#define DEFAULT_AUDIO_FREQ 48000
+
 #define MIN_AUDIO_FREQ 44100
+
+#if CPU_64BIT
 #define MAX_AUDIO_FREQ 192000
+#else
+#define MAX_AUDIO_FREQ 48000
+#endif
 
-#define MIXER_FRAC_BITS 32
-#define MIXER_FRAC_SCALE (1ULL << MIXER_FRAC_BITS)
-#define MIXER_FRAC_MASK (MIXER_FRAC_SCALE-1)
-
 #define MAX_AUDIO_DEVICES 99
 
 // for audio/video sync queue. (2^n-1 - don't change this! Queue buffer is already BIG in size)
@@ -31,11 +35,13 @@
 	bool linearPeriodsFlag, rescanAudioDevicesSupported;
 	volatile uint8_t interpolationType;
 	int32_t quickVolRampSamples, inputDeviceNum, outputDeviceNum, lastWorkingAudioFreq, lastWorkingAudioBits;
-	uint32_t freq, audLatencyPerfValInt, audLatencyPerfValFrac, samplesPerTick, musicTimeSpeedVal;
-	uint64_t tickTime64, tickTime64Frac, tickTimeTab[MAX_BPM+1];
-	double dRampQuickVolMul, dRampTickMul, dRampTickMulTab[MAX_BPM+1];
-	double *dMixBufferL, *dMixBufferR, *dMixBufferLUnaligned, *dMixBufferRUnaligned, dHz2MixDeltaMul;
-	double dAudioLatencyMs, dSamplesPerTick, dTickSampleCounter, dSamplesPerTickTab[MAX_BPM+1];
+	uint32_t tickTimeTab[(MAX_BPM-MIN_BPM)+1], tickTimeFracTab[(MAX_BPM-MIN_BPM)+1];
+	int64_t tickSampleCounter64, samplesPerTick64, samplesPerTick64Tab[(MAX_BPM-MIN_BPM)+1];
+	uint32_t freq, audLatencyPerfValInt, audLatencyPerfValFrac, samplesPerTick, samplesPerTickFrac, musicTimeSpeedVal;
+	uint64_t tickTime64, tickTime64Frac;
+	float fRampQuickVolMul, fRampTickMul, fRampTickMulTab[(MAX_BPM-MIN_BPM)+1];
+	float *fMixBufferL, *fMixBufferR, *fMixBufferLUnaligned, *fMixBufferRUnaligned;
+	double dHz2MixDeltaMul, dAudioLatencyMs;
 
 	SDL_AudioDeviceID dev;
 	uint32_t wantFreq, haveFreq, wantSamples, haveSamples, wantChannels, haveChannels;
@@ -45,24 +51,25 @@
 {
 	const int8_t *base8, *revBase8;
 	const int16_t *base16, *revBase16;
-	bool active, backwards, isFadeOutVoice, hasLooped;
-	uint8_t mixFuncOffset, pan, loopType;
-	int32_t pos, end, loopStart, loopLength, oldPeriod;
-	uint32_t volRampSamples;
-	uint64_t posFrac, delta, oldDelta;
+	bool active, samplingBackwards, isFadeOutVoice, hasLooped;
+	uint8_t mixFuncOffset, panning, loopType, scopeVolume;
+	int32_t position, sampleEnd, loopStart, loopLength, oldPeriod;
+	uint32_t volumeRampLength;
 
+	uintCPUWord_t positionFrac, delta, oldDelta, scopeDelta;
+#
 	// if (loopEnabled && hasLooped && samplingPos <= loopStart+SINC_LEFT_TAPS) readFixedTapsFromThisPointer();
 	const int8_t *leftEdgeTaps8;
 	const int16_t *leftEdgeTaps16;
 
-	const double *dSincLUT;
-	double dOldHz, dHz, dVol, dDestVolL, dDestVolR, dVolL, dVolR, dVolDeltaL, dVolDeltaR;
+	const float *fSincLUT;
+	float fVolume, fVolumeL, fVolumeR, fVolumeLDelta, fVolumeRDelta, fVolumeLTarget, fVolumeRTarget;
 } voice_t;
 
 typedef struct pattSyncData_t
 {
-	uint8_t pattern, globalVol, songPos, timer, tempo, patternPos;
-	uint16_t speed;
+	uint8_t pattNum, globalVolume, songPos, tick, speed, row;
+	uint16_t BPM;
 	uint64_t timestamp;
 } pattSyncData_t;
 
@@ -74,7 +81,7 @@
 
 typedef struct chSyncData_t
 {
-	syncedChannel_t channels[MAX_VOICES];
+	syncedChannel_t channels[MAX_CHANNELS];
 	uint64_t timestamp;
 } chSyncData_t;
 
@@ -100,11 +107,14 @@
 uint64_t getChQueueTimestamp(void);
 void resetSyncQueues(void);
 
+void decreaseMasterVol(void);
+void increaseMasterVol(void);
+
 void calcPanningTable(void);
 void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag);
 void setNewAudioFreq(uint32_t freq);
 void setBackOldAudioFreq(void);
-void P_SetSpeed(uint16_t bpm);
+void setMixerBPM(int32_t bpm);
 void audioSetVolRamp(bool volRamp);
 void audioSetInterpolationType(uint8_t interpolationType);
 void stopVoice(int32_t i);
--- a/src/ft2_audioselector.c
+++ b/src/ft2_audioselector.c
@@ -360,20 +360,6 @@
 
 void setToDefaultAudioOutputDevice(void)
 {
-	const char *devString = SDL_GetAudioDeviceName(0, false);
-	if (devString == NULL)
-	{
-		if (audio.currOutputDevice != NULL)
-		{
-			free(audio.currOutputDevice);
-			audio.currOutputDevice = NULL;
-		}
-
-		return;
-	}
-
-	const uint32_t stringLen = (uint32_t)strlen(devString);
-
 	if (audio.currOutputDevice != NULL)
 	{
 		free(audio.currOutputDevice);
@@ -380,30 +366,20 @@
 		audio.currOutputDevice = NULL;
 	}
 
-	audio.currOutputDevice = (char *)malloc(stringLen + 1);
-	if (audio.currOutputDevice == NULL)
-		return;
-
-	if (stringLen > 0)
-		strcpy(audio.currOutputDevice, devString);
+	/* If we have more than one device, we can't know which one
+	** is the default (and thus not get its device name).
+	** SDL2 ought to have a function for this!
+	*/
+	if (SDL_GetNumAudioDevices(false) == 1)
+	{
+		const char *devName = SDL_GetAudioDeviceName(0, false);
+		if (devName != NULL)
+			audio.currOutputDevice = strdup(devName);
+	}
 }
 
 void setToDefaultAudioInputDevice(void)
 {
-	const char *devString = SDL_GetAudioDeviceName(0, true);
-	if (devString == NULL)
-	{
-		if (audio.currInputDevice != NULL)
-		{
-			free(audio.currInputDevice);
-			audio.currInputDevice = NULL;
-		}
-
-		return;
-	}
-
-	const uint32_t stringLen = (uint32_t)strlen(devString);
-
 	if (audio.currInputDevice != NULL)
 	{
 		free(audio.currInputDevice);
@@ -410,12 +386,16 @@
 		audio.currInputDevice = NULL;
 	}
 
-	audio.currInputDevice = (char *)malloc(stringLen + 1);
-	if (audio.currInputDevice == NULL)
-		return;
-
-	if (stringLen > 0)
-		strcpy(audio.currInputDevice, devString);
+	/* If we have more than one device, we can't know which one
+	** is the default (and thus not get its device name).
+	** SDL2 ought to have a function for this!
+	*/
+	if (SDL_GetNumAudioDevices(true) == 1)
+	{
+		const char *devName = SDL_GetAudioDeviceName(0, true);
+		if (devName != NULL)
+			audio.currInputDevice = strdup(devName);
+	}
 }
 
 void rescanAudioDevices(void)
--- a/src/ft2_config.c
+++ b/src/ft2_config.c
@@ -32,6 +32,7 @@
 #include "ft2_tables.h"
 #include "ft2_bmp.h"
 #include "ft2_structs.h"
+#include "ft2_cpu.h"
 
 config_t config; // globalized
 
@@ -65,12 +66,18 @@
 	return checksum;
 }
 
-static void loadConfigFromBuffer(void)
+static void loadConfigFromBuffer(bool defaults)
 {
 	lockMixerCallback();
 
 	memcpy(&config, configBuffer, CONFIG_FILE_SIZE);
 
+	if (defaults)
+		config.audioFreq = DEFAULT_AUDIO_FREQ;
+
+	if (config.audioFreq > MAX_AUDIO_FREQ)
+		config.audioFreq = MAX_AUDIO_FREQ;
+
 	// if Nibbles highscore checksum is incorrect, load default highscores instead
 	const int32_t newChecksum = calcChecksum((uint8_t *)&config.NI_HighScore, sizeof (config.NI_HighScore));
 	if (newChecksum != config.NI_HighScoreChecksum)
@@ -125,9 +132,9 @@
 	config.ptnMaxChannels = CLAMP(config.ptnMaxChannels, 0, 3);
 	config.ptnFont = CLAMP(config.ptnFont, 0, 3);
 	config.mouseType = CLAMP(config.mouseType, 0, 3);
-	config.cfg_StdPalNr = CLAMP(config.cfg_StdPalNr, 0, 11);
+	config.cfg_StdPalNum = CLAMP(config.cfg_StdPalNum, 0, 11);
 	config.cfg_SortPriority = CLAMP(config.cfg_SortPriority, 0, 1);
-	config.NI_AntPlayers = CLAMP(config.NI_AntPlayers, 0, 1);
+	config.NI_NumPlayers = CLAMP(config.NI_NumPlayers, 0, 1);
 	config.NI_Speed = CLAMP(config.NI_Speed, 0, 3);
 	config.recMIDIVolSens = CLAMP(config.recMIDIVolSens, 0, 200);
 	config.recMIDIChn  = CLAMP(config.recMIDIChn, 1, 16);
@@ -146,8 +153,12 @@
 		config.recQuantRes = 16;
 	}
 
+#if CPU_64BIT
 	if (config.audioFreq != 44100 && config.audioFreq != 48000 && config.audioFreq != 96000 && config.audioFreq != 192000)
-		config.audioFreq = 48000;
+#else
+	if (config.audioFreq != 44100 && config.audioFreq != 48000)
+#endif
+		config.audioFreq = DEFAULT_AUDIO_FREQ;
 
 	if (config.audioInputFreq <= 1) // default value from FT2 (this was cdr_Sync) - set defaults
 		config.audioInputFreq = INPUT_FREQ_48KHZ;
@@ -174,12 +185,12 @@
 
 	audioSetInterpolationType(config.interpolation);
 	audioSetVolRamp((config.specialFlags & NO_VOLRAMP_FLAG) ? false : true);
-	setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32);
+	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
 	setMouseShape(config.mouseType);
 	changeLogoType(config.id_FastLogo);
 	changeBadgeType(config.id_TritonProd);
 	ui.maxVisibleChannels = (uint8_t)(2 + ((config.ptnMaxChannels + 1) * 2));
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	updatePattFontPtrs();
 
 	unlockMixerCallback();
@@ -195,7 +206,7 @@
 static void setDefaultConfigSettings(void)
 {
 	memcpy(configBuffer, defConfigData, CONFIG_FILE_SIZE);
-	loadConfigFromBuffer();
+	loadConfigFromBuffer(true);
 }
 
 void resetConfig(void)
@@ -320,7 +331,7 @@
 		return false;
 	}
 
-	loadConfigFromBuffer();
+	loadConfigFromBuffer(false);
 	return true;
 }
 
@@ -702,7 +713,7 @@
 		return;
 	}
 
-	loadConfigFromBuffer();
+	loadConfigFromBuffer(false);
 }
 
 // GUI-related code
@@ -812,8 +823,10 @@
 	{
 		         case 44100:  tmpID = RB_CONFIG_AUDIO_44KHZ;  break;
 		default: case 48000:  tmpID = RB_CONFIG_AUDIO_48KHZ;  break;
+#if CPU_64BIT
 		         case 96000:  tmpID = RB_CONFIG_AUDIO_96KHZ;  break;
 		         case 192000: tmpID = RB_CONFIG_AUDIO_192KHZ; break;
+#endif
 	}
 	radioButtons[tmpID].state = RADIOBUTTON_CHECKED;
 
@@ -850,7 +863,7 @@
 
 static void setConfigLayoutCheckButtonStates(void)
 {
-	checkBoxes[CB_CONF_PATTSTRETCH].checked = config.ptnUnpressed;
+	checkBoxes[CB_CONF_PATTSTRETCH].checked = config.ptnStretch;
 	checkBoxes[CB_CONF_HEXCOUNT].checked = config.ptnHex;
 	checkBoxes[CB_CONF_ACCIDENTAL].checked = config.ptnAcc ? true : false;
 	checkBoxes[CB_CONF_SHOWZEROES].checked = config.ptnInstrZero;
@@ -857,7 +870,7 @@
 	checkBoxes[CB_CONF_FRAMEWORK].checked = config.ptnFrmWrk;
 	checkBoxes[CB_CONF_LINECOLORS].checked = config.ptnLineLight;
 	checkBoxes[CB_CONF_CHANNUMS].checked = config.ptnChnNumbers;
-	checkBoxes[CB_CONF_SHOW_VOLCOL].checked = config.ptnS3M;
+	checkBoxes[CB_CONF_SHOW_VOLCOL].checked = config.ptnShowVolColumn;
 	checkBoxes[CB_CONF_SOFTWARE_MOUSE].checked = (config.specialFlags2 & HARDWARE_MOUSE) ? false : true;
 
 	showCheckBox(CB_CONF_PATTSTRETCH);
@@ -939,12 +952,12 @@
 
 	// PALETTE ENTRIES
 	uncheckRadioButtonGroup(RB_GROUP_CONFIG_PAL_ENTRIES);
-	radioButtons[RB_CONFIG_PAL_PATTERNTEXT + cfg_ColorNr].state = RADIOBUTTON_CHECKED;
+	radioButtons[RB_CONFIG_PAL_PATTERNTEXT + cfg_ColorNum].state = RADIOBUTTON_CHECKED;
 	showRadioButtonGroup(RB_GROUP_CONFIG_PAL_ENTRIES);
 
 	// PALETTE PRESET
 	uncheckRadioButtonGroup(RB_GROUP_CONFIG_PAL_PRESET);
-	switch (config.cfg_StdPalNr)
+	switch (config.cfg_StdPalNum)
 	{
 		default:
 		case PAL_ARCTIC:          tmpID = RB_CONFIG_PAL_ARCTIC;          break;
@@ -1141,9 +1154,10 @@
 			textOutShadow(509,   3, PAL_FORGRND, PAL_DSKTOP2, "Mixing frequency:");
 			textOutShadow(525,  17, PAL_FORGRND, PAL_DSKTOP2, "44100Hz");
 			textOutShadow(525,  31, PAL_FORGRND, PAL_DSKTOP2, "48000Hz (default)");
+#if CPU_64BIT
 			textOutShadow(525,  45, PAL_FORGRND, PAL_DSKTOP2, "96000Hz");
 			textOutShadow(525,  59, PAL_FORGRND, PAL_DSKTOP2, "192000Hz");
-
+#endif
 			textOutShadow(509,  76, PAL_FORGRND, PAL_DSKTOP2, "Frequency table:");
 			textOutShadow(525,  90, PAL_FORGRND, PAL_DSKTOP2, "Amiga freq. table");
 			textOutShadow(525, 104, PAL_FORGRND, PAL_DSKTOP2, "Linear freq. table");
@@ -1605,6 +1619,7 @@
 	setNewAudioSettings();
 }
 
+#if CPU_64BIT
 void rbConfigAudio96kHz(void)
 {
 	config.audioFreq = 96000;
@@ -1616,6 +1631,7 @@
 	config.audioFreq = 192000;
 	setNewAudioSettings();
 }
+#endif
 
 void rbConfigAudioInput44kHz(void)
 {
@@ -1638,7 +1654,7 @@
 void rbConfigFreqTableAmiga(void)
 {
 	lockMixerCallback();
-	setFrqTab(false);
+	setFrequencyTable(false);
 	unlockMixerCallback();
 }
 
@@ -1645,7 +1661,7 @@
 void rbConfigFreqTableLinear(void)
 {
 	lockMixerCallback();
-	setFrqTab(true);
+	setFrequencyTable(true);
 	unlockMixerCallback();
 }
 
@@ -1664,7 +1680,7 @@
 static void redrawPatternEditor(void) // called after changing some pattern editor settings in config
 {
 	// if the cursor was on the volume column while we turned volume column off, move it to effect type slot
-	if (!config.ptnS3M && (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2))
+	if (!config.ptnShowVolColumn && (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2))
 		cursor.object = CURSOR_EFX0;
 
 	updateChanNums();
@@ -1673,7 +1689,7 @@
 
 void cbConfigPattStretch(void)
 {
-	config.ptnUnpressed ^= 1;
+	config.ptnStretch ^= 1;
 	redrawPatternEditor();
 }
 
@@ -1716,7 +1732,7 @@
 
 void cbConfigShowVolCol(void)
 {
-	config.ptnS3M ^= 1;
+	config.ptnShowVolColumn ^= 1;
 	redrawPatternEditor();
 }
 
@@ -2156,7 +2172,7 @@
 	if (config.boostLevel != (int8_t)pos + 1)
 	{
 		config.boostLevel = (int8_t)pos + 1;
-		setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32);
+		setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
 		configDrawAmp();
 		updateWavRendererSettings();
 	}
@@ -2177,7 +2193,7 @@
 	if (config.masterVol != (int16_t)pos)
 	{
 		config.masterVol = (int16_t)pos;
-		setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32);
+		setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
 	}
 }
 
--- a/src/ft2_config.h
+++ b/src/ft2_config.h
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include "ft2_replayer.h"
 #include "ft2_palette.h"
+#include "ft2_cpu.h"
 
 #define CFG_ID_STR "FastTracker 2.0 configuration file\x1A"
 #define CONFIG_FILE_SIZE 1736
@@ -110,10 +111,11 @@
 #endif
 highScoreType;
 
+// this has some Swedish names, those variables are not used in our clone.
 typedef struct config_t // exact FT2.CFG layout (with some modifications)
 {
 	char cfgID[35];
-	uint16_t ver;
+	uint16_t version;
 	uint32_t audioFreq; // was "BIOSSum" (never used in FT2)
 	int16_t utEnhet, masterVol, inputVol, inputDev;
 	uint8_t interpolation, internMode, stereoMode;
@@ -120,7 +122,7 @@
 	uint8_t specialFlags2; // was lo-byte of "sample16Bit" (was used for external audio sampling)
 	uint8_t dontShowAgainFlags; // was hi-byte of "sample16Bit" (was used for external audio sampling)
 	int16_t inEnhet, sbPort, sbDMA, sbHiDMA, sbInt, sbOutFilter;
-	uint8_t true16Bit, ptnUnpressed, ptnHex, ptnInstrZero, ptnFrmWrk, ptnLineLight, ptnS3M, ptnChnNumbers;
+	uint8_t true16Bit, ptnStretch, ptnHex, ptnInstrZero, ptnFrmWrk, ptnLineLight, ptnShowVolColumn, ptnChnNumbers;
 	int16_t ptnLineLightStep, ptnFont, ptnAcc;
 	pal16 userPal[16];
 	uint16_t comMacro[10], volMacro[10];
@@ -146,18 +148,18 @@
 	uint8_t tracksPathLen;
 	char tracksPath[79+1];
 	uint8_t id_FastLogo, id_TritonProd;
-	int16_t cfg_StdPalNr;
+	int16_t cfg_StdPalNum;
 	uint8_t cfg_AutoSave;
 	int16_t smpEd_SampleNote;
 	highScoreType NI_HighScore[10];
-	int16_t NI_AntPlayers, NI_Speed;
+	int16_t NI_NumPlayers, NI_Speed;
 	uint8_t NI_Surround, NI_Grid, NI_Wrap;
 	int32_t NI_HighScoreChecksum;
-	int16_t mouseType, mouseAnimType, mouseSpeed, keyLayout, boostLevel, stdEnvP[6][2][12][2];
-	uint16_t stdVolEnvAnt[6], stdVolEnvSust[6], stdVolEnvRepS[6], stdVolEnvRepE[6];
-	uint16_t stdPanEnvAnt[6], stdPanEnvSust[6], stdPanEnvRepS[6], stdPanEnvRepE[6];
-	uint16_t stdFadeOut[6], stdVibRate[6], stdVibDepth[6], stdVibSweep[6], stdVibTyp[6];
-	uint16_t stdVolEnvTyp[6], stdPanEnvTyp[6];
+	int16_t mouseType, mouseAnimType, mouseSpeed, keyLayout, boostLevel, stdEnvPoints[6][2][12][2];
+	uint16_t stdVolEnvLength[6], stdVolEnvSustain[6], stdVolEnvLoopStart[6], stdVolEnvLoopEnd[6];
+	uint16_t stdPanEnvLength[6], stdPanEnvSustain[6], stdPanEnvLoopStart[6], stdPanEnvLoopEnd[6];
+	uint16_t stdFadeout[6], stdVibRate[6], stdVibDepth[6], stdVibSweep[6], stdVibType[6];
+	uint16_t stdVolEnvFlags[6], stdPanEnvFlags[6];
 	int16_t antStars, ptnMaxChannels;
 	uint16_t sampleRates[16];
 	uint8_t cfg_OverwriteWarning;
@@ -216,8 +218,10 @@
 void rbConfigAudioIntrpSinc(void);
 void rbConfigAudio44kHz(void);
 void rbConfigAudio48kHz(void);
+#if CPU_64BIT
 void rbConfigAudio96kHz(void);
 void rbConfigAudio192kHz(void);
+#endif
 void rbConfigAudioInput44kHz(void);
 void rbConfigAudioInput48kHz(void);
 void rbConfigAudioInput96kHz(void);
--- /dev/null
+++ b/src/ft2_cpu.h
@@ -1,0 +1,32 @@
+#pragma once
+
+#include <stdint.h>
+
+#ifdef _WIN32
+
+#ifdef _WIN64
+#define CPU_64BIT 1
+#else
+#define CPU_64BIT 0
+#endif
+
+#else
+#include <limits.h>
+
+#if __WORDSIZE == 64
+#define CPU_64BIT 1
+#else
+#define CPU_64BIT 0
+#endif
+
+#endif
+
+#if CPU_64BIT
+#define CPU_BITS 64
+#define uintCPUWord_t uint64_t
+#define intCPUWord_t int64_t
+#else
+#define CPU_BITS 32
+#define uintCPUWord_t uint32_t
+#define intCPUWord_t int32_t
+#endif
--- a/src/ft2_diskop.c
+++ b/src/ft2_diskop.c
@@ -848,18 +848,18 @@
 	}
 }
 
-static void createOverwriteText(char *name)
+void createFileOverwriteText(char *filename, char *buffer)
 {
 	char nameTmp[128];
 
 	// read entry name to a small buffer
-	const uint32_t nameLen = (uint32_t)strlen(name);
-	memcpy(nameTmp, name, (nameLen >= sizeof (nameTmp)) ? sizeof (nameTmp) : (nameLen + 1));
+	const uint32_t nameLen = (uint32_t)strlen(filename);
+	memcpy(nameTmp, filename, (nameLen >= sizeof (nameTmp)) ? sizeof (nameTmp) : (nameLen + 1));
 	nameTmp[sizeof (nameTmp) - 1] = '\0';
 
 	trimEntryName(nameTmp, false);
 
-	sprintf(FReq_SysReqText, "Overwrite file \"%s\"?", nameTmp);
+	sprintf(buffer, "Overwrite file \"%s\"?", nameTmp);
 }
 
 static void diskOpSave(bool checkOverwrite)
@@ -908,7 +908,7 @@
 
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
-				createOverwriteText(FReq_FileName);
+				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
 				if (okBox(2, "System request", FReq_SysReqText) != 1)
 					return;
 			}
@@ -932,7 +932,7 @@
 
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
-				createOverwriteText(FReq_FileName);
+				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
 				if (okBox(2, "System request", FReq_SysReqText) != 1)
 					return;
 			}
@@ -961,7 +961,7 @@
 
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
-				createOverwriteText(FReq_FileName);
+				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
 				if (okBox(2, "System request", FReq_SysReqText) != 1)
 					return;
 			}
@@ -985,7 +985,7 @@
 
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
-				createOverwriteText(FReq_FileName);
+				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
 				if (okBox(2, "System request", FReq_SysReqText) != 1)
 					return;
 			}
@@ -1008,7 +1008,7 @@
 
 			if (checkOverwrite && fileExistsAnsi(FReq_FileName))
 			{
-				createOverwriteText(FReq_FileName);
+				createFileOverwriteText(FReq_FileName, FReq_SysReqText);
 				if (okBox(2, "System request", FReq_SysReqText) != 1)
 					return;
 			}
--- a/src/ft2_diskop.h
+++ b/src/ft2_diskop.h
@@ -82,4 +82,5 @@
 void rbDiskOpSmpSaveRaw(void);
 void rbDiskOpSmpSaveIff(void);
 void trimEntryName(char *name, bool isDir);
+void createFileOverwriteText(char *filename, char *buffer);
 bool fileExistsAnsi(char *str);
--- a/src/ft2_edit.c
+++ b/src/ft2_edit.c
@@ -33,11 +33,11 @@
 static int8_t lastTranspVal;
 static uint8_t lastInsMode, lastTranspMode;
 static uint32_t transpDelNotes; // count of under-/overflowing notes for warning message
-static tonTyp clearNote;
+static note_t clearNote;
 
-static tonTyp blkCopyBuff[MAX_PATT_LEN * MAX_VOICES];
-static tonTyp ptnCopyBuff[MAX_PATT_LEN * MAX_VOICES];
-static tonTyp trackCopyBuff[MAX_PATT_LEN];
+static note_t blkCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];
+static note_t ptnCopyBuff[MAX_PATT_LEN * MAX_CHANNELS];
+static note_t trackCopyBuff[MAX_PATT_LEN];
 
 static const int8_t tickArr[16] = { 16, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1 };
 
@@ -47,7 +47,7 @@
 static bool testNoteKeys(SDL_Scancode scancode)
 {
 	const int8_t noteNum = scancodeKeyToNote(scancode);
-	if (noteNum == 97)
+	if (noteNum == NOTE_OFF)
 	{
 		// inserts "note off" if editing song
 		if (playMode == PLAYMODE_EDIT || playMode == PLAYMODE_RECPATT || playMode == PLAYMODE_RECSONG)
@@ -55,11 +55,11 @@
 			if (!allocatePattern(editor.editPattern))
 				return true; // key pressed
 
-			patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch].ton = 97;
+			pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch].note = NOTE_OFF;
 
-			const uint16_t pattLen = pattLens[editor.editPattern];
-			if (playMode == PLAYMODE_EDIT && pattLen >= 1)
-				setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true);
+			const uint16_t numRows = patternNumRows[editor.editPattern];
+			if (playMode == PLAYMODE_EDIT && numRows >= 1)
+				setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
 
 			ui.updatePatternEditor = true;
 			setSongModifiedFlag();
@@ -88,7 +88,6 @@
 static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode)
 {
 	int8_t i;
-	uint8_t oldVal;
 
 	if (cursor.object == CURSOR_NOTE)
 	{
@@ -153,18 +152,18 @@
 
 	// insert slot data
 
-	tonTyp *ton = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch];
+	note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
 	switch (cursor.object)
 	{
 		case CURSOR_INST1:
 		{
-			oldVal = ton->instr;
+			uint8_t oldVal = p->instr;
 
-			ton->instr = (ton->instr & 0x0F) | (i << 4);
-			if (ton->instr > MAX_INST)
-				ton->instr = MAX_INST;
+			p->instr = (p->instr & 0x0F) | (i << 4);
+			if (p->instr > MAX_INST)
+				p->instr = MAX_INST;
 
-			if (ton->instr != oldVal)
+			if (p->instr != oldVal)
 				setSongModifiedFlag();
 		}
 		break;
@@ -171,10 +170,10 @@
 
 		case CURSOR_INST2:
 		{
-			oldVal = ton->instr;
-			ton->instr = (ton->instr & 0xF0) | i;
+			uint8_t oldVal = p->instr;
+			p->instr = (p->instr & 0xF0) | i;
 
-			if (ton->instr != oldVal)
+			if (p->instr != oldVal)
 				setSongModifiedFlag();
 		}
 		break;
@@ -181,13 +180,13 @@
 
 		case CURSOR_VOL1:
 		{
-			oldVal = ton->vol;
+			uint8_t oldVal = p->vol;
 
-			ton->vol = (ton->vol & 0x0F) | ((i + 1) << 4);
-			if (ton->vol >= 0x51 && ton->vol <= 0x5F)
-				ton->vol = 0x50;
+			p->vol = (p->vol & 0x0F) | ((i + 1) << 4);
+			if (p->vol >= 0x51 && p->vol <= 0x5F)
+				p->vol = 0x50;
 
-			if (ton->vol != oldVal)
+			if (p->vol != oldVal)
 				setSongModifiedFlag();
 		}
 		break;
@@ -194,17 +193,17 @@
 
 		case CURSOR_VOL2:
 		{
-			oldVal = ton->vol;
+			uint8_t oldVal = p->vol;
 
-			if (ton->vol < 0x10)
-				ton->vol = 0x10 + i;
+			if (p->vol < 0x10)
+				p->vol = 0x10 + i;
 			else
-				ton->vol = (ton->vol & 0xF0) | i;
+				p->vol = (p->vol & 0xF0) | i;
 
-			if (ton->vol >= 0x51 && ton->vol <= 0x5F)
-				ton->vol = 0x50;
+			if (p->vol >= 0x51 && p->vol <= 0x5F)
+				p->vol = 0x50;
 
-			if (ton->vol != oldVal)
+			if (p->vol != oldVal)
 				setSongModifiedFlag();
 		}
 		break;
@@ -211,11 +210,10 @@
 
 		case CURSOR_EFX0:
 		{
-			oldVal = ton->effTyp;
+			uint8_t oldVal = p->efx;
 
-			ton->effTyp = i;
-
-			if (ton->effTyp != oldVal)
+			p->efx = i;
+			if (p->efx != oldVal)
 				setSongModifiedFlag();
 		}
 		break;
@@ -222,11 +220,10 @@
 
 		case CURSOR_EFX1:
 		{
-			oldVal = ton->eff;
+			uint8_t oldVal = p->efxData;
 
-			ton->eff = (ton->eff & 0x0F) | (i << 4);
-
-			if (ton->eff != oldVal)
+			p->efxData = (p->efxData & 0x0F) | (i << 4);
+			if (p->efxData != oldVal)
 				setSongModifiedFlag();
 		}
 		break;
@@ -233,11 +230,10 @@
 
 		case CURSOR_EFX2:
 		{
-			oldVal = ton->eff;
+			uint8_t oldVal = p->efxData;
 
-			ton->eff = (ton->eff & 0xF0) | i;
-
-			if (ton->eff != oldVal)
+			p->efxData = (p->efxData & 0xF0) | i;
+			if (p->efxData != oldVal)
 				setSongModifiedFlag();
 		}
 		break;
@@ -247,9 +243,9 @@
 
 	// increase row (only in edit mode)
 
-	const int16_t pattLen = pattLens[editor.editPattern];
-	if (playMode == PLAYMODE_EDIT && pattLen >= 1)
-		setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true);
+	const int16_t numRows = patternNumRows[editor.editPattern];
+	if (playMode == PLAYMODE_EDIT && numRows >= 1)
+		setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
 
 	if (i == 0) // if we inserted a zero, check if pattern is empty, for killing
 		killPatternIfUnused(editor.editPattern);
@@ -258,87 +254,85 @@
 	return true;
 }
 
-// directly ported from the original FT2 code (fun fact: named EvulateTimeStamp() in the FT2 code)
-static void evaluateTimeStamp(int16_t *songPos, int16_t *pattNr, int16_t *pattPos, int16_t *tick)
+static void evaluateTimeStamp(int16_t *songPos, int16_t *pattNum, int16_t *row, int16_t *tick)
 {
-	int16_t sp = editor.songPos;
-	int16_t nr = editor.editPattern;
-	int16_t row = editor.pattPos;
-	int16_t t = editor.tempo - editor.timer;
+	int16_t outSongPos = editor.songPos;
+	int16_t outPattern = editor.editPattern;
+	int16_t outRow = editor.row;
+	int16_t outTick = editor.speed - editor.tick;
 
-	t = CLAMP(t, 0, editor.tempo - 1);
+	outTick = CLAMP(outTick, 0, editor.speed - 1);
 
 	// this is needed, but also breaks quantization on speed>15
-	if (t > 15)
-		t = 15;
+	if (outTick > 15)
+		outTick = 15;
 
-	const int16_t pattLen = pattLens[nr];
+	const int16_t numRows = patternNumRows[outPattern];
 
 	if (config.recQuant > 0)
 	{
-		int16_t r;
 		if (config.recQuantRes >= 16)
 		{
-			t += (editor.tempo >> 1) + 1;
+			outTick += (editor.speed >> 1) + 1;
 		}
 		else
 		{
-			r = tickArr[config.recQuantRes-1];
+			int16_t r = tickArr[config.recQuantRes-1];
+			int16_t p = outRow & (r - 1);
 
-			int16_t p = row & (r - 1);
 			if (p < (r >> 1))
-				row -= p;
+				outRow -= p;
 			else
-				row = (row + r) - p;
+				outRow = (outRow + r) - p;
 
-			t = 0;
+			outTick = 0;
 		}
 	}
 
-	if (t > editor.tempo)
+	if (outTick > editor.speed)
 	{
-		t -= editor.tempo;
-		row++;
+		outTick -= editor.speed;
+		outRow++;
 	}
 
-	if (row >= pattLen)
+	if (outRow >= numRows)
 	{
+		outRow = 0;
+
 		if (playMode == PLAYMODE_RECSONG)
-			sp++;
+			outSongPos++;
 
-		row = 0;
-		if (sp >= song.len)
-			sp = song.repS;
+		if (outSongPos >= song.songLength)
+			outSongPos = song.songLoopStart;
 
-		nr = song.songTab[sp];
+		outPattern = song.orders[outSongPos];
 	}
 
-	*songPos = sp;
-	*pattNr = nr;
-	*pattPos = row;
-	*tick = t;
+	*songPos = outSongPos;
+	*pattNum = outPattern;
+	*row = outRow;
+	*tick = outTick;
 }
 
-// directly ported from the original FT2 code - what a mess, but it works...
-void recordNote(uint8_t note, int8_t vol)
+void recordNote(uint8_t noteNum, int8_t vol) // directly ported from the original FT2 code - what a mess, but it works...
 {
 	int8_t i;
-	int16_t nr, sp, pattpos, tick;
+	int16_t pattNum, songPos, row, tick;
 	int32_t time;
-	tonTyp *noteData;
+	note_t *p;
 
-	const int16_t oldpattpos = editor.pattPos;
+	const int16_t oldRow = editor.row;
 
 	if (songPlaying)
 	{
 		// row quantization
-		evaluateTimeStamp(&sp, &nr, &pattpos, &tick);
+		evaluateTimeStamp(&songPos, &pattNum, &row, &tick);
 	}
 	else
 	{
-		sp = editor.songPos;
-		nr = editor.editPattern;
-		pattpos = editor.pattPos;
+		songPos = editor.songPos;
+		pattNum = editor.editPattern;
+		row = editor.row;
 		tick = 0;
 	}
 
@@ -345,7 +339,7 @@
 	bool editmode = (playMode == PLAYMODE_EDIT);
 	bool recmode = (playMode == PLAYMODE_RECSONG) || (playMode == PLAYMODE_RECPATT);
 
-	if (note == 97)
+	if (noteNum == NOTE_OFF)
 		vol = 0;
 
 	int8_t c = -1;
@@ -358,7 +352,7 @@
 		if ((config.multiEdit && editmode) || (config.multiRec && recmode))
 		{
 			time = 0x7FFFFFFF;
-			for (i = 0; i < song.antChn; i++)
+			for (i = 0; i < song.numChannels; i++)
 			{
 				if (editor.chnMode[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0)
 				{
@@ -372,9 +366,9 @@
 			c = cursor.ch;
 		}
 
-		for (i = 0; i < song.antChn; i++)
+		for (i = 0; i < song.numChannels; i++)
 		{
-			if (note == editor.keyOnTab[i] && config.multiRecChn[i])
+			if (noteNum == editor.keyOnTab[i] && config.multiRecChn[i])
 				k = i;
 		}
 	}
@@ -388,7 +382,7 @@
 
 			if (songPlaying)
 			{
-				for (i = 0; i < song.antChn; i++)
+				for (i = 0; i < song.numChannels; i++)
 				{
 					if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0 && config.multiRecChn[i])
 					{
@@ -400,7 +394,7 @@
 
 			if (time == 0x7FFFFFFF)
 			{
-				for (i = 0; i < song.antChn; i++)
+				for (i = 0; i < song.numChannels; i++)
 				{
 					if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0)
 					{
@@ -415,9 +409,9 @@
 			c = cursor.ch;
 		}
 
-		for (i = 0; i < song.antChn; i++)
+		for (i = 0; i < song.numChannels; i++)
 		{
-			if (note == editor.keyOnTab[i])
+			if (noteNum == editor.keyOnTab[i])
 				k = i;
 		}
 	}
@@ -429,37 +423,37 @@
 
 		// play note
 
-		editor.keyOnTab[c] = note;
+		editor.keyOnTab[c] = noteNum;
 
-		if (pattpos >= oldpattpos) // non-FT2 fix: only do this if we didn't quantize to next row
+		if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row
 		{
 #ifdef HAS_MIDI
-			playTone(c, editor.curInstr, note, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);
+			playTone(c, editor.curInstr, noteNum, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);
 #else
-			playTone(c, editor.curInstr, note, vol, 0, 0);
+			playTone(c, editor.curInstr, noteNum, vol, 0, 0);
 #endif
 		}
 
 		if (editmode || recmode)
 		{
-			if (allocatePattern(nr))
+			if (allocatePattern(pattNum))
 			{
-				const int16_t pattLen  = pattLens[nr];
-				noteData = &patt[nr][(pattpos * MAX_VOICES) + c];
+				const int16_t numRows = patternNumRows[pattNum];
+				p = &pattern[pattNum][(row * MAX_CHANNELS) + c];
 
 				// insert data
-				noteData->ton = note;
+				p->note = noteNum;
 				if (editor.curInstr > 0)
-					noteData->instr = editor.curInstr;
+					p->instr = editor.curInstr;
 
 				if (vol >= 0)
-					noteData->vol = 0x10 + vol;
+					p->vol = 0x10 + vol;
 
 				if (!recmode)
 				{
 					// increase row (only in edit mode)
-					if (pattLen >= 1)
-						setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true);
+					if (numRows >= 1)
+						setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
 				}
 				else
 				{
@@ -466,8 +460,8 @@
 					// apply tick delay for note if quantization is disabled
 					if (!config.recQuant && tick > 0)
 					{
-						noteData->effTyp = 0x0E;
-						noteData->eff = 0xD0 + (tick & 0x0F);
+						p->efx = 0x0E;
+						p->efxData = 0xD0 + (tick & 0x0F);
 					}
 				}
 
@@ -486,51 +480,55 @@
 		if (c < 0)
 			return;
 
-		editor.keyOnTab[c]   = 0;
-		editor.keyOffTime[c] = ++editor.keyOffNr;
+		editor.keyOffNr++;
 
-		if (pattpos >= oldpattpos) // non-FT2 fix: only do this if we didn't quantize to next row
+		editor.keyOnTab[c] = 0;
+		editor.keyOffTime[c] = editor.keyOffNr;
+
+		if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row
 		{
 #ifdef HAS_MIDI
-			playTone(c, editor.curInstr, 97, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);
+			playTone(c, editor.curInstr, NOTE_OFF, vol, midi.currMIDIVibDepth, midi.currMIDIPitch);
 #else
-			playTone(c, editor.curInstr, 97, vol, 0, 0);
+			playTone(c, editor.curInstr, NOTE_OFF, vol, 0, 0);
 #endif
 		}
 
 		if (config.recRelease && recmode)
 		{
-			if (allocatePattern(nr))
+			if (allocatePattern(pattNum))
 			{
 				// insert data
 
-				int16_t pattLen = pattLens[nr];
-				noteData = &patt[nr][(pattpos * MAX_VOICES) + c];
+				int16_t numRows = patternNumRows[pattNum];
+				p = &pattern[pattNum][(row * MAX_CHANNELS) + c];
 
-				if (noteData->ton != 0)
-					pattpos++;
+				if (p->note != 0)
+					row++;
 
-				if (pattpos >= pattLen)
+				if (row >= numRows)
 				{
+					row = 0;
+
 					if (songPlaying)
-						sp++;
+					{
+						songPos++;
+						if (songPos >= song.songLength)
+							songPos = song.songLoopStart;
 
-					if (sp >= song.len)
-						sp  = song.repS;
-
-					nr = song.songTab[sp];
-					pattpos = 0;
-					pattLen = pattLens[nr];
+						pattNum = song.orders[songPos];
+						numRows = patternNumRows[pattNum];
+					}
 				}
 
-				noteData = &patt[nr][(pattpos * MAX_VOICES) + c];
-				noteData->ton = 97;
+				p = &pattern[pattNum][(row * MAX_CHANNELS) + c];
+				p->note = NOTE_OFF;
 
 				if (!recmode)
 				{
 					// increase row (only in edit mode)
-					if (pattLen >= 1)
-						setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true);
+					if (numRows >= 1)
+						setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
 				}
 				else
 				{
@@ -537,8 +535,8 @@
 					// apply tick delay for note if quantization is disabled
 					if (!config.recQuant && tick > 0)
 					{
-						noteData->effTyp = 0x0E;
-						noteData->eff = 0xD0 + (tick & 0x0F);
+						p->efx = 0x0E;
+						p->efxData = 0xD0 + (tick & 0x0F);
 					}
 				}
 
@@ -557,28 +555,28 @@
 		if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)
 			return false; // we're not editing, test other keys
 
-		if (patt[editor.editPattern] == NULL)
+		if (pattern[editor.editPattern] == NULL)
 			return true;
 
-		tonTyp *note = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch];
+		note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
 
 		if (keyb.leftShiftPressed)
 		{
 			// delete all
-			memset(note, 0, sizeof (tonTyp));
+			p->note = p->instr = p->vol = p->efx = p->efxData = 0;
 		}
 		else if (keyb.leftCtrlPressed)
 		{
 			// delete volume column + effect
-			note->vol = 0;
-			note->effTyp = 0;
-			note->eff = 0;
+			p->vol = 0;
+			p->efx = 0;
+			p->efxData = 0;
 		}
 		else if (keyb.leftAltPressed)
 		{
 			// delete effect
-			note->effTyp = 0;
-			note->eff = 0;
+			p->efx = 0;
+			p->efxData = 0;
 		}
 		else
 		{
@@ -585,13 +583,13 @@
 			if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
 			{
 				// delete volume column
-				note->vol = 0;
+				p->vol = 0;
 			}
 			else
 			{
 				// delete note + instrument
-				note->ton = 0;
-				note->instr = 0;
+				p->note = 0;
+				p->instr = 0;
 			}
 		}
 
@@ -598,9 +596,9 @@
 		killPatternIfUnused(editor.editPattern);
 
 		// increase row (only in edit mode)
-		const int16_t pattLen = pattLens[editor.editPattern];
-		if (playMode == PLAYMODE_EDIT && pattLen >= 1)
-			setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true);
+		const int16_t numRows = patternNumRows[editor.editPattern];
+		if (playMode == PLAYMODE_EDIT && numRows >= 1)
+			setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
 
 		ui.updatePatternEditor = true;
 		setSongModifiedFlag();
@@ -621,19 +619,19 @@
 void writeToMacroSlot(uint8_t slot)
 {
 	uint16_t writeVol = 0;
-	uint16_t writeEff = 0;
+	uint16_t writeEfx = 0;
 
-	if (patt[editor.editPattern] != NULL)
+	if (pattern[editor.editPattern] != NULL)
 	{
-		tonTyp *note = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch];
-		writeVol = note->vol;
-		writeEff = (note->effTyp << 8) | note->eff;
+		note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
+		writeVol = p->vol;
+		writeEfx = (p->efx << 8) | p->efxData;
 	}
 
 	if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
 		config.volMacro[slot] = writeVol;
 	else
-		config.comMacro[slot] = writeEff;
+		config.comMacro[slot] = writeEfx;
 }
 
 void writeFromMacroSlot(uint8_t slot)
@@ -643,32 +641,31 @@
 
 	if (!allocatePattern(editor.editPattern))
 		return;
-
-	const int16_t pattLen = pattLens[editor.editPattern];
-	tonTyp *note = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch];
-
+	
+	note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
 	if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
 	{
-		note->vol = (uint8_t)config.volMacro[slot];
+		p->vol = (uint8_t)config.volMacro[slot];
 	}
 	else
 	{
-		uint8_t effTyp = (uint8_t)(config.comMacro[slot] >> 8);
-		if (effTyp > 35)
+		uint8_t efx = (uint8_t)(config.comMacro[slot] >> 8);
+		if (efx > 35)
 		{
 			// illegal effect
-			note->effTyp = 0;
-			note->eff = 0;
+			p->efx = 0;
+			p->efxData = 0;
 		}
 		else
 		{
-			note->effTyp = effTyp;
-			note->eff = config.comMacro[slot] & 0xFF;
+			p->efx = efx;
+			p->efxData = config.comMacro[slot] & 0xFF;
 		}
 	}
 
-	if (playMode == PLAYMODE_EDIT && pattLen >= 1)
-		setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true);
+	const int16_t numRows = patternNumRows[editor.editPattern];
+	if (playMode == PLAYMODE_EDIT && numRows >= 1)
+		setPos(-1, (editor.row + editor.editRowSkip) % numRows, true);
 
 	killPatternIfUnused(editor.editPattern);
 
@@ -681,24 +678,22 @@
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	const int16_t nr = editor.editPattern;
-
-	tonTyp *pattPtr = patt[nr];
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 		return;
 
-	const int16_t pattPos = editor.pattPos;
-	const int16_t pattLen = pattLens[nr];
+	const int16_t row = editor.row;
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
-	if (pattLen > 1)
+	if (numRows > 1)
 	{
-		for (int32_t i = pattLen-2; i >= pattPos; i--)
-			pattPtr[((i + 1) * MAX_VOICES) + cursor.ch] = pattPtr[(i * MAX_VOICES) + cursor.ch];
+		for (int32_t i = numRows-2; i >= row; i--)
+			p[((i+1) * MAX_CHANNELS) + cursor.ch] = p[(i * MAX_CHANNELS) + cursor.ch];
 	}
 
-	memset(&pattPtr[(pattPos * MAX_VOICES) + cursor.ch], 0, sizeof (tonTyp));
+	memset(&p[(row * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t));
 
-	killPatternIfUnused(nr);
+	killPatternIfUnused(editor.editPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -709,28 +704,26 @@
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	const int16_t nr = editor.editPattern;
+	setPatternLen(editor.editPattern, patternNumRows[editor.editPattern] + config.recTrueInsert); // config.recTrueInsert is 0 or 1
 
-	setPatternLen(nr, pattLens[nr] + config.recTrueInsert); // config.recTrueInsert is 0 or 1
-
-	tonTyp *pattPtr = patt[nr];
-	if (pattPtr != NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p != NULL)
 	{
-		const int16_t pattPos = editor.pattPos;
-		const int16_t pattLen = pattLens[nr];
+		const int16_t row = editor.row;
+		const int16_t numRows = patternNumRows[editor.editPattern];
 
-		if (pattLen > 1)
+		if (numRows > 1)
 		{
-			for (int32_t i = pattLen-2; i >= pattPos; i--)
+			for (int32_t i = numRows-2; i >= row; i--)
 			{
-				for (int32_t j = 0; j < MAX_VOICES; j++)
-					pattPtr[((i + 1) * MAX_VOICES) + j] = pattPtr[(i * MAX_VOICES) + j];
+				for (int32_t j = 0; j < MAX_CHANNELS; j++)
+					p[((i+1) * MAX_CHANNELS) + j] = p[(i * MAX_CHANNELS) + j];
 			}
 		}
 
-		memset(&pattPtr[pattPos * MAX_VOICES], 0, TRACK_WIDTH);
+		memset(&p[row * MAX_CHANNELS], 0, TRACK_WIDTH);
 
-		killPatternIfUnused(nr);
+		killPatternIfUnused(editor.editPattern);
 	}
 
 	ui.updatePatternEditor = true;
@@ -742,34 +735,33 @@
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	const int16_t nr = editor.editPattern;
-	int16_t pattPos = editor.pattPos;
-	const int16_t pattLen = pattLens[nr];
+	int16_t row = editor.row;
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr != NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p != NULL)
 	{
-		if (pattPos > 0)
+		if (row > 0)
 		{
-			pattPos--;
-			editor.pattPos = song.pattPos = pattPos;
+			row--;
+			editor.row = song.row = row;
 
-			for (int32_t i = pattPos; i < pattLen-1; i++)
-				pattPtr[(i * MAX_VOICES) + cursor.ch] = pattPtr[((i + 1) * MAX_VOICES) + cursor.ch];
+			for (int32_t i = row; i < numRows-1; i++)
+				p[(i * MAX_CHANNELS) + cursor.ch] = p[((i+1) * MAX_CHANNELS) + cursor.ch];
 
-			memset(&pattPtr[((pattLen - 1) * MAX_VOICES) + cursor.ch], 0, sizeof (tonTyp));
+			memset(&p[((numRows-1) * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t));
 		}
 	}
 	else
 	{
-		if (pattPos > 0)
+		if (row > 0)
 		{
-			pattPos--;
-			editor.pattPos = song.pattPos = pattPos;
+			row--;
+			editor.row = song.row = row;
 		}
 	}
 
-	killPatternIfUnused(nr);
+	killPatternIfUnused(editor.editPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -780,40 +772,39 @@
 	if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG)
 		return;
 
-	const int16_t nr = editor.editPattern;
-	int16_t pattPos = editor.pattPos;
-	const int16_t pattLen = pattLens[nr];
+	int16_t row = editor.row;
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr != NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p != NULL)
 	{
-		if (pattPos > 0)
+		if (row > 0)
 		{
-			pattPos--;
-			editor.pattPos = song.pattPos = pattPos;
+			row--;
+			editor.row = song.row = row;
 
-			for (int32_t i = pattPos; i < pattLen-1; i++)
+			for (int32_t i = row; i < numRows-1; i++)
 			{
-				for (int32_t j = 0; j < MAX_VOICES; j++)
-					pattPtr[(i * MAX_VOICES) + j] = pattPtr[((i + 1) * MAX_VOICES) + j];
+				for (int32_t j = 0; j < MAX_CHANNELS; j++)
+					p[(i * MAX_CHANNELS) + j] = p[((i+1) * MAX_CHANNELS) + j];
 			}
 
-			memset(&pattPtr[(pattLen - 1) * MAX_VOICES], 0, TRACK_WIDTH);
+			memset(&p[(numRows-1) * MAX_CHANNELS], 0, TRACK_WIDTH);
 		}
 	}
 	else
 	{
-		if (pattPos > 0)
+		if (row > 0)
 		{
-			pattPos--;
-			editor.pattPos = song.pattPos = pattPos;
+			row--;
+			editor.row = song.row = row;
 		}
 	}
 
-	if (config.recTrueInsert && pattLen > 1)
-		setPatternLen(nr, pattLen - 1);
+	if (config.recTrueInsert && numRows > 1)
+		setPatternLen(editor.editPattern, numRows-1);
 
-	killPatternIfUnused(nr);
+	killPatternIfUnused(editor.editPattern);
 
 	ui.updatePatternEditor = true;
 	setSongModifiedFlag();
@@ -823,32 +814,25 @@
 
 static void countOverflowingNotes(uint8_t currInsOnly, uint8_t transpMode, int8_t addVal)
 {
-	uint8_t ton;
-	uint16_t p, pattLen, ch, row;
-	tonTyp *pattPtr;
-
 	transpDelNotes = 0;
 	switch (transpMode)
 	{
 		case TRANSP_TRACK:
 		{
-			pattPtr = patt[editor.editPattern];
-			if (pattPtr == NULL)
+			note_t *p = pattern[editor.editPattern];
+			if (p == NULL)
 				return; // empty pattern
 
-			pattPtr += cursor.ch;
+			p += cursor.ch;
 
-			pattLen = pattLens[editor.editPattern];
-			for (row = 0; row < pattLen; row++)
+			const int32_t numRows = patternNumRows[editor.editPattern];
+			for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS)
 			{
-				ton = pattPtr->ton;
-				if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr))
+				if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
 				{
-					if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0)
+					if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
 						transpDelNotes++;
 				}
-
-				pattPtr += MAX_VOICES;
 			}
 		}
 		break;
@@ -855,26 +839,23 @@
 
 		case TRANSP_PATT:
 		{
-			pattPtr = patt[editor.editPattern];
-			if (pattPtr == NULL)
+			note_t *p = pattern[editor.editPattern];
+			if (p == NULL)
 				return; // empty pattern
 
-			pattLen = pattLens[editor.editPattern];
-			for (row = 0; row < pattLen; row++)
+			const int32_t numRows = patternNumRows[editor.editPattern];
+			const int32_t pitch = MAX_CHANNELS-song.numChannels;
+
+			for (int32_t row = 0; row < numRows; row++, p += pitch)
 			{
-				for (ch = 0; ch < song.antChn; ch++)
+				for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 				{
-					ton = pattPtr->ton;
-					if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr))
+					if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
 					{
-						if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0)
+						if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
 							transpDelNotes++;
 					}
-
-					pattPtr++;
 				}
-
-				pattPtr += MAX_VOICES - song.antChn;
 			}
 		}
 		break;
@@ -881,28 +862,24 @@
 
 		case TRANSP_SONG:
 		{
-			for (p = 0; p < MAX_PATTERNS; p++)
+			const int32_t pitch = MAX_CHANNELS-song.numChannels;
+			for (int32_t i = 0; i < MAX_PATTERNS; i++)
 			{
-				pattPtr = patt[p];
-				if (pattPtr == NULL)
+				note_t *p = pattern[i];
+				if (p == NULL)
 					continue; // empty pattern
 
-				pattLen = pattLens[p];
-				for (row = 0; row < pattLen; row++)
+				const int32_t numRows = patternNumRows[i];
+				for (int32_t row = 0; row < numRows; row++, p += pitch)
 				{
-					for (ch = 0; ch < song.antChn; ch++)
+					for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 					{
-						ton = pattPtr->ton;
-						if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr))
+						if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
 						{
-							if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0)
+							if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
 								transpDelNotes++;
 						}
-
-						pattPtr++;
 					}
-
-					pattPtr += MAX_VOICES - song.antChn;
 				}
 			}
 		}
@@ -913,28 +890,23 @@
 			if (pattMark.markY1 == pattMark.markY2)
 				return; // no pattern marking
 
-			pattPtr = patt[editor.editPattern];
-			if (pattPtr == NULL)
+			note_t *p = pattern[editor.editPattern];
+			if (p == NULL)
 				return; // empty pattern
 
-			pattPtr += (pattMark.markY1 * MAX_VOICES) + pattMark.markX1;
+			p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1;
 
-			pattLen = pattLens[editor.editPattern];
-			for (row = pattMark.markY1; row < pattMark.markY2; row++)
+			const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1);
+			for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch)
 			{
-				for (ch = pattMark.markX1; ch <= pattMark.markX2; ch++)
+				for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++)
 				{
-					ton = pattPtr->ton;
-					if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr))
+					if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr))
 					{
-						if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0)
+						if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0)
 							transpDelNotes++;
 					}
-
-					pattPtr++;
 				}
-
-				pattPtr += MAX_VOICES - ((pattMark.markX2 + 1) - pattMark.markX1);
 			}
 		}
 		break;
@@ -946,9 +918,6 @@
 void doTranspose(void)
 {
 	char text[48];
-	uint8_t ton;
-	uint16_t p, pattLen, ch, row;
-	tonTyp *pattPtr;
 
 	countOverflowingNotes(lastInsMode, lastTranspMode, lastTranspVal);
 	if (transpDelNotes > 0)
@@ -963,26 +932,24 @@
 	{
 		case TRANSP_TRACK:
 		{
-			pattPtr = patt[editor.editPattern];
-			if (pattPtr == NULL)
+			note_t *p = pattern[editor.editPattern];
+			if (p == NULL)
 				return; // empty pattern
 
-			pattPtr += cursor.ch;
+			p += cursor.ch;
 
-			pattLen = pattLens[editor.editPattern];
-			for (row = 0; row < pattLen; row++)
+			const int32_t numRows = patternNumRows[editor.editPattern];
+			for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS)
 			{
-				ton = pattPtr->ton;
-				if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr))
+				uint8_t note = p->note;
+				if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
 				{
-					ton += lastTranspVal;
-					if (ton > 96)
-						ton = 0; // also handles underflow
+					note += lastTranspVal;
+					if (note > 96)
+						note = 0; // also handles underflow
 
-					pattPtr->ton = ton;
+					p->note = note;
 				}
-
-				pattPtr += MAX_VOICES;
 			}
 		}
 		break;
@@ -989,29 +956,27 @@
 
 		case TRANSP_PATT:
 		{
-			pattPtr = patt[editor.editPattern];
-			if (pattPtr == NULL)
+			note_t *p = pattern[editor.editPattern];
+			if (p == NULL)
 				return; // empty pattern
 
-			pattLen = pattLens[editor.editPattern];
-			for (row = 0; row < pattLen; row++)
+			const int32_t numRows = patternNumRows[editor.editPattern];
+			const int32_t pitch = MAX_CHANNELS - song.numChannels;
+
+			for (int32_t row = 0; row < numRows; row++, p += pitch)
 			{
-				for (ch = 0; ch < song.antChn; ch++)
+				for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 				{
-					ton = pattPtr->ton;
-					if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr))
+					uint8_t note = p->note;
+					if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
 					{
-						ton += lastTranspVal;
-						if (ton > 96)
-							ton = 0; // also handles underflow
+						note += lastTranspVal;
+						if (note > 96)
+							note = 0; // also handles underflow
 
-						pattPtr->ton = ton;
+						p->note = note;
 					}
-
-					pattPtr++;
 				}
-
-				pattPtr += MAX_VOICES - song.antChn;
 			}
 		}
 		break;
@@ -1018,31 +983,28 @@
 
 		case TRANSP_SONG:
 		{
-			for (p = 0; p < MAX_PATTERNS; p++)
+			const int32_t pitch = MAX_CHANNELS - song.numChannels;
+			for (int32_t i = 0; i < MAX_PATTERNS; i++)
 			{
-				pattPtr = patt[p];
-				if (pattPtr == NULL)
+				note_t *p = pattern[i];
+				if (p == NULL)
 					continue; // empty pattern
 
-				pattLen  = pattLens[p];
-				for (row = 0; row < pattLen; row++)
+				const int32_t numRows = patternNumRows[i];
+				for (int32_t row = 0; row < numRows; row++, p += pitch)
 				{
-					for (ch = 0; ch < song.antChn; ch++)
+					for (int32_t ch = 0; ch < song.numChannels; ch++, p++)
 					{
-						ton = pattPtr->ton;
-						if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr))
+						uint8_t note = p->note;
+						if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
 						{
-							ton += lastTranspVal;
-							if (ton > 96)
-								ton = 0; // also handles underflow
+							note += lastTranspVal;
+							if (note > 96)
+								note = 0; // also handles underflow
 
-							pattPtr->ton = ton;
+							p->note = note;
 						}
-
-						pattPtr++;
 					}
-
-					pattPtr += MAX_VOICES - song.antChn;
 				}
 			}
 		}
@@ -1053,31 +1015,27 @@
 			if (pattMark.markY1 == pattMark.markY2)
 				return; // no pattern marking
 
-			pattPtr = patt[editor.editPattern];
-			if (pattPtr == NULL)
+			note_t *p = pattern[editor.editPattern];
+			if (p == NULL)
 				return; // empty pattern
 
-			pattPtr += (pattMark.markY1 * MAX_VOICES) + pattMark.markX1;
+			p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1;
 
-			pattLen = pattLens[editor.editPattern];
-			for (row = pattMark.markY1; row < pattMark.markY2; row++)
+			const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1);
+			for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch)
 			{
-				for (ch = pattMark.markX1; ch <= pattMark.markX2; ch++)
+				for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++)
 				{
-					ton = pattPtr->ton;
-					if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr))
+					uint8_t note = p->note;
+					if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr))
 					{
-						ton += lastTranspVal;
-						if (ton > 96)
-							ton = 0; // also handles underflow
+						note += lastTranspVal;
+						if (note > 96)
+							note = 0; // also handles underflow
 
-						pattPtr->ton = ton;
+						p->note = note;
 					}
-
-					pattPtr++;
 				}
-
-				pattPtr += MAX_VOICES - ((pattMark.markX2 + 1) - pattMark.markX1);
 			}
 		}
 		break;
@@ -1345,15 +1303,15 @@
 	doTranspose();
 }
 
-void copyNote(tonTyp *src, tonTyp *dst)
+void copyNote(note_t *src, note_t *dst)
 {
 	if (editor.copyMaskEnable)
 	{
-		if (editor.copyMask[0]) dst->ton = src->ton;
+		if (editor.copyMask[0]) dst->note = src->note;
 		if (editor.copyMask[1]) dst->instr = src->instr;
 		if (editor.copyMask[2]) dst->vol = src->vol;
-		if (editor.copyMask[3]) dst->effTyp = src->effTyp;
-		if (editor.copyMask[4]) dst->eff = src->eff;
+		if (editor.copyMask[3]) dst->efx = src->efx;
+		if (editor.copyMask[4]) dst->efxData = src->efxData;
 	}
 	else
 	{
@@ -1361,15 +1319,15 @@
 	}
 }
 
-void pasteNote(tonTyp *src, tonTyp *dst)
+void pasteNote(note_t *src, note_t *dst)
 {
 	if (editor.copyMaskEnable)
 	{
-		if (editor.copyMask[0] && (src->ton    != 0 || !editor.transpMask[0])) dst->ton = src->ton;
-		if (editor.copyMask[1] && (src->instr  != 0 || !editor.transpMask[1])) dst->instr = src->instr;
-		if (editor.copyMask[2] && (src->vol    != 0 || !editor.transpMask[2])) dst->vol = src->vol;
-		if (editor.copyMask[3] && (src->effTyp != 0 || !editor.transpMask[3])) dst->effTyp = src->effTyp;
-		if (editor.copyMask[4] && (src->eff    != 0 || !editor.transpMask[4])) dst->eff = src->eff;
+		if (editor.copyMask[0] && (src->note    != 0 || !editor.transpMask[0])) dst->note = src->note;
+		if (editor.copyMask[1] && (src->instr   != 0 || !editor.transpMask[1])) dst->instr = src->instr;
+		if (editor.copyMask[2] && (src->vol     != 0 || !editor.transpMask[2])) dst->vol = src->vol;
+		if (editor.copyMask[3] && (src->efx     != 0 || !editor.transpMask[3])) dst->efx = src->efx;
+		if (editor.copyMask[4] && (src->efxData != 0 || !editor.transpMask[4])) dst->efxData = src->efxData;
 	}
 	else
 	{
@@ -1379,24 +1337,24 @@
 
 void cutTrack(void)
 {
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 		return;
 
-	const int16_t pattLen = pattLens[editor.editPattern];
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
 	if (config.ptnCutToBuffer)
 	{
-		memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (tonTyp));
-		for (int16_t i = 0; i < pattLen; i++)
-			copyNote(&pattPtr[(i * MAX_VOICES) + cursor.ch], &trackCopyBuff[i]);
+		memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t));
+		for (int16_t i = 0; i < numRows; i++)
+			copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]);
 
-		trkBufLen = pattLen;
+		trkBufLen = numRows;
 	}
 
 	pauseMusic();
-	for (int16_t i = 0; i < pattLen; i++)
-		pasteNote(&clearNote, &pattPtr[(i * MAX_VOICES) + cursor.ch]);
+	for (int16_t i = 0; i < numRows; i++)
+		pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + cursor.ch]);
 	resumeMusic();
 
 	killPatternIfUnused(editor.editPattern);
@@ -1407,17 +1365,17 @@
 
 void copyTrack(void)
 {
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 		return;
 
-	const int16_t pattLen = pattLens[editor.editPattern];
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
-	memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (tonTyp));
-	for (int16_t i = 0; i < pattLen; i++)
-		copyNote(&pattPtr[(i * MAX_VOICES) + cursor.ch], &trackCopyBuff[i]);
+	memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t));
+	for (int16_t i = 0; i < numRows; i++)
+		copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]);
 
-	trkBufLen = pattLen;
+	trkBufLen = numRows;
 }
 
 void pasteTrack(void)
@@ -1425,12 +1383,12 @@
 	if (trkBufLen == 0 || !allocatePattern(editor.editPattern))
 		return;
 
-	tonTyp *pattPtr = patt[editor.editPattern];
-	const int16_t pattLen = pattLens[editor.editPattern];
+	note_t *p = pattern[editor.editPattern];
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
 	pauseMusic();
-	for (int16_t i = 0; i < pattLen; i++)
-		pasteNote(&trackCopyBuff[i], &pattPtr[(i * MAX_VOICES) + cursor.ch]);
+	for (int16_t i = 0; i < numRows; i++)
+		pasteNote(&trackCopyBuff[i], &p[(i * MAX_CHANNELS) + cursor.ch]);
 	resumeMusic();
 
 	killPatternIfUnused(editor.editPattern);
@@ -1441,29 +1399,29 @@
 
 void cutPattern(void)
 {
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 		return;
 
-	const int16_t pattLen = pattLens[editor.editPattern];
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
 	if (config.ptnCutToBuffer)
 	{
-		memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_VOICES) * sizeof (tonTyp));
-		for (int16_t x = 0; x < song.antChn; x++)
+		memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t));
+		for (int16_t x = 0; x < song.numChannels; x++)
 		{
-			for (int16_t i = 0; i < pattLen; i++)
-				copyNote(&pattPtr[(i * MAX_VOICES) + x], &ptnCopyBuff[(i * MAX_VOICES) + x]);
+			for (int16_t i = 0; i < numRows; i++)
+				copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]);
 		}
 
-		ptnBufLen = pattLen;
+		ptnBufLen = numRows;
 	}
 
 	pauseMusic();
-	for (int16_t x = 0; x < song.antChn; x++)
+	for (int16_t x = 0; x < song.numChannels; x++)
 	{
-		for (int16_t i = 0; i < pattLen; i++)
-			pasteNote(&clearNote, &pattPtr[(i * MAX_VOICES) + x]);
+		for (int16_t i = 0; i < numRows; i++)
+			pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + x]);
 	}
 	resumeMusic();
 
@@ -1475,20 +1433,20 @@
 
 void copyPattern(void)
 {
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 		return;
 
-	const int16_t pattLen = pattLens[editor.editPattern];
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
-	memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_VOICES) * sizeof (tonTyp));
-	for (int16_t x = 0; x < song.antChn; x++)
+	memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t));
+	for (int16_t x = 0; x < song.numChannels; x++)
 	{
-		for (int16_t i = 0; i < pattLen; i++)
-			copyNote(&pattPtr[(i * MAX_VOICES) + x], &ptnCopyBuff[(i * MAX_VOICES) + x]);
+		for (int16_t i = 0; i < numRows; i++)
+			copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]);
 	}
 
-	ptnBufLen = pattLen;
+	ptnBufLen = numRows;
 
 	ui.updatePatternEditor = true;
 }
@@ -1498,7 +1456,7 @@
 	if (ptnBufLen == 0)
 		return;
 
-	if (pattLens[editor.editPattern] != ptnBufLen)
+	if (patternNumRows[editor.editPattern] != ptnBufLen)
 	{
 		if (okBox(1, "System request", "Change pattern length to copybuffer's length?") == 1)
 			setPatternLen(editor.editPattern, ptnBufLen);
@@ -1507,14 +1465,14 @@
 	if (!allocatePattern(editor.editPattern))
 		return;
 
-	tonTyp *pattPtr = patt[editor.editPattern];
-	const int16_t pattLen = pattLens[editor.editPattern];
+	note_t *p = pattern[editor.editPattern];
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
 	pauseMusic();
-	for (int16_t x = 0; x < song.antChn; x++)
+	for (int16_t x = 0; x < song.numChannels; x++)
 	{
-		for (int16_t i = 0; i < pattLen; i++)
-			pasteNote(&ptnCopyBuff[(i * MAX_VOICES) + x], &pattPtr[(i * MAX_VOICES) + x]);
+		for (int16_t i = 0; i < numRows; i++)
+			pasteNote(&ptnCopyBuff[(i * MAX_CHANNELS) + x], &p[(i * MAX_CHANNELS) + x]);
 	}
 	resumeMusic();
 
@@ -1529,8 +1487,8 @@
 	if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
 		return;
 
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 		return;
 
 	if (config.ptnCutToBuffer)
@@ -1539,9 +1497,8 @@
 		{
 			for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
 			{
-				assert(x < song.antChn && y < pattLens[editor.editPattern]);
-				copyNote(&pattPtr[(y * MAX_VOICES) + x],
-				         &blkCopyBuff[((y - pattMark.markY1) * MAX_VOICES) + (x - pattMark.markX1)]);
+				assert(x < song.numChannels && y < patternNumRows[editor.editPattern]);
+				copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]);
 			}
 		}
 	}
@@ -1550,7 +1507,7 @@
 	for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
 	{
 		for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
-			pasteNote(&clearNote, &pattPtr[(y * MAX_VOICES) + x]);
+			pasteNote(&clearNote, &p[(y * MAX_CHANNELS) + x]);
 	}
 	resumeMusic();
 
@@ -1569,8 +1526,8 @@
 	if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
 		return;
 
-	tonTyp *pattPtr = patt[editor.editPattern];
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 		return;
 
 	for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++)
@@ -1577,9 +1534,8 @@
 	{
 		for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++)
 		{
-			assert(x < song.antChn && y < pattLens[editor.editPattern]);
-			copyNote(&pattPtr[(y * MAX_VOICES) + x],
-			         &blkCopyBuff[((y - pattMark.markY1) * MAX_VOICES) + (x - pattMark.markX1)]);
+			assert(x < song.numChannels && y < patternNumRows[editor.editPattern]);
+			copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]);
 		}
 	}
 
@@ -1593,20 +1549,20 @@
 	if (!blockCopied || !allocatePattern(editor.editPattern))
 		return;
 
-	const int16_t pattLen = pattLens[editor.editPattern];
+	const int16_t numRows = patternNumRows[editor.editPattern];
 
 	const int32_t xpos = cursor.ch;
-	const int32_t ypos = editor.pattPos;
+	const int32_t ypos = editor.row;
 
 	int32_t j = markXSize;
-	if (j+xpos >= song.antChn)
-		j = song.antChn - xpos - 1;
+	if (j+xpos >= song.numChannels)
+		j = song.numChannels - xpos - 1;
 
 	int32_t k = markYSize;
-	if (k+ypos >= pattLen)
-		k = pattLen - ypos;
+	if (k+ypos >= numRows)
+		k = numRows-ypos;
 
-	tonTyp *pattPtr = patt[editor.editPattern];
+	note_t *p = pattern[editor.editPattern];
 
 	pauseMusic();
 	for (int32_t x = xpos; x <= xpos+j; x++)
@@ -1613,8 +1569,8 @@
 	{
 		for (int32_t y = ypos; y < ypos+k; y++)
 		{
-			assert(x < song.antChn && y < pattLen);
-			pasteNote(&blkCopyBuff[((y - ypos) * MAX_VOICES) + (x - xpos)], &pattPtr[(y * MAX_VOICES) + x]);
+			assert(x < song.numChannels && y < numRows);
+			pasteNote(&blkCopyBuff[((y - ypos) * MAX_CHANNELS) + (x - xpos)], &p[(y * MAX_CHANNELS) + x]);
 		}
 	}
 	resumeMusic();
@@ -1625,29 +1581,24 @@
 	setSongModifiedFlag();
 }
 
-static void remapInstrXY(uint16_t nr, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t src, uint8_t dst)
+static void remapInstrXY(uint16_t pattNum, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t src, uint8_t dst)
 {
 	// this routine is only used sanely, so no need to check input
 
-	tonTyp *pattPtr = patt[nr];
+	note_t *pattPtr = pattern[pattNum];
 	if (pattPtr == NULL)
 		return;
 
-	const int32_t noteSkipLen = MAX_VOICES - ((x2 + 1) - x1);
-	tonTyp *note = &pattPtr[(y1 * MAX_VOICES) + x1];
+	note_t *p = &pattPtr[(y1 * MAX_CHANNELS) + x1];
 
-	for (uint16_t y = y1; y <= y2; y++)
+	const int32_t pitch = MAX_CHANNELS - ((x2 + 1) - x1);
+	for (uint16_t y = y1; y <= y2; y++, p += pitch)
 	{
-		for (uint16_t x = x1; x <= x2; x++)
+		for (uint16_t x = x1; x <= x2; x++, p++)
 		{
-			assert(x < song.antChn && y < pattLens[nr]);
-			if (note->instr == src)
-				note->instr = dst;
-
-			note++;
+			if (p->instr == src)
+				p->instr = dst;
 		}
-
-		note += noteSkipLen;
 	}
 }
 
@@ -1675,7 +1626,7 @@
 	pauseMusic();
 	remapInstrXY(editor.editPattern,
 	             cursor.ch, 0,
-	             cursor.ch, pattLens[editor.editPattern] - 1,
+	             cursor.ch, patternNumRows[editor.editPattern] - 1,
 	             editor.srcInstr, editor.curInstr);
 	resumeMusic();
 
@@ -1691,7 +1642,7 @@
 	pauseMusic();
 	remapInstrXY(editor.editPattern,
 	             0, 0,
-	             (uint16_t)(song.antChn - 1), pattLens[editor.editPattern] - 1,
+	             (uint16_t)(song.numChannels - 1), patternNumRows[editor.editPattern] - 1,
 	             editor.srcInstr, editor.curInstr);
 	resumeMusic();
 
@@ -1707,11 +1658,11 @@
 	pauseMusic();
 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
 	{
-		const uint8_t pattNr = (uint8_t)i;
+		const uint8_t pattNum = (uint8_t)i;
 
-		remapInstrXY(pattNr,
+		remapInstrXY(pattNum,
 		             0, 0,
-		             (uint16_t)(song.antChn - 1), pattLens[pattNr] - 1,
+		             (uint16_t)(song.numChannels - 1), patternNumRows[pattNum] - 1,
 		             editor.srcInstr, editor.curInstr);
 	}
 	resumeMusic();
@@ -1722,22 +1673,22 @@
 
 // "scale-fade volume" routines
 
-static int8_t getNoteVolume(tonTyp *note)
+static int8_t getNoteVolume(note_t *p)
 {
 	int8_t nv, vv, ev;
 
-	if (note->vol >= 0x10 && note->vol <= 0x50)
-		vv = note->vol - 0x10;
+	if (p->vol >= 0x10 && p->vol <= 0x50)
+		vv = p->vol - 0x10;
 	else
 		vv = -1;
 
-	if (note->effTyp == 0xC)
-		ev = MIN(note->eff, 64);
+	if (p->efx == 0xC)
+		ev = MIN(p->efxData, 64);
 	else
 		ev = -1;
 
-	if (note->instr != 0 && instr[note->instr] != NULL)
-		nv = (int8_t)instr[note->instr]->samp[0].vol;
+	if (p->instr != 0 && instr[p->instr] != NULL)
+		nv = (int8_t)instr[p->instr]->smp[0].volume;
 	else
 		nv = -1;
 
@@ -1749,38 +1700,38 @@
 	return finalv;
 }
 
-static void setNoteVolume(tonTyp *note, int8_t newVol)
+static void setNoteVolume(note_t *p, int8_t newVol)
 {
 	if (newVol < 0)
 		return;
 
-	const int8_t oldv = getNoteVolume(note);
-	if (note->vol == oldv)
+	const int8_t oldv = getNoteVolume(p);
+	if (p->vol == oldv)
 		return; // volume is the same
 
-	if (note->effTyp == 0x0C)
-		note->eff = newVol; // Cxx effect
+	if (p->efx == 0x0C)
+		p->efxData = newVol; // Cxx effect
 	else
-		note->vol = 0x10 + newVol; // volume column
+		p->vol = 0x10 + newVol; // volume column
 }
 
-static void scaleNote(uint16_t ptn, int8_t ch, int16_t row, double dScale)
+static void scaleNote(uint16_t pattNum, int8_t ch, int16_t row, double dScale)
 {
-	if (patt[ptn] == NULL)
+	if (pattern[pattNum] == NULL)
 		return;
 
-	const int16_t pattLen = pattLens[ptn];
-	if (row < 0 || row >= pattLen || ch < 0 || ch >= song.antChn)
+	const int16_t numRows = patternNumRows[pattNum];
+	if (row < 0 || row >= numRows || ch < 0 || ch >= song.numChannels)
 		return;
 
-	tonTyp *note = &patt[ptn][(row * MAX_VOICES) + ch];
+	note_t *p = &pattern[pattNum][(row * MAX_CHANNELS) + ch];
 
-	int32_t vol = getNoteVolume(note);
+	int32_t vol = getNoteVolume(p);
 	if (vol >= 0)
 	{
 		vol = (int32_t)((vol * dScale) + 0.5); // rounded
 		vol = MIN(MAX(0, vol), 64);
-		setNoteVolume(note, (int8_t)vol);
+		setNoteVolume(p, (int8_t)vol);
 	}
 }
 
@@ -1808,7 +1759,7 @@
 		return false;
 	}
 
-	dVolScaleFK1 = atof(val1);
+	dVolScaleFK1 = atof(val1+0);
 	dVolScaleFK2 = atof(val2+1);
 
 	return true;
@@ -1819,22 +1770,22 @@
 	if (!askForScaleFade("Volume scale-fade track (start-, end scale)"))
 		return;
 
-	if (patt[editor.editPattern] == NULL)
+	if (pattern[editor.editPattern] == NULL)
 		return;
 
-	const int32_t pattLen = pattLens[editor.editPattern];
+	const int32_t numRows = patternNumRows[editor.editPattern];
 
-	double dIPy = 0.0;
-	if (pattLen > 0)
-		dIPy = (dVolScaleFK2 - dVolScaleFK1) / pattLen;
+	double dVolDelta = 0.0;
+	if (numRows > 0)
+		dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows;
 
 	double dVol = dVolScaleFK1;
 
 	pauseMusic();
-	for (int16_t row = 0; row < pattLen; row++)
+	for (int16_t row = 0; row < numRows; row++)
 	{
 		scaleNote(editor.editPattern, cursor.ch, row, dVol);
-		dVol += dIPy;
+		dVol += dVolDelta;
 	}
 	resumeMusic();
 }
@@ -1844,24 +1795,24 @@
 	if (!askForScaleFade("Volume scale-fade pattern (start-, end scale)"))
 		return;
 
-	if (patt[editor.editPattern] == NULL)
+	if (pattern[editor.editPattern] == NULL)
 		return;
 
-	const int32_t pattLen = pattLens[editor.editPattern];
+	const int32_t numRows = patternNumRows[editor.editPattern];
 
-	double dIPy = 0.0;
-	if (pattLen > 0)
-		dIPy = (dVolScaleFK2 - dVolScaleFK1) / pattLen;
+	double dVolDelta = 0.0;
+	if (numRows > 0)
+		dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows;
 
 	double dVol = dVolScaleFK1;
 
 	pauseMusic();
-	for (int16_t row = 0; row < pattLen; row++)
+	for (int16_t row = 0; row < numRows; row++)
 	{
-		for (int8_t ch = 0; ch < song.antChn; ch++)
+		for (int8_t ch = 0; ch < song.numChannels; ch++)
 			scaleNote(editor.editPattern, ch, row, dVol);
 
-		dVol += dIPy;
+		dVol += dVolDelta;
 	}
 	resumeMusic();
 }
@@ -1871,14 +1822,14 @@
 	if (!askForScaleFade("Volume scale-fade block (start-, end scale)"))
 		return;
 
-	if (patt[editor.editPattern] == NULL || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
+	if (pattern[editor.editPattern] == NULL || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2)
 		return;
 
-	const int32_t dy = pattMark.markY2 - pattMark.markY1;
+	const int32_t numRows = pattMark.markY2 - pattMark.markY1;
 
-	double dIPy = 0.0;
-	if (dy > 0)
-		dIPy = (dVolScaleFK2 - dVolScaleFK1) / dy;
+	double dVolDelta = 0.0;
+	if (numRows > 0)
+		dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows;
 
 	double dVol = dVolScaleFK1;
 
@@ -1888,7 +1839,7 @@
 		for (int16_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++)
 			scaleNote(editor.editPattern, (uint8_t)ch, row, dVol);
 
-		dVol += dIPy;
+		dVol += dVolDelta;
 	}
 	resumeMusic();
 }
--- a/src/ft2_edit.h
+++ b/src/ft2_edit.h
@@ -4,7 +4,7 @@
 #include <SDL2/SDL.h>
 
 bool handleEditKeys(SDL_Keycode keycode, SDL_Scancode scancode);
-void recordNote(uint8_t note, int8_t vol);
+void recordNote(uint8_t noteNum, int8_t vol);
 void testNoteKeysRelease(SDL_Scancode scancode);
 void writeToMacroSlot(uint8_t slot);
 void writeFromMacroSlot(uint8_t slot);
--- a/src/ft2_events.c
+++ b/src/ft2_events.c
@@ -33,10 +33,10 @@
 #include "ft2_sample_ed_features.h"
 #include "ft2_structs.h"
 
-#define CRASH_TEXT "Oh no!\nThe Fasttracker II clone has crashed...\n\nA backup .xm was hopefully " \
+#define CRASH_TEXT "Oh no! The Fasttracker II clone has crashed...\nA backup .xm was hopefully " \
                    "saved to the current module directory.\n\nPlease report this bug if you can.\n" \
                    "Try to mention what you did before the crash happened.\n" \
-                   "My email can be found at the bottom of 16-bits.org."
+                   "My email can be found at the bottom of www.16-bits.org."
 
 static bool backupMadeAfterCrash;
 
@@ -100,7 +100,7 @@
 {
 	if (okBoxData.active)
 	{
-		okBoxData.returnData = okBox(okBoxData.typ, okBoxData.headline, okBoxData.text);
+		okBoxData.returnData = okBox(okBoxData.type, okBoxData.headline, okBoxData.text);
 		okBoxData.active = false;
 	}
 }
--- a/src/ft2_gui.c
+++ b/src/ft2_gui.c
@@ -12,7 +12,7 @@
 #include "ft2_nibbles.h"
 #include "ft2_gui.h"
 #include "ft2_pattern_ed.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_help.h"
 #include "ft2_sample_ed.h"
 #include "ft2_inst_ed.h"
@@ -203,7 +203,7 @@
 		s->thumbH = 0;
 	}
 
-	setPal16(palTable[config.cfg_StdPalNr], false);
+	setPal16(palTable[config.cfg_StdPalNum], false);
 	seedAboutScreenRandom((uint32_t)time(NULL));
 	setupInitialTextBoxPointers();
 	setInitialTrimFlags();
@@ -1141,7 +1141,7 @@
 		textOutShadow(4, 64, PAL_FORGRND, PAL_DSKTOP2, "Repstart");
 		drawPosEdNums(song.songPos);
 		drawSongLength();
-		drawSongRepS();
+		drawSongLoopStart();
 
 		// logo button
 		showPushButton(PB_LOGO);
@@ -1181,8 +1181,8 @@
 		textOutShadow(116, 64, PAL_FORGRND, PAL_DSKTOP2, "Add.");
 		textOutShadow(210, 36, PAL_FORGRND, PAL_DSKTOP2, "Ptn.");
 		textOutShadow(210, 50, PAL_FORGRND, PAL_DSKTOP2, "Ln.");
-		drawSongBPM(song.speed);
-		drawSongSpeed(song.tempo);
+		drawSongBPM(song.BPM);
+		drawSongSpeed(song.speed);
 		drawEditPattern(editor.editPattern);
 		drawPatternLength(editor.editPattern);
 		drawIDAdd();
@@ -1190,7 +1190,7 @@
 		// status bar
 		drawFramework(0, 77, 291, 15, FRAMEWORK_TYPE1);
 		textOutShadow(4, 80, PAL_FORGRND, PAL_DSKTOP2, "Global volume");
-		drawGlobalVol(song.globVol);
+		drawGlobalVol(song.globalVolume);
 
 		ui.updatePosSections = true;
 
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -8,25 +8,29 @@
 #define WIN32_MEAN_AND_LEAN
 #include <windows.h>
 #else
-#include <limits.h> // PATH_MAX
+#include <limits.h> // also has PATH_MAX
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.46"
+#define PROG_VER_STR "1.47"
 
 // do NOT change these! It will only mess things up...
 
+#define FT2_VBLANK_HZ 70
+#define SCREEN_W 632
+#define SCREEN_H 400
+
 /* "60Hz" ranges everywhere from 59..61Hz depending on the monitor, so with
 ** no vsync we will get stuttering because the rate is not perfect...
 */
 #define VBLANK_HZ 60
 
+// 70Hz (FT2 vblank) delta -> 60Hz vblank delta (rounded)
+#define SCALE_VBLANK_DELTA(x) (int32_t)(((x) * ((double)VBLANK_HZ / FT2_VBLANK_HZ)) + 0.5)
+
 // scopes must be clocked slightly higher than the nominal vblank rate
 #define SCOPE_HZ 64
 
-#define FT2_VBLANK_HZ 70
-#define SCREEN_W 632
-#define SCREEN_H 400
 
 /* Amount of extra bytes to allocate for every instrument sample,
 ** this is used for a hack for resampling interpolation to be
@@ -33,8 +37,8 @@
 ** branchless in the inner channel mixer loop.
 ** Warning: Do not change this!
 */
-#define LOOP_FIX_LEN 32
-#define SMP_DAT_OFFSET 8
+#define SMP_DAT_OFFSET 32
+#define SAMPLE_PAD_LENGTH (SMP_DAT_OFFSET+32)
 
 #ifndef _WIN32
 #define _stricmp strcasecmp
@@ -51,6 +55,14 @@
 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
 
+#define DROUND(x) \
+	if (x < 0.0) x -= 0.5; \
+	else if (x > 0.0) x += 0.5
+
+#define FROUND(x) \
+	if (x < 0.0f) x -= 0.5f; \
+	else if (x > 0.0f) x += 0.5f
+
 // fast 32-bit -> 8-bit clamp
 #define CLAMP8(i) if ((int8_t)(i) != i) i = 0x7F ^ (i >> 31)
 
@@ -60,16 +72,33 @@
 #define ALIGN_PTR(p, x) (((uintptr_t)(p) + ((x)-1)) & ~((x)-1))
 #define MALLOC_PAD(size, pad) (malloc((size) + (pad)))
 
-#define SWAP16(value) \
+#define SWAP16(x) \
 ( \
-	(((uint16_t)((value) & 0x00FF)) << 8) | \
-	(((uint16_t)((value) & 0xFF00)) >> 8)   \
+	(((uint16_t)((x) & 0x00FF)) << 8) | \
+	(((uint16_t)((x) & 0xFF00)) >> 8)   \
 )
 
-#define SWAP32(value) \
+#define SWAP32(x) \
 ( \
-	(((uint32_t)((value) & 0x000000FF)) << 24) | \
-	(((uint32_t)((value) & 0x0000FF00)) <<  8) | \
-	(((uint32_t)((value) & 0x00FF0000)) >>  8) | \
-	(((uint32_t)((value) & 0xFF000000)) >> 24)   \
+	(((uint32_t)((x) & 0x000000FF)) << 24) | \
+	(((uint32_t)((x) & 0x0000FF00)) <<  8) | \
+	(((uint32_t)((x) & 0x00FF0000)) >>  8) | \
+	(((uint32_t)((x) & 0xFF000000)) >> 24)   \
 )
+
+#define SWAP64(x) \
+( \
+	(((x) << 56) & 0xFF00000000000000ULL) | \
+	(((x) << 40) & 0x00FF000000000000ULL) | \
+	(((x) << 24) & 0x0000FF0000000000ULL) | \
+	(((x) <<  8) & 0x000000FF00000000ULL) | \
+	(((x) >>  8) & 0x00000000FF000000ULL) | \
+	(((x) >> 24) & 0x0000000000FF0000ULL) | \
+	(((x) >> 40) & 0x000000000000FF00ULL) | \
+	(((x) >> 56) & 0x00000000000000FFULL)  \
+)
+
+typedef struct smpPtr_t
+{
+	int8_t *origPtr, *ptr;
+} smpPtr_t;
--- a/src/ft2_help.c
+++ b/src/ft2_help.c
@@ -26,10 +26,10 @@
 #define MAX_HELP_LINES 768
 #define HELP_SIZE sizeof (helpRec)
 #define MAX_SUBJ 10
-#define HELP_KOL 135
-#define HELP_WIDTH (596 - HELP_KOL)
+#define HELP_COLUMN 135
+#define HELP_WIDTH (596 - HELP_COLUMN)
 
-static uint8_t fHlp_Nr;
+static uint8_t fHlp_Num;
 static int16_t textLine, fHlp_Line, subjLen[MAX_SUBJ];
 static int32_t helpBufferPos;
 static helpRec *subjPtrArr[MAX_SUBJ];
@@ -101,7 +101,7 @@
 	return s;
 }
 
-static void readHelp(void) // this is really messy, directly ported from Pascal code...
+static void readHelp(void) // this is a bit messy...
 {
 	char text[256], text2[256], *s, *sEnd, *s3;
 	int16_t a, b, i, k;
@@ -122,7 +122,7 @@
 	for (int16_t subj = 0; subj < MAX_SUBJ; subj++)
 	{
 		textLine = 0;
-		int16_t currKol = 0;
+		int16_t currColumn = 0;
 		uint8_t currColor = PAL_FORGRND;
 
 		getLine(text); s = text;
@@ -139,12 +139,12 @@
 
 			if (*(uint16_t *)s == 0x4C40) // @L - "big font"
 			{
-				addText(&tempText[textLine], currKol, currColor, s2);
+				addText(&tempText[textLine], currColumn, currColor, s2);
 				s += 2;
 
 				if (*(uint16_t *)s == 0x5840) // @X - "change X position"
 				{
-					currKol = controlCodeToNum(&s[2]);
+					currColumn = controlCodeToNum(&s[2]);
 					s += 5;
 				}
 
@@ -156,7 +156,7 @@
 				}
 
 				helpRec *t = &tempText[textLine];
-				t->xPos = currKol;
+				t->xPos = currColumn;
 				t->color = currColor;
 				t->bigFont = true;
 				t->noLine = false;
@@ -171,13 +171,13 @@
 			{
 				if (*s == '>')
 				{
-					addText(&tempText[textLine], currKol, currColor, s2);
+					addText(&tempText[textLine], currColumn, currColor, s2);
 					s++;
 				}
 
 				if (*(uint16_t *)s == 0x5840) // @X - "set X position (relative to help X start)"
 				{
-					currKol = controlCodeToNum(&s[2]);
+					currColumn = controlCodeToNum(&s[2]);
 					s += 5;
 				}
 
@@ -191,9 +191,9 @@
 				s = ltrim(rtrim(s));
 				if (*s == '\0')
 				{
-					addText(&tempText[textLine], currKol, currColor, s2);
+					addText(&tempText[textLine], currColumn, currColor, s2);
 					strcpy(s2, " ");
-					addText(&tempText[textLine], currKol, currColor, s2);
+					addText(&tempText[textLine], currColumn, currColor, s2);
 				}
 
 				int16_t sLen = (int16_t)strlen(s);
@@ -214,7 +214,7 @@
 						s += 5; sLen -= 5;
 
 						s3 = &s2[strlen(s2)];
-						while (textWidth(s2) + charWidth(' ') + 1 < k-currKol)
+						while (textWidth(s2) + charWidth(' ') + 1 < k-currColumn)
 						{
 							s3[0] = ' ';
 							s3[1] = '\0';
@@ -222,17 +222,17 @@
 						}
 
 						b = textWidth(s2) + 1;
-						if (b < (k - currKol))
+						if (b < k-currColumn)
 						{
 							s3 = &s2[strlen(s2)];
-							for (a = 0; a < k-b-currKol; a++)
+							for (a = 0; a < k-b-currColumn; a++)
 								s3[a] = 127; // one-pixel spacer glyph
 							s3[a] = '\0';
 						}
 					}
 
-					if (textWidth(s2)+textNWidth(s,i)+2 > HELP_WIDTH-currKol)
-						addText(&tempText[textLine], currKol, currColor, s2);
+					if (textWidth(s2)+textNWidth(s,i)+2 > HELP_WIDTH-currColumn)
+						addText(&tempText[textLine], currColumn, currColor, s2);
 
 					strncat(s2, s, i);
 
@@ -301,42 +301,42 @@
 
 static void writeHelp(void)
 {
-	helpRec *pek = subjPtrArr[fHlp_Nr];
-	if (pek == NULL)
+	helpRec *ptr = subjPtrArr[fHlp_Num];
+	if (ptr == NULL)
 		return;
 
 	for (int16_t i = 0; i < HELP_LINES; i++)
 	{
 		const int16_t k = i + fHlp_Line;
-		if (k >= subjLen[fHlp_Nr])
+		if (k >= subjLen[fHlp_Num])
 			break;
 
-		clearRect(HELP_KOL, 5 + (i * 11), HELP_WIDTH, 11);
+		clearRect(HELP_COLUMN, 5 + (i * 11), HELP_WIDTH, 11);
 
-		if (pek[k].noLine)
+		if (ptr[k].noLine)
 		{
 			if (i == 0)
-				bigTextOutHalf(HELP_KOL + pek[k-1].xPos, 5 + (i * 11), PAL_FORGRND, false, pek[k-1].text);
+				bigTextOutHalf(HELP_COLUMN + ptr[k-1].xPos, 5 + (i * 11), PAL_FORGRND, false, ptr[k-1].text);
 		}
 		else
 		{
-			if (pek[k].bigFont)
+			if (ptr[k].bigFont)
 			{
-				if (i == (HELP_LINES - 1))
+				if (i == HELP_LINES-1)
 				{
-					bigTextOutHalf(HELP_KOL + pek[k].xPos, 5 + (i * 11), PAL_FORGRND, true, pek[k].text);
+					bigTextOutHalf(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), PAL_FORGRND, true, ptr[k].text);
 					return;
 				}
 				else
 				{
-					clearRect(HELP_KOL, 5 + ((i + 1) * 11), HELP_WIDTH, 11);
-					bigTextOut(HELP_KOL + pek[k].xPos, 5 + (i * 11), PAL_FORGRND, pek[k].text);
+					clearRect(HELP_COLUMN, 5 + ((i + 1) * 11), HELP_WIDTH, 11);
+					bigTextOut(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), PAL_FORGRND, ptr[k].text);
 					i++;
 				}
 			}
 			else
 			{
-				textOut(HELP_KOL + pek[k].xPos, 5 + (i * 11), pek[k].color, pek[k].text);
+				textOut(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), ptr[k].color, ptr[k].text);
 			}
 		}
 	}
@@ -353,7 +353,7 @@
 
 void helpScrollDown(void)
 {
-	if (fHlp_Line < subjLen[fHlp_Nr]-1)
+	if (fHlp_Line < subjLen[fHlp_Num]-1)
 	{
 		scrollBarScrollDown(SB_HELP_SCROLL, 1);
 		writeHelp();
@@ -388,12 +388,12 @@
 	showPushButton(PB_HELP_SCROLL_DOWN);
 
 	uncheckRadioButtonGroup(RB_GROUP_HELP);
-	switch (fHlp_Nr)
+	switch (fHlp_Num)
 	{
 		default:
 		case 0: tmpID = RB_HELP_FEATURES;       break;
 		case 1: tmpID = RB_HELP_EFFECTS;        break;
-		case 2: tmpID = RB_HELP_KEYBOARD;       break;
+		case 2: tmpID = RB_HELP_KEYBINDINGS;       break;
 		case 3: tmpID = RB_HELP_HOW_TO_USE_FT2; break;
 		case 4: tmpID = RB_HELP_FAQ;            break;
 		case 5: tmpID = RB_HELP_KNOWN_BUGS;     break;
@@ -407,7 +407,7 @@
 	textOutShadow(4,   4, PAL_FORGRND, PAL_DSKTOP2, "Subjects:");
 	textOutShadow(21, 19, PAL_FORGRND, PAL_DSKTOP2, "Features");
 	textOutShadow(21, 35, PAL_FORGRND, PAL_DSKTOP2, "Effects");
-	textOutShadow(21, 51, PAL_FORGRND, PAL_DSKTOP2, "Keyboard");
+	textOutShadow(21, 51, PAL_FORGRND, PAL_DSKTOP2, "Keybindings");
 	textOutShadow(21, 67, PAL_FORGRND, PAL_DSKTOP2, "How to use FT2");
 	textOutShadow(21, 83, PAL_FORGRND, PAL_DSKTOP2, "Problems/FAQ");
 	textOutShadow(21, 99, PAL_FORGRND, PAL_DSKTOP2, "Known bugs");
@@ -435,10 +435,10 @@
 
 static void setHelpSubject(uint8_t Nr)
 {
-	fHlp_Nr = Nr;
+	fHlp_Num = Nr;
 	fHlp_Line = 0;
 
-	setScrollBarEnd(SB_HELP_SCROLL, subjLen[fHlp_Nr]);
+	setScrollBarEnd(SB_HELP_SCROLL, subjLen[fHlp_Num]);
 	setScrollBarPos(SB_HELP_SCROLL, 0, false);
 }
 
@@ -456,9 +456,9 @@
 	writeHelp();
 }
 
-void rbHelpKeyboard(void)
+void rbHelpKeybindings(void)
 {
-	checkRadioButton(RB_HELP_KEYBOARD);
+	checkRadioButton(RB_HELP_KEYBINDINGS);
 	setHelpSubject(2);
 	writeHelp();
 }
--- a/src/ft2_help.h
+++ b/src/ft2_help.h
@@ -16,7 +16,7 @@
 void windUpFTHelp(void);
 void rbHelpFeatures(void);
 void rbHelpEffects(void);
-void rbHelpKeyboard(void);
+void rbHelpKeybindings(void);
 void rbHelpHowToUseFT2(void);
 void rbHelpFAQ(void);
 void rbHelpKnownBugs(void);
--- a/src/ft2_inst_ed.c
+++ b/src/ft2_inst_ed.c
@@ -12,7 +12,7 @@
 #include "ft2_audio.h"
 #include "ft2_pattern_ed.h"
 #include "ft2_gui.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_sample_ed.h"
 #include "ft2_mouse.h"
 #include "ft2_video.h"
@@ -27,66 +27,66 @@
 #pragma pack(push)
 #pragma pack(1)
 #endif
-typedef struct instrPATHeaderTyp_t
+typedef struct patHdr_t
 {
-	char id[22], copyright[60];
-	uint8_t antInstr, activeVoices, antChannels;
-	int16_t waveForms, masterVol;
+	char ID[22], junk1[60];
+	uint8_t numInstrs, junk2, numChannels;
+	int16_t waveforms, masterVol;
 	int32_t dataSize;
-	char reserved1[36];
-	int16_t instrNr;
+	char junk4[36];
+	int16_t junk5;
 	char instrName[16];
 	int32_t instrSize;
 	uint8_t layers;
-	char reserved2[40];
-	uint8_t layerDuplicate, layerByte;
-	int32_t layerSize;
-	uint8_t antSamp;
-	char reserved3[40];
+	char junk6[40];
+	uint8_t junk7, junk8;
+	int32_t junk9;
+	uint8_t numSamples;
+	char junk10[40];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-instrPATHeaderTyp;
+patHdr_t;
 
-typedef struct instrPATWaveHeaderTyp_t
+typedef struct patWaveHdr_t
 {
 	char name[7];
 	uint8_t fractions;
-	int32_t waveSize, repS, repE;
+	int32_t sampleLength, loopStart, loopEnd;
 	uint16_t sampleRate;
 	int32_t lowFrq, highFreq, rootFrq;
-	int16_t fineTune;
-	uint8_t pan, envRate[6], envOfs[6], tremSweep, tremRate;
-	uint8_t tremDepth, vibSweep, vibRate, vibDepth, mode;
-	int16_t scaleFrq;
-	uint16_t scaleFactor;
-	char reserved[36];
+	int16_t finetune;
+	uint8_t panning, envRate[6], envOfs[6], tremSweep, tremRate;
+	uint8_t tremDepth, vibSweep, vibRate, vibDepth, flags;
+	int16_t junk1;
+	uint16_t junk2;
+	char junk3[36];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-instrPATWaveHeaderTyp;
+patWaveHdr_t;
 
-typedef struct instrXIHeaderTyp_t
+typedef struct xiHdr_t
 {
-	char sig[21], name[23], progName[20];
-	uint16_t ver;
-	uint8_t ta[96];
-	int16_t envVP[12][2], envPP[12][2];
-	uint8_t envVPAnt, envPPAnt, envVSust, envVRepS, envVRepE, envPSust, envPRepS;
-	uint8_t envPRepE, envVTyp, envPTyp, vibTyp, vibSweep, vibDepth, vibRate;
-	uint16_t fadeOut;
+	char ID[21], name[23], progName[20];
+	uint16_t version;
+	uint8_t note2SampleLUT[96];
+	int16_t volEnvPoints[12][2], panEnvPoints[12][2];
+	uint8_t volEnvLength, panEnvLength, volEnvSustain, volEnvLoopStart, volEnvLoopEnd, panEnvSustain, panEnvLoopStart;
+	uint8_t panEnvLoopEnd, volEnvFlags, panEnvFlags, vibType, vibSweep, vibDepth, vibRate;
+	uint16_t fadeout;
 	uint8_t midiOn, midiChannel;
 	int16_t midiProgram, midiBend;
 	uint8_t mute, reserved[15];
-	int16_t antSamp;
-	sampleHeaderTyp samp[16];
+	int16_t numSamples;
+	xmSmpHdr_t smp[16];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-instrXIHeaderTyp;
+xiHdr_t;
 
 #define PIANOKEY_WHITE_W 10
 #define PIANOKEY_WHITE_H 46
@@ -114,7 +114,7 @@
 };
 
 // thread data
-static uint16_t saveInstrNr;
+static uint16_t saveInstrNum;
 static SDL_Thread *thread;
 
 extern const uint16_t *note2Period; // ft2_replayer.c
@@ -122,8 +122,45 @@
 void updateInstEditor(void);
 void updateNewInstrument(void);
 
-static instrTyp *getCurDispInstr(void)
+void sanitizeInstrument(instr_t *ins)
 {
+	if (ins == NULL)
+		return;
+
+	ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
+	ins->midiBend = CLAMP(ins->midiBend, 0, 36);
+
+	if (ins->midiChannel > 15) ins->midiChannel = 15;
+	if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
+	if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
+	if (ins->vibType > 3) ins->vibType = 0;
+
+	for (int32_t i = 0; i < 96; i++)
+	{
+		if (ins->note2SampleLUT[i] >= MAX_SMP_PER_INST)
+			ins->note2SampleLUT[i] = MAX_SMP_PER_INST-1;
+	}
+
+	if (ins->volEnvLength > 12) ins->volEnvLength = 12;
+	if (ins->volEnvLoopStart > 11) ins->volEnvLoopStart = 11;
+	if (ins->volEnvLoopEnd > 11) ins->volEnvLoopEnd = 11;
+	if (ins->volEnvSustain > 11) ins->volEnvSustain = 11;
+	if (ins->panEnvLength > 12) ins->panEnvLength = 12;
+	if (ins->panEnvLoopStart > 11) ins->panEnvLoopStart = 11;
+	if (ins->panEnvLoopEnd > 11) ins->panEnvLoopEnd = 11;
+	if (ins->panEnvSustain > 11) ins->panEnvSustain = 11;
+
+	for (int32_t i = 0; i < 12; i++)
+	{
+		if ((uint16_t)ins->volEnvPoints[i][0] > 32767) ins->volEnvPoints[i][0] = 32767;
+		if ((uint16_t)ins->panEnvPoints[i][0] > 32767) ins->panEnvPoints[i][0] = 32767;
+		if ((uint16_t)ins->volEnvPoints[i][1] > 64) ins->volEnvPoints[i][1] = 64;
+		if ((uint16_t)ins->panEnvPoints[i][1] > 63) ins->panEnvPoints[i][1] = 63;
+	}
+}
+
+static instr_t *getCurDispInstr(void)
+{
 	if (instr[editor.curInstr] == NULL)
 		return instr[131];
 
@@ -132,44 +169,28 @@
 
 static int32_t SDLCALL copyInstrThread(void *ptr)
 {
-	bool error = false;
+	const int16_t dstIns = editor.curInstr;
+	const int16_t srcIns = editor.srcInstr;
 
-	const int16_t destIns = editor.curInstr;
-	const int16_t sourceIns = editor.srcInstr;
-
 	pauseAudio();
-	
-	freeInstr(destIns);
+	freeInstr(dstIns);
 
-	if (instr[sourceIns] != NULL)
+	bool error = true;
+	if (instr[srcIns] != NULL)
 	{
-		if (allocateInstr(destIns))
+		if (allocateInstr(dstIns))
 		{
-			memcpy(instr[destIns], instr[sourceIns], sizeof (instrTyp));
+			memcpy(instr[dstIns], instr[srcIns], sizeof (instr_t));
 
-			sampleTyp *src = instr[sourceIns]->samp;
-			sampleTyp *dst = instr[destIns]->samp;
+			sample_t *srcSmp = instr[srcIns]->smp;
+			sample_t *dstSmp = instr[dstIns]->smp;
 
-			for (int16_t i = 0; i < MAX_SMP_PER_INST; i++, src++, dst++)
+			for (int16_t i = 0; i < MAX_SMP_PER_INST; i++, srcSmp++, dstSmp++)
 			{
-				dst->origPek = NULL;
-				dst->pek = NULL;
-
-				if (src->origPek != NULL)
-				{
-					int8_t *p = (int8_t *)malloc(src->len + LOOP_FIX_LEN);
-					if (p != NULL)
-					{
-						dst->origPek = p;
-						dst->pek = dst->origPek + SMP_DAT_OFFSET;
-
-						memcpy(dst->origPek, src->origPek, src->len + LOOP_FIX_LEN);
-					}
-					else error = true;
-				}
+				if (!cloneSample(srcSmp, dstSmp))
+					error = false;
 			}
 		}
-		else error = true;
 	}
 
 	resumeAudio();
@@ -179,12 +200,15 @@
 
 	// do not change instrument names!
 
-	editor.updateCurInstr = true;
-	setSongModifiedFlag();
+	if (!error)
+	{
+		editor.updateCurInstr = true;
+		setSongModifiedFlag();
+	}
+
 	setMouseBusy(false);
 
 	return false;
-
 	(void)ptr;
 }
 
@@ -211,11 +235,11 @@
 
 	lockMixerCallback();
 
-	instrTyp *src = instr[editor.srcInstr];
-	instrTyp *dst = instr[editor.curInstr];
+	instr_t *src = instr[editor.srcInstr];
+	instr_t *dst = instr[editor.curInstr];
 
 	// swap instruments
-	instrTyp dstTmp = *dst;
+	instr_t dstTmp = *dst;
 	*dst = *src;
 	*src = dstTmp;
 
@@ -229,7 +253,7 @@
 
 static void drawMIDICh(void)
 {
-	instrTyp *ins = getCurDispInstr();
+	instr_t *ins = getCurDispInstr();
 	assert(ins->midiChannel <= 15);
 	const uint8_t val = ins->midiChannel + 1;
 	textOutFixed(156, 132, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
@@ -237,7 +261,7 @@
 
 static void drawMIDIPrg(void)
 {
-	instrTyp *ins = getCurDispInstr();
+	instr_t *ins = getCurDispInstr();
 	assert(ins->midiProgram <= 127);
 	textOutFixed(149, 146, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[ins->midiProgram]);
 }
@@ -244,7 +268,7 @@
 
 static void drawMIDIBend(void)
 {
-	instrTyp *ins = getCurDispInstr();
+	instr_t *ins = getCurDispInstr();
 	assert(ins->midiBend <= 36);
 	textOutFixed(156, 160, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->midiBend]);
 }
@@ -287,7 +311,7 @@
 
 void sbMidiChPos(uint32_t pos)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 	{
 		setScrollBarPos(SB_INST_EXT_MIDI_CH, 0, false);
@@ -304,7 +328,7 @@
 
 void sbMidiPrgPos(uint32_t pos)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 	{
 		setScrollBarPos(SB_INST_EXT_MIDI_PRG, 0, false);
@@ -321,7 +345,7 @@
 
 void sbMidiBendPos(uint32_t pos)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 	{
 		setScrollBarPos(SB_INST_EXT_MIDI_BEND, 0, false);
@@ -374,66 +398,66 @@
 
 static void drawVolEnvSus(void)
 {
-	instrTyp *ins = getCurDispInstr();
-	assert(ins->envVSust < 100);
-	textOutFixed(382, 206, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envVSust]);
+	instr_t *ins = getCurDispInstr();
+	assert(ins->volEnvSustain < 100);
+	textOutFixed(382, 206, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvSustain]);
 }
 
 static void drawVolEnvRepS(void)
 {
-	instrTyp *ins = getCurDispInstr();
-	assert(ins->envVRepS < 100);
-	textOutFixed(382, 233, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envVRepS]);
+	instr_t *ins = getCurDispInstr();
+	assert(ins->volEnvLoopStart < 100);
+	textOutFixed(382, 233, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvLoopStart]);
 }
 
 static void drawVolEnvRepE(void)
 {
-	instrTyp *ins = getCurDispInstr();
-	assert(ins->envVRepE < 100);
-	textOutFixed(382, 247, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envVRepE]);
+	instr_t *ins = getCurDispInstr();
+	assert(ins->volEnvLoopEnd < 100);
+	textOutFixed(382, 247, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvLoopEnd]);
 }
 
 static void drawPanEnvSus(void)
 {
-	instrTyp *ins = getCurDispInstr();
-	assert(ins->envPSust < 100);
-	textOutFixed(382, 293, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envPSust]);
+	instr_t *ins = getCurDispInstr();
+	assert(ins->panEnvSustain < 100);
+	textOutFixed(382, 293, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvSustain]);
 }
 
 static void drawPanEnvRepS(void)
 {
-	instrTyp *ins = getCurDispInstr();
-	assert(ins->envPRepS < 100);
-	textOutFixed(382, 320, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envPRepS]);
+	instr_t *ins = getCurDispInstr();
+	assert(ins->panEnvLoopStart < 100);
+	textOutFixed(382, 320, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvLoopStart]);
 }
 
 static void drawPanEnvRepE(void)
 {
-	instrTyp *ins = getCurDispInstr();
-	assert(ins->envPRepE < 100);
-	textOutFixed(382, 334, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envPRepE]);
+	instr_t *ins = getCurDispInstr();
+	assert(ins->panEnvLoopEnd < 100);
+	textOutFixed(382, 334, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvLoopEnd]);
 }
 
 static void drawVolume(void)
 {
-	sampleTyp *s;
+	sample_t *s;
 	if (instr[editor.curInstr] == NULL)
-		s = &instr[0]->samp[0];
+		s = &instr[0]->smp[0];
 	else
-		s = &instr[editor.curInstr]->samp[editor.curSmp];
+		s = &instr[editor.curInstr]->smp[editor.curSmp];
 
-	hexOutBg(505, 177, PAL_FORGRND, PAL_DESKTOP, s->vol, 2);
+	hexOutBg(505, 177, PAL_FORGRND, PAL_DESKTOP, s->volume, 2);
 }
 
 static void drawPanning(void)
 {
-	sampleTyp *s;
+	sample_t *s;
 	if (instr[editor.curInstr] == NULL)
-		s = &instr[0]->samp[0];
+		s = &instr[0]->smp[0];
 	else
-		s = &instr[editor.curInstr]->samp[editor.curSmp];
+		s = &instr[editor.curInstr]->smp[editor.curSmp];
 
-	hexOutBg(505, 191, PAL_FORGRND, PAL_DESKTOP, s->pan, 2);
+	hexOutBg(505, 191, PAL_FORGRND, PAL_DESKTOP, s->panning, 2);
 }
 
 void drawC4Rate(void)
@@ -443,9 +467,9 @@
 	int32_t C4Hz = 0;
 	if (editor.curInstr != 0)
 	{
-		instrTyp *ins = instr[editor.curInstr];
+		instr_t *ins = instr[editor.curInstr];
 		if (ins != NULL)
-			C4Hz = (int32_t)(getSampleC4Rate(&ins->samp[editor.curSmp]) + 0.5); // rounded
+			C4Hz = (int32_t)(getSampleC4Rate(&ins->smp[editor.curSmp]) + 0.5); // rounded
 	}
 
 	char str[64];
@@ -455,15 +479,15 @@
 
 static void drawFineTune(void)
 {
-	sampleTyp *s;
+	sample_t *s;
 	if (instr[editor.curInstr] == NULL)
-		s = &instr[0]->samp[0];
+		s = &instr[0]->smp[0];
 	else
-		s = &instr[editor.curInstr]->samp[editor.curSmp];
+		s = &instr[editor.curInstr]->smp[editor.curSmp];
 
 	fillRect(491, 205, 27, 8, PAL_DESKTOP);
 
-	int16_t  ftune = s->fine;
+	int16_t  ftune = s->finetune;
 	if (ftune == 0)
 	{
 		charOut(512, 205, PAL_FORGRND, '0');
@@ -495,7 +519,7 @@
 
 static void drawFadeout(void)
 {
-	hexOutBg(498, 222, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->fadeOut, 3);
+	hexOutBg(498, 222, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->fadeout, 3);
 }
 
 static void drawVibSpeed(void)
@@ -513,7 +537,7 @@
 	hexOutBg(505, 264, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibSweep, 2);
 }
 
-static void drawRelTone(void)
+static void drawRelativeNote(void)
 {
 	char noteChar1, noteChar2;
 	int8_t note2;
@@ -527,7 +551,7 @@
 	if (editor.curInstr == 0)
 		note2 = 48;
 	else
-		note2 = 48 + instr[editor.curInstr]->samp[editor.curSmp].relTon;
+		note2 = 48 + instr[editor.curInstr]->smp[editor.curSmp].relativeNote;
 
 	const int8_t note = note2 % 12;
 	if (config.ptnAcc == 0)
@@ -548,7 +572,7 @@
 	charOutBg(616, 299, PAL_FORGRND, PAL_BCKGRND, octaChar);
 }
 
-static void setStdVolEnvelope(instrTyp *ins, uint8_t num)
+static void setStdVolEnvelope(instr_t *ins, uint8_t num)
 {
 	if (editor.curInstr == 0 || ins == NULL)
 		return;
@@ -555,23 +579,23 @@
 
 	pauseMusic();
 
-	ins->fadeOut = config.stdFadeOut[num];
-	ins->envVSust = (uint8_t)config.stdVolEnvSust[num];
-	ins->envVRepS = (uint8_t)config.stdVolEnvRepS[num];
-	ins->envVRepE = (uint8_t)config.stdVolEnvRepE[num];
-	ins->envVPAnt = (uint8_t)config.stdVolEnvAnt[num];
-	ins->envVTyp = (uint8_t)config.stdVolEnvTyp[num];
+	ins->fadeout = config.stdFadeout[num];
+	ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[num];
+	ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[num];
+	ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[num];
+	ins->volEnvLength = (uint8_t)config.stdVolEnvLength[num];
+	ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[num];
 	ins->vibRate = (uint8_t)config.stdVibRate[num];
 	ins->vibDepth = (uint8_t)config.stdVibDepth[num];
 	ins->vibSweep = (uint8_t)config.stdVibSweep[num];
-	ins->vibTyp = (uint8_t)config.stdVibTyp[num];
+	ins->vibType = (uint8_t)config.stdVibType[num];
 
-	memcpy(ins->envVP, config.stdEnvP[num][0], sizeof (int16_t) * 12 * 2);
+	memcpy(ins->volEnvPoints, config.stdEnvPoints[num][0], sizeof (int16_t) * 12 * 2);
 
 	resumeMusic();
 }
 
-static void setStdPanEnvelope(instrTyp *ins, uint8_t num)
+static void setStdPanEnvelope(instr_t *ins, uint8_t num)
 {
 	if (editor.curInstr == 0 || ins == NULL)
 		return;
@@ -578,13 +602,13 @@
 
 	pauseMusic();
 
-	ins->envPPAnt = (uint8_t)config.stdPanEnvAnt[num];
-	ins->envPSust = (uint8_t)config.stdPanEnvSust[num];
-	ins->envPRepS = (uint8_t)config.stdPanEnvRepS[num];
-	ins->envPRepE = (uint8_t)config.stdPanEnvRepE[num];
-	ins->envPTyp = (uint8_t)config.stdPanEnvTyp[num];
+	ins->panEnvLength = (uint8_t)config.stdPanEnvLength[num];
+	ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[num];
+	ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[num];
+	ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[num];
+	ins->panEnvFlags = (uint8_t)config.stdPanEnvFlags[num];
 
-	memcpy(ins->envPP, config.stdEnvP[num][1], sizeof (int16_t) * 12 * 2);
+	memcpy(ins->panEnvPoints, config.stdEnvPoints[num][1], sizeof (int16_t) * 12 * 2);
 
 	resumeMusic();
 }
@@ -591,7 +615,7 @@
 
 static void setOrStoreVolEnvPreset(uint8_t num)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
@@ -598,18 +622,18 @@
 	if (mouse.rightButtonReleased)
 	{
 		// store preset
-		config.stdFadeOut[num] = ins->fadeOut;
-		config.stdVolEnvSust[num] = ins->envVSust;
-		config.stdVolEnvRepS[num] = ins->envVRepS;
-		config.stdVolEnvRepE[num] = ins->envVRepE;
-		config.stdVolEnvAnt[num] = ins->envVPAnt;
-		config.stdVolEnvTyp[num] = ins->envVTyp;
+		config.stdFadeout[num] = ins->fadeout;
+		config.stdVolEnvSustain[num] = ins->volEnvSustain;
+		config.stdVolEnvLoopStart[num] = ins->volEnvLoopStart;
+		config.stdVolEnvLoopEnd[num] = ins->volEnvLoopEnd;
+		config.stdVolEnvLength[num] = ins->volEnvLength;
+		config.stdVolEnvFlags[num] = ins->volEnvFlags;
 		config.stdVibRate[num] = ins->vibRate;
 		config.stdVibDepth[num] = ins->vibDepth;
 		config.stdVibSweep[num] = ins->vibSweep;
-		config.stdVibTyp[num] = ins->vibTyp;
+		config.stdVibType[num] = ins->vibType;
 
-		memcpy(config.stdEnvP[num][0], ins->envVP, sizeof (int16_t) * 12 * 2);
+		memcpy(config.stdEnvPoints[num][0], ins->volEnvPoints, sizeof (int16_t) * 12 * 2);
 	}
 	else if (mouse.leftButtonReleased)
 	{
@@ -623,7 +647,7 @@
 
 static void setOrStorePanEnvPreset(uint8_t num)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
@@ -630,18 +654,18 @@
 	if (mouse.rightButtonReleased)
 	{
 		// store preset
-		config.stdFadeOut[num] = ins->fadeOut;
-		config.stdPanEnvSust[num] = ins->envPSust;
-		config.stdPanEnvRepS[num] = ins->envPRepS;
-		config.stdPanEnvRepE[num] = ins->envPRepE;
-		config.stdPanEnvAnt[num] = ins->envPPAnt;
-		config.stdPanEnvTyp[num] = ins->envPTyp;
+		config.stdFadeout[num] = ins->fadeout;
+		config.stdPanEnvSustain[num] = ins->panEnvSustain;
+		config.stdPanEnvLoopStart[num] = ins->panEnvLoopStart;
+		config.stdPanEnvLoopEnd[num] = ins->panEnvLoopEnd;
+		config.stdPanEnvLength[num] = ins->panEnvLength;
+		config.stdPanEnvFlags[num] = ins->panEnvFlags;
 		config.stdVibRate[num] = ins->vibRate;
 		config.stdVibDepth[num] = ins->vibDepth;
 		config.stdVibSweep[num] = ins->vibSweep;
-		config.stdVibTyp[num] = ins->vibTyp;
+		config.stdVibType[num] = ins->vibType;
 
-		memcpy(config.stdEnvP[num][1], ins->envPP, sizeof (int16_t) * 12 * 2);
+		memcpy(config.stdEnvPoints[num][1], ins->panEnvPoints, sizeof (int16_t) * 12 * 2);
 	}
 	else if (mouse.leftButtonReleased)
 	{
@@ -725,67 +749,67 @@
 		setOrStorePanEnvPreset(6 - 1);
 }
 
-void relToneOctUp(void)
+void relativeNoteOctUp(void)
 {
-	sampleTyp *s;
+	sample_t *s;
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->relTon <= 71-12)
-		s->relTon += 12;
+	s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->relativeNote <= 71-12)
+		s->relativeNote += 12;
 	else
-		s->relTon = 71;
+		s->relativeNote = 71;
 
-	drawRelTone();
+	drawRelativeNote();
 	drawC4Rate();
 	setSongModifiedFlag();
 }
 
-void relToneOctDown(void)
+void relativeNoteOctDown(void)
 {
-	sampleTyp *s;
+	sample_t *s;
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->relTon >= -48+12)
-		s->relTon -= 12;
+	s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->relativeNote >= -48+12)
+		s->relativeNote -= 12;
 	else
-		s->relTon = -48;
+		s->relativeNote = -48;
 
-	drawRelTone();
+	drawRelativeNote();
 	drawC4Rate();
 	setSongModifiedFlag();
 }
 
-void relToneUp(void)
+void relativeNoteUp(void)
 {
-	sampleTyp *s;
+	sample_t *s;
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->relTon < 71)
+	s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->relativeNote < 71)
 	{
-		s->relTon++;
-		drawRelTone();
+		s->relativeNote++;
+		drawRelativeNote();
 		drawC4Rate();
 		setSongModifiedFlag();
 	}
 }
 
-void relToneDown(void)
+void relativeNoteDown(void)
 {
-	sampleTyp *s;
+	sample_t *s;
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->relTon > -48)
+	s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->relativeNote > -48)
 	{
-		s->relTon--;
-		drawRelTone();
+		s->relativeNote--;
+		drawRelativeNote();
 		drawC4Rate();
 		setSongModifiedFlag();
 	}
@@ -793,11 +817,11 @@
 
 void volEnvAdd(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (editor.curInstr == 0 || ins == NULL)
 		return;
 
-	const int16_t ant = ins->envVPAnt;
+	const int16_t ant = ins->volEnvLength;
 	if (ant >= 12)
 		return;
 
@@ -809,37 +833,37 @@
 			i = 0;
 	}
 
-	if (i < ant-1 && ins->envVP[i+1][0]-ins->envVP[i][0] < 2)
+	if (i < ant-1 && ins->volEnvPoints[i+1][0]-ins->volEnvPoints[i][0] < 2)
 		return;
 
-	if (ins->envVP[i][0] >= 323)
+	if (ins->volEnvPoints[i][0] >= 323)
 		return;
 
 	for (int16_t j = ant; j > i; j--)
 	{
-		ins->envVP[j][0] = ins->envVP[j-1][0];
-		ins->envVP[j][1] = ins->envVP[j-1][1];
+		ins->volEnvPoints[j][0] = ins->volEnvPoints[j-1][0];
+		ins->volEnvPoints[j][1] = ins->volEnvPoints[j-1][1];
 	}
 
-	if (ins->envVSust > i) { ins->envVSust++; drawVolEnvSus();  }
-	if (ins->envVRepS > i) { ins->envVRepS++; drawVolEnvRepS(); }
-	if (ins->envVRepE > i) { ins->envVRepE++; drawVolEnvRepE(); }
+	if (ins->volEnvSustain > i) { ins->volEnvSustain++; drawVolEnvSus();  }
+	if (ins->volEnvLoopStart > i) { ins->volEnvLoopStart++; drawVolEnvRepS(); }
+	if (ins->volEnvLoopEnd > i) { ins->volEnvLoopEnd++; drawVolEnvRepE(); }
 
 	if (i < ant-1)
 	{
-		ins->envVP[i+1][0] = (ins->envVP[i][0] + ins->envVP[i+2][0]) / 2;
-		ins->envVP[i+1][1] = (ins->envVP[i][1] + ins->envVP[i+2][1]) / 2;
+		ins->volEnvPoints[i+1][0] = (ins->volEnvPoints[i][0] + ins->volEnvPoints[i+2][0]) / 2;
+		ins->volEnvPoints[i+1][1] = (ins->volEnvPoints[i][1] + ins->volEnvPoints[i+2][1]) / 2;
 	}
 	else
 	{
-		ins->envVP[i+1][0] = ins->envVP[i][0] + 10;
-		ins->envVP[i+1][1] = ins->envVP[i][1];
+		ins->volEnvPoints[i+1][0] = ins->volEnvPoints[i][0] + 10;
+		ins->volEnvPoints[i+1][1] = ins->volEnvPoints[i][1];
 	}
 
-	if (ins->envVP[i+1][0] > 324)
-		ins->envVP[i+1][0] = 324;
+	if (ins->volEnvPoints[i+1][0] > 324)
+		ins->volEnvPoints[i+1][0] = 324;
 
-	ins->envVPAnt++;
+	ins->volEnvLength++;
 
 	updateVolEnv = true;
 	setSongModifiedFlag();
@@ -847,18 +871,18 @@
 
 void volEnvDel(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
-	if (ins == NULL || editor.curInstr == 0 || ins->envVPAnt <= 2)
+	instr_t *ins = instr[editor.curInstr];
+	if (ins == NULL || editor.curInstr == 0 || ins->volEnvLength <= 2)
 		return;
 
 	int16_t i = (int16_t)editor.currVolEnvPoint;
-	if (i < 0 || i >= ins->envVPAnt)
+	if (i < 0 || i >= ins->volEnvLength)
 		return;
 
-	for (int16_t j = i; j < ins->envVPAnt; j++)
+	for (int16_t j = i; j < ins->volEnvLength; j++)
 	{
-		ins->envVP[j][0] = ins->envVP[j+1][0];
-		ins->envVP[j][1] = ins->envVP[j+1][1];
+		ins->volEnvPoints[j][0] = ins->volEnvPoints[j+1][0];
+		ins->volEnvPoints[j][1] = ins->volEnvPoints[j+1][1];
 	}
 
 	bool drawSust = false;
@@ -865,25 +889,25 @@
 	bool drawRepS = false;
 	bool drawRepE = false;
 
-	if (ins->envVSust > i) { ins->envVSust--; drawSust = true; }
-	if (ins->envVRepS > i) { ins->envVRepS--; drawRepS = true; }
-	if (ins->envVRepE > i) { ins->envVRepE--; drawRepE = true; }
+	if (ins->volEnvSustain > i) { ins->volEnvSustain--; drawSust = true; }
+	if (ins->volEnvLoopStart > i) { ins->volEnvLoopStart--; drawRepS = true; }
+	if (ins->volEnvLoopEnd > i) { ins->volEnvLoopEnd--; drawRepE = true; }
 
-	ins->envVP[0][0] = 0;
-	ins->envVPAnt--;
+	ins->volEnvPoints[0][0] = 0;
+	ins->volEnvLength--;
 
-	if (ins->envVSust >= ins->envVPAnt) { ins->envVSust = ins->envVPAnt - 1; drawSust = true; }
-	if (ins->envVRepS >= ins->envVPAnt) { ins->envVRepS = ins->envVPAnt - 1; drawRepS = true; }
-	if (ins->envVRepE >= ins->envVPAnt) { ins->envVRepE = ins->envVPAnt - 1; drawRepE = true; }
+	if (ins->volEnvSustain >= ins->volEnvLength) { ins->volEnvSustain = ins->volEnvLength - 1; drawSust = true; }
+	if (ins->volEnvLoopStart >= ins->volEnvLength) { ins->volEnvLoopStart = ins->volEnvLength - 1; drawRepS = true; }
+	if (ins->volEnvLoopEnd >= ins->volEnvLength) { ins->volEnvLoopEnd = ins->volEnvLength - 1; drawRepE = true; }
 
 	if (drawSust) drawVolEnvSus();
 	if (drawRepS) drawVolEnvRepS();
 	if (drawRepE) drawVolEnvRepE();
 
-	if (ins->envVPAnt == 0)
+	if (ins->volEnvLength == 0)
 		editor.currVolEnvPoint = 0;
-	else if (editor.currVolEnvPoint >= ins->envVPAnt)
-		editor.currVolEnvPoint = ins->envVPAnt-1;
+	else if (editor.currVolEnvPoint >= ins->volEnvLength)
+		editor.currVolEnvPoint = ins->volEnvLength-1;
 
 	updateVolEnv = true;
 	setSongModifiedFlag();
@@ -891,13 +915,13 @@
 
 void volEnvSusUp(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envVSust < ins->envVPAnt-1)
+	if (ins->volEnvSustain < ins->volEnvLength-1)
 	{
-		ins->envVSust++;
+		ins->volEnvSustain++;
 		drawVolEnvSus();
 		updateVolEnv = true;
 		setSongModifiedFlag();
@@ -906,13 +930,13 @@
 
 void volEnvSusDown(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envVSust > 0)
+	if (ins->volEnvSustain > 0)
 	{
-		ins->envVSust--;
+		ins->volEnvSustain--;
 		drawVolEnvSus();
 		updateVolEnv = true;
 		setSongModifiedFlag();
@@ -921,13 +945,13 @@
 
 void volEnvRepSUp(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envVRepS < ins->envVRepE)
+	if (ins->volEnvLoopStart < ins->volEnvLoopEnd)
 	{
-		ins->envVRepS++;
+		ins->volEnvLoopStart++;
 		drawVolEnvRepS();
 		updateVolEnv = true;
 		setSongModifiedFlag();
@@ -936,13 +960,13 @@
 
 void volEnvRepSDown(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envVRepS > 0)
+	if (ins->volEnvLoopStart > 0)
 	{
-		ins->envVRepS--;
+		ins->volEnvLoopStart--;
 		drawVolEnvRepS();
 		updateVolEnv = true;
 		setSongModifiedFlag();
@@ -951,13 +975,13 @@
 
 void volEnvRepEUp(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envVRepE < ins->envVPAnt-1)
+	if (ins->volEnvLoopEnd < ins->volEnvLength-1)
 	{
-		ins->envVRepE++;
+		ins->volEnvLoopEnd++;
 		drawVolEnvRepE();
 		updateVolEnv = true;
 		setSongModifiedFlag();
@@ -966,13 +990,13 @@
 
 void volEnvRepEDown(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envVRepE > ins->envVRepS)
+	if (ins->volEnvLoopEnd > ins->volEnvLoopStart)
 	{
-		ins->envVRepE--;
+		ins->volEnvLoopEnd--;
 		drawVolEnvRepE();
 		updateVolEnv = true;
 		setSongModifiedFlag();
@@ -981,11 +1005,11 @@
 
 void panEnvAdd(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	const int16_t ant = ins->envPPAnt;
+	const int16_t ant = ins->panEnvLength;
 	if (ant >= 12)
 		return;
 
@@ -997,37 +1021,37 @@
 			i = 0;
 	}
 
-	if (i < ant-1 && ins->envPP[i+1][0]-ins->envPP[i][0] < 2)
+	if (i < ant-1 && ins->panEnvPoints[i+1][0]-ins->panEnvPoints[i][0] < 2)
 		return;
 
-	if (ins->envPP[i][0] >= 323)
+	if (ins->panEnvPoints[i][0] >= 323)
 		return;
 
 	for (int16_t j = ant; j > i; j--)
 	{
-		ins->envPP[j][0] = ins->envPP[j-1][0];
-		ins->envPP[j][1] = ins->envPP[j-1][1];
+		ins->panEnvPoints[j][0] = ins->panEnvPoints[j-1][0];
+		ins->panEnvPoints[j][1] = ins->panEnvPoints[j-1][1];
 	}
 
-	if (ins->envPSust > i) { ins->envPSust++; drawPanEnvSus();  }
-	if (ins->envPRepS > i) { ins->envPRepS++; drawPanEnvRepS(); }
-	if (ins->envPRepE > i) { ins->envPRepE++; drawPanEnvRepE(); }
+	if (ins->panEnvSustain > i) { ins->panEnvSustain++; drawPanEnvSus();  }
+	if (ins->panEnvLoopStart > i) { ins->panEnvLoopStart++; drawPanEnvRepS(); }
+	if (ins->panEnvLoopEnd > i) { ins->panEnvLoopEnd++; drawPanEnvRepE(); }
 
 	if (i < ant-1)
 	{
-		ins->envPP[i+1][0] = (ins->envPP[i][0] + ins->envPP[i+2][0]) / 2;
-		ins->envPP[i+1][1] = (ins->envPP[i][1] + ins->envPP[i+2][1]) / 2;
+		ins->panEnvPoints[i+1][0] = (ins->panEnvPoints[i][0] + ins->panEnvPoints[i+2][0]) / 2;
+		ins->panEnvPoints[i+1][1] = (ins->panEnvPoints[i][1] + ins->panEnvPoints[i+2][1]) / 2;
 	}
 	else
 	{
-		ins->envPP[i+1][0] = ins->envPP[i][0] + 10;
-		ins->envPP[i+1][1] = ins->envPP[i][1];
+		ins->panEnvPoints[i+1][0] = ins->panEnvPoints[i][0] + 10;
+		ins->panEnvPoints[i+1][1] = ins->panEnvPoints[i][1];
 	}
 
-	if (ins->envPP[i+1][0] > 324)
-		ins->envPP[i+1][0] = 324;
+	if (ins->panEnvPoints[i+1][0] > 324)
+		ins->panEnvPoints[i+1][0] = 324;
 
-	ins->envPPAnt++;
+	ins->panEnvLength++;
 
 	updatePanEnv = true;
 	setSongModifiedFlag();
@@ -1035,18 +1059,18 @@
 
 void panEnvDel(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
-	if (ins == NULL || editor.curInstr == 0 || ins->envPPAnt <= 2)
+	instr_t *ins = instr[editor.curInstr];
+	if (ins == NULL || editor.curInstr == 0 || ins->panEnvLength <= 2)
 		return;
 
 	int16_t i = (int16_t)editor.currPanEnvPoint;
-	if (i < 0 || i >= ins->envPPAnt)
+	if (i < 0 || i >= ins->panEnvLength)
 		return;
 
-	for (int16_t j = i; j < ins->envPPAnt; j++)
+	for (int16_t j = i; j < ins->panEnvLength; j++)
 	{
-		ins->envPP[j][0] = ins->envPP[j+1][0];
-		ins->envPP[j][1] = ins->envPP[j+1][1];
+		ins->panEnvPoints[j][0] = ins->panEnvPoints[j+1][0];
+		ins->panEnvPoints[j][1] = ins->panEnvPoints[j+1][1];
 	}
 
 	bool drawSust = false;
@@ -1053,25 +1077,25 @@
 	bool drawRepS = false;
 	bool drawRepE = false;
 
-	if (ins->envPSust > i) { ins->envPSust--; drawSust = true; }
-	if (ins->envPRepS > i) { ins->envPRepS--; drawRepS = true; }
-	if (ins->envPRepE > i) { ins->envPRepE--; drawRepE = true; }
+	if (ins->panEnvSustain > i) { ins->panEnvSustain--; drawSust = true; }
+	if (ins->panEnvLoopStart > i) { ins->panEnvLoopStart--; drawRepS = true; }
+	if (ins->panEnvLoopEnd > i) { ins->panEnvLoopEnd--; drawRepE = true; }
 
-	ins->envPP[0][0] = 0;
-	ins->envPPAnt--;
+	ins->panEnvPoints[0][0] = 0;
+	ins->panEnvLength--;
 
-	if (ins->envPSust >= ins->envPPAnt) { ins->envPSust = ins->envPPAnt - 1; drawSust = true; }
-	if (ins->envPRepS >= ins->envPPAnt) { ins->envPRepS = ins->envPPAnt - 1; drawRepS = true; }
-	if (ins->envPRepE >= ins->envPPAnt) { ins->envPRepE = ins->envPPAnt - 1; drawRepE = true; }
+	if (ins->panEnvSustain >= ins->panEnvLength) { ins->panEnvSustain = ins->panEnvLength - 1; drawSust = true; }
+	if (ins->panEnvLoopStart >= ins->panEnvLength) { ins->panEnvLoopStart = ins->panEnvLength - 1; drawRepS = true; }
+	if (ins->panEnvLoopEnd >= ins->panEnvLength) { ins->panEnvLoopEnd = ins->panEnvLength - 1; drawRepE = true; }
 
 	if (drawSust) drawPanEnvSus();
 	if (drawRepS) drawPanEnvRepS();
 	if (drawRepE) drawPanEnvRepE();
 
-	if (ins->envPPAnt == 0)
+	if (ins->panEnvLength == 0)
 		editor.currPanEnvPoint = 0;
-	else if (editor.currPanEnvPoint >= ins->envPPAnt)
-		editor.currPanEnvPoint = ins->envPPAnt-1;
+	else if (editor.currPanEnvPoint >= ins->panEnvLength)
+		editor.currPanEnvPoint = ins->panEnvLength-1;
 
 	updatePanEnv = true;
 	setSongModifiedFlag();
@@ -1079,13 +1103,13 @@
 
 void panEnvSusUp(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envPSust < ins->envPPAnt-1)
+	if (ins->panEnvSustain < ins->panEnvLength-1)
 	{
-		ins->envPSust++;
+		ins->panEnvSustain++;
 		drawPanEnvSus();
 		updatePanEnv = true;
 		setSongModifiedFlag();
@@ -1094,13 +1118,13 @@
 
 void panEnvSusDown(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envPSust > 0)
+	if (ins->panEnvSustain > 0)
 	{
-		ins->envPSust--;
+		ins->panEnvSustain--;
 		drawPanEnvSus();
 		updatePanEnv = true;
 		setSongModifiedFlag();
@@ -1109,13 +1133,13 @@
 
 void panEnvRepSUp(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envPRepS < ins->envPRepE)
+	if (ins->panEnvLoopStart < ins->panEnvLoopEnd)
 	{
-		ins->envPRepS++;
+		ins->panEnvLoopStart++;
 		drawPanEnvRepS();
 		updatePanEnv = true;
 		setSongModifiedFlag();
@@ -1124,13 +1148,13 @@
 
 void panEnvRepSDown(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envPRepS > 0)
+	if (ins->panEnvLoopStart > 0)
 	{
-		ins->envPRepS--;
+		ins->panEnvLoopStart--;
 		drawPanEnvRepS();
 		updatePanEnv = true;
 		setSongModifiedFlag();
@@ -1139,13 +1163,13 @@
 
 void panEnvRepEUp(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envPRepE < ins->envPPAnt-1)
+	if (ins->panEnvLoopEnd < ins->panEnvLength-1)
 	{
-		ins->envPRepE++;
+		ins->panEnvLoopEnd++;
 		drawPanEnvRepE();
 		updatePanEnv = true;
 		setSongModifiedFlag();
@@ -1154,13 +1178,13 @@
 
 void panEnvRepEDown(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 		return;
 
-	if (ins->envPRepE > ins->envPRepS)
+	if (ins->panEnvLoopEnd > ins->panEnvLoopStart)
 	{
-		ins->envPRepE--;
+		ins->panEnvLoopEnd--;
 		drawPanEnvRepE();
 		updatePanEnv = true;
 		setSongModifiedFlag();
@@ -1263,10 +1287,10 @@
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->vol != (uint8_t)pos)
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->volume != (uint8_t)pos)
 	{
-		s->vol = (uint8_t)pos;
+		s->volume = (uint8_t)pos;
 		drawVolume();
 		setSongModifiedFlag();
 	}
@@ -1280,10 +1304,10 @@
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->pan != (uint8_t)pos)
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->panning != (uint8_t)pos)
 	{
-		s->pan = (uint8_t)pos;
+		s->panning = (uint8_t)pos;
 		drawPanning();
 		setSongModifiedFlag();
 	}
@@ -1297,10 +1321,10 @@
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->fine != (int8_t)(pos - 128))
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->finetune != (int8_t)(pos - 128))
 	{
-		s->fine = (int8_t)(pos - 128);
+		s->finetune = (int8_t)(pos - 128);
 		drawFineTune();
 		drawC4Rate();
 		setSongModifiedFlag();
@@ -1309,7 +1333,7 @@
 
 void setFadeoutScroll(uint32_t pos)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL)
 	{
 		setScrollBarPos(SB_INST_FADEOUT, 0, false);
@@ -1322,9 +1346,9 @@
 		return;
 	}
 
-	if (ins->fadeOut != (uint16_t)pos)
+	if (ins->fadeout != (uint16_t)pos)
 	{
-		ins->fadeOut = (uint16_t)pos;
+		ins->fadeout = (uint16_t)pos;
 		drawFadeout();
 		setSongModifiedFlag();
 	}
@@ -1332,7 +1356,7 @@
 
 void setVibSpeedScroll(uint32_t pos)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 	{
 		setScrollBarPos(SB_INST_VIBSPEED, 0, false);
@@ -1349,7 +1373,7 @@
 
 void setVibDepthScroll(uint32_t pos)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 	{
 		setScrollBarPos(SB_INST_VIBDEPTH, 0, false);
@@ -1366,7 +1390,7 @@
 
 void setVibSweepScroll(uint32_t pos)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 	if (ins == NULL || editor.curInstr == 0)
 	{
 		setScrollBarPos(SB_INST_VIBSWEEP, 0, false);
@@ -1386,7 +1410,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibTyp = 0;
+	instr[editor.curInstr]->vibType = 0;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_SINE].state = RADIOBUTTON_CHECKED;
@@ -1399,7 +1423,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibTyp = 1;
+	instr[editor.curInstr]->vibType = 1;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_SQUARE].state = RADIOBUTTON_CHECKED;
@@ -1412,7 +1436,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibTyp = 2;
+	instr[editor.curInstr]->vibType = 2;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_RAMP_DOWN].state = RADIOBUTTON_CHECKED;
@@ -1425,7 +1449,7 @@
 	if (instr[editor.curInstr] == NULL || editor.curInstr == 0)
 		return;
 
-	instr[editor.curInstr]->vibTyp = 3;
+	instr[editor.curInstr]->vibType = 3;
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 	radioButtons[RB_INST_WAVE_RAMP_UP].state = RADIOBUTTON_CHECKED;
@@ -1442,7 +1466,7 @@
 		return;
 	}
 
-	instr[editor.curInstr]->envVTyp ^= 1;
+	instr[editor.curInstr]->volEnvFlags ^= 1;
 	updateVolEnv = true;
 
 	setSongModifiedFlag();
@@ -1457,7 +1481,7 @@
 		return;
 	}
 
-	instr[editor.curInstr]->envVTyp ^= 2;
+	instr[editor.curInstr]->volEnvFlags ^= 2;
 	updateVolEnv = true;
 
 	setSongModifiedFlag();
@@ -1472,7 +1496,7 @@
 		return;
 	}
 
-	instr[editor.curInstr]->envVTyp ^= 4;
+	instr[editor.curInstr]->volEnvFlags ^= 4;
 	updateVolEnv = true;
 
 	setSongModifiedFlag();
@@ -1487,7 +1511,7 @@
 		return;
 	}
 
-	instr[editor.curInstr]->envPTyp ^= 1;
+	instr[editor.curInstr]->panEnvFlags ^= 1;
 	updatePanEnv = true;
 
 	setSongModifiedFlag();
@@ -1502,7 +1526,7 @@
 		return;
 	}
 
-	instr[editor.curInstr]->envPTyp ^= 2;
+	instr[editor.curInstr]->panEnvFlags ^= 2;
 	updatePanEnv = true;
 
 	setSongModifiedFlag();
@@ -1517,7 +1541,7 @@
 		return;
 	}
 
-	instr[editor.curInstr]->envPTyp ^= 4;
+	instr[editor.curInstr]->panEnvFlags ^= 4;
 	updatePanEnv = true;
 
 	setSongModifiedFlag();
@@ -1546,7 +1570,7 @@
 {
 	uint8_t number = 0;
 	if (instr[editor.curInstr] != NULL && editor.curInstr != 0)
-		number = instr[editor.curInstr]->ta[note];
+		number = instr[editor.curInstr]->note2SampleLUT[note];
 
 	const uint16_t x = keyDigitXPos[key] + (octave * 77);
 
@@ -1633,9 +1657,9 @@
 	}
 
 	const uint8_t note = (octave * 12) + key;
-	if (instr[editor.curInstr]->ta[note] != editor.curSmp)
+	if (instr[editor.curInstr]->note2SampleLUT[note] != editor.curSmp)
 	{
-		instr[editor.curInstr]->ta[note] = editor.curSmp;
+		instr[editor.curInstr]->note2SampleLUT[note] = editor.curSmp;
 		writePianoNumber(note, key, octave);
 		setSongModifiedFlag();
 	}
@@ -1654,20 +1678,20 @@
 		if (chSyncData != NULL) // song is playing, use replayer channel state
 		{
 			syncedChannel_t *c = chSyncData->channels;
-			for (int32_t i = 0; i < song.antChn; i++, c++)
+			for (int32_t i = 0; i < song.numChannels; i++, c++)
 			{
-				if (c->instrNr == editor.curInstr && c->pianoNoteNr <= 95)
-					newStatus[c->pianoNoteNr] = true;
+				if (c->instrNum == editor.curInstr && c->pianoNoteNum <= 95)
+					newStatus[c->pianoNoteNum] = true;
 			}
 		}
 		else // song is not playing (jamming from keyboard/MIDI)
 		{
-			stmTyp *c = stm;
-			for (int32_t i = 0; i < song.antChn; i++, c++)
+			channel_t *c = channel;
+			for (int32_t i = 0; i < song.numChannels; i++, c++)
 			{
-				if (c->instrNr == editor.curInstr && c->envSustainActive)
+				if (c->instrNum == editor.curInstr && c->envSustainActive)
 				{
-					const int32_t note = getPianoKey(c->finalPeriod, c->fineTune, c->relTonNr);
+					const int32_t note = getPianoKey(c->finalPeriod, c->finetune, c->relativeNote);
 					if (note >= 0 && note <= 95)
 						newStatus[note] = true;
 				}
@@ -1694,7 +1718,7 @@
 	}
 }
 
-static void envelopeLine(int32_t nr, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t col)
+static void envelopeLine(int32_t envNum, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t pal)
 {
 	y1 = CLAMP(y1, 0, 66);
 	y2 = CLAMP(y2, 0, 66);
@@ -1701,12 +1725,12 @@
 	x1 = CLAMP(x1, 0, 335);
 	x2 = CLAMP(x2, 0, 335);
 
-	if (nr == 0)
+	if (envNum == 0) // volume envelope
 	{
 		y1 += 189;
 		y2 += 189;
 	}
-	else
+	else // panning envelope
 	{
 		y1 += 276;
 		y2 += 276;
@@ -1723,7 +1747,7 @@
 
 	const uint32_t pal1 = video.palette[PAL_BLCKMRK];
 	const uint32_t pal2 = video.palette[PAL_BLCKTXT];
-	const uint32_t pixVal = video.palette[col];
+	const uint32_t pixVal = video.palette[pal];
 	const int32_t pitch = sy * SCREEN_W;
 
 	uint32_t *dst32 = &video.frameBuffer[(y * SCREEN_W) + x];
@@ -1787,15 +1811,15 @@
 	}
 }
 
-static void envelopePixel(int32_t nr, int16_t x, int16_t y, uint8_t col)
+static void envelopePixel(int32_t envNum, int16_t x, int16_t y, uint8_t pal)
 {
-	y += (nr == 0) ? 189 : 276;
-	video.frameBuffer[(y * SCREEN_W) + x] = video.palette[col];
+	y += (envNum == 0) ? 189 : 276;
+	video.frameBuffer[(y * SCREEN_W) + x] = video.palette[pal];
 }
 
-static void envelopeDot(int32_t nr, int16_t x, int16_t y)
+static void envelopeDot(int32_t envNum, int16_t x, int16_t y)
 {
-	y += (nr == 0) ? 189 : 276;
+	y += (envNum == 0) ? 189 : 276;
 
 	const uint32_t pixVal = video.palette[PAL_BLCKTXT];
 	uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
@@ -1810,11 +1834,11 @@
 	}
 }
 
-static void envelopeVertLine(int32_t nr, int16_t x, int16_t y, uint8_t col)
+static void envelopeVertLine(int32_t envNum, int16_t x, int16_t y, uint8_t pal)
 {
-	y += (nr == 0) ? 189 : 276;
+	y += (envNum == 0) ? 189 : 276;
 
-	const uint32_t pixVal1 = video.palette[col];
+	const uint32_t pixVal1 = video.palette[pal];
 	const uint32_t pixVal2 = video.palette[PAL_BLCKTXT];
 
 	uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x];
@@ -1827,45 +1851,46 @@
 	}
 }
 
-static void writeEnvelope(int32_t nr)
+static void writeEnvelope(int32_t envNum)
 {
 	uint8_t selected;
 	int16_t i, nd, sp, ls, le, (*curEnvP)[2];
 
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 
 	// clear envelope area
-	if (nr == 0)
+	if (envNum == 0)
 		clearRect(5, 189, 333, 67);
 	else
 		clearRect(5, 276, 333, 67);
 
 	// draw dotted x/y lines
-	for (i = 0; i <= 32; i++) envelopePixel(nr, 5, 1 + i * 2, PAL_PATTEXT);
-	for (i = 0; i <= 8; i++) envelopePixel(nr, 4, 1 + i * 8, PAL_PATTEXT);
-	for (i = 0; i <= 162; i++) envelopePixel(nr, 8 + i *  2, 65, PAL_PATTEXT);
-	for (i = 0; i <= 6; i++) envelopePixel(nr, 8 + i * 50, 66, PAL_PATTEXT);
+	for (i = 0; i <= 32; i++) envelopePixel(envNum, 5, 1 + i * 2, PAL_PATTEXT);
+	for (i = 0; i <= 8; i++) envelopePixel(envNum, 4, 1 + i * 8, PAL_PATTEXT);
+	for (i = 0; i <= 162; i++) envelopePixel(envNum, 8 + i *  2, 65, PAL_PATTEXT);
+	for (i = 0; i <= 6; i++) envelopePixel(envNum, 8 + i * 50, 66, PAL_PATTEXT);
 
 	// draw center line on pan envelope
-	if (nr == 1)
-		envelopeLine(nr, 8, 33, 332, 33, PAL_BLCKMRK);
+	if (envNum == 1)
+		envelopeLine(envNum, 8, 33, 332, 33, PAL_BLCKMRK);
 
 	if (ins == NULL)
 		return;
 
 	// collect variables
-	if (nr == 0)
+
+	if (envNum == 0) // volume envelope
 	{
-		nd = ins->envVPAnt;
-		if (ins->envVTyp & 2)
-			sp = ins->envVSust;
+		nd = ins->volEnvLength;
+		if (ins->volEnvFlags & ENV_SUSTAIN)
+			sp = ins->volEnvSustain;
 		else
 			sp = -1;
 
-		if (ins->envVTyp & 4)
+		if (ins->volEnvFlags & ENV_LOOP)
 		{
-			ls = ins->envVRepS;
-			le = ins->envVRepE;
+			ls = ins->volEnvLoopStart;
+			le = ins->volEnvLoopEnd;
 		}
 		else
 		{
@@ -1873,21 +1898,21 @@
 			le = -1;
 		}
 
-		curEnvP = ins->envVP;
+		curEnvP = ins->volEnvPoints;
 		selected = editor.currVolEnvPoint;
 	}
-	else
+	else // panning envelope
 	{
-		nd = ins->envPPAnt;
-		if (ins->envPTyp & 2)
-			sp = ins->envPSust;
+		nd = ins->panEnvLength;
+		if (ins->panEnvFlags & ENV_SUSTAIN)
+			sp = ins->panEnvSustain;
 		else
 			sp = -1;
 
-		if (ins->envPTyp & 4)
+		if (ins->panEnvFlags & ENV_LOOP)
 		{
-			ls = ins->envPRepS;
-			le = ins->envPRepE;
+			ls = ins->panEnvLoopStart;
+			le = ins->panEnvLoopEnd;
 		}
 		else
 		{
@@ -1895,7 +1920,7 @@
 			le = -1;
 		}
 
-		curEnvP = ins->envPP;
+		curEnvP = ins->panEnvPoints;
 		selected = editor.currPanEnvPoint;
 	}
 
@@ -1913,48 +1938,48 @@
 
 		x = CLAMP(x, 0, 324);
 		
-		if (nr == 0)
+		if (envNum == 0) // volume envelope
 			y = CLAMP(y, 0, 64);
-		else
+		else // panning envelope
 			y = CLAMP(y, 0, 63);
 
 		if ((uint16_t)curEnvP[i][0] <= 324)
 		{
-			envelopeDot(nr, 7 + x, 64 - y);
+			envelopeDot(envNum, 7 + x, 64 - y);
 
 			// draw "envelope selected" data
 			if (i == selected)
 			{
-				envelopeLine(nr, 5  + x, 64 - y, 5  + x, 66 - y, PAL_BLCKTXT);
-				envelopeLine(nr, 11 + x, 64 - y, 11 + x, 66 - y, PAL_BLCKTXT);
-				envelopePixel(nr, 5, 65 - y, PAL_BLCKTXT);
-				envelopePixel(nr, 8 + x, 65, PAL_BLCKTXT);
+				envelopeLine(envNum, 5  + x, 64 - y, 5  + x, 66 - y, PAL_BLCKTXT);
+				envelopeLine(envNum, 11 + x, 64 - y, 11 + x, 66 - y, PAL_BLCKTXT);
+				envelopePixel(envNum, 5, 65 - y, PAL_BLCKTXT);
+				envelopePixel(envNum, 8 + x, 65, PAL_BLCKTXT);
 			}
 
 			// draw loop start marker
 			if (i == ls)
 			{
-				envelopeLine(nr, x + 6, 1, x + 10, 1, PAL_PATTEXT);
-				envelopeLine(nr, x + 7, 2, x +  9, 2, PAL_PATTEXT);
-				envelopeVertLine(nr, x + 8, 1, PAL_PATTEXT);
+				envelopeLine(envNum, x + 6, 1, x + 10, 1, PAL_PATTEXT);
+				envelopeLine(envNum, x + 7, 2, x +  9, 2, PAL_PATTEXT);
+				envelopeVertLine(envNum, x + 8, 1, PAL_PATTEXT);
 			}
 
 			// draw sustain marker
 			if (i == sp)
-				envelopeVertLine(nr, x + 8, 1, PAL_BLCKTXT);
+				envelopeVertLine(envNum, x + 8, 1, PAL_BLCKTXT);
 
 			// draw loop end marker
 			if (i == le)
 			{
-				envelopeLine(nr, x + 6, 65, x + 10, 65, PAL_PATTEXT);
-				envelopeLine(nr, x + 7, 64, x +  9, 64, PAL_PATTEXT);
-				envelopeVertLine(nr, x + 8, 1, PAL_PATTEXT);
+				envelopeLine(envNum, x + 6, 65, x + 10, 65, PAL_PATTEXT);
+				envelopeLine(envNum, x + 7, 64, x +  9, 64, PAL_PATTEXT);
+				envelopeVertLine(envNum, x + 8, 1, PAL_PATTEXT);
 			}
 		}
 
 		// draw envelope line
 		if (i > 0 && lx < x)
-			envelopeLine(nr, lx + 8, 65 - ly, x + 8, 65 - y, PAL_PATTEXT);
+			envelopeLine(envNum, lx + 8, 65 - ly, x + 8, 65 - y, PAL_PATTEXT);
 
 		lx = x;
 		ly = y;
@@ -2010,7 +2035,7 @@
 {
 	int16_t tick, val;
 
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 
 	if (updateVolEnv)
 	{
@@ -2020,10 +2045,10 @@
 		tick = 0;
 		val = 0;
 
-		if (ins != NULL && ins->envVPAnt > 0)
+		if (ins != NULL && ins->volEnvLength > 0)
 		{
-			tick = ins->envVP[editor.currVolEnvPoint][0];
-			val = ins->envVP[editor.currVolEnvPoint][1];
+			tick = ins->volEnvPoints[editor.currVolEnvPoint][0];
+			val = ins->volEnvPoints[editor.currVolEnvPoint][1];
 		}
 
 		drawVolEnvCoords(tick, val);
@@ -2037,10 +2062,10 @@
 		tick = 0;
 		val = 32;
 
-		if (ins != NULL && ins->envPPAnt > 0)
+		if (ins != NULL && ins->panEnvLength > 0)
 		{
-			tick = ins->envPP[editor.currPanEnvPoint][0];
-			val = ins->envPP[editor.currPanEnvPoint][1];
+			tick = ins->panEnvPoints[editor.currPanEnvPoint][0];
+			val = ins->panEnvPoints[editor.currPanEnvPoint][1];
 		}
 
 		drawPanEnvCoords(tick, val);
@@ -2126,13 +2151,13 @@
 void updateInstEditor(void)
 {
 	uint16_t tmpID;
-	sampleTyp *s;
-	instrTyp *ins = getCurDispInstr();
+	sample_t *s;
+	instr_t *ins = getCurDispInstr();
 
 	if (instr[editor.curInstr] == NULL)
-		s = &ins->samp[0];
+		s = &ins->smp[0];
 	else
-		s = &ins->samp[editor.curSmp];
+		s = &ins->smp[editor.curSmp];
 
 	// update instrument editor extension
 	if (ui.instEditorExtShown)
@@ -2169,13 +2194,13 @@
 	drawVibDepth();
 	drawVibSweep();
 	drawC4Rate();
-	drawRelTone();
+	drawRelativeNote();
 
 	// set scroll bars
-	setScrollBarPos(SB_INST_VOL, s->vol, false);
-	setScrollBarPos(SB_INST_PAN, s->pan, false);
-	setScrollBarPos(SB_INST_FTUNE, 128 + s->fine, false);
-	setScrollBarPos(SB_INST_FADEOUT, ins->fadeOut, false);
+	setScrollBarPos(SB_INST_VOL, s->volume, false);
+	setScrollBarPos(SB_INST_PAN, s->panning, false);
+	setScrollBarPos(SB_INST_FTUNE, 128 + s->finetune, false);
+	setScrollBarPos(SB_INST_FADEOUT, ins->fadeout, false);
 	setScrollBarPos(SB_INST_VIBSPEED, ins->vibRate, false);
 	setScrollBarPos(SB_INST_VIBDEPTH, ins->vibDepth, false);
 	setScrollBarPos(SB_INST_VIBSWEEP, ins->vibSweep, false);
@@ -2183,7 +2208,7 @@
 	// set radio buttons
 
 	uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
-	switch (ins->vibTyp)
+	switch (ins->vibType)
 	{
 		default:
 		case 0: tmpID = RB_INST_WAVE_SINE;      break;
@@ -2194,18 +2219,19 @@
 
 	radioButtons[tmpID].state = RADIOBUTTON_CHECKED;
 
-	// set check boxes
+	// set checkboxes
 
-	checkBoxes[CB_INST_VENV].checked = (ins->envVTyp & 1) ? true : false;
-	checkBoxes[CB_INST_VENV_SUS].checked = (ins->envVTyp & 2) ? true : false;
-	checkBoxes[CB_INST_VENV_LOOP].checked = (ins->envVTyp & 4) ? true : false;
-	checkBoxes[CB_INST_PENV].checked = (ins->envPTyp & 1) ? true : false;
-	checkBoxes[CB_INST_PENV_SUS].checked = (ins->envPTyp & 2) ? true : false;
-	checkBoxes[CB_INST_PENV_LOOP].checked = (ins->envPTyp & 4) ? true : false;
+	checkBoxes[CB_INST_VENV].checked      = (ins->volEnvFlags & ENV_ENABLED) ? true : false;
+	checkBoxes[CB_INST_VENV_SUS].checked  = (ins->volEnvFlags & ENV_SUSTAIN) ? true : false;
+	checkBoxes[CB_INST_VENV_LOOP].checked = (ins->volEnvFlags & ENV_LOOP)    ? true : false;
 
-	if (editor.currVolEnvPoint >= ins->envVPAnt) editor.currVolEnvPoint = 0;
-	if (editor.currPanEnvPoint >= ins->envPPAnt) editor.currPanEnvPoint = 0;
+	checkBoxes[CB_INST_PENV].checked      = (ins->panEnvFlags & ENV_ENABLED) ? true : false;
+	checkBoxes[CB_INST_PENV_SUS].checked  = (ins->panEnvFlags & ENV_SUSTAIN) ? true : false;
+	checkBoxes[CB_INST_PENV_LOOP].checked = (ins->panEnvFlags & ENV_LOOP)    ? true : false;
 
+	if (editor.currVolEnvPoint >= ins->volEnvLength) editor.currVolEnvPoint = 0;
+	if (editor.currPanEnvPoint >= ins->panEnvLength) editor.currPanEnvPoint = 0;
+
 	showRadioButtonGroup(RB_GROUP_INST_WAVEFORM);
 
 	drawCheckBox(CB_INST_VENV);
@@ -2257,7 +2283,7 @@
 	textOutShadow(342, 334, PAL_FORGRND, PAL_DSKTOP2, "End");
 	textOutShadow(443, 177, PAL_FORGRND, PAL_DSKTOP2, "Volume");
 	textOutShadow(443, 191, PAL_FORGRND, PAL_DSKTOP2, "Panning");
-	textOutShadow(443, 205, PAL_FORGRND, PAL_DSKTOP2, "Tune");
+	textOutShadow(443, 205, PAL_FORGRND, PAL_DSKTOP2, "F.tune");
 	textOutShadow(442, 222, PAL_FORGRND, PAL_DSKTOP2, "Fadeout");
 	textOutShadow(442, 236, PAL_FORGRND, PAL_DSKTOP2, "Vib.speed");
 	textOutShadow(442, 250, PAL_FORGRND, PAL_DSKTOP2, "Vib.depth");
@@ -2363,9 +2389,9 @@
 	if (!ui.instEditorShown || editor.curInstr == 0 || instr[editor.curInstr] == NULL)
 		return false;
 
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 
-	uint8_t ant = ins->envVPAnt;
+	uint8_t ant = ins->volEnvLength;
 	if (ant > 12)
 		ant = 12;
 
@@ -2377,7 +2403,7 @@
 		if (my < 189 || my > 256 || mx < 7 || mx > 334)
 			return false;
 
-		if (ins->envVPAnt == 0)
+		if (ins->volEnvLength == 0)
 			return true;
 
 		lastMouseX = mx;
@@ -2385,8 +2411,8 @@
 
 		for (uint8_t i = 0; i < ant; i++)
 		{
-			const int32_t x = 8 + ins->envVP[i][0];
-			const int32_t y = 190 + (64 - ins->envVP[i][1]);
+			const int32_t x = 8 + ins->volEnvPoints[i][0];
+			const int32_t y = 190 + (64 - ins->volEnvPoints[i][1]);
 
 			if (mx >= x-2 && mx <= x+2 && my >= y-2 && my <= y+2)
 			{
@@ -2404,7 +2430,7 @@
 		return true;
 	}
 
-	if (ins->envVPAnt == 0)
+	if (ins->volEnvLength == 0)
 		return true;
 
 	if (mx != lastMouseX)
@@ -2418,19 +2444,19 @@
 
 			if (editor.currVolEnvPoint == ant-1)
 			{
-				minX = ins->envVP[editor.currVolEnvPoint-1][0] + 1;
+				minX = ins->volEnvPoints[editor.currVolEnvPoint-1][0] + 1;
 				maxX = 324;
 			}
 			else
 			{
-				minX = ins->envVP[editor.currVolEnvPoint-1][0] + 1;
-				maxX = ins->envVP[editor.currVolEnvPoint+1][0] - 1;
+				minX = ins->volEnvPoints[editor.currVolEnvPoint-1][0] + 1;
+				maxX = ins->volEnvPoints[editor.currVolEnvPoint+1][0] - 1;
 			}
 
 			minX = CLAMP(minX, 0, 324);
 			maxX = CLAMP(maxX, 0, 324);
 
-			ins->envVP[editor.currVolEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX));
+			ins->volEnvPoints[editor.currVolEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX));
 			updateVolEnv = true;
 
 			setSongModifiedFlag();
@@ -2444,7 +2470,7 @@
 		my -= saveMouseY;
 		my = 64 - CLAMP(my, 0, 64);
 
-		ins->envVP[editor.currVolEnvPoint][1] = (int16_t)my;
+		ins->volEnvPoints[editor.currVolEnvPoint][1] = (int16_t)my;
 		updateVolEnv = true;
 
 		setSongModifiedFlag();
@@ -2460,9 +2486,9 @@
 	if (!ui.instEditorShown || editor.curInstr == 0 || instr[editor.curInstr] == NULL)
 		return false;
 
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 
-	uint8_t ant = ins->envPPAnt;
+	uint8_t ant = ins->panEnvLength;
 	if (ant > 12)
 		ant = 12;
 
@@ -2474,7 +2500,7 @@
 		if (my < 277 || my > 343 || mx < 7 || mx > 334)
 			return false;
 
-		if (ins->envPPAnt == 0)
+		if (ins->panEnvLength == 0)
 			return true;
 
 		lastMouseX = mx;
@@ -2482,8 +2508,8 @@
 
 		for (uint8_t i = 0; i < ant; i++)
 		{
-			const int32_t x = 8 + ins->envPP[i][0];
-			const int32_t y = 277 + (63 - ins->envPP[i][1]);
+			const int32_t x = 8 + ins->panEnvPoints[i][0];
+			const int32_t y = 277 + (63 - ins->panEnvPoints[i][1]);
 
 			if (mx >= x-2 && mx <= x+2 && my >= y-2 && my <= y+2)
 			{
@@ -2501,7 +2527,7 @@
 		return true;
 	}
 
-	if (ins->envPPAnt == 0)
+	if (ins->panEnvLength == 0)
 		return true;
 
 	if (mx != lastMouseX)
@@ -2515,19 +2541,19 @@
 
 			if (editor.currPanEnvPoint == ant-1)
 			{
-				minX = ins->envPP[editor.currPanEnvPoint-1][0] + 1;
+				minX = ins->panEnvPoints[editor.currPanEnvPoint-1][0] + 1;
 				maxX = 324;
 			}
 			else
 			{
-				minX = ins->envPP[editor.currPanEnvPoint-1][0] + 1;
-				maxX = ins->envPP[editor.currPanEnvPoint+1][0] - 1;
+				minX = ins->panEnvPoints[editor.currPanEnvPoint-1][0] + 1;
+				maxX = ins->panEnvPoints[editor.currPanEnvPoint+1][0] - 1;
 			}
 
 			minX = CLAMP(minX, 0, 324);
 			maxX = CLAMP(maxX, 0, 324);
 
-			ins->envPP[editor.currPanEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX));
+			ins->panEnvPoints[editor.currPanEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX));
 			updatePanEnv = true;
 
 			setSongModifiedFlag();
@@ -2541,7 +2567,7 @@
 		my -= saveMouseY;
 		my  = 63 - CLAMP(my, 0, 63);
 
-		ins->envPP[editor.currPanEnvPoint][1] = (int16_t)my;
+		ins->panEnvPoints[editor.currPanEnvPoint][1] = (int16_t)my;
 		updatePanEnv = true;
 
 		setSongModifiedFlag();
@@ -2578,7 +2604,7 @@
 
 void drawInstEditorExt(void)
 {
-	instrTyp *ins = instr[editor.curInstr];
+	instr_t *ins = instr[editor.curInstr];
 
 	drawFramework(0,  92, 291, 17, FRAMEWORK_TYPE1);
 	drawFramework(0, 109, 291, 19, FRAMEWORK_TYPE1);
@@ -2870,8 +2896,8 @@
 
 static int32_t SDLCALL saveInstrThread(void *ptr)
 {
-	instrXIHeaderTyp ih;
-	sampleTyp *s;
+	xiHdr_t ih;
+	sample_t *s;
 
 	if (editor.tmpFilenameU == NULL)
 	{
@@ -2879,8 +2905,8 @@
 		return false;
 	}
 
-	const int16_t n = getUsedSamples(saveInstrNr);
-	if (n == 0 || instr[saveInstrNr] == NULL)
+	const int32_t numSamples = getUsedSamples(saveInstrNum);
+	if (numSamples == 0 || instr[saveInstrNum] == NULL)
 	{
 		okBoxThreadSafe(0, "System message", "Instrument is empty!");
 		return false;
@@ -2895,63 +2921,94 @@
 
 	memset(&ih, 0, sizeof (ih)); // important, also clears reserved stuff
 
-	memcpy(ih.sig, "Extended Instrument: ", 21);
-	memset(ih.name, ' ', 22);
-	memcpy(ih.name, song.instrName[saveInstrNr], strlen(song.instrName[saveInstrNr]));
+	memcpy(ih.ID, "Extended Instrument: ", 21);
+	ih.version = 0x0102;
+
+	// song name
+	int32_t nameLength = (int32_t)strlen(song.instrName[saveInstrNum]);
+	if (nameLength > 22)
+		nameLength = 22;
+
+	memset(ih.name, ' ', 22); // yes, FT2 pads the name with spaces
+	if (nameLength > 0)
+		memcpy(ih.name, song.instrName[saveInstrNum], nameLength);
+
 	ih.name[22] = 0x1A;
-	memcpy(ih.progName, PROG_NAME_STR, 20);
-	ih.ver = 0x0102;
 
+	// program/tracker name
+	nameLength = (int32_t)strlen(PROG_NAME_STR);
+	if (nameLength > 20)
+		nameLength = 20;
+
+	memset(ih.progName, ' ', 20); // yes, FT2 pads the name with spaces
+	if (nameLength > 0)
+		memcpy(ih.progName, PROG_NAME_STR, nameLength);
+
 	// copy over instrument struct data to instrument header
-	instrTyp *ins = instr[saveInstrNr];
-	memcpy(ih.ta, ins->ta, 96);
-	memcpy(ih.envVP, ins->envVP, 12*2*sizeof(int16_t));
-	memcpy(ih.envPP, ins->envPP, 12*2*sizeof(int16_t));
-	ih.envVPAnt = ins->envVPAnt;
-	ih.envPPAnt = ins->envPPAnt;
-	ih.envVSust = ins->envVSust;
-	ih.envVRepS = ins->envVRepS;
-	ih.envVRepE = ins->envVRepE;
-	ih.envPSust = ins->envPSust;
-	ih.envPRepS = ins->envPRepS;
-	ih.envPRepE = ins->envPRepE;
-	ih.envVTyp = ins->envVTyp;
-	ih.envPTyp = ins->envPTyp;
-	ih.vibTyp = ins->vibTyp;
+	instr_t *ins = instr[saveInstrNum];
+	memcpy(ih.note2SampleLUT, ins->note2SampleLUT, 96);
+	memcpy(ih.volEnvPoints, ins->volEnvPoints, 12*2*sizeof(int16_t));
+	memcpy(ih.panEnvPoints, ins->panEnvPoints, 12*2*sizeof(int16_t));
+	ih.volEnvLength = ins->volEnvLength;
+	ih.panEnvLength = ins->panEnvLength;
+	ih.volEnvSustain = ins->volEnvSustain;
+	ih.volEnvLoopStart = ins->volEnvLoopStart;
+	ih.volEnvLoopEnd = ins->volEnvLoopEnd;
+	ih.panEnvSustain = ins->panEnvSustain;
+	ih.panEnvLoopStart = ins->panEnvLoopStart;
+	ih.panEnvLoopEnd = ins->panEnvLoopEnd;
+	ih.volEnvFlags = ins->volEnvFlags;
+	ih.panEnvFlags = ins->panEnvFlags;
+	ih.vibType = ins->vibType;
 	ih.vibSweep = ins->vibSweep;
 	ih.vibDepth = ins->vibDepth;
 	ih.vibRate = ins->vibRate;
-	ih.fadeOut = ins->fadeOut;
+	ih.fadeout = ins->fadeout;
 	ih.midiOn = ins->midiOn ? 1 : 0;
 	ih.midiChannel = ins->midiChannel;
 	ih.midiProgram = ins->midiProgram;
 	ih.midiBend = ins->midiBend;
 	ih.mute = ins->mute ? 1 : 0;
-	ih.antSamp = n;
+	ih.numSamples = (uint16_t)numSamples;
 
 	// copy over sample struct datas to sample headers
-	s = instr[saveInstrNr]->samp;
-	for (int32_t i = 0; i < n; i++, s++)
+	s = instr[saveInstrNum]->smp;
+	for (int32_t i = 0; i < numSamples; i++, s++)
 	{
-		sampleHeaderTyp *dst = &ih.samp[i];
+		xmSmpHdr_t *dst = &ih.smp[i];
 
-		dst->len = s->len;
-		dst->repS = s->repS;
-		dst->repL = s->repL;
-		dst->vol = s->vol;
-		dst->fine = s->fine;
-		dst->typ = s->typ;
-		dst->pan = s->pan;
-		dst->relTon = s->relTon;
+		bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 
-		dst->nameLen = (uint8_t)strlen(s->name);
-		memcpy(dst->name, s->name, 22);
+		dst->length = s->length;
+		dst->loopStart = s->loopStart;
+		dst->loopLength = s->loopLength;
 
-		if (s->pek == NULL)
-			dst->len = 0;
+		if (sample16Bit)
+		{
+			dst->length <<= 1;
+			dst->loopStart <<= 1;
+			dst->loopLength <<= 1;
+		}
+
+		dst->volume = s->volume;
+		dst->finetune = s->finetune;
+		dst->flags = s->flags;
+		dst->panning = s->panning;
+		dst->relativeNote = s->relativeNote;
+
+		dst->nameLength = (uint8_t)strlen(s->name);
+		if (dst->nameLength > 22)
+			dst->nameLength = 22;
+
+		memset(dst->name, 0, 22);
+		if (dst->nameLength > 0)
+			memcpy(dst->name, s->name, dst->nameLength);
+
+		if (s->dataPtr == NULL)
+			dst->length = 0;
 	}
 
-	size_t result = fwrite(&ih, INSTR_XI_HEADER_SIZE + (ih.antSamp * sizeof (sampleHeaderTyp)), 1, f);
+	size_t result = fwrite(&ih, INSTR_XI_HEADER_SIZE + (ih.numSamples * sizeof (xmSmpHdr_t)), 1, f);
 	if (result != 1)
 	{
 		fclose(f);
@@ -2960,20 +3017,20 @@
 	}
 
 	pauseAudio();
-	s = instr[saveInstrNr]->samp;
-	for (int32_t i = 0; i < n; i++, s++)
+	s = instr[saveInstrNum]->smp;
+	for (int32_t i = 0; i < numSamples; i++, s++)
 	{
-		if (s->pek != NULL && s->len > 0)
+		if (s->dataPtr != NULL && s->length > 0)
 		{
-			restoreSample(s);
-			samp2Delta(s->pek, s->len, s->typ);
+			unfixSample(s);
+			samp2Delta(s->dataPtr, s->length, s->flags);
 
-			result = fwrite(s->pek, 1, s->len, f);
+			result = fwrite(s->dataPtr, 1, SAMPLE_LENGTH_BYTES(s), f);
 
-			delta2Samp(s->pek, s->len, s->typ);
+			delta2Samp(s->dataPtr, s->length, s->flags);
 			fixSample(s);
 
-			if (result != (size_t)s->len) // write not OK
+			if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK
 			{
 				resumeAudio();
 				fclose(f);
@@ -2987,19 +3044,19 @@
 	fclose(f);
 
 	editor.diskOpReadDir = true; // force diskop re-read
-
 	setMouseBusy(false);
+
 	return true;
 
 	(void)ptr;
 }
 
-void saveInstr(UNICHAR *filenameU, int16_t nr)
+void saveInstr(UNICHAR *filenameU, int16_t insNum)
 {
-	if (nr == 0)
+	if (insNum == 0)
 		return;
 
-	saveInstrNr = nr;
+	saveInstrNum = insNum;
 	UNICHAR_STRCPY(editor.tmpFilenameU, filenameU);
 
 	mouseAnimOn();
@@ -3015,8 +3072,8 @@
 
 static int16_t getPATNote(int32_t freq)
 {
-	const double dNote = (log2(freq * (1.0 / 440000.0)) * 12.0) + 57.0;
-	const int32_t note = (const int32_t)(dNote + 0.5);
+	const double dNote = (log2(freq / 440000.0) * 12.0) + 57.0;
+	const int32_t note = (const int32_t)(dNote + 0.5); // rounded
 
 	return (int16_t)note;
 }
@@ -3023,17 +3080,17 @@
 
 static int32_t SDLCALL loadInstrThread(void *ptr)
 {
-	int8_t *newPtr;
 	int16_t a, b;
-	int32_t i, j;
-	instrXIHeaderTyp ih;
-	instrPATHeaderTyp ih_PAT;
-	instrPATWaveHeaderTyp ih_PATWave;
-	sampleHeaderTyp *src;
-	sampleTyp *s;
-	instrTyp *ins;
+	int32_t i, j, numLoadedSamples;
+	xiHdr_t xi_h;
+	patHdr_t pat_h;
+	patWaveHdr_t patWave_h;
+	xmSmpHdr_t *src;
+	sample_t *s;
+	instr_t *ins;
 
 	bool stereoWarning = false;
+	numLoadedSamples = 0;
 
 	if (editor.tmpInstrFilenameU == NULL)
 	{
@@ -3048,29 +3105,34 @@
 		return false;
 	}
 
-	memset(&ih, 0, sizeof (ih));
+	memset(&xi_h, 0, sizeof (xi_h));
+	memset(&pat_h, 0, sizeof (pat_h));
+	memset(&patWave_h, 0, sizeof (patWave_h));
 
-	fread(&ih, INSTR_XI_HEADER_SIZE, 1, f);
-	if (!strncmp(ih.sig, "Extended Instrument: ", 21))
+	fread(&xi_h, INSTR_XI_HEADER_SIZE, 1, f);
+	if (!strncmp(xi_h.ID, "Extended Instrument: ", 21))
 	{
 		// XI - Extended Instrument
 
-		if (ih.ver != 0x0101 && ih.ver != 0x0102)
+		if (xi_h.version != 0x0101 && xi_h.version != 0x0102)
 		{
 			okBoxThreadSafe(0, "System message", "Incompatible format version!");
 			goto loadDone;
 		}
 
-		if (ih.ver == 0x0101) // not even FT2.01 can save old v1.01 .XI files, so I have no way to verify this.
+		// not even FT2.01 can save old v1.01 .XI files, so I have no way to verify this
+		if (xi_h.version == 0x0101) 
 		{
 			fseek(f, -20, SEEK_CUR);
-			ih.antSamp = ih.midiProgram;
-			ih.midiProgram = 0;
-			ih.midiBend = 0;
-			ih.mute = false;
+			xi_h.numSamples = xi_h.midiProgram;
+			xi_h.midiProgram = 0;
+			xi_h.midiBend = 0;
+			xi_h.mute = false;
 		}
 
-		memcpy(song.instrName[editor.curInstr], ih.name, 22);
+		numLoadedSamples = xi_h.numSamples;
+
+		memcpy(song.instrName[editor.curInstr], xi_h.name, 22);
 		song.instrName[editor.curInstr][22] = '\0';
 
 		pauseAudio();
@@ -3077,7 +3139,7 @@
 
 		freeInstr(editor.curInstr);
 
-		if (ih.antSamp > 0)
+		if (xi_h.numSamples > 0)
 		{
 			if (!allocateInstr(editor.curInstr))
 			{
@@ -3089,69 +3151,36 @@
 			// copy instrument header elements to our instrument struct
 
 			ins = instr[editor.curInstr];
-			memcpy(ins->ta, ih.ta, 96);
-			memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
-			memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
-			ins->envVPAnt = ih.envVPAnt;
-			ins->envPPAnt = ih.envPPAnt;
-			ins->envVSust = ih.envVSust;
-			ins->envVRepS = ih.envVRepS;
-			ins->envVRepE = ih.envVRepE;
-			ins->envPSust = ih.envPSust;
-			ins->envPRepS = ih.envPRepS;
-			ins->envPRepE = ih.envPRepE;
-			ins->envVTyp = ih.envVTyp;
-			ins->envPTyp = ih.envPTyp;
-			ins->vibTyp = ih.vibTyp;
-			ins->vibSweep = ih.vibSweep;
-			ins->vibDepth = ih.vibDepth;
-			ins->vibRate = ih.vibRate;
-			ins->fadeOut = ih.fadeOut;
-			ins->midiOn = (ih.midiOn == 1) ? true : false;
-			ins->midiChannel = ih.midiChannel;
-			ins->midiProgram = ih.midiProgram;
-			ins->midiBend = ih.midiBend;
-			ins->mute = (ih.mute == 1) ? true : false; // correct logic! Don't mess with this
-			ins->antSamp = ih.antSamp; // used in loadInstrSample()
+			memcpy(ins->note2SampleLUT, xi_h.note2SampleLUT, 96);
+			memcpy(ins->volEnvPoints, xi_h.volEnvPoints, 12*2*sizeof(int16_t));
+			memcpy(ins->panEnvPoints, xi_h.panEnvPoints, 12*2*sizeof(int16_t));
+			ins->volEnvLength = xi_h.volEnvLength;
+			ins->panEnvLength = xi_h.panEnvLength;
+			ins->volEnvSustain = xi_h.volEnvSustain;
+			ins->volEnvLoopStart = xi_h.volEnvLoopStart;
+			ins->volEnvLoopEnd = xi_h.volEnvLoopEnd;
+			ins->panEnvSustain = xi_h.panEnvSustain;
+			ins->panEnvLoopStart = xi_h.panEnvLoopStart;
+			ins->panEnvLoopEnd = xi_h.panEnvLoopEnd;
+			ins->volEnvFlags = xi_h.volEnvFlags;
+			ins->panEnvFlags = xi_h.panEnvFlags;
+			ins->vibType = xi_h.vibType;
+			ins->vibSweep = xi_h.vibSweep;
+			ins->vibDepth = xi_h.vibDepth;
+			ins->vibRate = xi_h.vibRate;
+			ins->fadeout = xi_h.fadeout;
+			ins->midiOn = (xi_h.midiOn == 1) ? true : false;
+			ins->midiChannel = xi_h.midiChannel;
+			ins->midiProgram = xi_h.midiProgram;
+			ins->midiBend = xi_h.midiBend;
+			ins->mute = (xi_h.mute == 1) ? true : false; // correct logic! Don't mess with this
+			ins->numSamples = xi_h.numSamples; // used in loadInstrSample()
 
-			// sanitize stuff for broken/unsupported instruments
-			ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
-			ins->midiBend = CLAMP(ins->midiBend, 0, 36);
-
-			if (ins->midiChannel > 15) ins->midiChannel = 15;
-			if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
-			if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
-			if (ins->vibTyp > 3) ins->vibTyp = 0;
-
-			for (i = 0; i < 96; i++)
-			{
-				if (ins->ta[i] >= MAX_SMP_PER_INST)
-					ins->ta[i] = MAX_SMP_PER_INST-1;
-			}
-
-			if (ins->envVPAnt > 12) ins->envVPAnt = 12;
-			if (ins->envVRepS > 11) ins->envVRepS = 11;
-			if (ins->envVRepE > 11) ins->envVRepE = 11;
-			if (ins->envVSust > 11) ins->envVSust = 11;
-			if (ins->envPPAnt > 12) ins->envPPAnt = 12;
-			if (ins->envPRepS > 11) ins->envPRepS = 11;
-			if (ins->envPRepE > 11) ins->envPRepE = 11;
-			if (ins->envPSust > 11) ins->envPSust = 11;
-
-			for (i = 0; i < 12; i++)
-			{
-				if ((uint16_t)ins->envVP[i][0] > 32767) ins->envVP[i][0] = 32767;
-				if ((uint16_t)ins->envPP[i][0] > 32767) ins->envPP[i][0] = 32767;
-				if ((uint16_t)ins->envVP[i][1] > 64) ins->envVP[i][1] = 64;
-				if ((uint16_t)ins->envPP[i][1] > 63) ins->envPP[i][1] = 63;
-				
-			}
-
-			int32_t sampleHeadersToRead = ih.antSamp;
+			int32_t sampleHeadersToRead = xi_h.numSamples;
 			if (sampleHeadersToRead > MAX_SMP_PER_INST)
 				sampleHeadersToRead = MAX_SMP_PER_INST;
 
-			if (fread(ih.samp, sampleHeadersToRead * sizeof (sampleHeaderTyp), 1, f) != 1)
+			if (fread(xi_h.smp, sampleHeadersToRead * sizeof (xmSmpHdr_t), 1, f) != 1)
 			{
 				freeInstr(editor.curInstr);
 				resumeAudio();
@@ -3159,63 +3188,63 @@
 				goto loadDone;
 			}
 
-			if (ih.antSamp > MAX_SMP_PER_INST)
+			if (xi_h.numSamples > MAX_SMP_PER_INST)
 			{
-				const int32_t sampleHeadersToSkip = ih.antSamp - MAX_SMP_PER_INST;
-				fseek(f, sampleHeadersToSkip * sizeof (sampleHeaderTyp), SEEK_CUR);
+				const int32_t sampleHeadersToSkip = xi_h.numSamples - MAX_SMP_PER_INST;
+				fseek(f, sampleHeadersToSkip * sizeof (xmSmpHdr_t), SEEK_CUR);
 			}
 
 			for (i = 0; i < sampleHeadersToRead; i++)
 			{
-				s = &instr[editor.curInstr]->samp[i];
-				src = &ih.samp[i];
+				s = &instr[editor.curInstr]->smp[i];
+				src = &xi_h.smp[i];
 
 				// copy sample header elements to our sample struct
 
-				s->len = src->len;
-				s->repS = src->repS;
-				s->repL = src->repL;
-				s->vol = src->vol;
-				s->fine = src->fine;
-				s->typ = src->typ;
-				s->pan = src->pan;
-				s->relTon = src->relTon;
+				s->length = src->length;
+				s->loopStart = src->loopStart;
+				s->loopLength = src->loopLength;
+				s->volume = src->volume;
+				s->finetune = src->finetune;
+				s->flags = src->flags;
+				s->panning = src->panning;
+				s->relativeNote = src->relativeNote;
 				memcpy(s->name, src->name, 22);
 				s->name[22] = '\0';
 
-				// dst->pek is set up later
-
-				// trim off spaces at end of name
-				for (j = 21; j >= 0; j--)
-				{
-					if (s->name[j] == ' ' || s->name[j] == 0x1A)
-						s->name[j] = '\0';
-					else
-						break;
-				}
-
-				// sanitize stuff broken/unsupported samples
-				if (s->vol > 64)
-					s->vol = 64;
-
-				s->relTon = CLAMP(s->relTon, -48, 71);
+				// dst->dataPtr is set up later
 			}
 		}
 
-		for (i = 0; i < ih.antSamp; i++)
+		for (i = 0; i < xi_h.numSamples; i++)
 		{
-			s = &instr[editor.curInstr]->samp[i];
+			s = &instr[editor.curInstr]->smp[i];
 
-			// if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 mixer behavior)
-			if ((s->typ & 3) == 3)
-				s->typ &= 0xFE;
-
-			if (s->len > 0)
+			if (s->length <= 0)
 			{
-				s->pek = NULL;
-				s->origPek = (int8_t *)malloc(s->len + LOOP_FIX_LEN);
-				if (s->origPek == NULL)
+				s->length = 0;
+				s->loopStart = 0;
+				s->loopLength = 0;
+				s->flags = 0;
+			}
+			else
+			{
+				const int32_t lengthInFile = s->length;
+				bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+				bool stereoSample = !!(s->flags & SAMPLE_STEREO);
+
+				if (sample16Bit) // we use units of samples (not bytes like in FT2)
 				{
+					s->length >>= 1;
+					s->loopStart >>= 1;
+					s->loopLength >>= 1;
+				}
+
+				if (s->length > MAX_SAMPLE_LEN)
+					s->length = MAX_SAMPLE_LEN;
+
+				if (!allocateSmpData(s, s->length, sample16Bit))
+				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
 					okBoxThreadSafe(0, "System message", "Not enough memory!");
@@ -3222,9 +3251,8 @@
 					goto loadDone;
 				}
 
-				s->pek = s->origPek + SMP_DAT_OFFSET;
-
-				if (fread(s->pek, s->len, 1, f) != 1)
+				const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s);
+				if (fread(s->dataPtr, sampleLengthInBytes, 1, f) != 1)
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
@@ -3232,29 +3260,23 @@
 					goto loadDone;
 				}
 
-				delta2Samp(s->pek, s->len, s->typ); // stereo samples are handled here
+				if (sampleLengthInBytes < lengthInFile)
+					fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR);
 
+				delta2Samp(s->dataPtr, s->length, s->flags); // stereo samples are handled here
+
 				// check if it was a stereo sample
-				if (s->typ & 32)
+				if (stereoSample)
 				{
-					s->typ &= ~32;
+					s->flags &= ~SAMPLE_STEREO;
 
-					s->len >>= 1;
-					s->repL >>= 1;
-					s->repS >>= 1;
+					s->length >>= 1;
+					s->loopStart >>= 1;
+					s->loopLength >>= 1;
 
-					newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
-					if (newPtr != NULL)
-					{
-						s->origPek = newPtr;
-						s->pek = s->origPek + SMP_DAT_OFFSET;
-					}
-
+					reallocateSmpData(s, s->length, sample16Bit);
 					stereoWarning = true;
 				}
-
-				checkSampleRepeat(s);
-				fixSample(s);
 			}
 		}
 
@@ -3264,20 +3286,22 @@
 	{
 		rewind(f);
 
-		fread(&ih_PAT, 1, sizeof (instrPATHeaderTyp), f);
-		if (!memcmp(ih_PAT.id, "GF1PATCH110\0ID#000002\0", 22))
+		fread(&pat_h, 1, sizeof (patHdr_t), f);
+		if (!memcmp(pat_h.ID, "GF1PATCH110\0ID#000002\0", 22))
 		{
-			// PAT - Gravis Ultrasound GF1 patch
+			// PAT - Gravis Ultrasound patch
 
-			if (ih_PAT.antSamp == 0)
-				ih_PAT.antSamp = 1; // to some patch makers, 0 means 1
+			if (pat_h.numSamples == 0)
+				pat_h.numSamples = 1; // to some patch makers, 0 means 1
 
-			if (ih_PAT.layers > 1 || ih_PAT.antSamp > MAX_SMP_PER_INST)
+			if (pat_h.layers > 1 || pat_h.numSamples > MAX_SMP_PER_INST)
 			{
 				okBoxThreadSafe(0, "System message", "Incompatible instrument!");
 				goto loadDone;
 			}
 
+			numLoadedSamples = pat_h.numSamples;
+
 			pauseAudio();
 			freeInstr(editor.curInstr);
 
@@ -3287,15 +3311,15 @@
 				goto loadDone;
 			}
 
-			memset(song.instrName[editor.curInstr], 0, 22 + 1);
-			memcpy(song.instrName[editor.curInstr], ih_PAT.instrName, 16);
+			memcpy(song.instrName[editor.curInstr], pat_h.instrName, 16);
+			song.instrName[editor.curInstr][16] = '\0';
 
-			for (i = 0; i < ih_PAT.antSamp; i++)
+			for (i = 0; i < pat_h.numSamples; i++)
 			{
-				s = &instr[editor.curInstr]->samp[i];
+				s = &instr[editor.curInstr]->smp[i];
 				ins = instr[editor.curInstr];
 
-				if (fread(&ih_PATWave, 1, sizeof (ih_PATWave), f) != sizeof (ih_PATWave))
+				if (fread(&patWave_h, 1, sizeof (patWave_h), f) != sizeof (patWave_h))
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
@@ -3303,10 +3327,22 @@
 					goto loadDone;
 				}
 
-				s->pek = NULL;
-				s->origPek = (int8_t *)malloc(ih_PATWave.waveSize + LOOP_FIX_LEN);
-				if (s->origPek == NULL)
+				const int32_t lengthInFile = patWave_h.sampleLength;
+
+				bool sample16Bit = !!(patWave_h.flags & 1);
+
+				s->length = lengthInFile;
+				if (sample16Bit)
 				{
+					s->flags |= SAMPLE_16BIT;
+					s->length >>= 1;
+				}
+
+				if (s->length > MAX_SAMPLE_LEN)
+					s->length = MAX_SAMPLE_LEN;
+
+				if (!allocateSmpData(s, s->length, sample16Bit))
+				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
 					okBoxThreadSafe(0, "System message", "Not enough memory!");
@@ -3313,75 +3349,54 @@
 					goto loadDone;
 				}
 
-				s->pek = s->origPek + SMP_DAT_OFFSET;
-
 				if (i == 0)
 				{
-					ins->vibSweep = ih_PATWave.vibSweep;
-
-					ins->vibRate = (ih_PATWave.vibRate + 2) >> 2;
-					if (ins->vibRate > 0x3F)
-						ins->vibRate = 0x3F;
-
-					ins->vibDepth = (ih_PATWave.vibDepth + 1) >> 1;
-					if (ins->vibDepth > 0x0F)
-						ins->vibDepth = 0x0F;
+					ins->vibSweep = patWave_h.vibSweep;
+					ins->vibRate = (patWave_h.vibRate + 2) >> 2; // rounded
+					ins->vibDepth = (patWave_h.vibDepth + 1) >> 1; // rounded
 				}
 
-				s = &instr[editor.curInstr]->samp[i];
+				s = &instr[editor.curInstr]->smp[i];
 
-				memcpy(s->name, ih_PATWave.name, 7);
+				memcpy(s->name, patWave_h.name, 7);
+				s->name[7] = '\0';
 
-				s->typ = (ih_PATWave.mode & 1) << 4; // 16-bit flag
-				if (ih_PATWave.mode & 4) // loop enabled?
+				if (patWave_h.flags & 4) // loop enabled?
 				{
-					if (ih_PATWave.mode & 8)
-						s->typ |= 2; // pingpong loop
+					if (patWave_h.flags & 8)
+						s->flags |= LOOP_BIDI;
 					else
-						s->typ |= 1; // forward loop
+						s->flags |= LOOP_FWD;
 				}
 
-				s->pan = ((ih_PATWave.pan << 4) & 0xF0) | (ih_PATWave.pan & 0xF);
+				s->panning = ((patWave_h.panning << 4) & 0xF0) | (patWave_h.panning & 0xF);
+				s->loopStart = patWave_h.loopStart;
+				s->loopLength = patWave_h.loopEnd - patWave_h.loopStart;
 
-				if (s->typ & 16)
+				if (sample16Bit)
 				{
-					ih_PATWave.waveSize &= 0xFFFFFFFE;
-					ih_PATWave.repS &= 0xFFFFFFFE;
-					ih_PATWave.repE &= 0xFFFFFFFE;
+					s->loopStart >>= 1;
+					s->loopLength >>= 1;
 				}
 
-				s->len = ih_PATWave.waveSize;
-				if (s->len > MAX_SAMPLE_LEN)
-					s->len = MAX_SAMPLE_LEN;
+				const double dFreq = (1.0 + (patWave_h.finetune / 512.0)) * patWave_h.sampleRate;
+				tuneSample(s, (int32_t)(dFreq + 0.5), audio.linearPeriodsFlag);
 
-				s->repS = ih_PATWave.repS;
-				if (s->repS > s->len)
-					s->repS = 0;
+				a = getPATNote(patWave_h.rootFrq) - (12 * 3);
+				s->relativeNote -= (uint8_t)a;
 
-				s->repL = ih_PATWave.repE - ih_PATWave.repS;
-				if (s->repL < 0)
-					s->repL = 0;
+				a = getPATNote(patWave_h.lowFrq);
+				b = getPATNote(patWave_h.highFreq);
 
-				if (s->repS+s->repL > s->len)
-					s->repL = s->len - s->repS;
-
-				const double dFreq = (1.0 + (ih_PATWave.fineTune / 512.0)) * ih_PATWave.sampleRate;
-				int32_t freq = (const int32_t)(dFreq + 0.5);
-				tuneSample(s, freq, audio.linearPeriodsFlag);
-
-				a = s->relTon - (getPATNote(ih_PATWave.rootFrq) - (12 * 3));
-				s->relTon = (uint8_t)CLAMP(a, -48, 71);
-
-				a = getPATNote(ih_PATWave.lowFrq);
-				b = getPATNote(ih_PATWave.highFreq);
-
 				a = CLAMP(a, 0, 95);
 				b = CLAMP(b, 0, 95);
 
 				for (j = a; j <= b; j++)
-					ins->ta[j] = (uint8_t)i;
+					ins->note2SampleLUT[j] = (uint8_t)i;
 
-				if (fread(s->pek, ih_PATWave.waveSize, 1, f) != 1)
+				const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s);
+				size_t bytesRead = fread(s->dataPtr, sampleLengthInBytes, 1, f);
+				if (bytesRead != 1)
 				{
 					freeInstr(editor.curInstr);
 					resumeAudio();
@@ -3389,15 +3404,16 @@
 					goto loadDone;
 				}
 
-				if (ih_PATWave.mode & 2)
+				if (sampleLengthInBytes < lengthInFile)
+					fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR);
+
+				if (patWave_h.flags & 2) // unsigned samples?
 				{
-					if (s->typ & 16)
-						conv16BitSample(s->pek, s->len, false);
+					if (sample16Bit)
+						conv16BitSample(s->dataPtr, s->length, false);
 					else
-						conv8BitSample(s->pek, s->len, false);
+						conv8BitSample(s->dataPtr, s->length, false);
 				}
-
-				fixSample(s);
 			}
 
 			resumeAudio();
@@ -3407,10 +3423,26 @@
 loadDone:
 	fclose(f);
 
-	fixInstrAndSampleNames(editor.curInstr);
+	numLoadedSamples = CLAMP(numLoadedSamples, 1, MAX_SMP_PER_INST);
+
+	ins = instr[editor.curInstr];
+	if (ins != NULL)
+	{
+		sanitizeInstrument(ins);
+		for (i = 0; i < numLoadedSamples; i++)
+		{
+			s = &ins->smp[i];
+			sanitizeSample(s);
+
+			if (s->dataPtr != NULL)
+				fixSample(s);
+		}
+
+		fixInstrAndSampleNames(editor.curInstr);
+	}
 	editor.updateCurInstr = true; // setMouseBusy(false) is called in the input/video thread when done
 
-	if (ih.antSamp > MAX_SMP_PER_INST)
+	if (numLoadedSamples > MAX_SMP_PER_INST)
 		okBoxThreadSafe(0, "System message", "Warning: The instrument contained >16 samples. The extra samples were discarded!");
 
 	if (stereoWarning)
@@ -3417,7 +3449,6 @@
 		okBoxThreadSafe(0, "System message", "Warning: The instrument contained stereo sample(s). They were mixed to mono!");
 
 	return true;
-
 	(void)ptr;
 }
 
--- a/src/ft2_inst_ed.h
+++ b/src/ft2_inst_ed.h
@@ -7,10 +7,9 @@
 #include "ft2_unicode.h"
 
 void drawC4Rate(void);
-
+void sanitizeInstrument(instr_t *ins);
 bool fileIsInstr(UNICHAR *filenameU);
-
-void saveInstr(UNICHAR *filenameU, int16_t nr);
+void saveInstr(UNICHAR *filenameU, int16_t insNum);
 void loadInstr(UNICHAR *filenameU);
 void copyInstr(void); // dstInstr = srcInstr
 void xchgInstr(void); // dstInstr <-> srcInstr
@@ -43,10 +42,10 @@
 void panPreDef4(void);
 void panPreDef5(void);
 void panPreDef6(void);
-void relToneOctUp(void);
-void relToneOctDown(void);
-void relToneUp(void);
-void relToneDown(void);
+void relativeNoteOctUp(void);
+void relativeNoteOctDown(void);
+void relativeNoteUp(void);
+void relativeNoteDown(void);
 void volEnvAdd(void);
 void volEnvDel(void);
 void volEnvSusUp(void);
--- a/src/ft2_keyboard.c
+++ b/src/ft2_keyboard.c
@@ -4,6 +4,8 @@
 #endif
 
 #include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
 #include "ft2_header.h"
 #include "ft2_keyboard.h"
 #include "ft2_gui.h"
@@ -43,7 +45,7 @@
 int8_t scancodeKeyToNote(SDL_Scancode scancode)
 {
 	if (scancode == SDL_SCANCODE_CAPSLOCK || scancode == SDL_SCANCODE_NONUSBACKSLASH)
-		return 97; // key off
+		return NOTE_OFF;
 
 	// translate key to note
 	int8_t note = 0;
@@ -161,7 +163,7 @@
 static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey)
 {
 	// if we're holding numpad plus but not pressing bank keys, don't check any other key
-	if (keyb.numPadPlusPressed)
+	if (keyb.numPadPlusPressed && !keyb.leftCtrlPressed)
 	{
 		if (scanKey != SDL_SCANCODE_NUMLOCKCLEAR && scanKey != SDL_SCANCODE_KP_DIVIDE &&
 			scanKey != SDL_SCANCODE_KP_MULTIPLY  && scanKey != SDL_SCANCODE_KP_MINUS)
@@ -234,23 +236,53 @@
 
 		case SDL_SCANCODE_KP_MINUS:
 		{
-			if (keyb.numPadPlusPressed)
+			if (keyb.leftCtrlPressed) // non-FT2 feature: Decrease master volume by 16
 			{
-				if (editor.instrBankSwapped)
-					pbSetInstrBank16();
+				if (config.masterVol >= 16)
+					config.masterVol -= 16;
 				else
-					pbSetInstrBank8();
+					config.masterVol = 0;
+
+				setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
+				if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES)
+					showConfigScreen();
 			}
 			else
 			{
-				if (editor.instrBankSwapped)
-					pbSetInstrBank12();
+				if (keyb.numPadPlusPressed)
+				{
+					if (editor.instrBankSwapped)
+						pbSetInstrBank16();
+					else
+						pbSetInstrBank8();
+				}
 				else
-					pbSetInstrBank4();
+				{
+					if (editor.instrBankSwapped)
+						pbSetInstrBank12();
+					else
+						pbSetInstrBank4();
+				}
 			}
 		}
 		return;
 
+		case SDL_SCANCODE_KP_PLUS:
+		{
+			if (keyb.leftCtrlPressed) // non-FT2 feature: Increase master volume by 16
+			{
+				if (config.masterVol <= 256-16)
+					config.masterVol += 16;
+				else
+					config.masterVol = 256;
+
+				setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
+				if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES)
+					showConfigScreen();
+			}
+		}
+		return;
+
 		case SDL_SCANCODE_KP_PERIOD:
 		{
 			if (editor.curInstr > 0)
@@ -267,6 +299,7 @@
 					if (okBox(1, "System request", "Clear instrument?") == 1)
 					{
 						freeInstr(editor.curInstr);
+						memset(song.instrName[editor.curInstr], 0, sizeof(song.instrName[editor.curInstr]));
 						updateNewInstrument();
 						setSongModifiedFlag();
 					}
@@ -290,18 +323,18 @@
 			if (keyb.leftShiftPressed)
 			{
 				// decrease edit skip
-				if (editor.ID_Add == 0)
-					editor.ID_Add = 16;
+				if (editor.editRowSkip == 0)
+					editor.editRowSkip = 16;
 				else
-					editor.ID_Add--;
+					editor.editRowSkip--;
 			}
 			else
 			{
 				// increase edit skip
-				if (editor.ID_Add == 16)
-					editor.ID_Add = 0;
+				if (editor.editRowSkip == 16)
+					editor.editRowSkip = 0;
 				else
-					editor.ID_Add++;
+					editor.editRowSkip++;
 			}
 
 			if (!ui.nibblesShown     && !ui.configScreenShown &&
@@ -459,13 +492,13 @@
 		{
 			lockAudio();
 
-			song.pattPos = editor.ptnJumpPos[0];
-			if (song.pattPos >= song.pattLen)
-				song.pattPos = song.pattLen - 1;
+			song.row = editor.ptnJumpPos[0];
+			if (song.row >= song.currNumRows)
+				song.row = song.currNumRows - 1;
 
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -477,13 +510,13 @@
 		{
 			lockAudio();
 
-			song.pattPos = editor.ptnJumpPos[1];
-			if (song.pattPos >= song.pattLen)
-				song.pattPos = song.pattLen - 1;
+			song.row = editor.ptnJumpPos[1];
+			if (song.row >= song.currNumRows)
+				song.row = song.currNumRows - 1;
 
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -495,13 +528,13 @@
 		{
 			lockAudio();
 
-			song.pattPos = editor.ptnJumpPos[2];
-			if (song.pattPos >= song.pattLen)
-				song.pattPos  = song.pattLen - 1;
+			song.row = editor.ptnJumpPos[2];
+			if (song.row >= song.currNumRows)
+				song.row  = song.currNumRows - 1;
 
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -513,13 +546,13 @@
 		{
 			lockAudio();
 
-			song.pattPos = editor.ptnJumpPos[3];
-			if (song.pattPos >= song.pattLen)
-				song.pattPos = song.pattLen - 1;
+			song.row = editor.ptnJumpPos[3];
+			if (song.row >= song.currNumRows)
+				song.row = song.currNumRows - 1;
 
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -585,14 +618,14 @@
 			if (audioWasntLocked)
 				lockAudio();
 
-			if (song.pattPos >= 16)
-				song.pattPos -= 16;
+			if (song.row >= 16)
+				song.row -= 16;
 			else
-				song.pattPos = 0;
+				song.row = 0;
 
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -607,14 +640,14 @@
 			if (audioWasntLocked)
 				lockAudio();
 
-			if (song.pattPos < (song.pattLen - 16))
-				song.pattPos += 16;
+			if (song.row < song.currNumRows-16)
+				song.row += 16;
 			else
-				song.pattPos = song.pattLen - 1;
+				song.row = song.currNumRows-1;
 
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -629,10 +662,10 @@
 			if (audioWasntLocked)
 				lockAudio();
 
-			song.pattPos = 0;
+			song.row = 0;
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -647,10 +680,10 @@
 			if (audioWasntLocked)
 				lockAudio();
 
-			song.pattPos = pattLens[song.pattNr] - 1;
+			song.row = patternNumRows[song.pattNum] - 1;
 			if (!songPlaying)
 			{
-				editor.pattPos = (uint8_t)song.pattPos;
+				editor.row = (uint8_t)song.row;
 				ui.updatePatternEditor = true;
 			}
 
@@ -693,7 +726,7 @@
 			}
 			else if (keyb.leftShiftPressed)
 			{
-				editor.ptnJumpPos[0] = (uint8_t)editor.pattPos;
+				editor.ptnJumpPos[0] = (uint8_t)editor.row;
 				return true;
 			}
 		}
@@ -708,7 +741,7 @@
 			}
 			else if (keyb.leftShiftPressed)
 			{
-				editor.ptnJumpPos[1] = (uint8_t)editor.pattPos;
+				editor.ptnJumpPos[1] = (uint8_t)editor.row;
 				return true;
 			}
 		}
@@ -723,7 +756,7 @@
 			}
 			else if (keyb.leftShiftPressed)
 			{
-				editor.ptnJumpPos[2] = (uint8_t)editor.pattPos;
+				editor.ptnJumpPos[2] = (uint8_t)editor.row;
 				return true;
 			}
 		}
@@ -738,7 +771,7 @@
 			}
 			else if (keyb.leftShiftPressed)
 			{
-				editor.ptnJumpPos[3] = (uint8_t)editor.pattPos;
+				editor.ptnJumpPos[3] = (uint8_t)editor.row;
 				return true;
 			}
 		}
@@ -793,7 +826,7 @@
 					pattMark.markX1 = cursor.ch;
 					pattMark.markX2 = pattMark.markX1;
 					pattMark.markY1 = 0;
-					pattMark.markY2 = pattLens[editor.editPattern];
+					pattMark.markY2 = patternNumRows[editor.editPattern];
 
 					ui.updatePatternEditor = true;
 				}
--- a/src/ft2_main.c
+++ b/src/ft2_main.c
@@ -22,7 +22,7 @@
 #include "ft2_config.h"
 #include "ft2_sample_ed.h"
 #include "ft2_diskop.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_about.h"
 #include "ft2_pattern_ed.h"
 #include "ft2_module_loader.h"
@@ -180,20 +180,23 @@
 
 	setupPerfFreq();
 
-	if (!setupAudio(CONFIG_HIDE_ERRORS))
+	if (!setupAudio(CONFIG_HIDE_ERRORS)) // can we open the audio device?
 	{
-		// one LAST attempt (with default audio device and settings)
-		config.audioFreq = 48000;
-
-		// try 48kHz 16-bit audio at 1024 samples
-		config.specialFlags &= ~(BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_2048);
-		config.specialFlags |=  (BITDEPTH_16 + BUFFSIZE_1024);
-
+		// nope, try with the default audio device
 		setToDefaultAudioOutputDevice();
-		if (!setupAudio(CONFIG_SHOW_ERRORS))
+
+		if (!setupAudio(CONFIG_HIDE_ERRORS)) // does it work this time?
 		{
-			cleanUpAndExit(); // still no go...
-			return 1;
+			// nope, try safe values (44.1kHz 16-bit @ 1024 samples)
+			config.audioFreq = 44100;
+			config.specialFlags &= ~(BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_2048);
+			config.specialFlags |=  (BITDEPTH_16 + BUFFSIZE_1024);
+
+			if (!setupAudio(CONFIG_SHOW_ERRORS)) // this time it surely must work?!
+			{
+				cleanUpAndExit(); // well, nope!
+				return 1;
+			}
 		}
 	}
 
@@ -269,10 +272,10 @@
 	memset(&song, 0, sizeof (song));
 
 	// used for scopes and sampling position line (sampler screen)
-	for (int32_t i = 0; i < MAX_VOICES; i++)
+	for (int32_t i = 0; i < MAX_CHANNELS; i++)
 	{
-		lastChInstr[i].instrNr = 255;
-		lastChInstr[i].sampleNr = 255;
+		lastChInstr[i].instrNum = 255;
+		lastChInstr[i].smpNum = 255;
 	}
 
 	// now set data that must be initialized to non-zero values...
@@ -289,7 +292,7 @@
 
 	mouse.lastUsedObjectID = OBJECT_ID_NONE;
 
-	editor.ID_Add = 1;
+	editor.editRowSkip = 1;
 	editor.srcInstr = 1;
 	editor.curInstr = 1;
 	editor.curOctave = 4;
--- a/src/ft2_midi.c
+++ b/src/ft2_midi.c
@@ -68,10 +68,10 @@
 
 	if (recMIDIValidChn) // real FT2 forgot to check this here..
 	{
-		for (uint8_t i = 0; i < song.antChn; i++)
+		for (uint8_t i = 0; i < song.numChannels; i++)
 		{
-			if (stm[i].midiVibDepth != 0 || editor.keyOnTab[i] != 0)
-				stm[i].midiVibDepth = midi.currMIDIVibDepth;
+			if (channel[i].midiVibDepth != 0 || editor.keyOnTab[i] != 0)
+				channel[i].midiVibDepth = midi.currMIDIVibDepth;
 		}
 	}
 
@@ -88,8 +88,8 @@
 	midi.currMIDIPitch = pitch;
 	if (recMIDIValidChn)
 	{
-		stmTyp *ch = stm;
-		for (uint8_t i = 0; i < song.antChn; i++, ch++)
+		channel_t *ch = channel;
+		for (uint8_t i = 0; i < song.numChannels; i++, ch++)
 		{
 			if (ch->midiPitch != 0 || editor.keyOnTab[i] != 0)
 				ch->midiPitch = midi.currMIDIPitch;
@@ -214,27 +214,26 @@
 	return true;
 }
 
-void recordMIDIEffect(uint8_t effTyp, uint8_t effData)
+void recordMIDIEffect(uint8_t efx, uint8_t efxData)
 {
 	// only handle this in record mode
 	if (!midi.enable || (playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT))
 		return;
 
-	const int16_t nr = editor.editPattern;
 	if (config.multiRec)
 	{
-		tonTyp *note = &patt[nr][editor.pattPos * MAX_VOICES];
-		for (int32_t i = 0; i < song.antChn; i++, note++)
+		note_t *p = &pattern[editor.editPattern][editor.row * MAX_CHANNELS];
+		for (int32_t i = 0; i < song.numChannels; i++, p++)
 		{
 			if (config.multiRecChn[i] && editor.chnMode[i])
 			{
-				if (!allocatePattern(nr))
+				if (!allocatePattern(editor.editPattern))
 					return;
 
-				if (note->effTyp == 0)
+				if (p->efx == 0)
 				{
-					note->effTyp = effTyp;
-					note->eff = effData;
+					p->efx = efx;
+					p->efxData = efxData;
 					setSongModifiedFlag();
 				}
 			}
@@ -242,15 +241,15 @@
 	}
 	else
 	{
-		if (!allocatePattern(nr))
+		if (!allocatePattern(editor.editPattern))
 			return;
 
-		tonTyp *note = &patt[nr][(editor.pattPos * MAX_VOICES) + cursor.ch];
-		if (note->effTyp != effTyp || note->eff != effData)
+		note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch];
+		if (p->efx != efx || p->efxData != efxData)
 			setSongModifiedFlag();
 
-		note->effTyp = effTyp;
-		note->eff = effData;
+		p->efx = efx;
+		p->efxData = efxData;
 	}
 }
 
--- a/src/ft2_midi.h
+++ b/src/ft2_midi.h
@@ -25,7 +25,7 @@
 void freeMidiIn(void);
 bool initMidiIn(void);
 bool openMidiInDevice(uint32_t deviceID);
-void recordMIDIEffect(uint8_t effTyp, uint8_t effData);
+void recordMIDIEffect(uint8_t efx, uint8_t efxData);
 bool saveMidiInputDeviceToConfig(void);
 bool setMidiInputDeviceFromConfig(void);
 void freeMidiInputDeviceList(void);
--- a/src/ft2_module_loader.c
+++ b/src/ft2_module_loader.c
@@ -10,7 +10,7 @@
 #include <unistd.h>
 #endif
 #include "ft2_header.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_trim.h"
 #include "ft2_inst_ed.h"
 #include "ft2_sample_ed.h"
@@ -56,10 +56,10 @@
 
 // globals for module loaders
 volatile bool tmpLinearPeriodsFlag;
-int16_t pattLensTmp[MAX_PATTERNS];
-tonTyp *pattTmp[MAX_PATTERNS];
-instrTyp *instrTmp[1+256];
-songTyp songTmp;
+int16_t patternNumRowsTmp[MAX_PATTERNS];
+note_t *patternTmp[MAX_PATTERNS];
+instr_t *instrTmp[1+256];
+song_t songTmp;
 // --------------------------
 
 static volatile bool musicIsLoading, moduleLoaded, moduleFailedToLoad;
@@ -214,12 +214,12 @@
 
 static void clearTmpModule(void)
 {
-	memset(pattTmp, 0, sizeof (pattTmp));
+	memset(patternTmp, 0, sizeof (patternTmp));
 	memset(instrTmp, 0, sizeof (instrTmp));
 	memset(&songTmp, 0, sizeof (songTmp));
 
 	for (uint32_t i = 0; i < MAX_PATTERNS; i++)
-		pattLensTmp[i] = 64;
+		patternNumRowsTmp[i] = 64;
 }
 
 static int32_t SDLCALL loadMusicThread(void *ptr)
@@ -277,33 +277,33 @@
 	return false;
 }
 
-bool allocateTmpPatt(int32_t nr, uint16_t rows)
+bool allocateTmpPatt(int32_t pattNum, uint16_t numRows)
 {
-	pattTmp[nr] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
-	if (pattTmp[nr] == NULL)
+	patternTmp[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
+	if (patternTmp[pattNum] == NULL)
 		return false;
 
-	pattLensTmp[nr] = rows;
+	patternNumRowsTmp[pattNum] = numRows;
 	return true;
 }
 
-bool allocateTmpInstr(int16_t nr)
+bool allocateTmpInstr(int16_t insNum)
 {
-	if (instrTmp[nr] != NULL)
+	if (instrTmp[insNum] != NULL)
 		return false; // already allocated
 
-	instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp));
-	if (p == NULL)
+	instr_t *ins = (instr_t *)calloc(1, sizeof (instr_t));
+	if (ins == NULL)
 		return false;
 
-	sampleTyp *s = p->samp;
+	sample_t *s = ins->smp;
 	for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++)
 	{
-		s->pan = 128;
-		s->vol = 64;
+		s->panning = 128;
+		s->volume = 64;
 	}
 
-	instrTmp[nr] = p;
+	instrTmp[insNum] = ins;
 	return true;
 }
 
@@ -312,10 +312,10 @@
 	// free all patterns
 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
 	{
-		if (pattTmp[i] != NULL)
+		if (patternTmp[i] != NULL)
 		{
-			free(pattTmp[i]);
-			pattTmp[i] = NULL;
+			free(patternTmp[i]);
+			patternTmp[i] = NULL;
 		}
 	}
 
@@ -325,12 +325,9 @@
 		if (instrTmp[i] == NULL)
 			continue;
 
-		sampleTyp *s = instrTmp[i]->samp;
+		sample_t *s = instrTmp[i]->smp;
 		for (int32_t j = 0; j < MAX_SMP_PER_INST; j++, s++)
-		{
-			if (s->origPek != NULL)
-				free(s->origPek);
-		}
+			freeSmpData(s);
 
 		free(instrTmp[i]);
 		instrTmp[i] = NULL;
@@ -337,13 +334,13 @@
 	}
 }
 
-bool tmpPatternEmpty(uint16_t nr)
+bool tmpPatternEmpty(uint16_t pattNum)
 {
-	if (pattTmp[nr] == NULL)
+	if (patternTmp[pattNum] == NULL)
 		return true;
 
-	uint8_t *scanPtr = (uint8_t *)pattTmp[nr];
-	const uint32_t scanLen = pattLensTmp[nr] * TRACK_WIDTH;
+	uint8_t *scanPtr = (uint8_t *)patternTmp[pattNum];
+	const uint32_t scanLen = patternNumRowsTmp[pattNum] * TRACK_WIDTH;
 
 	for (uint32_t i = 0; i < scanLen; i++)
 	{
@@ -354,16 +351,16 @@
 	return true;
 }
 
-void clearUnusedChannels(tonTyp *p, int16_t pattLen, int32_t antChn)
+void clearUnusedChannels(note_t *pattPtr, int16_t numRows, int32_t numChannels)
 {
-	if (p == NULL || antChn >= MAX_VOICES)
+	if (pattPtr == NULL || numChannels >= MAX_CHANNELS)
 		return;
 
-	const int32_t width = sizeof (tonTyp) * (MAX_VOICES - antChn);
+	const int32_t width = sizeof (note_t) * (MAX_CHANNELS - numChannels);
 
-	tonTyp *ptr = &p[antChn];
-	for (int32_t i = 0; i < pattLen; i++, ptr += MAX_VOICES)
-		memset(ptr, 0, width);
+	note_t *p = &pattPtr[numChannels];
+	for (int32_t i = 0; i < numRows; i++, p += MAX_CHANNELS)
+		memset(p, 0, width);
 }
 
 // called from input/video thread after the module was done loading
@@ -388,12 +385,12 @@
 	// copy over new pattern pointers and lengths
 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
 	{
-		patt[i] = pattTmp[i];
-		pattLens[i] = pattLensTmp[i];
+		pattern[i] = patternTmp[i];
+		patternNumRows[i] = patternNumRowsTmp[i];
 	}
 
 	// copy over song struct
-	memcpy(&song, &songTmp, sizeof (songTyp));
+	memcpy(&song, &songTmp, sizeof (song_t));
 	fixSongName();
 
 	// copy over new instruments (includes sample pointers)
@@ -404,12 +401,13 @@
 
 		if (instr[i] != NULL)
 		{
+			sanitizeInstrument(instr[i]);
 			for (int32_t j = 0; j < MAX_SMP_PER_INST; j++)
 			{
-				sampleTyp *s = &instr[i]->samp[j];
+				sample_t *s = &instr[i]->smp[j];
 
-				checkSampleRepeat(s);
-				if (s->pek != NULL)
+				sanitizeSample(s);
+				if (s->dataPtr != NULL)
 					fixSample(s); // prepare sample for branchless linear interpolation
 			}
 		}
@@ -417,69 +415,67 @@
 
 	// we are the owners of the allocated memory ptrs set by the loader thread now
 
-	if (song.antChn == 0)
-		song.antChn = 2;
-
 	// support non-even channel numbers
-	if (song.antChn & 1)
+	if (song.numChannels & 1)
 	{
-		song.antChn++;
-		if (song.antChn > MAX_VOICES)
-			song.antChn = MAX_VOICES;
+		song.numChannels++;
+		if (song.numChannels > MAX_CHANNELS)
+			song.numChannels = MAX_CHANNELS;
 	}
 
-	if (song.len == 0)
-		song.len = 1;
+	song.numChannels = CLAMP(song.numChannels, 2, MAX_CHANNELS);
+	song.songLength = CLAMP(song.songLength, 1, MAX_ORDERS);
+	song.BPM = CLAMP(song.BPM, MIN_BPM, MAX_BPM);
+	song.initialSpeed = song.speed = CLAMP(song.speed, 1, MAX_SPEED);
 
-	if (song.repS >= song.len)
-		song.repS = 0;
+	if (song.songLoopStart >= song.songLength)
+		song.songLoopStart = 0;
 
-	song.globVol = 64;
+	song.globalVolume = 64;
 
 	// remove overflown stuff in pattern data (FT2 doesn't do this)
 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
 	{
-		if (pattLens[i] <= 0)
-			pattLens[i] = 64;
+		if (patternNumRows[i] <= 0)
+			patternNumRows[i] = 64;
 
-		if (pattLens[i] > MAX_PATT_LEN)
-			pattLens[i] = MAX_PATT_LEN;
+		if (patternNumRows[i] > MAX_PATT_LEN)
+			patternNumRows[i] = MAX_PATT_LEN;
 
-		tonTyp *p = patt[i];
-		if (p == NULL)
+		if (pattern[i] == NULL)
 			continue;
 
-		tonTyp *note = p;
-		for (int32_t j = 0; j < MAX_PATT_LEN * MAX_VOICES; j++, note++)
+		note_t *p = pattern[i];
+		for (int32_t j = 0; j < MAX_PATT_LEN * MAX_CHANNELS; j++, p++)
 		{
-			if (note->ton > 97)
-				note->ton = 0;
+			if (p->note > 97)
+				p->note = 0;
 
-			if (note->instr > 128)
-				note->instr = 0;
+			if (p->instr > 128)
+				p->instr = 0;
 
-			if (note->effTyp > 35)
+			if (p->efx > 35)
 			{
-				note->effTyp = 0;
-				note->eff = 0;
+				p->efx = 0;
+				p->efxData = 0;
 			}
 		}
 	}
 
-	setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5);
+	setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
 	setScrollBarPos(SB_POS_ED, 0, false);
 
 	resetChannels();
 	setPos(0, 0, true);
-	P_SetSpeed(song.speed);
+	setMixerBPM(song.BPM);
 
 	editor.tmpPattern = editor.editPattern; // set kludge variable
+	editor.BPM = song.BPM;
 	editor.speed = song.speed;
-	editor.tempo = song.tempo;
-	editor.timer = song.timer;
-	editor.globalVol = song.globVol;
+	editor.tick = song.tick;
+	editor.globalVolume = song.globalVolume;
 
-	setFrqTab(tmpLinearPeriodsFlag);
+	setFrequencyTable(tmpLinearPeriodsFlag);
 
 	unlockMixerCallback();
 
@@ -687,6 +683,8 @@
 			return;
 		}
 	}
+
+	exitTextEditing();
 
 	// pass UTF8 to these tests so that we can test file ending in ASCII/ANSI
 
--- a/src/ft2_module_loader.h
+++ b/src/ft2_module_loader.h
@@ -5,10 +5,10 @@
 #include "ft2_header.h"
 #include "ft2_unicode.h"
 
-bool tmpPatternEmpty(uint16_t nr);
-void clearUnusedChannels(tonTyp *p, int16_t pattLen, int32_t antChn);
-bool allocateTmpInstr(int16_t nr);
-bool allocateTmpPatt(int32_t nr, uint16_t rows);
+bool tmpPatternEmpty(uint16_t pattNum);
+void clearUnusedChannels(note_t *p, int16_t numRows, int32_t numChannels);
+bool allocateTmpInstr(int16_t insNum);
+bool allocateTmpPatt(int32_t pattNum, uint16_t numRows);
 void loadMusic(UNICHAR *filenameU);
 bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay);
 bool handleModuleLoadFromArg(int argc, char **argv);
@@ -19,7 +19,7 @@
 extern char *supportedModExtensions[];
 
 extern volatile bool tmpLinearPeriodsFlag;
-extern int16_t pattLensTmp[MAX_PATTERNS];
-extern tonTyp *pattTmp[MAX_PATTERNS];
-extern instrTyp *instrTmp[1+256];
-extern songTyp songTmp;
+extern int16_t patternNumRowsTmp[MAX_PATTERNS];
+extern note_t *patternTmp[MAX_PATTERNS];
+extern instr_t *instrTmp[1+256];
+extern song_t songTmp;
--- a/src/ft2_module_saver.c
+++ b/src/ft2_module_saver.c
@@ -14,16 +14,11 @@
 #include "ft2_tables.h"
 #include "ft2_structs.h"
 
-/* These savers are directly ported, so they should act identical to FT2
-** except for some very minor changes.
-*/
-
+static int8_t smpChunkBuf[1024];
+static uint8_t packedPattData[65536], modPattData[64*32*4];
 static SDL_Thread *thread;
 
-static uint8_t packedPattData[65536];
-static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows);
-
-static const char modSig[32][5] =
+static const char modIDs[32][5] =
 {
 	"1CHN", "2CHN", "3CHN", "4CHN", "5CHN", "6CHN", "7CHN", "8CHN",
 	"9CHN", "10CH", "11CH", "12CH", "13CH", "14CH", "15CH", "16CH",
@@ -31,16 +26,18 @@
 	"25CH", "26CH", "27CH", "28CH", "29CH", "30CH", "31CH", "32CH"
 };
 
+static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows);
+
 bool saveXM(UNICHAR *filenameU)
 {
 	int16_t i, j, k, a;
 	size_t result;
-	songHeaderTyp h;
-	patternHeaderTyp ph;
-	instrTyp *ins;
-	instrHeaderTyp ih;
-	sampleTyp *s;
-	sampleHeaderTyp *dst;
+	xmHdr_t h;
+	xmPatHdr_t ph;
+	instr_t *ins;
+	xmInsHdr_t ih;
+	sample_t *s;
+	xmSmpHdr_t *dst;
 
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
@@ -49,39 +46,56 @@
 		return false;
 	}
 
-	memcpy(h.sig, "Extended Module: ", 17);
-	memset(h.name, ' ', 20);
-	h.name[20] = 0x1A;
-	memcpy(h.name, song.name, strlen(song.name));
-	memcpy(h.progName, PROG_NAME_STR, 20);
-	h.ver = 0x0104;
+	memcpy(h.ID, "Extended Module: ", 17);
+
+	// song name
+	int32_t nameLength = (int32_t)strlen(song.name);
+	if (nameLength > 20)
+		nameLength = 20;
+
+	memset(h.name, ' ', 20); // yes, FT2 pads the name with spaces
+	if (nameLength > 0)
+		memcpy(h.name, song.name, nameLength);
+
+	h.x1A = 0x1A;
+
+	// program/tracker name
+	nameLength = (int32_t)strlen(PROG_NAME_STR);
+	if (nameLength > 20)
+		nameLength = 20;
+
+	memset(h.progName, ' ', 20); // yes, FT2 pads the name with spaces
+	if (nameLength > 0)
+		memcpy(h.progName, PROG_NAME_STR, nameLength);
+
+	h.version = 0x0104;
 	h.headerSize = 20 + 256;
-	h.len = song.len;
-	h.repS = song.repS;
-	h.antChn = (uint16_t)song.antChn;
-	h.defTempo = song.tempo;
-	h.defSpeed = song.speed;
+	h.numOrders = song.songLength;
+	h.songLoopStart = song.songLoopStart;
+	h.numChannels = (uint16_t)song.numChannels;
+	h.speed = song.speed;
+	h.BPM = song.BPM;
 
 	// count number of patterns
-	int16_t ap = MAX_PATTERNS;
+	i = MAX_PATTERNS;
 	do
 	{
-		if (patternEmpty(ap - 1))
-			ap--;
+		if (patternEmpty(i-1))
+			i--;
 		else
 			break;
 	}
-	while (ap > 0);
-	h.antPtn = ap;
+	while (i > 0);
+	h.numPatterns = i;
 
 	// count number of instruments
-	int16_t ai = 128;
-	while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0')
-		ai--;
-	h.antInstrs = ai;
+	i = 128;
+	while (i > 0 && getUsedSamples(i) == 0 && song.instrName[i][0] == '\0')
+		i--;
+	h.numInstr = i;
 
 	h.flags = audio.linearPeriodsFlag;
-	memcpy(h.songTab, song.songTab, sizeof (song.songTab));
+	memcpy(h.orders, song.orders, 256);
 
 	if (fwrite(&h, sizeof (h), 1, f) != 1)
 	{
@@ -90,27 +104,27 @@
 		return false;
 	}
 
-	for (i = 0; i < ap; i++)
+	for (i = 0; i < h.numPatterns; i++)
 	{
 		if (patternEmpty(i))
 		{
-			if (patt[i] != NULL)
+			if (pattern[i] != NULL)
 			{
-				free(patt[i]);
-				patt[i] = NULL;
+				free(pattern[i]);
+				pattern[i] = NULL;
 			}
 
-			pattLens[i] = 64;
+			patternNumRows[i] = 64;
 		}
 
-		ph.patternHeaderSize = sizeof (patternHeaderTyp);
-		ph.pattLen = pattLens[i];
-		ph.typ = 0;
+		ph.headerSize = sizeof (xmPatHdr_t);
+		ph.numRows = patternNumRows[i];
+		ph.type = 0;
 
-		if (patt[i] == NULL)
+		if (pattern[i] == NULL)
 		{
-			ph.dataLen = 0;
-			if (fwrite(&ph, ph.patternHeaderSize, 1, f) != 1)
+			ph.dataSize = 0;
+			if (fwrite(&ph, ph.headerSize, 1, f) != 1)
 			{
 				fclose(f);
 				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
@@ -119,10 +133,10 @@
 		}
 		else
 		{
-			ph.dataLen = packPatt(packedPattData, (uint8_t *)patt[i], pattLens[i]);
+			ph.dataSize = packPatt(packedPattData, (uint8_t *)pattern[i], patternNumRows[i]);
 
-			result = fwrite(&ph, ph.patternHeaderSize, 1, f);
-			result += fwrite(packedPattData, ph.dataLen, 1, f);
+			result = fwrite(&ph, ph.headerSize, 1, f);
+			result += fwrite(packedPattData, ph.dataSize, 1, f);
 
 			if (result != 2) // write was not OK
 			{
@@ -135,7 +149,7 @@
 
 	memset(&ih, 0, sizeof (ih)); // important, clears reserved stuff
 
-	for (i = 1; i <= ai; i++)
+	for (i = 1; i <= h.numInstr; i++)
 	{
 		if (instr[i] == NULL)
 			j = 0;
@@ -144,35 +158,40 @@
 
 		a = getUsedSamples(i);
 
-		memset(ih.name, 0, 22);
-		memcpy(ih.name, song.instrName[i], strlen(song.instrName[i]));
+		nameLength = (int32_t)strlen(song.instrName[i]);
+		if (nameLength > 22)
+			nameLength = 22;
 
-		ih.typ = 0;
-		ih.antSamp = a;
-		ih.sampleSize = sizeof (sampleHeaderTyp);
+		memset(ih.name, 0, 22); // pad with zero
+		if (nameLength > 0)
+			memcpy(ih.name, song.instrName[i], nameLength);
 
+		ih.type = 0;
+		ih.numSamples = a;
+		ih.sampleSize = sizeof (xmSmpHdr_t);
+
 		if (a > 0)
 		{
 			ins = instr[j];
 
-			memcpy(ih.ta, ins->ta, 96);
-			memcpy(ih.envVP, ins->envVP, 12*2*sizeof(int16_t));
-			memcpy(ih.envPP, ins->envPP, 12*2*sizeof(int16_t));
-			ih.envVPAnt = ins->envVPAnt;
-			ih.envPPAnt = ins->envPPAnt;
-			ih.envVSust = ins->envVSust;
-			ih.envVRepS = ins->envVRepS;
-			ih.envVRepE = ins->envVRepE;
-			ih.envPSust = ins->envPSust;
-			ih.envPRepS = ins->envPRepS;
-			ih.envPRepE = ins->envPRepE;
-			ih.envVTyp = ins->envVTyp;
-			ih.envPTyp = ins->envPTyp;
-			ih.vibTyp = ins->vibTyp;
+			memcpy(ih.note2SampleLUT, ins->note2SampleLUT, 96);
+			memcpy(ih.volEnvPoints, ins->volEnvPoints, 12*2*sizeof(int16_t));
+			memcpy(ih.panEnvPoints, ins->panEnvPoints, 12*2*sizeof(int16_t));
+			ih.volEnvLength = ins->volEnvLength;
+			ih.panEnvLength = ins->panEnvLength;
+			ih.volEnvSustain = ins->volEnvSustain;
+			ih.volEnvLoopStart = ins->volEnvLoopStart;
+			ih.volEnvLoopEnd = ins->volEnvLoopEnd;
+			ih.panEnvSustain = ins->panEnvSustain;
+			ih.panEnvLoopStart = ins->panEnvLoopStart;
+			ih.panEnvLoopEnd = ins->panEnvLoopEnd;
+			ih.volEnvFlags = ins->volEnvFlags;
+			ih.panEnvFlags = ins->panEnvFlags;
+			ih.vibType = ins->vibType;
 			ih.vibSweep = ins->vibSweep;
 			ih.vibDepth = ins->vibDepth;
 			ih.vibRate = ins->vibRate;
-			ih.fadeOut = ins->fadeOut;
+			ih.fadeout = ins->fadeout;
 			ih.midiOn = ins->midiOn ? 1 : 0;
 			ih.midiChannel = ins->midiChannel;
 			ih.midiProgram = ins->midiProgram;
@@ -182,26 +201,40 @@
 			
 			for (k = 0; k < a; k++)
 			{
-				s = &instr[j]->samp[k];
-				dst = &ih.samp[k];
+				s = &instr[j]->smp[k];
+				dst = &ih.smp[k];
 
-				dst->len = s->len;
-				dst->repS = s->repS;
-				dst->repL = s->repL;
-				dst->vol = s->vol;
-				dst->fine = s->fine;
-				dst->typ = s->typ;
-				dst->pan = s->pan;
-				dst->relTon = s->relTon;
+				bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 
-				uint8_t nameLen = (uint8_t)strlen(s->name);
+				dst->length = s->length;
+				dst->loopStart = s->loopStart;
+				dst->loopLength = s->loopLength;
 
-				dst->nameLen = nameLen;
-				memset(dst->name, ' ', 22);
-				memcpy(dst->name, s->name, nameLen);
+				if (sample16Bit)
+				{
+					dst->length <<= 1;
+					dst->loopStart <<= 1;
+					dst->loopLength <<= 1;
+				}
 
-				if (s->pek == NULL)
-					dst->len = 0;
+				dst->volume = s->volume;
+				dst->finetune = s->finetune;
+				dst->flags = s->flags;
+				dst->panning = s->panning;
+				dst->relativeNote = s->relativeNote;
+
+				nameLength = (int32_t)strlen(s->name);
+				if (nameLength > 22)
+					nameLength = 22;
+
+				dst->nameLength = (uint8_t)nameLength;
+
+				memset(dst->name, ' ', 22); // yes, FT2 pads the name with spaces
+				if (nameLength > 0)
+					memcpy(dst->name, s->name, nameLength);
+
+				if (s->dataPtr == NULL)
+					dst->length = 0;
 			}
 		}
 		else
@@ -209,7 +242,7 @@
 			ih.instrSize = 22 + 11;
 		}
 
-		if (fwrite(&ih, ih.instrSize + (a * sizeof (sampleHeaderTyp)), 1, f) != 1)
+		if (fwrite(&ih, ih.instrSize + (a * sizeof (xmSmpHdr_t)), 1, f) != 1)
 		{
 			fclose(f);
 			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
@@ -218,18 +251,18 @@
 
 		for (k = 1; k <= a; k++)
 		{
-			s = &instr[j]->samp[k-1];
-			if (s->pek != NULL)
+			s = &instr[j]->smp[k-1];
+			if (s->dataPtr != NULL)
 			{
-				restoreSample(s);
-				samp2Delta(s->pek, s->len, s->typ);
+				unfixSample(s);
+				samp2Delta(s->dataPtr, s->length, s->flags);
 
-				result = fwrite(s->pek, 1, s->len, f);
+				result = fwrite(s->dataPtr, 1, SAMPLE_LENGTH_BYTES(s), f);
 
-				delta2Samp(s->pek, s->len, s->typ);
+				delta2Samp(s->dataPtr, s->length, s->flags);
 				fixSample(s);
 
-				if (result != (size_t)s->len) // write not OK
+				if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK
 				{
 					fclose(f);
 					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
@@ -251,49 +284,67 @@
 
 static bool saveMOD(UNICHAR *filenameU)
 {
-	bool test, tooManyInstr, incompatEfx, noteUnderflow;
-	int8_t *srcPtr, *dstPtr;
-	uint8_t ton, inst, pattBuff[64*4*32];
-	int16_t a, i, ap;
-	int32_t j, k, l1, l2, l3, writeLen, bytesToWrite, bytesWritten;
-	FILE *f;
-	instrTyp *ins;
-	sampleTyp *smp;
-	tonTyp *t;
-	songMOD31HeaderTyp hm;
+	int16_t i;
+	int32_t j, k;
+	instr_t *ins;
+	sample_t *smp;
+	modHdr_t hdr;
 
-	tooManyInstr = false;
-	incompatEfx = false;
-	noteUnderflow = false;
-
 	if (audio.linearPeriodsFlag)
-		okBoxThreadSafe(0, "System message", "Linear frequency table used!");
+		okBoxThreadSafe(0, "System message", "Warning: Amiga frequency table isn't used!");
 
-	// sanity checking
+	int32_t songLength = song.songLength;
+	if (songLength > 128)
+	{
+		songLength = 128;
+		okBoxThreadSafe(0, "System message", "Warning: Song length is above 128!");
+	}
+	
+	// calculate number of patterns referenced (max 128 orders)
+	int32_t numPatterns = 0;
+	for (i = 0; i < songLength; i++)
+	{
+		if (song.orders[i] > numPatterns)
+			numPatterns = song.orders[i];
+	}
+	numPatterns++;
 
-	test = false;
-	if (song.len > 128)
-		test = true;
-
-	for (i = 100; i < 256; i++)
+	if (numPatterns > 100)
 	{
-		if (patt[i] != NULL)
-		{
-			test = true;
-			break;
-		}
+		numPatterns = 100;
+		okBoxThreadSafe(0, "System message", "Warning: Song has more than 100 patterns!");
 	}
-	if (test) okBoxThreadSafe(0, "System message", "Too many patterns!");
 
+	// check if song has more than 31 instruments
 	for (i = 32; i <= 128; i++)
 	{
 		if (getRealUsedSamples(i) > 0)
 		{
-			okBoxThreadSafe(0, "System message", "Too many instruments!");
+			okBoxThreadSafe(0, "System message", "Warning: Song has more than 31 instruments!");
 			break;
 		}
 	}
 
+	// check if the first 31 samples have a length above 65534 samples
+	bool test = false;
+	bool test2 = false;
+	for (i = 1; i <= 31; i++)
+	{
+		ins = instr[i];
+		if (ins == NULL)
+			continue;
+
+		smp = &ins->smp[0];
+
+		if (smp->length > 131070)
+			test = true;
+		else if (smp->length > 65534)
+			test2 = true;
+	}
+	if (test) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths that are too long for the MOD format!");
+	else if (test2) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths above 65534! Not all MOD players support this.");
+
+	// check if XM instrument features are being used
 	test = false;
 	for (i = 1; i <= 31; i++)
 	{
@@ -301,7 +352,7 @@
 		if (ins == NULL)
 			continue;
 
-		smp = &ins->samp[0];
+		smp = &ins->smp[0];
 
 		j = getRealUsedSamples(i);
 		if (j > 1)
@@ -312,8 +363,8 @@
 
 		if (j == 1)
 		{
-			if (smp->len > 65534 || ins->fadeOut != 0 || ins->envVTyp != 0 || ins->envPTyp != 0 ||
-				(smp->typ & 3) == 2 || smp->relTon != 0 || ins->midiOn)
+			if (ins->fadeout != 0 || ins->volEnvFlags != 0 || ins->panEnvFlags != 0 || ins->vibRate > 0 ||
+				GET_LOOPTYPE(smp->flags) == LOOP_BIDI || smp->relativeNote != 0 || ins->midiOn)
 			{
 				test = true;
 				break;
@@ -320,125 +371,129 @@
 			}
 		}
 	}
-	if (test) okBoxThreadSafe(0, "System message", "Incompatible instruments!");
+	if (test) okBoxThreadSafe(0, "System message", "Warning: Song is using XM instrument features!");
 
-	for (i = 0; i < 99; i++)
+	bool tooLongPatterns = false;
+	bool tooManyInstr = false;
+	bool incompatEfx = false;
+	bool noteUnderflow = false;
+
+	for (i = 0; i < numPatterns; i++)
 	{
-		if (patt[i] != NULL)
+		if (pattern[i] == NULL)
+			continue;
+
+		if (patternNumRows[i] < 64)
 		{
-			if (pattLens[i] != 64)
-			{
-				okBoxThreadSafe(0, "System message", "Unable to convert module. (Illegal pattern length)");
-				return false;
-			}
+			okBoxThreadSafe(0, "System message", "Error: Pattern lengths can't be below 64! Module wasn't saved.");
+			return false;
+		}
 
-			for (j = 0; j < 64; j++)
+		if (patternNumRows[i] > 64)
+			tooLongPatterns = true;
+
+		for (j = 0; j < 64; j++)
+		{
+			for (k = 0; k < song.numChannels; k++)
 			{
-				for (k = 0; k < song.antChn; k++)
-				{
-					t = &patt[i][(j * MAX_VOICES) + k];
+				note_t *p = &pattern[i][(j * MAX_CHANNELS) + k];
 
-					if (t->instr > 31)
-						tooManyInstr = true;
+				if (p->instr > 31)
+					tooManyInstr = true;
 
-					if (t->effTyp > 15 || t->vol != 0)
-						incompatEfx = true;
+				if (p->efx > 0xF || p->vol != 0)
+					incompatEfx = true;
 
-					// added security that wasn't present in FT2
-					if (t->ton > 0 && t->ton < 10)
-						noteUnderflow = true;
-				}
+				// added security that wasn't present in FT2
+				if (p->note > 0 && p->note < 10)
+					noteUnderflow = true;
 			}
 		}
 	}
-	if (tooManyInstr)  okBoxThreadSafe(0, "System message", "Instrument(s) above 31 was found in pattern data!");
-	if (incompatEfx)   okBoxThreadSafe(0, "System message", "Incompatible effect(s) was found in pattern data!");
-	if (noteUnderflow) okBoxThreadSafe(0, "System message", "Note(s) below A-0 were found in pattern data!");
 
-	// setup header buffer
-	memset(&hm, 0, sizeof (hm));
-	memcpy(hm.name, song.name, sizeof (hm.name));
-	hm.len = (uint8_t)song.len;
-	if (hm.len > 128) hm.len = 128;
-	hm.repS = (uint8_t)song.repS;
-	if (hm.repS > 127) hm.repS = 0;
-	memcpy(hm.songTab, song.songTab, song.len);
+	if (tooLongPatterns) okBoxThreadSafe(0, "System message", "Warning: Song has pattern lengths above 64!");
+	if (tooManyInstr) okBoxThreadSafe(0, "System message", "Warning: Patterns have instrument numbers above 31!");
+	if (incompatEfx) okBoxThreadSafe(0, "System message", "Warning: Patterns have incompatible effects!");
+	if (noteUnderflow) okBoxThreadSafe(0, "System message", "Warning: Patterns have notes below A-0!");
 
-	// calculate number of patterns
-	ap = 0;
-	for (i = 0; i < song.len; i++)
-	{
-		if (song.songTab[i] > ap)
-			ap = song.songTab[i];
-	}
+	// save module now
 
-	if (song.antChn == 4)
-		memcpy(hm.sig, (ap > 64) ? "M!K!" : "M.K.", 4);
+	memset(&hdr, 0, sizeof (hdr));
+
+	// song name
+	int32_t nameLength = (int32_t)strlen(song.name);
+	if (nameLength > 20)
+		nameLength = 20;
+
+	memset(hdr.name, 0, 20); // pad with zeroes
+	if (nameLength > 0)
+		memcpy(hdr.name, song.name, nameLength);
+
+	hdr.numOrders = (uint8_t)songLength; // pre-clamped to 0..128
+
+	hdr.songLoopStart = (uint8_t)song.songLoopStart;
+	if (hdr.songLoopStart >= hdr.numOrders) // repeat-point must be lower than the song length
+		hdr.songLoopStart = 0;
+
+	memcpy(hdr.orders, song.orders, hdr.numOrders);
+
+	if (song.numChannels == 4)
+		memcpy(hdr.ID, (numPatterns > 64) ? "M!K!" : "M.K.", 4);
 	else
-		memcpy(hm.sig, modSig[song.antChn-1], 4);
+		memcpy(hdr.ID, modIDs[song.numChannels-1], 4);
 
-	// read sample information into header buffer
+	// fill MOD sample headers
 	for (i = 1; i <= 31; i++)
 	{
-		songMODInstrHeaderTyp *modIns = &hm.instr[i-1];
+		modSmpHdr_t *modSmp = &hdr.smp[i-1];
 
-		memcpy(modIns->name, song.instrName[i], sizeof (modIns->name));
+		nameLength = (int32_t)strlen(song.instrName[i]);
+		if (nameLength > 22)
+			nameLength = 22;
+
+		memset(modSmp->name, 0, 22); // pad with zeroes
+		if (nameLength > 0)
+			memcpy(modSmp->name, song.instrName[i], nameLength);
+
 		if (instr[i] != NULL && getRealUsedSamples(i) != 0)
 		{
-			smp = &instr[i]->samp[0];
+			smp = &instr[i]->smp[0];
 
-			l1 = smp->len >> 1;
-			l2 = smp->repS >> 1;
-			l3 = smp->repL >> 1;
+			int32_t length = smp->length >> 1;
+			int32_t loopStart = smp->loopStart >> 1;
+			int32_t loopLength = smp->loopLength >> 1;
 
-			if (smp->typ & 16)
-			{
-				l1 >>= 1;
-				l2 >>= 1;
-				l3 >>= 1;
-			}
+			// length/loopStart/loopLength are now in units of words
 
-			if (l1 > 32767)
-				l1 = 32767;
+			if (length > UINT16_MAX)
+				length = UINT16_MAX;
 
-			if (l2 > l1)
-				l2 = l1;
-
-			if (l2+l3 > l1)
-				l3 = l1 - l2;
-
-			// FT2 bug-fix
-			if (l3 < 1)
+			if (GET_LOOPTYPE(smp->flags) == LOOP_OFF)
 			{
-				l2 = 0;
-				l3 = 1;
+				loopStart = 0;
+				loopLength = 1;
 			}
-
-			modIns->len = (uint16_t)(SWAP16(l1));
-			modIns->fine = ((smp->fine + 128) >> 4) ^ 8;
-			modIns->vol = smp->vol;
-
-			if ((smp->typ & 3) == 0)
+			else // looped sample
 			{
-				modIns->repS = 0;
-				modIns->repL = SWAP16(1);
+				if (loopLength == 0) // ProTracker hates loopLengths of zero
+					loopLength = 1;
+
+				if (loopStart+loopLength > length)
+				{
+					loopStart = 0;
+					loopLength = 1;
+				}
 			}
-			else
-			{
-				modIns->repS = (uint16_t)(SWAP16(l2));
-				modIns->repL = (uint16_t)(SWAP16(l3));
-			}
-		}
 
-		// FT2 bugfix: never allow replen being below 2 (1)
-		if (SWAP16(modIns->repL) < 1)
-		{
-			modIns->repS = SWAP16(0);
-			modIns->repL = SWAP16(1);
+			modSmp->length = (uint16_t)SWAP16(length);
+			modSmp->finetune = FINETUNE_XM2MOD(smp->finetune);
+			modSmp->volume = smp->volume;
+			modSmp->loopStart = (uint16_t)SWAP16(loopStart);
+			modSmp->loopLength = (uint16_t)SWAP16(loopLength);
 		}
 	}
 
-	f = UNICHAR_FOPEN(filenameU, "wb");
+	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
 	{
 		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?");
@@ -446,7 +501,7 @@
 	}
 
 	// write header
-	if (fwrite(&hm, 1, sizeof (hm), f) != sizeof (hm))
+	if (fwrite(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))
 	{
 		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
 		goto modSaveError;
@@ -453,66 +508,66 @@
 	}
 
 	// write pattern data
-	for (i = 0; i <= ap; i++)
+	const int32_t patternBytes = song.numChannels * 64 * 4;
+	for (i = 0; i < numPatterns; i++)
 	{
-		if (patt[i] == NULL)
+		if (pattern[i] == NULL) // empty pattern
 		{
-			// empty pattern
-			memset(pattBuff, 0, song.antChn * (64 * 4));
+			memset(modPattData, 0, patternBytes);
 		}
 		else
 		{
-			a = 0;
+			int32_t offs = 0;
 			for (j = 0; j < 64; j++)
 			{
-				for (k = 0; k < song.antChn; k++)
+				for (k = 0; k < song.numChannels; k++)
 				{
-					t = &patt[i][(j * MAX_VOICES) + k];
+					note_t *p = &pattern[i][(j * MAX_CHANNELS) + k];
 
-					inst = t->instr;
-					ton = t->ton;
+					uint8_t inst = p->instr;
+					uint8_t note = p->note;
 
 					// FT2 bugfix: prevent overflow
 					if (inst > 31)
 						inst = 0;
 
-					// FT2 bugfix: convert note-off into no note
-					if (ton == 97)
-						ton = 0;
+					// FT2 bugfix: convert note-off into no note for MOD saving
+					if (note == NOTE_OFF)
+						note = 0;
 
 					// FT2 bugfix: clamp notes below 10 (A-0) to prevent 12-bit period overflow
-					if (ton > 0 && ton < 10)
-						ton = 10;
+					if (note > 0 && note < 10)
+						note = 10;
 
-					if (ton == 0)
+					if (note == 0)
 					{
-						pattBuff[a+0] = inst & 0xF0;
-						pattBuff[a+1] = 0;
+						modPattData[offs+0] = inst & 0xF0;
+						modPattData[offs+1] = 0;
 					}
 					else
 					{
-						pattBuff[a+0] = (inst & 0xF0) | ((amigaPeriod[ton-1] >> 8) & 0x0F);
-						pattBuff[a+1] = amigaPeriod[ton-1] & 0xFF;
+						modPattData[offs+0] = (inst & 0xF0) | ((amigaPeriod[note-1] >> 8) & 0x0F);
+						modPattData[offs+1] = amigaPeriod[note-1] & 0xFF;
 					}
 
 					// FT2 bugfix: if effect is overflowing (0xF in .MOD), set effect and param to 0
-					if (t->effTyp > 0x0F)
+					if (p->efx > 0x0F)
 					{
-						pattBuff[a+2] = (inst & 0x0F) << 4;
-						pattBuff[a+3] = 0;
+						modPattData[offs+2] = (inst & 0x0F) << 4;
+						modPattData[offs+3] = 0;
 					}
 					else
 					{
-						pattBuff[a+2] = ((inst & 0x0F) << 4) | (t->effTyp & 0x0F);
-						pattBuff[a+3] = t->eff;
+						modPattData[offs+2] = ((inst & 0x0F) << 4) | (p->efx & 0x0F);
+						modPattData[offs+3] = p->efxData;
 					}
 
-					a += 4;
+					offs += 4;
 				}
 			}
 		}
 
-		if (fwrite(pattBuff, 1, song.antChn * (64 * 4), f) != (size_t)(song.antChn * (64 * 4)))
+		if (fwrite(modPattData, 1, patternBytes, f) != (size_t)patternBytes)
 		{
 			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
 			goto modSaveError;
@@ -520,39 +575,38 @@
 	}
 
 	// write sample data
-	for (i = 0; i < 31; i++)
+	for (i = 1; i <= 31; i++)
 	{
-		if (instr[1+i] == NULL || getRealUsedSamples(1+i) == 0)
+		if (instr[i] == NULL || getRealUsedSamples(i) == 0)
 			continue;
 
-		smp = &instr[1+i]->samp[0];
-		if (smp->pek == NULL || smp->len <= 0)
+		smp = &instr[i]->smp[0];
+		if (smp->dataPtr == NULL || smp->length <= 0)
 			continue;
 
-		restoreSample(smp);
+		modSmpHdr_t *modSmp = &hdr.smp[i-1];
 
-		l1 = smp->len >> 1;
-		if (smp->typ & 16) // 16-bit sample (convert to 8-bit)
-		{
-			if (l1 > 65534)
-				l1 = 65534;
+		unfixSample(smp);
 
-			// let's borrow "pattBuff" here
-			dstPtr = (int8_t *)pattBuff;
+		int32_t sampleBytes = SWAP16(modSmp->length) * 2;
 
-			writeLen = l1;
-			bytesWritten = 0;
-			while (bytesWritten < writeLen) // write in 8K blocks
+		if (smp->flags & SAMPLE_16BIT) // 16-bit sample (convert to 8-bit)
+		{
+			int8_t *dstPtr = (int8_t *)smpChunkBuf;
+			int32_t writeLen = sampleBytes;
+			int32_t samplesWritten = 0;
+
+			while (samplesWritten < writeLen) // write in chunks
 			{
-	 			bytesToWrite = sizeof (pattBuff);
-				if (bytesWritten+bytesToWrite > writeLen)
-					bytesToWrite = writeLen - bytesWritten;
+	 			int32_t samplesToWrite = sizeof (smpChunkBuf);
+				if (samplesWritten+samplesToWrite > writeLen)
+					samplesToWrite = writeLen - samplesWritten;
 
-				srcPtr = &smp->pek[(bytesWritten << 1) + 1]; // +1 to align to high byte
-				for (j = 0; j < bytesToWrite; j++)
-					dstPtr[j] = srcPtr[j << 1];
+				int16_t *srcPtr16 = (int16_t *)smp->dataPtr + samplesWritten;
+				for (j = 0; j < samplesToWrite; j++)
+					dstPtr[j] = srcPtr16[j] >> 8; // convert 16-bit to 8-bit
 
-				if (fwrite(dstPtr, 1, bytesToWrite, f) != (size_t)bytesToWrite)
+				if (fwrite(dstPtr, 1, samplesToWrite, f) != (size_t)samplesToWrite)
 				{
 					fixSample(smp);
 					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
@@ -559,19 +613,12 @@
 					goto modSaveError;
 				}
 
-				bytesWritten += bytesToWrite;
+				samplesWritten += samplesToWrite;
 			}
 		}
-		else
+		else // 8-bit sample
 		{
-			// 8-bit sample
-
-			if (l1 > 32767)
-				l1 = 32767;
-
-			l1 <<= 1;
-
-			if (fwrite(smp->pek, 1, l1, f) != (size_t)l1)
+			if (fwrite(smp->dataPtr, 1, sampleBytes, f) != (size_t)sampleBytes)
 			{
 				fixSample(smp);
 				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");
@@ -638,10 +685,10 @@
 
 	uint16_t totalPackLen = 0;
 
-	const int32_t pitch = sizeof (tonTyp) * (MAX_VOICES - song.antChn);
+	const int32_t pitch = sizeof (note_t) * (MAX_CHANNELS - song.numChannels);
 	for (int32_t row = 0; row < numRows; row++)
 	{
-		for (int32_t chn = 0; chn < song.antChn; chn++)
+		for (int32_t chn = 0; chn < song.numChannels; chn++)
 		{
 			bytes[0] = *pattPtr++;
 			bytes[1] = *pattPtr++;
--- a/src/ft2_mouse.c
+++ b/src/ft2_mouse.c
@@ -8,7 +8,7 @@
 #include "ft2_header.h"
 #include "ft2_gui.h"
 #include "ft2_video.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_help.h"
 #include "ft2_sample_ed.h"
 #include "ft2_inst_ed.h"
@@ -127,7 +127,7 @@
 							outX[xScale] = pixel;
 					}
 
-					outX += video.xScale;
+					outX += scaleFactor;
 				}
 			}
 		}
@@ -383,11 +383,11 @@
 	if (songPlaying)
 		return;
 
-	int16_t pattPos = editor.pattPos - 1;
-	if (pattPos < 0)
-		pattPos = pattLens[editor.editPattern] - 1;
+	int16_t row = editor.row - 1;
+	if (row < 0)
+		row = patternNumRows[editor.editPattern] - 1;
 
-	setPos(-1, pattPos, true);
+	setPos(-1, row, true);
 }
 
 static void mouseWheelIncRow(void)
@@ -395,11 +395,11 @@
 	if (songPlaying)
 		return;
 
-	int16_t pattPos = editor.pattPos + 1;
-	if (pattPos > (pattLens[editor.editPattern] - 1))
-		pattPos = 0;
+	int16_t row = editor.row + 1;
+	if (row >= patternNumRows[editor.editPattern])
+		row = 0;
 
-	setPos(-1, pattPos, true);
+	setPos(-1, row, true);
 }
 
 void mouseWheelHandler(bool directionUp)
@@ -569,7 +569,7 @@
 		{
 			// right mouse button released after hand-editing sample data
 			if (instr[editor.curInstr] != NULL)
-				fixSample(&instr[editor.curInstr]->samp[editor.curSmp]);
+				fixSample(&instr[editor.curInstr]->smp[editor.curSmp]);
 
 			resumeAudio();
 
--- a/src/ft2_nibbles.c
+++ b/src/ft2_nibbles.c
@@ -1,5 +1,3 @@
-// directly ported from FT2 source code (except some routines)
-
 #include <stdint.h>
 #include <stdio.h>
 #include <math.h> // round()
@@ -13,7 +11,6 @@
 #include "ft2_structs.h"
 
 #define STAGES_BMP_WIDTH 530
-
 #define NI_MAXLEVEL 30
 
 static const char *NI_HelpText[] =
@@ -35,14 +32,14 @@
 
 typedef struct
 {
-	int16_t antal;
+	int16_t length;
 	uint8_t data[8];
-} nibbleBufferTyp;
+} nibblesBuffer_t;
 
 typedef struct
 {
 	uint8_t x, y;
-} nibbleCrd;
+} nibblesCoord_t;
 
 static const char nibblesCheatCode1[] = "skip", nibblesCheatCode2[] = "triton";
 static char nibblesCheatBuffer[16];
@@ -51,11 +48,11 @@
 static const uint8_t NI_Speeds[4] = { 12, 8, 6, 4 };
 static bool NI_EternalLives;
 static uint8_t NI_CheatIndex, NI_CurSpeed, NI_CurTick60Hz, NI_CurSpeed60Hz, NI_Screen[51][23], NI_Level;
-static int16_t NI_P1Dir, NI_P2Dir, NI_P1Len, NI_P2Len, NI_Number, NI_NumberX, NI_NumberY, NI_P1NoRens, NI_P2NoRens;
+static int16_t NI_P1Dir, NI_P2Dir, NI_P1Len, NI_P2Len, NI_Number, NI_NumberX, NI_NumberY, NI_P1NoClear, NI_P2NoClear;
 static uint16_t NI_P1Lives, NI_P2Lives;
 static int32_t NI_P1Score, NI_P2Score;
-static nibbleCrd NI_P1[256], NI_P2[256];
-static nibbleBufferTyp nibblesBuffer[2];
+static nibblesCoord_t NI_P1[256], NI_P2[256];
+static nibblesBuffer_t nibblesBuffer[2];
 
 /* Non-FT2 feature: Check if either the Desktop or Buttons palette color
 ** is so close to black that the player would have troubles seeing the walls
@@ -150,29 +147,29 @@
 	}
 }
 
-static void nibblesAddBuffer(int16_t nr, uint8_t typ)
+static void nibblesAddBuffer(int16_t bufNum, uint8_t value)
 {
-	nibbleBufferTyp *n = &nibblesBuffer[nr];
-	if (n->antal < 8)
+	nibblesBuffer_t *n = &nibblesBuffer[bufNum];
+	if (n->length < 8)
 	{
-		n->data[n->antal] = typ;
-		n->antal++;
+		n->data[n->length] = value;
+		n->length++;
 	}
 }
 
-static bool nibblesBufferFull(int16_t nr)
+static bool nibblesBufferFull(int16_t bufNum)
 {
-	return (nibblesBuffer[nr].antal > 0);
+	return (nibblesBuffer[bufNum].length > 0);
 }
 
-static int16_t nibblesGetBuffer(int16_t nr)
+static int16_t nibblesGetBuffer(int16_t bufNum)
 {
-	nibbleBufferTyp *n = &nibblesBuffer[nr];
-	if (n->antal > 0)
+	nibblesBuffer_t *n = &nibblesBuffer[bufNum];
+	if (n->length > 0)
 	{
 		const int16_t dataOut = n->data[0];
 		memmove(&n->data[0], &n->data[1], 7);
-		n->antal--;
+		n->length--;
 
 		return dataOut;
 	}
@@ -180,10 +177,10 @@
 	return -1;
 }
 
-static void nibblesGetLevel(int16_t nr)
+static void nibblesGetLevel(int16_t levelNum)
 {
-	const int32_t readX = 1 + ((51+2) * (nr % 10));
-	const int32_t readY = 1 + ((23+2) * (nr / 10));
+	const int32_t readX = 1 + ((51+2) * (levelNum % 10));
+	const int32_t readY = 1 + ((23+2) * (levelNum / 10));
 
 	const uint8_t *stagePtr = &bmp.nibblesStages[(readY * STAGES_BMP_WIDTH) + readX];
 
@@ -196,12 +193,12 @@
 	}
 }
 
-static void nibblesCreateLevel(int16_t nr)
+static void nibblesCreateLevel(int16_t levelNum)
 {
-	if (nr >= NI_MAXLEVEL)
-		nr = NI_MAXLEVEL - 1;
+	if (levelNum >= NI_MAXLEVEL)
+		levelNum = NI_MAXLEVEL-1;
 
-	nibblesGetLevel(nr);
+	nibblesGetLevel(levelNum);
 
 	int32_t x1 = 0;
 	int32_t x2 = 0;
@@ -233,8 +230,8 @@
 		}
 	}
 
-	const int32_t readX = (51 + 2) * (nr % 10);
-	const int32_t readY = (23 + 2) * (nr / 10);
+	const int32_t readX = (51 + 2) * (levelNum % 10);
+	const int32_t readY = (23 + 2) * (levelNum / 10);
 
 	NI_P1Dir = bmp.nibblesStages[(readY * 530) + (readX + 1)];
 	NI_P2Dir = bmp.nibblesStages[(readY * 530) + (readX + 0)];
@@ -241,11 +238,11 @@
 
 	NI_P1Len = 5;
 	NI_P2Len = 5;
-	NI_P1NoRens = 0;
-	NI_P2NoRens = 0;
+	NI_P1NoClear = 0;
+	NI_P2NoClear = 0;
 	NI_Number = 0;
-	nibblesBuffer[0].antal = 0;
-	nibblesBuffer[1].antal = 0;
+	nibblesBuffer[0].length = 0;
+	nibblesBuffer[1].length = 0;
 
 	for (int32_t i = 0; i < 256; i++)
 	{
@@ -256,10 +253,10 @@
 	}
 }
 
-static void nibbleWriteLevelSprite(int16_t xOut, int16_t yOut, int16_t nr)
+static void nibbleWriteLevelSprite(int16_t xOut, int16_t yOut, int16_t levelNum)
 {
-	const int32_t readX = (51 + 2) * (nr % 10);
-	const int32_t readY = (23 + 2) * (nr / 10);
+	const int32_t readX = (51 + 2) * (levelNum % 10);
+	const int32_t readY = (23 + 2) * (levelNum / 10);
 
 	const uint8_t *src = (const uint8_t *)&bmp.nibblesStages[(readY * 530) + readX];
 	uint32_t *dst = &video.frameBuffer[(yOut * SCREEN_W) + xOut];
@@ -385,7 +382,7 @@
 	redrawNibblesScreen();
 
 	setNibbleDot(NI_P1[0].x, NI_P1[0].y, 6);
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 		setNibbleDot(NI_P2[0].x, NI_P2[0].y, 7);
 
 	if (!config.NI_Surround)
@@ -450,7 +447,7 @@
 
 		// prevent highscore table from showing overflowing level graphics
 		if (NI_Level >= NI_MAXLEVEL)
-			NI_Level = NI_MAXLEVEL - 1;
+			NI_Level = NI_MAXLEVEL-1;
 
 		if (NI_P1Score > config.NI_HighScore[9].score)
 		{
@@ -528,7 +525,7 @@
 
 	// cast to int16_t to simulate a bug in FT2
 	NI_P1Score += 0x10000 + (int16_t)((12 - NI_CurSpeed) * 0x2000);
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 		NI_P2Score += 0x10000;
 
 	NI_Level++;
@@ -536,7 +533,7 @@
 	if (NI_P1Lives < 99)
 		NI_P1Lives++;
 
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 	{
 		if (NI_P2Lives < 99)
 			NI_P2Lives++;
@@ -549,7 +546,7 @@
 	nibblesGenNewNumber();
 }
 
-void moveNibblePlayers(void)
+void moveNibblesPlayers(void)
 {
 	if (ui.sysReqShown || --NI_CurTick60Hz != 0)
 		return;
@@ -578,9 +575,9 @@
 		}
 	}
 
-	memmove(&NI_P1[1], &NI_P1[0], 255 * sizeof (nibbleCrd));
-	if (config.NI_AntPlayers == 1)
-		memmove(&NI_P2[1], &NI_P2[0], 255 * sizeof (nibbleCrd));
+	memmove(&NI_P1[1], &NI_P1[0], 255 * sizeof (nibblesCoord_t));
+	if (config.NI_NumPlayers == 1)
+		memmove(&NI_P2[1], &NI_P2[0], 255 * sizeof (nibblesCoord_t));
 
 	switch (NI_P1Dir)
 	{
@@ -591,7 +588,7 @@
 		default: break;
 	}
 
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 	{
 		switch (NI_P2Dir)
 		{
@@ -613,7 +610,7 @@
 	NI_P2[0].x %= 51;
 	NI_P2[0].y %= 23;
 
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 	{
 		if (nibblesInvalid(NI_P1[0].x, NI_P1[0].y, NI_P1Dir) && nibblesInvalid(NI_P2[0].x, NI_P2[0].y, NI_P2Dir))
 		{
@@ -651,10 +648,10 @@
 	{
 		NI_P1Score += (i & 15) * 999 * (NI_Level + 1);
 		nibblesEraseNumber(); j = 1;
-		NI_P1NoRens = NI_P1Len / 2;
+		NI_P1NoClear = NI_P1Len >> 1;
 	}
 
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 	{
 		i = NI_Screen[NI_P2[0].x][NI_P2[0].y];
 		if (i >= 16)
@@ -661,12 +658,12 @@
 		{
 			NI_P2Score += ((i & 15) * 999 * (NI_Level + 1));
 			nibblesEraseNumber(); j = 1;
-			NI_P2NoRens = NI_P2Len / 2;
+			NI_P2NoClear = NI_P2Len >> 1;
 		}
 	}
 
 	NI_P1Score -= 17;
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 		NI_P2Score -= 17;
 
 	if (NI_P1Score < 0) NI_P1Score = 0;
@@ -674,9 +671,9 @@
 
 	if (!config.NI_Surround)
 	{
-		if (NI_P1NoRens > 0 && NI_P1Len < 255)
+		if (NI_P1NoClear > 0 && NI_P1Len < 255)
 		{
-			NI_P1NoRens--;
+			NI_P1NoClear--;
 			NI_P1Len++;
 		}
 		else
@@ -684,11 +681,11 @@
 			setNibbleDot(NI_P1[NI_P1Len].x, NI_P1[NI_P1Len].y, 0);
 		}
 
-		if (config.NI_AntPlayers == 1)
+		if (config.NI_NumPlayers == 1)
 		{
-			if (NI_P2NoRens > 0 && NI_P2Len < 255)
+			if (NI_P2NoClear > 0 && NI_P2Len < 255)
 			{
-				NI_P2NoRens--;
+				NI_P2NoClear--;
 				NI_P2Len++;
 			}
 			else
@@ -699,7 +696,7 @@
 	}
 
 	setNibbleDot(NI_P1[0].x, NI_P1[0].y, 6);
-	if (config.NI_AntPlayers == 1)
+	if (config.NI_NumPlayers == 1)
 		setNibbleDot(NI_P2[0].x, NI_P2[0].y, 5);
 
 	if (j == 1 && !config.NI_Surround)
@@ -770,7 +767,7 @@
 	showCheckBox(CB_NIBBLES_WRAP);
 
 	uncheckRadioButtonGroup(RB_GROUP_NIBBLES_PLAYERS);
-	if (config.NI_AntPlayers == 0)
+	if (config.NI_NumPlayers == 0)
 		radioButtons[RB_NIBBLES_1PLAYER].state = RADIOBUTTON_CHECKED;
 	else
 		radioButtons[RB_NIBBLES_2PLAYERS].state = RADIOBUTTON_CHECKED;
@@ -821,7 +818,7 @@
 			return;
 	}
 
-	if (config.NI_Surround && config.NI_AntPlayers == 0)
+	if (config.NI_Surround && config.NI_NumPlayers == 0)
 	{
 		okBox(0, "Nibbles message", "Surround mode is not appropriate in one-player mode.");
 		return;
@@ -834,8 +831,8 @@
 	NI_CurSpeed = NI_Speeds[config.NI_Speed];
 
 	// adjust for 70Hz -> 60Hz frames
-	NI_CurSpeed60Hz = (uint8_t)round(NI_CurSpeed * ((double)VBLANK_HZ / FT2_VBLANK_HZ));
-	NI_CurTick60Hz = (uint8_t)round(NI_Speeds[2] * ((double)VBLANK_HZ / FT2_VBLANK_HZ));
+	NI_CurSpeed60Hz = (uint8_t)SCALE_VBLANK_DELTA(NI_CurSpeed);
+	NI_CurTick60Hz = (uint8_t)SCALE_VBLANK_DELTA(NI_Speeds[2]);
 
 	editor.NI_Play = true;
 	NI_P1Score = 0;
@@ -882,13 +879,13 @@
 
 void nibblesSet1Player(void)
 {
-	config.NI_AntPlayers = 0;
+	config.NI_NumPlayers = 0;
 	checkRadioButton(RB_NIBBLES_1PLAYER);
 }
 
 void nibblesSet2Players(void)
 {
-	config.NI_AntPlayers = 1;
+	config.NI_NumPlayers = 1;
 	checkRadioButton(RB_NIBBLES_2PLAYERS);
 }
 
--- a/src/ft2_nibbles.h
+++ b/src/ft2_nibbles.h
@@ -5,7 +5,7 @@
 #include <SDL2/SDL.h>
 
 void nibblesKeyAdministrator(SDL_Scancode scancode);
-void moveNibblePlayers(void);
+void moveNibblesPlayers(void);
 void showNibblesScreen(void);
 void hideNibblesScreen(void);
 void exitNibblesScreen(void);
--- a/src/ft2_palette.c
+++ b/src/ft2_palette.c
@@ -1,5 +1,6 @@
 #include <stdint.h>
 #include <stdbool.h>
+#include <math.h>
 #include "ft2_palette.h"
 #include "ft2_gui.h"
 #include "ft2_config.h"
@@ -7,7 +8,7 @@
 #include "ft2_palette.h"
 #include "ft2_tables.h"
 
-uint8_t cfg_ColorNr = 0; // globalized
+uint8_t cfg_ColorNum = 0; // globalized
 
 static uint8_t cfg_Red, cfg_Green, cfg_Blue, cfg_Contrast;
 
@@ -27,7 +28,7 @@
 
 void setPal16(pal16 *p, bool redrawScreen)
 {
-#define LOOP_PIN_COL_SUB 96
+#define LOOP_PIN_COL_SUB 110
 #define TEXT_MARK_COLOR 0x0078D7
 #define BOX_SELECT_COLOR 0x7F7F7F
 
@@ -89,12 +90,12 @@
 
 uint8_t palMax(int32_t c)
 {
-	return (uint8_t)(CLAMP(c, 0, 63));
+	return (uint8_t)CLAMP(c, 0, 63);
 }
 
 static void drawCurrentPaletteColor(void)
 {
-	const uint8_t palIndex = FTC_EditOrder[cfg_ColorNr];
+	const uint8_t palIndex = FTC_EditOrder[cfg_ColorNum];
 
 	const uint8_t r = P6_TO_P8(cfg_Red);
 	const uint8_t g = P6_TO_P8(cfg_Green);
@@ -108,14 +109,14 @@
 
 static void updatePaletteEditor(void)
 {
-	const uint8_t nr = FTC_EditOrder[cfg_ColorNr];
+	const uint8_t colorNum = FTC_EditOrder[cfg_ColorNum];
 
-	cfg_Red = palTable[config.cfg_StdPalNr][nr].r;
-	cfg_Green = palTable[config.cfg_StdPalNr][nr].g;
-	cfg_Blue = palTable[config.cfg_StdPalNr][nr].b;
+	cfg_Red = palTable[config.cfg_StdPalNum][colorNum].r;
+	cfg_Green = palTable[config.cfg_StdPalNum][colorNum].g;
+	cfg_Blue = palTable[config.cfg_StdPalNum][colorNum].b;
 
-	if (cfg_ColorNr == 4 || cfg_ColorNr == 5)
-		cfg_Contrast = palContrast[config.cfg_StdPalNr][cfg_ColorNr-4];
+	if (cfg_ColorNum == 4 || cfg_ColorNum == 5)
+		cfg_Contrast = palContrast[config.cfg_StdPalNum][cfg_ColorNum-4];
 	else
 		cfg_Contrast = 0;
 
@@ -129,7 +130,7 @@
 
 static void paletteDragMoved(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 	{
 		updatePaletteEditor(); // resets colors/contrast vars
 		showColorErrorMsg();
@@ -136,7 +137,7 @@
 		return;
 	}
 
-	if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 	{
 		updatePaletteEditor(); // resets colors/contrast vars
 		showMouseColorErrorMsg();
@@ -143,14 +144,14 @@
 		return;
 	}
 
-	const uint8_t nr = FTC_EditOrder[cfg_ColorNr];
-	const uint8_t p = (uint8_t)config.cfg_StdPalNr;
+	const uint8_t colorNum = FTC_EditOrder[cfg_ColorNum];
+	const uint8_t p = (uint8_t)config.cfg_StdPalNum;
 
-	palTable[p][nr].r = cfg_Red;
-	palTable[p][nr].g = cfg_Green;
-	palTable[p][nr].b = cfg_Blue;
+	palTable[p][colorNum].r = cfg_Red;
+	palTable[p][colorNum].g = cfg_Green;
+	palTable[p][colorNum].b = cfg_Blue;
 
-	if (cfg_ColorNr == 4 || cfg_ColorNr == 5)
+	if (cfg_ColorNum == 4 || cfg_ColorNum == 5)
 	{
 		double dRed = cfg_Red;
 		double dGreen = cfg_Green;
@@ -164,7 +165,7 @@
 
 		for (int32_t i = 0; i < 3; i++)
 		{
-			const int32_t k = scaleOrder[i] + (cfg_ColorNr - 4) * 2;
+			const int32_t k = scaleOrder[i] + (cfg_ColorNum - 4) * 2;
 
 			double dMul = palPow((i + 1) * (1.0 / 2.0), dContrast);
 
@@ -173,7 +174,7 @@
 			palTable[p][k].b = palMax((int32_t)((dBlue * dMul) + 0.5));
 		}
 
-		palContrast[p][cfg_ColorNr-4] = cfg_Contrast;
+		palContrast[p][cfg_ColorNum-4] = cfg_Contrast;
 	}
 	else
 	{
@@ -186,7 +187,7 @@
 
 	setScrollBarPos(SB_PAL_CONTRAST, cfg_Contrast, false);
 
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	drawCurrentPaletteColor();
 }
 
@@ -228,9 +229,9 @@
 
 void configPalRDown(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollLeft(SB_PAL_R, 1);
@@ -238,9 +239,9 @@
 
 void configPalRUp(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollRight(SB_PAL_R, 1);
@@ -248,9 +249,9 @@
 
 void configPalGDown(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollLeft(SB_PAL_G, 1);
@@ -258,9 +259,9 @@
 
 void configPalGUp(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollRight(SB_PAL_G, 1);
@@ -268,9 +269,9 @@
 
 void configPalBDown(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollLeft(SB_PAL_B, 1);
@@ -278,9 +279,9 @@
 
 void configPalBUp(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollRight(SB_PAL_B, 1);
@@ -288,9 +289,9 @@
 
 void configPalContDown(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollLeft(SB_PAL_CONTRAST, 1);
@@ -298,9 +299,9 @@
 
 void configPalContUp(void)
 {
-	if (config.cfg_StdPalNr != PAL_USER_DEFINED)
+	if (config.cfg_StdPalNum != PAL_USER_DEFINED)
 		showColorErrorMsg();
-	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3)
+	else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3)
 		showMouseColorErrorMsg();
 	else
 		scrollBarScrollRight(SB_PAL_CONTRAST, 1);
@@ -334,7 +335,7 @@
 
 void rbConfigPalPatternText(void)
 {
-	cfg_ColorNr = 0;
+	cfg_ColorNum = 0;
 	checkRadioButton(RB_CONFIG_PAL_PATTERNTEXT);
 	updatePaletteEditor();
 }
@@ -341,7 +342,7 @@
 
 void rbConfigPalBlockMark(void)
 {
-	cfg_ColorNr = 1;
+	cfg_ColorNum = 1;
 	checkRadioButton(RB_CONFIG_PAL_BLOCKMARK);
 	updatePaletteEditor();
 }
@@ -348,7 +349,7 @@
 
 void rbConfigPalTextOnBlock(void)
 {
-	cfg_ColorNr = 2;
+	cfg_ColorNum = 2;
 	checkRadioButton(RB_CONFIG_PAL_TEXTONBLOCK);
 	updatePaletteEditor();
 }
@@ -355,7 +356,7 @@
 
 void rbConfigPalMouse(void)
 {
-	cfg_ColorNr = 3;
+	cfg_ColorNum = 3;
 	checkRadioButton(RB_CONFIG_PAL_MOUSE);
 	updatePaletteEditor();
 }
@@ -362,7 +363,7 @@
 
 void rbConfigPalDesktop(void)
 {
-	cfg_ColorNr = 4;
+	cfg_ColorNum = 4;
 	checkRadioButton(RB_CONFIG_PAL_DESKTOP);
 	updatePaletteEditor();
 }
@@ -369,7 +370,7 @@
 
 void rbConfigPalButttons(void)
 {
-	cfg_ColorNr = 5;
+	cfg_ColorNum = 5;
 	checkRadioButton(RB_CONFIG_PAL_BUTTONS);
 	updatePaletteEditor();
 }
@@ -376,96 +377,96 @@
 
 void rbConfigPalArctic(void)
 {
-	config.cfg_StdPalNr = PAL_ARCTIC;
+	config.cfg_StdPalNum = PAL_ARCTIC;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_ARCTIC);
 }
 
 void rbConfigPalLitheDark(void)
 {
-	config.cfg_StdPalNr = PAL_LITHE_DARK;
+	config.cfg_StdPalNum = PAL_LITHE_DARK;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_LITHE_DARK);
 }
 
 void rbConfigPalAuroraBorealis(void)
 {
-	config.cfg_StdPalNr = PAL_AURORA_BOREALIS;
+	config.cfg_StdPalNum = PAL_AURORA_BOREALIS;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_AURORA_BOREALIS);
 }
 
 void rbConfigPalRose(void)
 {
-	config.cfg_StdPalNr = PAL_ROSE;
+	config.cfg_StdPalNum = PAL_ROSE;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_ROSE);
 }
 
 void rbConfigPalBlues(void)
 {
-	config.cfg_StdPalNr = PAL_BLUES;
+	config.cfg_StdPalNum = PAL_BLUES;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_BLUES);
 }
 
 void rbConfigPalDarkMode(void)
 {
-	config.cfg_StdPalNr = PAL_DARK_MODE;
+	config.cfg_StdPalNum = PAL_DARK_MODE;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_DARK_MODE);
 }
 
 void rbConfigPalGold(void)
 {
-	config.cfg_StdPalNr = PAL_GOLD;
+	config.cfg_StdPalNum = PAL_GOLD;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_GOLD);
 }
 
 void rbConfigPalViolent(void)
 {
-	config.cfg_StdPalNr = PAL_VIOLENT;
+	config.cfg_StdPalNum = PAL_VIOLENT;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_VIOLENT);
 }
 
 void rbConfigPalHeavyMetal(void)
 {
-	config.cfg_StdPalNr = PAL_HEAVY_METAL;
+	config.cfg_StdPalNum = PAL_HEAVY_METAL;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_HEAVY_METAL);
 }
 
 void rbConfigPalWhyColors(void)
 {
-	config.cfg_StdPalNr = PAL_WHY_COLORS;
+	config.cfg_StdPalNum = PAL_WHY_COLORS;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_WHY_COLORS);
 }
 
 void rbConfigPalJungle(void)
 {
-	config.cfg_StdPalNr = PAL_JUNGLE;
+	config.cfg_StdPalNum = PAL_JUNGLE;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_JUNGLE);
 }
 
 void rbConfigPalUserDefined(void)
 {
-	config.cfg_StdPalNr = PAL_USER_DEFINED;
+	config.cfg_StdPalNum = PAL_USER_DEFINED;
 	updatePaletteEditor();
-	setPal16(palTable[config.cfg_StdPalNr], true);
+	setPal16(palTable[config.cfg_StdPalNum], true);
 	checkRadioButton(RB_CONFIG_PAL_USER_DEFINED);
 }
--- a/src/ft2_palette.h
+++ b/src/ft2_palette.h
@@ -49,8 +49,6 @@
 	uint8_t r, g, b;
 } pal16;
 
-extern uint8_t cfg_ColorNr;
-
 void setCustomPalColor(uint32_t color);
 
 uint8_t palMax(int32_t c);
@@ -88,3 +86,5 @@
 void rbConfigPalWhyColors(void);
 void rbConfigPalJungle(void);
 void rbConfigPalUserDefined(void);
+
+extern uint8_t cfg_ColorNum;
--- a/src/ft2_pattern_draw.c
+++ b/src/ft2_pattern_draw.c
@@ -14,7 +14,7 @@
 #include "ft2_bmp.h"
 #include "ft2_structs.h"
 
-static tonTyp emptyPattern[MAX_VOICES * MAX_PATT_LEN];
+static note_t emptyPattern[MAX_CHANNELS * MAX_PATT_LEN];
 
 static const uint8_t *font4Ptr, *font5Ptr;
 static const uint8_t vol2charTab1[16] = { 39, 0, 1, 2, 3, 4, 36, 52, 53, 54, 28, 31, 25, 58, 59, 22 };
@@ -36,13 +36,13 @@
 static void pattCharOut(uint32_t xPos, uint32_t yPos, uint8_t chr, uint8_t fontType, uint32_t color);
 static void drawEmptyNoteSmall(uint32_t xPos, uint32_t yPos, uint32_t color);
 static void drawKeyOffSmall(uint32_t xPos, uint32_t yPos, uint32_t color);
-static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color);
+static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color);
 static void drawEmptyNoteMedium(uint32_t xPos, uint32_t yPos, uint32_t color);
 static void drawKeyOffMedium(uint32_t xPos, uint32_t yPos, uint32_t color);
-static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color);
+static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color);
 static void drawEmptyNoteBig(uint32_t xPos, uint32_t yPos, uint32_t color);
 static void drawKeyOffBig(uint32_t xPos, uint32_t yPos, uint32_t color);
-static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color);
+static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color);
 
 void updatePattFontPtrs(void)
 {
@@ -54,7 +54,7 @@
 void drawPatternBorders(void)
 {
 	// get heights/pos/rows depending on configuration
-	const pattCoord2_t *pattCoord = &pattCoord2Table[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended];
+	const pattCoord2_t *pattCoord = &pattCoord2Table[config.ptnStretch][ui.pattChanScrollShown][ui.extended];
 
 	// set pattern cursor Y position
 	editor.ptnCursorY = pattCoord->lowerRowsY - 9;
@@ -66,7 +66,7 @@
 	// in some configurations, there will be two empty channels to the right, fix that
 	if (chans == 2)
 		chans = 4;
-	else if (chans == 10 && !config.ptnS3M)
+	else if (chans == 10 && !config.ptnShowVolColumn)
 		chans = 12;
 
 	assert(chans >= 2 && chans <= 12);
@@ -177,7 +177,7 @@
 
 static void writeCursor(void)
 {
-	const int32_t tabOffset = (config.ptnS3M * 32) + (columnModeTab[ui.numChannelsShown-1] * 8) + cursor.object;
+	const int32_t tabOffset = (config.ptnShowVolColumn * 32) + (columnModeTab[ui.numChannelsShown-1] * 8) + cursor.object;
 
 	int32_t xPos = pattCursorXTab[tabOffset];
 	const int32_t width = pattCursorWTab[tabOffset];
@@ -212,7 +212,7 @@
 	if (pattMark.markX1 > endCh || pattMark.markX2 < startCh || pattMark.markY1 > endRow || pattMark.markY2 < startRow)
 		return;
 
-	const markCoord_t *markCoord = &markCoordTable[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended];
+	const markCoord_t *markCoord = &markCoordTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended];
 	const int32_t pattYStart = markCoord->upperRowsY;
 
 	// X1
@@ -264,7 +264,7 @@
 	}
 
 	// kludge! (some mark situations could overwrite illegal areas)
-	if (config.ptnUnpressed && ui.pattChanScrollShown)
+	if (config.ptnStretch && ui.pattChanScrollShown)
 	{
 		if (y1 == pattCoord->upperRowsY-1 || y1 == pattCoord->lowerRowsY-1)
 			y1++;
@@ -368,29 +368,29 @@
 
 // DRAWING ROUTINES (WITH VOLUME COLUMN)
 
-static void showNoteNum(uint32_t xPos, uint32_t yPos, int16_t ton, uint32_t color)
+static void showNoteNum(uint32_t xPos, uint32_t yPos, int16_t note, uint32_t color)
 {
 	xPos += 3;
 
-	assert(ton >= 0 && ton <= 97);
+	assert(note >= 0 && note <= 97);
 
 	if (ui.numChannelsShown <= 4)
 	{
-		if (ton <= 0 || ton > 97)
+		if (note <= 0 || note > 97)
 			drawEmptyNoteBig(xPos, yPos, color);
-		else if (ton == 97)
+		else if (note == NOTE_OFF)
 			drawKeyOffBig(xPos, yPos, color);
 		else
-			drawNoteBig(xPos, yPos, ton, color);
+			drawNoteBig(xPos, yPos, note, color);
 	}
 	else
 	{
-		if (ton <= 0 || ton > 97)
+		if (note <= 0 || note > 97)
 			drawEmptyNoteMedium(xPos, yPos, color);
-		else if (ton == 97)
+		else if (note == NOTE_OFF)
 			drawKeyOffMedium(xPos, yPos, color);
 		else
-			drawNoteMedium(xPos, yPos, ton, color);
+			drawNoteMedium(xPos, yPos, note, color);
 	}
 }
 
@@ -480,7 +480,7 @@
 	pattCharOut(xPos + charW, yPos, char2, fontType, color);
 }
 
-static void showEfx(uint32_t xPos, uint32_t yPos, uint8_t effTyp, uint8_t eff, uint32_t color)
+static void showEfx(uint32_t xPos, uint32_t yPos, uint8_t efx, uint8_t efxData, uint32_t color)
 {
 	uint8_t fontType, charW;
 
@@ -503,45 +503,45 @@
 		xPos += 55;
 	}
 
-	pattCharOut(xPos,               yPos, effTyp,     fontType, color);
-	pattCharOut(xPos +  charW,      yPos, eff >> 4,   fontType, color);
-	pattCharOut(xPos + (charW * 2), yPos, eff & 0x0F, fontType, color);
+	pattCharOut(xPos,               yPos, efx,            fontType, color);
+	pattCharOut(xPos +  charW,      yPos, efxData >> 4,   fontType, color);
+	pattCharOut(xPos + (charW * 2), yPos, efxData & 0x0F, fontType, color);
 }
 
 // DRAWING ROUTINES (WITHOUT VOLUME COLUMN)
 
-static void showNoteNumNoVolColumn(uint32_t xPos, uint32_t yPos, int16_t ton, uint32_t color)
+static void showNoteNumNoVolColumn(uint32_t xPos, uint32_t yPos, int16_t note, uint32_t color)
 {
 	xPos += 3;
 
-	assert(ton >= 0 && ton <= 97);
+	assert(note >= 0 && note <= 97);
 
 	if (ui.numChannelsShown <= 6)
 	{
-		if (ton <= 0 || ton > 97)
+		if (note <= 0 || note > 97)
 			drawEmptyNoteBig(xPos, yPos, color);
-		else if (ton == 97)
+		else if (note == NOTE_OFF)
 			drawKeyOffBig(xPos, yPos, color);
 		else
-			drawNoteBig(xPos, yPos, ton, color);
+			drawNoteBig(xPos, yPos, note, color);
 	}
 	else if (ui.numChannelsShown <= 8)
 	{
-		if (ton <= 0 || ton > 97)
+		if (note <= 0 || note > 97)
 			drawEmptyNoteMedium(xPos, yPos, color);
-		else if (ton == 97)
+		else if (note == NOTE_OFF)
 			drawKeyOffMedium(xPos, yPos, color);
 		else
-			drawNoteMedium(xPos, yPos, ton, color);
+			drawNoteMedium(xPos, yPos, note, color);
 	}
 	else
 	{
-		if (ton <= 0 || ton > 97)
+		if (note <= 0 || note > 97)
 			drawEmptyNoteSmall(xPos, yPos, color);
-		else if (ton == 97)
+		else if (note == NOTE_OFF)
 			drawKeyOffSmall(xPos, yPos, color);
 		else
-			drawNoteSmall(xPos, yPos, ton, color);
+			drawNoteSmall(xPos, yPos, note, color);
 	}
 }
 
@@ -600,7 +600,7 @@
 	(void)color;
 }
 
-static void showEfxNoVolColumn(uint32_t xPos, uint32_t yPos, uint8_t effTyp, uint8_t eff, uint32_t color)
+static void showEfxNoVolColumn(uint32_t xPos, uint32_t yPos, uint8_t efx, uint8_t efxData, uint32_t color)
 {
 	uint8_t charW, fontType;
 
@@ -629,12 +629,12 @@
 		xPos += 31;
 	}
 
-	pattCharOut(xPos,               yPos, effTyp,     fontType, color);
-	pattCharOut(xPos +  charW,      yPos, eff >> 4,   fontType, color);
-	pattCharOut(xPos + (charW * 2), yPos, eff & 0x0F, fontType, color);
+	pattCharOut(xPos,               yPos, efx,            fontType, color);
+	pattCharOut(xPos +  charW,      yPos, efxData >> 4,   fontType, color);
+	pattCharOut(xPos + (charW * 2), yPos, efxData & 0x0F, fontType, color);
 }
 
-void writePattern(int32_t currRow, int32_t pattern)
+void writePattern(int32_t currRow, int32_t currPattern)
 {
 	uint32_t noteTextColors[2];
 
@@ -663,8 +663,8 @@
 	ui.patternChannelWidth = (uint16_t)(chanWidth + 3);
 
 	// get heights/pos/rows depending on configuration
-	uint32_t rowHeight = config.ptnUnpressed ? 11 : 8;
-	const pattCoord_t *pattCoord = &pattCoordTable[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended];
+	uint32_t rowHeight = config.ptnStretch ? 11 : 8;
+	const pattCoord_t *pattCoord = &pattCoordTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended];
 	const int32_t midRowTextY = pattCoord->midRowTextY;
 	const int32_t lowerRowsTextY = pattCoord->lowerRowsTextY;
 	int32_t row = currRow - pattCoord->numUpperRows;
@@ -672,17 +672,15 @@
 	int32_t textY = pattCoord->upperRowsTextY;
 	const int32_t afterCurrRow = currRow + 1;
 	const int32_t numChannels = ui.numChannelsShown;
-	tonTyp *pattPtr = patt[pattern];
-	const int32_t numRows = pattLens[pattern];
+	note_t *pattPtr = pattern[currPattern];
+	const int32_t numRows = patternNumRows[currPattern];
 
-
-
 	// increment pattern data pointer by horizontal scrollbar offset/channel
 	if (pattPtr != NULL)
 		pattPtr += ui.channelOffset;
 
 	// set up function pointers for drawing
-	if (config.ptnS3M)
+	if (config.ptnShowVolColumn)
 	{
 		drawNote = showNoteNum;
 		drawInst = showInstrNum;
@@ -709,19 +707,17 @@
 
 			drawRowNums(textY, (uint8_t)row, selectedRowFlag);
 
-			const tonTyp *note = (pattPtr == NULL) ? emptyPattern : &pattPtr[(uint32_t)row * MAX_VOICES];
+			const note_t *p = (pattPtr == NULL) ? emptyPattern : &pattPtr[(uint32_t)row * MAX_CHANNELS];
 			const int32_t xWidth = ui.patternChannelWidth;
 			const uint32_t color = noteTextColors[selectedRowFlag];
 
 			int32_t xPos = 29;
-			for (int32_t j = 0; j < numChannels; j++, note++)
+			for (int32_t j = 0; j < numChannels; j++, p++, xPos += xWidth)
 			{
-				drawNote(xPos, textY, note->ton, color);
-				drawInst(xPos, textY, note->instr, color);
-				drawVolEfx(xPos, textY, note->vol, color);
-				drawEfx(xPos, textY, note->effTyp, note->eff, color);
-
-				xPos += xWidth;
+				drawNote(xPos, textY, p->note, color);
+				drawInst(xPos, textY, p->instr, color);
+				drawVolEfx(xPos, textY, p->vol, color);
+				drawEfx(xPos, textY, p->efx, p->efxData, color);
 			}
 		}
 
@@ -932,15 +928,14 @@
 	}
 }
 
-static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color)
+static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color)
 {
 	uint32_t char1, char2;
 
-	assert(ton >= 1 && ton <= 97);
-	ton--;
+	noteNum--;
 
-	const uint8_t note = noteTab1[ton];
-	const uint32_t char3 = noteTab2[ton] * FONT7_CHAR_W;
+	const uint8_t note = noteTab1[noteNum];
+	const uint32_t char3 = noteTab2[noteNum] * FONT7_CHAR_W;
 
 	if (config.ptnAcc == 0)
 	{
@@ -1039,14 +1034,14 @@
 	}
 }
 
-static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color)
+static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color)
 {
 	uint32_t char1, char2;
 
-	ton--;
+	noteNum--;
 
-	const uint8_t note = noteTab1[ton];
-	const uint32_t char3 = noteTab2[ton] * FONT4_CHAR_W;
+	const uint8_t note = noteTab1[noteNum];
+	const uint32_t char3 = noteTab2[noteNum] * FONT4_CHAR_W;
 
 	if (config.ptnAcc == 0)
 	{
@@ -1145,14 +1140,14 @@
 	}
 }
 
-static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color)
+static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color)
 {
 	uint32_t char1, char2;
 
-	ton--;
+	noteNum--;
 
-	const uint8_t note = noteTab1[ton];
-	const uint32_t char3 = noteTab2[ton] * FONT5_CHAR_W;
+	const uint8_t note = noteTab1[noteNum];
+	const uint32_t char3 = noteTab2[noteNum] * FONT5_CHAR_W;
 
 	if (config.ptnAcc == 0)
 	{
--- a/src/ft2_pattern_draw.h
+++ b/src/ft2_pattern_draw.h
@@ -4,5 +4,5 @@
 
 void updatePattFontPtrs(void);
 void drawPatternBorders(void);
-void writePattern(int32_t currRow, int32_t pattern);
+void writePattern(int32_t currRow, int32_t currPattern);
 void pattTwoHexOut(uint32_t xPos, uint32_t yPos, uint8_t val, uint32_t color);
--- a/src/ft2_pattern_ed.c
+++ b/src/ft2_pattern_ed.c
@@ -13,7 +13,7 @@
 #include "ft2_sample_ed.h"
 #include "ft2_pattern_draw.h"
 #include "ft2_inst_ed.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_diskop.h"
 #include "ft2_audio.h"
 #include "ft2_wav_renderer.h"
@@ -32,7 +32,7 @@
 // for pattern marking w/ mouse
 static int32_t lastMarkX1 = -1, lastMarkX2 = -1, lastMarkY1 = -1, lastMarkY2 = -1;
 
-static const uint8_t ptnAntLine[8] = { 27, 25, 20, 19, 42, 40, 31, 30 };
+static const uint8_t ptnNumRows[8] = { 27, 25, 20, 19, 42, 40, 31, 30 };
 static const uint8_t ptnLineSub[8] = { 13, 12,  9,  9, 20, 19, 15, 14 };
 static const uint8_t iSwitchExtW[4] = { 40, 40, 40, 39 };
 static const uint8_t iSwitchExtY[8] = { 2, 2, 2, 2, 19, 19, 19, 19 };
@@ -42,13 +42,13 @@
 static int32_t lastMouseX, lastMouseY;
 static int32_t last_TimeH, last_TimeM, last_TimeS;
 
-bool allocatePattern(uint16_t nr) // for tracker use only, not in loader!
+bool allocatePattern(uint16_t pattNum) // for tracker use only, not in loader!
 {
 	const bool audioWasntLocked = !audio.locked;
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (patt[nr] == NULL)
+	if (pattern[pattNum] == NULL)
 	{
 		/* Original FT2 allocates only the amount of rows needed, but we don't
 		** do that to avoid out of bondary row look-up between out-of-sync replayer
@@ -57,8 +57,8 @@
 		** patterns would be ~10MB.
 		**/
 
-		patt[nr] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
-		if (patt[nr] == NULL)
+		pattern[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
+		if (pattern[pattNum] == NULL)
 		{
 			if (audioWasntLocked)
 				unlockAudio();
@@ -66,7 +66,7 @@
 			return false;
 		}
 
-		song.pattLen = pattLens[nr];
+		song.currNumRows = patternNumRows[pattNum];
 	}
 
 	if (audioWasntLocked)
@@ -75,18 +75,18 @@
 	return true;
 }
 
-void killPatternIfUnused(uint16_t nr) // for tracker use only, not in loader!
+void killPatternIfUnused(uint16_t pattNum) // for tracker use only, not in loader!
 {
 	const bool audioWasntLocked = !audio.locked;
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (patternEmpty(nr))
+	if (patternEmpty(pattNum))
 	{
-		if (patt[nr] != NULL)
+		if (pattern[pattNum] != NULL)
 		{
-			free(patt[nr]);
-			patt[nr] = NULL;
+			free(pattern[pattNum]);
+			pattern[pattNum] = NULL;
 		}
 	}
 
@@ -97,7 +97,7 @@
 uint8_t getMaxVisibleChannels(void)
 {
 	assert(config.ptnMaxChannels >= 0 && config.ptnMaxChannels <= 3);
-	if (config.ptnS3M)
+	if (config.ptnShowVolColumn)
 		return maxVisibleChans1[config.ptnMaxChannels];
 	else
 		return maxVisibleChans2[config.ptnMaxChannels];
@@ -360,9 +360,9 @@
 
 	if (cursor.ch == 0)
 	{
-		cursor.ch = (uint8_t)(song.antChn - 1);
+		cursor.ch = (uint8_t)(song.numChannels - 1);
 		if (ui.pattChanScrollShown)
-			setScrollBarPos(SB_CHAN_SCROLL, song.antChn, true);
+			setScrollBarPos(SB_CHAN_SCROLL, song.numChannels, true);
 	}
 	else
 	{
@@ -379,7 +379,7 @@
 {
 	cursor.object = CURSOR_NOTE;
 
-	if (cursor.ch >= song.antChn-1)
+	if (cursor.ch >= song.numChannels-1)
 	{
 		cursor.ch = 0;
 		if (ui.pattChanScrollShown)
@@ -427,7 +427,7 @@
 {
 	cursor.object--;
 
-	if (!config.ptnS3M)
+	if (!config.ptnShowVolColumn)
 	{
 		while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
 			cursor.object--;
@@ -446,7 +446,7 @@
 {
 	cursor.object++;
 
-	if (!config.ptnS3M)
+	if (!config.ptnShowVolColumn)
 	{
 		while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
 			cursor.object++;
@@ -665,7 +665,7 @@
 	showInstrumentSwitcher();
 
 	drawSongLength();
-	drawSongRepS();
+	drawSongLoopStart();
 	drawEditPattern(editor.editPattern);
 	drawPatternLength(editor.editPattern);
 	drawPosEdNums(editor.songPos);
@@ -728,11 +728,11 @@
 
 void checkMarkLimits(void)
 {
-	const uint16_t limitY = pattLens[editor.editPattern];
+	const uint16_t limitY = patternNumRows[editor.editPattern];
 	pattMark.markY1 = CLAMP(pattMark.markY1, 0, limitY);
 	pattMark.markY2 = CLAMP(pattMark.markY2, 0, limitY);
 
-	const uint16_t limitX = (uint16_t)(song.antChn - 1);
+	const uint16_t limitX = (uint16_t)(song.numChannels - 1);
 	pattMark.markX1 = CLAMP(pattMark.markX1, 0, limitX);
 	pattMark.markX2 = CLAMP(pattMark.markX2, 0, limitX);
 
@@ -756,8 +756,8 @@
 	ch = CLAMP(ch, 0, chEnd);
 
 	// in some setups there can be non-used channels to the right, do clamping
-	if (ch >= song.antChn)
-		ch = (int8_t)(song.antChn - 1);
+	if (ch >= song.numChannels)
+		ch = (int8_t)(song.numChannels - 1);
 
 	return ch;
 }
@@ -764,19 +764,19 @@
 
 static int16_t mouseYToRow(void) // used to get row num from mouse y (for pattern marking)
 {
-	const pattCoordsMouse_t *pattCoordsMouse = &pattCoordMouseTable[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended];
+	const pattCoordsMouse_t *pattCoordsMouse = &pattCoordMouseTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended];
 
 	// clamp mouse y to boundaries
 	const int16_t maxY = ui.pattChanScrollShown ? 382 : 396;
 	const int16_t my = (int16_t)CLAMP(mouse.y, pattCoordsMouse->upperRowsY, maxY);
 
-	const uint8_t charHeight = config.ptnUnpressed ? 11 : 8;
+	const uint8_t charHeight = config.ptnStretch ? 11 : 8;
 
 	// test top/middle/bottom rows
 	if (my < pattCoordsMouse->midRowY)
 	{
 		// top rows
-		int16_t row = editor.pattPos - (pattCoordsMouse->numUpperRows - ((my - pattCoordsMouse->upperRowsY) / charHeight));
+		int16_t row = editor.row - (pattCoordsMouse->numUpperRows - ((my - pattCoordsMouse->upperRowsY) / charHeight));
 		if (row < 0)
 			row = 0;
 
@@ -785,22 +785,22 @@
 	else if (my >= pattCoordsMouse->midRowY && my <= pattCoordsMouse->midRowY+10)
 	{
 		// current row (middle)
-		return editor.pattPos;
+		return editor.row;
 	}
 	else
 	{
 		// bottom rows
-		int16_t row = (editor.pattPos + 1) + ((my - pattCoordsMouse->lowerRowsY) / charHeight);
+		int16_t row = (editor.row + 1) + ((my - pattCoordsMouse->lowerRowsY) / charHeight);
 
 		// prevent being able to mark the next unseen row on the bottom (in some configurations)
-		const uint8_t mode = (ui.extended * 4) + (config.ptnUnpressed * 2) + ui.pattChanScrollShown;
+		const uint8_t mode = (ui.extended * 4) + (config.ptnStretch * 2) + ui.pattChanScrollShown;
 
-		const int16_t maxRow = (ptnAntLine[mode] + (editor.pattPos - ptnLineSub[mode])) - 1;
+		const int16_t maxRow = (ptnNumRows[mode] + (editor.row - ptnLineSub[mode])) - 1;
 		if (row > maxRow)
 			row = maxRow;
 
 		// clamp to pattern length
-		const int16_t patternLen = pattLens[editor.editPattern];
+		const int16_t patternLen = patternNumRows[editor.editPattern];
 		if (row >= patternLen)
 			row = patternLen - 1;
 
@@ -897,8 +897,8 @@
 
 		if (mouse.y < y1)
 		{
-			if (editor.pattPos > 0)
-				setPos(-1, editor.pattPos - 1, true);
+			if (editor.row > 0)
+				setPos(-1, editor.row - 1, true);
 
 			forceMarking = true;
 			ui.updatePatternEditor = true;
@@ -905,9 +905,9 @@
 		}
 		else if (mouse.y > y2)
 		{
-			const int16_t pattLen = pattLens[editor.editPattern];
-			if (editor.pattPos < pattLen-1)
-				setPos(-1, editor.pattPos + 1, true);
+			const int16_t numRows = patternNumRows[editor.editPattern];
+			if (editor.row < numRows-1)
+				setPos(-1, editor.row + 1, true);
 
 			forceMarking = true;
 			ui.updatePatternEditor = true;
@@ -948,10 +948,10 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	song.pattPos = (song.pattPos - 1 + song.pattLen) % song.pattLen;
+	song.row = (song.row - 1 + song.currNumRows) % song.currNumRows;
 	if (!songPlaying)
 	{
-		editor.pattPos = (uint8_t)song.pattPos;
+		editor.row = (uint8_t)song.row;
 		ui.updatePatternEditor = true;
 	}
 
@@ -967,12 +967,12 @@
 
 	if (songPlaying)
 	{
-		song.timer = 2;
+		song.tick = 2;
 	}
 	else
 	{
-		song.pattPos = (song.pattPos + 1 + song.pattLen) % song.pattLen;
-		editor.pattPos = (uint8_t)song.pattPos;
+		song.row = (song.row + 1 + song.currNumRows) % song.currNumRows;
+		editor.row = (uint8_t)song.row;
 		ui.updatePatternEditor = true;
 	}
 
@@ -986,13 +986,13 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	song.pattPos -= amount;
-	if (song.pattPos < 0)
-		song.pattPos = 0;
+	song.row -= amount;
+	if (song.row < 0)
+		song.row = 0;
 
 	if (!songPlaying)
 	{
-		editor.pattPos = (uint8_t)song.pattPos;
+		editor.row = (uint8_t)song.row;
 		ui.updatePatternEditor = true;
 	}
 
@@ -1006,13 +1006,13 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	song.pattPos += amount;
-	if (song.pattPos >= song.pattLen)
-		song.pattPos = song.pattLen - 1;
+	song.row += amount;
+	if (song.row >= song.currNumRows)
+		song.row = song.currNumRows - 1;
 
 	if (!songPlaying)
 	{
-		editor.pattPos = (uint8_t)song.pattPos;
+		editor.row = (uint8_t)song.row;
 		ui.updatePatternEditor = true;
 	}
 
@@ -1023,30 +1023,30 @@
 void keybPattMarkUp(void)
 {
 	int8_t xPos = cursor.ch;
-	int16_t pattPos = editor.pattPos;
+	int16_t row = editor.row;
 
 	if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
 	{
 		pattMark.markX1 = xPos;
 		pattMark.markX2 = xPos;
-		pattMark.markY1 = pattPos;
-		pattMark.markY2 = pattPos + 1;
+		pattMark.markY1 = row;
+		pattMark.markY2 = row + 1;
 	}
 
-	if (pattPos == pattMark.markY1-1)
+	if (row == pattMark.markY1-1)
 	{
-		pattMark.markY1 = pattPos;
+		pattMark.markY1 = row;
 	}
-	else if (pattPos == pattMark.markY2)
+	else if (row == pattMark.markY2)
 	{
-		pattMark.markY2 = pattPos - 1;
+		pattMark.markY2 = row - 1;
 	}
-	else if (pattPos != pattMark.markY1 && pattPos != pattMark.markY2)
+	else if (row != pattMark.markY1 && row != pattMark.markY2)
 	{
 		pattMark.markX1 = xPos;
 		pattMark.markX2 = xPos;
-		pattMark.markY1 = pattPos;
-		pattMark.markY2 = pattPos + 1;
+		pattMark.markY1 = row;
+		pattMark.markY2 = row + 1;
 
 	}
 
@@ -1057,30 +1057,30 @@
 void keybPattMarkDown(void)
 {
 	int8_t xPos = cursor.ch;
-	int16_t pattPos = editor.pattPos;
+	int16_t row = editor.row;
 
 	if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
 	{
 		pattMark.markX1 = xPos;
 		pattMark.markX2 = xPos;
-		pattMark.markY1 = pattPos;
-		pattMark.markY2 = pattPos + 1;
+		pattMark.markY1 = row;
+		pattMark.markY2 = row + 1;
 	}
 
-	if (pattPos == pattMark.markY2)
+	if (row == pattMark.markY2)
 	{
-		pattMark.markY2 = pattPos + 1;
+		pattMark.markY2 = row + 1;
 	}
-	else if (pattPos == pattMark.markY1-1)
+	else if (row == pattMark.markY1-1)
 	{
-		pattMark.markY1 = pattPos + 2;
+		pattMark.markY1 = row + 2;
 	}
-	else if (pattPos != pattMark.markY1 && pattPos != pattMark.markY2)
+	else if (row != pattMark.markY1 && row != pattMark.markY2)
 	{
 		pattMark.markX1 = xPos;
 		pattMark.markX2 = xPos;
-		pattMark.markY1 = pattPos;
-		pattMark.markY2 = pattPos + 1;
+		pattMark.markY1 = row;
+		pattMark.markY2 = row + 1;
 	}
 
 	checkMarkLimits();
@@ -1090,12 +1090,12 @@
 void keybPattMarkLeft(void)
 {
 	int8_t xPos = cursor.ch;
-	int16_t pattPos = editor.pattPos;
+	int16_t row = editor.row;
 
-	if (pattPos != pattMark.markY1-1 && pattPos != pattMark.markY2)
+	if (row != pattMark.markY1-1 && row != pattMark.markY2)
 	{
-		pattMark.markY1 = pattPos - 1;
-		pattMark.markY2 = pattPos;
+		pattMark.markY1 = row - 1;
+		pattMark.markY2 = row;
 	}
 
 	if (xPos == pattMark.markX1)
@@ -1110,8 +1110,8 @@
 	{
 		pattMark.markX1 = xPos - 1;
 		pattMark.markX2 = xPos;
-		pattMark.markY1 = pattPos - 1;
-		pattMark.markY2 = pattPos;
+		pattMark.markY1 = row - 1;
+		pattMark.markY2 = row;
 	}
 
 	checkMarkLimits();
@@ -1121,12 +1121,12 @@
 void keybPattMarkRight(void)
 {
 	int8_t xPos = cursor.ch;
-	int16_t pattPos = editor.pattPos;
+	int16_t row = editor.row;
 
-	if (pattPos != pattMark.markY1-1 && pattPos != pattMark.markY2)
+	if (row != pattMark.markY1-1 && row != pattMark.markY2)
 	{
-		pattMark.markY1 = pattPos - 1;
-		pattMark.markY2 = pattPos;
+		pattMark.markY1 = row - 1;
+		pattMark.markY2 = row;
 	}
 
 	if (xPos == pattMark.markX2)
@@ -1141,8 +1141,8 @@
 	{
 		pattMark.markX1 = xPos;
 		pattMark.markX2 = xPos + 1;
-		pattMark.markY1 = pattPos - 1;
-		pattMark.markY2 = pattPos;
+		pattMark.markY1 = row - 1;
+		pattMark.markY2 = row;
 	}
 
 	checkMarkLimits();
@@ -1151,8 +1151,8 @@
 
 bool loadTrack(UNICHAR *filenameU)
 {
-	tonTyp loadBuff[MAX_PATT_LEN];
-	trackHeaderType th;
+	note_t loadBuff[MAX_PATT_LEN];
+	xtHdr_t h;
 
 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
 	if (f == NULL)
@@ -1161,55 +1161,56 @@
 		return false;
 	}
 
-	uint16_t nr = editor.editPattern;
-	int16_t pattLen = pattLens[nr];
-
-	if (fread(&th, 1, sizeof (th), f) != sizeof (th))
+	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
 	{
 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
 		goto trackLoadError;
 	}
 
-	if (th.ver != 1)
+	if (h.version != 1)
 	{
 		okBox(0, "System message", "Incompatible format version!");
 		goto trackLoadError;
 	}
 
-	if (th.len > MAX_PATT_LEN)
-		th.len = MAX_PATT_LEN;
+	if (h.numRows > MAX_PATT_LEN)
+		h.numRows = MAX_PATT_LEN;
 
-	if (pattLen > th.len)
-		pattLen = th.len;
+	int16_t numRows = patternNumRows[editor.editPattern];
+	if (numRows > h.numRows)
+		numRows = h.numRows;
 
-	if (fread(loadBuff, pattLen * sizeof (tonTyp), 1, f) != 1)
+	if (fread(loadBuff, numRows * sizeof (note_t), 1, f) != 1)
 	{
 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
 		goto trackLoadError;
 	}
 
-	if (!allocatePattern(nr))
+	if (!allocatePattern(editor.editPattern))
 	{
 		okBox(0, "System message", "Not enough memory!");
 		goto trackLoadError;
 	}
 
-	tonTyp *pattPtr = patt[nr];
-
 	lockMixerCallback();
-	for (int32_t i = 0; i < pattLen; i++)
+	for (int32_t i = 0; i < numRows; i++)
 	{
-		pattPtr = &patt[nr][(i * MAX_VOICES) + cursor.ch];
-		*pattPtr = loadBuff[i];
+		note_t *p = &pattern[editor.editPattern][(i * MAX_CHANNELS) + cursor.ch];
 
-		// non-FT2 security fix: remove overflown (illegal) stuff
-		if (pattPtr->ton > 97)
-			pattPtr->ton = 0;
+		*p = loadBuff[i];
 
-		if (pattPtr->effTyp > 35)
+		// sanitize stuff (FT2 doesn't do this!)
+
+		if (p->note > 97)
+			p->note = 0;
+
+		if (p->instr > 128)
+			p->instr = 0;
+
+		if (p->efx > 35)
 		{
-			pattPtr->effTyp = 0;
-			pattPtr->eff = 0;
+			p->efx = 0;
+			p->efxData = 0;
 		}
 	}
 	unlockMixerCallback();
@@ -1231,13 +1232,11 @@
 
 bool saveTrack(UNICHAR *filenameU)
 {
-	tonTyp saveBuff[MAX_PATT_LEN];
-	trackHeaderType th;
+	note_t saveBuff[MAX_PATT_LEN];
+	xtHdr_t h;
 
-	uint16_t nr = editor.editPattern;
-	tonTyp *pattPtr = patt[nr];
-
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 	{
 		okBox(0, "System message", "The current pattern is empty!");
 		return false;
@@ -1250,14 +1249,13 @@
 		return false;
 	}
 
-	const int16_t pattLen = pattLens[nr];
-	for (int32_t i = 0; i < pattLen; i++)
-		saveBuff[i] = pattPtr[(i * MAX_VOICES) + cursor.ch];
+	h.version = 1;
+	h.numRows = patternNumRows[editor.editPattern];
 
-	th.len = pattLen;
-	th.ver = 1;
+	for (int32_t i = 0; i < h.numRows; i++)
+		saveBuff[i] = p[(i * MAX_CHANNELS) + cursor.ch];
 
-	if (fwrite(&th, sizeof (th), 1, f) !=  1)
+	if (fwrite(&h, sizeof (h), 1, f) !=  1)
 	{
 		fclose(f);
 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
@@ -1264,7 +1262,7 @@
 		return false;
 	}
 
-	if (fwrite(saveBuff, pattLen * sizeof (tonTyp), 1, f) != 1)
+	if (fwrite(saveBuff, h.numRows * sizeof (note_t), 1, f) != 1)
 	{
 		fclose(f);
 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
@@ -1277,7 +1275,7 @@
 
 bool loadPattern(UNICHAR *filenameU)
 {
-	patternHeaderType th;
+	xpHdr_t h;
 
 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
 	if (f == NULL)
@@ -1286,37 +1284,31 @@
 		return false;
 	}
 
-	uint16_t nr = editor.editPattern;
-
-	if (!allocatePattern(nr))
+	if (!allocatePattern(editor.editPattern))
 	{
 		okBox(0, "System message", "Not enough memory!");
 		goto loadPattError;
 	}
 
-	tonTyp *pattPtr = patt[nr];
-	uint16_t pattLen = pattLens[nr];
-
-	if (fread(&th, 1, sizeof (th), f) != sizeof (th))
+	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
 	{
 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
 		goto loadPattError;
 	}
 
-	if (th.ver != 1)
+	if (h.version != 1)
 	{
 		okBox(0, "System message", "Incompatible format version!");
 		goto loadPattError;
 	}
 
-	if (th.len > MAX_PATT_LEN)
-		th.len = MAX_PATT_LEN;
+	if (h.numRows > MAX_PATT_LEN)
+		h.numRows = MAX_PATT_LEN;
 
-	pattLen = th.len;
-
 	lockMixerCallback();
 
-	if (fread(pattPtr, pattLen * TRACK_WIDTH, 1, f) != 1)
+	note_t *p = pattern[editor.editPattern];
+	if (fread(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
 	{
 		unlockMixerCallback();
 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
@@ -1323,31 +1315,35 @@
 		goto loadPattError;
 	}
 
-	// non-FT2 security fix: remove overflown (illegal) stuff
-	for (int32_t i = 0; i < pattLen; i++)
+	// sanitize data (FT2 doesn't do this!)
+	for (int32_t row = 0; row < h.numRows; row++)
 	{
-		for (int32_t j = 0; j < MAX_VOICES; j++)
+		for (int32_t ch = 0; ch < MAX_CHANNELS; ch++)
 		{
-			pattPtr = &patt[nr][(i * MAX_VOICES) + j];
-			if (pattPtr->ton > 97)
-				pattPtr->ton = 0;
+			p = &pattern[editor.editPattern][(row * MAX_CHANNELS) + ch];
 
-			if (pattPtr->effTyp > 35)
+			if (p->note > 97)
+				p->note = 0;
+
+			if (p->instr > 128)
+				p->instr = 128;
+
+			if (p->efx > 35)
 			{
-				pattPtr->effTyp = 0;
-				pattPtr->eff = 0;
+				p->efx = 0;
+				p->efxData = 0;
 			}
 		}
 	}
 
 	// set new pattern length (FT2 doesn't do this, strange...)
-	pattLens[nr] = pattLen;
-	song.pattLen = pattLen;
-	if (song.pattPos >= pattLen)
+	song.currNumRows = patternNumRows[editor.editPattern] = h.numRows;
+
+	if (song.row >= song.currNumRows)
 	{
-		song.pattPos = pattLen-1;
+		song.row = song.currNumRows-1;
 		if (!songPlaying)
-			editor.pattPos = song.pattPos;
+			editor.row = song.row;
 	}
 
 	unlockMixerCallback();
@@ -1369,12 +1365,10 @@
 
 bool savePattern(UNICHAR *filenameU)
 {
-	patternHeaderType th;
+	xpHdr_t h;
 
-	uint16_t nr = editor.editPattern;
-	tonTyp *pattPtr = patt[nr];
-
-	if (pattPtr == NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p == NULL)
 	{
 		okBox(0, "System message", "The current pattern is empty!");
 		return false;
@@ -1387,12 +1381,10 @@
 		return false;
 	}
 
-	uint16_t pattLen = pattLens[nr];
-
-	th.len = pattLen;
-	th.ver = 1;
-
-	if (fwrite(&th, 1, sizeof (th), f) != sizeof (th))
+	h.version = 1;
+	h.numRows = patternNumRows[editor.editPattern];
+	
+	if (fwrite(&h, 1, sizeof (h), f) != sizeof (h))
 	{
 		fclose(f);
 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
@@ -1399,7 +1391,7 @@
 		return false;
 	}
 
-	if (fwrite(pattPtr, pattLen * TRACK_WIDTH, 1, f) != 1)
+	if (fwrite(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
 	{
 		fclose(f);
 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
@@ -1433,9 +1425,9 @@
 
 	ui.channelOffset = (uint8_t)pos;
 
-	assert(song.antChn > ui.numChannelsShown);
-	if (ui.channelOffset >= song.antChn-ui.numChannelsShown)
-		ui.channelOffset = (uint8_t)(song.antChn-ui.numChannelsShown);
+	assert(song.numChannels > ui.numChannelsShown);
+	if (ui.channelOffset >= song.numChannels-ui.numChannelsShown)
+		ui.channelOffset = (uint8_t)(song.numChannels-ui.numChannelsShown);
 
 	if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
 	{
@@ -1451,26 +1443,26 @@
 	ui.updatePatternEditor = true;
 }
 
-void jumpToChannel(uint8_t channel) // for ALT+q..i ALT+a..k
+void jumpToChannel(uint8_t chNr) // for ALT+q..i ALT+a..k
 {
 	if (ui.sampleEditorShown || ui.instEditorShown)
 		return;
 
-	channel %= song.antChn;
-	if (cursor.ch == channel)
+	chNr %= song.numChannels;
+	if (cursor.ch == chNr)
 		return;
 
 	if (ui.pattChanScrollShown)
 	{
-		assert(song.antChn > ui.numChannelsShown);
+		assert(song.numChannels > ui.numChannelsShown);
 
-		if (channel >= ui.channelOffset+ui.numChannelsShown)
-			scrollBarScrollDown(SB_CHAN_SCROLL, (channel - (ui.channelOffset + ui.numChannelsShown)) + 1);
-		else if (channel < ui.channelOffset)
-			scrollBarScrollUp(SB_CHAN_SCROLL, ui.channelOffset - channel);
+		if (chNr >= ui.channelOffset+ui.numChannelsShown)
+			scrollBarScrollDown(SB_CHAN_SCROLL, (chNr - (ui.channelOffset + ui.numChannelsShown)) + 1);
+		else if (chNr < ui.channelOffset)
+			scrollBarScrollUp(SB_CHAN_SCROLL, ui.channelOffset - chNr);
 	}
 
-	cursor.ch = channel; // set it here since scrollBarScrollX() changes it...
+	cursor.ch = chNr; // set it here since scrollBarScrollX() changes it...
 	ui.updatePatternEditor = true;
 }
 
@@ -1499,17 +1491,17 @@
 
 void pbPosEdIns(void)
 {
-	if (song.len >= 255)
+	if (song.songLength >= 255)
 		return;
 
 	lockMixerCallback();
 
-	const uint8_t oldPatt = song.songTab[song.songPos];
+	const uint8_t oldPatt = song.orders[song.songPos];
 	for (uint16_t i = 0; i < 255-song.songPos; i++)
-		song.songTab[255-i] = song.songTab[254-i];
-	song.songTab[song.songPos] = oldPatt;
+		song.orders[255-i] = song.orders[254-i];
+	song.orders[song.songPos] = oldPatt;
 
-	song.len++;
+	song.songLength++;
 
 	ui.updatePosSections = true;
 	ui.updatePosEdScrollBar = true;
@@ -1520,7 +1512,7 @@
 
 void pbPosEdDel(void)
 {
-	if (song.len <= 1)
+	if (song.songLength <= 1)
 		return;
 
 	lockMixerCallback();
@@ -1528,16 +1520,16 @@
 	if (song.songPos < 254)
 	{
 		for (uint16_t i = 0; i < 254-song.songPos; i++)
-			song.songTab[song.songPos+i] = song.songTab[song.songPos+1+i];
+			song.orders[song.songPos+i] = song.orders[song.songPos+1+i];
 	}
 
-	song.len--;
-	if (song.repS >= song.len)
-		song.repS = song.len - 1;
+	song.songLength--;
+	if (song.songLoopStart >= song.songLength)
+		song.songLoopStart = song.songLength - 1;
 
-	if (song.songPos > song.len-1)
+	if (song.songPos > song.songLength-1)
 	{
-		editor.songPos = song.songPos = song.len-1;
+		editor.songPos = song.songPos = song.songLength-1;
 		setPos(song.songPos, -1, false);
 	}
 
@@ -1550,25 +1542,25 @@
 
 void pbPosEdPattUp(void)
 {
-	if (song.songTab[song.songPos] == 255)
+	if (song.orders[song.songPos] == 255)
 		return;
 
 	lockMixerCallback();
-	if (song.songTab[song.songPos] < 255)
+	if (song.orders[song.songPos] < 255)
 	{
-		song.songTab[song.songPos]++;
-		song.pattNr = song.songTab[song.songPos];
+		song.orders[song.songPos]++;
+		song.pattNum = song.orders[song.songPos];
 
-		song.pattLen = pattLens[song.pattNr];
-		if (song.pattPos >= song.pattLen)
+		song.currNumRows = patternNumRows[song.pattNum];
+		if (song.row >= song.currNumRows)
 		{
-			song.pattPos = song.pattLen-1;
+			song.row = song.currNumRows-1;
 			if (!songPlaying)
-				editor.pattPos = song.pattPos;
+				editor.row = song.row;
 		}
 
 		if (!songPlaying)
-			editor.editPattern = (uint8_t)song.pattNr;
+			editor.editPattern = (uint8_t)song.pattNum;
 
 		checkMarkLimits();
 		ui.updatePatternEditor = true;
@@ -1581,25 +1573,25 @@
 
 void pbPosEdPattDown(void)
 {
-	if (song.songTab[song.songPos] == 0)
+	if (song.orders[song.songPos] == 0)
 		return;
 
 	lockMixerCallback();
-	if (song.songTab[song.songPos] > 0)
+	if (song.orders[song.songPos] > 0)
 	{
-		song.songTab[song.songPos]--;
-		song.pattNr = song.songTab[song.songPos];
+		song.orders[song.songPos]--;
+		song.pattNum = song.orders[song.songPos];
 
-		song.pattLen = pattLens[song.pattNr];
-		if (song.pattPos >= song.pattLen)
+		song.currNumRows = patternNumRows[song.pattNum];
+		if (song.row >= song.currNumRows)
 		{
-			song.pattPos = song.pattLen-1;
+			song.row = song.currNumRows-1;
 			if (!songPlaying)
-				editor.pattPos = song.pattPos;
+				editor.row = song.row;
 		}
 
 		if (!songPlaying)
-			editor.editPattern = (uint8_t)song.pattNr;
+			editor.editPattern = (uint8_t)song.pattNum;
 
 		checkMarkLimits();
 		ui.updatePatternEditor = true;
@@ -1612,7 +1604,7 @@
 
 void pbPosEdLenUp(void)
 {
-	if (song.len >= 255)
+	if (song.songLength >= 255)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1619,7 +1611,7 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	song.len++;
+	song.songLength++;
 
 	ui.updatePosSections = true;
 	ui.updatePosEdScrollBar = true;
@@ -1631,7 +1623,7 @@
 
 void pbPosEdLenDown(void)
 {
-	if (song.len <= 1)
+	if (song.songLength <= 1)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1638,14 +1630,13 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	song.len--;
+	song.songLength--;
+	if (song.songLoopStart >= song.songLength)
+		song.songLoopStart = song.songLength - 1;
 
-	if (song.repS >= song.len)
-		song.repS = song.len - 1;
-
-	if (song.songPos >= song.len)
+	if (song.songPos >= song.songLength)
 	{
-		song.songPos = song.len - 1;
+		song.songPos = song.songLength - 1;
 		setPos(song.songPos, -1, false);
 	}
 
@@ -1663,9 +1654,9 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.repS < song.len-1)
+	if (song.songLoopStart < song.songLength-1)
 	{
-		song.repS++;
+		song.songLoopStart++;
 		ui.updatePosSections = true;
 		setSongModifiedFlag();
 	}
@@ -1680,9 +1671,9 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.repS > 0)
+	if (song.songLoopStart > 0)
 	{
-		song.repS--;
+		song.songLoopStart--;
 		ui.updatePosSections = true;
 		setSongModifiedFlag();
 	}
@@ -1693,7 +1684,7 @@
 
 void pbBPMUp(void)
 {
-	if (song.speed == 255)
+	if (song.BPM == 255)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1700,16 +1691,16 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.speed < 255)
+	if (song.BPM < 255)
 	{
-		song.speed++;
-		P_SetSpeed(song.speed);
+		song.BPM++;
+		setMixerBPM(song.BPM);
 
 		// if song is playing, the update is handled in the audio/video sync queue
 		if (!songPlaying)
 		{
-			editor.speed = song.speed;
-			drawSongBPM(song.speed);
+			editor.BPM = song.BPM;
+			drawSongBPM(song.BPM);
 		}
 	}
 
@@ -1719,7 +1710,7 @@
 
 void pbBPMDown(void)
 {
-	if (song.speed == 32)
+	if (song.BPM == 32)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1726,16 +1717,16 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.speed > 32)
+	if (song.BPM > 32)
 	{
-		song.speed--;
-		P_SetSpeed(song.speed);
+		song.BPM--;
+		setMixerBPM(song.BPM);
 
 		// if song is playing, the update is handled in the audio/video sync queue
 		if (!songPlaying)
 		{
-			editor.speed = song.speed;
-			drawSongBPM(editor.speed);
+			editor.BPM = song.BPM;
+			drawSongBPM(editor.BPM);
 		}
 	}
 
@@ -1745,7 +1736,7 @@
 
 void pbSpeedUp(void)
 {
-	if (song.tempo == 31)
+	if (song.speed == 31)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1752,15 +1743,15 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.tempo < 31)
+	if (song.speed < 31)
 	{
-		song.tempo++;
+		song.speed++;
 
 		// if song is playing, the update is handled in the audio/video sync queue
 		if (!songPlaying)
 		{
-			editor.tempo = song.tempo;
-			drawSongSpeed(editor.tempo);
+			editor.speed = song.speed;
+			drawSongSpeed(editor.speed);
 		}
 	}
 
@@ -1770,7 +1761,7 @@
 
 void pbSpeedDown(void)
 {
-	if (song.tempo == 0)
+	if (song.speed == 0)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1777,15 +1768,15 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.tempo > 0)
+	if (song.speed > 0)
 	{
-		song.tempo--;
+		song.speed--;
 
 		// if song is playing, the update is handled in the audio/video sync queue
 		if (!songPlaying)
 		{
-			editor.tempo = song.tempo;
-			drawSongSpeed(editor.tempo);
+			editor.speed = song.speed;
+			drawSongSpeed(editor.speed);
 		}
 	}
 
@@ -1795,10 +1786,10 @@
 
 void pbIncAdd(void)
 {
-	if (editor.ID_Add == 16)
-		editor.ID_Add = 0;
+	if (editor.editRowSkip == 16)
+		editor.editRowSkip = 0;
 	else
-		editor.ID_Add++;
+		editor.editRowSkip++;
 
 	drawIDAdd();
 }
@@ -1805,10 +1796,10 @@
 
 void pbDecAdd(void)
 {
-	if (editor.ID_Add == 0)
-		editor.ID_Add = 16;
+	if (editor.editRowSkip == 0)
+		editor.editRowSkip = 16;
 	else
-		editor.ID_Add--;
+		editor.editRowSkip--;
 
 	drawIDAdd();
 }
@@ -1815,11 +1806,11 @@
 
 void pbAddChan(void)
 {
-	if (song.antChn > 30)
+	if (song.numChannels > 30)
 		return;
 
 	lockMixerCallback();
-	song.antChn += 2;
+	song.numChannels += 2;
 
 	hideTopScreen();
 	showTopLeftMainScreen(true);
@@ -1834,12 +1825,12 @@
 
 void pbSubChan(void)
 {
-	if (song.antChn < 4)
+	if (song.numChannels < 4)
 		return;
 
 	lockMixerCallback();
 
-	song.antChn -= 2;
+	song.numChannels -= 2;
 
 	checkMarkLimits();
 
@@ -1860,20 +1851,20 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.pattNr < 255)
+	if (song.pattNum < 255)
 	{
-		song.pattNr++;
+		song.pattNum++;
 
-		song.pattLen = pattLens[song.pattNr];
-		if (song.pattPos >= song.pattLen)
+		song.currNumRows = patternNumRows[song.pattNum];
+		if (song.row >= song.currNumRows)
 		{
-			song.pattPos = song.pattLen-1;
+			song.row = song.currNumRows-1;
 			if (!songPlaying)
-				editor.pattPos = song.pattPos;
+				editor.row = song.row;
 		}
 
 		if (!songPlaying)
-			editor.editPattern = (uint8_t)song.pattNr;
+			editor.editPattern = (uint8_t)song.pattNum;
 
 		checkMarkLimits();
 		ui.updatePatternEditor = true;
@@ -1890,20 +1881,20 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.pattNr > 0)
+	if (song.pattNum > 0)
 	{
-		song.pattNr--;
+		song.pattNum--;
 
-		song.pattLen = pattLens[song.pattNr];
-		if (song.pattPos >= song.pattLen)
+		song.currNumRows = patternNumRows[song.pattNum];
+		if (song.row >= song.currNumRows)
 		{
-			song.pattPos = song.pattLen-1;
+			song.row = song.currNumRows-1;
 			if (!songPlaying)
-				editor.pattPos = song.pattPos;
+				editor.row = song.row;
 		}
 
 		if (!songPlaying)
-			editor.editPattern = (uint8_t)song.pattNr;
+			editor.editPattern = (uint8_t)song.pattNum;
 
 		checkMarkLimits();
 		ui.updatePatternEditor = true;
@@ -1916,8 +1907,8 @@
 
 void pbPattLenUp(void)
 {
-	const uint16_t pattLen = pattLens[editor.editPattern];
-	if (pattLen >= 256)
+	const uint16_t numRows = patternNumRows[editor.editPattern];
+	if (numRows >= MAX_PATT_LEN)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1924,8 +1915,8 @@
 	if (audioWasntLocked)
 		lockAudio();
 	
-	setPatternLen(editor.editPattern, pattLen + 1);
-	checkMarkLimits();
+	song.pattNum = editor.editPattern; // kludge
+	setPatternLen(editor.editPattern, numRows+1);
 
 	ui.updatePatternEditor = true;
 	ui.updatePosSections = true;
@@ -1937,8 +1928,8 @@
 
 void pbPattLenDown(void)
 {
-	const uint16_t pattLen = pattLens[editor.editPattern];
-	if (pattLen <= 1)
+	const uint16_t numRows = patternNumRows[editor.editPattern];
+	if (numRows <= 1)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -1945,8 +1936,8 @@
 	if (audioWasntLocked)
 		lockAudio();
 	
-	setPatternLen(editor.editPattern, pattLen - 1);
-	checkMarkLimits();
+	song.pattNum = editor.editPattern; // kludge
+	setPatternLen(editor.editPattern, numRows-1);
 
 	ui.updatePatternEditor = true;
 	ui.updatePosSections = true;
@@ -1958,8 +1949,8 @@
 
 void drawPosEdNums(int16_t songPos)
 {
-	if (songPos >= song.len)
-		songPos = song.len - 1;
+	if (songPos >= song.songLength)
+		songPos = song.songLength - 1;
 
 	// clear
 	if (ui.extended)
@@ -1990,12 +1981,12 @@
 		if (ui.extended)
 		{
 			pattTwoHexOut(8,  4 + (y * 9), (uint8_t)entry, color1);
-			pattTwoHexOut(32, 4 + (y * 9), song.songTab[entry], color1);
+			pattTwoHexOut(32, 4 + (y * 9), song.orders[entry], color1);
 		}
 		else
 		{
 			pattTwoHexOut(8,  4 + (y * 8), (uint8_t)entry, color1);
-			pattTwoHexOut(32, 4 + (y * 8), song.songTab[entry], color1);
+			pattTwoHexOut(32, 4 + (y * 8), song.orders[entry], color1);
 		}
 	}
 
@@ -2005,12 +1996,12 @@
 	if (ui.extended)
 	{
 		pattTwoHexOut(8,  23, (uint8_t)songPos, color2);
-		pattTwoHexOut(32, 23, song.songTab[songPos], color2);
+		pattTwoHexOut(32, 23, song.orders[songPos], color2);
 	}
 	else
 	{
 		pattTwoHexOut(8,  22, (uint8_t)songPos, color2);
-		pattTwoHexOut(32, 22, song.songTab[songPos], color2);
+		pattTwoHexOut(32, 22, song.orders[songPos], color2);
 	}
 
 	// bottom two
@@ -2017,18 +2008,18 @@
 	for (int16_t y = 0; y < 2; y++)
 	{
 		int16_t entry = songPos + (1 + y);
-		if (entry >= song.len)
+		if (entry >= song.songLength)
 			break;
 
 		if (ui.extended)
 		{
 			pattTwoHexOut(8,  33 + (y * 9), (uint8_t)entry, color1);
-			pattTwoHexOut(32, 33 + (y * 9), song.songTab[entry], color1);
+			pattTwoHexOut(32, 33 + (y * 9), song.orders[entry], color1);
 		}
 		else
 		{
 			pattTwoHexOut(8,  32 + (y * 8), (uint8_t)entry, color1);
-			pattTwoHexOut(32, 32 + (y * 8), song.songTab[entry], color1);
+			pattTwoHexOut(32, 32 + (y * 8), song.orders[entry], color1);
 		}
 	}
 }
@@ -2048,10 +2039,10 @@
 		y = 52;
 	}
 
-	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.len, 2);
+	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLength, 2);
 }
 
-void drawSongRepS(void)
+void drawSongLoopStart(void)
 {
 	int16_t x, y;
 
@@ -2066,36 +2057,18 @@
 		y = 64;
 	}
 
-	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.repS, 2);
+	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLoopStart, 2);
 }
 
 void drawSongBPM(uint16_t val)
 {
-	char str[4];
-	const char *strOut;
-	
 	if (ui.extended)
 		return;
 
-	if (val <= 255)
-	{
-		strOut = dec3StrTab[val];
-	}
-	else
-	{
-		if (val > MAX_BPM)
-			val = MAX_BPM;
+	if (val > 255)
+		val = 255;
 
-		assert(MAX_BPM == 999);
-		str[0] = '0' + (char)(val / 100);
-		str[1] = '0' + ((val / 10) % 10);
-		str[2] = '0' + (val % 10);
-		str[3] = 0;
-
-		strOut = str;
-	}
-
-	textOutFixed(145, 36, PAL_FORGRND, PAL_DESKTOP, strOut);
+	textOutFixed(145, 36, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[val]);
 }
 
 void drawSongSpeed(uint16_t val)
@@ -2142,7 +2115,7 @@
 		y = 50;
 	}
 
-	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, pattLens[editPattern], 3);
+	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, patternNumRows[editPattern], 3);
 }
 
 void drawGlobalVol(uint16_t val)
@@ -2156,8 +2129,8 @@
 
 void drawIDAdd(void)
 {
-	assert(editor.ID_Add <= 16);
-	textOutFixed(152, 64, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[editor.ID_Add]);
+	assert(editor.editRowSkip <= 16);
+	textOutFixed(152, 64, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[editor.editRowSkip]);
 }
 
 void resetPlaybackTime(void)
@@ -2173,10 +2146,14 @@
 	if (songPlaying)
 	{
 		const uint32_t ms1024 = song.musicTime64 >> 32; // milliseconds (scaled from 1000 to 1024)
-
 		uint32_t seconds = ms1024 >> 10;
-		last_TimeH = seconds / 3600; seconds -= last_TimeH * 3600;
-		last_TimeM = seconds / 60;   seconds -= last_TimeM * 60;
+
+		last_TimeH = seconds / 3600;
+		seconds -= last_TimeH * 3600;
+
+		last_TimeM = seconds / 60;
+		seconds -= last_TimeM * 60;
+
 		last_TimeS = seconds;
 	}
 
@@ -2584,37 +2561,37 @@
 {
 	lockMixerCallback();
 
-	song.len = 1;
-	song.repS = 0; // Bug: FT2 doesn't do this!
-	song.speed = 125;
-	song.tempo = 6;
+	song.songLength = 1;
+	song.songLoopStart = 0; // FT2 doesn't do this!
+	song.BPM = 125;
+	song.speed = 6;
 	song.songPos = 0;
-	song.globVol = 64;
+	song.globalVolume = 64;
 
 	memset(song.name, 0, sizeof (song.name));
-	memset(song.songTab, 0, sizeof (song.songTab));
+	memset(song.orders, 0, sizeof (song.orders));
 
 	// zero all pattern data and reset pattern lengths
 
 	freeAllPatterns();
-	for (uint16_t i = 0; i < MAX_PATTERNS; i++)
-		pattLens[i] = 64;
-	song.pattLen = pattLens[song.pattNr];
+	for (int32_t i = 0; i < MAX_PATTERNS; i++)
+		patternNumRows[i] = 64;
+	song.currNumRows = patternNumRows[song.pattNum];
 
 	resetMusic();
-	P_SetSpeed(song.speed);
+	setMixerBPM(song.BPM);
 
 	editor.songPos = song.songPos;
-	editor.editPattern = song.pattNr;
+	editor.editPattern = song.pattNum;
+	editor.BPM = song.BPM;
 	editor.speed = song.speed;
-	editor.tempo = song.tempo;
-	editor.globalVol = song.globVol;
-	editor.timer = 1;
+	editor.globalVolume = song.globalVolume;
+	editor.tick = 1;
 
 	resetPlaybackTime();
 
 	if (!audio.linearPeriodsFlag)
-		setFrqTab(true);
+		setFrequencyTable(true);
 
 	clearPattMark();
 	resetWavRenderer();
@@ -2622,7 +2599,7 @@
 	unlockMixerCallback();
 
 	setScrollBarPos(SB_POS_ED, 0, false);
-	setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5);
+	setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
 
 	updateWindowTitle(true);
 }
@@ -2705,7 +2682,7 @@
 
 void resetChannelOffset(void)
 {
-	ui.pattChanScrollShown = song.antChn > getMaxVisibleChannels();
+	ui.pattChanScrollShown = song.numChannels > getMaxVisibleChannels();
 	cursor.object = CURSOR_NOTE;
 	cursor.ch = 0;
 	setScrollBarPos(SB_CHAN_SCROLL, 0, true);
@@ -2717,48 +2694,46 @@
 	if (okBox(2, "System request", "Shrink pattern?") != 1)
 		return;
 
-	uint16_t nr = editor.editPattern;
+	int16_t numRows = patternNumRows[editor.editPattern];
+	if (numRows <= 1)
+		return;
 
-	int16_t pattLen = pattLens[nr];
-	if (pattLen > 1)
-	{
-		lockMixerCallback();
+	lockMixerCallback();
 
-		tonTyp *pattPtr = patt[nr];
-		if (pattPtr != NULL)
+	note_t *p = pattern[editor.editPattern];
+	if (p != NULL)
+	{
+		const int32_t length = numRows >> 1;
+		for (int32_t i = 0; i < length; i++)
 		{
-			for (int32_t i = 0; i < pattLen/2; i++)
-			{
-				for (int32_t j = 0; j < MAX_VOICES; j++)
-					pattPtr[(i * MAX_VOICES) + j] = pattPtr[((i * 2) * MAX_VOICES) + j];
-			}
+			for (int32_t j = 0; j < MAX_CHANNELS; j++)
+				p[(i * MAX_CHANNELS) + j] = p[((i*2) * MAX_CHANNELS) + j];
 		}
+	}
 
-		pattLens[nr] >>= 1;
+	patternNumRows[editor.editPattern] >>= 1;
+	numRows = patternNumRows[editor.editPattern];
 
-		if (song.pattNr == nr)
-			song.pattLen = pattLens[nr];
+	if (song.pattNum == editor.editPattern)
+		song.currNumRows = numRows;
 
-		song.pattPos >>= 1;
-		if (song.pattPos >= pattLens[nr])
-			song.pattPos = pattLens[nr] - 1;
+	song.row >>= 1;
+	if (song.row >= numRows)
+		song.row = numRows-1;
 
-		editor.pattPos = song.pattPos;
+	editor.row = song.row;
 
-		ui.updatePatternEditor = true;
-		ui.updatePosSections = true;
+	ui.updatePatternEditor = true;
+	ui.updatePosSections = true;
 
-		unlockMixerCallback();
-		setSongModifiedFlag();
-	}
+	unlockMixerCallback();
+	setSongModifiedFlag();
 }
 
 void expandPattern(void)
 {
-	uint16_t nr = editor.editPattern;
-
-	int16_t pattLen = pattLens[nr];
-	if (pattLen > 128)
+	int16_t numRows = patternNumRows[editor.editPattern];
+	if (numRows > 128)
 	{
 		okBox(0, "System message", "Pattern is too long to be expanded.");
 	}
@@ -2766,9 +2741,9 @@
 	{
 		lockMixerCallback();
 
-		if (patt[nr] != NULL)
+		if (pattern[editor.editPattern] != NULL)
 		{
-			tonTyp *tmpPtn = (tonTyp *)malloc((pattLen * 2) * TRACK_WIDTH);
+			note_t *tmpPtn = (note_t *)malloc((numRows * 2) * TRACK_WIDTH);
 			if (tmpPtn == NULL)
 			{
 				unlockMixerCallback();
@@ -2776,28 +2751,29 @@
 				return;
 			}
 
-			for (int32_t i = 0; i < pattLen; i++)
+			for (int32_t i = 0; i < numRows; i++)
 			{
-				for (int32_t j = 0; j < MAX_VOICES; j++)
-					tmpPtn[((i * 2) * MAX_VOICES) + j] = patt[nr][(i * MAX_VOICES) + j];
+				for (int32_t j = 0; j < MAX_CHANNELS; j++)
+					tmpPtn[((i * 2) * MAX_CHANNELS) + j] = pattern[editor.editPattern][(i * MAX_CHANNELS) + j];
 
-				memset(&tmpPtn[((i * 2) + 1) * MAX_VOICES], 0, TRACK_WIDTH);
+				memset(&tmpPtn[((i * 2) + 1) * MAX_CHANNELS], 0, TRACK_WIDTH);
 			}
 
-			free(patt[nr]);
-			patt[nr] = tmpPtn;
+			free(pattern[editor.editPattern]);
+			pattern[editor.editPattern] = tmpPtn;
 		}
 
-		pattLens[nr] *= 2;
+		patternNumRows[editor.editPattern] *= 2;
+		numRows = patternNumRows[editor.editPattern];
 
-		if (song.pattNr == nr)
-			song.pattLen = pattLens[nr];
+		if (song.pattNum == editor.editPattern)
+			song.currNumRows = numRows;
 
-		song.pattPos *= 2;
-		if (song.pattPos >= pattLens[nr])
-			song.pattPos = pattLens[nr] - 1;
+		song.row *= 2;
+		if (song.row >= numRows)
+			song.row = numRows-1;
 
-		editor.pattPos = song.pattPos;
+		editor.row = song.row;
 
 		ui.updatePatternEditor = true;
 		ui.updatePosSections = true;
--- a/src/ft2_pattern_ed.h
+++ b/src/ft2_pattern_ed.h
@@ -18,15 +18,15 @@
 	TRANSP_BLOCK = 3
 };
 
-typedef struct trackHeaderType_t
+typedef struct xtHdr_t
 {
-	uint16_t ver, len;
-} trackHeaderType;
+	uint16_t version, numRows;
+} xtHdr_t;
 
-typedef struct patternHeaderType_t
+typedef struct xpHdr_t
 {
-	uint16_t ver, len;
-} patternHeaderType;
+	uint16_t version, numRows;
+} xpHdr_t;
 
 typedef struct pattCoord_t
 {
@@ -61,8 +61,8 @@
 
 void resetPlaybackTime(void);
 
-bool allocatePattern(uint16_t nr);
-void killPatternIfUnused(uint16_t nr);
+bool allocatePattern(uint16_t pattNum);
+void killPatternIfUnused(uint16_t pattNum);
 uint8_t getMaxVisibleChannels(void);
 void updatePatternWidth(void);
 void updateAdvEdit(void);
@@ -106,7 +106,7 @@
 void scrollChannelLeft(void);
 void scrollChannelRight(void);
 void setChannelScrollPos(uint32_t pos);
-void jumpToChannel(uint8_t channel); // for ALT+q..i ALT+a..k
+void jumpToChannel(uint8_t chNr); // for ALT+q..i ALT+a..k
 void sbPosEdPos(uint32_t pos);
 void pbPosEdPosUp(void);
 void pbPosEdPosDown(void);
@@ -132,7 +132,7 @@
 void pbPattLenDown(void);
 void drawPosEdNums(int16_t songPos);
 void drawSongLength(void);
-void drawSongRepS(void);
+void drawSongLoopStart(void);
 void drawSongBPM(uint16_t val);
 void drawSongSpeed(uint16_t val);
 void drawEditPattern(uint16_t editPattern);
--- a/src/ft2_pushbuttons.c
+++ b/src/ft2_pushbuttons.c
@@ -219,7 +219,7 @@
 	{ 300, 382, 50, 16, 0, 0, "X-Fade",           NULL,    NULL,                  sampXFade },
 	{ 430, 348, 54, 16, 0, 0, "Exit",             NULL,    NULL,                  exitSampleEditor },
 	{ 594, 348, 35, 13, 0, 0, "Clr S.",           NULL,    NULL,                  clearSample },
-	{ 594, 360, 35, 13, 0, 0, "Min.",             NULL,    NULL,                  sampMin },
+	{ 594, 360, 35, 13, 0, 0, "Min.",             NULL,    NULL,                  sampMinimize },
 	{ 594, 373, 18, 13, 2, 4, ARROW_UP_STRING,    NULL,    sampRepeatUp,          NULL },
 	{ 611, 373, 18, 13, 2, 4, ARROW_DOWN_STRING,  NULL,    sampRepeatDown,        NULL },
 	{ 594, 385, 18, 13, 2, 4, ARROW_UP_STRING,    NULL,    sampReplenUp,          NULL },
@@ -228,10 +228,10 @@
 	// ------ SAMPLE EDITOR EXTENSION PUSHBUTTONS ------
 	//x,   y,   w,  h,  p, d, text #1,     text #2, funcOnDown, funcOnUp
 	{   3, 138, 52, 16, 0, 0, "Clr. c.bf", NULL,    NULL,       clearCopyBuffer },
-	{  56, 138, 49, 16, 0, 0, "Conv",      NULL,    NULL,       sampleConv },
+	{  56, 138, 49, 16, 0, 0, "Sign",      NULL,    NULL,       sampleChangeSign },
 	{ 106, 138, 49, 16, 0, 0, "Echo",      NULL,    NULL,       pbSampleEcho },
 	{   3, 155, 52, 16, 0, 0, "Backw.",    NULL,    NULL,       sampleBackwards },
-	{  56, 155, 49, 16, 0, 0, "Conv W",    NULL,    NULL,       sampleConvW },
+	{  56, 155, 49, 16, 0, 0, "B. swap",    NULL,    NULL,      sampleByteSwap },
 	{ 106, 155, 49, 16, 0, 0, "Fix DC",    NULL,    NULL,       fixDC },
 	{ 161, 121, 60, 16, 0, 0, "Copy ins.", NULL,    NULL,       copyInstr },
 	{ 222, 121, 66, 16, 0, 0, "Copy smp.", NULL,    NULL,       copySmp },
@@ -285,10 +285,10 @@
 	{ 606, 248, 23, 13, 1, 4, ARROW_RIGHT_STRING, NULL,    vibDepthUp,     NULL },
 	{ 521, 262, 23, 13, 1, 4, ARROW_LEFT_STRING,  NULL,    vibSweepDown,   NULL },
 	{ 606, 262, 23, 13, 1, 4, ARROW_RIGHT_STRING, NULL,    vibSweepUp,     NULL },
-	{ 441, 312, 94, 16, 1, 4, "Octave up",        NULL,    relToneOctUp,   NULL },
-	{ 536, 312, 93, 16, 1, 4, "Halftone up",      NULL,    relToneUp,      NULL },
-	{ 441, 329, 94, 16, 1, 4, "Octave down",      NULL,    relToneOctDown, NULL },
-	{ 536, 329, 93, 16, 1, 4, "Halftone down",    NULL,    relToneDown,    NULL },
+	{ 441, 312, 94, 16, 1, 4, "Octave up",        NULL,    relativeNoteOctUp,   NULL },
+	{ 536, 312, 93, 16, 1, 4, "Halftone up",      NULL,    relativeNoteUp,      NULL },
+	{ 441, 329, 94, 16, 1, 4, "Octave down",      NULL,    relativeNoteOctDown, NULL },
+	{ 536, 329, 93, 16, 1, 4, "Halftone down",    NULL,    relativeNoteDown,    NULL },
 
 	// ------ INSTRUMENT EDITOR EXTENSION PUSHBUTTONS ------
 	//x,   y,   w,  h,  p, d, text #1,            text #2, funcOnDown,   funcOnUp
--- a/src/ft2_radiobuttons.c
+++ b/src/ft2_radiobuttons.c
@@ -33,7 +33,7 @@
 	//x, y,   w,  group,         funcOnUp
 	{ 5, 18,  69, RB_GROUP_HELP, rbHelpFeatures },
 	{ 5, 34,  60, RB_GROUP_HELP, rbHelpEffects },
-	{ 5, 50,  71, RB_GROUP_HELP, rbHelpKeyboard },
+	{ 5, 50,  86, RB_GROUP_HELP, rbHelpKeybindings },
 	{ 5, 66, 109, RB_GROUP_HELP, rbHelpHowToUseFT2 },
 	{ 5, 82, 101, RB_GROUP_HELP, rbHelpFAQ },
 	{ 5, 98,  86, RB_GROUP_HELP, rbHelpKnownBugs },
@@ -74,7 +74,7 @@
 	// ------ CONFIG AUDIO ------
 
 	// audio buffer size
-	//x,   y,   w,  group,                           funcOnUp
+	//x,   y,  w,   group,                           funcOnUp
 	{ 390, 16,  46, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigSbs512  },
 	{ 390, 30, 113, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigSbs1024 },
 	{ 390, 44,  50, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigSbs2048 },
@@ -94,11 +94,13 @@
 	//x,   y,   w,  group,                      funcOnUp
 	{ 509, 16,  66, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio44kHz },
 	{ 509, 30, 121, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio48kHz },
+#if CPU_64BIT
 	{ 509, 44,  66, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio96kHz },
 	{ 509, 58,  73, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio192kHz },
+#endif
 
 	// audio input frequency
-	//x,   y,  w,   group,                            funcOnUp
+	//x,   y,   w,  group,                            funcOnUp
 	{ 180, 156, 60, RB_GROUP_CONFIG_AUDIO_INPUT_FREQ, rbConfigAudioInput44kHz },
 	{ 251, 156, 60, RB_GROUP_CONFIG_AUDIO_INPUT_FREQ, rbConfigAudioInput48kHz },
 	{ 322, 156, 60, RB_GROUP_CONFIG_AUDIO_INPUT_FREQ, rbConfigAudioInput96kHz },
@@ -128,7 +130,7 @@
 	{ 346, 145, 46, RB_GROUP_CONFIG_SCOPE, rbConfigScopeLined },
 
 	// visible pattern channels
-	//x,   y,   w,  group,                        funcOnUp
+	//x,   y,  w,  group,                        funcOnUp
 	{ 257, 42, 78, RB_GROUP_CONFIG_PATTERN_CHANS, rbConfigPatt4Chans },
 	{ 257, 56, 78, RB_GROUP_CONFIG_PATTERN_CHANS, rbConfigPatt6Chans },
 	{ 257, 70, 78, RB_GROUP_CONFIG_PATTERN_CHANS, rbConfigPatt8Chans },
--- a/src/ft2_radiobuttons.h
+++ b/src/ft2_radiobuttons.h
@@ -2,6 +2,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include "ft2_cpu.h"
 
 enum // RADIOBUTTONS
 {
@@ -8,7 +9,7 @@
 	// HELP
 	RB_HELP_FEATURES,
 	RB_HELP_EFFECTS,
-	RB_HELP_KEYBOARD,
+	RB_HELP_KEYBINDINGS,
 	RB_HELP_HOW_TO_USE_FT2,
 	RB_HELP_FAQ,
 	RB_HELP_KNOWN_BUGS,
@@ -62,8 +63,10 @@
 	// AUDIO FREQUENCY
 	RB_CONFIG_AUDIO_44KHZ,
 	RB_CONFIG_AUDIO_48KHZ,
+#if CPU_64BIT
 	RB_CONFIG_AUDIO_96KHZ,
 	RB_CONFIG_AUDIO_192KHZ,
+#endif
 
 	// AUDIO INPUT FREQUENCY
 	RB_CONFIG_AUDIO_INPUT_44KHZ,
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -15,7 +15,7 @@
 #include "ft2_inst_ed.h"
 #include "ft2_diskop.h"
 #include "ft2_midi.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_mouse.h"
 #include "ft2_sample_loader.h"
 #include "ft2_tables.h"
@@ -29,11 +29,11 @@
 
 static double dLogTab[768], dExp2MulTab[32];
 static bool bxxOverflow;
-static tonTyp nilPatternLine[MAX_VOICES];
+static note_t nilPatternLine[MAX_CHANNELS];
 
-typedef void (*volKolEfxRoutine)(stmTyp *ch);
-typedef void (*volKolEfxRoutine2)(stmTyp *ch, uint8_t *volKol);
-typedef void (*efxRoutine)(stmTyp *ch, uint8_t param);
+typedef void (*volColumnEfxRoutine)(channel_t *ch);
+typedef void (*volColumnEfxRoutine2)(channel_t *ch, uint8_t *volColumnData);
+typedef void (*efxRoutine)(channel_t *ch, uint8_t param);
 
 // globally accessed
 int8_t playMode = 0;
@@ -40,11 +40,11 @@
 bool songPlaying = false, audioPaused = false, musicPaused = false;
 volatile bool replayerBusy = false;
 const uint16_t *note2Period = NULL;
-int16_t pattLens[MAX_PATTERNS];
-stmTyp stm[MAX_VOICES];
-songTyp song;
-instrTyp *instr[132];
-tonTyp *patt[MAX_PATTERNS];
+int16_t patternNumRows[MAX_PATTERNS];
+channel_t channel[MAX_CHANNELS];
+song_t song;
+instr_t *instr[132];
+note_t *pattern[MAX_PATTERNS];
 // ----------------------------------
 
 void fixString(char *str, int32_t lastChrPos) // removes leading spaces and 0x1A chars
@@ -64,12 +64,12 @@
 	fixString(song.name, 19);
 }
 
-void fixInstrAndSampleNames(int16_t nr)
+void fixInstrAndSampleNames(int16_t insNum)
 {
-	fixString(song.instrName[nr], 21);
-	if (instr[nr] != NULL)
+	fixString(song.instrName[insNum], 21);
+	if (instr[insNum] != NULL)
 	{
-		sampleTyp *s = instr[nr]->samp;
+		sample_t *s = instr[insNum]->smp;
 		for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++)
 			fixString(s->name, 21);
 	}
@@ -81,10 +81,10 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	memset(stm, 0, sizeof (stm));
+	memset(channel, 0, sizeof (channel));
 
-	stmTyp *ch = stm;
-	for (int32_t i = 0; i < MAX_VOICES; i++, ch++)
+	channel_t *ch = channel;
+	for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++)
 	{
 		ch->instrPtr = instr[0];
 		ch->status = IS_Vol;
@@ -92,7 +92,7 @@
 		ch->outPan = 128;
 		ch->finalPan = 128;
 
-		ch->stOff = !editor.chnMode[i]; // set channel mute flag from global mute flag
+		ch->channelOff = !editor.chnMode[i]; // set channel mute flag from global mute flag
 	}
 
 	if (audioWasntLocked)
@@ -112,7 +112,7 @@
 }
 
 // used on external sample load and during sample loading in some module formats
-void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag)
+void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag)
 {
 	#define NOTE_C4 (4*12)
 	#define MIN_PERIOD (0)
@@ -123,7 +123,7 @@
 
 	if (midCFreq <= 0 || periodTab == NULL)
 	{
-		s->fine = s->relTon = 0;
+		s->finetune = s->relativeNote = 0;
 		return;
 	}
 
@@ -131,15 +131,15 @@
 
 	if (midCFreq <= (int32_t)dGetHzFromPeriod(periodTab[MIN_PERIOD]))
 	{
-		s->fine = -128;
-		s->relTon = -48;
+		s->finetune = -128;
+		s->relativeNote = -48;
 		return;
 	}
 
 	if (midCFreq >= (int32_t)dGetHzFromPeriod(periodTab[MAX_PERIOD]))
 	{
-		s->fine = 127;
-		s->relTon = 71;
+		s->finetune = 127;
+		s->relativeNote = 71;
 		return;
 	}
 
@@ -149,8 +149,8 @@
 	{
 		if (midCFreq == (int32_t)dGetHzFromPeriod(periodTab[16 + (i<<4)]))
 		{
-			s->fine = 0;
-			s->relTon = i - NOTE_C4;
+			s->finetune = 0;
+			s->relativeNote = i - NOTE_C4;
 			return;
 		}
 	}
@@ -178,14 +178,14 @@
 		}
 	}
 
-	s->fine = ((period & 31) - 16) << 3;
-	s->relTon = (int8_t)(((period & ~31) >> 4) - NOTE_C4);
+	s->finetune = ((period & 31) - 16) << 3;
+	s->relativeNote = (int8_t)(((period & ~31) >> 4) - NOTE_C4);
 }
 
-void setPatternLen(uint16_t nr, int16_t len)
+void setPatternLen(uint16_t pattNum, int16_t numRows)
 {
-	assert(nr < MAX_PATTERNS);
-	if ((len < 1 || len > MAX_PATT_LEN) || len == pattLens[nr])
+	assert(pattNum < MAX_PATTERNS);
+	if ((numRows < 1 || numRows > MAX_PATT_LEN) || numRows == patternNumRows[pattNum])
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -192,16 +192,23 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	pattLens[nr] = len;
+	patternNumRows[pattNum] = numRows;
 
-	if (patt[nr] != NULL)
-		killPatternIfUnused(nr);
+	if (pattern[pattNum] != NULL)
+		killPatternIfUnused(pattNum);
 
-	song.pattLen = pattLens[nr];
-	if (song.pattPos >= song.pattLen)
+	// non-FT2 security
+	song.pattDelTime = 0;
+	song.pattDelTime2 = 0;
+	song.pBreakFlag = false;
+	song.posJumpFlag = false;
+	song.pBreakPos = 0;
+
+	song.currNumRows = numRows;
+	if (song.row >= song.currNumRows)
 	{
-		song.pattPos = song.pattLen - 1;
-		editor.pattPos = song.pattPos;
+		song.row = song.currNumRows - 1;
+		editor.row = song.row;
 	}
 
 	checkMarkLimits();
@@ -213,15 +220,15 @@
 	ui.updatePosSections = true;
 }
 
-int16_t getUsedSamples(int16_t nr)
+int16_t getUsedSamples(int16_t smpNum)
 {
-	if (instr[nr] == NULL)
+	if (instr[smpNum] == NULL)
 		return 0;
 
-	instrTyp *ins = instr[nr];
+	instr_t *ins = instr[smpNum];
 
 	int16_t i = 16 - 1;
-	while (i >= 0 && ins->samp[i].pek == NULL && ins->samp[i].name[0] == '\0')
+	while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0')
 		i--;
 
 	/* Yes, 'i' can be -1 here, and will be set to at least 0
@@ -229,20 +236,20 @@
 	*/
 	for (int16_t j = 0; j < 96; j++)
 	{
-		if (ins->ta[j] > i)
-			i = ins->ta[j];
+		if (ins->note2SampleLUT[j] > i)
+			i = ins->note2SampleLUT[j];
 	}
 
 	return i+1;
 }
 
-int16_t getRealUsedSamples(int16_t nr)
+int16_t getRealUsedSamples(int16_t smpNum)
 {
-	if (instr[nr] == NULL)
+	if (instr[smpNum] == NULL)
 		return 0;
 
 	int8_t i = 16 - 1;
-	while (i >= 0 && instr[nr]->samp[i].pek == NULL)
+	while (i >= 0 && instr[smpNum]->smp[i].dataPtr == NULL)
 		i--;
 
 	return i+1;
@@ -253,10 +260,11 @@
 	if (period == 0)
 		return 0.0; // in FT2, a period of 0 results in 0Hz
 
-	const uint16_t invPeriod = (12 * 192 * 4) - (uint16_t)period; // intentionally underflows (uint16_t) to be FT2-accurate
-	const int32_t quotient = invPeriod / 768;
-	const int32_t remainder = invPeriod % 768;
+	const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 frequency quirk
 
+	const uint32_t quotient = invPeriod / 768;
+	const uint32_t remainder = invPeriod % 768;
+
 	return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y / 2^((14-quotient) & 31)
 }
 
@@ -273,24 +281,24 @@
 	return audio.linearPeriodsFlag ? dLinearPeriod2Hz(period) : dAmigaPeriod2Hz(period);
 }
 
-// returns *exact* FT2 C-4 voice rate (depending on finetune, relative note and linear/Amiga period mode)
-double getSampleC4Rate(sampleTyp *s)
+// returns *exact* FT2 C-4 voice rate (depending on finetune, relativeNote and linear/Amiga period mode)
+double getSampleC4Rate(sample_t *s)
 {
-	int32_t note = (96/2) + s->relTon;
+	int32_t note = (96/2) + s->relativeNote;
 	if (note >= (10*12)-1)
-		return -1; // B-9 (from relTon) = illegal! (won't play in replayer)
+		return -1; // B-9 (from relativeTone) = illegal! (won't play in replayer)
 
-	const int32_t C4Period = (note << 4) + (((int8_t)s->fine >> 3) + 16);
+	const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16);
 
 	const uint16_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period];
 	return dPeriod2Hz(period);
 }
 
-void setFrqTab(bool linear)
+void setFrequencyTable(bool linearPeriodsFlag)
 {
 	pauseAudio();
 
-	audio.linearPeriodsFlag = linear;
+	audio.linearPeriodsFlag = linearPeriodsFlag;
 
 	if (audio.linearPeriodsFlag)
 		note2Period = linearPeriods;
@@ -310,7 +318,7 @@
 	}
 }
 
-static void retrigVolume(stmTyp *ch)
+static void retrigVolume(channel_t *ch)
 {
 	ch->realVol = ch->oldVol;
 	ch->outVol = ch->oldVol;
@@ -318,7 +326,7 @@
 	ch->status |= IS_Vol + IS_Pan + IS_QuickVol;
 }
 
-static void retrigEnvelopeVibrato(stmTyp *ch)
+static void retrigEnvelopeVibrato(channel_t *ch)
 {
 	if (!(ch->waveCtrl & 0x04)) ch->vibPos = 0;
 	if (!(ch->waveCtrl & 0x40)) ch->tremPos = 0;
@@ -328,23 +336,23 @@
 
 	ch->envSustainActive = true;
 
-	instrTyp *ins = ch->instrPtr;
+	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
-	if (ins->envVTyp & 1)
+	if (ins->volEnvFlags & ENV_ENABLED)
 	{
-		ch->envVCnt = 65535;
-		ch->envVPos = 0;
+		ch->volEnvTick = 65535;
+		ch->volEnvPos = 0;
 	}
 
-	if (ins->envPTyp & 1)
+	if (ins->panEnvFlags & ENV_ENABLED)
 	{
-		ch->envPCnt = 65535;
-		ch->envPPos = 0;
+		ch->panEnvTick = 65535;
+		ch->panEnvPos = 0;
 	}
 
-	ch->fadeOutSpeed = ins->fadeOut; // FT2 doesn't check if fadeout is more than 4095
-	ch->fadeOutAmp = 32768;
+	ch->fadeoutSpeed = ins->fadeout; // FT2 doesn't check if fadeout is more than 4095!
+	ch->fadeoutVol = 32768;
 
 	if (ins->vibDepth > 0)
 	{
@@ -363,24 +371,18 @@
 	}
 }
 
-void keyOff(stmTyp *ch)
+void keyOff(channel_t *ch)
 {
 	ch->envSustainActive = false;
 
-	instrTyp *ins = ch->instrPtr;
+	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
-	if (!(ins->envPTyp & 1)) // yes, FT2 does this (!). Most likely a bug?
+	if (ins->volEnvFlags & ENV_ENABLED)
 	{
-		if (ch->envPCnt >= (uint16_t)ins->envPP[ch->envPPos][0])
-			ch->envPCnt = ins->envPP[ch->envPPos][0] - 1;
+		if (ch->volEnvTick >= (uint16_t)ins->volEnvPoints[ch->volEnvPos][0])
+			ch->volEnvTick = ins->volEnvPoints[ch->volEnvPos][0] - 1;
 	}
-
-	if (ins->envVTyp & 1)
-	{
-		if (ch->envVCnt >= (uint16_t)ins->envVP[ch->envVPos][0])
-			ch->envVCnt = ins->envVP[ch->envVPos][0] - 1;
-	}
 	else
 	{
 		ch->realVol = 0;
@@ -387,6 +389,12 @@
 		ch->outVol = 0;
 		ch->status |= IS_Vol + IS_QuickVol;
 	}
+
+	if (!(ins->panEnvFlags & ENV_ENABLED)) // What..? Probably an FT2 bug.
+	{
+		if (ch->panEnvTick >= (uint16_t)ins->panEnvPoints[ch->panEnvPos][0])
+			ch->panEnvTick = ins->panEnvPoints[ch->panEnvPos][0] - 1;
+	}
 }
 
 void calcReplayerLogTab(void)
@@ -395,7 +403,7 @@
 		dExp2MulTab[i] = 1.0 / exp2(i); // 1/2^i
 
 	for (int32_t i = 0; i < 768; i++)
-		dLogTab[i] = 8363.0 * 256.0 * exp2(i * (1.0 / 768.0));
+		dLogTab[i] = 8363.0 * 256.0 * exp2(i / 768.0);
 }
 
 void calcReplayerVars(int32_t audioFreq)
@@ -405,19 +413,19 @@
 		return;
 
 	audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq;
-	audio.quickVolRampSamples = (int32_t)((audioFreq / 200.0) + 0.5); // rounded
-	audio.dRampQuickVolMul = 1.0 / audio.quickVolRampSamples;
+	audio.quickVolRampSamples = (int32_t)round(audioFreq / 200.0);
+	audio.fRampQuickVolMul = (float)(1.0 / audio.quickVolRampSamples);
 
-	audio.dSamplesPerTickTab[0] = 0.0;
-	audio.tickTimeTab[0] = UINT64_MAX;
-	audio.dRampTickMulTab[0] = 0.0;
-
-	for (int32_t i = MIN_BPM; i <= MAX_BPM; i++)
+	for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++)
 	{
-		const double dBpmHz = i * (1.0 / 2.5); // i / 2.5
+		const int32_t i = bpm - MIN_BPM; // index for tables
+
+		const double dBpmHz = bpm / 2.5;
 		const double dSamplesPerTick = audioFreq / dBpmHz;
-		audio.dSamplesPerTickTab[i] = dSamplesPerTick;
 
+		// convert to 32.32 fixed-point
+		audio.samplesPerTick64Tab[i] = (int64_t)((dSamplesPerTick * (UINT32_MAX+1.0)) + 0.5); // rounded
+
 		// BPM Hz -> tick length for performance counter (syncing visuals to audio)
 		double dTimeInt;
 		double dTimeFrac = modf(editor.dPerfFreq / dBpmHz, &dTimeInt);
@@ -425,11 +433,12 @@
 
 		dTimeFrac = floor((UINT32_MAX+1.0) * dTimeFrac); // fractional part (scaled to 0..2^32-1)
 
-		audio.tickTimeTab[i] = ((uint64_t)timeInt << 32) | (uint32_t)dTimeFrac;
+		audio.tickTimeTab[i] = (uint32_t)timeInt;
+		audio.tickTimeFracTab[i] = (uint32_t)dTimeFrac;
 
 		// for calculating volume ramp length for tick-lenghted ramps
-		const int32_t samplesPerTick = (int32_t)(dSamplesPerTick + 0.5); // this has to be rounded first
-		audio.dRampTickMulTab[i] = 1.0 / samplesPerTick;
+		const int32_t samplesPerTickRounded = (int32_t)(dSamplesPerTick + 0.5); // must be rounded
+		audio.fRampTickMulTab[i] = (float)(1.0 / samplesPerTickRounded);
 	}
 }
 
@@ -438,7 +447,7 @@
 	assert(note2Period != NULL);
 
 	if (period > note2Period[0])
-		return -1; // out of lower range on piano
+		return -1; // outside lower range on piano
 
 	if (audio.linearPeriodsFlag)
 	{
@@ -449,7 +458,7 @@
 		return note;
 	}
 
-	/* Amiga periods require a slower method...
+	/* Amiga periods require a slightly slower method...
 	** This is not 100% accurate for all periods, but should be faster
 	** than using log2() and floating-point arithmetics.
 	*/
@@ -478,9 +487,9 @@
 	return note;
 }
 
-static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch)
+static void startTone(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch)
 {
-	if (ton == 97)
+	if (note == NOTE_OFF)
 	{
 		keyOff(ch);
 		return;
@@ -487,17 +496,17 @@
 	}
 
 	// if we came from Rxy (retrig), we didn't check note (Ton) yet
-	if (ton == 0)
+	if (note == 0)
 	{
-		ton = ch->tonNr;
-		if (ton == 0)
+		note = ch->noteNum;
+		if (note == 0)
 			return; // if still no note, exit from routine
 	}
 
-	ch->tonNr = ton;
+	ch->noteNum = note;
 
-	assert(ch->instrNr <= 130);
-	instrTyp *ins = instr[ch->instrNr];
+	assert(ch->instrNum <= 130);
+	instr_t *ins = instr[ch->instrNum];
 	if (ins == NULL)
 		ins = instr[0]; // empty instruments use this placeholder instrument
 
@@ -504,31 +513,30 @@
 	ch->instrPtr = ins;
 	ch->mute = ins->mute;
 
-	if (ton > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now)
-		ton = 96;
+	if (note > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now)
+		note = 96;
 
-	const uint8_t smp = ins->ta[ton-1] & 0xF;
-	ch->sampleNr = smp;
+	ch->smpNum = ins->note2SampleLUT[note-1] & 0xF; // FT2 doesn't AND here, but let's do it for safety
+	sample_t *s = &ins->smp[ch->smpNum];
 
-	sampleTyp *s = &ins->samp[smp];
 	ch->smpPtr = s;
-	ch->relTonNr = s->relTon;
+	ch->relativeNote = s->relativeNote;
 
-	ton += ch->relTonNr;
-	if (ton >= 10*12)
+	note += ch->relativeNote;
+	if (note >= 10*12)
 		return;
 
-	ch->oldVol = s->vol;
-	ch->oldPan = s->pan;
+	ch->oldVol = s->volume;
+	ch->oldPan = s->panning;
 
-	if (effTyp == 0x0E && (eff & 0xF0) == 0x50)
-		ch->fineTune = ((eff & 0x0F) << 4) - 128; // result is now -128..127
+	if (efx == 0xE && (efxData & 0xF0) == 0x50)
+		ch->finetune = ((efxData & 0x0F) << 4) - 128;
 	else
-		ch->fineTune = s->fine;
+		ch->finetune = s->finetune;
 
-	if (ton != 0)
+	if (note != 0)
 	{
-		const uint16_t tmpTon = ((ton-1) << 4) + (((int8_t)ch->fineTune >> 3) + 16); // 0..1935
+		const uint16_t tmpTon = ((note-1) << 4) + (((int8_t)ch->finetune >> 3) + 16); // 0..1935
 		if (tmpTon < MAX_NOTES) // tmpTon is *always* below MAX_NOTES here, so this check is not really needed
 		{
 			assert(note2Period != NULL);
@@ -536,12 +544,12 @@
 		}
 	}
 
-	ch->status |= IS_Period + IS_Vol + IS_Pan + IS_NyTon + IS_QuickVol;
+	ch->status |= IS_Period + IS_Vol + IS_Pan + IS_Trigger + IS_QuickVol;
 
-	if (effTyp == 9)
+	if (efx == 9)
 	{
-		if (eff)
-			ch->smpOffset = ch->eff;
+		if (efxData > 0)
+			ch->smpOffset = ch->efxData;
 
 		ch->smpStartPos = ch->smpOffset << 8;
 	}
@@ -551,11 +559,11 @@
 	}
 }
 
-static void volume(stmTyp *ch, uint8_t param); // volume slide
-static void vibrato2(stmTyp *ch);
-static void tonePorta(stmTyp *ch, uint8_t param);
+static void volume(channel_t *ch, uint8_t param); // volume slide
+static void vibrato2(channel_t *ch);
+static void tonePorta(channel_t *ch, uint8_t param);
 
-static void dummy(stmTyp *ch, uint8_t param)
+static void dummy(channel_t *ch, uint8_t param)
 {
 	(void)ch;
 	(void)param;
@@ -562,7 +570,7 @@
 	return;
 }
 
-static void finePortaUp(stmTyp *ch, uint8_t param)
+static void finePortaUp(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->fPortaUpSpeed;
@@ -577,7 +585,7 @@
 	ch->status |= IS_Period;
 }
 
-static void finePortaDown(stmTyp *ch, uint8_t param)
+static void finePortaDown(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->fPortaDownSpeed;
@@ -592,42 +600,42 @@
 	ch->status |= IS_Period;
 }
 
-static void setGlissCtrl(stmTyp *ch, uint8_t param)
+static void setGlissCtrl(channel_t *ch, uint8_t param)
 {
 	ch->glissFunk = param;
 }
 
-static void setVibratoCtrl(stmTyp *ch, uint8_t param)
+static void setVibratoCtrl(channel_t *ch, uint8_t param)
 {
 	ch->waveCtrl = (ch->waveCtrl & 0xF0) | param;
 }
 
-static void jumpLoop(stmTyp *ch, uint8_t param)
+static void jumpLoop(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 	{
-		ch->pattPos = song.pattPos & 0xFF;
+		ch->jumpToRow = song.row & 0xFF;
 	}
-	else if (ch->loopCnt == 0)
+	else if (ch->patLoopCounter == 0)
 	{
-		ch->loopCnt = param;
+		ch->patLoopCounter = param;
 
-		song.pBreakPos = ch->pattPos;
+		song.pBreakPos = ch->jumpToRow;
 		song.pBreakFlag = true;
 	}
-	else if (--ch->loopCnt > 0)
+	else if (--ch->patLoopCounter > 0)
 	{
-		song.pBreakPos = ch->pattPos;
+		song.pBreakPos = ch->jumpToRow;
 		song.pBreakFlag = true;
 	}
 }
 
-static void setTremoloCtrl(stmTyp *ch, uint8_t param)
+static void setTremoloCtrl(channel_t *ch, uint8_t param)
 {
 	ch->waveCtrl = (param << 4) | (ch->waveCtrl & 0x0F);
 }
 
-static void volFineUp(stmTyp *ch, uint8_t param)
+static void volFineUp(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->fVolSlideUpSpeed;
@@ -642,7 +650,7 @@
 	ch->status |= IS_Vol;
 }
 
-static void volFineDown(stmTyp *ch, uint8_t param)
+static void volFineDown(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->fVolSlideDownSpeed;
@@ -657,7 +665,7 @@
 	ch->status |= IS_Vol;
 }
 
-static void noteCut0(stmTyp *ch, uint8_t param)
+static void noteCut0(channel_t *ch, uint8_t param)
 {
 	if (param == 0) // only a parameter of zero is handled here
 	{
@@ -667,7 +675,7 @@
 	}
 }
 
-static void pattDelay(stmTyp *ch, uint8_t param)
+static void pattDelay(channel_t *ch, uint8_t param)
 {
 	if (song.pattDelTime2 == 0)
 		song.pattDelTime = param + 1;
@@ -695,12 +703,12 @@
 	dummy // F
 };
 
-static void E_Effects_TickZero(stmTyp *ch, uint8_t param)
+static void E_Effects_TickZero(channel_t *ch, uint8_t param)
 {
 	const uint8_t efx = param >> 4;
 	param &= 0x0F;
 
-	if (ch->stOff) // channel is muted, only handle some E effects
+	if (ch->channelOff) // channel is muted, only handle some E effects
 	{
 		     if (efx == 0x6) jumpLoop(ch, param);
 		else if (efx == 0xE) pattDelay(ch, param);
@@ -711,12 +719,12 @@
 	EJumpTab_TickZero[efx](ch, param);
 }
 
-static void posJump(stmTyp *ch, uint8_t param)
+static void posJump(channel_t *ch, uint8_t param)
 {
 	if (playMode != PLAYMODE_PATT && playMode != PLAYMODE_RECPATT)
 	{
 		const int16_t pos = (int16_t)param - 1;
-		if (pos < 0 || pos >= song.len)
+		if (pos < 0 || pos >= song.songLength)
 			bxxOverflow = true; // non-FT2 security fix...
 		else
 			song.songPos = pos;
@@ -728,10 +736,8 @@
 	(void)ch;
 }
 
-static void pattBreak(stmTyp *ch, uint8_t param)
+static void pattBreak(channel_t *ch, uint8_t param)
 {
-	song.posJumpFlag = true;
-
 	param = ((param >> 4) * 10) + (param & 0x0F);
 	if (param <= 63)
 		song.pBreakPos = param;
@@ -738,66 +744,68 @@
 	else
 		song.pBreakPos = 0;
 
+	song.posJumpFlag = true;
+
 	(void)ch;
 }
 
-static void setSpeed(stmTyp *ch, uint8_t param)
+static void setSpeed(channel_t *ch, uint8_t param)
 {
 	if (param >= 32)
 	{
-		song.speed = param;
-		P_SetSpeed(song.speed);
+		song.BPM = param;
+		setMixerBPM(song.BPM);
 	}
 	else
 	{
-		song.timer = song.tempo = param;
+		song.tick = song.speed = param;
 	}
 
 	(void)ch;
 }
 
-static void setGlobaVol(stmTyp *ch, uint8_t param)
+static void setGlobalVolume(channel_t *ch, uint8_t param)
 {
 	if (param > 64)
 		param = 64;
 
-	song.globVol = param;
+	song.globalVolume = param;
 
-	stmTyp *c = stm;
-	for (int32_t i = 0; i < song.antChn; i++, c++) // update all voice volumes
+	channel_t *c = channel;
+	for (int32_t i = 0; i < song.numChannels; i++, c++) // update all voice volumes
 		c->status |= IS_Vol;
 
 	(void)ch;
 }
 
-static void setEnvelopePos(stmTyp *ch, uint8_t param)
+static void setEnvelopePos(channel_t *ch, uint8_t param)
 {
 	bool envUpdate;
 	int8_t point;
 	int16_t tick;
 
-	instrTyp *ins = ch->instrPtr;
+	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
 	// *** VOLUME ENVELOPE ***
-	if (ins->envVTyp & 1)
+	if (ins->volEnvFlags & ENV_ENABLED)
 	{
-		ch->envVCnt = param-1;
+		ch->volEnvTick = param-1;
 
 		point = 0;
 		envUpdate = true;
 		tick = param;
 
-		if (ins->envVPAnt > 1)
+		if (ins->volEnvLength > 1)
 		{
 			point++;
-			for (int32_t i = 0; i < ins->envVPAnt-1; i++)
+			for (int32_t i = 0; i < ins->volEnvLength-1; i++)
 			{
-				if (tick < ins->envVP[point][0])
+				if (tick < ins->volEnvPoints[point][0])
 				{
 					point--;
 
-					tick -= ins->envVP[point][0];
+					tick -= ins->volEnvPoints[point][0];
 					if (tick == 0)
 					{
 						envUpdate = false;
@@ -804,15 +812,18 @@
 						break;
 					}
 
-					if (ins->envVP[point+1][0] <= ins->envVP[point][0])
+					if (ins->volEnvPoints[point+1][0] <= ins->volEnvPoints[point][0])
 					{
 						envUpdate = true;
 						break;
 					}
 
-					ch->envVIPValue = ((ins->envVP[point+1][1] - ins->envVP[point][1]) << 16) / (ins->envVP[point+1][0] - ins->envVP[point][0]);
-					ch->envVAmp = (ch->envVIPValue * (tick-1)) + (ins->envVP[point][1] << 16);
+					int32_t delta = (int8_t)((ins->volEnvPoints[point+1][1] - ins->volEnvPoints[point][1]) & 0xFF) << 16;
+					delta /= (ins->volEnvPoints[point+1][0]-ins->volEnvPoints[point][0]);
 
+					ch->volEnvDelta = delta;
+					ch->volEnvValue = (ch->volEnvDelta * (tick-1)) + ((int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16);
+
 					point++;
 
 					envUpdate = false;
@@ -828,39 +839,39 @@
 
 		if (envUpdate)
 		{
-			ch->envVIPValue = 0;
-			ch->envVAmp = ins->envVP[point][1];
+			ch->volEnvDelta = 0;
+			ch->volEnvValue = (int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16;
 		}
 
-		if (point >= ins->envVPAnt)
+		if (point >= ins->volEnvLength)
 		{
-			point = ins->envVPAnt-1;
+			point = ins->volEnvLength-1;
 			if (point < 0)
 				point = 0;
 		}
 
-		ch->envVPos = point;
+		ch->volEnvPos = point;
 	}
 
 	// *** PANNING ENVELOPE ***
-	if (ins->envVTyp & 2) // probably an FT2 bug
+	if (ins->volEnvFlags & ENV_SUSTAIN) // What..? Probably an FT2 bug!
 	{
-		ch->envPCnt = param-1;
+		ch->panEnvTick = param-1;
 
 		point = 0;
 		envUpdate = true;
 		tick = param;
 
-		if (ins->envPPAnt > 1)
+		if (ins->panEnvLength > 1)
 		{
 			point++;
-			for (int32_t i = 0; i < ins->envPPAnt-1; i++)
+			for (int32_t i = 0; i < ins->panEnvLength-1; i++)
 			{
-				if (tick < ins->envPP[point][0])
+				if (tick < ins->panEnvPoints[point][0])
 				{
 					point--;
 
-					tick -= ins->envPP[point][0];
+					tick -= ins->panEnvPoints[point][0];
 					if (tick == 0)
 					{
 						envUpdate = false;
@@ -867,15 +878,18 @@
 						break;
 					}
 
-					if (ins->envPP[point+1][0] <= ins->envPP[point][0])
+					if (ins->panEnvPoints[point+1][0] <= ins->panEnvPoints[point][0])
 					{
 						envUpdate = true;
 						break;
 					}
 
-					ch->envPIPValue = ((ins->envPP[point+1][1] - ins->envPP[point][1]) << 16) / (ins->envPP[point+1][0] - ins->envPP[point][0]);
-					ch->envPAmp = (ch->envPIPValue * (tick-1)) + (ins->envPP[point][1] << 16);
+					int32_t delta = (int8_t)((ins->panEnvPoints[point+1][1] - ins->panEnvPoints[point][1]) & 0xFF) << 16;
+					delta /= (ins->panEnvPoints[point+1][0]-ins->panEnvPoints[point][0]);
 
+					ch->panEnvDelta = delta;
+					ch->panEnvValue = (ch->panEnvDelta * (tick-1)) + ((int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16);
+
 					point++;
 
 					envUpdate = false;
@@ -891,18 +905,18 @@
 
 		if (envUpdate)
 		{
-			ch->envPIPValue = 0;
-			ch->envPAmp = ins->envPP[point][1];
+			ch->panEnvDelta = 0;
+			ch->panEnvValue = (int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16;
 		}
 
-		if (point >= ins->envPPAnt)
+		if (point >= ins->panEnvLength)
 		{
-			point = ins->envPPAnt-1;
+			point = ins->panEnvLength-1;
 			if (point < 0)
 				point = 0;
 		}
 
-		ch->envPPos = point;
+		ch->panEnvPos = point;
 	}
 }
 
@@ -924,7 +938,7 @@
 	pattBreak, // D
 	E_Effects_TickZero, // E
 	setSpeed, // F
-	setGlobaVol, // G
+	setGlobalVolume, // G
 	dummy, // H
 	dummy, // I
 	dummy, // J
@@ -946,12 +960,12 @@
 	dummy  // Z
 };
 
-static void handleMoreEffects_TickZero(stmTyp *ch) // called even if channel is muted
+static void handleMoreEffects_TickZero(channel_t *ch) // called even if channel is muted
 {
-	if (ch->effTyp > 35)
+	if (ch->efx > 35)
 		return;
 
-	JumpTab_TickZero[ch->effTyp](ch, ch->eff);
+	JumpTab_TickZero[ch->efx](ch, ch->efxData);
 }
 
 /* -- tick-zero volume column effects --
@@ -958,56 +972,56 @@
 ** 2nd parameter is used for a volume column quirk with the Rxy command (multiretrig)
 */
 
-static void v_SetVibSpeed(stmTyp *ch, uint8_t *volKol)
+static void v_SetVibSpeed(channel_t *ch, uint8_t *volColumnData)
 {
-	*volKol = (ch->volKolVol & 0x0F) << 2;
-	if (*volKol != 0)
-		ch->vibSpeed = *volKol;
+	*volColumnData = (ch->volColumnVol & 0x0F) << 2;
+	if (*volColumnData != 0)
+		ch->vibSpeed = *volColumnData;
 }
 
-static void v_Volume(stmTyp *ch, uint8_t *volKol)
+static void v_Volume(channel_t *ch, uint8_t *volColumnData)
 {
-	*volKol -= 16;
-	if (*volKol > 64) // no idea why FT2 has this check...
-		*volKol = 64;
+	*volColumnData -= 16;
+	if (*volColumnData > 64) // no idea why FT2 has this check...
+		*volColumnData = 64;
 
-	ch->outVol = ch->realVol = *volKol;
+	ch->outVol = ch->realVol = *volColumnData;
 	ch->status |= IS_Vol + IS_QuickVol;
 }
 
-static void v_FineSlideDown(stmTyp *ch, uint8_t *volKol)
+static void v_FineSlideDown(channel_t *ch, uint8_t *volColumnData)
 {
-	*volKol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol;
-	if ((int8_t)*volKol < 0)
-		*volKol = 0;
+	*volColumnData = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol;
+	if ((int8_t)*volColumnData < 0)
+		*volColumnData = 0;
 
-	ch->outVol = ch->realVol = *volKol;
+	ch->outVol = ch->realVol = *volColumnData;
 	ch->status |= IS_Vol;
 }
 
-static void v_FineSlideUp(stmTyp *ch, uint8_t *volKol)
+static void v_FineSlideUp(channel_t *ch, uint8_t *volColumnData)
 {
-	*volKol = (ch->volKolVol & 0x0F) + ch->realVol;
-	if (*volKol > 64)
-		*volKol = 64;
+	*volColumnData = (ch->volColumnVol & 0x0F) + ch->realVol;
+	if (*volColumnData > 64)
+		*volColumnData = 64;
 
-	ch->outVol = ch->realVol = *volKol;
+	ch->outVol = ch->realVol = *volColumnData;
 	ch->status |= IS_Vol;
 }
 
-static void v_SetPan(stmTyp *ch, uint8_t *volKol)
+static void v_SetPan(channel_t *ch, uint8_t *volColumnData)
 {
-	*volKol <<= 4;
+	*volColumnData <<= 4;
 
-	ch->outPan = *volKol;
+	ch->outPan = *volColumnData;
 	ch->status |= IS_Pan;
 }
 
 // -- non-tick-zero volume column effects --
 
-static void v_SlideDown(stmTyp *ch)
+static void v_SlideDown(channel_t *ch)
 {
-	uint8_t newVol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol;
+	uint8_t newVol = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol;
 	if ((int8_t)newVol < 0)
 		newVol = 0;
 
@@ -1015,9 +1029,9 @@
 	ch->status |= IS_Vol;
 }
 
-static void v_SlideUp(stmTyp *ch)
+static void v_SlideUp(channel_t *ch)
 {
-	uint8_t newVol = (ch->volKolVol & 0x0F) + ch->realVol;
+	uint8_t newVol = (ch->volColumnVol & 0x0F) + ch->realVol;
 	if (newVol > 64)
 		newVol = 64;
 
@@ -1025,9 +1039,9 @@
 	ch->status |= IS_Vol;
 }
 
-static void v_Vibrato(stmTyp *ch)
+static void v_Vibrato(channel_t *ch)
 {
-	const uint8_t param = ch->volKolVol & 0xF;
+	const uint8_t param = ch->volColumnVol & 0xF;
 	if (param > 0)
 		ch->vibDepth = param;
 
@@ -1034,9 +1048,9 @@
 	vibrato2(ch);
 }
 
-static void v_PanSlideLeft(stmTyp *ch)
+static void v_PanSlideLeft(channel_t *ch)
 {
-	uint16_t tmp16 = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->outPan;
+	uint16_t tmp16 = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->outPan;
 	if (tmp16 < 256) // includes an FT2 bug: pan-slide-left of 0 = set pan to 0
 		tmp16 = 0;
 
@@ -1044,9 +1058,9 @@
 	ch->status |= IS_Pan;
 }
 
-static void v_PanSlideRight(stmTyp *ch)
+static void v_PanSlideRight(channel_t *ch)
 {
-	uint16_t tmp16 = (ch->volKolVol & 0x0F) + ch->outPan;
+	uint16_t tmp16 = (ch->volColumnVol & 0x0F) + ch->outPan;
 	if (tmp16 > 255)
 		tmp16 = 255;
 
@@ -1054,25 +1068,25 @@
 	ch->status |= IS_Pan;
 }
 
-static void v_TonePorta(stmTyp *ch)
+static void v_TonePorta(channel_t *ch)
 {
 	tonePorta(ch, 0); // the last parameter is actually not used in tonePorta()
 }
 
-static void v_dummy(stmTyp *ch)
+static void v_dummy(channel_t *ch)
 {
 	(void)ch;
 	return;
 }
 
-static void v_dummy2(stmTyp *ch, uint8_t *volKol)
+static void v_dummy2(channel_t *ch, uint8_t *volColumnData)
 {
 	(void)ch;
-	(void)volKol;
+	(void)volColumnData;
 	return;
 }
 
-static const volKolEfxRoutine VJumpTab_TickNonZero[16] =
+static const volColumnEfxRoutine VJumpTab_TickNonZero[16] =
 {
 	v_dummy,        v_dummy,         v_dummy,  v_dummy,
 	v_dummy,        v_dummy,     v_SlideDown, v_SlideUp,
@@ -1080,7 +1094,7 @@
 	v_dummy, v_PanSlideLeft, v_PanSlideRight, v_TonePorta
 };
 
-static const volKolEfxRoutine2 VJumpTab_TickZero[16] =
+static const volColumnEfxRoutine2 VJumpTab_TickZero[16] =
 {
 	       v_dummy2,      v_Volume,      v_Volume, v_Volume,
 	       v_Volume,      v_Volume,      v_dummy2, v_dummy2,
@@ -1088,13 +1102,13 @@
 	       v_SetPan,      v_dummy2,      v_dummy2, v_dummy2
 };
 
-static void setPan(stmTyp *ch, uint8_t param)
+static void setPan(channel_t *ch, uint8_t param)
 {
 	ch->outPan = param;
 	ch->status |= IS_Pan;
 }
 
-static void setVol(stmTyp *ch, uint8_t param)
+static void setVol(channel_t *ch, uint8_t param)
 {
 	if (param > 64)
 		param = 64;
@@ -1103,7 +1117,7 @@
 	ch->status |= IS_Vol + IS_QuickVol;
 }
 
-static void xFinePorta(stmTyp *ch, uint8_t param)
+static void xFinePorta(channel_t *ch, uint8_t param)
 {
 	const uint8_t type = param >> 4;
 	param &= 0x0F;
@@ -1142,7 +1156,7 @@
 	}
 }
 
-static void doMultiRetrig(stmTyp *ch, uint8_t param) // "param" is never used (needed for efx jumptable structure)
+static void doMultiRetrig(channel_t *ch, uint8_t param) // "param" is never used (needed for efx jumptable structure)
 {
 	uint8_t cnt = ch->retrigCnt + 1;
 	if (cnt < ch->retrigSpeed)
@@ -1178,14 +1192,14 @@
 	ch->realVol = (uint8_t)vol;
 	ch->outVol = ch->realVol;
 
-	if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50)
+	if (ch->volColumnVol >= 0x10 && ch->volColumnVol <= 0x50)
 	{
-		ch->outVol = ch->volKolVol - 0x10;
+		ch->outVol = ch->volColumnVol - 0x10;
 		ch->realVol = ch->outVol;
 	}
-	else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF)
+	else if (ch->volColumnVol >= 0xC0 && ch->volColumnVol <= 0xCF)
 	{
-		ch->outPan = (ch->volKolVol & 0x0F) << 4;
+		ch->outPan = (ch->volColumnVol & 0x0F) << 4;
 	}
 
 	startTone(0, 0, 0, ch);
@@ -1193,7 +1207,7 @@
 	(void)param;
 }
 
-static void multiRetrig(stmTyp *ch, uint8_t param, uint8_t volumeColumnData)
+static void multiRetrig(channel_t *ch, uint8_t param, uint8_t volumeColumnData)
 {
 	uint8_t tmpParam;
 
@@ -1213,36 +1227,36 @@
 		doMultiRetrig(ch, 0); // the second parameter is never used (needed for efx jumptable structure)
 }
 
-static void handleEffects_TickZero(stmTyp *ch)
+static void handleEffects_TickZero(channel_t *ch)
 {
 	// volume column effects
-	uint8_t newVolKol = ch->volKolVol; // manipulated by vol. column effects, then used for multiretrig check (FT2 quirk)
-	VJumpTab_TickZero[ch->volKolVol >> 4](ch, &newVolKol);
+	uint8_t newVolCol = ch->volColumnVol; // manipulated by vol. column effects, then used for multiretrig check (FT2 quirk)
+	VJumpTab_TickZero[ch->volColumnVol >> 4](ch, &newVolCol);
 
 	// normal effects
-	const uint8_t param = ch->eff;
-	if (ch->effTyp == 0 && param == 0)
+	const uint8_t param = ch->efxData;
+	if (ch->efx == 0 && param == 0)
 		return; // no effect
 
-	     if (ch->effTyp ==  8) setPan(ch, param);
-	else if (ch->effTyp == 12) setVol(ch, param);
-	else if (ch->effTyp == 27) multiRetrig(ch, param, newVolKol);
-	else if (ch->effTyp == 33) xFinePorta(ch, param);
+	     if (ch->efx ==  8) setPan(ch, param);
+	else if (ch->efx == 12) setVol(ch, param);
+	else if (ch->efx == 27) multiRetrig(ch, param, newVolCol);
+	else if (ch->efx == 33) xFinePorta(ch, param);
 
 	handleMoreEffects_TickZero(ch);
 }
 
-static void fixTonePorta(stmTyp *ch, const tonTyp *p, uint8_t inst)
+static void fixTonePorta(channel_t *ch, const note_t *p, uint8_t inst)
 {
-	if (p->ton > 0)
+	if (p->note > 0)
 	{
-		if (p->ton == 97)
+		if (p->note == NOTE_OFF)
 		{
 			keyOff(ch);
 		}
 		else
 		{
-			const uint16_t note = (((p->ton-1) + ch->relTonNr) << 4) + (((int8_t)ch->fineTune >> 3) + 16);
+			const uint16_t note = (((p->note-1) + ch->relativeNote) << 4) + (((int8_t)ch->finetune >> 3) + 16);
 			if (note < MAX_NOTES)
 			{
 				assert(note2Period != NULL);
@@ -1249,11 +1263,11 @@
 				ch->wantPeriod = note2Period[note];
 
 				if (ch->wantPeriod == ch->realPeriod)
-					ch->portaDir = 0;
+					ch->portaDirection = 0;
 				else if (ch->wantPeriod > ch->realPeriod)
-					ch->portaDir = 1;
+					ch->portaDirection = 1;
 				else
-					ch->portaDir = 2;
+					ch->portaDirection = 2;
 			}
 		}
 	}
@@ -1261,18 +1275,18 @@
 	if (inst > 0)
 	{
 		retrigVolume(ch);
-		if (p->ton != 97)
+		if (p->note != NOTE_OFF)
 			retrigEnvelopeVibrato(ch);
 	}
 }
 
-static void getNewNote(stmTyp *ch, const tonTyp *p)
+static void getNewNote(channel_t *ch, const note_t *p)
 {
-	ch->volKolVol = p->vol;
+	ch->volColumnVol = p->vol;
 
-	if (ch->effTyp == 0)
+	if (ch->efx == 0)
 	{
-		if (ch->eff > 0) // we have an arpeggio running, set period back
+		if (ch->efxData > 0) // we have an arpeggio running, set period back
 		{
 			ch->outPeriod = ch->realPeriod;
 			ch->status |= IS_Period;
@@ -1281,7 +1295,7 @@
 	else
 	{
 		// if we have a vibrato on previous row (ch) that ends at current row (p), set period back
-		if ((ch->effTyp == 4 || ch->effTyp == 6) && (p->effTyp != 4 && p->effTyp != 6))
+		if ((ch->efx == 4 || ch->efx == 6) && (p->efx != 4 && p->efx != 6))
 		{
 			ch->outPeriod = ch->realPeriod;
 			ch->status |= IS_Period;
@@ -1288,11 +1302,11 @@
 		}
 	}
 
-	ch->effTyp = p->effTyp;
-	ch->eff = p->eff;
-	ch->tonTyp = (p->instr << 8) | p->ton;
+	ch->efx = p->efx;
+	ch->efxData = p->efxData;
+	ch->noteData = (p->instr << 8) | p->note;
 
-	if (ch->stOff) // channel is muted, only handle some effects
+	if (ch->channelOff) // channel is muted, only handle some effects
 	{
 		handleMoreEffects_TickZero(ch);
 		return;
@@ -1303,27 +1317,27 @@
 	if (inst > 0)
 	{
 		if (inst <= MAX_INST)
-			ch->instrNr = inst;
+			ch->instrNum = inst;
 		else
 			inst = 0;
 	}
 
 	bool checkEfx = true;
-	if (p->effTyp == 0x0E)
+	if (p->efx == 0x0E)
 	{
-		if (p->eff >= 0xD1 && p->eff <= 0xDF)
+		if (p->efxData >= 0xD1 && p->efxData <= 0xDF)
 			return; // we have a note delay (ED1..EDF)
-		else if (p->eff == 0x90)
+		else if (p->efxData == 0x90)
 			checkEfx = false;
 	}
 
 	if (checkEfx)
 	{
-		if ((ch->volKolVol & 0xF0) == 0xF0) // gxx
+		if ((ch->volColumnVol & 0xF0) == 0xF0) // gxx
 		{
-			const uint8_t volKolParam = ch->volKolVol & 0x0F;
-			if (volKolParam > 0)
-				ch->portaSpeed = volKolParam << 6;
+			const uint8_t volColumnData = ch->volColumnVol & 0x0F;
+			if (volColumnData > 0)
+				ch->portaSpeed = volColumnData << 6;
 
 			fixTonePorta(ch, p, inst);
 			handleEffects_TickZero(ch);
@@ -1330,10 +1344,10 @@
 			return;
 		}
 
-		if (p->effTyp == 3 || p->effTyp == 5) // 3xx or 5xx
+		if (p->efx == 3 || p->efx == 5) // 3xx or 5xx
 		{
-			if (p->effTyp != 5 && p->eff != 0)
-				ch->portaSpeed = p->eff << 2;
+			if (p->efx != 5 && p->efxData != 0)
+				ch->portaSpeed = p->efxData << 2;
 
 			fixTonePorta(ch, p, inst);
 			handleEffects_TickZero(ch);
@@ -1340,7 +1354,7 @@
 			return;
 		}
 
-		if (p->effTyp == 0x14 && p->eff == 0) // K00 (KeyOff - only handle tick 0 here)
+		if (p->efx == 0x14 && p->efxData == 0) // K00 (KeyOff - only handle tick 0 here)
 		{
 			keyOff(ch);
 
@@ -1351,7 +1365,7 @@
 			return;
 		}
 
-		if (p->ton == 0)
+		if (p->note == 0)
 		{
 			if (inst > 0)
 			{
@@ -1364,15 +1378,15 @@
 		}
 	}
 
-	if (p->ton == 97)
+	if (p->note == NOTE_OFF)
 		keyOff(ch);
 	else
-		startTone(p->ton, p->effTyp, p->eff, ch);
+		startTone(p->note, p->efx, p->efxData, ch);
 
 	if (inst > 0)
 	{
 		retrigVolume(ch);
-		if (p->ton != 97)
+		if (p->note != NOTE_OFF)
 			retrigEnvelopeVibrato(ch);
 	}
 
@@ -1379,7 +1393,7 @@
 	handleEffects_TickZero(ch);
 }
 
-static void fixaEnvelopeVibrato(stmTyp *ch)
+static void updateChannel(channel_t *ch)
 {
 	bool envInterpolateFlag, envDidInterpolate;
 	uint8_t envPos;
@@ -1386,9 +1400,9 @@
 	int16_t autoVibVal;
 	uint16_t autoVibAmp;
 	int32_t envVal;
-	double dVol;
+	float fVol;
 
-	instrTyp *ins = ch->instrPtr;
+	instr_t *ins = ch->instrPtr;
 	assert(ins != NULL);
 
 	// *** FADEOUT ***
@@ -1397,14 +1411,14 @@
 		ch->status |= IS_Vol;
 
 		// unsigned clamp + reset
-		if (ch->fadeOutAmp >= ch->fadeOutSpeed)
+		if (ch->fadeoutVol >= ch->fadeoutSpeed)
 		{
-			ch->fadeOutAmp -= ch->fadeOutSpeed;
+			ch->fadeoutVol -= ch->fadeoutSpeed;
 		}
 		else
 		{
-			ch->fadeOutAmp = 0;
-			ch->fadeOutSpeed = 0;
+			ch->fadeoutVol = 0;
+			ch->fadeoutSpeed = 0;
 		}
 	}
 
@@ -1412,27 +1426,27 @@
 	{
 		// *** VOLUME ENVELOPE ***
 		envVal = 0;
-		if (ins->envVTyp & 1)
+		if (ins->volEnvFlags & ENV_ENABLED)
 		{
 			envDidInterpolate = false;
-			envPos = ch->envVPos;
+			envPos = ch->volEnvPos;
 
-			if (++ch->envVCnt == ins->envVP[envPos][0])
+			if (++ch->volEnvTick == ins->volEnvPoints[envPos][0])
 			{
-				ch->envVAmp = ins->envVP[envPos][1] << 16;
+				ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16;
 
 				envPos++;
-				if (ins->envVTyp & 4)
+				if (ins->volEnvFlags & ENV_LOOP)
 				{
 					envPos--;
 
-					if (envPos == ins->envVRepE)
+					if (envPos == ins->volEnvLoopEnd)
 					{
-						if (!(ins->envVTyp & 2) || envPos != ins->envVSust || ch->envSustainActive)
+						if (!(ins->volEnvFlags & ENV_SUSTAIN) || envPos != ins->volEnvSustain || ch->envSustainActive)
 						{
-							envPos = ins->envVRepS;
-							ch->envVCnt = ins->envVP[envPos][0];
-							ch->envVAmp = ins->envVP[envPos][1] << 16;
+							envPos = ins->volEnvLoopStart;
+							ch->volEnvTick = ins->volEnvPoints[envPos][0];
+							ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16;
 						}
 					}
 
@@ -1439,15 +1453,15 @@
 					envPos++;
 				}
 
-				if (envPos < ins->envVPAnt)
+				if (envPos < ins->volEnvLength)
 				{
 					envInterpolateFlag = true;
-					if ((ins->envVTyp & 2) && ch->envSustainActive)
+					if ((ins->volEnvFlags & ENV_SUSTAIN) && ch->envSustainActive)
 					{
-						if (envPos-1 == ins->envVSust)
+						if (envPos-1 == ins->volEnvSustain)
 						{
 							envPos--;
-							ch->envVIPValue = 0;
+							ch->volEnvDelta = 0;
 							envInterpolateFlag = false;
 						}
 					}
@@ -1454,14 +1468,16 @@
 
 					if (envInterpolateFlag)
 					{
-						ch->envVPos = envPos;
+						ch->volEnvPos = envPos;
 
-						ch->envVIPValue = 0;
-						if (ins->envVP[envPos][0] > ins->envVP[envPos-1][0])
+						ch->volEnvDelta = 0;
+						if (ins->volEnvPoints[envPos][0] > ins->volEnvPoints[envPos-1][0])
 						{
-							ch->envVIPValue = ((ins->envVP[envPos][1] - ins->envVP[envPos-1][1]) << 16) / (ins->envVP[envPos][0] - ins->envVP[envPos-1][0]);
+							int32_t delta = (int8_t)((ins->volEnvPoints[envPos][1] - ins->volEnvPoints[envPos-1][1]) & 0xFF) << 16;
+							delta /= (ins->volEnvPoints[envPos][0]-ins->volEnvPoints[envPos-1][0]);
+							ch->volEnvDelta = delta;
 
-							envVal = ch->envVAmp;
+							envVal = ch->volEnvValue;
 							envDidInterpolate = true;
 						}
 					}
@@ -1468,15 +1484,15 @@
 				}
 				else
 				{
-					ch->envVIPValue = 0;
+					ch->volEnvDelta = 0;
 				}
 			}
 
 			if (!envDidInterpolate)
 			{
-				ch->envVAmp += ch->envVIPValue;
+				ch->volEnvValue += ch->volEnvDelta;
 
-				envVal = ch->envVAmp;
+				envVal = ch->volEnvValue;
 				if (envVal > 64<<16)
 				{
 					if (envVal > 128<<16)
@@ -1484,60 +1500,58 @@
 					else
 						envVal = 0;
 
-					ch->envVIPValue = 0;
+					ch->volEnvDelta = 0;
 				}
 			}
 
-			const int32_t vol = song.globVol * ch->outVol * ch->fadeOutAmp;
+			const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
 
-			dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0));
-			dVol *= (int32_t)envVal * (1.0 / (64.0 * (1 << 16)));
+			fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
+			fVol *= (int32_t)envVal * (1.0f / (64.0f * (1 << 16))); // volume envelope value
 
-			ch->status |= IS_Vol; // update vol every tick because vol envelope is enabled
+			ch->status |= IS_Vol; // update mixer vol every tick when vol envelope is enabled
 		}
 		else
 		{
-			const int32_t vol = song.globVol * ch->outVol * ch->fadeOutAmp;
-			dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0));
+			const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol;
+			fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f));
 		}
 
-		if (dVol > 1.0) // shouldn't happen, but just in case...
-			dVol = 1.0;
-
-		ch->dFinalVol = dVol;
-		ch->finalVol = (uint8_t)(int32_t)((ch->dFinalVol * 255) + 0.5); // 0.0 .. 1.0 -> 0..255 rounded, used for visuals
+		/* FT2 doesn't clamp here, but it's actually important if you
+		** move envelope points with the mouse while playing the instrument.
+		*/
+		ch->fFinalVol = CLAMP(fVol, 0.0f, 1.0f);
 	}
 	else
 	{
-		ch->dFinalVol = 0.0;
-		ch->finalVol = 0;
+		ch->fFinalVol = 0.0f;
 	}
 
 	// *** PANNING ENVELOPE ***
 
 	envVal = 0;
-	if (ins->envPTyp & 1)
+	if (ins->panEnvFlags & ENV_ENABLED)
 	{
 		envDidInterpolate = false;
-		envPos = ch->envPPos;
+		envPos = ch->panEnvPos;
 
-		if (++ch->envPCnt == ins->envPP[envPos][0])
+		if (++ch->panEnvTick == ins->panEnvPoints[envPos][0])
 		{
-			ch->envPAmp = ins->envPP[envPos][1] << 16;
+			ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16;
 
 			envPos++;
-			if (ins->envPTyp & 4)
+			if (ins->panEnvFlags & ENV_LOOP)
 			{
 				envPos--;
 
-				if (envPos == ins->envPRepE)
+				if (envPos == ins->panEnvLoopEnd)
 				{
-					if (!(ins->envPTyp & 2) || envPos != ins->envPSust || ch->envSustainActive)
+					if (!(ins->panEnvFlags & ENV_SUSTAIN) || envPos != ins->panEnvSustain || ch->envSustainActive)
 					{
-						envPos = ins->envPRepS;
+						envPos = ins->panEnvLoopStart;
 
-						ch->envPCnt = ins->envPP[envPos][0];
-						ch->envPAmp = ins->envPP[envPos][1] << 16;
+						ch->panEnvTick = ins->panEnvPoints[envPos][0];
+						ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16;
 					}
 				}
 
@@ -1544,15 +1558,15 @@
 				envPos++;
 			}
 
-			if (envPos < ins->envPPAnt)
+			if (envPos < ins->panEnvLength)
 			{
 				envInterpolateFlag = true;
-				if ((ins->envPTyp & 2) && ch->envSustainActive)
+				if ((ins->panEnvFlags & ENV_SUSTAIN) && ch->envSustainActive)
 				{
-					if (envPos-1 == ins->envPSust)
+					if (envPos-1 == ins->panEnvSustain)
 					{
 						envPos--;
-						ch->envPIPValue = 0;
+						ch->panEnvDelta = 0;
 						envInterpolateFlag = false;
 					}
 				}
@@ -1559,14 +1573,16 @@
 
 				if (envInterpolateFlag)
 				{
-					ch->envPPos = envPos;
+					ch->panEnvPos = envPos;
 
-					ch->envPIPValue = 0;
-					if (ins->envPP[envPos][0] > ins->envPP[envPos-1][0])
+					ch->panEnvDelta = 0;
+					if (ins->panEnvPoints[envPos][0] > ins->panEnvPoints[envPos-1][0])
 					{
-						ch->envPIPValue = ((ins->envPP[envPos][1] - ins->envPP[envPos-1][1]) << 16) / (ins->envPP[envPos][0] - ins->envPP[envPos-1][0]);
+						int32_t delta = (int8_t)((ins->panEnvPoints[envPos][1] - ins->panEnvPoints[envPos-1][1]) & 0xFF) << 16;
+						delta /= (ins->panEnvPoints[envPos][0]-ins->panEnvPoints[envPos-1][0]);
+						ch->panEnvDelta = delta;
 
-						envVal = ch->envPAmp;
+						envVal = ch->panEnvValue;
 						envDidInterpolate = true;
 					}
 				}
@@ -1573,15 +1589,15 @@
 			}
 			else
 			{
-				ch->envPIPValue = 0;
+				ch->panEnvDelta = 0;
 			}
 		}
 
 		if (!envDidInterpolate)
 		{
-			ch->envPAmp += ch->envPIPValue;
+			ch->panEnvValue += ch->panEnvDelta;
 
-			envVal = ch->envPAmp;
+			envVal = ch->panEnvValue;
 			if (envVal > 64<<16)
 			{
 				if (envVal > 128<<16)
@@ -1589,7 +1605,7 @@
 				else
 					envVal = 0;
 
-				ch->envPIPValue = 0;
+				ch->panEnvDelta = 0;
 			}
 		}
 
@@ -1597,7 +1613,7 @@
 
 		const int32_t pan = 128 - ABS(ch->outPan-128);
 		const int32_t panAdd = (pan * envVal) >> (16+5);
-		ch->finalPan = (uint8_t)CLAMP(ch->outPan + panAdd, 0, 255);
+		ch->finalPan = (uint8_t)(ch->outPan + panAdd);
 
 		ch->status |= IS_Pan; // update pan every tick because pan envelope is enabled
 	}
@@ -1642,9 +1658,9 @@
 #endif
 		ch->eVibPos += ins->vibRate;
 
-		     if (ins->vibTyp == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square
-		else if (ins->vibTyp == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up
-		else if (ins->vibTyp == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down
+		     if (ins->vibType == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square
+		else if (ins->vibType == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up
+		else if (ins->vibType == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down
 		else autoVibVal = vibSineTab[ch->eVibPos]; // sine
 
 		autoVibVal <<= 2;
@@ -1677,11 +1693,11 @@
 }
 
 // for arpeggio and portamento (semitone-slide mode)
-static uint16_t relocateTon(uint16_t period, uint8_t arpNote, stmTyp *ch)
+static uint16_t relocateTon(uint16_t period, uint8_t arpNote, channel_t *ch)
 {
 	int32_t tmpPeriod;
 
-	const int32_t fineTune = ((int8_t)ch->fineTune >> 3) + 16;
+	const int32_t fineTune = ((int8_t)ch->finetune >> 3) + 16;
 
 	/* FT2 bug, should've been 10*12*16. Notes above B-7 (95) will have issues.
 	** You can only achieve such high notes by having a high relative note setting.
@@ -1711,7 +1727,7 @@
 	return note2Period[tmpPeriod];
 }
 
-static void vibrato2(stmTyp *ch)
+static void vibrato2(channel_t *ch)
 {
 	uint8_t tmpVib = (ch->vibPos >> 2) & 0x1F;
 
@@ -1744,11 +1760,11 @@
 	ch->vibPos += ch->vibSpeed;
 }
 
-static void arp(stmTyp *ch, uint8_t param)
+static void arp(channel_t *ch, uint8_t param)
 {
 	uint8_t note;
 
-	const uint8_t tick = arpTab[song.timer & 0xFF]; // non-FT2 protection (we have 248 extra overflow bytes in LUT, but not more!)
+	const uint8_t tick = arpTab[song.tick & 0xFF]; // non-FT2 protection (we have 248 extra overflow bytes in LUT, but not more!)
 	if (tick == 0)
 	{
 		ch->outPeriod = ch->realPeriod;
@@ -1766,7 +1782,7 @@
 	ch->status |= IS_Period;
 }
 
-static void portaUp(stmTyp *ch, uint8_t param)
+static void portaUp(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->portaUpSpeed;
@@ -1781,7 +1797,7 @@
 	ch->status |= IS_Period;
 }
 
-static void portaDown(stmTyp *ch, uint8_t param)
+static void portaDown(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->portaDownSpeed;
@@ -1796,17 +1812,17 @@
 	ch->status |= IS_Period;
 }
 
-static void tonePorta(stmTyp *ch, uint8_t param)
+static void tonePorta(channel_t *ch, uint8_t param)
 {
-	if (ch->portaDir == 0)
+	if (ch->portaDirection == 0)
 		return;
 
-	if (ch->portaDir > 1)
+	if (ch->portaDirection > 1)
 	{
 		ch->realPeriod -= ch->portaSpeed;
 		if ((int16_t)ch->realPeriod <= (int16_t)ch->wantPeriod)
 		{
-			ch->portaDir = 1;
+			ch->portaDirection = 1;
 			ch->realPeriod = ch->wantPeriod;
 		}
 	}
@@ -1815,7 +1831,7 @@
 		ch->realPeriod += ch->portaSpeed;
 		if (ch->realPeriod >= ch->wantPeriod)
 		{
-			ch->portaDir = 1;
+			ch->portaDirection = 1;
 			ch->realPeriod = ch->wantPeriod;
 		}
 	}
@@ -1830,11 +1846,11 @@
 	(void)param;
 }
 
-static void vibrato(stmTyp *ch, uint8_t param)
+static void vibrato(channel_t *ch, uint8_t param)
 {
 	uint8_t tmp8;
 
-	if (ch->eff > 0)
+	if (param > 0)
 	{
 		tmp8 = param & 0x0F;
 		if (tmp8 > 0)
@@ -1848,7 +1864,7 @@
 	vibrato2(ch);
 }
 
-static void tonePlusVol(stmTyp *ch, uint8_t param)
+static void tonePlusVol(channel_t *ch, uint8_t param)
 {
 	tonePorta(ch, 0); // the last parameter is actually not used in tonePorta()
 	volume(ch, param);
@@ -1856,7 +1872,7 @@
 	(void)param;
 }
 
-static void vibratoPlusVol(stmTyp *ch, uint8_t param)
+static void vibratoPlusVol(channel_t *ch, uint8_t param)
 {
 	vibrato2(ch);
 	volume(ch, param);
@@ -1864,7 +1880,7 @@
 	(void)param;
 }
 
-static void tremolo(stmTyp *ch, uint8_t param)
+static void tremolo(channel_t *ch, uint8_t param)
 {
 	uint8_t tmp8;
 	int16_t tremVol;
@@ -1919,7 +1935,7 @@
 	ch->tremPos += ch->tremSpeed;
 }
 
-static void volume(stmTyp *ch, uint8_t param) // volume slide
+static void volume(channel_t *ch, uint8_t param) // volume slide
 {
 	if (param == 0)
 		param = ch->volSlideSpeed;
@@ -1946,7 +1962,7 @@
 	ch->status |= IS_Vol;
 }
 
-static void globalVolSlide(stmTyp *ch, uint8_t param)
+static void globalVolSlide(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->globVolSlideSpeed;
@@ -1953,7 +1969,7 @@
 
 	ch->globVolSlideSpeed = param;
 
-	uint8_t newVol = (uint8_t)song.globVol;
+	uint8_t newVol = (uint8_t)song.globalVolume;
 	if ((param & 0xF0) == 0)
 	{
 		newVol -= param;
@@ -1969,20 +1985,20 @@
 			newVol = 64;
 	}
 
-	song.globVol = newVol;
+	song.globalVolume = newVol;
 
-	stmTyp *c = stm;
-	for (int32_t i = 0; i < song.antChn; i++, c++) // update all voice volumes
+	channel_t *c = channel;
+	for (int32_t i = 0; i < song.numChannels; i++, c++) // update all voice volumes
 		c->status |= IS_Vol;
 }
 
-static void keyOffCmd(stmTyp *ch, uint8_t param)
+static void keyOffCmd(channel_t *ch, uint8_t param)
 {
-	if ((uint8_t)(song.tempo-song.timer) == (param & 31))
+	if ((uint8_t)(song.speed-song.tick) == (param & 31))
 		keyOff(ch);
 }
 
-static void panningSlide(stmTyp *ch, uint8_t param)
+static void panningSlide(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->panningSlideSpeed;
@@ -2009,7 +2025,7 @@
 	ch->status |= IS_Pan;
 }
 
-static void tremor(stmTyp *ch, uint8_t param)
+static void tremor(channel_t *ch, uint8_t param)
 {
 	if (param == 0)
 		param = ch->tremorSave;
@@ -2039,12 +2055,12 @@
 	ch->status |= IS_Vol + IS_QuickVol;
 }
 
-static void retrigNote(stmTyp *ch, uint8_t param)
+static void retrigNote(channel_t *ch, uint8_t param)
 {
 	if (param == 0) // E9x with a param of zero is handled in getNewNote()
 		return;
 
-	if ((song.tempo-song.timer) % param == 0)
+	if ((song.speed-song.tick) % param == 0)
 	{
 		startTone(0, 0, 0, ch);
 		retrigEnvelopeVibrato(ch);
@@ -2051,9 +2067,9 @@
 	}
 }
 
-static void noteCut(stmTyp *ch, uint8_t param)
+static void noteCut(channel_t *ch, uint8_t param)
 {
-	if ((uint8_t)(song.tempo-song.timer) == param)
+	if ((uint8_t)(song.speed-song.tick) == param)
 	{
 		ch->outVol = ch->realVol = 0;
 		ch->status |= IS_Vol + IS_QuickVol;
@@ -2060,25 +2076,25 @@
 	}
 }
 
-static void noteDelay(stmTyp *ch, uint8_t param)
+static void noteDelay(channel_t *ch, uint8_t param)
 {
-	if ((uint8_t)(song.tempo-song.timer) == param)
+	if ((uint8_t)(song.speed-song.tick) == param)
 	{
-		startTone(ch->tonTyp & 0xFF, 0, 0, ch);
+		startTone(ch->noteData & 0xFF, 0, 0, ch);
 
-		if ((ch->tonTyp & 0xFF00) > 0)
+		if ((ch->noteData & 0xFF00) > 0)
 			retrigVolume(ch);
 
 		retrigEnvelopeVibrato(ch);
 
-		if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50)
+		if (ch->volColumnVol >= 0x10 && ch->volColumnVol <= 0x50)
 		{
-			ch->outVol = ch->volKolVol - 16;
+			ch->outVol = ch->volColumnVol - 16;
 			ch->realVol = ch->outVol;
 		}
-		else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF)
+		else if (ch->volColumnVol >= 0xC0 && ch->volColumnVol <= 0xCF)
 		{
-			ch->outPan = (ch->volKolVol & 0x0F) << 4;
+			ch->outPan = (ch->volColumnVol & 0x0F) << 4;
 		}
 	}
 }
@@ -2103,7 +2119,7 @@
 	dummy // F
 };
 
-static void E_Effects_TickNonZero(stmTyp *ch, uint8_t param)
+static void E_Effects_TickNonZero(channel_t *ch, uint8_t param)
 {
 	EJumpTab_TickNonZero[param >> 4](ch, param & 0xF);
 }
@@ -2148,27 +2164,27 @@
 	dummy  // Z
 };
 
-static void handleEffects_TickNonZero(stmTyp *ch)
+static void handleEffects_TickNonZero(channel_t *ch)
 {
-	if (ch->stOff)
+	if (ch->channelOff)
 		return; // muted
 
 	// volume column effects
-	VJumpTab_TickNonZero[ch->volKolVol >> 4](ch);
+	VJumpTab_TickNonZero[ch->volColumnVol >> 4](ch);
 
 	// normal effects
-	if ((ch->eff == 0 && ch->effTyp == 0) || ch->effTyp > 35)
+	if ((ch->efx == 0 && ch->efxData == 0) || ch->efx > 35)
 		return; // no effect
 
-	JumpTab_TickNonZero[ch->effTyp](ch, ch->eff);
+	JumpTab_TickNonZero[ch->efx](ch, ch->efxData);
 }
 
 static void getNextPos(void)
 {
-	if (song.timer != 1)
+	if (song.tick != 1)
 		return;
 
-	song.pattPos++;
+	song.row++;
 
 	if (song.pattDelTime > 0)
 	{
@@ -2180,18 +2196,18 @@
 	{
 		song.pattDelTime2--;
 		if (song.pattDelTime2 > 0)
-			song.pattPos--;
+			song.row--;
 	}
 
 	if (song.pBreakFlag)
 	{
 		song.pBreakFlag = false;
-		song.pattPos = song.pBreakPos;
+		song.row = song.pBreakPos;
 	}
 
-	if (song.pattPos >= song.pattLen || song.posJumpFlag)
+	if (song.row >= song.currNumRows || song.posJumpFlag)
 	{
-		song.pattPos = song.pBreakPos;
+		song.row = song.pBreakPos;
 		song.pBreakPos = 0;
 		song.posJumpFlag = false;
 
@@ -2202,15 +2218,15 @@
 				song.songPos = 0;
 				bxxOverflow = false;
 			}
-			else if (++song.songPos >= song.len)
+			else if (++song.songPos >= song.songLength)
 			{
 				editor.wavReachedEndFlag = true;
-				song.songPos = song.repS;
+				song.songPos = song.songLoopStart;
 			}
 
 			assert(song.songPos <= 255);
-			song.pattNr = song.songTab[song.songPos & 0xFF];
-			song.pattLen = pattLens[song.pattNr & 0xFF];
+			song.pattNum = song.orders[song.songPos & 0xFF];
+			song.currNumRows = patternNumRows[song.pattNum & 0xFF];
 		}
 	}
 }
@@ -2230,62 +2246,57 @@
 void tickReplayer(void) // periodically called from audio callback
 {
 	int32_t i;
-	stmTyp *c;
+	channel_t *ch;
 
 	if (musicPaused || !songPlaying)
 	{
-		c = stm;
-		for (i = 0; i < song.antChn; i++, c++)
-			fixaEnvelopeVibrato(c);
+		ch = channel;
+		for (i = 0; i < song.numChannels; i++, ch++)
+			updateChannel(ch);
 
 		return;
 	}
 
 	// for song playback counter (hh:mm:ss)
-	if (song.speed >= MIN_BPM && song.speed <= MAX_BPM)
-		song.musicTime64 += musicTimeTab64[song.speed];
+	if (song.BPM >= MIN_BPM && song.BPM <= MAX_BPM)
+		song.musicTime64 += musicTimeTab64[song.BPM-MIN_BPM];
 
 	bool tickZero = false;
-	if (--song.timer == 0)
+	if (--song.tick == 0)
 	{
-		song.timer = song.tempo;
+		song.tick = song.speed;
 		tickZero = true;
 	}
 
-	song.curReplayerTimer = (uint8_t)song.timer; // for audio/video syncing (and recording)
+	song.curReplayerTick = (uint8_t)song.tick; // for audio/video syncing (and recording)
 
 	const bool readNewNote = tickZero && (song.pattDelTime2 == 0);
 	if (readNewNote)
 	{
 		// set audio/video syncing variables
-		song.curReplayerPattPos = (uint8_t)song.pattPos;
-		song.curReplayerPattNr = (uint8_t)song.pattNr;
+		song.curReplayerRow = (uint8_t)song.row;
+		song.curReplayerPattNum = (uint8_t)song.pattNum;
 		song.curReplayerSongPos = (uint8_t)song.songPos;
 		// ----------------------------------------------
 
-		const tonTyp *pattPtr = nilPatternLine;
-		if (patt[song.pattNr] != NULL)
-		{
-			assert(song.pattNr  >= 0 && song.pattNr  < MAX_PATTERNS &&
-			       song.pattPos >= 0 && song.pattPos < MAX_PATT_LEN);
+		const note_t *p = nilPatternLine;
+		if (pattern[song.pattNum] != NULL)
+			p = &pattern[song.pattNum][song.row * MAX_CHANNELS];
 
-			pattPtr = &patt[song.pattNr][song.pattPos * MAX_VOICES];
-		}
-
-		c = stm;
-		for (i = 0; i < song.antChn; i++, c++, pattPtr++)
+		ch = channel;
+		for (i = 0; i < song.numChannels; i++, ch++, p++)
 		{
-			getNewNote(c, pattPtr);
-			fixaEnvelopeVibrato(c);
+			getNewNote(ch, p);
+			updateChannel(ch);
 		}
 	}
 	else
 	{
-		c = stm;
-		for (i = 0; i < song.antChn; i++, c++)
+		ch = channel;
+		for (i = 0; i < song.numChannels; i++, ch++)
 		{
-			handleEffects_TickNonZero(c);
-			fixaEnvelopeVibrato(c);
+			handleEffects_TickNonZero(ch);
+			updateChannel(ch);
 		}
 	}
 
@@ -2298,7 +2309,7 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	song.timer = 1;
+	song.tick = 1;
 	stopVoices();
 
 	if (audioWasntLocked)
@@ -2308,12 +2319,12 @@
 
 	if (!songPlaying)
 	{
-		setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5);
+		setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
 		setScrollBarPos(SB_POS_ED, 0, false);
 	}
 }
 
-void setPos(int16_t songPos, int16_t pattPos, bool resetTimer)
+void setPos(int16_t songPos, int16_t row, bool resetTimer)
 {
 	const bool audioWasntLocked = !audio.locked;
 	if (audioWasntLocked)
@@ -2322,35 +2333,35 @@
 	if (songPos > -1)
 	{
 		song.songPos = songPos;
-		if (song.len > 0 && song.songPos >= song.len)
-			song.songPos = song.len - 1;
+		if (song.songLength > 0 && song.songPos >= song.songLength)
+			song.songPos = song.songLength - 1;
 
-		song.pattNr = song.songTab[song.songPos];
-		assert(song.pattNr < MAX_PATTERNS);
-		song.pattLen = pattLens[song.pattNr];
+		song.pattNum = song.orders[song.songPos];
+		assert(song.pattNum < MAX_PATTERNS);
+		song.currNumRows = patternNumRows[song.pattNum];
 
 		checkMarkLimits(); // non-FT2 safety
 	}
 
-	if (pattPos > -1)
+	if (row > -1)
 	{
-		song.pattPos = pattPos;
-		if (song.pattPos >= song.pattLen)
-			song.pattPos = song.pattLen-1;
+		song.row = row;
+		if (song.row >= song.currNumRows)
+			song.row = song.currNumRows-1;
 	}
 
 	// if not playing, update local position variables
 	if (!songPlaying)
 	{
-		if (pattPos > -1)
+		if (row > -1)
 		{
-			editor.pattPos = (uint8_t)pattPos;
+			editor.row = (uint8_t)row;
 			ui.updatePatternEditor = true;
 		}
 
 		if (songPos > -1)
 		{
-			editor.editPattern = (uint8_t)song.pattNr;
+			editor.editPattern = (uint8_t)song.pattNum;
 			editor.songPos = song.songPos;
 			ui.updatePosSections = true;
 		}
@@ -2357,70 +2368,71 @@
 	}
 
 	if (resetTimer)
-		song.timer = 1;
+		song.tick = 1;
 
 	if (audioWasntLocked)
 		unlockAudio();
 }
 
-void delta2Samp(int8_t *p, int32_t len, uint8_t typ)
+void delta2Samp(int8_t *p, int32_t length, uint8_t smpFlags)
 {
-	if (typ & 16) len >>= 1; // 16-bit
-	if (typ & 32) len >>= 1; // stereo
+	bool sample16Bit = !!(smpFlags & SAMPLE_16BIT);
+	bool stereo = !!(smpFlags & SAMPLE_STEREO);
 
-	if (typ & 32)
+	if (stereo)
 	{
-		if (typ & 16)
+		length >>= 1;
+
+		if (sample16Bit)
 		{
-			int16_t *p16 = (int16_t *)p;
+			int16_t *p16L = (int16_t *)p;
+			int16_t *p16R = (int16_t *)p + length;
 
 			int16_t olds16L = 0;
 			int16_t olds16R = 0;
 
-			for (int32_t i = 0; i < len; i++)
+			for (int32_t i = 0; i < length; i++)
 			{
-				const int16_t news16L = p16[i] + olds16L;
-				p16[i] = news16L;
+				const int16_t news16L = p16L[i] + olds16L;
+				p16L[i] = news16L;
 				olds16L = news16L;
 
-				const int16_t news16R = p16[len+i] + olds16R;
-				p16[len+i] = news16R;
+				const int16_t news16R = p16R[i] + olds16R;
+				p16R[i] = news16R;
 				olds16R = news16R;
 
-				const int32_t tmp32 = olds16L + olds16R;
-				p16[i] = (int16_t)(tmp32 >> 1);
+				p16L[i] = (int16_t)((olds16L + olds16R) >> 1);
 			}
 		}
-		else
+		else // 8-bit
 		{
-			int8_t *p8 = (int8_t *)p;
-
+			int8_t *p8L = (int8_t *)p;
+			int8_t *p8R = (int8_t *)p + length;
 			int8_t olds8L = 0;
 			int8_t olds8R = 0;
 
-			for (int32_t i = 0; i < len; i++)
+			for (int32_t i = 0; i < length; i++)
 			{
-				const int8_t news8L = p8[i] + olds8L;
-				p8[i] = news8L;
+				const int8_t news8L = p8L[i] + olds8L;
+				p8L[i] = news8L;
 				olds8L = news8L;
 
-				const int8_t news8R = p8[len+i] + olds8R;
-				p8[len+i] = news8R;
+				const int8_t news8R = p8R[i] + olds8R;
+				p8R[i] = news8R;
 				olds8R = news8R;
 
-				const int16_t tmp16 = olds8L + olds8R;
-				p8[i] = (int8_t)(tmp16 >> 1);
+				p8L[i] = (int8_t)((olds8L + olds8R) >> 1);
 			}
 		}
 	}
-	else
+	else // mono (normal sample)
 	{
-		if (typ & 16)
+		if (sample16Bit)
 		{
 			int16_t *p16 = (int16_t *)p;
 
 			int16_t olds16L = 0;
-			for (int32_t i = 0; i < len; i++)
+			for (int32_t i = 0; i < length; i++)
 			{
 				const int16_t news16 = p16[i] + olds16L;
 				p16[i] = news16;
@@ -2427,12 +2439,12 @@
 				olds16L = news16;
 			}
 		}
-		else
+		else // 8-bit
 		{
 			int8_t *p8 = (int8_t *)p;
 
 			int8_t olds8L = 0;
-			for (int32_t i = 0; i < len; i++)
+			for (int32_t i = 0; i < length; i++)
 			{
 				const int8_t news8 = p8[i] + olds8L;
 				p8[i] = news8;
@@ -2442,51 +2454,48 @@
 	}
 }
 
-void samp2Delta(int8_t *p, int32_t len, uint8_t typ)
+void samp2Delta(int8_t *p, int32_t length, uint8_t smpFlags)
 {
-	if (typ & 16)
-		len >>= 1; // 16-bit
-
-	if (typ & 16)
+	if (smpFlags & SAMPLE_16BIT)
 	{
 		int16_t *p16 = (int16_t *)p;
 
-		int16_t news16 = 0;
-		for (int32_t i = 0; i < len; i++)
+		int16_t newS16 = 0;
+		for (int32_t i = 0; i < length; i++)
 		{
-			const int16_t olds16 = p16[i];
-			p16[i] -= news16;
-			news16 = olds16;
+			const int16_t oldS16 = p16[i];
+			p16[i] -= newS16;
+			newS16 = oldS16;
 		}
 	}
-	else
+	else // 8-bit
 	{
 		int8_t *p8 = (int8_t *)p;
 
-		int8_t news8 = 0;
-		for (int32_t i = 0; i < len; i++)
+		int8_t newS8 = 0;
+		for (int32_t i = 0; i < length; i++)
 		{
-			const int8_t olds8 = p8[i];
-			p8[i] -= news8;
-			news8 = olds8;
+			const int8_t oldS8 = p8[i];
+			p8[i] -= newS8;
+			newS8 = oldS8;
 		}
 	}
 }
 
-bool allocateInstr(int16_t nr)
+bool allocateInstr(int16_t insNum)
 {
-	if (instr[nr] != NULL)
+	if (instr[insNum] != NULL)
 		return false; // already allocated
 
-	instrTyp *p = (instrTyp *)malloc(sizeof (instrTyp));
+	instr_t *p = (instr_t *)malloc(sizeof (instr_t));
 	if (p == NULL)
 		return false;
 
-	memset(p, 0, sizeof (instrTyp));
+	memset(p, 0, sizeof (instr_t));
 	for (int32_t i = 0; i < MAX_SMP_PER_INST; i++)
 	{
-		p->samp[i].pan = 128;
-		p->samp[i].vol = 64;
+		p->smp[i].panning = 128;
+		p->smp[i].volume = 64;
 	}
 
 	setStdEnvelope(p, 0, 3);
@@ -2495,7 +2504,7 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	instr[nr] = p;
+	instr[insNum] = p;
 
 	if (audioWasntLocked)
 		unlockAudio();
@@ -2503,22 +2512,19 @@
 	return true;
 }
 
-void freeInstr(int32_t nr)
+void freeInstr(int32_t insNum)
 {
-	if (instr[nr] == NULL)
+	if (instr[insNum] == NULL)
 		return; // not allocated
 
 	pauseAudio(); // channel instrument pointers are now cleared
 
-	sampleTyp *s = instr[nr]->samp;
+	sample_t *s = instr[insNum]->smp;
 	for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) // free sample data
-	{
-		if (s->origPek != NULL)
-			free(s->origPek);
-	}
+		freeSmpData(s);
 
-	free(instr[nr]);
-	instr[nr] = NULL;
+	free(instr[insNum]);
+	instr[insNum] = NULL;
 	
 	resumeAudio();
 }
@@ -2530,12 +2536,9 @@
 	{
 		if (instr[i] != NULL)
 		{
-			sampleTyp *s = instr[i]->samp;
+			sample_t *s = instr[i]->smp;
 			for (int32_t j = 0; j < MAX_SMP_PER_INST; j++, s++) // free sample data
-			{
-				if (s->origPek != NULL)
-					free(s->origPek);
-			}
+				freeSmpData(s);
 
 			free(instr[i]);
 			instr[i] = NULL;
@@ -2544,22 +2547,20 @@
 	resumeAudio();
 }
 
-void freeSample(int16_t nr, int16_t nr2)
+void freeSample(int16_t insNum, int16_t smpNum)
 {
-	if (instr[nr] == NULL)
+	if (instr[insNum] == NULL)
 		return; // instrument not allocated
 
 	pauseAudio(); // voice sample pointers are now cleared
 
-	sampleTyp *s = &instr[nr]->samp[nr2];
-	if (s->origPek != NULL)
-		free(s->origPek);
+	sample_t *s = &instr[insNum]->smp[smpNum];
+	freeSmpData(s);
 
-	memset(s, 0, sizeof (sampleTyp));
+	memset(s, 0, sizeof (sample_t));
+	s->panning = 128;
+	s->volume = 64;
 
-	s->pan = 128;
-	s->vol = 64;
-
 	resumeAudio();
 }
 
@@ -2568,16 +2569,16 @@
 	pauseAudio();
 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
 	{
-		if (patt[i] != NULL)
+		if (pattern[i] != NULL)
 		{
-			free(patt[i]);
-			patt[i] = NULL;
+			free(pattern[i]);
+			pattern[i] = NULL;
 		}
 	}
 	resumeAudio();
 }
 
-void setStdEnvelope(instrTyp *ins, int16_t i, uint8_t typ)
+void setStdEnvelope(instr_t *ins, int16_t i, uint8_t type)
 {
 	if (ins == NULL)
 		return;
@@ -2584,35 +2585,35 @@
 
 	pauseMusic();
 
-	if (typ & 1)
+	if (type & 1)
 	{
-		memcpy(ins->envVP, config.stdEnvP[i][0], 2*2*12);
-		ins->envVPAnt = (uint8_t)config.stdVolEnvAnt[i];
-		ins->envVSust = (uint8_t)config.stdVolEnvSust[i];
-		ins->envVRepS = (uint8_t)config.stdVolEnvRepS[i];
-		ins->envVRepE = (uint8_t)config.stdVolEnvRepE[i];
-		ins->fadeOut = config.stdFadeOut[i];
+		memcpy(ins->volEnvPoints, config.stdEnvPoints[i][0], 2*2*12);
+		ins->volEnvLength = (uint8_t)config.stdVolEnvLength[i];
+		ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[i];
+		ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[i];
+		ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[i];
+		ins->fadeout = config.stdFadeout[i];
 		ins->vibRate = (uint8_t)config.stdVibRate[i];
 		ins->vibDepth = (uint8_t)config.stdVibDepth[i];
 		ins->vibSweep = (uint8_t)config.stdVibSweep[i];
-		ins->vibTyp = (uint8_t)config.stdVibTyp[i];
-		ins->envVTyp = (uint8_t)config.stdVolEnvTyp[i];
+		ins->vibType = (uint8_t)config.stdVibType[i];
+		ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[i];
 	}
 
-	if (typ & 2)
+	if (type & 2)
 	{
-		memcpy(ins->envPP, config.stdEnvP[i][1], 2*2*12);
-		ins->envPPAnt = (uint8_t)config.stdPanEnvAnt[0];
-		ins->envPSust = (uint8_t)config.stdPanEnvSust[0];
-		ins->envPRepS = (uint8_t)config.stdPanEnvRepS[0];
-		ins->envPRepE = (uint8_t)config.stdPanEnvRepE[0];
-		ins->envPTyp  = (uint8_t)config.stdPanEnvTyp[0];
+		memcpy(ins->panEnvPoints, config.stdEnvPoints[i][1], 2*2*12);
+		ins->panEnvLength = (uint8_t)config.stdPanEnvLength[0];
+		ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[0];
+		ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[0];
+		ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[0];
+		ins->panEnvFlags  = (uint8_t)config.stdPanEnvFlags[0];
 	}
 
 	resumeMusic();
 }
 
-void setNoEnvelope(instrTyp *ins)
+void setNoEnvelope(instr_t *ins)
 {
 	if (ins == NULL)
 		return;
@@ -2619,36 +2620,36 @@
 
 	pauseMusic();
 
-	memcpy(ins->envVP, config.stdEnvP[0][0], 2*2*12);
-	ins->envVPAnt = (uint8_t)config.stdVolEnvAnt[0];
-	ins->envVSust = (uint8_t)config.stdVolEnvSust[0];
-	ins->envVRepS = (uint8_t)config.stdVolEnvRepS[0];
-	ins->envVRepE = (uint8_t)config.stdVolEnvRepE[0];
-	ins->envVTyp = 0;
+	memcpy(ins->volEnvPoints, config.stdEnvPoints[0][0], 2*2*12);
+	ins->volEnvLength = (uint8_t)config.stdVolEnvLength[0];
+	ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[0];
+	ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[0];
+	ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[0];
+	ins->volEnvFlags = 0;
 
-	memcpy(ins->envPP, config.stdEnvP[0][1], 2*2*12);
-	ins->envPPAnt = (uint8_t)config.stdPanEnvAnt[0];
-	ins->envPSust = (uint8_t)config.stdPanEnvSust[0];
-	ins->envPRepS = (uint8_t)config.stdPanEnvRepS[0];
-	ins->envPRepE = (uint8_t)config.stdPanEnvRepE[0];
-	ins->envPTyp = 0;
+	memcpy(ins->panEnvPoints, config.stdEnvPoints[0][1], 2*2*12);
+	ins->panEnvLength = (uint8_t)config.stdPanEnvLength[0];
+	ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[0];
+	ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[0];
+	ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[0];
+	ins->panEnvFlags = 0;
 
-	ins->fadeOut = 0;
+	ins->fadeout = 0;
 	ins->vibRate = 0;
 	ins->vibDepth = 0;
 	ins->vibSweep = 0;
-	ins->vibTyp = 0;
+	ins->vibType = 0;
 
 	resumeMusic();
 }
 
-bool patternEmpty(uint16_t nr)
+bool patternEmpty(uint16_t pattNum)
 {
-	if (patt[nr] == NULL)
+	if (pattern[pattNum] == NULL)
 		return true;
 
-	const uint8_t *scanPtr = (const uint8_t *)patt[nr];
-	const uint32_t scanLen = pattLens[nr] * TRACK_WIDTH;
+	const uint8_t *scanPtr = (const uint8_t *)pattern[pattNum];
+	const uint32_t scanLen = patternNumRows[pattNum] * TRACK_WIDTH;
 
 	for (uint32_t i = 0; i < scanLen; i++)
 	{
@@ -2661,21 +2662,21 @@
 
 void updateChanNums(void)
 {
-	assert(!(song.antChn & 1));
+	assert(!(song.numChannels & 1));
 
 	const int32_t maxChannelsShown = getMaxVisibleChannels();
 
-	int32_t channelsShown = song.antChn;
+	int32_t channelsShown = song.numChannels;
 	if (channelsShown > maxChannelsShown)
 		channelsShown = maxChannelsShown;
 
 	ui.numChannelsShown = (uint8_t)channelsShown;
-	ui.pattChanScrollShown = (song.antChn > maxChannelsShown);
+	ui.pattChanScrollShown = (song.numChannels > maxChannelsShown);
 
 	if (ui.patternEditorShown)
 	{
-		if (ui.channelOffset > song.antChn-ui.numChannelsShown)
-			setScrollBarPos(SB_CHAN_SCROLL, song.antChn - ui.numChannelsShown, true);
+		if (ui.channelOffset > song.numChannels-ui.numChannelsShown)
+			setScrollBarPos(SB_CHAN_SCROLL, song.numChannels - ui.numChannelsShown, true);
 	}
 
 	if (ui.pattChanScrollShown)
@@ -2687,7 +2688,7 @@
 			showPushButton(PB_CHAN_SCROLL_RIGHT);
 		}
 
-		setScrollBarEnd(SB_CHAN_SCROLL, song.antChn);
+		setScrollBarEnd(SB_CHAN_SCROLL, song.numChannels);
 		setScrollBarPageLength(SB_CHAN_SCROLL, ui.numChannelsShown);
 	}
 	else
@@ -2705,51 +2706,48 @@
 		cursor.ch = ui.channelOffset+ui.numChannelsShown - 1;
 }
 
-void conv8BitSample(int8_t *p, int32_t len, bool stereo)
+void conv8BitSample(int8_t *p, int32_t length, bool stereo) // changes sample sign
 {
 	if (stereo)
 	{
-		len /= 2;
+		length >>= 1;
 
-		int8_t *p2 = &p[len];
-		for (int32_t i = 0; i < len; i++)
+		int8_t *p2 = &p[length];
+		for (int32_t i = 0; i < length; i++)
 		{
 			const int8_t l = p[i] ^ 0x80;
 			const int8_t r = p2[i] ^ 0x80;
 
-			int16_t tmp16 = l + r;
-			p[i] = (int8_t)(tmp16 >> 1);
+			p[i] = (int8_t)((l + r) >> 1);
 		}
 	}
 	else
 	{
-		for (int32_t i = 0; i < len; i++)
+		for (int32_t i = 0; i < length; i++)
 			p[i] ^= 0x80;
 	}
 }
 
-void conv16BitSample(int8_t *p, int32_t len, bool stereo)
+void conv16BitSample(int8_t *p, int32_t length, bool stereo) // changes sample sign
 {
 	int16_t *p16_1 = (int16_t *)p;
-	len /= 2;
 
 	if (stereo)
 	{
-		len /= 2;
+		length >>= 1;
 
-		int16_t *p16_2 = (int16_t *)&p[len * 2];
-		for (int32_t i = 0; i < len; i++)
+		int16_t *p16_2 = (int16_t *)p + length;
+		for (int32_t i = 0; i < length; i++)
 		{
 			const int16_t l = p16_1[i] ^ 0x8000;
 			const int16_t r = p16_2[i] ^ 0x8000;
 
-			int32_t tmp32 = l + r;
-			p16_1[i] = (int16_t)(tmp32 >> 1);
+			p16_1[i] = (int16_t)((l + r) >> 1);
 		}
 	}
 	else
 	{
-		for (int32_t i = 0; i < len; i++)
+		for (int32_t i = 0; i < length; i++)
 			p16_1[i] ^= 0x8000;
 	}
 }
@@ -2785,23 +2783,22 @@
 bool setupReplayer(void)
 {
 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
-		pattLens[i] = 64;
+		patternNumRows[i] = 64;
 
 	playMode = PLAYMODE_IDLE;
 	songPlaying = false;
 
 	// unmute all channels (must be done before resetChannels() call)
-	for (int32_t i = 0; i < MAX_VOICES; i++)
+	for (int32_t i = 0; i < MAX_CHANNELS; i++)
 		editor.chnMode[i] = 1;
 
 	resetChannels();
 
-	song.len = 1;
-	song.antChn = 8;
-	editor.speed = song.speed = 125;
-	editor.tempo = song.tempo = 6;
-	editor.globalVol = song.globVol = 64;
-	song.initialTempo = song.tempo;
+	song.songLength = 1;
+	song.numChannels = 8;
+	editor.BPM = song.BPM = 125;
+	editor.speed = song.initialSpeed = song.speed = 6;
+	editor.globalVolume = song.globalVolume = 64;
 	audio.linearPeriodsFlag = true;
 	note2Period = linearPeriods;
 
@@ -2820,7 +2817,7 @@
 		showErrorMsgBox("Not enough memory!");
 		return false;
 	}
-	instr[0]->samp[0].vol = 0;
+	instr[0]->smp[0].volume = 0;
 
 	if (!allocateInstr(130))
 	{
@@ -2827,7 +2824,7 @@
 		showErrorMsgBox("Not enough memory!");
 		return false;
 	}
-	memset(instr[130], 0, sizeof (instrTyp));
+	memset(instr[130], 0, sizeof (instr_t));
 
 	if (!allocateInstr(131)) // Instr. Ed. display instrument for unallocated/empty instruments
 	{
@@ -2835,9 +2832,9 @@
 		return false;
 	}
 
-	memset(instr[131], 0, sizeof (instrTyp));
+	memset(instr[131], 0, sizeof (instr_t));
 	for (int32_t i = 0; i < 16; i++)
-		instr[131]->samp[i].pan = 128;
+		instr[131]->smp[i].panning = 128;
 
 	editor.tmpPattern = 65535; // pattern editor update/redraw kludge
 	return true;
@@ -2860,10 +2857,10 @@
 	resetPlaybackTime();
 
 	// non-FT2 fix: If song speed was 0, set it back to initial speed on play
-	if (song.tempo == 0)
-		song.tempo = song.initialTempo;
+	if (song.speed == 0)
+		song.speed = song.initialSpeed;
 
-	audio.dTickSampleCounter = 0.0; // zero tick sample counter so that it will instantly initiate a tick
+	audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick
 
 	unlockMixerCallback();
 
@@ -2885,13 +2882,13 @@
 	}
 	else
 	{
-		for (uint8_t i = 0; i < MAX_VOICES; i++)
-			playTone(i, 0, 97, -1, 0, 0);
+		for (uint8_t i = 0; i < MAX_CHANNELS; i++)
+			playTone(i, 0, NOTE_OFF, -1, 0, 0);
 	}
 
-	// if song was playing, update local pattPos (fixes certain glitches)
+	// if song was playing, update local row (fixes certain glitches)
 	if (songWasPlaying)
-		editor.pattPos = song.pattPos;
+		editor.row = song.row;
 
 #ifdef HAS_MIDI
 	midi.currMIDIVibDepth = 0;
@@ -2900,35 +2897,40 @@
 
 	memset(editor.keyOnTab, 0, sizeof (editor.keyOnTab));
 
+	// kludge to prevent UI from being out of sync with replayer vars on stop
+	song.songPos = editor.songPos;
+	song.row = editor.row;
+	song.pattNum = editor.editPattern;
+
 	ui.updatePosSections = true;
 	ui.updatePatternEditor = true;
 
 	// certain non-FT2 fixes
-	song.timer = editor.timer = 1;
-	song.globVol = editor.globalVol = 64;
+	song.tick = editor.tick = 1;
+	song.globalVolume = editor.globalVolume = 64;
 	ui.drawGlobVolFlag = true;
 }
 
 // from keyboard/smp. ed.
-void playTone(uint8_t stmm, uint8_t inst, uint8_t ton, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch)
+void playTone(uint8_t chNum, uint8_t insNum, uint8_t note, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch)
 {
-	instrTyp *ins = instr[inst];
+	instr_t *ins = instr[insNum];
 	if (ins == NULL)
 		return;
 
-	assert(stmm < MAX_VOICES && inst <= MAX_INST && ton <= 97);
-	stmTyp *ch = &stm[stmm];
+	assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && note <= NOTE_OFF);
+	channel_t *ch = &channel[chNum];
 
 	// FT2 bugfix: Don't play tone if certain requirements are not met
-	if (ton != 97)
+	if (note != NOTE_OFF)
 	{
-		if (ton == 0 || ton > 96)
+		if (note == 0 || note > 96)
 			return;
 
-		sampleTyp *s = &ins->samp[ins->ta[ton-1] & 0xF];
+		sample_t *s = &ins->smp[ins->note2SampleLUT[note-1] & 0xF];
 
-		int16_t newTon = (int16_t)ton + s->relTon;
-		if (s->pek == NULL || s->len == 0 || newTon <= 0 || newTon >= 12*10)
+		int16_t finalNote = (int16_t)note + s->relativeNote;
+		if (s->dataPtr == NULL || s->length == 0 || finalNote <= 0 || finalNote >= 12*10)
 			return;
 	}
 	// -------------------
@@ -2935,19 +2937,19 @@
 
 	lockAudio();
 
-	if (inst != 0 && ton != 97)
+	if (insNum != 0 && note != NOTE_OFF)
 	{
-		ch->tonTyp = (inst << 8) | (ch->tonTyp & 0xFF);
-		ch->instrNr = inst;
+		ch->noteData = (insNum << 8) | (ch->noteData & 0xFF);
+		ch->instrNum = insNum;
 	}
 
-	ch->tonTyp = (ch->tonTyp & 0xFF00) | ton;
-	ch->effTyp = 0;
-	ch->eff = 0;
+	ch->noteData = (ch->noteData & 0xFF00) | note;
+	ch->efx = 0;
+	ch->efxData = 0;
 
-	startTone(ton, 0, 0, ch);
+	startTone(note, 0, 0, ch);
 
-	if (ton != 97)
+	if (note != NOTE_OFF)
 	{
 		retrigVolume(ch);
 		retrigEnvelopeVibrato(ch);
@@ -2963,39 +2965,39 @@
 	ch->midiVibDepth = midiVibDepth;
 	ch->midiPitch = midiPitch;
 
-	fixaEnvelopeVibrato(ch);
+	updateChannel(ch);
 
 	unlockAudio();
 }
 
 // smp. ed.
-void playSample(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch)
+void playSample(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch)
 {
-	if (instr[inst] == NULL)
+	if (instr[insNum] == NULL)
 		return;
 
 	// for sampling playback line in Smp. Ed.
-	lastChInstr[stmm].instrNr = 255;
-	lastChInstr[stmm].sampleNr = 255;
+	lastChInstr[chNum].instrNum = 255;
+	lastChInstr[chNum].smpNum = 255;
 	editor.curPlayInstr = 255;
 	editor.curPlaySmp = 255;
 
-	assert(stmm < MAX_VOICES && inst <= MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97);
-	stmTyp *ch = &stm[stmm];
+	assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && smpNum < MAX_SMP_PER_INST && note <= NOTE_OFF);
+	channel_t *ch = &channel[chNum];
 
-	memcpy(&instr[130]->samp[0], &instr[inst]->samp[smpNr], sizeof (sampleTyp));
+	memcpy(&instr[130]->smp[0], &instr[insNum]->smp[smpNum], sizeof (sample_t));
 
-	uint8_t vol = instr[inst]->samp[smpNr].vol;
+	uint8_t vol = instr[insNum]->smp[smpNum].volume;
 	
 	lockAudio();
 
-	ch->instrNr = 130;
-	ch->tonTyp = (ch->instrNr << 8) | ton;
-	ch->effTyp = 0;
+	ch->instrNum = 130;
+	ch->noteData = (ch->instrNum << 8) | note;
+	ch->efx = 0;
 
-	startTone(ton, 0, 0, ch);
+	startTone(note, 0, 0, ch);
 
-	if (ton != 97)
+	if (note != NOTE_OFF)
 	{
 		retrigVolume(ch);
 		retrigEnvelopeVibrato(ch);
@@ -3008,11 +3010,11 @@
 	ch->midiVibDepth = midiVibDepth;
 	ch->midiPitch = midiPitch;
 
-	fixaEnvelopeVibrato(ch);
+	updateChannel(ch);
 
 	unlockAudio();
 
-	while (ch->status & IS_NyTon); // wait for sample to latch in mixer
+	while (ch->status & IS_Trigger); // wait for sample to latch in mixer
 
 	// for sampling playback line in Smp. Ed.
 	editor.curPlayInstr = editor.curInstr;
@@ -3020,52 +3022,45 @@
 }
 
 // smp. ed.
-void playRange(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch, int32_t offs, int32_t len)
+void playRange(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch, int32_t smpOffset, int32_t length)
 {
-	if (instr[inst] == NULL)
+	if (instr[insNum] == NULL)
 		return;
 
 	// for sampling playback line in Smp. Ed.
-	lastChInstr[stmm].instrNr = 255;
-	lastChInstr[stmm].sampleNr = 255;
+	lastChInstr[chNum].instrNum = 255;
+	lastChInstr[chNum].smpNum = 255;
 	editor.curPlayInstr = 255;
 	editor.curPlaySmp = 255;
 
-	assert(stmm < MAX_VOICES && inst <= MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97);
+	assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && smpNum < MAX_SMP_PER_INST && note <= NOTE_OFF);
 
-	stmTyp *ch = &stm[stmm];
-	sampleTyp *s = &instr[130]->samp[0];
+	channel_t *ch = &channel[chNum];
+	sample_t *s = &instr[130]->smp[0];
 
-	memcpy(s, &instr[inst]->samp[smpNr], sizeof (sampleTyp));
+	memcpy(s, &instr[insNum]->smp[smpNum], sizeof (sample_t));
 
-	uint8_t vol = instr[inst]->samp[smpNr].vol;
+	uint8_t vol = instr[insNum]->smp[smpNum].volume;
 
-	if (s->typ & 16)
-	{
-		offs &= 0xFFFFFFFE;
-		len &= 0xFFFFFFFE;
-	}
-
 	lockAudio();
 
-	s->len = offs + len;
-	s->repS = 0;
-	s->repL = 0;
-	s->typ &= 16; // only keep 8-bit/16-bit flag (disable loop)
+	s->length = smpOffset + length;
+	s->loopStart = 0;
+	s->loopLength = 0;
+	DISABLE_LOOP(s->flags); // disable loop on sample #129 (placeholder)
 
-	int32_t samplePlayOffset = offs;
-	if (s->typ & 16)
-		samplePlayOffset >>= 1;
+	int32_t samplePlayOffset = smpOffset;
 
-	ch->instrNr = 130;
-	ch->tonTyp = (ch->instrNr << 8) | ton;
-	ch->effTyp = 0;
+	ch->instrNum = 130;
+	ch->noteData = (ch->instrNum << 8) | note;
+	ch->efx = 0;
+	ch->efxData = 0;
 
-	startTone(ton, 0, 0, ch);
+	startTone(note, 0, 0, ch);
 
 	ch->smpStartPos = samplePlayOffset;
 
-	if (ton != 97)
+	if (note != NOTE_OFF)
 	{
 		retrigVolume(ch);
 		retrigEnvelopeVibrato(ch);
@@ -3078,11 +3073,11 @@
 	ch->midiVibDepth = midiVibDepth;
 	ch->midiPitch = midiPitch;
 
-	fixaEnvelopeVibrato(ch);
+	updateChannel(ch);
 
 	unlockAudio();
 
-	while (ch->status & IS_NyTon); // wait for sample to latch in mixer
+	while (ch->status & IS_Trigger); // wait for sample to latch in mixer
 
 	// for sampling playback line in Smp. Ed.
 	editor.curPlayInstr = editor.curInstr;
@@ -3095,21 +3090,23 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	stmTyp *ch = stm;
-	for (int32_t i = 0; i < MAX_VOICES; i++, ch++)
+	channel_t *ch = channel;
+	for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++)
 	{
-		lastChInstr[i].sampleNr = 255;
-		lastChInstr[i].instrNr = 255;
+		lastChInstr[i].smpNum = 255;
+		lastChInstr[i].instrNum = 255;
 
-		ch->tonTyp = 0;
-		ch->relTonNr = 0;
-		ch->instrNr = 0;
+		ch->noteData = 0;
+		ch->relativeNote = 0;
+		ch->smpNum = 0;
+		ch->smpPtr = NULL;
+		ch->instrNum = 0;
 		ch->instrPtr = instr[0]; // important: set instrument pointer to instr 0 (placeholder instrument)
 		ch->status = IS_Vol;
 		ch->realVol = 0;
 		ch->outVol = 0;
 		ch->oldVol = 0;
-		ch->dFinalVol = 0.0;
+		ch->fFinalVol = 0.0f;
 		ch->oldPan = 128;
 		ch->outPan = 128;
 		ch->finalPan = 128;
@@ -3116,8 +3113,7 @@
 		ch->vibDepth = 0;
 		ch->midiVibDepth = 0;
 		ch->midiPitch = 0;
-		ch->smpPtr = NULL;
-		ch->portaDir = 0; // FT2 bugfix: weird 3xx behavior if not used with note
+		ch->portaDirection = 0; // FT2 bugfix: weird 3xx behavior if not used with note
 
 		stopVoice(i);
 	}
@@ -3129,7 +3125,6 @@
 	stopAllScopes();
 	resetAudioDither();
 	resetCachedMixerVars();
-	resetCachedScopeVars();
 
 	// wait for scope thread to finish, so that we know pointers aren't deprecated
 	while (editor.scopeThreadMutex);
@@ -3147,10 +3142,10 @@
 
 	if (songPlaying)
 	{
-		song.globVol = 64;
+		song.globalVolume = 64;
 
-		stmTyp *ch = stm;
-		for (int32_t i = 0; i < song.antChn; i++, ch++)
+		channel_t *ch = channel;
+		for (int32_t i = 0; i < song.numChannels; i++, ch++)
 			ch->status |= IS_Vol;
 	}
 }
@@ -3161,8 +3156,8 @@
 	setPos((int16_t)pos, 0, true);
 
 	// non-FT2 fix: If song speed was 0, set it back to initial speed
-	if (song.tempo == 0)
-		song.tempo = song.initialTempo;
+	if (song.speed == 0)
+		song.speed = song.initialSpeed;
 }
 
 void decSongPos(void)
@@ -3183,7 +3178,7 @@
 
 void incSongPos(void)
 {
-	if (song.songPos == song.len-1)
+	if (song.songPos == song.songLength-1)
 		return;
 
 	const bool audioWasntLocked = !audio.locked;
@@ -3190,7 +3185,7 @@
 	if (audioWasntLocked)
 		lockAudio();
 
-	if (song.songPos < song.len-1)
+	if (song.songPos < song.songLength-1)
 		setNewSongPos(song.songPos + 1);
 
 	if (audioWasntLocked)
@@ -3287,7 +3282,7 @@
 
 void setSyncedReplayerVars(void)
 {
-	uint8_t scopeUpdateStatus[MAX_VOICES];
+	uint8_t scopeUpdateStatus[MAX_CHANNELS];
 
 	pattSyncEntry = NULL;
 	chSyncEntry = NULL;
@@ -3308,7 +3303,7 @@
 		if (chSyncEntry == NULL)
 			break;
 
-		for (int32_t i = 0; i < song.antChn; i++)
+		for (int32_t i = 0; i < song.numChannels; i++)
 			scopeUpdateStatus[i] |= chSyncEntry->channels[i].status; // yes, OR the status
 
 		if (!chQueuePop())
@@ -3356,23 +3351,23 @@
 
 	// we have a new tick
 
-	editor.timer = pattSyncEntry->timer;
+	editor.tick = pattSyncEntry->tick;
 
-	if (editor.speed != pattSyncEntry->speed)
+	if (editor.BPM != pattSyncEntry->BPM)
 	{
-		editor.speed = pattSyncEntry->speed;
+		editor.BPM = pattSyncEntry->BPM;
 		ui.drawBPMFlag = true;
 	}
 
-	if (editor.tempo != pattSyncEntry->tempo)
+	if (editor.speed != pattSyncEntry->speed)
 	{
-		editor.tempo = pattSyncEntry->tempo;
+		editor.speed = pattSyncEntry->speed;
 		ui.drawSpeedFlag = true;
 	}
 
-	if (editor.globalVol != pattSyncEntry->globalVol)
+	if (editor.globalVolume != pattSyncEntry->globalVolume)
 	{
-		editor.globalVol = pattSyncEntry->globalVol;
+		editor.globalVolume = pattSyncEntry->globalVolume;
 		ui.drawGlobVolFlag = true;
 	}
 
@@ -3383,15 +3378,15 @@
 	}
 
 	// somewhat of a kludge...
-	if (editor.tmpPattern != pattSyncEntry->pattern || editor.pattPos != pattSyncEntry->patternPos)
+	if (editor.tmpPattern != pattSyncEntry->pattNum || editor.row != pattSyncEntry->row)
 	{
 		// set pattern number
-		editor.editPattern = editor.tmpPattern = pattSyncEntry->pattern;
+		editor.editPattern = editor.tmpPattern = pattSyncEntry->pattNum;
 		checkMarkLimits();
 		ui.drawPattNumLenFlag = true;
 
 		// set row
-		editor.pattPos = (uint8_t)pattSyncEntry->patternPos;
+		editor.row = (uint8_t)pattSyncEntry->row;
 		ui.updatePatternEditor = true;
 	}
 }
--- a/src/ft2_replayer.h
+++ b/src/ft2_replayer.h
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include "ft2_unicode.h"
 #include "mixer/ft2_windowed_sinc.h"
+#include "ft2_cpu.h"
 
 enum
 {
@@ -10,7 +11,7 @@
 	// voice flags
 	IS_Vol = 1, // set volume
 	IS_Period = 2, // set resampling rate
-	IS_NyTon = 4, // trigger new sample
+	IS_Trigger = 4, // trigger sample
 	IS_Pan = 8, // set panning
 	IS_QuickVol = 16, // 5ms volramp instead of tick ms
 
@@ -37,12 +38,15 @@
 	CURSOR_EFX2 = 7
 };
 
-// DO NOT TOUCH!
-#define MIN_BPM 1
-#define MAX_BPM 999
-#define MAX_VOICES 32
-#define TRACK_WIDTH (5 * MAX_VOICES)
+// do not touch these!
+#define MIN_BPM 32
+#define MAX_BPM 255
+#define MAX_SPEED 31
+#define MAX_CHANNELS 32
+#define TRACK_WIDTH (5 * MAX_CHANNELS)
 #define MAX_FRQ 32000
+#define C4_FREQ 8363
+#define NOTE_OFF 97
 #define MAX_NOTES (10*12*16+16)
 #define MAX_PATTERNS 256
 #define MAX_PATT_LEN 256
@@ -55,6 +59,29 @@
 #define MAX_SAMPLE_LEN 0x3FFFFFFF
 #define PROG_NAME_STR "Fasttracker II clone"
 
+enum // sample flags
+{
+	LOOP_OFF = 0,
+	LOOP_FWD = 1,
+	LOOP_BIDI = 2,
+	SAMPLE_16BIT = 16,
+	SAMPLE_STEREO = 32,
+	SAMPLE_ADPCM = 64, // not an existing flag, but used by loader
+};
+
+enum // envelope flags
+{
+	ENV_ENABLED = 1,
+	ENV_SUSTAIN = 2,
+	ENV_LOOP    = 4
+};
+
+#define GET_LOOPTYPE(smpFlags) ((smpFlags) & (LOOP_FWD | LOOP_BIDI))
+#define DISABLE_LOOP(smpFlags) ((smpFlags) &= ~(LOOP_FWD | LOOP_BIDI))
+#define SAMPLE_LENGTH_BYTES(smp) (smp->length << !!(smp->flags & SAMPLE_16BIT))
+#define FINETUNE_MOD2XM(f) (((uint8_t)(f) & 0x0F) << 4)
+#define FINETUNE_XM2MOD(f) ((uint8_t)(f) >> 4)
+
 /* Some of the following structs MUST be packed!
 ** Please do NOT edit these structs unless you
 ** absolutely know what you are doing!
@@ -64,113 +91,112 @@
 #pragma pack(push)
 #pragma pack(1)
 #endif
-typedef struct songHeaderTyp_t
+typedef struct xmHdr_t
 {
-	char sig[17], name[21], progName[20];
-	uint16_t ver;
+	char ID[17], name[20], x1A, progName[20];
+	uint16_t version;
 	int32_t headerSize;
-	uint16_t len, repS, antChn, antPtn, antInstrs, flags, defTempo, defSpeed;
-	uint8_t songTab[256];
+	uint16_t numOrders, songLoopStart, numChannels, numPatterns;
+	uint16_t numInstr, flags, speed, BPM;
+	uint8_t orders[256];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songHeaderTyp;
+xmHdr_t;
 
-typedef struct patternHeaderTyp_t
+typedef struct xmPatHdr_t
 {
-	int32_t patternHeaderSize;
-	uint8_t typ;
-	int16_t pattLen;
-	uint16_t dataLen;
+	int32_t headerSize;
+	uint8_t type;
+	int16_t numRows;
+	uint16_t dataSize;
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-patternHeaderTyp;
+xmPatHdr_t;
 
-typedef struct songMODInstrHeaderTyp_t
+typedef struct modSmpHdr_t
 {
 	char name[22];
-	uint16_t len;
-	uint8_t fine, vol;
-	uint16_t repS, repL;
+	uint16_t length;
+	uint8_t finetune, volume;
+	uint16_t loopStart, loopLength;
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songMODInstrHeaderTyp;
+modSmpHdr_t;
 
-typedef struct songMOD31HeaderTyp_t
+typedef struct modHdr_t
 {
 	char name[20];
-	songMODInstrHeaderTyp instr[31];
-	uint8_t len, repS, songTab[128];
-	char sig[4];
+	modSmpHdr_t smp[31];
+	uint8_t numOrders, songLoopStart, orders[128];
+	char ID[4];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songMOD31HeaderTyp;
+modHdr_t;
 
-typedef struct sampleHeaderTyp_t
+typedef struct xmSmpHdr_t
 {
-	int32_t len, repS, repL;
-	uint8_t vol;
-	int8_t fine;
-	uint8_t typ, pan;
-	int8_t relTon;
-	uint8_t nameLen;
+	uint32_t length, loopStart, loopLength;
+	uint8_t volume;
+	int8_t finetune;
+	uint8_t flags, panning;
+	int8_t relativeNote;
+	uint8_t nameLength; // only handled before saving (ignored under load)
 	char name[22];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-sampleHeaderTyp;
+xmSmpHdr_t;
 
-typedef struct instrHeaderTyp_t
+typedef struct xmInsHdr_t
 {
 	uint32_t instrSize;
 	char name[22];
-	uint8_t typ;
-	int16_t antSamp;
+	uint8_t type;
+	int16_t numSamples;
 	int32_t sampleSize;
-	uint8_t ta[96];
-	int16_t envVP[12][2], envPP[12][2];
-	uint8_t envVPAnt, envPPAnt;
-	uint8_t envVSust, envVRepS, envVRepE;
-	uint8_t envPSust, envPRepS, envPRepE;
-	uint8_t envVTyp, envPTyp;
-	uint8_t vibTyp, vibSweep, vibDepth, vibRate;
-	uint16_t fadeOut;
+	uint8_t note2SampleLUT[96];
+	int16_t volEnvPoints[12][2], panEnvPoints[12][2];
+	uint8_t volEnvLength, panEnvLength;
+	uint8_t volEnvSustain, volEnvLoopStart, volEnvLoopEnd;
+	uint8_t panEnvSustain, panEnvLoopStart, panEnvLoopEnd;
+	uint8_t volEnvFlags, panEnvFlags;
+	uint8_t vibType, vibSweep, vibDepth, vibRate;
+	uint16_t fadeout;
 	uint8_t midiOn, midiChannel;
 	int16_t midiProgram, midiBend;
 	int8_t mute;
-	uint8_t reserved[15];
-	sampleHeaderTyp samp[16];
+	uint8_t junk[15];
+	xmSmpHdr_t smp[16];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-instrHeaderTyp;
+xmInsHdr_t;
 
-typedef struct tonTyp_t // must be packed on some systems, even though it consists of bytes only
+typedef struct pattNote_t // must be packed!
 {
-	uint8_t ton, instr, vol, effTyp, eff;
+	uint8_t note, instr, vol, efx, efxData;
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-tonTyp;
+note_t;
 
 typedef struct syncedChannel_t // used for audio/video sync queue (pack to save RAM)
 {
-	uint8_t status;
-	uint8_t pianoNoteNr;
-	uint8_t sampleNr, instrNr;
-	uint16_t smpStartPos;
-	uint8_t vol;
-	double dHz;
+	uint8_t status, pianoNoteNum, smpNum, instrNum;
+	int32_t smpStartPos;
+	uint8_t scopeVolume;
+	uintCPUWord_t scopeDelta;
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
@@ -181,13 +207,13 @@
 #pragma pack(pop)
 #endif
 
-typedef struct sampleTyp_t
+typedef struct sample_t
 {
 	char name[22+1];
-	bool fixed;
-	int8_t fine, relTon, *pek, *origPek;
-	uint8_t vol, typ, pan;
-	int32_t len, repS, repL;
+	bool isFixed;
+	int8_t finetune, relativeNote, *dataPtr, *origDataPtr;
+	uint8_t volume, flags, panning;
+	int32_t length, loopStart, loopLength;
 
 	// fix for resampling interpolation taps
 	int8_t leftEdgeTapSamples8[SINC_TAPS+SINC_LEFT_TAPS];
@@ -194,63 +220,62 @@
 	int16_t leftEdgeTapSamples16[SINC_TAPS+SINC_LEFT_TAPS];
 	int16_t fixedSmp[SINC_RIGHT_TAPS];
 	int32_t fixedPos;
-} sampleTyp;
+} sample_t;
 
-typedef struct instrTyp_t
+typedef struct instr_t
 {
 	bool midiOn, mute;
-	uint8_t midiChannel, ta[96];
-	uint8_t envVPAnt, envPPAnt;
-	uint8_t envVSust, envVRepS, envVRepE;
-	uint8_t envPSust, envPRepS, envPRepE;
-	uint8_t envVTyp, envPTyp;
-	uint8_t vibTyp, vibSweep, vibDepth, vibRate;
-	uint16_t fadeOut;
-	int16_t envVP[12][2], envPP[12][2], midiProgram, midiBend;
-	int16_t antSamp; // used by loader only
-	sampleTyp samp[16];
-} instrTyp;
+	uint8_t midiChannel, note2SampleLUT[96];
+	uint8_t volEnvLength, panEnvLength;
+	uint8_t volEnvSustain, volEnvLoopStart, volEnvLoopEnd;
+	uint8_t panEnvSustain, panEnvLoopStart, panEnvLoopEnd;
+	uint8_t volEnvFlags, panEnvFlags;
+	uint8_t vibType, vibSweep, vibDepth, vibRate;
+	uint16_t fadeout;
+	int16_t volEnvPoints[12][2], panEnvPoints[12][2], midiProgram, midiBend;
+	int16_t numSamples; // used by loader only
+	sample_t smp[16];
+} instr_t;
 
-typedef struct stmTyp_t
+typedef struct channel_t
 {
-	bool envSustainActive, stOff, mute;
+	bool envSustainActive, channelOff, mute;
 	volatile uint8_t status, tmpStatus;
-	int8_t relTonNr, fineTune;
-	uint8_t sampleNr, instrNr, effTyp, eff, smpOffset, tremorSave, tremorPos;
-	uint8_t globVolSlideSpeed, panningSlideSpeed, waveCtrl, portaDir;
+	int8_t relativeNote, finetune;
+	uint8_t smpNum, instrNum, efxData, efx, smpOffset, tremorSave, tremorPos;
+	uint8_t globVolSlideSpeed, panningSlideSpeed, waveCtrl, portaDirection;
 	uint8_t glissFunk, vibPos, tremPos, vibSpeed, vibDepth, tremSpeed, tremDepth;
-	uint8_t pattPos, loopCnt, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed;
+	uint8_t jumpToRow, patLoopCounter, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed;
 	uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed;
 	uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol;
-	uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol;
+	uint8_t volColumnVol, noteNum, panEnvPos, eVibPos, volEnvPos, realVol, oldVol, outVol;
 	uint8_t oldPan, outPan, finalPan;
 	int16_t midiPitch;
-	uint16_t outPeriod, realPeriod, finalPeriod, tonTyp, wantPeriod, portaSpeed;
-	uint16_t envVCnt, envPCnt, eVibAmp, eVibSweep;
-	uint16_t fadeOutAmp, fadeOutSpeed, midiVibDepth;
-	int32_t envVIPValue, envPIPValue, envVAmp, envPAmp;
+	uint16_t outPeriod, realPeriod, finalPeriod, noteData, wantPeriod, portaSpeed;
+	uint16_t volEnvTick, panEnvTick, eVibAmp, eVibSweep;
+	uint16_t fadeoutVol, fadeoutSpeed, midiVibDepth;
+	int32_t volEnvDelta, panEnvDelta, volEnvValue, panEnvValue;
 	int32_t oldFinalPeriod, smpStartPos;
 
-	uint8_t finalVol; // 0..255 (for visuals)
-	double dFinalVol; // 0.0 .. 1.0 (for mixer)
+	float fFinalVol; // 0.0f .. 1.0f
 
-	sampleTyp *smpPtr;
-	instrTyp *instrPtr;
-} stmTyp;
+	sample_t *smpPtr;
+	instr_t *instrPtr;
+} channel_t;
 
-typedef struct songTyp_t
+typedef struct song_t
 {
 	bool pBreakFlag, posJumpFlag, isModified;
 	char name[20+1], instrName[1+MAX_INST][22+1];
-	uint8_t curReplayerTimer, curReplayerPattPos, curReplayerSongPos, curReplayerPattNr; // used for audio/video sync queue
-	uint8_t pattDelTime, pattDelTime2, pBreakPos, songTab[MAX_ORDERS];
-	int16_t songPos, pattNr, pattPos, pattLen;
-	int32_t antChn;
-	uint16_t len, repS, speed, tempo, globVol, timer, ver, initialTempo;
+	uint8_t curReplayerTick, curReplayerRow, curReplayerSongPos, curReplayerPattNum; // used for audio/video sync queue
+	uint8_t pattDelTime, pattDelTime2, pBreakPos, orders[MAX_ORDERS];
+	int16_t songPos, pattNum, row, currNumRows;
+	uint16_t songLength, songLoopStart, BPM, speed, initialSpeed, globalVolume, tick;
+	int32_t numChannels;
 	uint64_t musicTime64;
-} songTyp;
+} song_t;
 
-double getSampleC4Rate(sampleTyp *s);
+double getSampleC4Rate(sample_t *s);
 
 void setNewSongPos(int32_t pos);
 void resetReplayerState(void);
@@ -257,12 +282,12 @@
 
 void fixString(char *str, int32_t lastChrPos); // removes leading spaces and 0x1A chars
 void fixSongName(void);
-void fixInstrAndSampleNames(int16_t nr);
+void fixInstrAndSampleNames(int16_t insNum);
 
 void calcReplayerVars(int32_t rate);
 
 // used on external sample load and during sample loading in some module formats
-void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag);
+void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag);
 
 void calcReplayerLogTab(void);
 
@@ -272,10 +297,10 @@
 
 int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote); // for piano in Instr. Ed.
 
-bool allocateInstr(int16_t nr);
-void freeInstr(int32_t nr);
+bool allocateInstr(int16_t insNum);
+void freeInstr(int32_t insNum);
 void freeAllInstr(void);
-void freeSample(int16_t nr, int16_t nr2);
+void freeSample(int16_t insNum, int16_t smpNum);
 
 void freeAllPatterns(void);
 void updateChanNums(void);
@@ -285,28 +310,28 @@
 void startPlaying(int8_t mode, int16_t row);
 void stopPlaying(void);
 void stopVoices(void);
-void setPos(int16_t songPos, int16_t pattPos, bool resetTimer);
+void setPos(int16_t songPos, int16_t row, bool resetTimer);
 void pauseMusic(void); // stops reading pattern data
 void resumeMusic(void); // starts reading pattern data
 void setSongModifiedFlag(void);
 void removeSongModifiedFlag(void);
-void playTone(uint8_t stmm, uint8_t inst, uint8_t ton, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch);
-void playSample(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch);
-void playRange(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch, int32_t offs, int32_t len);
-void keyOff(stmTyp *ch);
-void conv8BitSample(int8_t *p, int32_t len, bool stereo);
-void conv16BitSample(int8_t *p, int32_t len, bool stereo);
-void delta2Samp(int8_t *p, int32_t len, uint8_t typ);
-void samp2Delta(int8_t *p, int32_t len, uint8_t typ);
-void setPatternLen(uint16_t nr, int16_t len);
-void setFrqTab(bool linear);
+void playTone(uint8_t chNum, uint8_t insNum, uint8_t note, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch);
+void playSample(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch);
+void playRange(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch, int32_t smpOffset, int32_t length);
+void keyOff(channel_t *ch);
+void conv8BitSample(int8_t *p, int32_t length, bool stereo); // changes sample sign
+void conv16BitSample(int8_t *p, int32_t length, bool stereo); // changes sample sign
+void delta2Samp(int8_t *p, int32_t length, uint8_t smpFlags);
+void samp2Delta(int8_t *p, int32_t length, uint8_t smpFlags);
+void setPatternLen(uint16_t pattNum, int16_t numRows);
+void setFrequencyTable(bool linearPeriodsFlag);
 void tickReplayer(void); // periodically called from audio callback
 void resetChannels(void);
-bool patternEmpty(uint16_t nr);
-int16_t getUsedSamples(int16_t nr);
-int16_t getRealUsedSamples(int16_t nr);
-void setStdEnvelope(instrTyp *ins, int16_t i, uint8_t typ);
-void setNoEnvelope(instrTyp *ins);
+bool patternEmpty(uint16_t pattNum);
+int16_t getUsedSamples(int16_t smpNum);
+int16_t getRealUsedSamples(int16_t smpNum);
+void setStdEnvelope(instr_t *ins, int16_t i, uint8_t type);
+void setNoEnvelope(instr_t *ins);
 void setSyncedReplayerVars(void);
 void decSongPos(void);
 void incSongPos(void);
@@ -324,8 +349,8 @@
 extern bool songPlaying, audioPaused, musicPaused;
 extern volatile bool replayerBusy;
 extern const uint16_t *note2Period;
-extern int16_t pattLens[MAX_PATTERNS];
-extern stmTyp stm[MAX_VOICES];
-extern songTyp song;
-extern instrTyp *instr[132];
-extern tonTyp *patt[MAX_PATTERNS];
+extern int16_t patternNumRows[MAX_PATTERNS];
+extern channel_t channel[MAX_CHANNELS];
+extern song_t song;
+extern instr_t *instr[132];
+extern note_t *pattern[MAX_PATTERNS];
--- a/src/ft2_sample_ed.c
+++ b/src/ft2_sample_ed.c
@@ -18,7 +18,7 @@
 #include "ft2_audio.h"
 #include "ft2_pattern_ed.h"
 #include "ft2_gui.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_video.h"
 #include "ft2_inst_ed.h"
 #include "ft2_sample_ed.h"
@@ -27,6 +27,7 @@
 #include "ft2_diskop.h"
 #include "ft2_keyboard.h"
 #include "ft2_structs.h"
+#include "ft2_replayer.h"
 #include "mixer/ft2_windowed_sinc.h" // SINC_TAPS, SINC_NEGATIVE_TAPS
 
 static const char sharpNote1Char[12] = { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' };
@@ -36,12 +37,13 @@
 
 static char smpEd_SysReqText[64];
 static int8_t *smpCopyBuff;
-static bool updateLoopsOnMouseUp, writeSampleFlag;
+static bool updateLoopsOnMouseUp, writeSampleFlag, smpCopyDidCopyWholeSample;
 static int32_t smpEd_OldSmpPosLine = -1;
 static int32_t smpEd_ViewSize, smpEd_ScrPos, smpCopySize, smpCopyBits;
 static int32_t old_Rx1, old_Rx2, old_ViewSize, old_SmpScrPos;
-static int32_t lastMouseX, lastMouseY, lastDrawX, lastDrawY, mouseXOffs, curSmpRepS, curSmpRepL;
+static int32_t lastMouseX, lastMouseY, lastDrawX, lastDrawY, mouseXOffs, curSmpLoopStart, curSmpLoopLength;
 static double dScrPosScaled, dPos2ScrMul, dScr2SmpPosMul;
+static sample_t smpCopySample;
 static SDL_Thread *thread;
 
 // globals
@@ -48,100 +50,192 @@
 int32_t smpEd_Rx1 = 0, smpEd_Rx2 = 0;
 
 // allocs sample with proper alignment and padding for branchless resampling interpolation
-bool allocateTmpSmpData(sampleTyp *s, int32_t length)
+bool allocateSmpData(sample_t *s, int32_t length, bool sample16Bit)
 {
-	s->origPek = (int8_t *)malloc(length + LOOP_FIX_LEN);
-	if (s->origPek == NULL)
+	if (sample16Bit)
+		length <<= 1;
+
+	s->origDataPtr = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH);
+	if (s->origDataPtr == NULL)
 	{
-		s->pek = NULL;
+		s->dataPtr = NULL;
 		return false;
 	}
 
-	s->pek = s->origPek + SMP_DAT_OFFSET;
+	s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET;
 	return true;
 }
 
+bool allocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit)
+{
+	if (sample16Bit)
+		length <<= 1;
+
+	int8_t *newPtr = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH);
+	if (newPtr == NULL)
+		return false;
+
+	sp->origPtr = newPtr;
+
+	sp->ptr = sp->origPtr + SMP_DAT_OFFSET;
+	return true;
+}
+
 // reallocs sample with proper alignment and padding for branchless resampling interpolation
-bool reallocateTmpSmpData(sampleTyp *s, int32_t length)
+bool reallocateSmpData(sample_t *s, int32_t length, bool sample16Bit)
 {
-	if (s->origPek == NULL)
-		return allocateTmpSmpData(s, length);
+	if (s->origDataPtr == NULL)
+		return allocateSmpData(s, length, sample16Bit);
 
-	s->origPek = (int8_t *)realloc(s->origPek, length + LOOP_FIX_LEN);
-	if (s->origPek == NULL)
-	{
-		s->pek = NULL;
+	if (sample16Bit)
+		length <<= 1;
+
+	int8_t *newPtr = (int8_t *)realloc(s->origDataPtr, length + SAMPLE_PAD_LENGTH);
+	if (newPtr == NULL)
 		return false;
+
+	s->origDataPtr = newPtr;
+	s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET;
+
+	return true;
+}
+
+// reallocs sample with proper alignment and padding for branchless resampling interpolation
+bool reallocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit)
+{
+	if (sp->origPtr == NULL)
+		return allocateSmpDataPtr(sp, length, sample16Bit);
+
+	if (sample16Bit)
+		length <<= 1;
+
+	int8_t *newPtr = (int8_t *)realloc(sp->origPtr, length + SAMPLE_PAD_LENGTH);
+	if (newPtr == NULL)
+		return false;
+
+	sp->origPtr = newPtr;
+	sp->ptr = sp->origPtr + SMP_DAT_OFFSET;
+
+	return true;
+}
+
+void setSmpDataPtr(sample_t *s, smpPtr_t *sp)
+{
+	s->origDataPtr = sp->origPtr;
+	s->dataPtr = sp->ptr;
+}
+
+void freeSmpDataPtr(smpPtr_t *sp)
+{
+	if (sp->origPtr != NULL)
+	{
+		free(sp->origPtr);
+		sp->origPtr = NULL;
 	}
 
-	s->pek = s->origPek + SMP_DAT_OFFSET;
+	sp->ptr = NULL;
+}
+
+void freeSmpData(sample_t *s)
+{
+	if (s->origDataPtr != NULL)
+	{
+		free(s->origDataPtr);
+		s->origDataPtr = NULL;
+	}
+
+	s->dataPtr = NULL;
+	s->isFixed = false;
+}
+
+bool cloneSample(sample_t *src, sample_t *dst)
+{
+	smpPtr_t sp;
+
+	freeSmpData(dst);
+	memcpy(dst, src, sizeof (sample_t));
+
+	dst->origDataPtr = dst->dataPtr = NULL;
+	dst->isFixed = false;
+
+	if (src->length > 0 && src->dataPtr != NULL)
+	{
+		bool sample16Bit = !!(src->flags & SAMPLE_16BIT);
+		if (!allocateSmpDataPtr(&sp, src->length, sample16Bit))
+			return false;
+
+		memcpy(sp.ptr, src->dataPtr, src->length);
+		setSmpDataPtr(dst, &sp);
+		fixSample(dst);
+	}
+
 	return true;
 }
 
-sampleTyp *getCurSample(void)
+sample_t *getCurSample(void)
 {
 	if (editor.curInstr == 0 || instr[editor.curInstr] == NULL)
 		return NULL;
 
-	return &instr[editor.curInstr]->samp[editor.curSmp];
+	return &instr[editor.curInstr]->smp[editor.curSmp];
 }
 
-void checkSampleRepeat(sampleTyp *s)
+void sanitizeSample(sample_t *s)
 {
-	if (s->repS < 0) s->repS = 0;
-	if (s->repL < 0) s->repL = 0;
-	if (s->repS > s->len) s->repS = s->len;
-	if (s->repS+s->repL > s->len) s->repL = s->len - s->repS;
+	if (s == NULL)
+		return;
 
-	if (s->repL == 0) // non-FT2 fix: disable loop if loop length is 0
+	// if a sample has both forward loop and pingpong loop set, it means pingpong loop (FT2 mixer behavior)
+	if (GET_LOOPTYPE(s->flags) == (LOOP_FWD | LOOP_BIDI))
+		s->flags &= ~LOOP_FWD; // remove forward loop flag
+
+	if (s->volume > 64)
+		s->volume = 64;
+
+	s->relativeNote = CLAMP(s->relativeNote, -48, 71);
+	s->length = CLAMP(s->length, 0, MAX_SAMPLE_LEN);
+
+	if (s->loopStart < 0 || s->loopLength <= 0 || s->loopStart+s->loopLength > s->length)
 	{
-		s->repS = 0;
-		s->typ &= ~3;
+		s->loopStart = 0;
+		s->loopLength = 0;
+		DISABLE_LOOP(s->flags);
 	}
 }
 
+static int32_t myMod(int32_t a, int32_t b) // works on negative numbers!
+{
+	int32_t c = a % b;
+	return (c < 0) ? (c + b) : c;
+}
+
 // modifies samples before index 0, and after loop/end (for branchless mixer interpolation (kinda))
-void fixSample(sampleTyp *s)
+void fixSample(sample_t *s)
 {
 	int32_t pos;
 	bool backwards;
 
 	assert(s != NULL);
-	if (s->origPek == NULL || s->pek == NULL)
+	if (s->dataPtr == NULL || s->length <= 0)
 	{
-		s->fixed = false;
+		s->isFixed = false;
 		s->fixedPos = 0;
 		return; // empty sample
 	}
 
-	const bool sample16Bit = (s->typ & 16) ? true : false;
-	int16_t *ptr16 = (int16_t *)s->pek;
-	uint8_t loopType = s->typ & 3;
-	int32_t len = s->len;
-	int32_t loopStart = s->repS;
-	int32_t loopLen = s->repL;
-	int32_t loopEnd = s->repS + s->repL;
+	const bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+	int16_t *ptr16 = (int16_t *)s->dataPtr;
+	uint8_t loopType = GET_LOOPTYPE(s->flags);
+	int32_t length = s->length;
+	int32_t loopStart = s->loopStart;
+	int32_t loopLength = s->loopLength;
+	int32_t loopEnd = s->loopStart + s->loopLength;
 
-	if (sample16Bit)
-	{
-		len >>= 1;
-		loopStart >>= 1;
-		loopLen >>= 1;
-		loopEnd >>= 1;
-	}
-
-	if (len < 1)
-	{
-		s->fixed = false;
-		s->fixedPos = 0;
-		return; // empty sample
-	}
-
 	// treat loop as disabled if loopLen == 0 (FT2 does this)
-	if (loopType != 0 && loopLen <= 0)
+	if (loopType != 0 && loopLength <= 0)
 	{
 		loopType = 0;
-		loopStart = loopLen = loopEnd = 0;
+		loopStart = loopLength = loopEnd = 0;
 	}
 
 	/* All negative taps should be equal to the first sample point when at sampling
@@ -158,275 +252,195 @@
 	else
 	{
 		for (int32_t i = 0; i < SINC_LEFT_TAPS; i++)
-			s->pek[i-SINC_LEFT_TAPS] = s->pek[0];
+			s->dataPtr[i-SINC_LEFT_TAPS] = s->dataPtr[0];
 	}
 
-	// no loop
-	if (loopType == 0)
+	if (loopType == LOOP_OFF) // no loop
 	{
 		if (sample16Bit)
 		{
 			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
-				ptr16[len+i] = ptr16[len-1];
+				ptr16[length+i] = ptr16[length-1];
 		}
 		else
 		{
 			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
-				s->pek[len+i] = s->pek[len-1];
+				s->dataPtr[length+i] = s->dataPtr[length-1];
 		}
 
 		s->fixedPos = 0; // this value is not used for non-looping samples, set to zero
-
-		s->fixed = false; // no fixed samples inside actual sample data
+		s->isFixed = false; // no fixed samples inside actual sample data
 		return;
 	}
 
-	if (s->fixed)
-		return; // already fixed
-
 	s->fixedPos = loopEnd;
-	s->fixed = true;
+	s->isFixed = true;
 
-	// special-case for loop-lengt of 1
-	if (loopLen == 1)
+	if (loopType == LOOP_FWD) // forward loop
 	{
 		if (sample16Bit)
 		{
-			for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++)
-				s->leftEdgeTapSamples16[i] = ptr16[loopStart];
-
-			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
+			// left edge (we need SINC_TAPS amount of taps starting from the center tap)
+			for (int32_t i = -SINC_LEFT_TAPS; i < SINC_TAPS; i++)
 			{
-				s->fixedSmp[i] = ptr16[loopEnd+i];
-				ptr16[loopEnd+i] = ptr16[loopStart];
+				pos = loopStart + myMod(i, loopLength);
+				s->leftEdgeTapSamples16[SINC_LEFT_TAPS+i] = ptr16[pos];
 			}
-		}
-		else
-		{
-			for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++)
-				s->leftEdgeTapSamples8[i] = s->pek[loopStart];
 
-			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
-			{
-				s->fixedSmp[i] = s->pek[loopEnd+i];
-				s->pek[loopEnd+i] = s->pek[loopStart];
-			}
-		}
-
-		return;
-	}
-
-	if (loopType == 1)
-	{
-		// forward loop
-		if (sample16Bit)
-		{
-			// left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples)
-			for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++)
-			{
-				if (i < SINC_LEFT_TAPS) // negative taps
-				{
-					pos = loopEnd-i;
-					if (pos <= loopStart) // XXX: Correct?
-						pos += loopLen;
-				}
-				else // positive taps
-				{
-					pos = loopStart + ((i-SINC_LEFT_TAPS) % loopLen);
-				}
-
-				s->leftEdgeTapSamples16[i] = ptr16[pos];
-			}
-
 			// right edge (change actual sample data since data after loop is never used)
+			pos = loopStart;
 			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
 			{
 				s->fixedSmp[i] = ptr16[loopEnd+i];
-				ptr16[loopEnd+i] = ptr16[loopStart + (i % loopLen)];
+				ptr16[loopEnd+i] = ptr16[pos];
+
+				if (++pos >= loopEnd)
+					pos -= loopLength;
 			}
 		}
-		else
+		else // 8-bit
 		{
-			// left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples)
-			for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++)
+			// left edge (we need SINC_TAPS amount of taps starting from the center tap)
+			for (int32_t i = -SINC_LEFT_TAPS; i < SINC_TAPS; i++)
 			{
-				if (i < SINC_LEFT_TAPS) // negative taps
-				{
-					pos = loopEnd-i;
-					if (pos <= loopStart) // XXX: Correct?
-						pos += loopLen;
-				}
-				else // positive taps
-				{
-					pos = loopStart + ((i-SINC_LEFT_TAPS) % loopLen);
-				}
-
-				s->leftEdgeTapSamples8[i] = s->pek[pos];
+				pos = loopStart + myMod(i, loopLength);
+				s->leftEdgeTapSamples8[SINC_LEFT_TAPS+i] = s->dataPtr[pos];
 			}
 
 			// right edge (change actual sample data since data after loop is never used)
+			pos = loopStart;
 			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
 			{
-				s->fixedSmp[i] = s->pek[loopEnd+i];
-				s->pek[loopEnd+i] = s->pek[loopStart + (i % loopLen)];
+				s->fixedSmp[i] = s->dataPtr[loopEnd+i];
+				s->dataPtr[loopEnd+i] = s->dataPtr[pos];
+
+				if (++pos >= loopEnd)
+					pos -= loopLength;
 			}
 		}
 	}
-	else
+	else // pingpong loop
 	{
-		// pingpong loop
 		if (sample16Bit)
 		{
-			// left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples)
+			// left edge (positive taps, we need SINC_TAPS amount of taps starting from the center tap)
 			pos = loopStart;
 			backwards = false;
-			for (int32_t i = 0; i < SINC_LEFT_TAPS; i++)
+			for (int32_t i = 0; i < SINC_TAPS; i++)
 			{
 				if (backwards)
 				{
-					if (--pos < loopStart)
+					if (pos < loopStart)
 					{
 						pos = loopStart;
 						backwards = false;
 					}
 				}
-				else
+				else if (pos >= loopEnd) // forwards
 				{
-					if (++pos >= loopEnd)
-					{
-						pos = loopEnd-1;
-						backwards = true;
-					}
+					pos = loopEnd-1;
+					backwards = true;
 				}
 
-				s->leftEdgeTapSamples16[3-i] = ptr16[pos];
-			}
+				s->leftEdgeTapSamples16[SINC_LEFT_TAPS+i] = ptr16[pos];
 
-			pos = loopStart;
-			backwards = true;
-			for (int32_t i = 3; i < SINC_TAPS+SINC_LEFT_TAPS; i++)
-			{
 				if (backwards)
-				{
-					if (--pos < loopStart)
-					{
-						pos = loopStart;
-						backwards = false;
-					}
-				}
+					pos--;
 				else
-				{
-					if (++pos >= loopEnd)
-					{
-						pos = loopEnd-1;
-						backwards = true;
-					}
-				}
-				
-				s->leftEdgeTapSamples16[i] = ptr16[pos];
+					pos++;
 			}
 
+			// left edge (negative taps)
+			for (int32_t i = 0; i < SINC_LEFT_TAPS; i++)
+				s->leftEdgeTapSamples16[(SINC_LEFT_TAPS-1)-i] = s->leftEdgeTapSamples16[SINC_LEFT_TAPS+1+i];
+
 			// right edge (change actual sample data since data after loop is never used)
 			pos = loopEnd-1;
 			backwards = true;
 			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
 			{
-				s->fixedSmp[i] = ptr16[loopEnd+i];
-
-				ptr16[loopEnd+i] = ptr16[pos];
 				if (backwards)
 				{
-					if (--pos < loopStart)
+					if (pos < loopStart)
 					{
 						pos = loopStart;
 						backwards = false;
 					}
 				}
-				else
+				else if (pos >= loopEnd) // forwards
 				{
-					if (++pos >= loopEnd)
-					{
-						pos = loopEnd-1;
-						backwards = true;
-					}
+					pos = loopEnd-1;
+					backwards = true;
 				}
+
+				s->fixedSmp[i] = ptr16[loopEnd+i];
+				ptr16[loopEnd+i] = ptr16[pos];
+
+				if (backwards)
+					pos--;
+				else
+					pos++;
 			}
 		}
-		else
+		else // 8-bit
 		{
-			// left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples)
+			// left edge (positive taps, we need SINC_TAPS amount of taps starting from the center tap)
 			pos = loopStart;
 			backwards = false;
-			for (int32_t i = 0; i < SINC_LEFT_TAPS; i++)
+			for (int32_t i = 0; i < SINC_TAPS; i++)
 			{
 				if (backwards)
 				{
-					if (--pos < loopStart)
+					if (pos < loopStart)
 					{
 						pos = loopStart;
 						backwards = false;
 					}
 				}
-				else
+				else if (pos >= loopEnd) // forwards
 				{
-					if (++pos >= loopEnd)
-					{
-						pos = loopEnd-1;
-						backwards = true;
-					}
+					pos = loopEnd-1;
+					backwards = true;
 				}
 
-				s->leftEdgeTapSamples8[3-i] = s->pek[pos];
-			}
+				s->leftEdgeTapSamples8[SINC_LEFT_TAPS+i] = s->dataPtr[pos];
 
-			pos = loopStart;
-			backwards = true;
-			for (int32_t i = 3; i < SINC_TAPS+SINC_LEFT_TAPS; i++)
-			{
 				if (backwards)
-				{
-					if (--pos < loopStart)
-					{
-						pos = loopStart;
-						backwards = false;
-					}
-				}
+					pos--;
 				else
-				{
-					if (++pos >= loopEnd)
-					{
-						pos = loopEnd-1;
-						backwards = true;
-					}
-				}
-				
-				s->leftEdgeTapSamples8[i] = s->pek[pos];
+					pos++;
 			}
 
+			// left edge (negative taps)
+			for (int32_t i = 0; i < SINC_LEFT_TAPS; i++)
+				s->leftEdgeTapSamples8[(SINC_LEFT_TAPS-1)-i] = s->leftEdgeTapSamples8[SINC_LEFT_TAPS+1+i];
+
 			// right edge (change actual sample data since data after loop is never used)
 			pos = loopEnd-1;
 			backwards = true;
 			for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
 			{
-				s->fixedSmp[i] = s->pek[loopEnd+i];
-
-				s->pek[loopEnd+i] = s->pek[pos];
 				if (backwards)
 				{
-					if (--pos < loopStart)
+					if (pos < loopStart)
 					{
 						pos = loopStart;
 						backwards = false;
 					}
 				}
-				else
+				else if (pos >= loopEnd) // forwards
 				{
-					if (++pos >= loopEnd)
-					{
-						pos = loopEnd-1;
-						backwards = true;
-					}
+					pos = loopEnd-1;
+					backwards = true;
 				}
+
+				s->fixedSmp[i] = s->dataPtr[loopEnd+i];
+				s->dataPtr[loopEnd+i] = s->dataPtr[pos];
+
+				if (backwards)
+					pos--;
+				else
+					pos++;
 			}
 		}
 	}
@@ -433,61 +447,58 @@
 }
 
 // restores interpolation tap samples after loop/end
-void restoreSample(sampleTyp *s)
+void unfixSample(sample_t *s)
 {
 	assert(s != NULL);
-	if (s->origPek == NULL || s->pek == NULL || !s->fixed)
+	if (s->dataPtr == NULL || !s->isFixed)
 		return; // empty sample or not fixed (f.ex. no loop)
 
-	if (s->typ & 16)
+	if (s->flags & SAMPLE_16BIT)
 	{
-		// 16-bit sample
-		int16_t *ptr16 = (int16_t *)s->pek + s->fixedPos;
+		int16_t *ptr16 = (int16_t *)s->dataPtr + s->fixedPos;
 		for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
 			ptr16[i] = s->fixedSmp[i];
 	}
-	else
+	else // 8-bit
 	{
-		// 8-bit sample
-		int8_t *ptr8 = s->pek + s->fixedPos;
+		int8_t *ptr8 = s->dataPtr + s->fixedPos;
 		for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++)
 			ptr8[i] = (int8_t)s->fixedSmp[i];
 	}
 
-	s->fixed = false;
+	s->isFixed = false;
 }
 
-int16_t getSampleValue(int8_t *ptr, uint8_t typ, int32_t pos)
+double getSampleValue(int8_t *smpData, int32_t position, bool sample16Bit)
 {
-	assert(pos >= 0);
-	if (ptr == NULL)
+	if (smpData == NULL)
 		return 0;
 
-	if (typ & 16)
+	if (sample16Bit)
 	{
-		assert(!(pos & 1));
-		return *(int16_t *)&ptr[pos];
+		position <<= 1;
+		return *((int16_t *)&smpData[position]);
 	}
 	else
 	{
-		return ptr[pos];
+		return smpData[position];
 	}
 }
 
-void putSampleValue(int8_t *ptr, uint8_t typ, int32_t pos, int16_t val)
+void putSampleValue(int8_t *smpData, int32_t position, double dSample, bool sample16Bit)
 {
-	assert(pos >= 0);
-	if (ptr == NULL)
-		return;
+	DROUND(dSample);
+	int32_t sample = (int32_t)dSample;
 
-	if (typ & 16)
+	if (sample16Bit)
 	{
-		assert(!(pos & 1));
-		*(int16_t *)&ptr[pos] = val;
+		CLAMP16(sample);
+		*((int16_t *)&smpData[position<<1]) = (int16_t)sample;
 	}
 	else
 	{
-		ptr[pos] = (int8_t)val;
+		CLAMP8(sample);
+		smpData[position] = (int8_t)sample;
 	}
 }
 
@@ -501,9 +512,10 @@
 
 	smpCopySize = 0;
 	smpCopyBits = 8;
+	smpCopyDidCopyWholeSample = false;
 }
 
-int32_t getSampleMiddleCRate(sampleTyp *s)
+int32_t getSampleMiddleCRate(sample_t *s)
 {
 	return (int32_t)(getSampleC4Rate(s) + 0.5); // rounded
 }
@@ -536,7 +548,7 @@
 
 static void updateScrPos(void)
 {
-	dScrPosScaled = trunc(smpEd_ScrPos * dPos2ScrMul);
+	dScrPosScaled = floor(smpEd_ScrPos * dPos2ScrMul);
 }
 
 // sample pos -> screen x pos (if outside of visible area, will return <0 or >=SCREEN_W)
@@ -545,14 +557,14 @@
 	if (smpEd_ViewSize <= 0)
 		return -1;
 
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
 	if (s == NULL)
 		return -1;
 
-	if (pos > s->len)
-		pos = s->len;
+	if (pos > s->length)
+		pos = s->length;
 
-	double dPos = (pos * dPos2ScrMul) + 0.5; // rounding is needed here (+ 0.5)
+	double dPos = (pos * dPos2ScrMul) + 0.5; // pre-rounding bias is needed here
 	dPos -= dScrPosScaled;
 
 	// this is important, or else the result can mess up in some cases
@@ -568,7 +580,7 @@
 	if (smpEd_ViewSize <= 0)
 		return 0;
 
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
 	if (s == NULL)
 		return 0;
 
@@ -576,67 +588,73 @@
 		x = 0;
 
 	double dPos = (dScrPosScaled + x) * dScr2SmpPosMul;
+
 	x = (int32_t)dPos;
+	if (x > s->length)
+		x = s->length;
 
-	if (x > s->len)
-		x = s->len;
-
-	if (s->typ & 16)
-		x &= 0xFFFFFFFE;
-
 	return x;
 }
 
-static void fixRepeatGadgets(void)
+static void hideLoopPinSprites(void)
 {
-	bool showLoopPins = true;
+	hideSprite(SPRITE_LEFT_LOOP_PIN);
+	hideSprite(SPRITE_RIGHT_LOOP_PIN);
+}
 
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->len <= 0 || s->pek == NULL || (s->typ & 3) == 0 || !ui.sampleEditorShown)
-		showLoopPins = false;
-
-	if (ui.sampleEditorShown)
+static void fixLoopGadgets(void)
+{
+	if (!ui.sampleEditorShown)
 	{
-		// draw Repeat/Replen. numbers
-		hexOutBg(536, 375, PAL_FORGRND, PAL_DESKTOP, curSmpRepS, 8);
-		hexOutBg(536, 387, PAL_FORGRND, PAL_DESKTOP, curSmpRepL, 8);
+		hideLoopPinSprites();
+		return;
 	}
 
+	sample_t *s = getCurSample();
+
+	bool showLoopPins = true;
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0 || GET_LOOPTYPE(s->flags) == LOOP_OFF)
+		showLoopPins = false;
+
+	// draw Repeat/Replen. numbers
+	hexOutBg(536, 375, PAL_FORGRND, PAL_DESKTOP, curSmpLoopStart, 8);
+	hexOutBg(536, 387, PAL_FORGRND, PAL_DESKTOP, curSmpLoopLength, 8);
+
 	if (!showLoopPins)
 	{
-		hideSprite(SPRITE_LEFT_LOOP_PIN);
-		hideSprite(SPRITE_RIGHT_LOOP_PIN);
-		return;
+		hideLoopPinSprites();
 	}
+	else
+	{
+		// draw sample loop points
 
-	// draw sample loop points
+		const int32_t loopStart = smpPos2Scr(curSmpLoopStart);
+		const int32_t loopEnd = smpPos2Scr(curSmpLoopStart+curSmpLoopLength);
 
-	int32_t repS = smpPos2Scr(curSmpRepS);
-	int32_t repE = smpPos2Scr(curSmpRepS+curSmpRepL);
+		// do -8 test because part of the loop sprite sticks out on the left/right
 
-	// do -8 test because part of the loop sprite sticks out on the left/right
+		if (loopStart >= -8 && loopStart <= SAMPLE_AREA_WIDTH+8)
+			setSpritePos(SPRITE_LEFT_LOOP_PIN, (int16_t)(loopStart - 8), 174);
+		else
+			hideSprite(SPRITE_LEFT_LOOP_PIN);
 
-	if (repS >= -8 && repS <= SAMPLE_AREA_WIDTH+8)
-		setSpritePos(SPRITE_LEFT_LOOP_PIN, (int16_t)(repS - 8), 174);
-	else
-		hideSprite(SPRITE_LEFT_LOOP_PIN);
-
-	if (repE >= -8)
-	{
-		if (repE <= SAMPLE_AREA_WIDTH+8)
-			setSpritePos(SPRITE_RIGHT_LOOP_PIN, (int16_t)(repE - 8), 174);
+		if (loopEnd >= -8)
+		{
+			if (loopEnd <= SAMPLE_AREA_WIDTH+8)
+				setSpritePos(SPRITE_RIGHT_LOOP_PIN, (int16_t)(loopEnd - 8), 174);
+			else
+				hideSprite(SPRITE_RIGHT_LOOP_PIN);
+		}
 		else
+		{
 			hideSprite(SPRITE_RIGHT_LOOP_PIN);
+		}
 	}
-	else
-	{
-		hideSprite(SPRITE_RIGHT_LOOP_PIN);
-	}
 }
 
-static void fixSampleDrag(void)
+static void fixSampleScrollbar(void)
 {
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
 	if (s == NULL)
 	{
 		setScrollBarPageLength(SB_SAMP_SCROLL, 0);
@@ -646,11 +664,11 @@
 	}
 
 	setScrollBarPageLength(SB_SAMP_SCROLL, smpEd_ViewSize);
-	setScrollBarEnd(SB_SAMP_SCROLL, instr[editor.curInstr]->samp[editor.curSmp].len);
+	setScrollBarEnd(SB_SAMP_SCROLL, instr[editor.curInstr]->smp[editor.curSmp].length);
 	setScrollBarPos(SB_SAMP_SCROLL, smpEd_ScrPos, false);
 }
 
-static bool getCopyBuffer(int32_t size)
+static bool getCopyBuffer(int32_t size, bool sample16Bit)
 {
 	if (smpCopyBuff != NULL)
 		free(smpCopyBuff);
@@ -658,7 +676,7 @@
 	if (size > MAX_SAMPLE_LEN)
 		size = MAX_SAMPLE_LEN;
 
-	smpCopyBuff = (int8_t *)malloc(size);
+	smpCopyBuff = (int8_t *)malloc(size << sample16Bit);
 	if (smpCopyBuff == NULL)
 	{
 		smpCopySize = 0;
@@ -671,44 +689,19 @@
 
 static int32_t SDLCALL copySampleThread(void *ptr)
 {
-	bool error = false;
+	sample_t *src = &instr[editor.srcInstr]->smp[editor.srcSmp];
+	sample_t *dst = &instr[editor.curInstr]->smp[editor.curSmp];
 
-	int16_t destIns = editor.curInstr;
-	int16_t destSmp = editor.curSmp;
-	int16_t sourceIns = editor.srcInstr;
-	int16_t sourceSmp = editor.srcSmp;
-
 	pauseAudio();
 
-	if (instr[destIns] == NULL)
-		error = !allocateInstr(destIns);
+	if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr))
+		goto error;
 
-	if (!error)
-	{
-		freeSample(destIns, destSmp);
+	if (!cloneSample(src, dst))
+		goto error;
 
-		sampleTyp *src = &instr[sourceIns]->samp[sourceSmp];
-		sampleTyp *dst = &instr[destIns]->samp[destSmp];
-
-		if (instr[sourceIns] != NULL && src->origPek != NULL)
-		{
-			int8_t *p = (int8_t *)malloc(src->len + LOOP_FIX_LEN);
-			if (p != NULL)
-			{
-				memcpy(dst, src, sizeof (sampleTyp));
-				memcpy(p, src->origPek, src->len + LOOP_FIX_LEN);
-				dst->origPek = p;
-				dst->pek = dst->origPek + SMP_DAT_OFFSET;
-			}
-			else error = true;
-		}
-	}
-
 	resumeAudio();
 
-	if (error)
-		okBoxThreadSafe(0, "System message", "Not enough memory!");
-
 	editor.updateCurSmp = true;
 	setSongModifiedFlag();
 	setMouseBusy(false);
@@ -715,6 +708,11 @@
 
 	return true;
 
+error:
+	resumeAudio();
+	okBoxThreadSafe(0, "System message", "Not enough memory!");
+	return true;
+
 	(void)ptr;
 }
 
@@ -743,11 +741,11 @@
 		return;
 	}
 
-	sampleTyp *src = &instr[editor.curInstr]->samp[editor.srcSmp];
-	sampleTyp *dst = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *src = &instr[editor.curInstr]->smp[editor.srcSmp];
+	sample_t *dst = &instr[editor.curInstr]->smp[editor.curSmp];
 
 	lockMixerCallback();
-	const sampleTyp dstTmp = *dst;
+	const sample_t dstTmp = *dst;
 	*dst = *src;
 	*src = dstTmp;
 	unlockMixerCallback();
@@ -788,25 +786,21 @@
 	}
 }
 
-static int8_t getScaledSample(sampleTyp *s, int32_t index) // for drawing sample waveform in zoomed-in mode
+static int32_t getScaledSample(sample_t *s, int32_t index) // for sample data viewer
 {
-	int8_t sample;
-	int32_t tmp32;
+	int32_t tmp32, sample;
 
-	const int32_t loopEnd = s->repS + s->repL;
+	const int32_t loopEnd = s->loopStart + s->loopLength;
 
-	if (s->pek == NULL || index < 0 || index >= s->len)
+	if (s->dataPtr == NULL || index < 0 || index >= s->length)
 		return 0;
 
-	if (s->typ & 16)
+	if (s->flags & SAMPLE_16BIT)
 	{
-		assert(!(index & 1));
-		index >>= 1;
+		int16_t *ptr16 = (int16_t *)s->dataPtr;
 
-		int16_t *ptr16 = (int16_t *)s->pek;
-
 		// don't read fixed mixer interpolation samples, read the prestine ones instead
-		if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->len > loopEnd && s->fixed)
+		if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->length > loopEnd && s->isFixed)
 			tmp32 = s->fixedSmp[index-s->fixedPos];
 		else
 			tmp32 = ptr16[index];
@@ -813,32 +807,30 @@
 
 		sample = (int8_t)((tmp32 * SAMPLE_AREA_HEIGHT) >> 16);
 	}
-	else
+	else // 8-bit
 	{
-		// don't read fixed mixer interpolation samples, read the prestine ones instead
-		if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->len > loopEnd && s->fixed)
+		if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->length > loopEnd && s->isFixed)
 			tmp32 = s->fixedSmp[index-s->fixedPos];
 		else
-			tmp32 = s->pek[index];
+			tmp32 = s->dataPtr[index];
 
 		sample = (int8_t)((tmp32 * SAMPLE_AREA_HEIGHT) >> 8);
 	}
 
-	return sample;
+	return SAMPLE_AREA_Y_CENTER-sample;
 }
 
-static void sampleLine(int16_t x1, int16_t x2, int16_t y1, int16_t y2)
+void sampleLine(int32_t x1, int32_t x2, int32_t y1, int32_t y2)
 {
-	int16_t d;
-
-	const int16_t dx = x2 - x1;
-	const int16_t ax = ABS(dx) * 2;
-	const int16_t sx = SGN(dx);
-	const int16_t dy = y2 - y1;
-	const int16_t ay = ABS(dy) * 2;
-	const int16_t sy = SGN(dy);
-	int16_t x  = x1;
-	int16_t y  = y1;
+	int32_t d;
+	const int32_t dx = x2 - x1;
+	const int32_t ax = ABS(dx) * 2;
+	const int32_t sx = SGN(dx);
+	const int32_t dy = y2 - y1;
+	const int32_t ay = ABS(dy) * 2;
+	const int32_t sy = SGN(dy);
+	int32_t x  = x1;
+	int32_t y  = y1;
 	const uint32_t pal1 = video.palette[PAL_DESKTOP];
 	const uint32_t pal2 = video.palette[PAL_FORGRND];
 	const uint32_t pixVal = video.palette[PAL_PATTEXT];
@@ -1086,11 +1078,11 @@
 }
 
 // for scanning sample data peak where loopEnd+SINC_RIGHT_TAPS is within scan range (fixed interpolation tap samples)
-static void getSpecialMinMax16(sampleTyp *s, int32_t index, int32_t scanEnd, int16_t *min16, int16_t *max16)
+static void getSpecialMinMax16(sample_t *s, int32_t index, int32_t scanEnd, int16_t *min16, int16_t *max16)
 {
 	int16_t minVal2, maxVal2;
 
-	const int16_t *ptr16 = (const int16_t *)s->pek;
+	const int16_t *ptr16 = (const int16_t *)s->dataPtr;
 
 	int16_t minVal =  32767;
 	int16_t maxVal = -32768;
@@ -1131,11 +1123,11 @@
 }
 
 // for scanning sample data peak where loopEnd+SINC_RIGHT_TAPS is within scan range (fixed interpolation tap samples)
-static void getSpecialMinMax8(sampleTyp *s, int32_t index, int32_t scanEnd, int8_t *min8, int8_t *max8)
+static void getSpecialMinMax8(sample_t *s, int32_t index, int32_t scanEnd, int8_t *min8, int8_t *max8)
 {
 	int8_t minVal2, maxVal2;
 
-	const int8_t *ptr8 = (const int8_t *)s->pek;
+	const int8_t *ptr8 = (const int8_t *)s->dataPtr;
 
 	int8_t minVal =  127;
 	int8_t maxVal = -128;
@@ -1175,12 +1167,12 @@
 	*max8 = maxVal;
 }
 
-static void getSampleDataPeak(sampleTyp *s, int32_t index, int32_t numSamples, int16_t *outMin, int16_t *outMax)
+static void getSampleDataPeak(sample_t *s, int32_t index, int32_t length, int16_t *outMin, int16_t *outMax)
 {
 	int8_t min8, max8;
 	int16_t min16, max16;
 
-	if (numSamples == 0 || s->pek == NULL || s->len <= 0)
+	if (length == 0 || s->dataPtr == NULL || s->length <= 0)
 	{
 		*outMin = SAMPLE_AREA_Y_CENTER;
 		*outMax = SAMPLE_AREA_Y_CENTER;
@@ -1187,17 +1179,10 @@
 		return;
 	}
 
-	if (s->typ & 16)
+	if (s->isFixed && s->length > s->loopLength+s->loopStart)
 	{
-		assert(!(index & 1));
-		index >>= 1;
-		numSamples >>= 1;
-	}
+		const int32_t scanEnd = index + length;
 
-	if (s->fixed && s->len > s->repL+s->repS)
-	{
-		const int32_t scanEnd = index + numSamples;
-
 		/* If the scan area is including the fixed samples (for branchless mixer interpolation),
 		** do a special procedure to scan the original non-touched samples when needed.
 		*/
@@ -1204,16 +1189,14 @@
 		const bool insideRange = index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS;
 		if (insideRange || (index < s->fixedPos && scanEnd >= s->fixedPos))
 		{
-			if (s->typ & 16)
+			if (s->flags & SAMPLE_16BIT)
 			{
-				// 16-bit sample
 				getSpecialMinMax16(s, index, scanEnd, &min16, &max16);
 				*outMin = SAMPLE_AREA_Y_CENTER - ((min16 * SAMPLE_AREA_HEIGHT) >> 16);
 				*outMax = SAMPLE_AREA_Y_CENTER - ((max16 * SAMPLE_AREA_HEIGHT) >> 16);
 			}
-			else
+			else // 8-bit
 			{
-				// 8-bit sample
 				getSpecialMinMax8(s, index, scanEnd, &min8, &max8);
 				*outMin = SAMPLE_AREA_Y_CENTER - ((min8 * SAMPLE_AREA_HEIGHT) >> 8);
 				*outMax = SAMPLE_AREA_Y_CENTER - ((max8 * SAMPLE_AREA_HEIGHT) >> 8);
@@ -1223,18 +1206,16 @@
 		}
 	}
 
-	if (s->typ & 16)
+	if (s->flags & SAMPLE_16BIT)
 	{
-		// 16-bit sample
-		const int16_t *smpPtr16 = (int16_t *)s->pek;
-		getMinMax16(&smpPtr16[index], numSamples, &min16, &max16);
+		const int16_t *smpPtr16 = (int16_t *)s->dataPtr;
+		getMinMax16(&smpPtr16[index], length, &min16, &max16);
 		*outMin = SAMPLE_AREA_Y_CENTER - ((min16 * SAMPLE_AREA_HEIGHT) >> 16);
 		*outMax = SAMPLE_AREA_Y_CENTER - ((max16 * SAMPLE_AREA_HEIGHT) >> 16);
 	}
-	else
+	else // 8-bit
 	{
-		// 8-bit sample
-		getMinMax8(&s->pek[index], numSamples, &min8, &max8);
+		getMinMax8(&s->dataPtr[index], length, &min8, &max8);
 		*outMin = SAMPLE_AREA_Y_CENTER - ((min8 * SAMPLE_AREA_HEIGHT) >> 8);
 		*outMax = SAMPLE_AREA_Y_CENTER - ((max8 * SAMPLE_AREA_HEIGHT) >> 8);
 	}
@@ -1242,8 +1223,6 @@
 
 static void writeWaveform(void)
 {
-	int16_t y1, y2, min, max;
-
 	// clear sample data area
 	memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t));
 
@@ -1253,58 +1232,67 @@
 	if (instr[editor.curInstr] == NULL || smpEd_ViewSize == 0)
 		return;
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->pek == NULL || s->len == 0)
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	y1 = SAMPLE_AREA_Y_CENTER - getScaledSample(s, scr2SmpPos(0));
-
-	uint32_t viewSizeSamples = smpEd_ViewSize;
-	if (s->typ & 16)
-		viewSizeSamples >>= 1;
-
-	if (viewSizeSamples <= SAMPLE_AREA_WIDTH)
+	if (smpEd_ViewSize <= SAMPLE_AREA_WIDTH) // zoomed in (or 1:1)
 	{
-		// 1:1 or zoomed in
-		for (int16_t x = 1; x < SAMPLE_AREA_WIDTH; x++)
+		for (int32_t x = 0; x <= SAMPLE_AREA_WIDTH; x++)
 		{
-			y2 = SAMPLE_AREA_Y_CENTER - getScaledSample(s, scr2SmpPos(x));
-			sampleLine(x - 1, x, y1, y2);
-			y1 = y2;
+			int32_t currSmpPos = scr2SmpPos(x+0);
+			int32_t nextSmpPos = scr2SmpPos(x+1);
+
+			if (currSmpPos >= s->length) currSmpPos = s->length-1;
+			if (nextSmpPos >= s->length) nextSmpPos = s->length-1;
+
+			int32_t x1 = smpPos2Scr(currSmpPos);
+			int32_t x2 = smpPos2Scr(nextSmpPos);
+			int32_t y1 = getScaledSample(s, currSmpPos);
+			int32_t y2 = getScaledSample(s, nextSmpPos);
+
+			x1 = CLAMP(x1, 0, SAMPLE_AREA_WIDTH-1);
+			x2 = CLAMP(x2, 0, SAMPLE_AREA_WIDTH-1);
+
+			// kludge: sometimes the last point wouldn't reach the end of the sample window
+			if (x == SAMPLE_AREA_WIDTH)
+				x2 = SAMPLE_AREA_WIDTH-1;
+
+			sampleLine(x1, x2, y1, y2);
 		}
 	}
-	else
+	else // zoomed out
 	{
-		// zoomed out
+		const int32_t firstSamplePoint = getScaledSample(s, scr2SmpPos(0));
 
-		int16_t oldMin = y1;
-		int16_t oldMax = y1;
+		int32_t oldMin = firstSamplePoint;
+		int32_t oldMax = firstSamplePoint;
 
-		int32_t smpNumMin = (s->typ & 16) ? 2 : 1;
 		for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++)
 		{
-			int32_t smpIdx = scr2SmpPos(x);
+			int32_t smpIdx = scr2SmpPos(x+0);
 			int32_t smpNum = scr2SmpPos(x+1) - smpIdx;
 
 			// prevent look-up overflow (yes, this can happen near the end of the sample)
-			if (smpIdx+smpNum > s->len)
-				smpNum = s->len - smpNum;
+			if (smpIdx+smpNum > s->length)
+				smpNum = s->length - smpIdx;
 
-			if (smpNum < smpNumMin)
-				smpNum = smpNumMin;
-
-			getSampleDataPeak(s, smpIdx, smpNum, &min, &max);
-
-			if (x != 0)
+			if (smpNum > 0)
 			{
-				if (min > oldMax) sampleLine(x - 1, x, oldMax, min);
-				if (max < oldMin) sampleLine(x - 1, x, oldMin, max);
-			}
+				int16_t min, max;
+				getSampleDataPeak(s, smpIdx, smpNum, &min, &max);
 
-			sampleLine(x, x, max, min);
+				if (x != 0)
+				{
+					if (min > oldMax) sampleLine(x-1, x, oldMax, min);
+					if (max < oldMin) sampleLine(x-1, x, oldMin, max);
+				}
 
-			oldMin = min;
-			oldMax = max;
+				sampleLine(x, x, max, min);
+
+				oldMin = min;
+				oldMax = max;
+			}
 		}
 	}
 }
@@ -1312,17 +1300,17 @@
 void writeSample(bool forceSmpRedraw)
 {
 	int32_t tmpRx1, tmpRx2;
-	sampleTyp *s;
+	sample_t *s;
 
 	// update sample loop points for visuals
 
 	if (instr[editor.curInstr] == NULL)
-		s = &instr[0]->samp[0];
+		s = &instr[0]->smp[0];
 	else
-		s = &instr[editor.curInstr]->samp[editor.curSmp];
+		s = &instr[editor.curInstr]->smp[editor.curSmp];
 
-	curSmpRepS = s->repS;
-	curSmpRepL = s->repL;
+	curSmpLoopStart = s->loopStart;
+	curSmpLoopLength = s->loopLength;
 
 	// exchange range variables if x1 is after x2
 	if (smpEd_Rx1 > smpEd_Rx2)
@@ -1333,13 +1321,13 @@
 	}
 
 	// clamp range
-	smpEd_Rx1 = CLAMP(smpEd_Rx1, 0, s->len);
-	smpEd_Rx2 = CLAMP(smpEd_Rx2, 0, s->len);
+	smpEd_Rx1 = CLAMP(smpEd_Rx1, 0, s->length);
+	smpEd_Rx2 = CLAMP(smpEd_Rx2, 0, s->length);
 
 	// sanitize sample scroll position
-	if (smpEd_ScrPos+smpEd_ViewSize > s->len)
+	if (smpEd_ScrPos+smpEd_ViewSize > s->length)
 	{
-		smpEd_ScrPos = s->len - smpEd_ViewSize;
+		smpEd_ScrPos = s->length - smpEd_ViewSize;
 		updateScrPos();
 	}
  
@@ -1348,9 +1336,9 @@
 		smpEd_ScrPos = 0;
 		updateScrPos();
 
-		if (smpEd_ViewSize > s->len)
+		if (smpEd_ViewSize > s->length)
 		{
-			smpEd_ViewSize = s->len;
+			smpEd_ViewSize = s->length;
 			updateViewSize();
 		}
 	}
@@ -1400,11 +1388,11 @@
 			old_Rx2 = smpEd_Rx2;
 		}
 
-		fixRepeatGadgets();
+		fixLoopGadgets();
 	}
 
 	if (ui.sampleEditorShown)
-		fixSampleDrag();
+		fixSampleScrollbar();
 
 	updateSampleEditor();
 }
@@ -1418,8 +1406,6 @@
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-
 	if (start < 0)
 		start = 0;
 
@@ -1428,13 +1414,6 @@
 
 	smpEd_Rx1 = scr2SmpPos(start);
 	smpEd_Rx2 = scr2SmpPos(end);
-
-	// 2-byte align if sample is 16-bit
-	if (s->typ & 16)
-	{
-		smpEd_Rx1 &= 0xFFFFFFFE;
-		smpEd_Rx2 &= 0xFFFFFFFE;
-	}
 }
 
 void updateSampleEditorSample(void)
@@ -1448,7 +1427,7 @@
 	if (instr[editor.curInstr] == NULL)
 		smpEd_ViewSize = 0;
 	else
-		smpEd_ViewSize = instr[editor.curInstr]->samp[editor.curSmp].len;
+		smpEd_ViewSize = instr[editor.curInstr]->smp[editor.curSmp].length;
 
 	updateViewSize();
 
@@ -1458,8 +1437,8 @@
 void updateSampleEditor(void)
 {
 	char noteChar1, noteChar2;
-	uint8_t typ;
-	int32_t sampleLen;
+	uint8_t flags;
+	int32_t sampleLength;
 
 	if (!ui.sampleEditorShown)
 		return;
@@ -1466,18 +1445,18 @@
 
 	if (instr[editor.curInstr] == NULL)
 	{
-		typ = 0;
-		sampleLen = 0;
+		flags = 0;
+		sampleLength = 0;
 	}
 	else
 	{
-		typ = instr[editor.curInstr]->samp[editor.curSmp].typ;
-		sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len;
+		flags = instr[editor.curInstr]->smp[editor.curSmp].flags;
+		sampleLength = instr[editor.curInstr]->smp[editor.curSmp].length;
 	}
 
 	// sample bit depth radio buttons
 	uncheckRadioButtonGroup(RB_GROUP_SAMPLE_DEPTH);
-	if (typ & 16)
+	if (flags & SAMPLE_16BIT)
 		radioButtons[RB_SAMPLE_16BIT].state = RADIOBUTTON_CHECKED;
 	else
 		radioButtons[RB_SAMPLE_8BIT].state = RADIOBUTTON_CHECKED;
@@ -1485,17 +1464,15 @@
 
 	// sample loop radio buttons
 	uncheckRadioButtonGroup(RB_GROUP_SAMPLE_LOOP);
-	if (typ & 3)
-	{
-		if (typ & 2)
-			radioButtons[RB_SAMPLE_PINGPONG_LOOP].state = RADIOBUTTON_CHECKED;
-		else
-			radioButtons[RB_SAMPLE_FORWARD_LOOP].state = RADIOBUTTON_CHECKED;
-	}
-	else
-	{
+
+	uint8_t loopType = GET_LOOPTYPE(flags);
+	if (loopType == LOOP_OFF)
 		radioButtons[RB_SAMPLE_NO_LOOP].state = RADIOBUTTON_CHECKED;
-	}
+	else if (loopType == LOOP_FWD)
+		radioButtons[RB_SAMPLE_FORWARD_LOOP].state = RADIOBUTTON_CHECKED;
+	else
+		radioButtons[RB_SAMPLE_PINGPONG_LOOP].state = RADIOBUTTON_CHECKED;
+
 	showRadioButtonGroup(RB_GROUP_SAMPLE_LOOP);
 
 	// draw sample play note
@@ -1521,7 +1498,7 @@
 	// draw sample display/length
 
 	hexOutBg(536, 350, PAL_FORGRND, PAL_DESKTOP, smpEd_ViewSize, 8);
-	hexOutBg(536, 362, PAL_FORGRND, PAL_DESKTOP, sampleLen, 8);
+	hexOutBg(536, 362, PAL_FORGRND, PAL_DESKTOP, sampleLength, 8);
 }
 
 void sampPlayNoteUp(void)
@@ -1549,15 +1526,15 @@
 	if (instr[editor.curInstr] == NULL)
 		sampleLen = 0;
 	else
-		sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len;
+		sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length;
 
 	if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen)
 		return;
 
 	if (mouse.rightButtonPressed)
-		scrollAmount = smpEd_ViewSize / 14; // rounded from 16 (70Hz)
+		scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(16);
 	else
-		scrollAmount = smpEd_ViewSize / 27; // rounded from 32 (70Hz)
+		scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(32);
 
 	if (scrollAmount < 1)
 		scrollAmount = 1;
@@ -1576,15 +1553,15 @@
 	if (instr[editor.curInstr] == NULL)
 		sampleLen = 0;
 	else
-		sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len;
+		sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length;
 
 	if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen)
 		return;
 
 	if (mouse.rightButtonPressed)
-		scrollAmount = smpEd_ViewSize / 14; // was 16 (70Hz->60Hz)
+		scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(16);
 	else
-		scrollAmount = smpEd_ViewSize / 27; // was 32 (70Hz->60Hz)
+		scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(32);
 
 	if (scrollAmount < 1)
 		scrollAmount = 1;
@@ -1603,12 +1580,12 @@
 	if (instr[editor.curInstr] == NULL)
 		sampleLen = 0;
 	else
-		sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len;
+		sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length;
 
 	if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen)
 		return;
 
-	smpEd_ScrPos = (int32_t)pos;
+	smpEd_ScrPos = pos;
 	updateScrPos();
 }
 
@@ -1632,23 +1609,15 @@
 	if (editor.curInstr == 0 || instr[editor.curInstr] == NULL)
 		return;
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (s->pek == NULL)
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (s->dataPtr == NULL)
 		return;
 
 	if (smpEd_Rx1 < smpEd_Rx2)
 	{
 		smpEd_ViewSize = smpEd_Rx2 - smpEd_Rx1;
-
-		if (s->typ & 16)
-		{
-			if (smpEd_ViewSize < 4)
-				smpEd_ViewSize = 4;
-		}
-		else if (smpEd_ViewSize < 2)
-		{
+		if (smpEd_ViewSize < 2)
 			smpEd_ViewSize = 2;
-		}
 
 		updateViewSize();
 
@@ -1665,7 +1634,7 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
@@ -1678,15 +1647,14 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
 
-	int32_t minViewSize = (s->typ & 16) ? 4 : 2;
-	if (old_ViewSize <= minViewSize)
+	if (old_ViewSize <= 2)
 		return;
 
 	if (step < 1)
@@ -1693,8 +1661,8 @@
 		step = 1;
 
 	smpEd_ViewSize = old_ViewSize - (step * 2);
-	if (smpEd_ViewSize < minViewSize)
-		smpEd_ViewSize = minViewSize;
+	if (smpEd_ViewSize < 2)
+		smpEd_ViewSize = 2;
 
 	updateViewSize();
 
@@ -1705,10 +1673,10 @@
 	step += tmp32;
 
 	int64_t newScrPos64 = old_SmpScrPos + step;
-	if (newScrPos64+smpEd_ViewSize > s->len)
-		newScrPos64 = s->len - smpEd_ViewSize;
+	if (newScrPos64+smpEd_ViewSize > s->length)
+		newScrPos64 = s->length - smpEd_ViewSize;
 
-	smpEd_ScrPos = newScrPos64 & 0xFFFFFFFF;
+	smpEd_ScrPos = (uint32_t)newScrPos64;
 	updateScrPos();
 }
 
@@ -1716,13 +1684,13 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (old_ViewSize == s->len)
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (old_ViewSize == s->length)
 		return;
 
 	if (step < 1)
@@ -1729,9 +1697,9 @@
 		step = 1;
 
 	int64_t newViewSize64 = (int64_t)old_ViewSize + (step * 2);
-	if (newViewSize64 > s->len)
+	if (newViewSize64 > s->length)
 	{
-		smpEd_ViewSize = s->len;
+		smpEd_ViewSize = s->length;
 		smpEd_ScrPos = 0;
 	}
 	else
@@ -1748,8 +1716,8 @@
 		if (smpEd_ScrPos < 0)
 			smpEd_ScrPos = 0;
 
-		if ((smpEd_ScrPos + smpEd_ViewSize) > s->len)
-			smpEd_ScrPos = s->len - smpEd_ViewSize;
+		if (smpEd_ScrPos+smpEd_ViewSize > s->length)
+			smpEd_ScrPos = s->length - smpEd_ViewSize;
 	}
 
 	updateViewSize();
@@ -1760,7 +1728,7 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
@@ -1772,7 +1740,7 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
@@ -1784,13 +1752,13 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-	if (old_ViewSize == s->len)
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	if (old_ViewSize == s->length)
 		return;
 
 	int32_t tmp32 = old_ViewSize;
@@ -1804,12 +1772,12 @@
 	smpEd_ViewSize = old_ViewSize * 2;
 	if (smpEd_ViewSize < old_ViewSize)
 	{
-		smpEd_ViewSize = s->len;
+		smpEd_ViewSize = s->length;
 		smpEd_ScrPos = 0;
 	}
-	else if (smpEd_ViewSize+smpEd_ScrPos > s->len)
+	else if (smpEd_ViewSize+smpEd_ScrPos > s->length)
 	{
-		smpEd_ViewSize = s->len - smpEd_ScrPos;
+		smpEd_ViewSize = s->length - smpEd_ScrPos;
 	}
 
 	updateViewSize();
@@ -1820,7 +1788,7 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
@@ -1828,7 +1796,7 @@
 	smpEd_ScrPos = 0;
 	updateScrPos();
 
-	smpEd_ViewSize = instr[editor.curInstr]->samp[editor.curSmp].len;
+	smpEd_ViewSize = instr[editor.curInstr]->smp[editor.curSmp].length;
 	updateViewSize();
 }
 
@@ -1836,12 +1804,12 @@
 {
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
 
-	if (smpEd_Rx1 >= smpEd_Rx2)
+	if (smpEd_Rx1 == smpEd_Rx2)
 	{
 		okBox(0, "System message", "No range specified!");
 		return;
@@ -1879,10 +1847,18 @@
 	UNICHAR *filenameU = cp437ToUnichar(smpEd_SysReqText);
 	if (filenameU == NULL)
 	{
-		okBox(0, "System message", "Error converting string locale!");
+		okBox(0, "System message", "Out of memory!");
 		return;
 	}
 
+	if (fileExistsAnsi(smpEd_SysReqText))
+	{
+		char buf[256];
+		createFileOverwriteText(smpEd_SysReqText, buf);
+		if (okBox(2, "System request", buf) != 1)
+			return;
+	}
+
 	saveSample(filenameU, SAVE_RANGE);
 	free(filenameU);
 }
@@ -1889,23 +1865,23 @@
 
 static bool cutRange(bool cropMode, int32_t r1, int32_t r2)
 {
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
 	if (s == NULL)
 		return false;
 
-	assert(!(s->typ & 16) || (!(r1 & 1) && !(r2 & 1) && !(s->len & 1)));
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 
 	if (!cropMode)
 	{
-		if (editor.curInstr == 0 || s->pek == NULL || s->len == 0)
+		if (editor.curInstr == 0 || s->dataPtr == NULL || s->length == 0)
 			return false;
 
 		pauseAudio();
-		restoreSample(s);
+		unfixSample(s);
 
 		if (config.smpCutToBuffer)
 		{
-			if (!getCopyBuffer(r2 - r1))
+			if (!getCopyBuffer(r2-r1, sample16Bit))
 			{
 				fixSample(s);
 				resumeAudio();
@@ -1914,18 +1890,17 @@
 				return false;
 			}
 
-			memcpy(smpCopyBuff, &s->pek[r1], r2 - r1);
-			smpCopyBits = (s->typ & 16) ? 16 : 8;
+			memcpy(smpCopyBuff, &s->dataPtr[r1], (r2-r1) << sample16Bit);
+			smpCopyBits = sample16Bit ? 16 : 8;
 		}
 	}
 
-	memmove(&s->pek[r1], &s->pek[r2], s->len - r2);
+	memmove(&s->dataPtr[r1 << sample16Bit], &s->dataPtr[r2 << sample16Bit], (s->length-r2) << sample16Bit);
 
-	int32_t len = s->len - r2 + r1;
-	if (len > 0)
+	int32_t length = s->length - r2+r1;
+	if (length > 0)
 	{
-		int8_t *newPtr = (int8_t *)realloc(s->origPek, len + LOOP_FIX_LEN);
-		if (newPtr == NULL)
+		if (!reallocateSmpData(s, length, sample16Bit))
 		{
 			freeSample(editor.curInstr, editor.curSmp);
 			editor.updateCurSmp = true;
@@ -1937,46 +1912,36 @@
 			return false;
 		}
 
-		s->origPek = newPtr;
-		s->pek = s->origPek + SMP_DAT_OFFSET;
+		s->length = length;
 
-		s->len = len;
-
-		int32_t repE = s->repS + s->repL;
-		if (s->repS > r1)
+		int32_t loopEnd = s->loopStart + s->loopLength;
+		if (s->loopStart > r1)
 		{
-			s->repS -= r2 - r1;
-			if (s->repS < r1)
-				s->repS = r1;
+			s->loopStart -= r2-r1;
+			if (s->loopStart < r1)
+				s->loopStart = r1;
 		}
 
-		if (repE > r1)
+		if (loopEnd > r1)
 		{
-			repE -= r2 - r1;
-			if (repE < r1)
-				repE = r1;
+			loopEnd -= r2-r1;
+			if (loopEnd < r1)
+				loopEnd = r1;
 		}
 
-		s->repL = repE - s->repS;
-		if (s->repL < 0)
-			s->repL = 0;
+		s->loopLength = loopEnd - s->loopStart;
+		if (s->loopLength < 0)
+			s->loopLength = 0;
 
-		if (s->repS+s->repL > len)
-			s->repL = len - s->repS;
+		if (s->loopStart+s->loopLength > length)
+			s->loopLength = length - s->loopStart;
 
-		// 2-byte align loop points if sample is 16-bit
-		if (s->typ & 16)
+		if (s->loopLength <= 0)
 		{
-			s->repL &= 0xFFFFFFFE;
-			s->repS &= 0xFFFFFFFE;
+			s->loopStart = 0;
+			DISABLE_LOOP(s->flags);
 		}
 
-		if (s->repL == 0)
-		{
-			s->repS = 0;
-			s->typ &= ~3; // disable loop
-		}
-
 		if (!cropMode)
 			fixSample(s);
 	}
@@ -2014,8 +1979,8 @@
 
 void sampCut(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1)
 		return;
 
 	mouseAnimOn();
@@ -2031,22 +1996,30 @@
 
 static int32_t SDLCALL sampCopyThread(void *ptr)
 {
-	sampleTyp *s = getCurSample();
-	assert(s != NULL && (!(s->typ & 16) || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1))));
+	sample_t *s = getCurSample();
 
-	if (!getCopyBuffer(smpEd_Rx2 - smpEd_Rx1))
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+
+	if (!getCopyBuffer(smpEd_Rx2- smpEd_Rx1, sample16Bit))
 	{
 		okBoxThreadSafe(0, "System message", "Not enough memory!");
 		return true;
 	}
 
-	restoreSample(s);
-	memcpy(smpCopyBuff, &s->pek[smpEd_Rx1], smpEd_Rx2 - smpEd_Rx1);
+	unfixSample(s);
+	memcpy(smpCopyBuff, &s->dataPtr[smpEd_Rx1 << sample16Bit], (smpEd_Rx2-smpEd_Rx1) << sample16Bit);
 	fixSample(s);
 
-	smpCopyBits = (s->typ & 16) ? 16 : 8;
 	setMouseBusy(false);
 
+	// copy sample information (in case we paste over an empty sample)
+	if (smpEd_Rx1 == 0 && smpEd_Rx2 == s->length)
+	{
+		smpCopySample = *s;
+		smpCopyDidCopyWholeSample = true;
+	}
+
+	smpCopyBits = sample16Bit? 16 : 8;
 	return true;
 
 	(void)ptr;
@@ -2054,8 +2027,8 @@
 
 void sampCopy(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->origPek == NULL || s->len <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1)
 		return;
 
 	mouseAnimOn();
@@ -2069,10 +2042,11 @@
 	SDL_DetachThread(thread);
 }
 
-static void pasteOverwrite(sampleTyp *s)
+static void pasteOverwrite(sample_t *s)
 {
-	int8_t *p = (int8_t *)malloc(smpCopySize + LOOP_FIX_LEN);
-	if (p == NULL)
+	bool sample16Bit = (smpCopyBits == 16);
+
+	if (!reallocateSmpData(s, smpCopySize, sample16Bit))
 	{
 		okBoxThreadSafe(0, "System message", "Not enough memory!");
 		return;
@@ -2080,21 +2054,36 @@
 
 	pauseAudio();
 
-	if (s->origPek != NULL)
-		free(s->origPek);
+	memcpy(s->dataPtr, smpCopyBuff, smpCopySize << sample16Bit);
 
-	memset(s, 0, sizeof (sampleTyp));
+	if (smpCopyDidCopyWholeSample)
+	{
+		sample_t *src = &smpCopySample;
+		memcpy(s->name, src->name, 23);
+		s->length = src->length;
+		s->loopStart = src->loopStart;
+		s->loopLength = src->loopLength;
+		s->volume = src->volume;
+		s->panning = src->panning;
+		s->finetune = src->finetune;
+		s->relativeNote = src->relativeNote;
+		s->flags = src->flags;
+	}
+	else
+	{
+		s->name[0] = '\0';
+		s->length = smpCopySize;
+		s->loopStart = 0;
+		s->loopLength = 0;
+		s->volume = 64;
+		s->panning = 128;
+		s->finetune = 0;
+		s->relativeNote = 0;
+		s->flags = (smpCopyBits == 16) ? SAMPLE_16BIT : 0;
+	}
 
-	s->origPek = p;
-	s->pek = p + SMP_DAT_OFFSET;
+	s->isFixed = false;
 
-	memcpy(s->pek, smpCopyBuff, smpCopySize);
-
-	s->len = smpCopySize;
-	s->vol = 64;
-	s->pan = 128;
-	s->typ = (smpCopyBits == 16) ? 16 : 0;
-
 	fixSample(s);
 	resumeAudio();
 
@@ -2103,40 +2092,34 @@
 	setMouseBusy(false);
 }
 
-static void pasteCopiedData(int8_t *pek, int32_t offset, int32_t length, bool smpIs16Bit)
+static void pasteCopiedData(int8_t *dataPtr, int32_t offset, int32_t length, bool sample16Bit)
 {
-	if (smpIs16Bit)
+	if (sample16Bit) // destination sample is 16-bits
 	{
-		// destination sample = 16-bit
-
 		if (smpCopyBits == 16)
 		{
-			// src/dst = equal bits, copy directly
-			memcpy(&pek[offset], smpCopyBuff, length);
+			// src/dst bits are equal, do direct copy
+			memcpy(&dataPtr[offset<<1], smpCopyBuff, length * sizeof (int16_t));
 		}
 		else
 		{
-			// convert copy data to 16-bit then paste
-			int16_t *ptr16 = (int16_t *)&pek[offset];
-			int32_t len32 = length >> 1;
-
-			for (int32_t i = 0; i < len32; i++)
+			// convert copied data to 16-bit then paste
+			int16_t *ptr16 = (int16_t *)dataPtr + offset;
+			for (int32_t i = 0; i < length; i++)
 				ptr16[i] = smpCopyBuff[i] << 8;
 		}
 	}
-	else
+	else // destination sample is 8-bits
 	{
-		// destination sample = 8-bit
-
 		if (smpCopyBits == 8)
 		{
-			// src/dst = equal bits, copy directly
-			memcpy(&pek[offset], smpCopyBuff, length);
+			// src/dst bits are equal, do direct copy
+			memcpy(&dataPtr[offset], smpCopyBuff, length * sizeof (int8_t));
 		}
 		else
 		{
-			// convert copy data to 8-bit then paste
-			int8_t *ptr8 = (int8_t *)&pek[offset];
+			// convert copied data to 8-bit then paste
+			int8_t *ptr8 = (int8_t *)&dataPtr[offset];
 			int16_t *ptr16 = (int16_t *)smpCopyBuff;
 
 			for (int32_t i = 0; i < length; i++)
@@ -2147,6 +2130,8 @@
 
 static int32_t SDLCALL sampPasteThread(void *ptr)
 {
+	smpPtr_t sp;
+
 	if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr))
 	{
 		okBoxThreadSafe(0, "System message", "Not enough memory!");
@@ -2153,40 +2138,22 @@
 		return true;
 	}
 
-	sampleTyp *s = getCurSample();
-	if (smpEd_Rx2 == 0 || s == NULL || s->pek == NULL)
+	sample_t *s = getCurSample();
+	if (smpEd_Rx2 == 0 || s == NULL || s->dataPtr == NULL)
 	{
 		pasteOverwrite(s);
 		return true;
 	}
 
-	bool smpIs16Bit = (s->typ >> 4) & 1;
-	assert(!smpIs16Bit || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1) && !(s->len & 1)));
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 
-	if (s->len+smpCopySize > MAX_SAMPLE_LEN)
+	if (s->length+smpCopySize > MAX_SAMPLE_LEN)
 	{
 		okBoxThreadSafe(0, "System message", "Not enough room in sample!");
 		return true;
 	}
 
-	int32_t realCopyLen = smpCopySize;
-
-	if (smpIs16Bit)
-	{
-		// destination sample is 16-bit
-
-		if (smpCopyBits == 8) // copy buffer is 8-bit, multiply length by 2
-			realCopyLen <<= 1;
-	}
-	else
-	{
-		// destination sample is 8-bit
-
-		if (smpCopyBits == 16) // copy buffer is 16-bit, divide length by 2
-			realCopyLen >>= 1;
-	}
-
-	int32_t newLength = s->len + realCopyLen - (smpEd_Rx2 - smpEd_Rx1);
+	int32_t newLength = s->length + smpCopySize - (smpEd_Rx2 - smpEd_Rx1);
 	if (newLength <= 0)
 		return true;
 
@@ -2196,65 +2163,54 @@
 		return true;
 	}
 
-	int8_t *p = (int8_t *)malloc(newLength + LOOP_FIX_LEN);
-	if (p == NULL)
+	if (!allocateSmpDataPtr(&sp, newLength, sample16Bit))
 	{
 		okBoxThreadSafe(0, "System message", "Not enough memory!");
 		return true;
 	}
 
-	int8_t *newPek = p + SMP_DAT_OFFSET;
-
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
 	// paste left part of original sample
 	if (smpEd_Rx1 > 0)
-		memcpy(newPek, s->pek, smpEd_Rx1);
+		memcpy(sp.ptr, s->dataPtr, smpEd_Rx1 << sample16Bit);
 
 	// paste copied data
-	pasteCopiedData(newPek, smpEd_Rx1, realCopyLen, smpIs16Bit);
+	pasteCopiedData(sp.ptr, smpEd_Rx1, smpCopySize, sample16Bit);
 
 	// paste right part of original sample
-	if (smpEd_Rx2 < s->len)
-		memmove(&newPek[smpEd_Rx1+realCopyLen], &s->pek[smpEd_Rx2], s->len - smpEd_Rx2);
+	if (smpEd_Rx2 < s->length)
+		memmove(&sp.ptr[(smpEd_Rx1+smpCopySize) << sample16Bit], &s->dataPtr[smpEd_Rx2 << sample16Bit], (s->length-smpEd_Rx2) << sample16Bit);
 
-	free(s->origPek);
+	freeSmpData(s);
+	setSmpDataPtr(s, &sp);
 
 	// adjust loop points if necessary
-	if (smpEd_Rx2-smpEd_Rx1 != realCopyLen)
+	if (smpEd_Rx2-smpEd_Rx1 != smpCopySize)
 	{
-		int32_t loopAdjust = realCopyLen - (smpEd_Rx1 - smpEd_Rx2);
+		int32_t loopAdjust = smpCopySize - (smpEd_Rx1 - smpEd_Rx2);
 
-		if (s->repS > smpEd_Rx2)
+		if (s->loopStart > smpEd_Rx2)
 		{
-			s->repS += loopAdjust;
-			s->repL -= loopAdjust;
+			s->loopStart += loopAdjust;
+			s->loopLength -= loopAdjust;
 		}
 
-		if (s->repS+s->repL > smpEd_Rx2)
-			s->repL += loopAdjust;
+		if (s->loopStart+s->loopLength > smpEd_Rx2)
+			s->loopLength += loopAdjust;
 
-		if (s->repS > newLength)
+		if (s->loopStart > newLength)
 		{
-			s->repS = 0;
-			s->repL = 0;
+			s->loopStart = 0;
+			s->loopLength = 0;
 		}
 
-		if (s->repS+s->repL > newLength)
-			s->repL = newLength - s->repS;
-
-		// align loop points if sample is 16-bit
-		if (smpIs16Bit)
-		{
-			s->repL &= 0xFFFFFFFE;
-			s->repS &= 0xFFFFFFFE;
-		}
+		if (s->loopStart+s->loopLength > newLength)
+			s->loopLength = newLength - s->loopStart;
 	}
 
-	s->len = newLength;
-	s->origPek = p;
-	s->pek = s->origPek + SMP_DAT_OFFSET;
+	s->length = newLength;
 
 	fixSample(s);
 	resumeAudio();
@@ -2263,15 +2219,8 @@
 	setMouseBusy(false);
 
 	// set new range
-	smpEd_Rx2 = smpEd_Rx1 + realCopyLen;
+	smpEd_Rx2 = smpEd_Rx1 + smpCopySize;
 
-	// align sample marking points if sample is 16-bit
-	if (smpIs16Bit)
-	{
-		smpEd_Rx1 &= 0xFFFFFFFE;
-		smpEd_Rx2 &= 0xFFFFFFFE;
-	}
-
 	writeSampleFlag = true;
 	return true;
 
@@ -2285,8 +2234,8 @@
 
 	if (smpEd_Rx2 == 0) // no sample data marked, overwrite sample with copy buffer
 	{
-		sampleTyp *s = getCurSample();
-		if (s != NULL && s->pek != NULL)
+		sample_t *s = getCurSample();
+		if (s != NULL && s->dataPtr != NULL && s->length > 0)
 		{
 			if (okBox(2, "System request", "The current sample is not empty. Do you really want to overwrite it?") != 1)
 				return;
@@ -2306,16 +2255,15 @@
 
 static int32_t SDLCALL sampCropThread(void *ptr)
 {
-	sampleTyp *s = getCurSample();
-	assert(!(s->typ & 16) || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1) && !(s->len & 1)));
+	sample_t *s = getCurSample();
 
 	int32_t r1 = smpEd_Rx1;
 	int32_t r2 = smpEd_Rx2;
 
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
-	if (!cutRange(true, 0, r1) || !cutRange(true, r2 - r1, s->len))
+	if (!cutRange(true, 0, r1) || !cutRange(true, r2-r1, s->length))
 	{
 		fixSample(s);
 		resumeAudio();
@@ -2326,18 +2274,15 @@
 	resumeAudio();
 
 	r1 = 0;
-	r2 = s->len;
+	r2 = s->length;
 
-	if (s->typ & 16)
-		r2 &= 0xFFFFFFFE;
-
 	setSongModifiedFlag();
 	setMouseBusy(false);
 
 	smpEd_Rx1 = r1;
 	smpEd_Rx2 = r2;
-	writeSampleFlag = true;
 
+	writeSampleFlag = true;
 	return true;
 
 	(void)ptr;
@@ -2345,12 +2290,12 @@
 
 void sampCrop(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0 || smpEd_Rx1 >= smpEd_Rx2)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx1 == smpEd_Rx2)
 		return;
 
-	if (smpEd_Rx1 == 0 && smpEd_Rx2 >= s->len)
-		return; // no need to crop (the whole sample is marked)
+	if (smpEd_Rx1 == 0 && smpEd_Rx2 == s->length)
+		return; // nothing to crop (the whole sample is marked)
 
 	mouseAnimOn();
 	thread = SDL_CreateThread(sampCropThread, NULL, NULL);
@@ -2365,20 +2310,14 @@
 
 void sampXFade(void)
 {
-	int16_t c, d;
-	int32_t tmp32, i, y1, y2, a, b, d1, d2, d3, dist;
-	double dR, dS1, dS2, dS3, dS4;
+	int32_t y1, y2, d1, d2, d3;
 
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	assert(!(s->typ & 16) || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1) && !(s->len & 1)));
-
-	uint8_t t = s->typ;
-
 	// check if the sample has the loop flag enabled
-	if ((t & 3) == 0)
+	if (GET_LOOPTYPE(s->flags) == LOOP_OFF)
 	{
 		okBox(0, "System message", "X-Fade can only be used on a loop-enabled sample!");
 		return;
@@ -2401,20 +2340,16 @@
 	int32_t x1 = smpEd_Rx1;
 	int32_t x2 = smpEd_Rx2;
 
-	bool is16Bit = (t & 16) ? true : false;
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 
-	if ((t & 3) >= 2)
+	if (GET_LOOPTYPE(s->flags) == LOOP_BIDI)
 	{
-		// pingpong loop
-
-		y1 = s->repS;
-		if (x1 <= y1)
+		y1 = s->loopStart;
+		if (x1 <= y1) // first loop point
 		{
-			// first loop point
-
-			if (x2 <= y1 || x2 >= s->repS+s->repL)
+			if (x2 <= y1 || x2 >= s->loopStart+s->loopLength)
 			{
-				okBox(0, "System message", "Invalid range!");
+				okBox(0, "System message", "Error: No loop point found inside marked data.");
 				return;
 			}
 
@@ -2425,61 +2360,59 @@
 			d2 = y1 - x1;
 			d3 = x2 - y1;
 
-			if (d1 < 2 || d2 < 2 || d3 < 2)
+			if (d1 < 1 || d2 < 1 || d3 < 1)
 			{
-				okBox(0, "System message", "Invalid range!");
+				okBox(0, "System message", "Invalid range! Try to mark more data.");
 				return;
 			}
 
-			if (y1-d1 < 0 || y1+d1 >= s->len)
+			if (y1-d1 < 0 || y1+d1 >= s->length)
 			{
 				okBox(0, "System message", "Not enough sample data outside loop!");
 				return;
 			}
 
-			if (is16Bit)
-			{
-				y1 >>= 1;
-				d1 >>= 1;
-				d2 >>= 1;
-				d3 >>= 1;
-			}
+			const double dD2Mul = 1.0 / d2;
+			const double dD3Mul = 1.0 / d3;
 
 			pauseAudio();
-			restoreSample(s);
+			unfixSample(s);
 
-			i = 0;
-			while (i < d1)
+			for (int32_t i = 0; i < d1; i++)
 			{
-				a = getSampleValue(s->pek, t, (y1 - i - 1) << is16Bit);
-				b = getSampleValue(s->pek, t, (y1 + i) << is16Bit);
+				const int32_t aIdx = y1-i-1;
+				const int32_t bIdx = y1+i;
+				const double dI = i;
 
-				dS1 = 1.0 - i / (double)d2; dS2 = 2.0 - dS1;
-				dS3 = 1.0 - i / (double)d3; dS4 = 2.0 - dS3;
+				const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit);
+				const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit);
 
-				tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2));
-				c = (int16_t)tmp32;
+				if (i < d2)
+				{
+					const double dS1 = 1.0 - (dI * dD2Mul);
+					const double dS2 = 2.0 - dS1;
+					double dSample = (dA * dS2 + dB * dS1) / (dS1 + dS2);
+					putSampleValue(s->dataPtr, aIdx, dSample, sample16Bit);
+				}
 
-				tmp32 = (int32_t)round((b * dS4 + a * dS3) / (dS3 + dS4));
-				d = (int16_t)tmp32;
-
-				if (i < d2) putSampleValue(s->pek, t, (y1 - i - 1) << is16Bit, c);
-				if (i < d3) putSampleValue(s->pek, t, (y1 + i) << is16Bit, d);
-
-				i++;
+				if (i < d3)
+				{
+					const double dS1 = 1.0 - (dI * dD3Mul);
+					const double dS2 = 2.0 - dS1;
+					double dSample = (dB * dS2 + dA * dS1) / (dS1 + dS2);
+					putSampleValue(s->dataPtr, bIdx, dSample, sample16Bit);
+				}
 			}
 
 			fixSample(s);
 			resumeAudio();
 		}
-		else
+		else // last loop point
 		{
-			// last loop point
-
-			y1 += s->repL;
-			if (x1 >= y1 || x2 <= y1 || x2 >= s->len)
+			y1 += s->loopLength;
+			if (x1 >= y1 || x2 <= y1 || x2 >= s->length)
 			{
-				okBox(0, "System message", "Invalid range!");
+				okBox(0, "System message", "Error: No loop point found inside marked data.");
 				return;
 			}
 
@@ -2490,48 +2423,48 @@
 			d2 = y1 - x1;
 			d3 = x2 - y1;
 
-			if (d1 < 2 || d2 < 2 || d3 < 2)
+			if (d1 < 1 || d2 < 1 || d3 < 1)
 			{
-				okBox(0, "System message", "Invalid range!");
+				okBox(0, "System message", "Invalid range! Try to mark more data.");
 				return;
 			}
 
-			if (y1-d1 < 0 || y1+d1 >= s->len)
+			if (y1-d1 < 0 || y1+d1 >= s->length)
 			{
 				okBox(0, "System message", "Not enough sample data outside loop!");
 				return;
 			}
 
-			if (is16Bit)
-			{
-				y1 >>= 1;
-				d1 >>= 1;
-				d2 >>= 1;
-				d3 >>= 1;
-			}
+			const double dD2Mul = 1.0 / d2;
+			const double dD3Mul = 1.0 / d3;
 
 			pauseAudio();
-			restoreSample(s);
+			unfixSample(s);
 
-			i = 0;
-			while (i < d1)
+			for (int32_t i = 0; i < d1; i++)
 			{
-				a = getSampleValue(s->pek, t, (y1 - i - 1) << is16Bit);
-				b = getSampleValue(s->pek, t, (y1 + i) << is16Bit);
+				const int32_t aIdx = y1-i-1;
+				const int32_t bIdx = y1+i;
+				const double dI = i;
 
-				dS1 = 1.0 - i / (double)d2; dS2 = 2.0 - dS1;
-				dS3 = 1.0 - i / (double)d3; dS4 = 2.0 - dS3;
+				const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit);
+				const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit);
 
-				tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2));
-				c = (int16_t)tmp32;
+				if (i < d2)
+				{
+					const double dS1 = 1.0 - (dI * dD2Mul);
+					const double dS2 = 2.0 - dS1;
+					double dSample = (dA * dS2 + dB * dS1) / (dS1 + dS2);
+					putSampleValue(s->dataPtr, aIdx, dSample, sample16Bit);
+				}
 
-				tmp32 = (int32_t)round((b * dS4 + a * dS3) / (dS3 + dS4));
-				d = (int16_t)tmp32;
-
-				if (i < d2) putSampleValue(s->pek, t, (y1 - i - 1) << is16Bit, c);
-				if (i < d3) putSampleValue(s->pek, t, (y1 + i) << is16Bit, d);
-
-				i++;
+				if (i < d3)
+				{
+					const double dS1 = 1.0 - (dI * dD3Mul);
+					const double dS2 = 2.0 - dS1;
+					double dSample = (dB * dS2 + dA * dS1) / (dS1 + dS2);
+					putSampleValue(s->dataPtr, bIdx, dSample, sample16Bit);
+				}
 			}
 
 			fixSample(s);
@@ -2538,90 +2471,82 @@
 			resumeAudio();
 		}
 	}
-	else
+	else // forward loop
 	{
-		// standard loop
-
-		if (x1 > s->repS)
+		if (x1 > s->loopStart)
 		{
-			x1 -= s->repL;
-			x2 -= s->repL;
+			x1 -= s->loopLength;
+			x2 -= s->loopLength;
 		}
 
-		if (x1 < 0 || x2 <= x1 || x2 >= s->len)
+		if (x1 < 0 || x2 <= x1 || x2 >= s->length)
 		{
 			okBox(0, "System message", "Invalid range!");
 			return;
 		}
 
-		i = (x2 - x1 + 1) >> 1;
-		y1 = s->repS - i;
-		y2 = s->repS + s->repL - i;
+		const int32_t length = x2 - x1;
 
-		if (t & 16)
-		{
-			y1 &= 0xFFFFFFFE;
-			y2 &= 0xFFFFFFFE;
-		}
+		int32_t x = (length + 1) >> 1;
+		y1 = s->loopStart - x;
+		y2 = s->loopStart+s->loopLength - x;
 
-		if (y1 < 0 || y2+(x2-x1) >= s->len)
+		if (y1 < 0 || y2+length >= s->length)
 		{
 			okBox(0, "System message", "Not enough sample data outside loop!");
 			return;
 		}
 
-		d1 = x2 - x1;
-		d2 = s->repS - y1;
-		d3 = x2 - x1 - d2;
+		d1 = length;
+		d2 = s->loopStart - y1;
+		d3 = length - d2;
 
-		if (y1+(x2-x1) <= s->repS || d1 == 0 || d3 == 0 || d1 > s->repL)
+		if (y1+length <= s->loopStart || d1 == 0 || d3 == 0 || d1 > s->loopLength)
 		{
 			okBox(0, "System message", "Invalid range!");
 			return;
 		}
 
-		dR = (s->repS - i) / (double)(x2 - x1);
-		dist = is16Bit ? 2 : 1;
+		const double dR = (s->loopStart - x) / (double)length;
+		const double dD1 = d1;
+		const double dD1Mul = 1.0 / d1;
+		const double dD2Mul = 1.0 / d2;
+		const double dD3Mul = 1.0 / d3;
 
 		pauseAudio();
-		restoreSample(s);
+		unfixSample(s);
 
-		i = 0;
-		while (i < x2-x1)
+		for (int32_t i = 0; i < length; i++)
 		{
-			a = getSampleValue(s->pek, t, y1 + i);
-			b = getSampleValue(s->pek, t, y2 + i);
+			const int32_t aIdx = y1+i;
+			const int32_t bIdx = y2+i;
+			const double dI = i;
 
-			dS2 = i / (double)d1;
-			dS1 = 1.0 - dS2;
+			const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit);
+			const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit);
+			const double dS2 = dI * dD1Mul;
+			const double dS1 = 1.0 - dS2;
 
-			if (y1+i < s->repS)
+			double dC, dD;
+			if (y1+i < s->loopStart)
 			{
-				dS3 = 1.0 - (1.0 - dR) * i / d2;
-				dS4 = dR * i / d2;
-
-				tmp32 = (int32_t)round((a * dS3 + b * dS4) / (dS3 + dS4));
-				c = (int16_t)tmp32;
-
-				tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2));
-				d = (int16_t)tmp32;
+				const double dS3 = 1.0 - (1.0 - dR) * dI * dD2Mul;
+				const double dS4 = dR * dI * dD2Mul;
+				
+				dC = (dA * dS3 + dB * dS4) / (dS3 + dS4);
+				dD = (dA * dS2 + dB * dS1) / (dS1 + dS2);
 			}
 			else
 			{
-				dS3 = 1.0 - (1.0 - dR) * (d1 - i) / d3;
-				dS4 = dR * (d1 - i) / d3;
+				const double dS3 = 1.0 - (1.0 - dR) * (dD1 - dI) * dD3Mul;
+				const double dS4 = dR * (dD1 - dI) * dD3Mul;
 
-				tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2));
-				c = (int16_t)tmp32;
-
-				tmp32 = (int32_t)round((a * dS4 + b * dS3) / (dS3 + dS4));
-				d = (int16_t)tmp32;
+				dC = (dA * dS2 + dB * dS1) / (dS1 + dS2);
+				dD = (dA * dS4 + dB * dS3) / (dS3 + dS4);
 			}
 
-			putSampleValue(s->pek, t, y1 + i, c);
-			putSampleValue(s->pek, t, y2 + i, d);
-
-			i += dist;
+			putSampleValue(s->dataPtr, aIdx, dC, sample16Bit);
+			putSampleValue(s->dataPtr, bIdx, dD, sample16Bit);
 		}
 
 		fixSample(s);
@@ -2634,14 +2559,14 @@
 
 void rbSampleNoLoop(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	lockMixerCallback();
-	restoreSample(s);
+	unfixSample(s);
 
-	s->typ &= ~3;
+	DISABLE_LOOP(s->flags);
 
 	fixSample(s);
 	unlockMixerCallback();
@@ -2653,18 +2578,20 @@
 
 void rbSampleForwardLoop(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	lockMixerCallback();
-	restoreSample(s);
+	unfixSample(s);
 
-	s->typ = (s->typ & ~3) | 1;
-	if (s->repL+s->repS == 0)
+	DISABLE_LOOP(s->flags);
+	s->flags |= LOOP_FWD;
+
+	if (s->loopStart+s->loopLength == 0)
 	{
-		s->repS = 0;
-		s->repL = s->len;
+		s->loopStart = 0;
+		s->loopLength = s->length;
 	}
 
 	fixSample(s);
@@ -2677,18 +2604,20 @@
 
 void rbSamplePingpongLoop(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	lockMixerCallback();
-	restoreSample(s);
+	unfixSample(s);
 
-	s->typ = (s->typ & ~3) | 2;
-	if (s->repL+s->repS == 0)
+	DISABLE_LOOP(s->flags);
+	s->flags |= LOOP_BIDI;
+
+	if (s->loopStart+s->loopLength == 0)
 	{
-		s->repS = 0;
-		s->repL = s->len;
+		s->loopStart = 0;
+		s->loopLength = s->length;
 	}
 
 	fixSample(s);
@@ -2701,38 +2630,27 @@
 
 static int32_t SDLCALL convSmp8Bit(void *ptr)
 {
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
+	assert(s->dataPtr != NULL);
 
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
-	const int16_t *src16 = (const int16_t *)s->pek;
-	int32_t newLen = s->len >> 1;
+	const int16_t *src16 = (const int16_t *)s->dataPtr;
+	for (int32_t i = 0; i < s->length; i++)
+		s->dataPtr[i] = src16[i] >> 8;
 
-	for (int32_t i = 0; i < newLen; i++)
-		s->pek[i] = src16[i] >> 8;
+	reallocateSmpData(s, s->length, false);
 
-	assert(s->origPek != NULL);
+	s->flags &= ~SAMPLE_16BIT; // remove 16-bit flag
 
-	int8_t *newPtr = (int8_t *)realloc(s->origPek, newLen + LOOP_FIX_LEN);
-	if (newPtr != NULL)
-	{
-		s->origPek = newPtr;
-		s->pek = s->origPek + SMP_DAT_OFFSET;
-	}
-
-	s->repL >>= 1;
-	s->repS >>= 1;
-	s->len >>= 1;
-	s->typ &= ~16; // remove 16-bit flag
-
 	fixSample(s);
 	resumeAudio();
 
-	editor.updateCurSmp = true;
 	setSongModifiedFlag();
 	setMouseBusy(false);
 
+	editor.updateCurSmp = true;
 	return true;
 
 	(void)ptr;
@@ -2740,8 +2658,8 @@
 
 void rbSample8bit(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	if (okBox(2, "System request", "Convert sampledata?") == 1)
@@ -2760,9 +2678,11 @@
 	else
 	{
 		lockMixerCallback();
-		restoreSample(s);
+		unfixSample(s);
 
-		s->typ &= ~16; // remove 16-bit flag
+		s->flags &= ~SAMPLE_16BIT; // remove 16-bit flag
+		s->length <<= 1;
+		// no need to call reallocateSmpData, number of bytes allocated is the same
 
 		fixSample(s);
 		unlockMixerCallback();
@@ -2775,41 +2695,30 @@
 
 static int32_t SDLCALL convSmp16Bit(void *ptr)
 {
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
 
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
-	assert(s->origPek != NULL);
-
-	int8_t *newPtr = (int8_t *)realloc(s->origPek, (s->len * 2) + LOOP_FIX_LEN);
-	if (newPtr == NULL)
+	if (!reallocateSmpData(s, s->length, true))
 	{
 		okBoxThreadSafe(0, "System message", "Not enough memory!");
 		return true;
 	}
-	else
-	{
-		s->origPek = newPtr;
-		s->pek = s->origPek + SMP_DAT_OFFSET;
-	}
 
-	int16_t *dst16 = (int16_t *)s->pek;
-	for (int32_t i = s->len-1; i >= 0; i--)
-		dst16[i] = s->pek[i] << 8;
+	int16_t *dst16 = (int16_t *)s->dataPtr;
+	for (int32_t i = s->length-1; i >= 0; i--)
+		dst16[i] = s->dataPtr[i] << 8;
 
-	s->len <<= 1;
-	s->repL <<= 1;
-	s->repS <<= 1;
-	s->typ |= 16; // add 16-bit flag
+	s->flags |= SAMPLE_16BIT;
 
 	fixSample(s);
 	resumeAudio();
 
-	editor.updateCurSmp = true;
 	setSongModifiedFlag();
 	setMouseBusy(false);
 
+	editor.updateCurSmp = true;
 	return true;
 
 	(void)ptr;
@@ -2817,8 +2726,8 @@
 
 void rbSample16bit(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	if (okBox(2, "System request", "Convert sampledata?") == 1)
@@ -2837,15 +2746,12 @@
 	else
 	{
 		lockMixerCallback();
-		restoreSample(s);
+		unfixSample(s);
 
-		s->typ |= 16; // add 16-bit flag
+		s->flags |= SAMPLE_16BIT;
+		s->length >>= 1;
+		// no need to call reallocateSmpData, number of bytes allocated is the same
 
-		// make sure stuff is 2-byte aligned for 16-bit mode
-		s->repS &= 0xFFFFFFFE;
-		s->repL &= 0xFFFFFFFE;
-		s->len &= 0xFFFFFFFE;
-
 		fixSample(s);
 		unlockMixerCallback();
 
@@ -2857,8 +2763,8 @@
 
 void clearSample(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	if (okBox(1, "System request", "Clear sample?") != 1)
@@ -2869,125 +2775,112 @@
 	setSongModifiedFlag();
 }
 
-void sampMin(void)
+void sampMinimize(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	if (okBox(1, "System request", "Minimize sample?") != 1)
+	const bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF;
+	if (!hasLoop)
+	{
+		okBox(0, "System message", "Only a looped sample can be minimized!");
 		return;
+	}
 
-	const bool hasLoop = s->typ & 3;
-	if (hasLoop && s->len > s->repS+s->repL && s->repL < s->len)
+	if (s->loopStart+s->loopLength >= s->length)
 	{
-		lockMixerCallback();
+		okBox(0, "System message", "The sample can't be minimized any further.");
+		return;
+	}
 
-		s->len = s->repS + s->repL;
+	if (okBox(1, "System request", "Minimize sample?") != 1)
+		return;
+	
+	lockMixerCallback();
 
-		int8_t *newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
-		if (newPtr != NULL)
-		{
-			s->origPek = newPtr;
-			s->pek = s->origPek + SMP_DAT_OFFSET;
-		}
+	s->length = s->loopStart + s->loopLength;
 
-		// Note: we don't need to make a call to fixSample()
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+	reallocateSmpData(s, s->length, sample16Bit);
+	// note: we don't need to make a call to fixSample()
 
-		unlockMixerCallback();
+	unlockMixerCallback();
 
-		updateSampleEditorSample();
-		updateSampleEditor();
-		setSongModifiedFlag();
-	}
+	updateSampleEditorSample();
+	updateSampleEditor();
+	setSongModifiedFlag();
 }
 
 void sampRepeatUp(void)
 {
-	int32_t addVal, lenSub;
-
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	if (s->typ & 16)
-	{
-		lenSub = 4;
-		addVal = 2;
-	}
-	else
-	{
-		lenSub = 2;
-		addVal = 1;
-	}
+	int32_t loopStart = curSmpLoopStart;
+	int32_t loopLength = curSmpLoopLength;
 
-	int32_t repS = curSmpRepS;
-	int32_t repL = curSmpRepL;
+	if (loopStart < s->length-2)
+		loopStart++;
 
-	if (repS < s->len-lenSub)
-		repS += addVal;
+	if (loopStart+loopLength > s->length)
+		loopLength = s->length - loopStart;
 
-	if (repS+repL > s->len)
-		repL = s->len - repS;
+	curSmpLoopStart = loopStart;
+	curSmpLoopLength = loopLength;
 
-	curSmpRepS = (s->typ & 16) ? (int32_t)(repS & 0xFFFFFFFE) : repS;
-	curSmpRepL = (s->typ & 16) ? (int32_t)(repL & 0xFFFFFFFE) : repL;
-
-	fixRepeatGadgets();
+	fixLoopGadgets();
 	updateLoopsOnMouseUp = true;
 }
 
 void sampRepeatDown(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	int32_t repS = (s->typ & 16) ? curSmpRepS-2 : curSmpRepS-1;
-	if (repS < 0)
-		repS = 0;
+	int32_t loopStart = curSmpLoopStart - 1;
+	if (loopStart < 0)
+		loopStart = 0;
 
-	curSmpRepS = (s->typ & 16) ? (int32_t)(repS & 0xFFFFFFFE) : repS;
+	curSmpLoopStart = loopStart;
 
-	fixRepeatGadgets();
+	fixLoopGadgets();
 	updateLoopsOnMouseUp = true;
 }
 
 void sampReplenUp(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	int32_t repL = (s->typ & 16) ? curSmpRepL+2 : curSmpRepL+1;
-	if (curSmpRepS+repL > s->len)
-		repL = s->len - curSmpRepS;
+	int32_t loopLength = curSmpLoopLength + 1;
+	if (curSmpLoopStart+loopLength > s->length)
+		loopLength = s->length - curSmpLoopStart;
 
-	curSmpRepL = (s->typ & 16) ? (int32_t)(repL & 0xFFFFFFFE) : repL;
+	curSmpLoopLength = loopLength;
 
-	fixRepeatGadgets();
+	fixLoopGadgets();
 	updateLoopsOnMouseUp = true;
 }
 
 void sampReplenDown(void)
 {
-	int32_t repL;
+	int32_t loopLength;
 
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	if (s->typ & 16)
-		repL = curSmpRepL - 2;
-	else
-		repL = curSmpRepL - 1;
+	loopLength = curSmpLoopLength - 1;
+	if (loopLength < 0)
+		loopLength = 0;
 
-	if (repL < 0)
-		repL = 0;
+	curSmpLoopLength = loopLength;
 
-	curSmpRepL = (s->typ & 16) ? (int32_t)(repL & 0xFFFFFFFE) : repL;
-
-	fixRepeatGadgets();
+	fixLoopGadgets();
 	updateLoopsOnMouseUp = true;
 }
 
@@ -3104,7 +2997,7 @@
 
 	showScrollBar(SB_SAMP_SCROLL);
 
-	// clear two lines that are never written to when the sampler is open
+	// clear two lines in the sample data view that are never written to when the sampler is open
 	hLine(0, 173, SAMPLE_AREA_WIDTH, PAL_BCKGRND);
 	hLine(0, 328, SAMPLE_AREA_WIDTH, PAL_BCKGRND);
 
@@ -3127,7 +3020,7 @@
 	}
 }
 
-static void writeSmpXORLine(int32_t x)
+static void invertSamplePosLine(int32_t x)
 {
 	if (x < 0 || x >= SCREEN_W)
 		return;
@@ -3141,10 +3034,10 @@
 {
 	uint8_t ins, smp;
 
-	assert(editor.curSmpChannel < MAX_VOICES);
+	assert(editor.curSmpChannel < MAX_CHANNELS);
 	lastChInstr_t *c = &lastChInstr[editor.curSmpChannel];
 
-	if (c->instrNr == 130) // "Play Wave/Range/Display" in Smp. Ed.
+	if (c->instrNum == 130) // "Play Wave/Range/Display" in Smp. Ed.
 	{
 		ins = editor.curPlayInstr;
 		smp = editor.curPlaySmp;
@@ -3151,8 +3044,8 @@
 	}
 	else
 	{
-		ins = c->instrNr;
-		smp = c->sampleNr;
+		ins = c->instrNum;
+		smp = c->smpNum;
 	}
 
 	if (editor.curInstr == ins && editor.curSmp == smp)
@@ -3166,8 +3059,8 @@
 			{
 				if (scrPos != smpEd_OldSmpPosLine)
 				{
-					writeSmpXORLine(smpEd_OldSmpPosLine); // remove old line
-					writeSmpXORLine(scrPos); // write new line
+					invertSamplePosLine(smpEd_OldSmpPosLine); // remove old line
+					invertSamplePosLine(scrPos); // write new line
 				}
 
 				smpEd_OldSmpPosLine = scrPos;
@@ -3177,7 +3070,7 @@
 	}
 
 	if (smpEd_OldSmpPosLine != -1)
-		writeSmpXORLine(smpEd_OldSmpPosLine);
+		invertSamplePosLine(smpEd_OldSmpPosLine);
 
 	smpEd_OldSmpPosLine = -1;
 }
@@ -3204,61 +3097,52 @@
 
 static void setLeftLoopPinPos(int32_t x)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	int32_t newPos = scr2SmpPos(x) - curSmpRepS;
-	int32_t repS = curSmpRepS + newPos;
-	int32_t repL = curSmpRepL - newPos;
+	int32_t newPos = scr2SmpPos(x) - curSmpLoopStart;
+	int32_t loopStart = curSmpLoopStart + newPos;
+	int32_t loopLength = curSmpLoopLength - newPos;
 
-	if (repS < 0)
+	if (loopStart < 0)
 	{
-		repL += repS;
-		repS = 0;
+		loopLength += loopStart;
+		loopStart = 0;
 	}
 
-	if (repL < 0)
+	if (loopLength < 0)
 	{
-		repL = 0;
-		repS = curSmpRepS + curSmpRepL;
+		loopLength = 0;
+		loopStart = curSmpLoopStart + curSmpLoopLength;
 	}
 
-	if (s->typ & 16)
-	{
-		repS &= 0xFFFFFFFE;
-		repL &= 0xFFFFFFFE;
-	}
+	curSmpLoopStart = loopStart;
+	curSmpLoopLength = loopLength;
 
-	curSmpRepS = repS;
-	curSmpRepL = repL;
-
-	fixRepeatGadgets();
+	fixLoopGadgets();
 	updateLoopsOnMouseUp = true;
 }
 
 static void setRightLoopPinPos(int32_t x)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
-	int32_t repL = scr2SmpPos(x) - curSmpRepS;
-	if (repL < 0)
-		repL = 0;
+	int32_t loopLength = scr2SmpPos(x) - curSmpLoopStart;
+	if (loopLength < 0)
+		loopLength = 0;
 
-	if (repL+curSmpRepS > s->len)
-		repL = s->len - curSmpRepS;
+	if (loopLength+curSmpLoopStart > s->length)
+		loopLength = s->length - curSmpLoopStart;
 
-	if (repL < 0)
-		repL = 0;
+	if (loopLength < 0)
+		loopLength = 0;
 
-	if (s->typ & 16)
-		repL &= 0xFFFFFFFE;
+	curSmpLoopLength = loopLength;
 
-	curSmpRepL = repL;
-
-	fixRepeatGadgets();
+	fixLoopGadgets();
 	updateLoopsOnMouseUp = true;
 }
 
@@ -3266,8 +3150,8 @@
 {
 	my -= 174; // 0..SAMPLE_AREA_HEIGHT-1
 
-	const double dTmp = round(my * (256.0 / SAMPLE_AREA_HEIGHT));
-	const int32_t tmp32 = (const int32_t)dTmp;
+	const double dTmp = my * (256.0 / SAMPLE_AREA_HEIGHT);
+	const int32_t tmp32 = (const int32_t)(dTmp + 0.5); // rounded
 
 	return 255 - CLAMP(tmp32, 0, 255);
 }
@@ -3278,8 +3162,8 @@
 	int16_t *ptr16;
 	int32_t tmp32, p, vl, tvl, r, rl, rvl, start, end;
 
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	int32_t mx = mouse.x;
@@ -3291,12 +3175,10 @@
 	if (!mouseButtonHeld)
 	{
 		pauseAudio();
-		restoreSample(s);
+		unfixSample(s);
 		editor.editSampleFlag = true;
 
 		lastDrawX = scr2SmpPos(mx);
-		if (s->typ & 16)
-			lastDrawX >>= 1;
 
 		lastDrawY = mouseYToSampleY(my);
 
@@ -3309,15 +3191,9 @@
 	}
 
 	if (mx != lastMouseX)
-	{
 		p = scr2SmpPos(mx);
-		if (s->typ & 16)
-			p >>= 1;
-	}
 	else
-	{
 		p = lastDrawX;
-	}
 
 	if (!keyb.leftShiftPressed && my != lastMouseY)
 		vl = mouseYToSampleY(my);
@@ -3344,21 +3220,17 @@
 		vl = tmp32;
 	}
 
-	if (s->typ & 16)
+	if (s->flags & SAMPLE_16BIT)
 	{
-		// 16-bit
+		ptr16 = (int16_t *)s->dataPtr;
 
-		ptr16 = (int16_t *)s->pek;
-
 		start = p;
-		end = lastDrawX+1;
-
 		if (start < 0)
 			start = 0;
 
-		tmp32 = s->len >> 1;
-		if (end > tmp32)
-			end = tmp32;
+		end = lastDrawX+1;
+		if (end > s->length)
+			end = s->length;
 
 		if (p == lastDrawX)
 		{
@@ -3390,19 +3262,17 @@
 			}
 		}
 	}
-	else
+	else // 8-bit
 	{
-		// 8-bit
+		ptr8 = s->dataPtr;
 
-		ptr8 = s->pek;
-
 		start = p;
 		if (start < 0)
 			start = 0;
 
 		end = lastDrawX+1;
-		if (end > s->len)
-			end = s->len;
+		if (end > s->length)
+			end = s->length;
 
 		if (p == lastDrawX)
 		{
@@ -3641,23 +3511,26 @@
 {
 	int8_t tmp8, *ptrStart, *ptrEnd;
 	int16_t tmp16, *ptrStart16, *ptrEnd16;
-	sampleTyp *s = getCurSample();
 
-	if (s->typ & 16)
+	const bool sampleDataMarked = (smpEd_Rx1 != smpEd_Rx2);
+	sample_t *s = getCurSample();
+	const bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+
+	if (sample16Bit)
 	{
-		if (smpEd_Rx1 >= smpEd_Rx2)
+		if (!sampleDataMarked)
 		{
-			ptrStart16 = (int16_t *)s->pek;
-			ptrEnd16 = (int16_t *)&s->pek[s->len-2];
+			ptrStart16 = (int16_t *)s->dataPtr;
+			ptrEnd16 = (int16_t *)s->dataPtr + (s->length-1);
 		}
 		else
 		{
-			ptrStart16 = (int16_t *)&s->pek[smpEd_Rx1];
-			ptrEnd16 = (int16_t *)&s->pek[smpEd_Rx2-2];
+			ptrStart16 = (int16_t *)s->dataPtr + smpEd_Rx1;
+			ptrEnd16 = (int16_t *)s->dataPtr + (smpEd_Rx2-1);
 		}
 
 		pauseAudio();
-		restoreSample(s);
+		unfixSample(s);
 
 		while (ptrStart16 < ptrEnd16)
 		{
@@ -3671,19 +3544,19 @@
 	}
 	else
 	{
-		if (smpEd_Rx1 >= smpEd_Rx2)
+		if (!sampleDataMarked)
 		{
-			ptrStart = s->pek;
-			ptrEnd = &s->pek[s->len-1];
+			ptrStart = s->dataPtr;
+			ptrEnd = &s->dataPtr[s->length-1];
 		}
 		else
 		{
-			ptrStart = &s->pek[smpEd_Rx1];
-			ptrEnd = &s->pek[smpEd_Rx2-1];
+			ptrStart = &s->dataPtr[smpEd_Rx1];
+			ptrEnd = &s->dataPtr[smpEd_Rx2-1];
 		}
 
 		pauseAudio();
-		restoreSample(s);
+		unfixSample(s);
 
 		while (ptrStart < ptrEnd)
 		{
@@ -3698,8 +3571,8 @@
 
 	setSongModifiedFlag();
 	setMouseBusy(false);
-	writeSampleFlag = true;
 
+	writeSampleFlag = true;
 	return true;
 
 	(void)ptr;
@@ -3707,8 +3580,8 @@
 
 void sampleBackwards(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len < 2)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length < 2)
 		return;
 
 	mouseAnimOn();
@@ -3722,30 +3595,23 @@
 	SDL_DetachThread(thread);
 }
 
-static int32_t SDLCALL sampleConvThread(void *ptr)
+static int32_t SDLCALL sampleChangeSignThread(void *ptr)
 {
-	int8_t *ptr8;
-	int16_t *ptr16;
-	int32_t i, len;
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
 
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
-	if (s->typ & 16)
+	if (s->flags & SAMPLE_16BIT)
 	{
-		len = s->len / 2;
-		ptr16 = (int16_t *)s->pek;
-
-		for (i = 0; i < len; i++)
+		int16_t *ptr16 = (int16_t *)s->dataPtr;
+		for (int32_t i = 0; i < s->length; i++)
 			ptr16[i] ^= 0x8000;
 	}
 	else
 	{
-		len = s->len;
-		ptr8 = s->pek;
-
-		for (i = 0; i < len; i++)
+		int8_t *ptr8 = s->dataPtr;
+		for (int32_t i = 0; i < s->length; i++)
 			ptr8[i] ^= 0x80;
 	}
 
@@ -3754,21 +3620,21 @@
 
 	setSongModifiedFlag();
 	setMouseBusy(false);
-	writeSampleFlag = true;
 
+	writeSampleFlag = true;
 	return true;
 
 	(void)ptr;
 }
 
-void sampleConv(void)
+void sampleChangeSign(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	mouseAnimOn();
-	thread = SDL_CreateThread(sampleConvThread, NULL, NULL);
+	thread = SDL_CreateThread(sampleChangeSignThread, NULL, NULL);
 	if (thread == NULL)
 	{
 		okBox(0, "System message", "Couldn't create thread!");
@@ -3778,17 +3644,19 @@
 	SDL_DetachThread(thread);
 }
 
-static int32_t SDLCALL sampleConvWThread(void *ptr)
+static int32_t SDLCALL sampleByteSwapThread(void *ptr)
 {
-	sampleTyp *s = getCurSample();
+	sample_t *s = getCurSample();
 
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
-	int32_t len = s->len / 2;
-	int8_t *ptr8 = s->pek;
+	int32_t length = s->length;
+	if (!(s->flags & SAMPLE_16BIT))
+		length >>= 1;
 
-	for (int32_t i = 0; i < len; i++, ptr8 += 2)
+	int8_t *ptr8 = s->dataPtr;
+	for (int32_t i = 0; i < length; i++, ptr8 += 2)
 	{
 		const int8_t tmp = ptr8[0];
 		ptr8[0] = ptr8[1];
@@ -3800,21 +3668,27 @@
 
 	setSongModifiedFlag();
 	setMouseBusy(false);
-	writeSampleFlag = true;
 
+	writeSampleFlag = true;
 	return true;
 
 	(void)ptr;
 }
 
-void sampleConvW(void)
+void sampleByteSwap(void)
 {
-	sampleTyp *s = getCurSample();
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
+	if (!(s->flags & SAMPLE_16BIT))
+	{
+		if (okBox(2, "System request", "Byte swapping only makes sense on a 16-bit sample. Continue?") != 1)
+			return;
+	}
+
 	mouseAnimOn();
-	thread = SDL_CreateThread(sampleConvWThread, NULL, NULL);
+	thread = SDL_CreateThread(sampleByteSwapThread, NULL, NULL);
 	if (thread == NULL)
 	{
 		okBox(0, "System message", "Couldn't create thread!");
@@ -3828,30 +3702,25 @@
 {
 	int8_t *ptr8;
 	int16_t *ptr16;
-	int32_t i, len, smpSub, smp32;
-	sampleTyp *s = getCurSample();
+	int32_t length;
 
-	int64_t averageDC = 0;
+	const bool sampleDataMarked = (smpEd_Rx1 != smpEd_Rx2);
+	sample_t *s = getCurSample();
 
-	if (s->typ & 16)
+	if (s->flags & SAMPLE_16BIT)
 	{
-		if (smpEd_Rx1 >= smpEd_Rx2)
+		if (!sampleDataMarked)
 		{
-			assert(!(s->len & 1));
-
-			ptr16 = (int16_t *)s->pek;
-			len = s->len >> 1;
+			ptr16 = (int16_t *)s->dataPtr;
+			length = s->length;
 		}
 		else
 		{
-			assert(!(smpEd_Rx1 & 1));
-			assert(!(smpEd_Rx2 & 1));
-
-			ptr16 = (int16_t *)&s->pek[smpEd_Rx1];
-			len = (smpEd_Rx2 - smpEd_Rx1) >> 1;
+			ptr16 = (int16_t *)&s->dataPtr + smpEd_Rx1;
+			length = smpEd_Rx2 - smpEd_Rx1;
 		}
 
-		if (len < 0 || len > s->len>>1)
+		if (length < 0 || length > s->length)
 		{
 			setMouseBusy(false);
 			return true;
@@ -3858,16 +3727,17 @@
 		}
 
 		pauseAudio();
-		restoreSample(s);
+		unfixSample(s);
 
-		for (i = 0; i < len; i++)
+		int64_t	averageDC = 0;
+		for (int32_t i = 0; i < length; i++)
 			averageDC += ptr16[i];
-		averageDC /= len;
+		averageDC = (averageDC + (length>>1)) / length; // rounded
 
-		smpSub = (int32_t)averageDC;
-		for (i = 0; i < len; i++)
+		const int32_t smpSub = (int32_t)averageDC;
+		for (int32_t i = 0; i < length; i++)
 		{
-			smp32 = ptr16[i] - smpSub;
+			int32_t smp32 = ptr16[i] - smpSub;
 			CLAMP16(smp32);
 			ptr16[i] = (int16_t)smp32;
 		}
@@ -3875,20 +3745,20 @@
 		fixSample(s);
 		resumeAudio();
 	}
-	else
+	else // 8-bit
 	{
-		if (smpEd_Rx1 >= smpEd_Rx2)
+		if (!sampleDataMarked)
 		{
-			ptr8 = s->pek;
-			len = s->len;
+			ptr8 = s->dataPtr;
+			length = s->length;
 		}
 		else
 		{
-			ptr8 = &s->pek[smpEd_Rx1];
-			len = smpEd_Rx2 - smpEd_Rx1;
+			ptr8 = &s->dataPtr[smpEd_Rx1];
+			length = smpEd_Rx2 - smpEd_Rx1;
 		}
 
-		if (len < 0 || len > s->len)
+		if (length < 0 || length > s->length)
 		{
 			setMouseBusy(false);
 			return true;
@@ -3895,16 +3765,17 @@
 		}
 
 		pauseAudio();
-		restoreSample(s);
+		unfixSample(s);
 
-		for (i = 0; i < len; i++)
+		int64_t	averageDC = 0;
+		for (int32_t i = 0; i < length; i++)
 			averageDC += ptr8[i];
-		averageDC /= len;
+		averageDC = (averageDC + (length>>1)) / length; // rounded
 
-		smpSub = (int32_t)averageDC;
-		for (i = 0; i < len; i++)
+		const int32_t smpSub = (int32_t)averageDC;
+		for (int32_t i = 0; i < length; i++)
 		{
-			smp32 = ptr8[i] - smpSub;
+			int32_t smp32 = ptr8[i] - smpSub;
 			CLAMP8(smp32);
 			ptr8[i] = (int8_t)smp32;
 		}
@@ -3913,10 +3784,10 @@
 		resumeAudio();
 	}
 
-	writeSampleFlag = true;
 	setSongModifiedFlag();
 	setMouseBusy(false);
 
+	writeSampleFlag = true;
 	return true;
 
 	(void)ptr;
@@ -3924,9 +3795,8 @@
 
 void fixDC(void)
 {
-	sampleTyp *s = getCurSample();
-
-	if (s == NULL || s->pek == NULL || s->len <= 0)
+	sample_t *s = getCurSample();
+	if (s == NULL || s->dataPtr == NULL || s->length <= 0)
 		return;
 
 	mouseAnimOn();
@@ -3953,23 +3823,20 @@
 	{
 		updateLoopsOnMouseUp = false;
 
-		sampleTyp *s = getCurSample();
+		sample_t *s = getCurSample();
 		if (s == NULL)
 			return;
 
-		if (s->repS != curSmpRepS || s->repL != curSmpRepL)
+		if (s->loopStart != curSmpLoopStart || s->loopLength != curSmpLoopLength)
 		{
 			lockMixerCallback();
-			restoreSample(s);
-
-			setSongModifiedFlag();
-
-			s->repS = curSmpRepS;
-			s->repL = curSmpRepL;
-
+			unfixSample(s);
+			s->loopStart = curSmpLoopStart;
+			s->loopLength = curSmpLoopLength;
 			fixSample(s);
 			unlockMixerCallback();
 
+			setSongModifiedFlag();
 			writeSample(true);
 		}
 	}
--- a/src/ft2_sample_ed.h
+++ b/src/ft2_sample_ed.h
@@ -1,7 +1,7 @@
 #pragma once
 
 #include <stdint.h>
-#include "ft2_replayer.h"
+#include "ft2_header.h"
 
 #define SAMPLE_AREA_HEIGHT 154
 #define SAMPLE_AREA_WIDTH 632
@@ -8,18 +8,25 @@
 #define SAMPLE_AREA_Y_CENTER 250
 
 // allocs sample with proper alignment and padding for branchless resampling interpolation
-bool allocateTmpSmpData(sampleTyp *s, int32_t length);
+bool allocateSmpData(sample_t *s, int32_t length, bool sample16Bit);
+bool allocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit);
 
 // reallocs sample with proper alignment and padding for branchless resampling interpolation
-bool reallocateTmpSmpData(sampleTyp *s, int32_t length);
+bool reallocateSmpData(sample_t *s, int32_t length, bool sample16Bit);
+bool reallocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit);
 
-sampleTyp *getCurSample(void);
-void checkSampleRepeat(sampleTyp *s);
-void fixSample(sampleTyp *s); // modifies samples before index 0, and after loop/end (for branchless mixer interpolation)
-void restoreSample(sampleTyp *s); // restores samples after loop/end
+void setSmpDataPtr(sample_t *s, smpPtr_t *sp);
+void freeSmpDataPtr(smpPtr_t *sp);
+void freeSmpData(sample_t *s);
+
+bool cloneSample(sample_t *src, sample_t *dst);
+sample_t *getCurSample(void);
+void sanitizeSample(sample_t *s);
+void fixSample(sample_t *s); // modifies samples before index 0, and after loop/end (for branchless mixer interpolation)
+void unfixSample(sample_t *s); // restores samples after loop/end
 void clearSample(void);
 void clearCopyBuffer(void);
-int32_t getSampleMiddleCRate(sampleTyp *s);
+int32_t getSampleMiddleCRate(sample_t *s);
 int32_t getSampleRangeStart(void);
 int32_t getSampleRangeEnd(void);
 int32_t getSampleRangeLength(void);
@@ -50,13 +57,13 @@
 void rbSamplePingpongLoop(void);
 void rbSample8bit(void);
 void rbSample16bit(void);
-void sampMin(void);
+void sampMinimize(void);
 void sampRepeatUp(void);
 void sampRepeatDown(void);
 void sampReplenUp(void);
 void sampReplenDown(void);
-int16_t getSampleValue(int8_t *ptr, uint8_t typ, int32_t pos);
-void putSampleValue(int8_t *ptr, uint8_t typ, int32_t pos, int16_t val);
+double getSampleValue(int8_t *smpData, int32_t position, bool sample16Bit);
+void putSampleValue(int8_t *smpData, int32_t position, double dSample, bool sample16Bit);
 void writeSample(bool forceSmpRedraw);
 void handleSampleDataMouseDown(bool mouseButtonHeld);
 void updateSampleEditorSample(void);
@@ -72,10 +79,12 @@
 void drawSampleEditorExt(void);
 void handleSampleEditorExtRedrawing(void);
 void sampleBackwards(void);
-void sampleConv(void);
-void sampleConvW(void);
+void sampleChangeSign(void);
+void sampleByteSwap(void);
 void fixDC(void);
 void smpEdStop(void);
 void testSmpEdMouseUp(void);
+
+void sampleLine(int32_t x1, int32_t x2, int32_t y1, int32_t y2);
 
 extern int32_t smpEd_Rx1, smpEd_Rx2;
--- a/src/ft2_sample_ed_features.c
+++ b/src/ft2_sample_ed_features.c
@@ -30,8 +30,9 @@
 
 static int8_t smpEd_RelReSmp, mix_Balance = 50;
 static bool echo_AddMemory, exitFlag, outOfMemory;
-static int16_t vol_StartVol = 100, vol_EndVol = 100, echo_nEcho = 1, echo_VolChange = 30;
+static int16_t echo_nEcho = 1, echo_VolChange = 30;
 static int32_t echo_Distance = 0x100;
+static double dVol_StartVol = 100.0, dVol_EndVol = 100.0;
 static SDL_Thread *thread;
 
 static void pbExit(void)
@@ -81,22 +82,22 @@
 
 static int32_t SDLCALL resampleThread(void *ptr)
 {
+	smpPtr_t sp;
+
 	if (instr[editor.curInstr] == NULL)
 		return true;
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 
-	const uint32_t mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF;
-	const double dRatio = exp2(smpEd_RelReSmp / 12.0);
+	const double dRatio = exp2((int32_t)smpEd_RelReSmp / 12.0);
 
-	double dNewLen = s->len * dRatio;
+	double dNewLen = s->length * dRatio;
 	if (dNewLen > (double)MAX_SAMPLE_LEN)
 		dNewLen = (double)MAX_SAMPLE_LEN;
 
-	const uint32_t newLen = (int32_t)dNewLen & mask;
-
-	int8_t *p2 = (int8_t *)malloc(newLen + LOOP_FIX_LEN);
-	if (p2 == NULL)
+	const uint32_t newLen = (int32_t)floor(dNewLen);
+	if (!allocateSmpDataPtr(&sp, newLen, sample16Bit))
 	{
 		outOfMemory = true;
 		setMouseBusy(false);
@@ -104,8 +105,8 @@
 		return true;
 	}
 
-	int8_t *newPtr = p2 + SMP_DAT_OFFSET;
-	int8_t *p1 = s->pek;
+	int8_t *dst = sp.ptr;
+	int8_t *src = s->dataPtr;
 
 	// 32.32 fixed-point logic
 	const uint64_t delta64 = (const uint64_t)round((UINT32_MAX+1.0) / dRatio);
@@ -112,7 +113,7 @@
 	uint64_t posFrac64 = 0;
 
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
 	/* Nearest-neighbor resampling (no interpolation).
 	**
@@ -123,58 +124,42 @@
 
 	if (newLen > 0)
 	{
-		if (s->typ & 16)
+		if (sample16Bit)
 		{
-			const int16_t *src16 = (const int16_t *)p1;
-			int16_t *dst16 = (int16_t *)newPtr;
+			const int16_t *src16 = (const int16_t *)src;
+			int16_t *dst16 = (int16_t *)dst;
 
-			const uint32_t resampleLen = newLen >> 1;
-			for (uint32_t i = 0; i < resampleLen; i++)
+			for (uint32_t i = 0; i < newLen; i++)
 			{
-				dst16[i] = src16[posFrac64 >> 32];
+				const uint32_t position = posFrac64 >> 32;
+				dst16[i] = src16[position];
 				posFrac64 += delta64;
 			}
 		}
-		else
+		else // 8-bit
 		{
-			const int8_t *src8 = p1;
-			int8_t *dst8 = newPtr;
+			const int8_t *src8 = src;
+			int8_t *dst8 = dst;
 
 			for (uint32_t i = 0; i < newLen; i++)
 			{
-				dst8[i] = src8[posFrac64 >> 32];
+				const uint32_t position = posFrac64 >> 32;
+				dst8[i] = src8[position];
 				posFrac64 += delta64;
 			}
 		}
 	}
 
-	free(s->origPek);
+	freeSmpData(s);
+	setSmpDataPtr(s, &sp);
 
-	s->relTon = CLAMP(s->relTon + smpEd_RelReSmp, -48, 71);
-	s->len = newLen & mask;
-	s->origPek = p2;
-	s->pek = s->origPek + SMP_DAT_OFFSET;
-	s->repS = (int32_t)(s->repS * dRatio);
-	s->repL = (int32_t)(s->repL * dRatio);
-	s->repS &= mask;
-	s->repL &= mask;
+	s->relativeNote += smpEd_RelReSmp;
+	s->length = newLen;
+	s->loopStart = (int32_t)(s->loopStart * dRatio);
+	s->loopLength = (int32_t)(s->loopLength * dRatio);
 
-	if (s->repS >= s->len)
-		s->repS = s->len-1;
+	sanitizeSample(s);
 
-	if (s->repS+s->repL > s->len)
-		s->repL = s->len - s->repS;
-
-	if (s->typ & 16)
-	{
-		s->len &= 0xFFFFFFFE;
-		s->repS &= 0xFFFFFFFE;
-		s->repL &= 0xFFFFFFFE;
-	}
-
-	if (s->repL <= 0)
-		s->typ &= ~3; // disable loop
-
 	fixSample(s);
 	resumeAudio();
 
@@ -223,18 +208,17 @@
 	vLine(x + w - 3, y + 2,     h - 4, PAL_BUTTON1);
 	hLine(x + 2,     y + h - 3, w - 4, PAL_BUTTON1);
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
 
-	uint32_t mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF;
 	double dLenMul = exp2(smpEd_RelReSmp * (1.0 / 12.0));
 
-	double dNewLen = s->len * dLenMul;
+	double dNewLen = s->length * dLenMul;
 	if (dNewLen > (double)MAX_SAMPLE_LEN)
 		dNewLen = (double)MAX_SAMPLE_LEN;
 
 	textOutShadow(215, 236, PAL_FORGRND, PAL_BUTTON2, "Rel. h.tones");
 	textOutShadow(215, 250, PAL_FORGRND, PAL_BUTTON2, "New sample size");
-	hexOut(361, 250, PAL_FORGRND, (uint32_t)dNewLen & mask, 8);
+	hexOut(361, 250, PAL_FORGRND, (int32_t)dNewLen, 8);
 
 	     if (smpEd_RelReSmp == 0) sign = ' ';
 	else if (smpEd_RelReSmp  < 0) sign = '-';
@@ -326,7 +310,7 @@
 
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
@@ -425,6 +409,8 @@
 
 static int32_t SDLCALL createEchoThread(void *ptr)
 {
+	smpPtr_t sp;
+
 	if (echo_nEcho < 1)
 	{
 		ui.sysReqShown = false;
@@ -431,17 +417,16 @@
 		return true;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
 
-	int32_t readLen = s->len;
-	int8_t *readPtr = s->pek;
-	bool is16Bit = (s->typ & 16) ? true : false;
+	int32_t readLen = s->length;
+	int8_t *readPtr = s->dataPtr;
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
 	int32_t distance = echo_Distance * 16;
-
 	double dVolChange = echo_VolChange / 100.0;
 
 	// calculate real number of echoes
-	double dSmp = is16Bit ? 32768.0 : 128.0;
+	double dSmp = sample16Bit ? 32768.0 : 128.0;
 	int32_t k = 0;
 	while (k++ < echo_nEcho && dSmp >= 1.0)
 		dSmp *= dVolChange;
@@ -458,21 +443,15 @@
 	if (echo_AddMemory)
 	{
 		int64_t tmp64 = (int64_t)distance * (nEchoes - 1);
-		if (is16Bit)
-			tmp64 <<= 1;
 
 		tmp64 += writeLen;
 		if (tmp64 > MAX_SAMPLE_LEN)
 			tmp64 = MAX_SAMPLE_LEN;
 
-		writeLen = (int32_t)tmp64;
-
-		if (is16Bit)
-			writeLen &= 0xFFFFFFFE;
+		writeLen = (uint32_t)tmp64;
 	}
 
-	int8_t *writePtr = (int8_t *)malloc(writeLen + LOOP_FIX_LEN);
-	if (writePtr == NULL)
+	if (!allocateSmpDataPtr(&sp, writeLen, sample16Bit))
 	{
 		outOfMemory = true;
 		setMouseBusy(false);
@@ -481,18 +460,15 @@
 	}
 
 	pauseAudio();
-	restoreSample(s);
+	unfixSample(s);
 
 	int32_t writeIdx = 0;
 
-	if (is16Bit)
+	if (sample16Bit)
 	{
 		const int16_t *readPtr16 = (const int16_t *)readPtr;
-		int16_t *writePtr16 = (int16_t *)&writePtr[SMP_DAT_OFFSET];
+		int16_t *writePtr16 = (int16_t *)sp.ptr;
 
-		writeLen >>= 1;
-		readLen >>= 1;
-
 		while (writeIdx < writeLen)
 		{
 			double dSmpOut = 0.0;
@@ -513,20 +489,16 @@
 					break;
 			}
 
-			// rounding (faster than calling round())
-			     if (dSmpOut < 0.0) dSmpOut -= 0.5;
-			else if (dSmpOut > 0.0) dSmpOut += 0.5;
+			DROUND(dSmpOut);
 
-			int32_t smpOut = (int32_t)dSmpOut;
-			CLAMP16(smpOut);
-			writePtr16[writeIdx++] = (int16_t)smpOut;
+			int32_t smp32 = (int32_t)dSmpOut;
+			CLAMP16(smp32);
+			writePtr16[writeIdx++] = (int16_t)smp32;
 		}
-
-		writeLen <<= 1;
 	}
-	else
+	else // 8-bit
 	{
-		int8_t *writePtr8 = writePtr + SMP_DAT_OFFSET;
+		int8_t *writePtr8 = sp.ptr;
 		while (writeIdx < writeLen)
 		{
 			double dSmpOut = 0.0;
@@ -547,51 +519,26 @@
 					break;
 			}
 
-			// rounding (faster than calling round())
-			     if (dSmpOut < 0.0) dSmpOut -= 0.5;
-			else if (dSmpOut > 0.0) dSmpOut += 0.5;
+			DROUND(dSmpOut);
 
-			int32_t smpOut = (int32_t)dSmpOut;
-			     if (smpOut < -128) smpOut = -128;
-			else if (smpOut >  127) smpOut =  127;
-			writePtr8[writeIdx++] = (int8_t)smpOut;
+			int32_t smp32 = (int32_t)dSmpOut;
+			CLAMP8(smp32);
+			writePtr8[writeIdx++] = (int8_t)smp32;
 		}
 	}
 
-	free(s->origPek);
+	freeSmpData(s);
+	setSmpDataPtr(s, &sp);
 
-	if (stopThread)
+	if (stopThread) // we stopped before echo was done, realloc length
 	{
 		writeLen = writeIdx;
-
-		int8_t *newPtr = (int8_t *)realloc(writePtr, writeIdx + LOOP_FIX_LEN);
-		if (newPtr != NULL)
-		{
-			s->origPek = newPtr;
-			s->pek = s->origPek + SMP_DAT_OFFSET;
-		}
-		else
-		{
-			if (writePtr != NULL)
-				free(writePtr);
-
-			s->origPek = s->pek = NULL;
-			writeLen = 0;
-		}
-
+		reallocateSmpData(s, writeLen, sample16Bit);
 		editor.updateCurSmp = true;
 	}
-	else
-	{
-		s->origPek = writePtr;
-		s->pek = s->origPek + SMP_DAT_OFFSET;
-	}
 
-	if (is16Bit)
-		writeLen &= 0xFFFFFFFE;
+	s->length = writeLen;
 
-	s->len = writeLen;
-
 	fixSample(s);
 	resumeAudio();
 
@@ -651,7 +598,7 @@
 	charOut(315 + (3 * 7), 226, PAL_FORGRND, '0' + (echo_nEcho % 10));
 
 	assert(echo_Distance <= 0x4000);
-	hexOut(308, 240, PAL_FORGRND, (uint32_t)echo_Distance << 4, 5);
+	hexOut(308, 240, PAL_FORGRND, echo_Distance << 4, 5);
 
 	assert(echo_VolChange <= 100);
 	textOutFixed(312, 254, PAL_FORGRND, PAL_BUTTONS, dec3StrTab[echo_VolChange]);
@@ -816,11 +763,9 @@
 
 void pbSampleEcho(void)
 {
-	uint16_t i;
-
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
@@ -845,15 +790,15 @@
 		setScrollBarPos(1, echo_Distance, false);
 		setScrollBarPos(2, echo_VolChange, false);
 		drawCheckBox(0);
-		for (i = 0; i < 8; i++) drawPushButton(i);
-		for (i = 0; i < 3; i++) drawScrollBar(i);
+		for (uint16_t i = 0; i < 8; i++) drawPushButton(i);
+		for (uint16_t i = 0; i < 3; i++) drawScrollBar(i);
 
 		flipFrame();
 	}
 
 	hideCheckBox(0);
-	for (i = 0; i < 8; i++) hidePushButton(i);
-	for (i = 0; i < 3; i++) hideScrollBar(i);
+	for (uint16_t i = 0; i < 8; i++) hidePushButton(i);
+	for (uint16_t i = 0; i < 3; i++) hideScrollBar(i);
 
 	windowClose(echo_AddMemory ? false : true);
 
@@ -863,16 +808,21 @@
 
 static int32_t SDLCALL mixThread(void *ptr)
 {
-	int8_t *destPtr, *mixPtr;
-	uint8_t mixTyp, destTyp;
-	int32_t destLen, mixLen;
+	smpPtr_t sp;
 
-	int16_t destIns = editor.curInstr;
-	int16_t destSmp = editor.curSmp;
+	int8_t *dstPtr, *mixPtr;
+	uint8_t mixFlags, dstFlags;
+	int32_t dstLen, mixLen;
+
+	int16_t dstIns = editor.curInstr;
+	int16_t dstSmp = editor.curSmp;
 	int16_t mixIns = editor.srcInstr;
 	int16_t mixSmp = editor.srcSmp;
 
-	if (destIns == mixIns && destSmp == mixSmp)
+	sample_t *s = &instr[dstIns]->smp[dstSmp];
+	sample_t *sSrc = &instr[mixIns]->smp[mixSmp];
+
+	if (dstIns == mixIns && dstSmp == mixSmp)
 	{
 		setMouseBusy(false);
 		ui.sysReqShown = false;
@@ -883,49 +833,45 @@
 	{
 		mixLen = 0;
 		mixPtr = NULL;
-		mixTyp = 0;
+		mixFlags = 0;
 	}
 	else
 	{
-		mixLen = instr[mixIns]->samp[mixSmp].len;
-		mixPtr = instr[mixIns]->samp[mixSmp].pek;
-		mixTyp = instr[mixIns]->samp[mixSmp].typ;
+		mixLen = sSrc->length;
+		mixPtr = sSrc->dataPtr;
+		mixFlags = sSrc->flags;
 
 		if (mixPtr == NULL)
 		{
 			mixLen = 0;
-			mixTyp = 0;
+			mixFlags = 0;
 		}
 	}
 
-	if (instr[destIns] == NULL)
+	if (instr[dstIns] == NULL)
 	{
-		destLen = 0;
-		destPtr = NULL;
-		destTyp = 0;
+		dstLen = 0;
+		dstPtr = NULL;
+		dstFlags = 0;
 	}
 	else
 	{
-		destLen = instr[destIns]->samp[destSmp].len;
-		destPtr = instr[destIns]->samp[destSmp].pek;
-		destTyp = instr[destIns]->samp[destSmp].typ;
+		dstLen = s->length;
+		dstPtr = s->dataPtr;
+		dstFlags = s->flags;
 
-		if (destPtr == NULL)
+		if (dstPtr == NULL)
 		{
-			destLen = 0;
-			destTyp = 0;
+			dstLen = 0;
+			dstFlags = 0;
 		}
 	}
 
-	bool src16Bits = (mixTyp >> 4) & 1;
-	bool dst16Bits = (destTyp >> 4) & 1;
+	bool src16Bits = !!(mixFlags & SAMPLE_16BIT);
+	bool dst16Bits = !!(dstFlags & SAMPLE_16BIT);
 
-	int32_t mix8Size = src16Bits ? (mixLen >> 1) : mixLen;
-	int32_t dest8Size = dst16Bits ? (destLen >> 1) : destLen;
-	int32_t max8Size = (dest8Size > mix8Size) ? dest8Size : mix8Size;
-	int32_t maxLen = dst16Bits ? (max8Size << 1) : max8Size;
-
-	if (maxLen <= 0)
+	int32_t maxLen = (dstLen > mixLen) ? dstLen : mixLen;
+	if (maxLen == 0)
 	{
 		setMouseBusy(false);
 		ui.sysReqShown = false;
@@ -932,8 +878,7 @@
 		return true;
 	}
 
-	int8_t *p = (int8_t *)calloc(maxLen + LOOP_FIX_LEN, sizeof (int8_t));
-	if (p == NULL)
+	if (!allocateSmpDataPtr(&sp, maxLen, dst16Bits))
 	{
 		outOfMemory = true;
 		setMouseBusy(false);
@@ -940,8 +885,9 @@
 		ui.sysReqShown = false;
 		return true;
 	}
+	memset(sp.ptr, 0, maxLen);
 
-	if (instr[destIns] == NULL && !allocateInstr(destIns))
+	if (instr[dstIns] == NULL && !allocateInstr(dstIns))
 	{
 		outOfMemory = true;
 		setMouseBusy(false);
@@ -950,65 +896,38 @@
 	}
 
 	pauseAudio();
+	unfixSample(s);
 
-	restoreSample(&instr[destIns]->samp[destSmp]);
-
-	// restore source sample
+	// unfix source sample
 	if (instr[mixIns] != NULL)
-		restoreSample(&instr[mixIns]->samp[mixSmp]);
+		unfixSample(sSrc);
 
 	const double dAmp1 = mix_Balance / 100.0;
 	const double dAmp2 = 1.0 - dAmp1;
+	const double dSmp1ScaleMul = src16Bits ? (1.0 / 32768.0) : (1.0 / 128.0);
+	const double dSmp2ScaleMul = dst16Bits ? (1.0 / 32768.0) : (1.0 / 128.0);
+	const double dNormalizeMul = dst16Bits ? 32768.0 : 128.0;
 
-	int8_t *destPek = p + SMP_DAT_OFFSET;
-	for (int32_t i = 0; i < max8Size; i++)
+	for (int32_t i = 0; i < maxLen; i++)
 	{
-		int32_t index16 = i << 1;
+		double dSmp1 = (i >= mixLen) ? 0.0 : (getSampleValue(mixPtr, i, src16Bits) * dSmp1ScaleMul); // -1.0 .. 0.999inf
+		double dSmp2 = (i >= dstLen) ? 0.0 : (getSampleValue(dstPtr, i, dst16Bits) * dSmp2ScaleMul); // -1.0 .. 0.999inf
 
-		int32_t smp1 = (i >= mix8Size) ? 0 : getSampleValue(mixPtr, mixTyp, src16Bits ? index16 : i);
-		int32_t smp2 = (i >= dest8Size) ? 0 : getSampleValue(destPtr, destTyp, dst16Bits ? index16 : i);
-
-		if (!src16Bits) smp1 <<= 8;
-		if (!dst16Bits) smp2 <<= 8;
-
-		double dSmp = (smp1 * dAmp1) + (smp2 * dAmp2);
-		if (!dst16Bits)
-			dSmp *= 1.0 / 256.0;
-
-		// rounding (faster than calling round())
-		     if (dSmp < 0.0) dSmp -= 0.5;
-		else if (dSmp > 0.0) dSmp += 0.5;
-
-		int32_t smp32 = (int32_t)dSmp;
-		if (dst16Bits)
-		{
-			CLAMP16(smp32);
-		}
-		else
-		{
-			     if (smp32 < -128) smp32 = -128;
-			else if (smp32 >  127) smp32 =  127;
-		}
-
-		putSampleValue(destPek, destTyp, dst16Bits ? index16 : i, (int16_t)smp32);
+		const double dSmp = ((dSmp1 * dAmp1) + (dSmp2 * dAmp2)) * dNormalizeMul;
+		putSampleValue(sp.ptr, i, dSmp, dst16Bits);
 	}
 
-	if (instr[destIns]->samp[destSmp].origPek != NULL)
-		free(instr[destIns]->samp[destSmp].origPek);
+	freeSmpData(s);
+	setSmpDataPtr(s, &sp);
 
-	instr[destIns]->samp[destSmp].origPek = p;
-	instr[destIns]->samp[destSmp].pek = instr[destIns]->samp[destSmp].origPek + SMP_DAT_OFFSET;
-	instr[destIns]->samp[destSmp].len = maxLen;
-	instr[destIns]->samp[destSmp].typ = destTyp;
+	s->length = maxLen;
+	s->flags = dstFlags;
 
-	if (dst16Bits)
-		instr[destIns]->samp[destSmp].len &= 0xFFFFFFFE;
+	fixSample(s);
 
-	fixSample(&instr[destIns]->samp[destSmp]);
-
-	// fix source sample
+	// re-fix source sample again
 	if (instr[mixIns] != NULL)
-		fixSample(&instr[mixIns]->samp[mixSmp]);
+		fixSample(sSrc);
 
 	resumeAudio();
 
@@ -1187,64 +1106,60 @@
 
 static void sbSetStartVolPos(uint32_t pos)
 {
-	int16_t val = (int16_t)(pos - 500);
-	if (val != vol_StartVol)
+	int32_t val = (int32_t)(pos - 200);
+	if (val != (int32_t)dVol_StartVol)
 	{
 		     if (ABS(val)       < 10) val =    0;
 		else if (ABS(val - 100) < 10) val =  100;
-		else if (ABS(val - 200) < 10) val =  200;
-		else if (ABS(val - 300) < 10) val =  300;
-		else if (ABS(val - 400) < 10) val =  400;
 		else if (ABS(val + 100) < 10) val = -100;
-		else if (ABS(val + 200) < 10) val = -200;
-		else if (ABS(val + 300) < 10) val = -300;
-		else if (ABS(val + 400) < 10) val = -400;
 
-		vol_StartVol = val;
+		dVol_StartVol = (double)val;
 	}
 }
 
 static void sbSetEndVolPos(uint32_t pos)
 {
-	int16_t val = (int16_t)(pos - 500);
-	if (val != vol_EndVol)
+	int32_t val = (int32_t)(pos - 200);
+	if (val != (int32_t)dVol_EndVol)
 	{
 		     if (ABS(val)       < 10) val =    0;
 		else if (ABS(val - 100) < 10) val =  100;
-		else if (ABS(val - 200) < 10) val =  200;
-		else if (ABS(val - 300) < 10) val =  300;
-		else if (ABS(val - 400) < 10) val =  400;
 		else if (ABS(val + 100) < 10) val = -100;
-		else if (ABS(val + 200) < 10) val = -200;
-		else if (ABS(val + 300) < 10) val = -300;
-		else if (ABS(val + 400) < 10) val = -400;
 
-		vol_EndVol = val;
+		dVol_EndVol = val;
 	}
 }
 
 static void pbSampStartVolDown(void)
 {
-	if (vol_StartVol > -500)
-		vol_StartVol--;
+	if (dVol_StartVol > -200.0)
+		dVol_StartVol -= 1.0;
+
+	dVol_StartVol = floor(dVol_StartVol);
 }
 
 static void pbSampStartVolUp(void)
 {
-	if (vol_StartVol < 500)
-		vol_StartVol++;
+	if (dVol_StartVol < 200.0)
+		dVol_StartVol += 1.0;
+
+	dVol_StartVol = floor(dVol_StartVol);
 }
 
 static void pbSampEndVolDown(void)
 {
-	if (vol_EndVol > -500)
-		vol_EndVol--;
+	if (dVol_EndVol > -200.0)
+		dVol_EndVol -= 1.0;
+
+	dVol_EndVol = floor(dVol_EndVol);
 }
 
 static void pbSampEndVolUp(void)
 {
-	if (vol_EndVol < 500)
-		vol_EndVol++;
+	if (dVol_EndVol < 200.0)
+		dVol_EndVol += 1.0;
+
+	dVol_EndVol = floor(dVol_EndVol);
 }
 
 static int32_t SDLCALL applyVolumeThread(void *ptr)
@@ -1254,7 +1169,7 @@
 	if (instr[editor.curInstr] == NULL)
 		goto applyVolumeExit;
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
 
 	if (smpEd_Rx1 < smpEd_Rx2)
 	{
@@ -1261,8 +1176,8 @@
 		x1 = smpEd_Rx1;
 		x2 = smpEd_Rx2;
 
-		if (x2 > s->len)
-			x2 = s->len;
+		if (x2 > s->length)
+			x2 = s->length;
 
 		if (x1 < 0)
 			x1 = 0;
@@ -1269,69 +1184,79 @@
 
 		if (x2 <= x1)
 			goto applyVolumeExit;
-
-		if (s->typ & 16)
-		{
-			x1 &= 0xFFFFFFFE;
-			x2 &= 0xFFFFFFFE;
-		}
 	}
 	else
 	{
+		// no mark, operate on whole sample
 		x1 = 0;
-		x2 = s->len;
+		x2 = s->length;
 	}
 
-	if (s->typ & 16)
-	{
-		x1 >>= 1;
-		x2 >>= 1;
-	}
-
 	const int32_t len = x2 - x1;
 	if (len <= 0)
 		goto applyVolumeExit;
 
-	const double dVol1 = vol_StartVol / 100.0;
-	const double dVol2 = vol_EndVol / 100.0;
-	const double dPosMul = (dVol2 - dVol1) / len;
+	bool mustInterpolate = (dVol_StartVol != dVol_EndVol);
+	const double dVol = dVol_StartVol / 100.0;
+	const double dPosMul = ((dVol_EndVol / 100.0) - dVol) / len;
 
 	pauseAudio();
-	restoreSample(s);
-	if (s->typ & 16)
+	unfixSample(s);
+	if (s->flags & SAMPLE_16BIT)
 	{
-		int16_t *ptr16 = (int16_t *)s->pek + x1;
-		for (int32_t i = 0; i < len; i++)
+		int16_t *ptr16 = (int16_t *)s->dataPtr + x1;
+		if (mustInterpolate)
 		{
-			const double dAmp = dVol1 + (i * dPosMul); // linear interpolation
+			for (int32_t i = 0; i < len; i++)
+			{
+				double dSmp = (int32_t)ptr16[i] * (dVol + (i * dPosMul)); // linear interpolation
+				DROUND(dSmp);
 
-			double dSmp = ptr16[i] * dAmp;
+				int32_t smp32 = (int32_t)dSmp;
+				CLAMP16(smp32);
+				ptr16[i] = (int16_t)smp32;
+			}
 
-			// rounding (faster than calling round())
-			     if (dSmp < 0.0) dSmp -= 0.5;
-			else if (dSmp > 0.0) dSmp += 0.5;
+		}
+		else // no interpolation needed
+		{
+			for (int32_t i = 0; i < len; i++)
+			{
+				double dSmp = (int32_t)ptr16[i] * dVol;
+				DROUND(dSmp);
 
-			int32_t amp32 = (int32_t)dSmp;
-			CLAMP16(amp32);
-			ptr16[i] = (int16_t)amp32;
+				int32_t smp32 = (int32_t)dSmp;
+				CLAMP16(smp32);
+				ptr16[i] = (int16_t)smp32;
+			}
 		}
 	}
-	else
+	else // 8-bit sample
 	{
-		int8_t *ptr8 = s->pek + x1;
-		for (int32_t i = 0; i < len; i++)
+		int8_t *ptr8 = s->dataPtr + x1;
+		if (mustInterpolate)
 		{
-			const double dAmp = dVol1 + (i * dPosMul); // linear interpolation
+			for (int32_t i = 0; i < len; i++)
+			{
+				double dSmp = (int32_t)ptr8[i] * (dVol + (i * dPosMul)); // linear interpolation
+				DROUND(dSmp);
 
-			double dSmp = ptr8[i] * dAmp;
+				int32_t smp32 = (int32_t)dSmp;
+				CLAMP8(smp32);
+				ptr8[i] = (int8_t)smp32;
+			}
+		}
+		else // no interpolation needed
+		{
+			for (int32_t i = 0; i < len; i++)
+			{
+				double dSmp = (int32_t)ptr8[i] * dVol;
+				DROUND(dSmp);
 
-			// rounding (faster than calling round())
-			     if (dSmp < 0.0) dSmp -= 0.5;
-			else if (dSmp > 0.0) dSmp += 0.5;
-
-			int32_t amp32 = (int32_t)dSmp;
-			CLAMP8(amp32);
-			ptr8[i] = (int8_t)amp32;
+				int32_t smp32 = (int32_t)dSmp;
+				CLAMP8(smp32);
+				ptr8[i] = (int8_t)smp32;
+			}
 		}
 	}
 	fixSample(s);
@@ -1350,7 +1275,7 @@
 
 static void pbApplyVolume(void)
 {
-	if (vol_StartVol == 100 && vol_EndVol == 100)
+	if (dVol_StartVol == 100.0 && dVol_EndVol == 100.0)
 	{
 		ui.sysReqShown = false;
 		return; // no volume change to be done
@@ -1374,7 +1299,7 @@
 	if (instr[editor.curInstr] == NULL)
 		goto getScaleExit;
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp];
 
 	if (smpEd_Rx1 < smpEd_Rx2)
 	{
@@ -1381,8 +1306,8 @@
 		x1 = smpEd_Rx1;
 		x2 = smpEd_Rx2;
 
-		if (x2 > s->len)
-			x2 = s->len;
+		if (x2 > s->length)
+			x2 = s->length;
 
 		if (x1 < 0)
 			x1 = 0;
@@ -1389,37 +1314,38 @@
 
 		if (x2 <= x1)
 			goto getScaleExit;
-
-		if (s->typ & 16)
-		{
-			x1 &= 0xFFFFFFFE;
-			x2 &= 0xFFFFFFFE;
-		}
 	}
 	else
 	{
 		// no sample marking, operate on the whole sample
 		x1 = 0;
-		x2 = s->len;
+		x2 = s->length;
 	}
 
 	uint32_t len = x2 - x1;
-	if (s->typ & 16)
-		len >>= 1;
-
 	if (len <= 0)
 	{
-		vol_StartVol = 0;
-		vol_EndVol = 0;
+		dVol_StartVol = dVol_EndVol = 100.0;
 		goto getScaleExit;
 	}
 
-	restoreSample(s);
+	double dVolChange = 100.0;
 
+	/* If sample is looped and the loopEnd point is inside the marked range,
+	** we need to unfix the fixed interpolation sample before scanning,
+	** and fix it again after we're done.
+	*/
+	bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF;
+	const int32_t loopEnd = s->loopStart + s->loopLength;
+	bool fixedSampleInRange = hasLoop && (x1 <= loopEnd) && (x2 >= loopEnd);
+
+	if (fixedSampleInRange)
+		unfixSample(s);
+
 	int32_t maxAmp = 0;
-	if (s->typ & 16)
+	if (s->flags & SAMPLE_16BIT)
 	{
-		const int16_t *ptr16 = (const int16_t *)&s->pek[x1];
+		const int16_t *ptr16 = (const int16_t *)s->dataPtr + x1;
 		for (uint32_t i = 0; i < len; i++)
 		{
 			const int32_t absSmp = ABS(ptr16[i]);
@@ -1426,10 +1352,13 @@
 			if (absSmp > maxAmp)
 				maxAmp = absSmp;
 		}
+
+		if (maxAmp > 0)
+			dVolChange = (32767.0 / maxAmp) * 100.0;
 	}
-	else
+	else // 8-bit
 	{
-		const int8_t *ptr8 = (const int8_t *)&s->pek[x1];
+		const int8_t *ptr8 = (const int8_t *)&s->dataPtr[x1];
 		for (uint32_t i = 0; i < len; i++)
 		{
 			const int32_t absSmp = ABS(ptr8[i]);
@@ -1437,25 +1366,17 @@
 				maxAmp = absSmp;
 		}
 
-		maxAmp <<= 8;
+		if (maxAmp > 0)
+			dVolChange = (127.0 / maxAmp) * 100.0;
 	}
 
-	fixSample(s);
+	if (fixedSampleInRange)
+		fixSample(s);
 
-	if (maxAmp <= 0)
-	{
-		vol_StartVol = 0;
-		vol_EndVol = 0;
-	}
-	else
-	{
-		int32_t vol = (100 * 32768) / maxAmp;
-		if (vol > 500)
-			vol = 500;
+	if (dVolChange < 100.0) // yes, this can happen...
+		dVolChange = 100.0;
 
-		vol_StartVol = (int16_t)vol;
-		vol_EndVol = (int16_t)vol;
-	}
+	dVol_StartVol = dVol_EndVol = dVolChange;
 
 getScaleExit:
 	setMouseBusy(false);
@@ -1484,7 +1405,7 @@
 	const int16_t y = 230;
 	const int16_t w = 301;
 	const int16_t h = 52;
-	uint16_t val;
+	uint32_t val;
 
 	// main fill
 	fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS);
@@ -1506,52 +1427,75 @@
 	charOutShadow(282, 236, PAL_FORGRND, PAL_BUTTON2, '%');
 	charOutShadow(282, 250, PAL_FORGRND, PAL_BUTTON2, '%');
 
-	     if (vol_StartVol == 0) sign = ' ';
-	else if (vol_StartVol  < 0) sign = '-';
-	else sign = '+';
+	const int32_t startVol = (int32_t)dVol_StartVol;
+	const int32_t endVol = (int32_t)dVol_EndVol;
 
-	val = ABS(vol_StartVol);
-	if (val > 99)
+	if (startVol > 200)
 	{
-		charOut(253, 236, PAL_FORGRND, sign);
-		charOut(260, 236, PAL_FORGRND, '0' + (char)(val / 100));
-		charOut(267, 236, PAL_FORGRND, '0' + ((val / 10) % 10));
-		charOut(274, 236, PAL_FORGRND, '0' + (val % 10));
+		charOut(253, 236, PAL_FORGRND, '>');
+		charOut(260, 236, PAL_FORGRND, '2');
+		charOut(267, 236, PAL_FORGRND, '0');
+		charOut(274, 236, PAL_FORGRND, '0');
 	}
-	else if (val > 9)
-	{
-		charOut(260, 236, PAL_FORGRND, sign);
-		charOut(267, 236, PAL_FORGRND, '0' + (char)(val / 10));
-		charOut(274, 236, PAL_FORGRND, '0' + (val % 10));
-	}
 	else
 	{
-		charOut(267, 236, PAL_FORGRND, sign);
-		charOut(274, 236, PAL_FORGRND, '0' + (char)val);
+		     if (startVol == 0) sign = ' ';
+		else if (startVol  < 0) sign = '-';
+		else sign = '+';
+
+		val = ABS(startVol);
+		if (val > 99)
+		{
+			charOut(253, 236, PAL_FORGRND, sign);
+			charOut(260, 236, PAL_FORGRND, '0' + (char)(val / 100));
+			charOut(267, 236, PAL_FORGRND, '0' + ((val / 10) % 10));
+			charOut(274, 236, PAL_FORGRND, '0' + (val % 10));
+		}
+		else if (val > 9)
+		{
+			charOut(260, 236, PAL_FORGRND, sign);
+			charOut(267, 236, PAL_FORGRND, '0' + (char)(val / 10));
+			charOut(274, 236, PAL_FORGRND, '0' + (val % 10));
+		}
+		else
+		{
+			charOut(267, 236, PAL_FORGRND, sign);
+			charOut(274, 236, PAL_FORGRND, '0' + (char)val);
+		}
 	}
 
-	     if (vol_EndVol == 0) sign = ' ';
-	else if (vol_EndVol  < 0) sign = '-';
-	else sign = '+';
-
-	val = ABS(vol_EndVol);
-	if (val > 99)
+	if (endVol > 200)
 	{
-		charOut(253, 250, PAL_FORGRND, sign);
-		charOut(260, 250, PAL_FORGRND, '0' + (char)(val / 100));
-		charOut(267, 250, PAL_FORGRND, '0' + ((val / 10) % 10));
-		charOut(274, 250, PAL_FORGRND, '0' + (val % 10));
+		charOut(253, 250, PAL_FORGRND, '>');
+		charOut(260, 250, PAL_FORGRND, '2');
+		charOut(267, 250, PAL_FORGRND, '0');
+		charOut(274, 250, PAL_FORGRND, '0');
 	}
-	else if (val > 9)
-	{
-		charOut(260, 250, PAL_FORGRND, sign);
-		charOut(267, 250, PAL_FORGRND, '0' + (char)(val / 10));
-		charOut(274, 250, PAL_FORGRND, '0' + (val % 10));
-	}
 	else
 	{
-		charOut(267, 250, PAL_FORGRND, sign);
-		charOut(274, 250, PAL_FORGRND, '0' + (char)val);
+		     if (endVol == 0) sign = ' ';
+		else if (endVol  < 0) sign = '-';
+		else sign = '+';
+
+		val = ABS(endVol);
+		if (val > 99)
+		{
+			charOut(253, 250, PAL_FORGRND, sign);
+			charOut(260, 250, PAL_FORGRND, '0' + (char)(val / 100));
+			charOut(267, 250, PAL_FORGRND, '0' + ((val / 10) % 10));
+			charOut(274, 250, PAL_FORGRND, '0' + (val % 10));
+		}
+		else if (val > 9)
+		{
+			charOut(260, 250, PAL_FORGRND, sign);
+			charOut(267, 250, PAL_FORGRND, '0' + (char)(val / 10));
+			charOut(274, 250, PAL_FORGRND, '0' + (val % 10));
+		}
+		else
+		{
+			charOut(267, 250, PAL_FORGRND, sign);
+			charOut(274, 250, PAL_FORGRND, '0' + (char)val);
+		}
 	}
 }
 
@@ -1653,8 +1597,8 @@
 	s->callbackFunc = sbSetStartVolPos;
 	s->visible = true;
 	setScrollBarPageLength(0, 1);
-	setScrollBarEnd(0, 500 * 2);
-	setScrollBarPos(0, 500, false);
+	setScrollBarEnd(0, 200 * 2);
+	setScrollBarPos(0, 200, false);
 
 	// volume end scrollbar
 	s = &scrollBars[1];
@@ -1666,8 +1610,8 @@
 	s->callbackFunc = sbSetEndVolPos;
 	s->visible = true;
 	setScrollBarPageLength(1, 1);
-	setScrollBarEnd(1, 500 * 2);
-	setScrollBarPos(1, 500, false);
+	setScrollBarEnd(1, 200 * 2);
+	setScrollBarPos(1, 200, false);
 }
 
 void pbSampleVolume(void)
@@ -1676,7 +1620,7 @@
 
 	if (editor.curInstr == 0 ||
 		instr[editor.curInstr] == NULL ||
-		instr[editor.curInstr]->samp[editor.curSmp].pek == NULL)
+		instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL)
 	{
 		return;
 	}
@@ -1701,8 +1645,12 @@
 		if (ui.setMouseIdle) mouseAnimOff();
 
 		drawSampleVolumeBox();
-		setScrollBarPos(0, 500 + vol_StartVol, false);
-		setScrollBarPos(1, 500 + vol_EndVol, false);
+
+		const int32_t startVol = (int32_t)dVol_StartVol;
+		const int32_t endVol = (int32_t)dVol_EndVol;
+
+		setScrollBarPos(0, 200 + startVol, false);
+		setScrollBarPos(1, 200 + endVol, false);
 		for (i = 0; i < 7; i++) drawPushButton(i);
 		for (i = 0; i < 2; i++) drawScrollBar(i);
 
--- a/src/ft2_sample_loader.c
+++ b/src/ft2_sample_loader.c
@@ -16,6 +16,10 @@
 #include "ft2_diskop.h"
 #include "ft2_structs.h"
 
+#ifdef HAS_LIBFLAC
+bool loadFLAC(FILE *f, uint32_t filesize);
+#endif
+
 bool loadAIFF(FILE *f, uint32_t filesize);
 bool loadIFF(FILE *f, uint32_t filesize);
 bool loadRAW(FILE *f, uint32_t filesize);
@@ -26,7 +30,8 @@
 	FORMAT_UNKNOWN = 0,
 	FORMAT_IFF = 1,
 	FORMAT_WAV = 2,
-	FORMAT_AIFF = 3
+	FORMAT_AIFF = 3,
+	FORMAT_FLAC = 4
 };
 
 // file extensions accepted by Disk Op. in sample mode
@@ -33,7 +38,7 @@
 char *supportedSmpExtensions[] =
 {
 	"iff", "raw", "wav", "snd", "smp", "sam", "aif", "pat",
-	"aiff",
+	"aiff","flac",
 
 	// IMPORTANT: Remember comma after last entry above
 	"END_OF_LIST" // do NOT move, remove or edit this line!
@@ -43,13 +48,13 @@
 bool loadAsInstrFlag, smpFilenameSet;
 char *smpFilename;
 uint8_t sampleSlot;
-sampleTyp tmpSmp;
+sample_t tmpSmp;
 // --------------------------
 
 static volatile bool sampleIsLoading;
 static SDL_Thread *thread;
 
-static void freeTmpSample(sampleTyp *s);
+static void freeTmpSample(sample_t *s);
 
 // Crude sample detection routine. These aren't always accurate detections!
 static int8_t detectSample(FILE *f)
@@ -62,6 +67,9 @@
 	fread(D, 1, sizeof (D), f);
 	fseek(f, oldPos, SEEK_SET);
 
+	if (!memcmp("fLaC", &D[0], 4)) // XXX: Kinda lousy detection...
+		return FORMAT_FLAC;
+
 	if (!memcmp("FORM", &D[0], 4) && (!memcmp("8SVX", &D[8], 4) || !memcmp("16SV", &D[8], 4)))
 		return FORMAT_IFF;
 
@@ -105,6 +113,16 @@
 	rewind(f);
 	switch (format)
 	{
+		case FORMAT_FLAC:
+		{
+#ifdef HAS_LIBFLAC
+			sampleLoaded = loadFLAC(f, filesize);
+#else
+			loaderMsgBox("Can't load sample: Program is not compiled with FLAC support!");
+#endif
+		}
+		break;
+
 		case FORMAT_IFF: sampleLoaded = loadIFF(f, filesize); break;
 		case FORMAT_WAV: sampleLoaded = loadWAV(f, filesize); break;
 		case FORMAT_AIFF: sampleLoaded = loadAIFF(f, filesize); break;
@@ -116,7 +134,7 @@
 		goto loadError;
 
 	// sample loaded successfully!
-	
+
 	if (!smpFilenameSet) // if we didn't set a custom sample name in the loader, set it to its filename
 	{
 		char *tmpFilename = unicharToCp437(editor.tmpFilenameU, true);
@@ -166,10 +184,13 @@
 		goto loadError;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[sampleSlot];
+	sample_t *s = &instr[editor.curInstr]->smp[sampleSlot];
 
 	freeSample(editor.curInstr, sampleSlot);
-	memcpy(s, &tmpSmp, sizeof (sampleTyp));
+	memcpy(s, &tmpSmp, sizeof (sample_t));
+
+	sanitizeSample(s);
+
 	fixSample(s); // prepares sample for branchless resampling interpolation
 	fixInstrAndSampleNames(editor.curInstr);
 
@@ -191,15 +212,9 @@
 	(void)ptr;
 }
 
-static void freeTmpSample(sampleTyp *s)
+static void freeTmpSample(sample_t *s)
 {
-	if (s->origPek != NULL)
-	{
-		free(s->origPek);
-		s->origPek = NULL;
-	}
-
-	s->pek = NULL;
+	freeSmpData(s);
 }
 
 void removeSampleIsLoadingFlag(void)
--- a/src/ft2_sample_loader.h
+++ b/src/ft2_sample_loader.h
@@ -23,7 +23,7 @@
 extern bool loadAsInstrFlag, smpFilenameSet;
 extern char *smpFilename;
 extern uint8_t sampleSlot;
-extern sampleTyp tmpSmp;
+extern sample_t tmpSmp;
 // --------------------------
 
 // file extensions accepted by Disk Op. in sample mode
--- a/src/ft2_sample_saver.c
+++ b/src/ft2_sample_saver.c
@@ -52,38 +52,58 @@
 static SDL_Thread *thread;
 
 // restores modified interpolation tap samples after loopEnd (for .RAW/.IFF/.WAV samples after save)
-static void fileRestoreFixedSampleData(UNICHAR *filenameU, uint32_t sampleDataOffset, sampleTyp *s)
+static void fileRestoreFixedSampleData(UNICHAR *filenameU, uint32_t sampleDataOffset, sample_t *s)
 {
-	if (!s->fixed)
+	if (!s->isFixed)
 		return; // nothing to restore
 
-	FILE *f = UNICHAR_FOPEN(filenameU, "r+"); // open in read+update mode
-	if (f == NULL)
-		return;
+	int32_t sampleFixPos = s->fixedPos;
+	int32_t sampleFixOffset = 0;
+	int32_t samplesToWrite = SINC_RIGHT_TAPS;
 
-	const bool sample16Bit = (s->typ & 16) ? true : false;
+	if (saveRangeFlag)
+	{
+		const int32_t markStart = getSampleRangeStart();
+		const int32_t markEnd = getSampleRangeEnd();
 
-	uint32_t fixedPos = s->fixedPos;
-	if (sample16Bit)
-		fixedPos *= 2;
+		if (markStart > sampleFixPos+SINC_RIGHT_TAPS || markEnd < sampleFixPos)
+			return; // nothing to do here
 
-	if (fixedPos >= (uint32_t)s->len)
+		if (markStart > sampleFixPos)
+		{
+			sampleFixOffset += markStart-sampleFixPos;
+			samplesToWrite -= markStart-sampleFixPos;
+		}
+
+		sampleFixPos -= markStart;
+
+		if (sampleFixPos + samplesToWrite > markEnd)
+			samplesToWrite = markEnd - sampleFixPos;
+
+		if (samplesToWrite < 0 || samplesToWrite > SINC_RIGHT_TAPS || sampleFixPos < 0 || sampleFixOffset < 0 || sampleFixOffset >= SINC_RIGHT_TAPS)
+			return;
+
+	}
+
+	FILE* f = UNICHAR_FOPEN(filenameU, "r+"); // open in read+update mode
+	if (f == NULL)
 		return;
 
-	uint32_t bytesToWrite = SINC_RIGHT_TAPS * (sample16Bit+1);
-	if (fixedPos+bytesToWrite > (uint32_t)s->len)
-		bytesToWrite = s->len - fixedPos;
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+	if (sample16Bit)
+		fseek(f, sampleDataOffset + (sampleFixPos * 2), SEEK_SET);
+	else
+		fseek(f, sampleDataOffset + sampleFixPos, SEEK_SET);
 
-	fseek(f, sampleDataOffset+fixedPos, SEEK_SET);
 	if (sample16Bit)
 	{
-		fwrite(s->fixedSmp, sizeof (int16_t), bytesToWrite / 2, f);
+		fwrite(&s->fixedSmp[sampleFixOffset], sizeof (int16_t), samplesToWrite, f);
 	}
 	else
 	{
-		for (uint32_t i = 0; i < bytesToWrite; i++)
+		for (int32_t i = 0; i < samplesToWrite; i++)
 		{
-			int8_t fixedSmp = (int8_t)s->fixedSmp[i];
+			int8_t fixedSmp = (int8_t)s->fixedSmp[sampleFixOffset+i];
 			if (editor.sampleSaveMode == SMP_SAVE_MODE_WAV) // on 8-bit WAVs the sample data is unsigned
 				fixedSmp ^= 0x80; // signed -> unsigned
 
@@ -99,23 +119,25 @@
 	int8_t *samplePtr;
 	uint32_t sampleLen;
 
-	instrTyp *ins = instr[editor.curInstr];
-	if (ins == NULL || ins->samp[editor.curSmp].pek == NULL || ins->samp[editor.curSmp].len == 0)
+	instr_t *ins = instr[editor.curInstr];
+	if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0)
 	{
 		okBoxThreadSafe(0, "System message", "The sample is empty!");
 		return false;
 	}
 
-	sampleTyp *smp = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *smp = &instr[editor.curInstr]->smp[editor.curSmp];
+	bool sample16Bit = !!(smp->flags & SAMPLE_16BIT);
+
 	if (saveRangedData)
 	{
-		samplePtr = &smp->pek[getSampleRangeStart()];
+		samplePtr = &smp->dataPtr[getSampleRangeStart() << sample16Bit];
 		sampleLen = getSampleRangeLength();
 	}
 	else
 	{
-		sampleLen = smp->len;
-		samplePtr = smp->pek;
+		sampleLen = smp->length;
+		samplePtr = smp->dataPtr;
 	}
 
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
@@ -135,8 +157,8 @@
 	fclose(f);
 
 	// restore modified interpolation tap samples after loopEnd
-	const bool loopEnabled = (smp->typ & 3) ? true : false;
-	if (loopEnabled && smp->len > smp->repS+smp->repL)
+	bool loopEnabled = GET_LOOPTYPE(smp->flags) != LOOP_OFF;
+	if (loopEnabled && smp->length > smp->loopStart+smp->loopLength)
 		fileRestoreFixedSampleData(filenameU, 0, smp);
 
 	editor.diskOpReadDir = true; // force diskop re-read
@@ -181,14 +203,14 @@
 	int8_t *samplePtr;
 	uint32_t sampleLen, smpNameLen, chunkLen;
 
-	instrTyp *ins = instr[editor.curInstr];
-	if (ins == NULL || ins->samp[editor.curSmp].pek == NULL || ins->samp[editor.curSmp].len == 0)
+	instr_t *ins = instr[editor.curInstr];
+	if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0)
 	{
 		okBoxThreadSafe(0, "System message", "The sample is empty!");
 		return false;
 	}
 
-	sampleTyp *smp = &instr[editor.curInstr]->samp[editor.curSmp];
+	sample_t *smp = &instr[editor.curInstr]->smp[editor.curSmp];
 
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
@@ -197,28 +219,30 @@
 		return false;
 	}
 
+	bool sample16Bit = !!(smp->flags & SAMPLE_16BIT);
+
 	if (saveRangedData)
 	{
-		samplePtr = &smp->pek[getSampleRangeStart()];
+		samplePtr = &smp->dataPtr[getSampleRangeStart() << sample16Bit];
 		sampleLen = getSampleRangeLength();
 	}
 	else
 	{
-		sampleLen = smp->len;
-		samplePtr = smp->pek;
+		sampleLen = smp->length;
+		samplePtr = smp->dataPtr;
 	}
 
 	// "FORM" chunk
 	iffWriteChunkHeader(f, "FORM", 0); // "FORM" chunk size is overwritten later
-	iffWriteUint32(f, (smp->typ & 16) ? 0x31365356 : 0x38535658); // bitdepth - "16SV" (16-bit) or "8SVX" (8-bit)
+	iffWriteUint32(f, sample16Bit ? 0x31365356 : 0x38535658); // bitdepth - "16SV" (16-bit) or "8SVX" (8-bit)
 
 	// "VHDR" chunk
 	iffWriteChunkHeader(f, "VHDR", 20);
 
-	if (!saveRangedData && (smp->typ & 3)) // loop enabled?
+	if (!saveRangedData && GET_LOOPTYPE(smp->flags) != LOOP_OFF)
 	{
-		iffWriteUint32(f, smp->repS); // oneShotHiSamples
-		iffWriteUint32(f, smp->repL); // repeatHiSamples
+		iffWriteUint32(f, smp->loopStart << sample16Bit); // oneShotHiSamples
+		iffWriteUint32(f, smp->loopLength << sample16Bit); // repeatHiSamples
 	}
 	else
 	{
@@ -235,7 +259,7 @@
 
 	iffWriteUint8(f, 1); // ctOctave (number of samples)
 	iffWriteUint8(f, 0); // sCompression
-	iffWriteUint32(f, smp->vol * 1024); // volume (max: 65536/0x10000)
+	iffWriteUint32(f, smp->volume * 1024); // volume (max: 65536/0x10000)
 
 	// "NAME" chunk
 
@@ -272,7 +296,7 @@
 	iffWriteChunkData(f, PROG_NAME_STR, chunkLen);
 
 	// "BODY" chunk
-	chunkLen = sampleLen;
+	chunkLen = sampleLen << sample16Bit;
 	iffWriteChunkHeader(f, "BODY", chunkLen);
 	const uint32_t sampleDataPos = ftell(f);
 	iffWriteChunkData(f, samplePtr, chunkLen);
@@ -285,8 +309,8 @@
 	fclose(f);
 
 	// restore modified interpolation tap samples after loopEnd
-	const bool loopEnabled = (smp->typ & 3) ? true : false;
-	if (loopEnabled && smp->len > smp->repS+smp->repL)
+	bool loopEnabled = GET_LOOPTYPE(smp->flags) != LOOP_OFF;
+	if (loopEnabled && smp->length > smp->loopStart+smp->loopLength)
 		fileRestoreFixedSampleData(filenameU, sampleDataPos, smp);
 
 	editor.diskOpReadDir = true; // force diskop re-read
@@ -304,14 +328,14 @@
 	samplerChunk_t samplerChunk;
 	mptExtraChunk_t mptExtraChunk;
 
-	instrTyp *ins = instr[editor.curInstr];
-	if (ins == NULL || ins->samp[editor.curSmp].pek == NULL || ins->samp[editor.curSmp].len == 0)
+	instr_t *ins = instr[editor.curInstr];
+	if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0)
 	{
 		okBoxThreadSafe(0, "System message", "The sample is empty!");
 		return false;
 	}
 
-	sampleTyp *smp = &ins->samp[editor.curSmp];
+	sample_t *smp = &ins->smp[editor.curSmp];
 
 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
 	if (f == NULL)
@@ -320,18 +344,20 @@
 		return false;
 	}
 
+	bool sample16Bit = !!(smp->flags & SAMPLE_16BIT);
+
 	if (saveRangedData)
 	{
-		samplePtr = &smp->pek[getSampleRangeStart()];
+		samplePtr = &smp->dataPtr[getSampleRangeStart() << sample16Bit];
 		sampleLen = getSampleRangeLength();
 	}
 	else
 	{
-		sampleLen = smp->len;
-		samplePtr = smp->pek;
+		sampleLen = smp->length;
+		samplePtr = smp->dataPtr;
 	}
 
-	const uint8_t sampleBitDepth = (smp->typ & 16) ? 16 : 8;
+	const uint8_t sampleBitDepth = sample16Bit ? 16 : 8;
 
 	wavHeader.chunkID = 0x46464952; // "RIFF"
 	wavHeader.chunkSize = 0; // is filled later
@@ -345,7 +371,7 @@
 	wavHeader.blockAlign = (wavHeader.numChannels * sampleBitDepth) / 8;
 	wavHeader.bitsPerSample = sampleBitDepth;
 	wavHeader.subchunk2ID = 0x61746164; // "data"
-	wavHeader.subchunk2Size = sampleLen;
+	wavHeader.subchunk2Size = sampleLen << sample16Bit;
 
 	// write main header
 	fwrite(&wavHeader, sizeof (wavHeader_t), 1, f);
@@ -354,7 +380,7 @@
 	const uint32_t sampleDataPos = ftell(f);
 	if (sampleBitDepth == 16)
 	{
-		fwrite((int16_t *)samplePtr, sizeof (int16_t), sampleLen / 2, f);
+		fwrite((int16_t *)samplePtr, sizeof (int16_t), sampleLen, f);
 	}
 	else
 	{
@@ -366,7 +392,7 @@
 		fputc(0, f); // write pad byte if chunk size is uneven
 
 	// write "smpl" chunk if loop is enabled
-	if (!saveRangedData && (smp->typ & 3))
+	if (!saveRangedData && GET_LOOPTYPE(smp->flags) != LOOP_OFF)
 	{
 		memset(&samplerChunk, 0, sizeof (samplerChunk));
 
@@ -375,21 +401,10 @@
 		samplerChunk.dwSamplePeriod = 1000000000 / wavHeader.sampleRate;
 		samplerChunk.dwMIDIUnityNote = 60; // 60 = MIDI middle-C
 		samplerChunk.cSampleLoops = 1;
-		samplerChunk.loop.dwType = (smp->typ & 3) - 1; // 0 = forward, 1 = ping-pong
+		samplerChunk.loop.dwType = GET_LOOPTYPE(smp->flags)-1; // 0 = forward, 1 = ping-pong
+		samplerChunk.loop.dwStart = smp->loopStart;
+		samplerChunk.loop.dwEnd = (smp->loopStart + smp->loopLength) - 1;
 
-		if (sampleBitDepth == 16)
-		{
-			// divide loop points by 2 to get samples insetad of bytes
-			samplerChunk.loop.dwStart = smp->repS / 2;
-			samplerChunk.loop.dwEnd = ((smp->repS + smp->repL) / 2) - 1;
-		}
-		else
-		{
-			// 8-bit sample
-			samplerChunk.loop.dwStart = smp->repS;
-			samplerChunk.loop.dwEnd = (smp->repS + smp->repL) - 1;
-		}
-
 		fwrite(&samplerChunk, sizeof (samplerChunk), 1, f);
 		if (samplerChunk.chunkSize & 1)
 			fputc(0, f); // write pad byte if chunk size is uneven
@@ -403,10 +418,10 @@
 		mptExtraChunk.chunkID = 0x61727478; // "xtra"
 		mptExtraChunk.chunkSize = sizeof (mptExtraChunk) - 4 - 4;
 		mptExtraChunk.flags = 0x20; // set pan flag
-		mptExtraChunk.defaultPan = smp->pan; // 0..255
-		mptExtraChunk.defaultVolume = smp->vol * 4; // 0..256
+		mptExtraChunk.defaultPan = smp->panning; // 0..255
+		mptExtraChunk.defaultVolume = smp->volume * 4; // 0..256
 		mptExtraChunk.globalVolume = 64; // 0..64
-		mptExtraChunk.vibratoType = ins->vibTyp; // 0..3    0 = sine, 1 = square, 2 = ramp up, 3 = ramp down
+		mptExtraChunk.vibratoType = ins->vibType; // 0..3    0 = sine, 1 = square, 2 = ramp up, 3 = ramp down
 		mptExtraChunk.vibratoSweep = ins->vibSweep; // 0..255
 		mptExtraChunk.vibratoDepth = ins->vibDepth; // 0..15
 		mptExtraChunk.vibratoRate= ins->vibRate; // 0..63
@@ -474,8 +489,8 @@
 	fclose(f);
 
 	// restore modified interpolation tap samples after loopEnd
-	const bool loopEnabled = (smp->typ & 3) ? true : false;
-	if (loopEnabled && smp->len > smp->repS+smp->repL)
+	bool loopEnabled = GET_LOOPTYPE(smp->flags) != LOOP_OFF;
+	if (loopEnabled && smp->length > smp->loopStart+smp->loopLength)
 		fileRestoreFixedSampleData(filenameU, sampleDataPos, smp);
 
 	editor.diskOpReadDir = true; // force diskop re-read
--- a/src/ft2_sampling.c
+++ b/src/ft2_sampling.c
@@ -13,28 +13,38 @@
 #include "ft2_sampling.h"
 #include "ft2_structs.h"
 
-// these may very well change after opening the audio input device
+#define STEREO_SAMPLE_HEIGHT (SAMPLE_AREA_HEIGHT/2)
+#define SAMPLE_L_CENTER (SAMPLE_AREA_Y_CENTER - (STEREO_SAMPLE_HEIGHT/2))
+#define SAMPLE_R_CENTER (SAMPLE_AREA_Y_CENTER + (STEREO_SAMPLE_HEIGHT/2))
+
+#define MONO_SAMPLE_HEIGHT SAMPLE_AREA_HEIGHT
+#define SAMPLE_CENTER SAMPLE_AREA_Y_CENTER
+
+// this number may change when the audio input device is opened (must be 2^n)
 #define SAMPLING_BUFFER_SIZE 2048
 
+// (must be 2^n)
+#if defined(_WIN32) || defined(__APPLE__)
+#define PREVIEW_SAMPLES 2048
+#else
+#define PREVIEW_SAMPLES 8192
+#endif
+
 static bool sampleInStereo;
 static volatile bool drawSamplingBufferFlag, outOfMemoryFlag, noMoreRoomFlag;
-static int16_t *currWriteBuf;
-static int16_t displayBuffer1[SAMPLING_BUFFER_SIZE * 2], displayBuffer2[SAMPLING_BUFFER_SIZE * 2];
-static int32_t bytesSampled, samplingBufferBytes;
+static int16_t previewBufL[2][PREVIEW_SAMPLES], previewBufR[2][PREVIEW_SAMPLES];
+static int32_t samplesSampled, samplingBufferSize, currPreviewBufNum, oldPreviewSmpPos, currSampleLen;
 static uint32_t samplingRate;
-static volatile int32_t currSampleLen;
+static sample_t *smpL, *smpR;
 static SDL_AudioDeviceID recordDev;
-static int16_t rightChSmpSlot = -1;
 
-static void SDLCALL samplingCallback(void *userdata, Uint8 *stream, int len)
+static void SDLCALL stereoSamplingCallback(void *userdata, Uint8 *stream, int len)
 {
-	if (instr[editor.curInstr] == NULL || len < 0 || len > samplingBufferBytes)
+	const int32_t samples = len >> 2;
+	if (instr[editor.curInstr] == NULL || samples < 0 || samples > samplingBufferSize)
 		return;
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
-
-	int8_t *newPtr = (int8_t *)realloc(s->origPek, (s->len + len) + LOOP_FIX_LEN);
-	if (newPtr == NULL)
+	if (!reallocateSmpData(smpL, smpL->length + samples, true) || !reallocateSmpData(smpR, smpR->length + samples, true))
 	{
 		drawSamplingBufferFlag = false;
 		outOfMemoryFlag = true;
@@ -41,254 +51,243 @@
 		return;
 	}
 
-	s->origPek = newPtr;
-	s->pek = s->origPek + SMP_DAT_OFFSET;
+	const int16_t *src16 = (int16_t *)stream;
+	int16_t *dst16_L = (int16_t *)smpL->dataPtr + smpL->length;
+	int16_t *dst16_R = (int16_t *)smpR->dataPtr + smpR->length;
 
-	memcpy(&s->pek[s->len], stream, len);
+	for (int32_t i = 0; i < samples; i++)
+	{
+		dst16_L[i] = *src16++;
+		dst16_R[i] = *src16++;
+	}
 
-	s->len += len;
-	if (s->len > MAX_SAMPLE_LEN) // length overflow
+	smpL->length += samples;
+	smpR->length += samples;
+
+	if (smpL->length > MAX_SAMPLE_LEN) // length overflow
 	{
-		s->len -= len;
+		smpL->length -= samples;
+		smpR->length -= samples;
 		noMoreRoomFlag = true;
 		return;
 	}
 
-	bytesSampled += len;
-	if (bytesSampled >= samplingBufferBytes)
+	currSampleLen = smpL->length;
+
+	// if we have gathared enough samples, fill the current display buffer
+
+	samplesSampled += samples;
+	if (samplesSampled >= PREVIEW_SAMPLES)
 	{
-		bytesSampled -= samplingBufferBytes;
+		samplesSampled -= PREVIEW_SAMPLES;
 
-		currSampleLen = s->len - samplingBufferBytes;
+		if (oldPreviewSmpPos > 0)
+		{
+			int16_t *dstL = previewBufL[currPreviewBufNum^1];
+			int16_t *dstR = previewBufR[currPreviewBufNum^1];
+			const int16_t *srcL = (int16_t *)smpL->dataPtr + oldPreviewSmpPos;
+			const int16_t *srcR = (int16_t *)smpR->dataPtr + oldPreviewSmpPos;
 
-		// fill display buffer
-		memcpy(currWriteBuf, &s->pek[currSampleLen], samplingBufferBytes);
+			memcpy(dstL, srcL, PREVIEW_SAMPLES * sizeof (int16_t));
+			memcpy(dstR, srcR, PREVIEW_SAMPLES * sizeof (int16_t));
 
-		// swap write buffer (double-buffering)
-		if (currWriteBuf == displayBuffer1)
-			currWriteBuf = displayBuffer2;
-		else
-			currWriteBuf = displayBuffer1;
+			drawSamplingBufferFlag = true;
+		}
 
-		drawSamplingBufferFlag = true;
+		oldPreviewSmpPos = smpL->length - PREVIEW_SAMPLES;
 	}
 
 	(void)userdata;
 }
 
-void stopSampling(void)
+static void SDLCALL monoSamplingCallback(void *userdata, Uint8 *stream, int len)
 {
-	int8_t *newPtr;
-	int16_t *dst16, *src16;
-	int32_t i, len;
+	const int32_t samples = len >> 1;
+	if (instr[editor.curInstr] == NULL || samples < 0 || samples > samplingBufferSize)
+		return;
 
-	resumeAudio();
-	mouseAnimOff();
-
-	SDL_CloseAudioDevice(recordDev);
-	editor.samplingAudioFlag = false;
-
-	sampleTyp *currSmp = NULL;
-	sampleTyp *nextSmp = NULL;
-
-	if (instr[editor.curInstr] != NULL)
-		currSmp = &instr[editor.curInstr]->samp[editor.curSmp];
-
-	if (sampleInStereo)
+	if (!reallocateSmpData(smpL, smpL->length + samples, true))
 	{
-		// read right channel data
-		
-		if (currSmp->pek != NULL && rightChSmpSlot != -1)
-		{
-			nextSmp = &instr[editor.curInstr]->samp[rightChSmpSlot];
-
-			nextSmp->origPek = (int8_t *)malloc((currSmp->len >> 1) + LOOP_FIX_LEN);
-			if (nextSmp->origPek != NULL)
-			{
-				nextSmp->pek = nextSmp->origPek + SMP_DAT_OFFSET;
-				nextSmp->len = currSmp->len >> 1;
-
-				src16 = (int16_t *)currSmp->pek;
-				dst16 = (int16_t *)nextSmp->pek;
-
-				len = nextSmp->len >> 1;
-				for (i = 0; i < len; i++)
-					dst16[i] = src16[(i << 1) + 1];
-			}
-			else
-			{
-				freeSample(editor.curInstr, rightChSmpSlot);
-			}
-
-			currSmp->len >>= 1;
-
-			// read left channel data by skipping every other sample
-
-			dst16 = (int16_t *)currSmp->pek;
-
-			len = currSmp->len >> 1;
-			for (i = 0; i < len; i++)
-				dst16[i] = dst16[i << 1];
-		}
+		drawSamplingBufferFlag = false;
+		outOfMemoryFlag = true;
+		return;
 	}
 
-	if (currSmp->origPek != NULL)
-	{
-		newPtr = (int8_t *)realloc(currSmp->origPek, currSmp->len + LOOP_FIX_LEN);
-		if (newPtr != NULL)
-		{
-			currSmp->origPek = newPtr;
-			currSmp->pek = currSmp->origPek + SMP_DAT_OFFSET;
-		}
+	const int16_t *src16 = (int16_t *)stream;
+	int16_t *dst16 = (int16_t *)smpL->dataPtr + smpL->length;
+	memcpy(dst16, src16, samples * sizeof (int16_t));
 
-		fixSample(currSmp);
-	}
-	else
+	smpL->length += samples;
+	if (smpL->length > MAX_SAMPLE_LEN) // length overflow
 	{
-		freeSample(editor.curInstr, editor.curSmp);
+		smpL->length -= samples;
+		noMoreRoomFlag = true;
+		return;
 	}
 
-	if (nextSmp != NULL && nextSmp->origPek != NULL)
-		fixSample(nextSmp);
+	// if we have gathared enough samples, fill the current display buffer
 
-	updateSampleEditorSample();
-	editor.updateCurInstr = true;
-}
-
-static uint8_t getDispBuffPeakMono(const int16_t *smpData, int32_t smpNum)
-{
-	uint32_t max = 0;
-	for (int32_t i = 0; i < smpNum; i++)
+	samplesSampled += samples;
+	if (samplesSampled >= PREVIEW_SAMPLES)
 	{
-		const int16_t smp16 = smpData[i];
+		samplesSampled -= PREVIEW_SAMPLES;
 
-		const uint32_t smpAbs = ABS(smp16);
-		if (smpAbs > max)
-			max = smpAbs;
+		if (oldPreviewSmpPos > 0)
+		{
+			int16_t *dst = previewBufL[currPreviewBufNum^1];
+			const int16_t *src = (int16_t *)smpL->dataPtr + oldPreviewSmpPos;
+			memcpy(dst, src, PREVIEW_SAMPLES * sizeof (int16_t));
+
+			drawSamplingBufferFlag = true;
+		}
+
+		oldPreviewSmpPos = smpL->length - PREVIEW_SAMPLES;
 	}
 
-	max = (max * SAMPLE_AREA_HEIGHT) >> 16;
-	if (max > 76)
-		max = 76;
+	currSampleLen = smpL->length;
 
-	return (uint8_t)max;
+	(void)userdata;
 }
 
-static uint8_t getDispBuffPeakLeft(const int16_t *smpData, int32_t smpNum)
+void stopSampling(void)
 {
-	smpNum <<= 1;
+	resumeAudio();
+	mouseAnimOff();
 
-	uint32_t max = 0;
-	for (int32_t i = 0; i < smpNum; i += 2)
-	{
-		const int16_t smp16 = smpData[i];
+	SDL_CloseAudioDevice(recordDev);
 
-		const uint32_t smpAbs = ABS(smp16);
-		if (smpAbs > max)
-			max = smpAbs;
-	}
+	if (smpL != NULL) fixSample(smpL);
+	if (smpR != NULL) fixSample(smpR);
 
-	max = (max * SAMPLE_AREA_HEIGHT) >> (16 + 1);
-	if (max > 38)
-		max = 38;
+	editor.samplingAudioFlag = false;
 
-	return (uint8_t)max;
+	updateSampleEditorSample();
+	editor.updateCurInstr = true;
 }
 
-static uint8_t getDispBuffPeakRight(const int16_t *smpData, int32_t smpNum)
+static void getMinMax16(const int16_t *p, uint32_t position, uint32_t scanLen, int16_t *min16, int16_t *max16)
 {
-	smpNum <<= 1;
+	int16_t minVal =  32767;
+	int16_t maxVal = -32768;
 
-	uint32_t max = 0;
-	for (int32_t i = 0; i < smpNum; i += 2)
-	{
-		const int16_t smp16 = smpData[i];
+	assert(position+scanLen <= PREVIEW_SAMPLES);
 
-		const uint32_t smpAbs = ABS(smp16);
-		if (smpAbs > max)
-			max = smpAbs;
+	const int16_t *ptr16 = (const int16_t *)p + position;
+	for (uint32_t i = 0; i < scanLen; i++)
+	{
+		const int16_t smp16 = ptr16[i];
+		if (smp16 < minVal) minVal = smp16;
+		if (smp16 > maxVal) maxVal = smp16;
 	}
 
-	max = (max * SAMPLE_AREA_HEIGHT) >> (16 + 1);
-	if (max > 38)
-		max = 38;
-
-	return (uint8_t)max;
+	*min16 = minVal;
+	*max16 = maxVal;
 }
 
-static inline int32_t scrPos2SmpBufPos(int32_t x) // x = 0..SAMPLE_AREA_WIDTH
+static int32_t scr2BufPos(int32_t x)
 {
-	return (x * ((SAMPLING_BUFFER_SIZE << 16) / SAMPLE_AREA_WIDTH)) >> 16;
+	const double dXScaleMul = PREVIEW_SAMPLES / (double)SAMPLE_AREA_WIDTH;
+	return (int32_t)(x * dXScaleMul);
 }
 
 static void drawSamplingPreview(void)
 {
-	uint8_t smpAbs;
-	int16_t *readBuf;
-	uint16_t x;
-	int32_t smpIdx, smpNum;
-	uint32_t *centerPtrL, *centerPtrR;
+	int16_t min, max;
 
-	const uint32_t pixVal = video.palette[PAL_PATTEXT];
+	// clear sample data area
+	memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t));
 
-	// select buffer currently not being written to (double-buffering)
-	if (currWriteBuf == displayBuffer1)
-		readBuf = displayBuffer2;
-	else
-		readBuf = displayBuffer1;
-
-	if (sampleInStereo)
+	if (sampleInStereo) // stereo sampling
 	{
-		// stereo sampling
+		const int16_t *smpDataL = previewBufL[currPreviewBufNum];
+		const int16_t *smpDataR = previewBufR[currPreviewBufNum];
 
-		const uint16_t centerL = SAMPLE_AREA_Y_CENTER - (SAMPLE_AREA_HEIGHT / 4);
-		const uint16_t centerR = SAMPLE_AREA_Y_CENTER + (SAMPLE_AREA_HEIGHT / 4);
+		int32_t oldMinL = SAMPLE_L_CENTER;
+		int32_t oldMaxL = SAMPLE_L_CENTER;
+		int32_t oldMinR = SAMPLE_R_CENTER;
+		int32_t oldMaxR = SAMPLE_R_CENTER;
 
-		centerPtrL = &video.frameBuffer[centerL*SCREEN_W];
-		centerPtrR = &video.frameBuffer[centerR*SCREEN_W];
+		hLine(0, SAMPLE_L_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line
+		hLine(0, SAMPLE_R_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line
 
-		for (x = 0; x < SAMPLE_AREA_WIDTH; x++)
+		for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++)
 		{
-			smpIdx = scrPos2SmpBufPos(x);
-			smpNum = scrPos2SmpBufPos(x+1) - smpIdx;
+			int32_t smpIdx = scr2BufPos(x+0);
+			int32_t smpNum = scr2BufPos(x+1) - smpIdx;
 
-			if (smpIdx+smpNum >= SAMPLING_BUFFER_SIZE)
-				smpNum = SAMPLING_BUFFER_SIZE - smpIdx;
+			if (smpIdx+smpNum > PREVIEW_SAMPLES)
+				smpNum = PREVIEW_SAMPLES-smpIdx;
 
-			// left channel samples
-			smpAbs = getDispBuffPeakLeft(&readBuf[(smpIdx * 2) + 0], smpNum);
-			if (smpAbs == 0)
-				centerPtrL[x] = pixVal;
-			else
-				vLine(x, centerL - smpAbs, (smpAbs * 2) + 1, PAL_PATTEXT);
+			if (smpNum > 0)
+			{
+				// left channel
+				getMinMax16(smpDataL, smpIdx, smpNum, &min, &max);
+				min = SAMPLE_L_CENTER - ((min * STEREO_SAMPLE_HEIGHT) >> 16);
+				max = SAMPLE_L_CENTER - ((max * STEREO_SAMPLE_HEIGHT) >> 16);
 
-			// right channel samples
-			smpAbs = getDispBuffPeakRight(&readBuf[(smpIdx * 2) + 1], smpNum);
-			if (smpAbs == 0)
-				centerPtrR[x] = pixVal;
-			else
-				vLine(x, centerR - smpAbs, (smpAbs * 2) + 1, PAL_PATTEXT);
+				if (x != 0)
+				{
+					if (min > oldMaxL) sampleLine(x-1, x, oldMaxL, min);
+					if (max < oldMinL) sampleLine(x-1, x, oldMinL, max);
+				}
+
+				sampleLine(x, x, max, min);
+
+				oldMinL = min;
+				oldMaxL = max;
+
+				// right channel
+				getMinMax16(smpDataR, smpIdx, smpNum, &min, &max);
+				min = SAMPLE_R_CENTER - ((min * STEREO_SAMPLE_HEIGHT) >> 16);
+				max = SAMPLE_R_CENTER - ((max * STEREO_SAMPLE_HEIGHT) >> 16);
+
+				if (x != 0)
+				{
+					if (min > oldMaxR) sampleLine(x-1, x, oldMaxR, min);
+					if (max < oldMinR) sampleLine(x-1, x, oldMinR, max);
+				}
+
+				sampleLine(x, x, max, min);
+
+				oldMinR = min;
+				oldMaxR = max;
+			}
 		}
 	}
-	else
+	else // mono sampling
 	{
-		// mono sampling
+		const int16_t *smpData = previewBufL[currPreviewBufNum];
 
-		centerPtrL = &video.frameBuffer[SAMPLE_AREA_Y_CENTER * SCREEN_W];
+		int32_t oldMin = SAMPLE_CENTER;
+		int32_t oldMax = SAMPLE_CENTER;
 
-		for (x = 0; x < SAMPLE_AREA_WIDTH; x++)
+		hLine(0, SAMPLE_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line
+
+		for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++)
 		{
-			smpIdx = scrPos2SmpBufPos(x);
-			smpNum = scrPos2SmpBufPos(x+1) - smpIdx;
+			int32_t smpIdx = scr2BufPos(x+0);
+			int32_t smpNum = scr2BufPos(x+1) - smpIdx;
 
-			if (smpIdx+smpNum >= SAMPLING_BUFFER_SIZE)
-				smpNum = SAMPLING_BUFFER_SIZE - smpIdx;
+			if (smpIdx+smpNum > PREVIEW_SAMPLES)
+				smpNum = PREVIEW_SAMPLES-smpIdx;
 
-			smpAbs = getDispBuffPeakMono(&readBuf[smpIdx], smpNum);
-			if (smpAbs == 0)
-				centerPtrL[x] = pixVal;
-			else
-				vLine(x, SAMPLE_AREA_Y_CENTER - smpAbs, (smpAbs * 2) + 1, PAL_PATTEXT);
+			if (smpNum > 0)
+			{
+				getMinMax16(smpData, smpIdx, smpNum, &min, &max);
+				min = SAMPLE_CENTER - ((min * MONO_SAMPLE_HEIGHT) >> 16);
+				max = SAMPLE_CENTER - ((max * MONO_SAMPLE_HEIGHT) >> 16);
+
+				if (x != 0)
+				{
+					if (min > oldMax) sampleLine(x-1, x, oldMax, min);
+					if (max < oldMin) sampleLine(x-1, x, oldMin, max);
+				}
+
+				sampleLine(x, x, max, min);
+
+				oldMin = min;
+				oldMax = max;
+			}
 		}
 	}
 }
@@ -315,19 +314,13 @@
 	{
 		drawSamplingBufferFlag = false;
 
-		// clear sample data area
-		memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t));
-
 		drawSamplingPreview();
-
-		// clear and draw new sample length number
-		fillRect(536, 362, 56, 10, PAL_DESKTOP);
-
-		if (sampleInStereo)
-			hexOut(536, 362, PAL_FORGRND, currSampleLen >> 1, 8);
-		else
-			hexOut(536, 362, PAL_FORGRND, currSampleLen, 8);
+		currPreviewBufNum ^= 1;
 	}
+
+	// clear and draw new sample length number
+	fillRect(536, 362, 56, 10, PAL_DESKTOP);
+	hexOut(536, 362, PAL_FORGRND, currSampleLen, 8);
 }
 
 void startSampling(void)
@@ -346,23 +339,20 @@
 		return;
 
 	sampleInStereo = (result == 2);
-	samplingBufferBytes = sampleInStereo ? (SAMPLING_BUFFER_SIZE * 4) : (SAMPLING_BUFFER_SIZE * 2);
-
 	mouseAnimOn();
 
 	switch (config.audioInputFreq)
 	{
-		case INPUT_FREQ_96KHZ: samplingRate = 96000; break;
-		case INPUT_FREQ_44KHZ: samplingRate = 44100; break;
-		default: samplingRate = 48000; break;
+		         case INPUT_FREQ_44KHZ: samplingRate = 44100; break;
+		default: case INPUT_FREQ_48KHZ: samplingRate = 48000; break;
+		         case INPUT_FREQ_96KHZ: samplingRate = 96000; break;
 	}
 
 	memset(&want, 0, sizeof (SDL_AudioSpec));
 	want.freq = samplingRate;
 	want.format = AUDIO_S16;
-	want.channels = 1 + sampleInStereo;
-	want.callback = samplingCallback;
-	want.userdata = NULL;
+	want.channels = sampleInStereo ? 2 : 1;
+	want.callback = sampleInStereo ? stereoSamplingCallback : monoSamplingCallback;
 	want.samples = SAMPLING_BUFFER_SIZE;
 
 	recordDev = SDL_OpenAudioDevice(audio.currInputDevice, true, &want, &have, 0);
@@ -372,63 +362,60 @@
 		return;
 	}
 
+	samplingRate = have.freq;
+	samplingBufferSize = have.samples;
+
 	pauseAudio();
 
 	if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr))
 	{
-		resumeAudio();
+		stopSampling();
 		okBox(0, "System message", "Not enough memory!");
 		return;
 	}
 
-	sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp];
+	if (sampleInStereo && editor.curSmp+1 >= MAX_SMP_PER_INST)
+	{
+		stopSampling();
+		okBox(0, "System message", "Error: No free sample slot for the right channel!");
+		return;
+	}
 
-	// wipe current sample and prepare it
-	freeSample(editor.curInstr, editor.curSmp);
-	s->typ |= 16; // we always sample in 16-bit
+	smpL = &instr[editor.curInstr]->smp[editor.curSmp];
+	freeSample(editor.curInstr, editor.curSmp); // also sets pan to 128 and vol to 64
+	tuneSample(smpL, samplingRate, audio.linearPeriodsFlag);
+	smpL->flags |= SAMPLE_16BIT;
 
-	tuneSample(s, samplingRate, audio.linearPeriodsFlag); // tune sample (relTone/finetune) to the sampling frequency we obtained
-
 	if (sampleInStereo)
 	{
-		strcpy(s->name, "Left sample");
-		s->pan = 0;
+		smpR = &instr[editor.curInstr]->smp[editor.curSmp+1];
+		freeSample(editor.curInstr, editor.curSmp+1); // also sets pan to 128 and vol to 64
+		tuneSample(smpR, samplingRate, audio.linearPeriodsFlag);
+		smpR->flags |= SAMPLE_16BIT;
 
-		if (editor.curSmp+1 < MAX_SMP_PER_INST)
-			rightChSmpSlot = editor.curSmp+1;
-		else
-			rightChSmpSlot = -1;
+		strcpy(smpL->name, "Left sample");
+		smpL->panning = 0;
 
-		if (rightChSmpSlot != -1)
-		{
-			// wipe current sample and prepare it
-			freeSample(editor.curInstr, rightChSmpSlot);
-			sampleTyp *nextSmp = &instr[editor.curInstr]->samp[rightChSmpSlot];
-
-			strcpy(nextSmp->name, "Right sample");
-			nextSmp->typ |= 16; // we always sample in 16-bit
-			nextSmp->pan = 255;
-
-			tuneSample(nextSmp, samplingRate, audio.linearPeriodsFlag); // tune sample (relTone/finetune) to the sampling frequency we obtained
-		}
+		strcpy(smpR->name, "Right sample");
+		smpR->panning = 255;
 	}
 	else
 	{
-		strcpy(s->name, "Mono-mixed sample");
+		strcpy(smpL->name, "Mono sample");
 	}
 
+	memset(previewBufL, 0, sizeof (previewBufL));
+	memset(previewBufR, 0, sizeof (previewBufR));
+	currPreviewBufNum = 0;
+
+	samplesSampled = currSampleLen = oldPreviewSmpPos = 0;
+	noMoreRoomFlag = outOfMemoryFlag = drawSamplingBufferFlag = false;
+
 	updateSampleEditorSample();
 	updateSampleEditor();
 	setSongModifiedFlag();
 
-	currWriteBuf = displayBuffer1;
-	memset(displayBuffer1, 0, sizeof (displayBuffer1));
-	memset(displayBuffer2, 0, sizeof (displayBuffer2));
-
 	editor.samplingAudioFlag = true;
-	bytesSampled = 0;
-	currSampleLen = 0;
-
 	SDL_PauseAudioDevice(recordDev, false);
 #endif
 }
--- a/src/ft2_scopedraw.c
+++ /dev/null
@@ -1,402 +1,0 @@
-#include "ft2_scopes.h"
-#include "ft2_scopedraw.h"
-#include "ft2_video.h"
-#include "ft2_palette.h"
-
-/* ----------------------------------------------------------------------- */
-/*                          SCOPE DRAWING MACROS                           */
-/* ----------------------------------------------------------------------- */
-
-#define SCOPE_REGS_NO_LOOP \
-	const int32_t vol = s->vol; \
-	const int32_t end = s->end; \
-	const uint32_t delta = s->drawDelta; \
-	const uint32_t color = video.palette[PAL_PATTEXT]; \
-	const uint32_t width = x + w; \
-	int32_t sample; \
-	int32_t pos = s->pos; \
-	int32_t posFrac = 0; \
-
-#define SCOPE_REGS_LOOP \
-	const int32_t vol = s->vol; \
-	const int32_t end = s->end; \
-	const int32_t loopStart = s->loopStart; \
-	const int32_t loopLength = s->loopLength; \
-	const uint32_t delta = s->drawDelta; \
-	const uint32_t color = video.palette[PAL_PATTEXT]; \
-	const uint32_t width = x + w; \
-	int32_t sample; \
-	int32_t pos = s->pos; \
-	int32_t posFrac = 0; \
-
-#define SCOPE_REGS_PINGPONG \
-	const int32_t vol = s->vol; \
-	const int32_t end = s->end; \
-	const int32_t loopStart = s->loopStart; \
-	const int32_t loopLength = s->loopLength; \
-	const uint32_t delta = s->drawDelta; \
-	const uint32_t color = video.palette[PAL_PATTEXT]; \
-	const uint32_t width = x + w; \
-	int32_t sample; \
-	int32_t pos = s->pos; \
-	int32_t posFrac = 0; \
-	int32_t direction = s->direction; \
-
-#define LINED_SCOPE_REGS_NO_LOOP \
-	const int32_t vol = s->vol; \
-	const int32_t end = s->end; \
-	const uint32_t delta = s->drawDelta; \
-	const uint32_t width = (x + w) - 1; \
-	int32_t sample; \
-	int32_t y1, y2; \
-	int32_t pos = s->pos; \
-	int32_t posFrac = 0; \
-
-#define LINED_SCOPE_REGS_LOOP \
-	const int32_t vol = s->vol; \
-	const int32_t end = s->end; \
-	const int32_t loopStart = s->loopStart; \
-	const int32_t loopLength = s->loopLength; \
-	const uint32_t delta = s->drawDelta; \
-	const uint32_t width = (x + w) - 1; \
-	int32_t sample; \
-	int32_t y1, y2; \
-	int32_t pos = s->pos; \
-	int32_t posFrac = 0; \
-
-#define LINED_SCOPE_REGS_PINGPONG \
-	const int32_t vol = s->vol; \
-	const int32_t end = s->end; \
-	const int32_t loopStart = s->loopStart; \
-	const int32_t loopLength = s->loopLength; \
-	const uint32_t delta = s->drawDelta; \
-	const uint32_t width = (x + w) - 1; \
-	int32_t sample; \
-	int32_t y1, y2; \
-	int32_t pos = s->pos; \
-	int32_t posFrac = 0; \
-	int32_t direction = s->direction; \
-
-#define SCOPE_GET_SMP8 \
-	if (s->active) \
-	{ \
-		assert(pos >= 0 && pos < end); \
-		sample = (s->base8[pos] * vol) >> 8; \
-	} \
-	else \
-	{ \
-		sample = 0; \
-	} \
-
-#define SCOPE_GET_SMP16 \
-	if (s->active) \
-	{ \
-		assert(pos >= 0 && pos < end); \
-		sample = (int8_t)((s->base16[pos] * vol) >> 16); \
-	} \
-	else \
-	{ \
-		sample = 0; \
-	} \
-
-#define SCOPE_UPDATE_DRAWPOS \
-	posFrac += delta; \
-	pos += posFrac >> SCOPE_DRAW_FRAC_BITS; \
-	posFrac &= SCOPE_DRAW_FRAC_MASK; \
-
-#define SCOPE_UPDATE_DRAWPOS_PINGPONG \
-	posFrac += delta; \
-	pos += (int32_t)(posFrac >> SCOPE_DRAW_FRAC_BITS) * direction; \
-	posFrac &= SCOPE_DRAW_FRAC_MASK; \
-
-#define SCOPE_DRAW_SMP \
-	video.frameBuffer[((lineY - sample) * SCREEN_W) + x] = color;
-
-#define LINED_SCOPE_PREPARE_SMP8 \
-	SCOPE_GET_SMP8 \
-	y1 = lineY - sample; \
-	SCOPE_UPDATE_DRAWPOS
-
-#define LINED_SCOPE_PREPARE_SMP16 \
-	SCOPE_GET_SMP16 \
-	y1 = lineY - sample; \
-	SCOPE_UPDATE_DRAWPOS
-
-#define LINED_SCOPE_DRAW_SMP \
-	y2 = lineY - sample; \
-	scopeLine(x, y1, y2); \
-	y1 = y2; \
-
-#define SCOPE_HANDLE_POS_NO_LOOP \
-	if (pos >= end) \
-		s->active = false; \
-
-#define SCOPE_HANDLE_POS_LOOP \
-	if (pos >= end) \
-	{ \
-		if (loopLength >= 2) \
-			pos = loopStart + ((pos - end) % loopLength); \
-		else \
-			pos = loopStart; \
-		\
-		assert(pos >= loopStart && pos < end); \
-	} \
-
-#define SCOPE_HANDLE_POS_PINGPONG \
-	if (direction == -1 && pos < loopStart) \
-	{ \
-		direction = 1; /* change direction to forwards */ \
-		\
-		if (loopLength >= 2) \
-			pos = loopStart + ((loopStart - pos - 1) % loopLength); \
-		else \
-			pos = loopStart; \
-		\
-		assert(pos >= loopStart && pos < end); \
-	} \
-	else if (pos >= end) \
-	{ \
-		direction = -1; /* change direction to backwards */ \
-		\
-		if (loopLength >= 2) \
-			pos = (end - 1) - ((pos - end) % loopLength); \
-		else \
-			pos = end - 1; \
-		\
-		assert(pos >= loopStart && pos < end); \
-	} \
-	assert(pos >= 0); \
-
-static void scopeLine(int32_t x1, int32_t y1, int32_t y2)
-{
-	const int32_t dy = y2 - y1;
-	const int32_t sy = SGN(dy);
-	const uint32_t color = video.palette[PAL_PATTEXT];
-	const int32_t pitch = sy * SCREEN_W;
-
-	uint32_t *dst32 = &video.frameBuffer[(y1 * SCREEN_W) + x1];
-
-	*dst32 = color; // set first pixel
-
-	int32_t ay = ABS(dy);
-	if (ay <= 1)
-	{
-		if (ay != 0)
-			dst32 += pitch;
-
-		*++dst32 = color;
-		return;
-	}
-
-	int32_t d = 1 - ay;
-
-	ay <<= 1;
-	while (y1 != y2)
-	{
-		if (d >= 0)
-		{
-			d -= ay;
-			dst32++;
-		}
-
-		y1 += sy;
-		d += 2;
-
-		dst32 += pitch;
-		*dst32 = color;
-	}
-}
-
-/* ----------------------------------------------------------------------- */
-/*                         SCOPE DRAWING ROUTINES                          */
-/* ----------------------------------------------------------------------- */
-
-static void scopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	SCOPE_REGS_NO_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP8
-		SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_NO_LOOP
-	}
-}
-
-static void scopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	SCOPE_REGS_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP8
-		SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_LOOP
-	}
-}
-
-static void scopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	SCOPE_REGS_PINGPONG
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP8
-		SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS_PINGPONG
-		SCOPE_HANDLE_POS_PINGPONG
-	}
-}
-
-static void scopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	SCOPE_REGS_NO_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP16
-		SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_NO_LOOP
-	}
-}
-
-static void scopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	SCOPE_REGS_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP16
-		SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_LOOP
-	}
-}
-
-static void scopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	SCOPE_REGS_PINGPONG
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP16
-		SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS_PINGPONG
-		SCOPE_HANDLE_POS_PINGPONG
-	}
-}
-
-/* ----------------------------------------------------------------------- */
-/*                      LINED SCOPE DRAWING ROUTINES                       */
-/* ----------------------------------------------------------------------- */
-
-static void linedScopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	LINED_SCOPE_REGS_NO_LOOP
-	LINED_SCOPE_PREPARE_SMP8
-	SCOPE_HANDLE_POS_NO_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP8
-		LINED_SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_NO_LOOP
-	}
-}
-
-static void linedScopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	LINED_SCOPE_REGS_LOOP
-	LINED_SCOPE_PREPARE_SMP8
-	SCOPE_HANDLE_POS_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP8
-		LINED_SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_LOOP
-	}
-}
-
-static void linedScopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	LINED_SCOPE_REGS_PINGPONG
-	LINED_SCOPE_PREPARE_SMP8
-	SCOPE_HANDLE_POS_PINGPONG
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP8
-		LINED_SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS_PINGPONG
-		SCOPE_HANDLE_POS_PINGPONG
-	}
-}
-
-static void linedScopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	LINED_SCOPE_REGS_NO_LOOP
-	LINED_SCOPE_PREPARE_SMP16
-	SCOPE_HANDLE_POS_NO_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP16
-		LINED_SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_NO_LOOP
-	}
-}
-
-static void linedScopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	LINED_SCOPE_REGS_LOOP
-	LINED_SCOPE_PREPARE_SMP16
-	SCOPE_HANDLE_POS_LOOP
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP16
-		LINED_SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS
-		SCOPE_HANDLE_POS_LOOP
-	}
-}
-
-static void linedScopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
-{
-	LINED_SCOPE_REGS_PINGPONG
-	LINED_SCOPE_PREPARE_SMP16
-	SCOPE_HANDLE_POS_PINGPONG
-
-	for (; x < width; x++)
-	{
-		SCOPE_GET_SMP16
-		LINED_SCOPE_DRAW_SMP
-		SCOPE_UPDATE_DRAWPOS_PINGPONG
-		SCOPE_HANDLE_POS_PINGPONG
-	}
-}
-
-// -----------------------------------------------------------------------
-
-const scopeDrawRoutine scopeDrawRoutineTable[12] =
-{
-	(scopeDrawRoutine)scopeDrawNoLoop_8bit,
-	(scopeDrawRoutine)scopeDrawLoop_8bit,
-	(scopeDrawRoutine)scopeDrawPingPong_8bit,
-	(scopeDrawRoutine)scopeDrawNoLoop_16bit,
-	(scopeDrawRoutine)scopeDrawLoop_16bit,
-	(scopeDrawRoutine)scopeDrawPingPong_16bit,
-	(scopeDrawRoutine)linedScopeDrawNoLoop_8bit,
-	(scopeDrawRoutine)linedScopeDrawLoop_8bit,
-	(scopeDrawRoutine)linedScopeDrawPingPong_8bit,
-	(scopeDrawRoutine)linedScopeDrawNoLoop_16bit,
-	(scopeDrawRoutine)linedScopeDrawLoop_16bit,
-	(scopeDrawRoutine)linedScopeDrawPingPong_16bit
-};
--- a/src/ft2_scopedraw.h
+++ /dev/null
@@ -1,8 +1,0 @@
-#pragma once
-
-#include <stdint.h>
-#include "ft2_scopes.h"
-
-typedef void (*scopeDrawRoutine)(const scope_t *, uint32_t, uint32_t, uint32_t);
-
-extern const scopeDrawRoutine scopeDrawRoutineTable[12]; // ft2_scopedraw.c
--- a/src/ft2_scopes.c
+++ /dev/null
@@ -1,641 +1,0 @@
-// for finding memory leaks in debug mode with Visual Studio
-#if defined _DEBUG && defined _MSC_VER
-#include <crtdbg.h>
-#endif
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <math.h> // modf()
-#ifndef _WIN32
-#include <unistd.h> // usleep()
-#endif
-#include "ft2_header.h"
-#include "ft2_events.h"
-#include "ft2_config.h"
-#include "ft2_audio.h"
-#include "ft2_gui.h"
-#include "ft2_midi.h"
-#include "ft2_bmp.h"
-#include "ft2_scopes.h"
-#include "ft2_mouse.h"
-#include "ft2_video.h"
-#include "ft2_scopedraw.h"
-#include "ft2_tables.h"
-#include "ft2_structs.h"
-
-static volatile bool scopesUpdatingFlag, scopesDisplayingFlag;
-static uint32_t scopeTimeLen, scopeTimeLenFrac;
-static uint64_t timeNext64, timeNext64Frac;
-static volatile scope_t scope[MAX_VOICES];
-static SDL_Thread *scopeThread;
-
-lastChInstr_t lastChInstr[MAX_VOICES]; // global
-
-void resetCachedScopeVars(void)
-{
-	volatile scope_t *sc = scope;
-	for (int32_t i = 0; i < MAX_VOICES; i++, sc++)
-	{
-		sc->dOldHz = -1.0;
-		sc->oldDelta = 0;
-		sc->oldDrawDelta = 0;
-	}
-}
-
-int32_t getSamplePosition(uint8_t ch)
-{
-	if (ch >= song.antChn)
-		return -1;
-
-	volatile scope_t *sc = &scope[ch];
-
-	// cache some stuff
-	volatile bool active = sc->active;
-	volatile int32_t pos = sc->pos;
-	volatile int32_t end = sc->end;
-	volatile bool sampleIs16Bit = sc->sampleIs16Bit;
-
-	if (!active || end == 0)
-		return -1;
-
-	if (pos >= 0 && pos < end)
-	{
-		if (sampleIs16Bit)
-			pos <<= 1;
-
-		return pos;
-	}
-
-	return -1; // not active or overflown
-}
-
-void stopAllScopes(void)
-{
-	// wait for scopes to finish updating
-	while (scopesUpdatingFlag);
-	
-	volatile scope_t *sc = scope;
-	for (int32_t i = 0; i < MAX_VOICES; i++, sc++)
-		sc->active = false;
-
-	// wait for scope displaying to be done (safety)
-	while (scopesDisplayingFlag);
-}
-
-// toggle mute
-static void setChannel(int32_t nr, bool on)
-{
-	stmTyp *ch = &stm[nr];
-
-	ch->stOff = !on;
-	if (ch->stOff)
-	{
-		ch->effTyp = 0;
-		ch->eff = 0;
-		ch->realVol = 0;
-		ch->outVol = 0;
-		ch->oldVol = 0;
-		ch->dFinalVol = 0.0;
-		ch->outPan = 128;
-		ch->oldPan = 128;
-		ch->finalPan = 128;
-		ch->status = IS_Vol;
-
-		ch->envSustainActive = false; // non-FT2 bug fix for stuck piano keys
-	}
-
-	scope[nr].wasCleared = false;
-}
-
-static void drawScopeNumber(uint16_t scopeXOffs, uint16_t scopeYOffs, uint8_t channel, bool outline)
-{
-	scopeXOffs++;
-	scopeYOffs++;
-	channel++;
-
-	if (outline)
-	{
-		if (channel < 10) // one digit?
-		{
-			charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + channel);
-		}
-		else
-		{
-			charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[channel]);
-			charOutOutlined(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[channel]);
-		}
-	}
-	else
-	{
-		if (channel < 10) // one digit?
-		{
-			charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + channel);
-		}
-		else
-		{
-			charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[channel]);
-			charOut(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[channel]);
-		}
-	}
-}
-
-static void redrawScope(int32_t ch)
-{
-	int32_t i;
-
-	int32_t chansPerRow = (uint32_t)song.antChn >> 1;
-	int32_t chanLookup = chansPerRow - 1;
-	const uint16_t *scopeLens = scopeLenTab[chanLookup];
-
-	// get x,y,len for scope according to channel (we must do it this way since 'len' can differ!)
-
-	uint16_t x = 2;
-	uint16_t y = 94;
-
-	uint16_t scopeLen = 0; // prevent compiler warning
-	for (i = 0; i < song.antChn; i++)
-	{
-		scopeLen = scopeLens[i];
-
-		if (i == chansPerRow) // did we reach end of row?
-		{
-			// yes, go one row down
-			x  = 2;
-			y += 39;
-		}
-
-		if (i == ch)
-			break;
-
-		// adjust position to next channel
-		x += scopeLen + 3;
-	}
-
-	drawFramework(x, y, scopeLen + 2, 38, FRAMEWORK_TYPE2);
-
-	// draw mute graphics if channel is muted
-	if (!editor.chnMode[i])
-	{
-		const uint16_t muteGfxLen = scopeMuteBMP_Widths[chanLookup];
-		const uint16_t muteGfxX = x + ((scopeLen - muteGfxLen) >> 1);
-
-		blitFastClipX(muteGfxX, y + 6, bmp.scopeMute+scopeMuteBMP_Offs[chanLookup], 162, scopeMuteBMP_Heights[chanLookup], muteGfxLen);
-
-		if (config.ptnChnNumbers)
-			drawScopeNumber(x + 1, y + 1, (uint8_t)i, true);
-	}
-
-	scope[ch].wasCleared = false;
-}
-
-void refreshScopes(void)
-{
-	for (int32_t i = 0; i < MAX_VOICES; i++)
-		scope[i].wasCleared = false;
-}
-
-static void channelMode(int32_t chn)
-{
-	int32_t i;
-	
-	assert(chn < song.antChn);
-
-	bool m = mouse.leftButtonPressed && !mouse.rightButtonPressed;
-	bool m2 = mouse.rightButtonPressed && mouse.leftButtonPressed;
-
-	if (m2)
-	{
-		bool test = false;
-		for (i = 0; i < song.antChn; i++)
-		{
-			if (i != chn && !editor.chnMode[i])
-				test = true;
-		}
-
-		if (test)
-		{
-			for (i = 0; i < song.antChn; i++)
-				editor.chnMode[i] = true;
-		}
-		else
-		{
-			for (i = 0; i < song.antChn; i++)
-				editor.chnMode[i] = (i == chn);
-		}
-	}
-	else if (m)
-	{
-		editor.chnMode[chn] ^= 1;
-	}
-	else
-	{
-		if (editor.chnMode[chn])
-		{
-			config.multiRecChn[chn] ^= 1;
-		}
-		else
-		{
-			config.multiRecChn[chn] = true;
-			editor.chnMode[chn] = true;
-			m = true;
-		}
-	}
-
-	for (i = 0; i < song.antChn; i++)
-		setChannel(i, editor.chnMode[i]);
-
-	if (m2)
-	{
-		for (i = 0; i < song.antChn; i++)
-			redrawScope(i);
-	}
-	else
-	{
-		redrawScope(chn);
-	}
-}
-
-bool testScopesMouseDown(void)
-{
-	int32_t i;
-
-	if (!ui.scopesShown)
-		return false;
-
-	if (mouse.y >= 95 && mouse.y <= 169 && mouse.x >= 3 && mouse.x <= 288)
-	{
-		if (mouse.y > 130 && mouse.y < 134)
-			return true;
-
-		int32_t chansPerRow = (uint32_t)song.antChn >> 1;
-		const uint16_t *scopeLens = scopeLenTab[chansPerRow-1];
-
-		// find out if we clicked inside a scope
-		uint16_t x = 3;
-		for (i = 0; i < chansPerRow; i++)
-		{
-			if (mouse.x >= x && mouse.x < x+scopeLens[i])
-				break;
-
-			x += scopeLens[i]+3;
-		}
-
-		if (i == chansPerRow)
-			return true; // scope framework was clicked instead
-
-		int32_t chanToToggle = i;
-		if (mouse.y >= 134) // second row of scopes?
-			chanToToggle += chansPerRow; // yes, increase lookup offset
-
-		channelMode(chanToToggle);
-		return true;
-	}
-
-	return false;
-}
-
-static void scopeTrigger(int32_t ch, const sampleTyp *s, int32_t playOffset)
-{
-	scope_t tempState;
-
-	volatile scope_t *sc = &scope[ch];
-
-	int32_t length = s->len;
-	int32_t loopStart = s->repS;
-	int32_t loopLength = s->repL;
-	int32_t loopEnd = s->repS + s->repL;
-	uint8_t loopType = s->typ & 3;
-	bool sampleIs16Bit = (s->typ >> 4) & 1;
-
-	if (sampleIs16Bit)
-	{
-		assert(!(length & 1));
-		assert(!(loopStart & 1));
-		assert(!(loopLength & 1));
-		assert(!(loopEnd & 1));
-
-		length >>= 1;
-		loopStart >>= 1;
-		loopLength >>= 1;
-		loopEnd >>= 1;
-	}
-
-	if (s->pek == NULL || length < 1)
-	{
-		sc->active = false; // shut down scope (illegal parameters)
-		return;
-	}
-
-	if (loopLength < 1) // disable loop if loopLength is below 1
-		loopType = 0;
-
-	if (sampleIs16Bit)
-		tempState.base16 = (const int16_t *)s->pek;
-	else
-		tempState.base8 = s->pek;
-
-	tempState.sampleIs16Bit = sampleIs16Bit;
-	tempState.loopType = loopType;
-
-	tempState.direction = 1; // forwards
-	tempState.end = (loopType > 0) ? loopEnd : length;
-	tempState.loopStart = loopStart;
-	tempState.loopLength = loopLength;
-	tempState.pos = playOffset;
-	tempState.posFrac = 0;
-	
-	// if position overflows (f.ex. through 9xx command), shut down scopes
-	if (tempState.pos >= tempState.end)
-	{
-		sc->active = false;
-		return;
-	}
-
-	// these has to be copied so that they are not lost
-	tempState.wasCleared = sc->wasCleared;
-	tempState.delta = sc->delta;
-	tempState.oldDelta = sc->oldDelta;
-	tempState.drawDelta = sc->drawDelta;
-	tempState.oldDrawDelta = sc->oldDrawDelta;
-	tempState.dOldHz = sc->dOldHz;
-	tempState.vol = sc->vol;
-
-	tempState.active = true;
-
-	/* Update live scope now.
-	** In theory it -can- be written to in the middle of a cached read,
-	** then the read thread writes its own non-updated cached copy back and
-	** the trigger never happens. So far I have never seen it happen,
-	** so it's probably very rare. Yes, this is not good coding...
-	*/
-
-	*sc = tempState;
-}
-
-static void updateScopes(void)
-{
-	int32_t loopOverflowVal;
-
-	scopesUpdatingFlag = true;
-
-	volatile scope_t *sc = scope;
-	for (int32_t i = 0; i < song.antChn; i++, sc++)
-	{
-		scope_t s = *sc; // cache it
-		if (!s.active)
-			continue; // scope is not active, no need
-
-		// scope position update
-
-		s.posFrac += s.delta;
-		const int32_t wholeSamples = s.posFrac >> 32;
-		s.posFrac &= 0xFFFFFFFF;
-
-		if (s.direction == 1)
-			s.pos += wholeSamples; // forwards
-		else
-			s.pos -= wholeSamples; // backwards
-
-		// handle loop wrapping or sample end
-		if (s.direction == -1 && s.pos < s.loopStart) // sampling backwards (definitely pingpong loop)
-		{
-			s.direction = 1; // change direction to forwards
-
-			if (s.loopLength >= 2)
-				s.pos = s.loopStart + ((s.loopStart - s.pos - 1) % s.loopLength);
-			else
-				s.pos = s.loopStart;
-
-			assert(s.pos >= s.loopStart && s.pos < s.end);
-		}
-		else if (s.pos >= s.end)
-		{
-			if (s.loopLength >= 2)
-				loopOverflowVal = (s.pos - s.end) % s.loopLength;
-			else
-				loopOverflowVal = 0;
-
-			if (s.loopType == LOOP_DISABLED)
-			{
-				s.active = false;
-			}
-			else if (s.loopType == LOOP_FORWARD)
-			{
-				s.pos = s.loopStart + loopOverflowVal;
-				assert(s.pos >= s.loopStart && s.pos < s.end);
-			}
-			else // pingpong loop
-			{
-				s.direction = -1; // change direction to backwards
-				s.pos = (s.end - 1) - loopOverflowVal;
-				assert(s.pos >= s.loopStart && s.pos < s.end);
-			}
-		}
-		assert(s.pos >= 0);
-
-		*sc = s; // update scope state
-	}
-	scopesUpdatingFlag = false;
-}
-
-void drawScopes(void)
-{
-	scopesDisplayingFlag = true;
-	int32_t chansPerRow = (uint32_t)song.antChn >> 1;
-
-	const uint16_t *scopeLens = scopeLenTab[chansPerRow-1];
-	uint16_t scopeXOffs = 3;
-	uint16_t scopeYOffs = 95;
-	int16_t scopeLineY = 112;
-
-	for (int32_t i = 0; i < song.antChn; i++)
-	{
-		// if we reached the last scope on the row, go to first scope on the next row
-		if (i == chansPerRow)
-		{
-			scopeXOffs = 3;
-			scopeYOffs = 134;
-			scopeLineY = 151;
-		}
-
-		const uint16_t scopeDrawLen = scopeLens[i];
-		if (!editor.chnMode[i]) // scope muted (mute graphics blit()'ed elsewhere)
-		{
-			scopeXOffs += scopeDrawLen+3; // align x to next scope
-			continue;
-		}
-
-		const scope_t s = scope[i]; // cache scope to lower thread race condition issues
-		if (s.active && s.vol > 0 && !audio.locked)
-		{
-			// scope is active
-			scope[i].wasCleared = false;
-
-			// clear scope background
-			clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT);
-
-			// draw scope
-			bool linedScopesFlag = !!(config.specialFlags & LINED_SCOPES);
-			scopeDrawRoutineTable[(linedScopesFlag * 6) + (s.sampleIs16Bit * 3) + s.loopType](&s, scopeXOffs, scopeLineY, scopeDrawLen);
-		}
-		else
-		{
-			// scope is inactive
-			volatile scope_t *sc = &scope[i];
-			if (!sc->wasCleared)
-			{
-				// clear scope background
-				clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT);
-
-				// draw empty line
-				hLine(scopeXOffs, scopeLineY, scopeDrawLen, PAL_PATTEXT);
-
-				sc->wasCleared = true;
-			}
-		}
-
-		// draw channel numbering (if enabled)
-		if (config.ptnChnNumbers)
-			drawScopeNumber(scopeXOffs, scopeYOffs, (uint8_t)i, false);
-
-		// draw rec. symbol (if enabled)
-		if (config.multiRecChn[i])
-			blit(scopeXOffs + 1, scopeYOffs + 31, bmp.scopeRec, 13, 4);
-
-		scopeXOffs += scopeDrawLen+3; // align x to next scope
-	}
-
-	scopesDisplayingFlag = false;
-}
-
-void drawScopeFramework(void)
-{
-	drawFramework(0, 92, 291, 81, FRAMEWORK_TYPE1);
-	for (int32_t i = 0; i < song.antChn; i++)
-		redrawScope(i);
-}
-
-void handleScopesFromChQueue(chSyncData_t *chSyncData, uint8_t *scopeUpdateStatus)
-{
-	volatile scope_t *sc = scope;
-	syncedChannel_t *ch = chSyncData->channels;
-	for (int32_t i = 0; i < song.antChn; i++, sc++, ch++)
-	{
-		const uint8_t status = scopeUpdateStatus[i];
-
-		if (status & IS_Vol)
-			sc->vol = ((ch->vol * SCOPE_HEIGHT) + 128) >> 8; // rounded
-
-		if (status & IS_Period)
-		{
-			// use cached values when possible
-			if (ch->dHz != sc->dOldHz)
-			{
-				sc->dOldHz = ch->dHz;
-
-				const double dHz2ScopeDeltaMul = SCOPE_FRAC_SCALE / (double)SCOPE_HZ;
-				sc->oldDelta = (int64_t)((ch->dHz * dHz2ScopeDeltaMul) + 0.5); // Hz -> 32.32fp delta (rounded)
-
-				const double dRelativeHz = ch->dHz * (1.0 / (8363.0 / 2.0));
-				sc->oldDrawDelta = (int32_t)((dRelativeHz * SCOPE_DRAW_FRAC_SCALE) + 0.5); // Hz -> 13.19fp draw delta (rounded)
-			}
-
-			sc->delta = sc->oldDelta;
-			sc->drawDelta = sc->oldDrawDelta;
-		}
-
-		if (status & IS_NyTon)
-		{
-			if (instr[ch->instrNr] != NULL)
-			{
-				scopeTrigger(i, &instr[ch->instrNr]->samp[ch->sampleNr], ch->smpStartPos);
-
-				// set some stuff used by Smp. Ed. for sampling position line
-
-				if (ch->instrNr == 130 || (ch->instrNr == editor.curInstr && ch->sampleNr == editor.curSmp))
-					editor.curSmpChannel = (uint8_t)i;
-
-				lastChInstr[i].instrNr = ch->instrNr;
-				lastChInstr[i].sampleNr = ch->sampleNr;
-			}
-			else
-			{
-				// empty instrument, shut down scope
-				scope[i].active = false;
-				lastChInstr[i].instrNr = 255;
-				lastChInstr[i].sampleNr = 255;
-			}
-		}
-	}
-}
-
-static int32_t SDLCALL scopeThreadFunc(void *ptr)
-{
-	// this is needed for scope stability (confirmed)
-	SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
-
-	// set next frame time
-	timeNext64 = SDL_GetPerformanceCounter() + scopeTimeLen;
-	timeNext64Frac = scopeTimeLenFrac;
-
-	while (editor.programRunning)
-	{
-		editor.scopeThreadMutex = true;
-		updateScopes();
-		editor.scopeThreadMutex = false;
-
-		uint64_t time64 = SDL_GetPerformanceCounter();
-		if (time64 < timeNext64)
-		{
-			time64 = timeNext64 - time64;
-			if (time64 > INT32_MAX)
-				time64 = INT32_MAX;
-
-			const int32_t diff32 = (int32_t)time64;
-
-			// convert and round to microseconds
-			const int32_t time32 = (int32_t)((diff32 * editor.dPerfFreqMulMicro) + 0.5);
-
-			// delay until we have reached the next frame
-			if (time32 > 0)
-				usleep(time32);
-		}
-
-		// update next tick time
-		timeNext64 += scopeTimeLen;
-		timeNext64Frac += scopeTimeLenFrac;
-		if (timeNext64Frac > UINT32_MAX)
-		{
-			timeNext64Frac &= UINT32_MAX;
-			timeNext64++;
-		}
-	}
-
-	(void)ptr;
-	return true;
-}
-
-bool initScopes(void)
-{
-	double dInt;
-
-	// calculate scope time for performance counters and split into int/frac
-	double dFrac = modf(editor.dPerfFreq / SCOPE_HZ, &dInt);
-
-	// integer part
-	scopeTimeLen = (int32_t)dInt;
-
-	// fractional part (scaled to 0..2^32-1)
-	dFrac *= UINT32_MAX+1.0;
-	scopeTimeLenFrac = (uint32_t)dFrac;
-
-	scopeThread = SDL_CreateThread(scopeThreadFunc, NULL, NULL);
-	if (scopeThread == NULL)
-	{
-		showErrorMsgBox("Couldn't create channel scope thread!");
-		return false;
-	}
-
-	SDL_DetachThread(scopeThread);
-	return true;
-}
--- a/src/ft2_scopes.h
+++ /dev/null
@@ -1,48 +1,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-#include "ft2_header.h"
-#include "ft2_audio.h"
-
-#define SCOPE_HEIGHT 36
-
-#define SCOPE_FRAC_BITS 32
-#define SCOPE_FRAC_SCALE (1ULL << SCOPE_FRAC_BITS)
-#define SCOPE_FRAC_MASK (SCOPE_FRAC_SCALE-1)
-
-// *absolute* max safe bits (Amiga periods, period 1), don't mess with it!
-#define SCOPE_DRAW_FRAC_BITS 19
-#define SCOPE_DRAW_FRAC_SCALE (1UL << SCOPE_DRAW_FRAC_BITS)
-#define SCOPE_DRAW_FRAC_MASK (SCOPE_DRAW_FRAC_SCALE-1)
-
-void resetCachedScopeVars(void);
-int32_t getSamplePosition(uint8_t ch);
-void stopAllScopes(void);
-void refreshScopes(void);
-bool testScopesMouseDown(void);
-void drawScopes(void);
-void drawScopeFramework(void);
-bool initScopes(void);
-
-// actual scope data
-typedef struct scope_t
-{
-	volatile bool active;
-	const int8_t *base8;
-	const int16_t *base16;
-	bool wasCleared, sampleIs16Bit;
-	uint8_t loopType;
-	int32_t vol, loopStart, loopLength, end, pos, direction;
-	uint32_t drawDelta, oldDrawDelta;
-	uint64_t delta, oldDelta, posFrac;
-
-	double dOldHz;
-} scope_t;
-
-typedef struct lastChInstr_t
-{
-	uint8_t sampleNr, instrNr;
-} lastChInstr_t;
-
-extern lastChInstr_t lastChInstr[MAX_VOICES];
--- a/src/ft2_structs.h
+++ b/src/ft2_structs.h
@@ -22,16 +22,16 @@
 
 	bool autoPlayOnDrop, trimThreadWasDone, throwExit, editTextFlag;
 	bool copyMaskEnable, diskOpReadOnOpen, samplingAudioFlag, editSampleFlag;
-	bool instrBankSwapped, chnMode[MAX_VOICES], NI_Play;
+	bool instrBankSwapped, chnMode[MAX_CHANNELS], NI_Play;
 
 	uint8_t curPlayInstr, curPlaySmp, curSmpChannel, currPanEnvPoint, currVolEnvPoint;
 	uint8_t copyMask[5], pasteMask[5], transpMask[5], smpEd_NoteNr, instrBankOffset, sampleBankOffset;
 	uint8_t srcInstr, curInstr, srcSmp, curSmp, currHelpScreen, currConfigScreen, textCursorBlinkCounter;
-	uint8_t keyOnTab[MAX_VOICES], ID_Add, curOctave;
+	uint8_t keyOnTab[MAX_CHANNELS], editRowSkip, curOctave;
 	uint8_t sampleSaveMode, moduleSaveMode, ptnJumpPos[4];
-	int16_t globalVol, songPos, pattPos;
-	uint16_t tmpPattern, editPattern, speed, tempo, timer, ptnCursorY;
-	int32_t keyOffNr, keyOffTime[MAX_VOICES];
+	int16_t globalVolume, songPos, row;
+	uint16_t tmpPattern, editPattern, BPM, speed, tick, ptnCursorY;
+	int32_t keyOffNr, keyOffTime[MAX_CHANNELS];
 	uint32_t framesPassed, wavRendererTime;
 	double dPerfFreq, dPerfFreqMulMicro, dPerfFreqMulMs;
 } editor_t;
--- a/src/ft2_sysreqs.c
+++ b/src/ft2_sysreqs.c
@@ -54,7 +54,7 @@
 typedef struct quitType_t
 {
 	const char *text;
-	uint8_t typ;
+	uint8_t type;
 } quitType_t;
 
 #define QUIT_MESSAGES 11
@@ -180,7 +180,7 @@
 }
 
 // WARNING: This routine must ONLY be called from the main input/video thread!
-int16_t okBox(int16_t typ, const char *headline, const char *text)
+int16_t okBox(int16_t type, const char *headline, const char *text)
 {
 #define PUSHBUTTON_W 80
 
@@ -207,9 +207,9 @@
 	int16_t oldLastUsedObjectType = mouse.lastUsedObjectType;
 
 	// count number of buttons
-	uint16_t knp = 0;
-	while (buttonText[typ][knp][0] != '\0' && knp < 5)
-		knp++;
+	uint16_t numButtons = 0;
+	while (buttonText[type][numButtons][0] != '\0' && numButtons < 5)
+		numButtons++;
 
 	uint16_t tlen = textWidth(text);
 	uint16_t hlen = textWidth(headline);
@@ -218,7 +218,7 @@
 	if (hlen > tlen)
 		wlen = hlen;
 
-	uint16_t tx = (knp * 100) - 20;
+	uint16_t tx = (numButtons * 100) - 20;
 	if (tx > wlen)
 		wlen = tx;
 
@@ -234,7 +234,7 @@
 	uint16_t y = ui.extended ? SYSTEM_REQUEST_Y_EXT : SYSTEM_REQUEST_Y;
 
 	// set up buttons
-	for (i = 0; i < knp; i++)
+	for (i = 0; i < numButtons; i++)
 	{
 		p = &pushButtons[i];
 
@@ -242,12 +242,12 @@
 		p->y = y + 42;
 		p->w = PUSHBUTTON_W;
 		p->h = 16;
-		p->caption = buttonText[typ][i];
+		p->caption = buttonText[type][i];
 		p->visible = true;
 	}
 
 	// set up checkbox (special okBox types only!)
-	if (typ >= 6 && typ <= 7)
+	if (type >= 6 && type <= 7)
 	{
 		checkBox_t *c = &checkBoxes[0];
 		c->x = x + 5;
@@ -256,12 +256,12 @@
 		c->clickAreaHeight = 12;
 		c->checked = false;
 
-		if (typ == 6)
+		if (type == 6)
 		{
 			// S3M load warning
 			c->callbackFunc = configToggleImportWarning;
 		}
-		else if (typ == 7)
+		else if (type == 7)
 		{
 			// "setting not yet applied"
 			c->callbackFunc = configToggleNotYetAppliedWarning;
@@ -312,9 +312,9 @@
 					keyb.ignoreCurrKeyUp = true; // don't handle key up event for any keys that were pressed
 				}
 
-				for (i = 0; i < knp; i++)
+				for (i = 0; i < numButtons; i++)
 				{
-					if (shortCut[typ][i] == inputEvent.key.keysym.sym)
+					if (shortCut[type][i] == inputEvent.key.keysym.sym)
 					{
 						returnVal = i + 1;
 						ui.sysReqShown = false;
@@ -327,7 +327,7 @@
 			{
 				if (mouseButtonUpLogic(inputEvent.button.button))
 				{
-					if (typ >= 6 && typ <= 7)
+					if (type >= 6 && type <= 7)
 						testCheckBoxMouseRelease();
 
 					returnVal = testPushButtonMouseRelease(false) + 1;
@@ -360,8 +360,8 @@
 		drawWindow(wlen);
 		textOutShadow(headlineX, y +  4, PAL_FORGRND, PAL_BUTTON2, headline);
 		textOutShadow(textX,     y + 24, PAL_FORGRND, PAL_BUTTON2, text);
-		for (i = 0; i < knp; i++) drawPushButton(i);
-		if (typ >= 6 && typ <= 7)
+		for (i = 0; i < numButtons; i++) drawPushButton(i);
+		if (type >= 6 && type <= 7)
 		{
 			drawCheckBox(0);
 			textOutShadow(x + 21, y + 52, PAL_FORGRND, PAL_BUTTON2, "Don't show again");
@@ -371,10 +371,10 @@
 		endFPSCounter();
 	}
 
-	for (i = 0; i < knp; i++)
+	for (i = 0; i < numButtons; i++)
 		hidePushButton(i);
 
-	if (typ >= 6 && typ <= 7)
+	if (type >= 6 && type <= 7)
 		hideCheckBox(0);
 
 	mouse.lastUsedObjectID = oldLastUsedObjectID;
@@ -391,7 +391,7 @@
 ** - This routine must ONLY be called from the main input/video thread!!
 ** - edText must be null-terminated
 */
-int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxStrLen)
+int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxStrLen)
 {
 #define PUSHBUTTON_W 80
 #define TEXTBOX_W 250
@@ -443,15 +443,15 @@
 	uint16_t headlineX = (SCREEN_W - wlen) >> 1;
 
 	// count number of buttons
-	uint16_t knp = 0;
-	while (buttonText[typ][knp][0] != '\0' && knp < 5)
-		knp++;
+	uint16_t numButtons = 0;
+	while (buttonText[type][numButtons][0] != '\0' && numButtons < 5)
+		numButtons++;
 
 	uint16_t tx = TEXTBOX_W;
 	if (tx > wlen)
 		wlen = tx;
 
-	tx = (knp * 100) - 20;
+	tx = (numButtons * 100) - 20;
 	if (tx > wlen)
 		wlen = tx;
 
@@ -470,13 +470,13 @@
 	// setup buttons
 
 	pushButton_t *p = pushButtons;
-	for (i = 0; i < knp; i++, p++)
+	for (i = 0; i < numButtons; i++, p++)
 	{
 		p->w = PUSHBUTTON_W;
 		p->h = 16;
 		p->x = ((SCREEN_W - tx) >> 1) + (i * 100);
 		p->y = y + 42;
-		p->caption = buttonText[typ][i];
+		p->caption = buttonText[type][i];
 		p->visible = true;
 	}
 
@@ -553,7 +553,7 @@
 				}
 				else
 				{
-					for (i = 0; i < knp; i++)
+					for (i = 0; i < numButtons; i++)
 					{
 						if (shortCut[1][i] == inputEvent.key.keysym.sym)
 						{
@@ -604,7 +604,7 @@
 		hLine(t->x,        t->y + t->h, t->w + 1, PAL_BUTTON1);
 		vLine(t->x + t->w, t->y,        t->h,     PAL_BUTTON1);
 		drawTextBox(0);
-		for (i = 0; i < knp; i++) drawPushButton(i);
+		for (i = 0; i < numButtons; i++) drawPushButton(i);
 
 		flipFrame();
 		endFPSCounter();
@@ -613,7 +613,7 @@
 	editor.editTextFlag = false;
 	SDL_StopTextInput();
 
-	for (i = 0; i < knp; i++)
+	for (i = 0; i < numButtons; i++)
 		hidePushButton(i);
 	hideTextBox(0);
 
@@ -630,7 +630,7 @@
 }
 
 // WARNING: This routine must NOT be called from the main input/video thread!
-int16_t okBoxThreadSafe(int16_t typ, const char *headline, const char *text)
+int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text)
 {
 	if (!editor.mainLoopOngoing)
 		return 0; // main loop was not even started yet, bail out.
@@ -639,7 +639,7 @@
 	while (okBoxData.active)
 		SDL_Delay(1000 / VBLANK_HZ);
 
-	okBoxData.typ = typ;
+	okBoxData.type = type;
 	okBoxData.headline = headline;
 	okBoxData.text = text;
 	okBoxData.active = true;
@@ -653,7 +653,7 @@
 static bool askQuit_RandomMsg(void)
 {
 	uint8_t msg = rand() % QUIT_MESSAGES;
-	int16_t button = okBox(quitMessage[msg].typ, "System request", quitMessage[msg].text);
+	int16_t button = okBox(quitMessage[msg].type, "System request", quitMessage[msg].text);
 
 	return (button == 1) ? true : false;
 }
--- a/src/ft2_sysreqs.h
+++ b/src/ft2_sysreqs.h
@@ -13,14 +13,14 @@
 typedef struct okBoxData_t
 {
 	volatile bool active;
-	int16_t typ, returnData;
+	int16_t type, returnData;
 	const char *headline, *text;
 } okBoxData_t;
 
-int16_t okBoxThreadSafe(int16_t typ, const char *headline, const char *text);
-int16_t okBox(int16_t typ, const char *headline, const char *text);
+int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text);
+int16_t okBox(int16_t type, const char *headline, const char *text);
 int16_t quitBox(bool skipQuitMsg);
-int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxStrLen);
+int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxStrLen);
 bool askUnsavedChanges(uint8_t type);
 
 void myLoaderMsgBoxThreadSafe(const char *fmt, ...);
--- a/src/ft2_tables.c
+++ b/src/ft2_tables.c
@@ -2,7 +2,7 @@
 #include <stdbool.h>
 #include "ft2_palette.h" // pal16 typedef
 #include "ft2_pattern_ed.h" // pattCoord_t/pattCoord2_t/pattCoordsMouse_t/markCoord_t typedef
-#include "ft2_header.h" // MAX_VOICES
+#include "ft2_header.h" // MAX_CHANNELS
 #include "ft2_config.h" // CONFIG_FILE_SIZE
 #include "ft2_bmp.h"
 
@@ -694,7 +694,7 @@
 };
 
 // these two are for channel numbering on pattern data/scopes
-const char chDecTab1[MAX_VOICES+1] = 
+const char chDecTab1[MAX_CHANNELS+1] = 
 {
 	'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
 	'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
@@ -702,7 +702,7 @@
 	'3', '3', '3'
 };
 
-const char chDecTab2[MAX_VOICES+1] = 
+const char chDecTab2[MAX_CHANNELS+1] = 
 {
 	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
 	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
@@ -872,176 +872,50 @@
 /* ----------------------------------------------------------------------- */
 
 /*
-** const double dBpmMs1024 = 1024.0 / (bpm / 2.5); // milliseconds (scaled from 1000 to 1024)
-** x = (uint64_t)floor((UINT32_MAX+1.0) * dBpmMs1024);
+** for (int32_t bpm = 32; bpm <= 255; bpm++)
+** {
+**     const double dBpmMs1024 = 1024.0 / (bpm / 2.5); // milliseconds (scaled from 1000 to 1024)
+**     uint64_t x = (uint64_t)floor((UINT32_MAX+1.0) * dBpmMs1024);
+** }
 */
-const uint64_t musicTimeTab64[MAX_BPM+1] =
+const uint64_t musicTimeTab64[(MAX_BPM-MIN_BPM)+1] =
 {
-	0x00000000000,0xA0000000000,0x50000000000,0x35555555555,0x28000000000,0x20000000000,
-	0x1AAAAAAAAAA,0x16DB6DB6DB6,0x14000000000,0x11C71C71C71,0x10000000000,0x0E8BA2E8BA2,
-	0x0D555555555,0x0C4EC4EC4EC,0x0B6DB6DB6DB,0x0AAAAAAAAAA,0x0A000000000,0x09696969696,
-	0x08E38E38E38,0x086BCA1AF28,0x08000000000,0x079E79E79E7,0x0745D1745D1,0x06F4DE9BD37,
-	0x06AAAAAAAAA,0x06666666666,0x06276276276,0x05ED097B425,0x05B6DB6DB6D,0x058469EE584,
-	0x05555555555,0x05294A5294A,0x05000000000,0x04D9364D936,0x04B4B4B4B4B,0x04924924924,
-	0x0471C71C71C,0x045306EB3E4,0x0435E50D794,0x041A41A41A4,0x04000000000,0x03E7063E706,
-	0x03CF3CF3CF3,0x03B88EE23B8,0x03A2E8BA2E8,0x038E38E38E3,0x037A6F4DE9B,0x03677D46CEF,
-	0x03555555555,0x0343EB1A1F5,0x03333333333,0x03232323232,0x0313B13B13B,0x0304D4873EC,
-	0x02F684BDA12,0x02E8BA2E8BA,0x02DB6DB6DB6,0x02CE98B3A62,0x02C234F72C2,0x02B63CBEEA4,
-	0x02AAAAAAAAA,0x029F79B4758,0x0294A5294A5,0x028A28A28A2,0x02800000000,0x02762762762,
-	0x026C9B26C9B,0x026357E16EC,0x025A5A5A5A5,0x02519F89467,0x02492492492,0x0240E6C2B44,
-	0x0238E38E38E,0x0231188C462,0x022983759F2,0x02222222222,0x021AF286BCA,0x0213F2B3884,
-	0x020D20D20D2,0x02067B23A54,0x02000000000,0x01F9ADD3C0C,0x01F3831F383,0x01ED7E75346,
-	0x01E79E79E79,0x01E1E1E1E1E,0x01DC47711DC,0x01D6CDFA1D6,0x01D1745D174,0x01CC398730E,
-	0x01C71C71C71,0x01C21C21C21,0x01BD37A6F4D,0x01B86E1B86E,0x01B3BEA3677,0x01AF286BCA1,
-	0x01AAAAAAAAA,0x01A6449E59B,0x01A1F58D0FA,0x019DBCC4867,0x01999999999,0x01958B67EBB,
-	0x01919191919,0x018DAB7EC1D,0x0189D89D89D,0x01861861861,0x01826A439F6,0x017ECDC1CB5,
-	0x017B425ED09,0x0177C7A20E1,0x01745D1745D,0x0171024E6A1,0x016DB6DB6DB,0x016A7A5616A,
-	0x01674C59D31,0x01642C8590B,0x01611A7B961,0x015E15E15E1,0x015B1E5F752,0x015833A1583,
-	0x01555555555,0x0152832C6E0,0x014FBCDA3AC,0x014D0214D02,0x014A5294A52,0x0147AE147AE,
-	0x01451451451,0x0142850A142,0x01400000000,0x013D84F613D,0x013B13B13B1,0x0138ABF82EE,
-	0x01364D9364D,0x0133F84CFE1,0x0131ABF0B76,0x012F684BDA1,0x012D2D2D2D2,0x012AFA64E7B,
-	0x0128CFC4A33,0x0126AD1F4F3,0x01249249249,0x01227F179A5,0x012073615A2,0x011E6EFE35B,
-	0x011C71C71C7,0x011A7B9611A,0x01188C46231,0x0116A3B35FC,0x0114C1BACF9,0x0112E63A6A8,
-	0x01111111111,0x010F421E843,0x010D79435E5,0x010BB6610BB,0x0109F959C42,0x01084210842,
-	0x01069069069,0x0104E447BEC,0x01033D91D2A,0x01019C2D14E,0x01000000000,0x00FE68F1B07,
-	0x00FCD6E9E06,0x00FB49D0E22,0x00F9C18F9C1,0x00F83E0F83E,0x00F6BF3A9A3,0x00F544FB66B,
-	0x00F3CF3CF3C,0x00F25DEACAF,0x00F0F0F0F0F,0x00EF883BE20,0x00EE23B88EE,0x00ECC35458C,
-	0x00EB66FD0EB,0x00EA0EA0EA0,0x00E8BA2E8BA,0x00E76994F8C,0x00E61CC3987,0x00E4D3AA30A,
-	0x00E38E38E38,0x00E24C602D4,0x00E10E10E10,0x00DFD33C272,0x00DE9BD37A6,0x00DD67C8A60,
-	0x00DC370DC37,0x00DB0995382,0x00D9DF51B3B,0x00D8B8362E0,0x00D79435E50,0x00D673445B2,
-	0x00D55555555,0x00D43A5CD98,0x00D3224F2CD,0x00D20D20D20,0x00D0FAC687D,0x00CFEB35477,
-	0x00CEDE62433,0x00CDD442E4F,0x00CCCCCCCCC,0x00CBC7F5CF9,0x00CAC5B3F5D,0x00C9C5FD7A5,
-	0x00C8C8C8C8C,0x00C7CE0C7CE,0x00C6D5BF60E,0x00C5DFD86CD,0x00C4EC4EC4E,0x00C3FB19B8F,
-	0x00C30C30C30,0x00C21F8B86A,0x00C13521CFB,0x00C04CEB916,0x00BF66E0E5A,0x00BE82FA0BE,
-	0x00BDA12F684,0x00BCC17982F,0x00BBE3D1070,0x00BB082EC20,0x00BA2E8BA2E,0x00B956E0B95,
-	0x00B88127350,0x00B7AD58650,0x00B6DB6DB6D,0x00B60B60B60,0x00B53D2B0B5,0x00B470C67C0,
-	0x00B3A62CE98,0x00B2DD58507,0x00B21642C85,0x00B150E682C,0x00B08D3DCB0,0x00AFCB43057,
-	0x00AF0AF0AF0,0x00AE4C415C9,0x00AD8F2FBA9,0x00ACD3B68C6,0x00AC19D0AC1,0x00AB617909A,
-	0x00AAAAAAAAA,0x00A9F560A9F,0x00A94196370,0x00A88F46959,0x00A7DE6D1D6,0x00A72F05397,
-	0x00A6810A681,0x00A5D4783A0,0x00A5294A529,0x00A47F7C66C,0x00A3D70A3D7,0x00A32FEFAE6,
-	0x00A28A28A28,0x00A1E5B1133,0x00A142850A1,0x00A0A0A0A0A,0x00A00000000,0x009F609F609,
-	0x009EC27B09E,0x009E258F520,0x009D89D89D8,0x009CEF535F2,0x009C55FC177,0x009BBDCF54A,
-	0x009B26C9B26,0x009A90E7D95,0x0099FC267F0,0x0099688265A,0x0098D5F85BB,0x009844853BF,
-	0x0097B425ED0,0x009724D7614,0x00969696969,0x00960960960,0x00957D3273D,0x0094F2094F2,
-	0x009467E2519,0x0093DEBAAF9,0x0093568FA79,0x0092CF5E824,0x00924924924,0x0091C3DF33E,
-	0x00913F8BCD2,0x0090BC27CD5,0x009039B0AD1,0x008FB823EE0,0x008F377F1AD,0x008EB7BFC6E,
-	0x008E38E38E3,0x008DBAE8154,0x008D3DCB08D,0x008CC18A1DE,0x008C4623118,0x008BCB93A8A,
-	0x008B51D9AFE,0x008AD8F2FBA,0x008A60DD67C,0x0089E996D77,0x0089731D354,0x0088FD6E72B,
-	0x00888888888,0x00881469763,0x0087A10F421,0x00872E77F93,0x0086BCA1AF2,0x00864B8A7DE,
-	0x0085DB3085D,0x00856B91EDA,0x0084FCACE21,0x00848E7F95F,0x00842108421,0x0083B445250,
-	0x00834834834,0x0082DCD4A6D,0x00827223DF6,0x00820820820,0x00819EC8E95,0x0081361B751,
-	0x0080CE168A7,0x008066B893A,0x00800000000,0x007F99EB43C,0x007F3478D83,0x007ECFA73B7,
-	0x007E6B74F03,0x007E07E07E0,0x007DA4E8711,0x007D428B5A0,0x007CE0C7CE0,0x007C7F9C66B,
-	0x007C1F07C1F,0x007BBF0881E,0x007B5F9D4D1,0x007B00C4CE0,0x007AA27DB35,0x007A44C6AFC,
-	0x0079E79E79E,0x00798B03CC5,0x00792EF5657,0x0078D372078,0x00787878787,0x00781E0781E,
-	0x0077C41DF10,0x00776ABA96C,0x007711DC477,0x0076B981DAE,0x007661AA2C6,0x00760A541A8,
-	0x0075B37E875,0x00755D28580,0x00750750750,0x0074B1F5CA0,0x00745D1745D,0x007408B3DA4,
-	0x0073B4CA7C6,0x0073615A240,0x00730E61CC3,0x0072BBE072B,0x007269D5185,0x0072183EC08,
-	0x0071C71C71C,0x0071766D352,0x0071263016A,0x0070D66424A,0x00708708708,0x0070381C0E0,
-	0x006FE99E139,0x006F9B8D9A2,0x006F4DE9BD3,0x006F00B19AB,0x006EB3E4530,0x006E678108F,
-	0x006E1B86E1B,0x006DCFF504C,0x006D84CA9C1,0x006D3A06D3A,0x006CEFA8D9D,0x006CA5AFDF6,
-	0x006C5C1B170,0x006C12E9B5B,0x006BCA1AF28,0x006B81AE06B,0x006B39A22D9,0x006AF1F6A46,
-	0x006AAAAAAAA,0x006A63BD81A,0x006A1D2E6CC,0x0069D6FCB14,0x00699127966,0x00694BAE655,
-	0x00690690690,0x0068C1CCEE5,0x00687D6343E,0x00683952BA4,0x0067F59AA3B,0x0067B23A544,
-	0x00676F31219,0x00672C7E634,0x0066EA21727,0x0066A819AA0,0x00666666666,0x0066250705B,
-	0x0065E3FAE7C,0x0065A3416DE,0x006562D9FAE,0x006522C3F35,0x0064E2FEBD2,0x0064A389BFD,
-	0x00646464646,0x0064258E154,0x0063E7063E7,0x0063A8CC4D3,0x00636ADFB07,0x00632D3FD85,
-	0x0062EFEC366,0x0062B2E43DA,0x00627627627,0x006239B51A6,0x0061FD8CDC7,0x0061C1AE20F,
-	0x00618618618,0x00614ACB18E,0x00610FC5C35,0x0060D507DE1,0x00609A90E7D,0x00606060606,
-	0x00602675C8B,0x005FECD0A31,0x005FB37072D,0x005F7A54BC9,0x005F417D05F,0x005F08E8D5D,
-	0x005ED097B42,0x005E988929F,0x005E60BCC17,0x005E293205E,0x005DF1E8838,0x005DBADFC7C,
-	0x005D8417610,0x005D4D8EDEC,0x005D1745D17,0x005CE13BCA9,0x005CAB705CA,0x005C75E31B2,
-	0x005C40939A8,0x005C0B81702,0x005BD6AC328,0x005BA21378D,0x005B6DB6DB6,0x005B3995F37,
-	0x005B05B05B0,0x005AD205AD2,0x005A9E9585A,0x005A6B5F816,0x005A38633E0,0x005A05A05A0,
-	0x0059D31674C,0x0059A0C52E7,0x00596EAC283,0x00593CCB03E,0x00590B21642,0x0058D9AEEC9,
-	0x0058A873416,0x0058776E07B,0x0058469EE58,0x00581605816,0x0057E5A182B,0x0057B57291D,
-	0x00578578578,0x005755B27D8,0x00572620AE4,0x0056F6C294E,0x0056C797DD4,0x005698A033F,
-	0x005669DB463,0x00563B48C20,0x00560CE8560,0x0055DEB9B1A,0x0055B0BC84D,0x005582F0804,
-	0x00555555555,0x005527EAB60,0x0054FAB054F,0x0054CDA5E57,0x0054A0CB1B8,0x0054741FAB8,
-	0x005447A34AC,0x00541B55AF0,0x0053EF368EB,0x0053C345A0B,0x005397829CB,0x00536BED3AE,
-	0x00534085340,0x0053154A416,0x0052EA3C1D0,0x0052BF5A814,0x005294A5294,0x00526A1BD09,
-	0x00523FBE336,0x0052158C0E5,0x0051EB851EB,0x0051C1A9223,0x005197F7D73,0x00516E70FC6,
-	0x00514514514,0x00511BE1958,0x0050F2D8899,0x0050C9F8EE5,0x0050A142850,0x005078B50F9,
-	0x00505050505,0x005028140A0,0x00500000000,0x004FD813F60,0x004FB04FB04,0x004F88B2F39,
-	0x004F613D84F,0x004F39EF2A1,0x004F12C7A90,0x004EEBC6C84,0x004EC4EC4EC,0x004E9E3803E,
-	0x004E77A9AF9,0x004E514119F,0x004E2AFE0BB,0x004E04E04E0,0x004DDEE7AA5,0x004DB913EAA,
-	0x004D9364D93,0x004D6DDA40D,0x004D4873ECA,0x004D2331A84,0x004CFE133F8,0x004CD9187EC,
-	0x004CB44132D,0x004C8F8D28A,0x004C6AFC2DD,0x004C468E103,0x004C22429DF,0x004BFE19A5C,
-	0x004BDA12F68,0x004BB62E5F9,0x004B926BB0A,0x004B6ECAB9C,0x004B4B4B4B4,0x004B27ED360,
-	0x004B04B04B0,0x004AE1945BB,0x004ABE9939E,0x004A9BBEB7B,0x004A7904A79,0x004A566ADC3,
-	0x004A33F128C,0x004A119760C,0x0049EF5D57C,0x0049CD42E20,0x0049AB47D3C,0x0049896C01D,
-	0x004967AF412,0x00494611670,0x00492492492,0x00490331BD6,0x0048E1EF99F,0x0048C0CBB56,
-	0x00489FC5E69,0x00487EDE048,0x00485E13E6A,0x00483D6764A,0x00481CD8568,0x0047FC66947,
-	0x0047DC11F70,0x0047BBDA56F,0x00479BBF8D6,0x00477BC173B,0x00475BDFE37,0x00473C1AB68,
-	0x00471C71C71,0x0046FCE4EF9,0x0046DD740AA,0x0046BE1EF32,0x00469EE5846,0x00467FC799C,
-	0x004660C50EF,0x004641DDBFE,0x0046231188C,0x00460460460,0x0045E5C9D45,0x0045C74E109,
-	0x0045A8ECD7F,0x00458AA607D,0x00456C797DD,0x00454E6717D,0x0045306EB3E,0x00451290305,
-	0x0044F4CB6BB,0x0044D72044D,0x0044B98E9AA,0x00449C164C5,0x00447EB7395,0x00446171416,
-	0x00444444444,0x00442730221,0x00440A34BB1,0x0043ED51EFD,0x0043D087A10,0x0043B3D5AF9,
-	0x0043973BFC9,0x00437ABA696,0x00435E50D79,0x004341FF28C,0x004325C53EF,0x004309A2FC3,
-	0x0042ED9842E,0x0042D1A4F58,0x0042B5C8F6D,0x00429A0429A,0x00427E56710,0x004262BFB05,
-	0x0042473FCAF,0x00422BD6A49,0x00421084210,0x0041F548244,0x0041DA22928,0x0041BF13502,
-	0x0041A41A41A,0x004189374BC,0x00416E6A536,0x004153B33DA,0x00413911EFB,0x00411E864EF,
-	0x00410410410,0x0040E9AFAB9,0x0040CF6474A,0x0040B52E823,0x00409B0DBA8,0x00408102040,
-	0x0040670B453,0x00404D2964D,0x0040335C49D,0x004019A3DB2,0x00400000000,0x003FE6709FC,
-	0x003FCCF5A1E,0x003FB38EEE1,0x003F9A3C6C1,0x003F80FE03F,0x003F67D39DB,0x003F4EBD21A,
-	0x003F35BA781,0x003F1CCB89A,0x003F03F03F0,0x003EEB2880F,0x003ED274388,0x003EB9D34EC,
-	0x003EA145AD0,0x003E88CB3C9,0x003E7063E70,0x003E580F960,0x003E3FCE335,0x003E279FA8F,
-	0x003E0F83E0F,0x003DF77AC58,0x003DDF8440F,0x003DC7A03DC,0x003DAFCEA68,0x003D980F660,
-	0x003D8062670,0x003D68C7948,0x003D513ED9A,0x003D39C821A,0x003D226357E,0x003D0B1067C,
-	0x003CF3CF3CF,0x003CDC9FC32,0x003CC581E62,0x003CAE75920,0x003C977AB2B,0x003C8091348,
-	0x003C69B903C,0x003C52F20CD,0x003C3C3C3C3,0x003C25977EA,0x003C0F03C0F,0x003BF880EFE,
-	0x003BE20EF88,0x003BCBADC7F,0x003BB55D4B6,0x003B9F1D702,0x003B88EE23B,0x003B72CF539,
-	0x003B5CC0ED7,0x003B46C2DF0,0x003B30D5163,0x003B1AF780E,0x003B052A0D4,0x003AEF6CA97,
-	0x003AD9BF43A,0x003AC421CA6,0x003AAE942C0,0x003A9916572,0x003A83A83A8,0x003A6E49C4D,
-	0x003A58FAE50,0x003A43BB8A0,0x003A2E8BA2E,0x003A196B1ED,0x003A0459ED2,0x0039EF57FD1,
-	0x0039DA653E3,0x0039C5819FF,0x0039B0AD120,0x00399BE7842,0x00398730E61,0x0039728927D,
-	0x00395DF0395,0x003949660AB,0x003934EA8C2,0x0039207DADE,0x00390C1F604,0x0038F7CF93C,
-	0x0038E38E38E,0x0038CF5B404,0x0038BB369A9,0x0038A72038A,0x003893180B5,0x00387F1E038,
-	0x00386B32125,0x0038575428D,0x00384384384,0x00382FC231D,0x00381C0E070,0x00380867A92,
-	0x0037F4CF09C,0x0037E1441A8,0x0037CDC6CD1,0x0037BA57132,0x0037A6F4DE9,0x003793A0215,
-	0x00378058CD5,0x00376D1ED4B,0x003759F2298,0x003746D2BE0,0x003733C0847,0x003720BB6F4,
-	0x00370DC370D,0x0036FAD87BB,0x0036E7FA826,0x0036D529779,0x0036C2654E0,0x0036AFADF87,
-	0x00369D0369D,0x00368A6594F,0x003677D46CE,0x0036654FE4C,0x003652D7EFB,0x0036406C80D,
-	0x00362E0D8B8,0x00361BBB030,0x00360974DAD,0x0035F73B066,0x0035E50D794,0x0035D2EC270,
-	0x0035C0D7035,0x0035AECE020,0x00359CD116C,0x00358AE0358,0x003578FB523,0x0035672260C,
-	0x00355555555,0x0035439423F,0x003531DEC0D,0x00352035203,0x00350E97366,0x0034FD04F7B,
-	0x0034EB7E58A,0x0034DA034DA,0x0034C893CB3,0x0034B72FC60,0x0034A5D732A,0x0034948A05E,
-	0x00348348348,0x00347211B34,0x003460E6772,0x00344FC6750,0x00343EB1A1F,0x00342DA7F2F,
-	0x00341CA95D2,0x00340BB5D5B,0x0033FACD51D,0x0033E9EFC6E,0x0033D91D2A2,0x0033C85570F,
-	0x0033B79890C,0x0033A6E67F3,0x0033963F31A,0x003385A29DC,0x00337510B93,0x0033648979B,
-	0x0033540CD50,0x0033439AC0E,0x00333333333,0x003322D621E,0x0033128382D,0x0033023B4C3,
-	0x0032F1FD73E,0x0032E1C9F01,0x0032D1A0B6F,0x0032C181BEA,0x0032B16CFD7,0x0032A16269B,
-	0x00329161F9A,0x0032816BA3D,0x0032717F5E9,0x0032619D206,0x003251C4DFE,0x003241F693A,
-	0x00323232323,0x00322277B24,0x003212C70AA,0x00320320320,0x0031F3831F3,0x0031E3EFC91,
-	0x0031D466269,0x0031C4E62EA,0x0031B56FD83,0x0031A6031A6,0x0031969FEC2,0x0031874644B,
-	0x003177F61B3,0x003168AF66D,0x003159721ED,0x00314A3E3A8,0x00313B13B13,0x00312BF27A5,
-	0x00311CDA8D3,0x00310DCBE15,0x0030FEC66E3,0x0030EFCA2B6,0x0030E0D7107,0x0030D1ED150,
-	0x0030C30C30C,0x0030B4345B5,0x0030A5658C7,0x0030969FBBF,0x003087E2E1A,0x0030792EF56,
-	0x00306A83EF0,0x00305BE1C69,0x00304D4873E,0x00303EB7EF1,0x00303030303,0x003021B12F3,
-	0x0030133AE45,0x003004CD47B,0x002FF668518,0x002FE80BFA0,0x002FD9B8396,0x002FCB6D081,
-	0x002FBD2A5E4,0x002FAEF0347,0x002FA0BE82F,0x002F9295424,0x002F84746AE,0x002F765BF55,
-	0x002F684BDA1,0x002F5A4411C,0x002F4C4494F,0x002F3E4D5C6,0x002F305E60B,0x002F22779AA,
-	0x002F149902F,0x002F06C2925,0x002EF8F441C,0x002EEB2E09F,0x002EDD6FE3E,0x002ECFB9C86,
-	0x002EC20BB08,0x002EB465952,0x002EA6C76F6,0x002E9931383,0x002E8BA2E8B,0x002E7E1C7A0,
-	0x002E709DE54,0x002E632723A,0x002E55B82E5,0x002E4850FE8,0x002E3AF18D9,0x002E2D99D4B,
-	0x002E2049CD4,0x002E1301709,0x002E05C0B81,0x002DF8879D2,0x002DEB56194,0x002DDE2C25D,
-	0x002DD109BC6,0x002DC3EED68,0x002DB6DB6DB,0x002DA9CF7B9,0x002D9CCAF9B,0x002D8FCDE1D,
-	0x002D82D82D8,0x002D75E9D68,0x002D6902D69,0x002D5C23276,0x002D4F4AC2D,0x002D4279A2A,
-	0x002D35AFC0B,0x002D28ED16D,0x002D1C319F0,0x002D0F7D531,0x002D02D02D0,0x002CF62A26C,
-	0x002CE98B3A6,0x002CDCF361D,0x002CD062973,0x002CC3D8D4A,0x002CB756141,0x002CAADA4FD,
-	0x002C9E6581F,0x002C91F7A4A,0x002C8590B21,0x002C7930A48,0x002C6CD7764,0x002C6085218,
-	0x002C5439A0B,0x002C47F4EE0,0x002C3BB703D,0x002C2F7FDCA,0x002C234F72C,0x002C1725C09,
-	0x002C0B02C0B,0x002BFEE66D7,0x002BF2D0C15,0x002BE6C1B70,0x002BDAB948E,0x002BCEB771A,
-	0x002BC2BC2BC,0x002BB6C771E,0x002BAAD93EC,0x002B9EF18CF,0x002B9310572,0x002B8735981,
-	0x002B7B614A7,0x002B6F93690,0x002B63CBEEA,0x002B580AD60,0x002B4C5019F,0x002B409BB56,
-	0x002B34EDA31,0x002B2945DE0,0x002B1DA4610,0x002B1209270,0x002B06742B0,0x002AFAE567F,
-	0x002AEF5CD8D,0x002AE3DA78A,0x002AD85E426,0x002ACCE8313,0x002AC178402,0x002AB60E6A3,
-	0x002AAAAAAAA,0x002A9F4CFC8,0x002A93F55B0,0x002A88A3C14,0x002A7D582A7,0x002A721291E,
-	0x002A66D2F2B,0x002A5B99484,0x002A50658DC,0x002A4537BE7,0x002A3A0FD5C,0x002A2EEDCEF,
-	0x002A23D1A56,0x002A18BB547,0x002A0DAAD78,0x002A02A02A0,0x0029F79B475,0x0029EC9C2AF,
-	0x0029E1A2D05,0x0029D6AF32F,0x0029CBC14E5,0x0029C0D91E0,0x0029B5F69D7,0x0029AB19C84,
-	0x0029A0429A0,0x002995710E4,0x00298AA520B,0x00297FDECCE,0x0029751E0E8,0x00296A62E13,
-	0x00295FAD40A,0x002954FD288,0x00294A5294A,0x00293FAD80A,0x0029350DE84,0x00292A73C76,
-	0x00291FDF19B,0x0029154FDB0,0x00290AC6072,0x002900419A0
+	0x5000000000,0x4D9364D936,0x4B4B4B4B4B,0x4924924924,0x471C71C71C,0x45306EB3E4,
+	0x435E50D794,0x41A41A41A4,0x4000000000,0x3E7063E706,0x3CF3CF3CF3,0x3B88EE23B8,
+	0x3A2E8BA2E8,0x38E38E38E3,0x37A6F4DE9B,0x3677D46CEF,0x3555555555,0x343EB1A1F5,
+	0x3333333333,0x3232323232,0x313B13B13B,0x304D4873EC,0x2F684BDA12,0x2E8BA2E8BA,
+	0x2DB6DB6DB6,0x2CE98B3A62,0x2C234F72C2,0x2B63CBEEA4,0x2AAAAAAAAA,0x29F79B4758,
+	0x294A5294A5,0x28A28A28A2,0x2800000000,0x2762762762,0x26C9B26C9B,0x26357E16EC,
+	0x25A5A5A5A5,0x2519F89467,0x2492492492,0x240E6C2B44,0x238E38E38E,0x231188C462,
+	0x22983759F2,0x2222222222,0x21AF286BCA,0x213F2B3884,0x20D20D20D2,0x2067B23A54,
+	0x2000000000,0x1F9ADD3C0C,0x1F3831F383,0x1ED7E75346,0x1E79E79E79,0x1E1E1E1E1E,
+	0x1DC47711DC,0x1D6CDFA1D6,0x1D1745D174,0x1CC398730E,0x1C71C71C71,0x1C21C21C21,
+	0x1BD37A6F4D,0x1B86E1B86E,0x1B3BEA3677,0x1AF286BCA1,0x1AAAAAAAAA,0x1A6449E59B,
+	0x1A1F58D0FA,0x19DBCC4867,0x1999999999,0x1958B67EBB,0x1919191919,0x18DAB7EC1D,
+	0x189D89D89D,0x1861861861,0x1826A439F6,0x17ECDC1CB5,0x17B425ED09,0x177C7A20E1,
+	0x1745D1745D,0x171024E6A1,0x16DB6DB6DB,0x16A7A5616A,0x1674C59D31,0x1642C8590B,
+	0x1611A7B961,0x15E15E15E1,0x15B1E5F752,0x15833A1583,0x1555555555,0x152832C6E0,
+	0x14FBCDA3AC,0x14D0214D02,0x14A5294A52,0x147AE147AE,0x1451451451,0x142850A142,
+	0x1400000000,0x13D84F613D,0x13B13B13B1,0x138ABF82EE,0x1364D9364D,0x133F84CFE1,
+	0x131ABF0B76,0x12F684BDA1,0x12D2D2D2D2,0x12AFA64E7B,0x128CFC4A33,0x126AD1F4F3,
+	0x1249249249,0x1227F179A5,0x12073615A2,0x11E6EFE35B,0x11C71C71C7,0x11A7B9611A,
+	0x1188C46231,0x116A3B35FC,0x114C1BACF9,0x112E63A6A8,0x1111111111,0x10F421E843,
+	0x10D79435E5,0x10BB6610BB,0x109F959C42,0x1084210842,0x1069069069,0x104E447BEC,
+	0x1033D91D2A,0x1019C2D14E,0x1000000000,0x0FE68F1B07,0x0FCD6E9E06,0x0FB49D0E22,
+	0x0F9C18F9C1,0x0F83E0F83E,0x0F6BF3A9A3,0x0F544FB66B,0x0F3CF3CF3C,0x0F25DEACAF,
+	0x0F0F0F0F0F,0x0EF883BE20,0x0EE23B88EE,0x0ECC35458C,0x0EB66FD0EB,0x0EA0EA0EA0,
+	0x0E8BA2E8BA,0x0E76994F8C,0x0E61CC3987,0x0E4D3AA30A,0x0E38E38E38,0x0E24C602D4,
+	0x0E10E10E10,0x0DFD33C272,0x0DE9BD37A6,0x0DD67C8A60,0x0DC370DC37,0x0DB0995382,
+	0x0D9DF51B3B,0x0D8B8362E0,0x0D79435E50,0x0D673445B2,0x0D55555555,0x0D43A5CD98,
+	0x0D3224F2CD,0x0D20D20D20,0x0D0FAC687D,0x0CFEB35477,0x0CEDE62433,0x0CDD442E4F,
+	0x0CCCCCCCCC,0x0CBC7F5CF9,0x0CAC5B3F5D,0x0C9C5FD7A5,0x0C8C8C8C8C,0x0C7CE0C7CE,
+	0x0C6D5BF60E,0x0C5DFD86CD,0x0C4EC4EC4E,0x0C3FB19B8F,0x0C30C30C30,0x0C21F8B86A,
+	0x0C13521CFB,0x0C04CEB916,0x0BF66E0E5A,0x0BE82FA0BE,0x0BDA12F684,0x0BCC17982F,
+	0x0BBE3D1070,0x0BB082EC20,0x0BA2E8BA2E,0x0B956E0B95,0x0B88127350,0x0B7AD58650,
+	0x0B6DB6DB6D,0x0B60B60B60,0x0B53D2B0B5,0x0B470C67C0,0x0B3A62CE98,0x0B2DD58507,
+	0x0B21642C85,0x0B150E682C,0x0B08D3DCB0,0x0AFCB43057,0x0AF0AF0AF0,0x0AE4C415C9,
+	0x0AD8F2FBA9,0x0ACD3B68C6,0x0AC19D0AC1,0x0AB617909A,0x0AAAAAAAAA,0x0A9F560A9F,
+	0x0A94196370,0x0A88F46959,0x0A7DE6D1D6,0x0A72F05397,0x0A6810A681,0x0A5D4783A0,
+	0x0A5294A529,0x0A47F7C66C,0x0A3D70A3D7,0x0A32FEFAE6,0x0A28A28A28,0x0A1E5B1133,
+	0x0A142850A1,0x0A0A0A0A0A
 };
--- a/src/ft2_tables.h
+++ b/src/ft2_tables.h
@@ -3,7 +3,7 @@
 #include <stdint.h>
 #include "ft2_palette.h" // pal16 typedef
 #include "ft2_pattern_ed.h" // pattCoord_t/pattCoord2_t/pattCoordsMouse_t/markCoord_t typedef
-#include "ft2_header.h" // MAX_VOICES
+#include "ft2_header.h" // MAX_CHANNELS
 #include "ft2_config.h" // CONFIG_FILE_SIZE
 
 #define KEY2VOL_ENTRIES (signed)(sizeof (key2VolTab) / sizeof (SDL_Keycode))
@@ -37,8 +37,8 @@
 extern const markCoord_t markCoordTable[2][2][2];
 extern const uint8_t pattCursorXTab[2 * 4 * 8];
 extern const uint8_t pattCursorWTab[2 * 4 * 8];
-extern const char chDecTab1[MAX_VOICES+1];
-extern const char chDecTab2[MAX_VOICES+1];
+extern const char chDecTab1[MAX_CHANNELS+1];
+extern const char chDecTab2[MAX_CHANNELS+1];
 extern const SDL_Keycode key2VolTab[16];
 extern const SDL_Keycode key2EfxTab[36];
 extern const SDL_Keycode key2HexTab[16];
@@ -49,4 +49,4 @@
 
 extern const uint8_t defConfigData[CONFIG_FILE_SIZE];
 
-extern const uint64_t musicTimeTab64[MAX_BPM+1];
+extern const uint64_t musicTimeTab64[(MAX_BPM-MIN_BPM)+1];
--- a/src/ft2_textboxes.c
+++ b/src/ft2_textboxes.c
@@ -727,7 +727,7 @@
 void updateTextBoxPointers(void)
 {
 	int32_t i;
-	instrTyp *curIns = instr[editor.curInstr];
+	instr_t *curIns = instr[editor.curInstr];
 
 	// instrument names
 	for (i = 0; i < 8; i++)
@@ -742,7 +742,7 @@
 	else
 	{
 		for (i = 0; i < 5; i++)
-			textBoxes[TB_SAMP1+i].textPtr = curIns->samp[editor.sampleBankOffset+i].name;
+			textBoxes[TB_SAMP1+i].textPtr = curIns->smp[editor.sampleBankOffset+i].name;
 	}
 
 	// song name
--- a/src/ft2_trim.c
+++ b/src/ft2_trim.c
@@ -10,7 +10,7 @@
 #include "ft2_header.h"
 #include "ft2_sample_ed.h"
 #include "ft2_gui.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_pattern_ed.h"
 #include "ft2_replayer.h"
 #include "ft2_audio.h"
@@ -24,8 +24,8 @@
 static uint8_t instrUsed[MAX_INST], instrOrder[MAX_INST], pattUsed[MAX_PATTERNS], pattOrder[MAX_PATTERNS];
 static int16_t oldPattLens[MAX_PATTERNS], tmpPattLens[MAX_PATTERNS];
 static int64_t xmSize64 = -1, xmAfterTrimSize64 = -1, spaceSaved64 = -1;
-static tonTyp *oldPatts[MAX_PATTERNS], *tmpPatt[MAX_PATTERNS];
-static instrTyp *tmpInstr[1 + MAX_INST], *tmpInst[MAX_INST]; // tmpInstr[x] = copy of instr[x] for "after trim" size calculation
+static note_t *oldPatts[MAX_PATTERNS], *tmpPatt[MAX_PATTERNS];
+static instr_t *tmpInstr[1 + MAX_INST], *tmpInst[MAX_INST]; // tmpInstr[x] = copy of instr[x] for "after trim" size calculation
 static SDL_Thread *trimThread;
 
 void pbTrimCalc(void);
@@ -52,7 +52,7 @@
 	{
 		if (instr[i] != NULL)
 		{
-			tmpInstr[i] = (instrTyp *)malloc(sizeof (instrTyp));
+			tmpInstr[i] = (instr_t *)malloc(sizeof (instr_t));
 			if (tmpInstr[i] == NULL)
 			{
 				freeTmpInstruments();
@@ -70,29 +70,30 @@
 {
 	for (int32_t i = 0; i < ap; i++)
 	{
-		tonTyp *pattPtr = patt[i];
+		note_t *pattPtr = pattern[i];
 		if (pattPtr == NULL)
 			continue;
 
-		const int32_t readLen = pattLens[i] * MAX_VOICES;
-		for (int32_t j = 0; j < readLen; j++)
+		const int32_t readLen = patternNumRows[i] * MAX_CHANNELS;
+
+		note_t *p = pattPtr;
+		for (int32_t j = 0; j < readLen; j++, p++)
 		{
-			tonTyp *note = &pattPtr[j];
-			if (note->instr == src)
-				note->instr = dst;
+			if (p->instr == src)
+				p->instr = dst;
 		}
 	}
 }
 
-static int16_t getUsedTempSamples(uint16_t nr)
+static int16_t getUsedTempSamples(uint16_t insNum)
 {
-	if (tmpInstr[nr] == NULL)
+	if (tmpInstr[insNum] == NULL)
 		return 0;
 
-	instrTyp *ins = tmpInstr[nr];
+	instr_t *ins = tmpInstr[insNum];
 
 	int16_t i = 16 - 1;
-	while (i >= 0 && ins->samp[i].pek == NULL && ins->samp[i].name[0] == '\0')
+	while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0')
 		i--;
 
 	/* Yes, 'i' can be -1 here, and will be set to at least 0
@@ -100,8 +101,8 @@
 	**/
 	for (int16_t j = 0; j < 96; j++)
 	{
-		if (ins->ta[j] > i)
-			i = ins->ta[j];
+		if (ins->note2SampleLUT[j] > i)
+			i = ins->note2SampleLUT[j];
 	}
 
 	return i+1;
@@ -127,15 +128,21 @@
 
 		const int16_t a = getUsedTempSamples(i);
 		if (a > 0)
-			currSize64 += INSTR_HEADER_SIZE + (a * sizeof (sampleHeaderTyp));
+			currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t));
 		else
 			currSize64 += 22+11;
 
-		instrTyp *ins = tmpInstr[j];
+		instr_t *ins = tmpInstr[j];
 		for (int16_t k = 0; k < a; k++)
 		{
-			if (ins->samp[k].pek != NULL)
-				currSize64 += ins->samp[k].len;
+			sample_t *s = &ins->smp[k];
+			if (s->dataPtr != NULL && s->length > 0)
+			{
+				if (s->flags & SAMPLE_16BIT)
+					currSize64 += s->length << 1;
+				else
+					currSize64 += s->length;
+			}
 		}
 	}
 
@@ -144,10 +151,9 @@
 
 static void wipeInstrUnused(bool testWipeSize, int16_t *ai, int32_t ap, int32_t antChn)
 {
-	uint8_t newInst;
-	int16_t pattLen;
+	int16_t numRows;
 	int32_t i, j, k;
-	tonTyp *pattPtr;
+	note_t *p;
 
 	int32_t numInsts = *ai;
 
@@ -157,31 +163,31 @@
 	{
 		if (testWipeSize)
 		{
-			pattPtr = tmpPatt[i];
-			pattLen = tmpPattLens[i];
+			p = tmpPatt[i];
+			numRows = tmpPattLens[i];
 		}
 		else
 		{
-			pattPtr = patt[i];
-			pattLen = pattLens[i];
+			p = pattern[i];
+			numRows = patternNumRows[i];
 		}
 
-		if (pattPtr == NULL)
+		if (p == NULL)
 			continue;
 
-		for (j = 0; j < pattLen; j++)
+		for (j = 0; j < numRows; j++)
 		{
 			for (k = 0; k < antChn; k++)
 			{
-				newInst = pattPtr[(j * MAX_VOICES) + k].instr;
-				if (newInst > 0 && newInst <= MAX_INST)
-					instrUsed[newInst-1] = true;
+				uint8_t ins = p[(j * MAX_CHANNELS) + k].instr;
+				if (ins > 0 && ins <= MAX_INST)
+					instrUsed[ins-1] = true;
 			}
 		}
 	}
 
 	int16_t instToDel = 0;
-	newInst = 0;
+	uint8_t newInst = 0;
 	int16_t newNumInsts = 0;
 
 	memset(instrOrder, 0, numInsts);
@@ -271,15 +277,15 @@
 {
 	uint8_t newPatt;
 	int16_t i, *pLens;
-	tonTyp **p;
+	note_t **p;
 
 	int16_t usedPatts = *ap;
 	memset(pattUsed, 0, usedPatts);
 
 	int16_t newUsedPatts = 0;
-	for (i = 0; i < song.len; i++)
+	for (i = 0; i < song.songLength; i++)
 	{
-		newPatt = song.songTab[i];
+		newPatt = song.orders[i];
 		if (newPatt < usedPatts && !pattUsed[newPatt])
 		{
 			pattUsed[newPatt] = true;
@@ -305,13 +311,13 @@
 	}
 	else
 	{
-		p = patt;
-		pLens = pattLens;
+		p = pattern;
+		pLens = patternNumRows;
 	}
 
-	memcpy(oldPatts, p, usedPatts * sizeof (tonTyp *));
+	memcpy(oldPatts, p, usedPatts * sizeof (note_t *));
 	memcpy(oldPattLens, pLens, usedPatts * sizeof (int16_t));
-	memset(p, 0, usedPatts * sizeof (tonTyp *));
+	memset(p, 0, usedPatts * sizeof (note_t *));
 	memset(pLens, 0, usedPatts * sizeof (int16_t));
 
 	// relocate patterns
@@ -339,17 +345,17 @@
 	{
 		for (i = 0; i < MAX_PATTERNS; i++)
 		{
-			if (patt[i] == NULL)
-				pattLens[i] = 64;
+			if (pattern[i] == NULL)
+				patternNumRows[i] = 64;
 		}
 
 		// reorder order list (and clear unused entries)
 		for (i = 0; i < 256; i++)
 		{
-			if (i < song.len)
-				song.songTab[i] = pattOrder[song.songTab[i]];
+			if (i < song.songLength)
+				song.orders[i] = pattOrder[song.orders[i]];
 			else
-				song.songTab[i] = 0;
+				song.orders[i] = 0;
 		}
 	}
 
@@ -360,8 +366,8 @@
 {
 	uint8_t smpUsed[16], smpOrder[16];
 	int16_t j, k, l;
-	instrTyp *ins;
-	sampleTyp tempSamples[16];
+	instr_t *ins;
+	sample_t tempSamples[16];
 
 	for (int16_t i = 1; i <= ai; i++)
 	{
@@ -389,13 +395,13 @@
 		memset(smpUsed, 0, l);
 		if (l > 0)
 		{
-			sampleTyp *s = ins->samp;
+			sample_t *s = ins->smp;
 			for (j = 0; j < l; j++, s++)
 			{
 				// check if sample is referenced in instrument
 				for (k = 0; k < 96; k++)
 				{
-					if (ins->ta[k] == j)
+					if (ins->note2SampleLUT[k] == j)
 					{
 						smpUsed[j] = true;
 						break; // sample is used
@@ -406,12 +412,10 @@
 				{
 					// sample is unused
 
-					if (s->origPek != NULL && !testWipeSize)
-						free(s->origPek);
+					if (s->dataPtr != NULL && !testWipeSize)
+						freeSmpData(s);
 
-					memset(s, 0, sizeof (sampleTyp));
-					s->origPek = NULL;
-					s->pek = NULL;
+					memset(s, 0, sizeof (sample_t));
 				}
 			}
 
@@ -426,23 +430,23 @@
 
 			// re-order samples
 
-			memcpy(tempSamples, ins->samp, l * sizeof (sampleTyp));
-			memset(ins->samp, 0, l * sizeof (sampleTyp));
+			memcpy(tempSamples, ins->smp, l * sizeof (sample_t));
+			memset(ins->smp, 0, l * sizeof (sample_t));
 
 			for (j = 0; j < l; j++)
 			{
 				if (smpUsed[j])
-					ins->samp[smpOrder[j]] = tempSamples[j];
+					ins->smp[smpOrder[j]] = tempSamples[j];
 			}
 
 			// re-order note->sample list
 			for (j = 0; j < 96; j++)
 			{
-				newSamp = ins->ta[j];
+				newSamp = ins->note2SampleLUT[j];
 				if (smpUsed[newSamp])
-					ins->ta[j] = smpOrder[newSamp];
+					ins->note2SampleLUT[j] = smpOrder[newSamp];
 				else
-					ins->ta[j] = 0;
+					ins->note2SampleLUT[j] = 0;
 			}
 		}
 	}
@@ -451,7 +455,7 @@
 static void wipeSmpDataAfterLoop(bool testWipeSize, int16_t ai)
 {
 	int16_t l;
-	instrTyp *ins;
+	instr_t *ins;
 
 	for (int16_t i = 1; i <= ai; i++)
 	{
@@ -476,33 +480,25 @@
 			l = getUsedTempSamples(i);
 		}
 
-		sampleTyp *s = ins->samp;
+		sample_t *s = ins->smp;
 		for (int16_t j = 0; j < l; j++, s++)
 		{
-			if (s->origPek != NULL && s->typ & 3 && s->len > 0 && s->len > s->repS+s->repL)
+			if (s->dataPtr != NULL && GET_LOOPTYPE(s->flags) != LOOP_OFF && s->length > 0 && s->length > s->loopStart+s->loopLength)
 			{
 				if (!testWipeSize)
-					restoreSample(s);
+					unfixSample(s);
 
-				s->len = s->repS + s->repL;
+				s->length = s->loopStart + s->loopLength;
 				if (!testWipeSize)
 				{
-					if (s->len <= 0)
+					if (s->length <= 0)
 					{
-						s->len = 0;
-
-						free(s->origPek);
-						s->origPek = NULL;
-						s->pek = NULL;
+						s->length = 0;
+						freeSmpData(s);
 					}
 					else
 					{
-						int8_t *newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
-						if (newPtr != NULL)
-						{
-							s->origPek = newPtr;
-							s->pek = s->origPek + SMP_DAT_OFFSET;
-						}
+						reallocateSmpData(s, s->length, !!(s->flags & SAMPLE_16BIT));
 					}
 				}
 
@@ -516,7 +512,7 @@
 static void convertSamplesTo8bit(bool testWipeSize, int16_t ai)
 {
 	int16_t k;
-	instrTyp *ins;
+	instr_t *ins;
 
 	for (int16_t i = 1; i <= ai; i++)
 	{
@@ -541,42 +537,28 @@
 			k = getUsedTempSamples(i);
 		}
 
-		sampleTyp *s = ins->samp;
+		sample_t *s = ins->smp;
 		for (int16_t j = 0; j < k; j++, s++)
 		{
-			if (s->origPek != NULL && (s->typ & 16) && s->len > 0)
+			if (s->dataPtr != NULL && s->length > 0 && (s->flags & SAMPLE_16BIT))
 			{
 				if (testWipeSize)
 				{
-					s->typ &= ~16;
-					s->len >>= 1;
-					s->repL >>= 1;
-					s->repS >>= 1;
+					s->flags &= ~SAMPLE_16BIT;
 				}
 				else
 				{
-					restoreSample(s);
+					unfixSample(s);
 
-					assert(s->pek != NULL);
-					const int16_t *src16 = (const int16_t *)s->pek;
-					int8_t *dst8 = s->pek;
+					const int16_t *src16 = (const int16_t *)s->dataPtr;
+					int8_t *dst8 = s->dataPtr;
 
-					const int32_t newLen = s->len >> 1;
-					for (int32_t a = 0; a < newLen; a++)
+					for (int32_t a = 0; a < s->length; a++)
 						dst8[a] = src16[a] >> 8;
 
-					s->repL >>= 1;
-					s->repS >>= 1;
-					s->len >>= 1;
-					s->typ &= ~16;
+					s->flags &= ~SAMPLE_16BIT;
 
-					int8_t *newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
-					if (newPtr != NULL)
-					{
-						s->origPek = newPtr;
-						s->pek = s->origPek + SMP_DAT_OFFSET;
-					}
-
+					reallocateSmpData(s, s->length, true);
 					fixSample(s);
 				}
 			}
@@ -584,12 +566,12 @@
 	}
 }
 
-static uint16_t getPackedPattSize(tonTyp *pattern, int32_t numRows, int32_t antChn)
+static uint16_t getPackedPattSize(note_t *p, int32_t numRows, int32_t antChn)
 {
-	uint8_t bytes[sizeof (tonTyp)];
+	uint8_t bytes[sizeof (note_t)];
 
 	uint16_t totalPackLen = 0;
-	uint8_t *pattPtr = (uint8_t *)pattern;
+	uint8_t *pattPtr = (uint8_t *)p;
 	uint8_t *writePtr = pattPtr;
 
 	for (int32_t row = 0; row < numRows; row++)
@@ -624,22 +606,22 @@
 		}
 
 		// skip unused channels
-		pattPtr += sizeof (tonTyp) * (MAX_VOICES - antChn);
+		pattPtr += sizeof (note_t) * (MAX_CHANNELS - antChn);
 	}
 
 	return totalPackLen;
 }
 
-static bool tmpPatternEmpty(uint16_t nr, int32_t antChn)
+static bool tmpPatternEmpty(uint16_t pattNum, int32_t numChannels)
 {
-	if (tmpPatt[nr] == NULL)
+	if (tmpPatt[pattNum] == NULL)
 		return true;
 
-	uint8_t *scanPtr = (uint8_t *)tmpPatt[nr];
-	int32_t scanLen = antChn * sizeof (tonTyp);
-	int32_t pattLen = tmpPattLens[nr];
+	uint8_t *scanPtr = (uint8_t *)tmpPatt[pattNum];
+	int32_t scanLen = numChannels * sizeof (note_t);
+	int32_t numRows = tmpPattLens[pattNum];
 
-	for (int32_t i = 0; i < pattLen; i++)
+	for (int32_t i = 0; i < numRows; i++, scanPtr += TRACK_WIDTH)
 	{
 		for (int32_t j = 0; j < scanLen; j++)
 		{
@@ -646,8 +628,6 @@
 			if (scanPtr[j] != 0)
 				return false;
 		}
-
-		scanPtr += TRACK_WIDTH;
 	}
 
 	return true;
@@ -656,7 +636,7 @@
 static int64_t calculateXMSize(void)
 {
 	// count header size in song
-	int64_t currSize64 = sizeof (songHeaderTyp);
+	int64_t currSize64 = sizeof (xmHdr_t);
 
 	// count number of patterns that would be saved
 	int16_t ap = MAX_PATTERNS;
@@ -677,9 +657,9 @@
 	// count packed pattern data size in song
 	for (int16_t i = 0; i < ap; i++)
 	{
-		currSize64 += sizeof (patternHeaderTyp);
+		currSize64 += sizeof (xmPatHdr_t);
 		if (!patternEmpty(i))
-			currSize64 += getPackedPattSize(patt[i], pattLens[i], song.antChn);
+			currSize64 += getPackedPattSize(pattern[i], patternNumRows[i], song.numChannels);
 	}
 
 	// count instrument and sample data size in song
@@ -693,15 +673,21 @@
 
 		const int16_t a = getUsedSamples(i);
 		if (a > 0)
-			currSize64 += INSTR_HEADER_SIZE + (a * sizeof (sampleHeaderTyp));
+			currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t));
 		else
 			currSize64 += 22+11;
 
-		instrTyp *ins = instr[j];
+		instr_t *ins = instr[j];
 		for (int16_t k = 0; k < a; k++)
 		{
-			if (ins->samp[k].pek != NULL)
-				currSize64 += ins->samp[k].len;
+			sample_t* s = &ins->smp[k];
+			if (s->dataPtr != NULL && s->length > 0)
+			{
+				if (s->flags & SAMPLE_16BIT)
+					currSize64 += s->length << 1;
+				else
+					currSize64 += s->length;
+			}
 		}
 	}
 
@@ -712,7 +698,7 @@
 {
 	int16_t i, j, k;
 
-	int32_t antChn = song.antChn;
+	int32_t numChannels = song.numChannels;
 	int32_t pattDataLen = 0;
 	int32_t newPattDataLen = 0;
 	int64_t bytes64 = 0;
@@ -719,8 +705,8 @@
 	int64_t oldInstrSize64 = 0;
 
 	// copy over temp data
-	memcpy(tmpPatt, patt, sizeof (tmpPatt));
-	memcpy(tmpPattLens, pattLens, sizeof (tmpPattLens));
+	memcpy(tmpPatt, pattern, sizeof (tmpPatt));
+	memcpy(tmpPattLens, patternNumRows, sizeof (tmpPattLens));
 	memcpy(tmpInstrName, song.instrName, sizeof (tmpInstrName));
 
 	if (!setTmpInstruments())
@@ -737,7 +723,7 @@
 	int16_t ap = MAX_PATTERNS;
 	do
 	{
-		if (tmpPatternEmpty(ap - 1, antChn))
+		if (tmpPatternEmpty(ap - 1, numChannels))
 			ap--;
 		else
 			break;
@@ -763,9 +749,9 @@
 	{
 		for (i = 0; i < ap; i++)
 		{
-			pattDataLen += sizeof (patternHeaderTyp);
-			if (!tmpPatternEmpty(i, antChn))
-				pattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], antChn);
+			pattDataLen += sizeof (xmPatHdr_t);
+			if (!tmpPatternEmpty(i, numChannels))
+				pattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels);
 		}
 	}
 
@@ -776,17 +762,17 @@
 		int16_t highestChan = -1;
 		for (i = 0; i < ap; i++)
 		{
-			tonTyp *pattPtr = tmpPatt[i];
+			note_t *pattPtr = tmpPatt[i];
 			if (pattPtr == NULL)
 				continue;
 
-			const int16_t pattLen = tmpPattLens[i];
-			for (j = 0; j < pattLen; j++)
+			const int16_t numRows = tmpPattLens[i];
+			for (j = 0; j < numRows; j++)
 			{
-				for (k = 0; k < antChn; k++)
+				for (k = 0; k < numChannels; k++)
 				{
-					tonTyp *note = &pattPtr[(j * MAX_VOICES) + k];
-					if (note->eff || note->effTyp || note->instr || note->ton || note->vol)
+					note_t *p = &pattPtr[(j * MAX_CHANNELS) + k];
+					if (p->note > 0 || p->instr > 0 || p->vol > 0 || p->efx > 0 || p->efxData > 0)
 					{
 						if (k > highestChan)
 							highestChan = k;
@@ -802,7 +788,7 @@
 			if (highestChan & 1)
 				highestChan++;
 
-			antChn = (uint8_t)(CLAMP(highestChan, 2, antChn));
+			numChannels = (uint8_t)(CLAMP(highestChan, 2, numChannels));
 		}
 	}
 
@@ -814,9 +800,9 @@
 	{
 		for (i = 0; i < ap; i++)
 		{
-			newPattDataLen += sizeof (patternHeaderTyp);
-			if (!tmpPatternEmpty(i, antChn))
-				newPattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], antChn);
+			newPattDataLen += sizeof (xmPatHdr_t);
+			if (!tmpPatternEmpty(i, numChannels))
+				newPattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels);
 		}
 
 		assert(pattDataLen >= newPattDataLen);
@@ -826,9 +812,9 @@
 	}
 
 	// calculate "remove unused instruments" size
-	if (removeInst) wipeInstrUnused(true, &ai, ap, antChn);
+	if (removeInst) wipeInstrUnused(true, &ai, ap, numChannels);
 
-	// calculat new instruments and samples size
+	// calculate new instruments and samples size
 	if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit)
 	{
 		int64_t newInstrSize64 = getTempInsAndSmpSize();
@@ -889,17 +875,17 @@
 		int16_t highestChan = -1;
 		for (i = 0; i < ap; i++)
 		{
-			tonTyp *pattPtr = patt[i];
+			note_t *pattPtr = pattern[i];
 			if (pattPtr == NULL)
 				continue;
 
-			const int16_t pattLen = pattLens[i];
-			for (j = 0; j < pattLen; j++)
+			const int16_t numRows = patternNumRows[i];
+			for (j = 0; j < numRows; j++)
 			{
-				for (k = 0; k < song.antChn; k++)
+				for (k = 0; k < song.numChannels; k++)
 				{
-					tonTyp *note = &pattPtr[(j * MAX_VOICES) + k];
-					if (note->eff || note->effTyp || note->instr || note->ton || note->vol)
+					note_t *p = &pattPtr[(j * MAX_CHANNELS) + k];
+					if (p->note > 0 || p->vol > 0 || p->instr > 0 || p->efx > 0 || p->efxData > 0)
 					{
 						if (k > highestChan)
 							highestChan = k;
@@ -915,21 +901,21 @@
 			if (highestChan & 1)
 				highestChan++;
 
-			song.antChn = (uint8_t)(CLAMP(highestChan, 2, song.antChn));
+			song.numChannels = (uint8_t)(CLAMP(highestChan, 2, song.numChannels));
 		}
 
 		// clear potentially unused channel data
-		if (song.antChn < MAX_VOICES)
+		if (song.numChannels < MAX_CHANNELS)
 		{
 			for (i = 0; i < MAX_PATTERNS; i++)
 			{
-				tonTyp *pattPtr = patt[i];
-				if (pattPtr == NULL)
+				note_t *p = pattern[i];
+				if (p == NULL)
 					continue;
 
-				const int16_t pattLen = pattLens[i];
-				for (j = 0; j < pattLen; j++)
-					memset(&pattPtr[(j * MAX_VOICES) + song.antChn], 0, sizeof (tonTyp) * (MAX_VOICES - song.antChn));
+				const int16_t numRows = patternNumRows[i];
+				for (j = 0; j < numRows; j++)
+					memset(&p[(j * MAX_CHANNELS) + song.numChannels], 0, sizeof (note_t) * (MAX_CHANNELS - song.numChannels));
 			}
 		}
 	}
@@ -940,13 +926,12 @@
 
 	// remove unused instruments
 	if (removeInst)
-		wipeInstrUnused(false, &ai, ap, song.antChn);
+		wipeInstrUnused(false, &ai, ap, song.numChannels);
 
 	freeTmpInstruments();
 	editor.trimThreadWasDone = true;
 
 	return true;
-
 	(void)ptr;
 }
 
@@ -953,7 +938,7 @@
 void trimThreadDone(void)
 {
 	if (removePatt)
-		setPos(song.songPos, song.pattPos, false);
+		setPos(song.songPos, song.row, false);
 
 	if (removeInst)
 	{
@@ -971,8 +956,8 @@
 	{
 		if (ui.patternEditorShown)
 		{
-			if (ui.channelOffset > song.antChn-ui.numChannelsShown)
-				setScrollBarPos(SB_CHAN_SCROLL, song.antChn - ui.numChannelsShown, true);
+			if (ui.channelOffset > song.numChannels-ui.numChannelsShown)
+				setScrollBarPos(SB_CHAN_SCROLL, song.numChannels - ui.numChannelsShown, true);
 		}
 
 		if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
--- a/src/ft2_video.c
+++ b/src/ft2_video.c
@@ -20,7 +20,7 @@
 #include "ft2_video.h"
 #include "ft2_events.h"
 #include "ft2_mouse.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_pattern_ed.h"
 #include "ft2_pattern_draw.h"
 #include "ft2_sample_ed.h"
@@ -34,6 +34,7 @@
 #include "ft2_midi.h"
 #include "ft2_bmp.h"
 #include "ft2_structs.h"
+#include "ft2_cpu.h"
 
 static const uint8_t textCursorData[12] =
 {
@@ -158,6 +159,9 @@
 
 void flipFrame(void)
 {
+	const uint32_t windowFlags = SDL_GetWindowFlags(video.window);
+	bool minimized = (windowFlags & SDL_WINDOW_MINIMIZED) ? true : false;
+
 	renderSprites();
 
 	if (video.showFPSCounter)
@@ -164,9 +168,14 @@
 		drawFPSCounter();
 
 	SDL_UpdateTexture(video.texture, NULL, video.frameBuffer, SCREEN_W * sizeof (int32_t));
-	SDL_RenderClear(video.renderer);
+
+	// SDL2 bug on Windows (?): This function consumes ever-increasing memory if the program is minimized
+	if (!minimized)
+		SDL_RenderClear(video.renderer);
+
 	SDL_RenderCopy(video.renderer, video.texture, NULL, NULL);
 	SDL_RenderPresent(video.renderer);
+
 	eraseSprites();
 
 	if (!video.vsync60HzPresent)
@@ -175,21 +184,19 @@
 	}
 	else
 	{
-		uint32_t windowFlags = SDL_GetWindowFlags(video.window);
-
 		/* We have VSync, but it can unexpectedly get inactive in certain scenarios.
 		** We have to force thread sleeping (to ~60Hz) if so.
 		*/
 #ifdef __APPLE__
 		// macOS: VSync gets disabled if the window is 100% covered by another window. Let's add a (crude) fix:
-		if ((windowFlags & SDL_WINDOW_MINIMIZED) || !(windowFlags & SDL_WINDOW_INPUT_FOCUS))
+		if (minimized || !(windowFlags & SDL_WINDOW_INPUT_FOCUS))
 			waitVBL();
 #elif __unix__
 		// *NIX: VSync gets disabled in fullscreen mode (at least on some distros/systems). Let's add a fix:
-		if ((windowFlags & SDL_WINDOW_MINIMIZED) || video.fullscreen)
+		if (minimized || video.fullscreen)
 			waitVBL();
 #else
-		if (windowFlags & SDL_WINDOW_MINIMIZED)
+		if (minimized)
 			waitVBL();
 #endif
 	}
@@ -267,6 +274,7 @@
 	// for mouse cursor creation
 	video.xScale = (uint32_t)round(video.renderW * (1.0 / SCREEN_W));
 	video.yScale = (uint32_t)round(video.renderH * (1.0 / SCREEN_H));
+
 	createMouseCursors();
 }
 
@@ -850,16 +858,16 @@
 		songTitleTrunc[sizeof (songTitleTrunc)-1] = '\0';
 
 		if (song.isModified)
-			sprintf(wndTitle, "Fasttracker II clone v%s - \"%s\" (unsaved)", PROG_VER_STR, songTitleTrunc);
+			sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"%s\" (unsaved)", PROG_VER_STR, CPU_BITS, songTitleTrunc);
 		else
-			sprintf(wndTitle, "Fasttracker II clone v%s - \"%s\"", PROG_VER_STR, songTitleTrunc);
+			sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"%s\"", PROG_VER_STR, CPU_BITS, songTitleTrunc);
 	}
 	else
 	{
 		if (song.isModified)
-			sprintf(wndTitle, "Fasttracker II clone v%s - \"untitled\" (unsaved)", PROG_VER_STR);
+			sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"untitled\" (unsaved)", PROG_VER_STR, CPU_BITS);
 		else
-			sprintf(wndTitle, "Fasttracker II clone v%s - \"untitled\"", PROG_VER_STR);
+			sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"untitled\"", PROG_VER_STR, CPU_BITS);
 	}
 
 	SDL_SetWindowTitle(video.window, wndTitle);
@@ -1004,6 +1012,7 @@
 	if (videoDriver != NULL && strcmp("KMSDRM", videoDriver) == 0)
 		video.useDesktopMouseCoords = false;
 
+	SDL_SetRenderDrawColor(video.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
 	return true;
 }
 
@@ -1018,7 +1027,7 @@
 		else if (ui.nibblesShown)
 		{
 			if (editor.NI_Play)
-				moveNibblePlayers();
+				moveNibblesPlayers();
 		}
 		else
 		{
@@ -1028,14 +1037,14 @@
 
 				if (!ui.diskOpShown)
 				{
-					drawSongRepS();
+					drawSongLoopStart();
 					drawSongLength();
 					drawPosEdNums(editor.songPos);
 					drawEditPattern(editor.editPattern);
 					drawPatternLength(editor.editPattern);
-					drawSongBPM(editor.speed);
-					drawSongSpeed(editor.tempo);
-					drawGlobalVol(editor.globalVol);
+					drawSongBPM(editor.BPM);
+					drawSongSpeed(editor.speed);
+					drawGlobalVol(editor.globalVolume);
 
 					if (!songPlaying || editor.wavIsRendering)
 						setScrollBarPos(SB_POS_ED, editor.songPos, false);
@@ -1057,7 +1066,7 @@
 			{
 				ui.updatePosEdScrollBar = false;
 				setScrollBarPos(SB_POS_ED, song.songPos, false);
-				setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5);
+				setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
 			}
 
 			if (!ui.extended)
@@ -1124,19 +1133,19 @@
 			if (ui.drawBPMFlag)
 			{
 				ui.drawBPMFlag = false;
-				drawSongBPM(editor.speed);
+				drawSongBPM(editor.BPM);
 			}
 			
 			if (ui.drawSpeedFlag)
 			{
 				ui.drawSpeedFlag = false;
-				drawSongSpeed(editor.tempo);
+				drawSongSpeed(editor.speed);
 			}
 
 			if (ui.drawGlobVolFlag)
 			{
 				ui.drawGlobVolFlag = false;
-				drawGlobalVol(editor.globalVol);
+				drawGlobalVol(editor.globalVolume);
 			}
 
 			if (ui.drawPosEdFlag)
@@ -1164,6 +1173,6 @@
 	{
 		ui.updatePatternEditor = false;
 		if (ui.patternEditorShown)
-			writePattern(editor.pattPos, editor.editPattern);
+			writePattern(editor.row, editor.editPattern);
 	}
 }
--- a/src/ft2_wav_renderer.c
+++ b/src/ft2_wav_renderer.c
@@ -7,10 +7,11 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "ft2_header.h"
+#include "ft2_audio.h"
 #include "ft2_gui.h"
 #include "ft2_pattern_ed.h"
 #include "ft2_diskop.h"
-#include "ft2_scopes.h"
+#include "scopes/ft2_scopes.h"
 #include "ft2_config.h"
 #include "ft2_mouse.h"
 #include "ft2_sample_ed.h"
@@ -36,10 +37,9 @@
 	uint32_t subchunk2ID, subchunk2Size;
 } wavHeader_t;
 
-static char WAV_SysReqText[192];
 static uint8_t WDBitDepth = 16, WDStartPos, WDStopPos, *wavRenderBuffer;
 static int16_t WDAmp;
-static uint32_t WDFrequency = 48000;
+static uint32_t WDFrequency = DEFAULT_AUDIO_FREQ;
 static SDL_Thread *thread;
 
 static void updateWavRenderer(void)
@@ -125,7 +125,7 @@
 void resetWavRenderer(void)
 {
 	WDStartPos = 0;
-	WDStopPos = (uint8_t)song.len - 1;
+	WDStopPos = (uint8_t)song.songLength - 1;
 
 	if (ui.wavRendererShown)
 		updateWavRenderer();
@@ -143,7 +143,7 @@
 	ui.scopesShown = false;
 
 	WDStartPos = 0;
-	WDStopPos = (uint8_t)song.len - 1;
+	WDStopPos = (uint8_t)song.songLength - 1;
 
 	drawWavRenderer();
 }
@@ -194,8 +194,8 @@
 	setAudioAmp(amp, config.masterVol, (WDBitDepth == 32));
 
 	stopVoices();
-	song.globVol = 64;
-	P_SetSpeed(song.speed);
+	song.globalVolume = 64;
+	setMixerBPM(song.BPM);
 
 	resetPlaybackTime();
 	return true;
@@ -251,12 +251,12 @@
 	stopPlaying();
 
 	// kludge: set speed to 6 if speed was set to 0
-	if (song.tempo == 0)
-		song.tempo = 6;
+	if (song.speed == 0)
+		song.speed = 6;
 
 	setBackOldAudioFreq();
-	P_SetSpeed(song.speed);
-	setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32);
+	setMixerBPM(song.BPM);
+	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
 	editor.wavIsRendering = false;
 
 	setMouseBusy(false);
@@ -264,13 +264,13 @@
 
 static bool dump_EndOfTune(int16_t endSongPos)
 {
-	bool returnValue = (editor.wavReachedEndFlag && song.pattPos == 0 && song.timer == 1) || (song.tempo == 0);
+	bool returnValue = (editor.wavReachedEndFlag && song.row == 0 && song.tick == 1) || (song.speed == 0);
 
 	// 8bitbubsy: FT2 bugfix for EEx (pattern delay) on first row of a pattern
 	if (song.pattDelTime2 > 0)
 		returnValue = false;
 
-	if (song.songPos == endSongPos && song.pattPos == 0 && song.timer == 1)
+	if (song.songPos == endSongPos && song.row == 0 && song.tick == 1)
 		editor.wavReachedEndFlag = true;
 
 	return returnValue;
@@ -291,12 +291,12 @@
 
 static void updateVisuals(void)
 {
-	editor.editPattern = (uint8_t)song.pattNr;
-	editor.pattPos = song.pattPos;
+	editor.editPattern = (uint8_t)song.pattNum;
+	editor.row = song.row;
 	editor.songPos = song.songPos;
+	editor.BPM = song.BPM;
 	editor.speed = song.speed;
-	editor.tempo = song.tempo;
-	editor.globalVol = song.globVol;
+	editor.globalVolume = song.globalVolume;
 
 	ui.drawPosEdFlag = true;
 	ui.drawPattNumLenFlag = true;
@@ -324,7 +324,7 @@
 	uint32_t sampleCounter = 0;
 	bool renderDone = false;
 	uint8_t tickCounter = 4;
-	double dTickSampleCounter = 0.0;
+	int64_t tickSampleCounter64 = 0;
 
 	editor.wavReachedEndFlag = false;
 	while (!renderDone)
@@ -341,17 +341,16 @@
 				break;
 			}
 
-			if (dTickSampleCounter <= 0.0)
+			if (tickSampleCounter64 <= 0) // new replayer tick
 			{
-				// new replayer tick
 				dump_TickReplayer();
-				dTickSampleCounter += audio.dSamplesPerTick;
+				tickSampleCounter64 += audio.samplesPerTick64;
 			}
 
-			int32_t remainingTick = (int32_t)ceil(dTickSampleCounter);
+			int32_t remainingTick = (tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards)
 
 			mixReplayerTickToBuffer(remainingTick, ptr8, WDBitDepth);
-			dTickSampleCounter -= remainingTick;
+			tickSampleCounter64 -= (int64_t)remainingTick << 32;
 
 			remainingTick *= 2; // stereo
 			samplesInChunk += remainingTick;
@@ -392,24 +391,10 @@
 	(void)ptr;
 }
 
-static void createOverwriteText(char *name)
-{
-	char nameTmp[128];
-
-	// read entry name to a small buffer
-	uint32_t nameLen = (uint32_t)strlen(name);
-	memcpy(nameTmp, name, (nameLen >= sizeof (nameTmp)) ? sizeof (nameTmp) : (nameLen + 1));
-	nameTmp[sizeof (nameTmp) - 1] = '\0';
-
-	trimEntryName(nameTmp, false);
-
-	sprintf(WAV_SysReqText, "Overwrite file \"%s\"?", nameTmp);
-}
-
 static void wavRender(bool checkOverwrite)
 {
-	WDStartPos = (uint8_t)(MAX(0, MIN(WDStartPos, song.len - 1)));
-	WDStopPos  = (uint8_t)(MAX(0, MIN(MAX(WDStartPos, WDStopPos), song.len - 1)));
+	WDStartPos = (uint8_t)(MAX(0, MIN(WDStartPos, song.songLength - 1)));
+	WDStopPos  = (uint8_t)(MAX(0, MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1)));
 
 	updateWavRenderer();
 
@@ -418,8 +403,9 @@
 	char *filename = getDiskOpFilename();
 	if (checkOverwrite && fileExistsAnsi(filename))
 	{
-		createOverwriteText(filename);
-		if (okBox(2, "System request", WAV_SysReqText) != 1)
+		char buf[256];
+		createFileOverwriteText(filename, buf);
+		if (okBox(2, "System request", buf) != 1)
 			return;
 	}
 
@@ -457,8 +443,10 @@
 	if (WDFrequency < MAX_WAV_RENDER_FREQ)
 	{
 		     if (WDFrequency == 44100) WDFrequency = 48000;
+#if CPU_64BIT
 		else if (WDFrequency == 48000) WDFrequency = 96000;
 		else if (WDFrequency == 96000) WDFrequency = 192000;
+#endif
 		updateWavRenderer();
 	}
 }
@@ -467,10 +455,13 @@
 {
 	if (WDFrequency > MIN_WAV_RENDER_FREQ)
 	{
+#if CPU_64BIT
 		     if (WDFrequency == 192000) WDFrequency = 96000;
 		else if (WDFrequency == 96000) WDFrequency = 48000;
 		else if (WDFrequency == 48000) WDFrequency = 44100;
-
+#else
+		if (WDFrequency == 48000) WDFrequency = 44100;
+#endif
 		updateWavRenderer();
 	}
 }
@@ -495,11 +486,11 @@
 
 void pbWavSongStartUp(void)
 {
-	if (WDStartPos >= song.len-1)
+	if (WDStartPos >= song.songLength-1)
 		return;
 
 	WDStartPos++;
-	WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.len - 1));
+	WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1));
 	updateWavRenderer();
 }
 
@@ -518,7 +509,7 @@
 		return;
 
 	WDStopPos++;
-	WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.len - 1));
+	WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1));
 	updateWavRenderer();
 }
 
@@ -528,7 +519,7 @@
 		return;
 
 	WDStopPos--;
-	WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.len - 1));
+	WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1));
 	updateWavRenderer();
 }
 
--- a/src/ft2_wav_renderer.h
+++ b/src/ft2_wav_renderer.h
@@ -2,9 +2,15 @@
 
 #include <stdint.h>
 #include "ft2_header.h"
+#include "ft2_cpu.h"
 
 #define MIN_WAV_RENDER_FREQ 44100
+
+#if CPU_64BIT
 #define MAX_WAV_RENDER_FREQ 192000
+#else
+#define MAX_WAV_RENDER_FREQ 48000
+#endif
 
 #define MAX_WAV_RENDER_SAMPLES_PER_TICK (((MAX_WAV_RENDER_FREQ * 5) / 2) / MIN_BPM)
 
@@ -15,7 +21,6 @@
 void showWavRenderer(void);
 void hideWavRenderer(void);
 void exitWavRenderer(void);
-void dump_RenderTick(uint32_t samplesPerTick, uint8_t *buffer);
 void pbWavRender(void);
 void pbWavExit(void);
 void pbWavFreqUp(void);
--- a/src/helpdata/FT2.HLP
+++ b/src/helpdata/FT2.HLP
@@ -9,7 +9,7 @@
 >- 12-Point Volume- & Panning Envelope.
 >- Multisamples, up to 16 samples/instrument.
 >- 128 instruments.
->- "Unlimited" sample length. (1GB in this clone)
+>- "Unlimited" sample length. (1GB per sample in this clone)
 >- 8 octaves.
 >- Variable pattern length.
 >- Built in sampler/sample editor.
@@ -150,7 +150,7 @@
 Syntax: 4 + Rate + Depth
 
 Adds vibrato to the channel with a rate and speed. Set vibrato
-control (E4) can be used to change the vibrato wave form (see
+control (E4) can be used to change the vibrato waveform (see
 below).
 
 @X040@C001Tone portamento + volume slide
@@ -241,11 +241,11 @@
 >@X060@C002
 Syntax: E4 + Type
 
-This command controls the vibrato wave form.
+This command controls the vibrato waveform.
 
 Type: 0 = Sine       1 = Ramp down       2 = Square
 
-If you add 4 to the type, the wave form will not be retrigged when a
+If you add 4 to the type, the waveform will not be retrigged when a
 new instrument is played.
 
 @X040@C001Set fine-tune
@@ -268,7 +268,7 @@
 Syntax: E7 + Type
 
 This command works exactly as set vibrato control, but the
-tremolo wave form will be changed instead.
+tremolo waveform will be changed instead.
 
 @X040@C001Retrig note
 >@X060@C002
@@ -377,7 +377,7 @@
 END
 ;***************************************************************************
 ;***************************************************************************
-@LKeyboard
+@LKeybindings
 
 >@X020@C002
 >If you have an ambition to create music efficiently we strongly recommend
@@ -392,6 +392,11 @@
 references to non-ordinary keys might be wrong.
 Sh = shift key.
 >
+@X040@C001Audio:
+>@X060@C002
+>Ctrl & numpad+ @T160Increase master volume by 16.
+>Ctrl & numpad- @T160Decrease master volume by 16.
+
 @X040@C001Video:
 >@X060@C002
 Alt+Enter @T160Toggle fullscreen mode
@@ -600,14 +605,14 @@
 >   1 Volume envelope
 >   1 Panning envelope
 >   1 Auto-vibrato definition
->   1..16 Sample(s)
+>   1..16 sample(s)
 >   1 Keyboard split definition
 >   1 MIDI definition
 
 >A Fasttracker 2 sample is:
->   1 Volume/Panning/Fine-tune definition
->   1 Relative tone.
->   1 Wave form.
+>   1 Volume/Panning/Finetune definition
+>   1 Relative note
+>   1 Waveform
 
 >@X040@C001The volume envelope:
 >@X060@C002
@@ -632,10 +637,12 @@
 >Same as above, except from that the vibrato is not connected to
 the panning envelope.
 
->@X040@C001Tune:
+>@X040@C001Tune (finetune):
 >@X060@C002
->The fine-tune resolution has been changed from a signed nibble
+>The finetune resolution has been changed from a signed nibble
 (-8..+7) to a signed byte (-128..+127).
+>NOTE: The last 3 bits are discarded during playback, so the true step
+size is 8 instead of 1.
 
 >@X040@C001Fadeout:
 >@X060@C002
@@ -656,7 +663,7 @@
 
 >@X040@C001Important note:
 >@X060@C002
->The volume, panning, tune and relative tone is defined for EACH
+>The volume, panning, finetune and relative note is defined for EACH
 SAMPLE in an instrument. All other information is defined for the
 entire instrument.
 
@@ -688,11 +695,11 @@
 
 @X020@C001Sample Editor:
 >
->@X040@C001Play (Wave form, range, display):
+>@X040@C001Play (Waveform, range, display):
 >@X060@C002
->Plays the current sample with tone display above the "stop"
+>Plays the current sample with the note displayed above the "stop"
 button. Note that respect is taken to the particular sample's
-relative tone.
+relative note.
 
 >@X040@C001Save range:
 >@X060@C002
@@ -742,11 +749,11 @@
 >@X060@C002
 >Operates on the range (or the whole sample if no range is set).
 
->@X040@C001Convert:
+>@X040@C001Sign:
 >@X060@C002
->Converts the entire sample from/to signed/unsigned.
+>Converts between signed/unsigned.
 
->@X040@C001Convert W:
+>@X040@C001B. swap (byte swap):
 >@X060@C002
 Swaps the byte order to/from Intel from/to Motorola standard on
 the entire sample.
@@ -765,7 +772,7 @@
 
 >@X040@C001Resample:
 >@X060@C002
-Operates on the entire sample. The sample's relative tone is
+Operates on the entire sample. The sample's relative note is
 changed with respect to the resampling rate.
 
 >@X040@C001Mix sample:
@@ -823,7 +830,7 @@
 >@X040@C001Scopes:
 >@X060@C002
 "Std." (standard) will show the sample points as pixels (like FT2).
-"Lined" will draw lines between the points, like an oscilloscope.
+"Lined" will draw interpolated samples (linear interpolation.
 
 @X020@C001Configuration, Miscellaneous:
 >
--- a/src/helpdata/ft2_help_data.h
+++ b/src/helpdata/ft2_help_data.h
@@ -3,9 +3,9 @@
 
 #include <stdint.h>
 
-#define HELP_DATA_LEN 27385
+#define HELP_DATA_LEN 27622
 
-const uint8_t helpData[27385] =
+const uint8_t helpData[27622] =
 {
 	0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
@@ -38,93 +38,94 @@
 	0x70,0x20,0x74,0x6F,0x20,0x31,0x36,0x20,0x73,0x61,0x6D,0x70,
 	0x6C,0x65,0x73,0x2F,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
 	0x6E,0x74,0x2E,0x13,0x3E,0x2D,0x20,0x31,0x32,0x38,0x20,0x69,
-	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x2E,0x31,
+	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x2E,0x3C,
 	0x3E,0x2D,0x20,0x22,0x55,0x6E,0x6C,0x69,0x6D,0x69,0x74,0x65,
 	0x64,0x22,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x6C,0x65,
-	0x6E,0x67,0x74,0x68,0x2E,0x20,0x28,0x31,0x47,0x42,0x20,0x69,
-	0x6E,0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6C,0x6F,0x6E,0x65,
-	0x29,0x0D,0x3E,0x2D,0x20,0x38,0x20,0x6F,0x63,0x74,0x61,0x76,
-	0x65,0x73,0x2E,0x1B,0x3E,0x2D,0x20,0x56,0x61,0x72,0x69,0x61,
-	0x62,0x6C,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,
-	0x6C,0x65,0x6E,0x67,0x74,0x68,0x2E,0x22,0x3E,0x2D,0x20,0x42,
-	0x75,0x69,0x6C,0x74,0x20,0x69,0x6E,0x20,0x73,0x61,0x6D,0x70,
-	0x6C,0x65,0x72,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,
-	0x64,0x69,0x74,0x6F,0x72,0x2E,0x16,0x3E,0x2D,0x20,0x55,0x70,
-	0x20,0x74,0x6F,0x20,0x32,0x35,0x36,0x20,0x70,0x61,0x74,0x74,
-	0x65,0x72,0x6E,0x73,0x2E,0x19,0x3E,0x2D,0x20,0x53,0x6F,0x6E,
-	0x67,0x20,0x6C,0x65,0x6E,0x67,0x74,0x68,0x20,0x75,0x70,0x20,
-	0x74,0x6F,0x20,0x32,0x35,0x36,0x2E,0x25,0x3E,0x2D,0x20,0x4E,
-	0x65,0x77,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2F,0x70,0x61,
-	0x6E,0x6E,0x69,0x6E,0x67,0x2F,0x76,0x69,0x62,0x72,0x61,0x74,
-	0x6F,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x2E,0x0F,0x3E,0x2D,
-	0x20,0x53,0x6F,0x6E,0x67,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,
-	0x2E,0x19,0x3E,0x2D,0x20,0x46,0x75,0x6C,0x6C,0x20,0x73,0x63,
-	0x72,0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x20,0x6D,0x6F,
-	0x64,0x65,0x2E,0x1F,0x3E,0x2D,0x20,0x49,0x6D,0x70,0x72,0x6F,
-	0x76,0x65,0x64,0x20,0x65,0x64,0x69,0x74,0x69,0x6E,0x67,0x20,
-	0x66,0x61,0x63,0x69,0x6C,0x69,0x74,0x69,0x65,0x73,0x2E,0x00,
-	0x3C,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,
-	0x68,0x65,0x20,0x46,0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,
-	0x20,0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x73,0x20,0x74,0x68,
-	0x65,0x20,0x66,0x6F,0x6C,0x6C,0x6F,0x77,0x69,0x6E,0x67,0x20,
-	0x66,0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D,0x61,0x74,0x73,
-	0x3A,0x00,0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
-	0x31,0x4D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x3A,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x32,0x3E,0x2D,
-	0x20,0x53,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x20,0x6D,0x6F,
-	0x64,0x75,0x6C,0x65,0x73,0x20,0x28,0x31,0x35,0x2F,0x33,0x31,
-	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x29,0x2E,0x20,0x28,
-	0x4D,0x4F,0x44,0x2C,0x4E,0x53,0x54,0x2C,0x53,0x54,0x4B,0x29,
-	0x23,0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,
-	0x6B,0x65,0x72,0x20,0x49,0x49,0x20,0x6D,0x6F,0x64,0x75,0x6C,
-	0x65,0x73,0x2E,0x20,0x28,0x58,0x4D,0x2C,0x4D,0x4F,0x44,0x29,
-	0x21,0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,
-	0x6B,0x65,0x72,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,
-	0x20,0x28,0x4D,0x4F,0x44,0x2F,0x46,0x53,0x54,0x29,0x00,0x32,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x78,
-	0x70,0x65,0x72,0x69,0x6D,0x65,0x6E,0x74,0x61,0x6C,0x20,0x28,
-	0x69,0x6D,0x70,0x65,0x72,0x66,0x65,0x63,0x74,0x29,0x20,0x6D,
-	0x6F,0x64,0x75,0x6C,0x65,0x20,0x73,0x75,0x70,0x70,0x6F,0x72,
-	0x74,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D,
-	0x20,0x54,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x33,0x20,0x6D,
-	0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x33,0x4D,
-	0x29,0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D,0x20,
-	0x54,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,0x20,0x6D,0x6F,
-	0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x54,0x4D,0x29,
-	0x29,0x3E,0x2D,0x20,0x44,0x49,0x47,0x49,0x20,0x42,0x6F,0x6F,
-	0x73,0x74,0x65,0x72,0x20,0x28,0x6E,0x6F,0x6E,0x2D,0x50,0x72,
-	0x6F,0x29,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,
-	0x28,0x44,0x49,0x47,0x49,0x29,0x00,0x12,0x40,0x58,0x30,0x34,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,
-	0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x22,0x3E,0x2D,0x20,0x47,0x72,0x61,0x76,0x69,0x73,
-	0x20,0x55,0x6C,0x74,0x72,0x61,0x73,0x6F,0x75,0x6E,0x64,0x20,
-	0x50,0x61,0x74,0x63,0x68,0x65,0x73,0x2C,0x20,0x50,0x41,0x54,
-	0x2E,0x33,0x3E,0x2D,0x20,0x53,0x4D,0x50,0x2F,0x53,0x41,0x4D,
-	0x2F,0x52,0x41,0x57,0x2F,0x53,0x4E,0x44,0x20,0x64,0x61,0x74,
-	0x61,0x20,0x66,0x69,0x6C,0x65,0x73,0x2C,0x20,0x73,0x69,0x67,
-	0x6E,0x65,0x64,0x20,0x61,0x6E,0x64,0x20,0x75,0x6E,0x73,0x69,
-	0x67,0x6E,0x65,0x64,0x2E,0x0D,0x3E,0x2D,0x20,0x57,0x41,0x56,
-	0x20,0x66,0x69,0x6C,0x65,0x73,0x2E,0x0D,0x3E,0x2D,0x20,0x41,
-	0x6D,0x69,0x67,0x61,0x20,0x49,0x46,0x46,0x2E,0x0E,0x3E,0x2D,
-	0x20,0x41,0x70,0x70,0x6C,0x65,0x20,0x41,0x49,0x46,0x46,0x2E,
-	0x00,0x32,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x46,0x54,0x32,0x20,0x69,0x6E,0x74,0x72,0x6F,0x64,0x75,0x63,
-	0x65,0x73,0x20,0x73,0x65,0x76,0x65,0x72,0x61,0x6C,0x20,0x6E,
-	0x65,0x77,0x20,0x66,0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D,
-	0x61,0x74,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x1C,0x3E,0x2D,0x20,0x58,0x4D,0x20,0x20,
-	0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,
-	0x64,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x2E,0x20,0x3E,0x2D,
-	0x20,0x58,0x49,0x20,0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78,
-	0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x69,0x6E,0x73,0x74,0x72,
-	0x75,0x6D,0x65,0x6E,0x74,0x2E,0x1D,0x3E,0x2D,0x20,0x58,0x50,
-	0x20,0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E,
-	0x64,0x65,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,
-	0x1B,0x3E,0x2D,0x20,0x58,0x54,0x20,0x20,0x40,0x54,0x31,0x31,
-	0x30,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x74,0x72,
-	0x61,0x63,0x6B,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,
+	0x6E,0x67,0x74,0x68,0x2E,0x20,0x28,0x31,0x47,0x42,0x20,0x70,
+	0x65,0x72,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x69,0x6E,
+	0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x29,
+	0x0D,0x3E,0x2D,0x20,0x38,0x20,0x6F,0x63,0x74,0x61,0x76,0x65,
+	0x73,0x2E,0x1B,0x3E,0x2D,0x20,0x56,0x61,0x72,0x69,0x61,0x62,
+	0x6C,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,
+	0x65,0x6E,0x67,0x74,0x68,0x2E,0x22,0x3E,0x2D,0x20,0x42,0x75,
+	0x69,0x6C,0x74,0x20,0x69,0x6E,0x20,0x73,0x61,0x6D,0x70,0x6C,
+	0x65,0x72,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,
+	0x69,0x74,0x6F,0x72,0x2E,0x16,0x3E,0x2D,0x20,0x55,0x70,0x20,
+	0x74,0x6F,0x20,0x32,0x35,0x36,0x20,0x70,0x61,0x74,0x74,0x65,
+	0x72,0x6E,0x73,0x2E,0x19,0x3E,0x2D,0x20,0x53,0x6F,0x6E,0x67,
+	0x20,0x6C,0x65,0x6E,0x67,0x74,0x68,0x20,0x75,0x70,0x20,0x74,
+	0x6F,0x20,0x32,0x35,0x36,0x2E,0x25,0x3E,0x2D,0x20,0x4E,0x65,
+	0x77,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2F,0x70,0x61,0x6E,
+	0x6E,0x69,0x6E,0x67,0x2F,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,
+	0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x2E,0x0F,0x3E,0x2D,0x20,
+	0x53,0x6F,0x6E,0x67,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x2E,
+	0x19,0x3E,0x2D,0x20,0x46,0x75,0x6C,0x6C,0x20,0x73,0x63,0x72,
+	0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x20,0x6D,0x6F,0x64,
+	0x65,0x2E,0x1F,0x3E,0x2D,0x20,0x49,0x6D,0x70,0x72,0x6F,0x76,
+	0x65,0x64,0x20,0x65,0x64,0x69,0x74,0x69,0x6E,0x67,0x20,0x66,
+	0x61,0x63,0x69,0x6C,0x69,0x74,0x69,0x65,0x73,0x2E,0x00,0x3C,
+	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,
+	0x65,0x20,0x46,0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x20,
+	0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x73,0x20,0x74,0x68,0x65,
+	0x20,0x66,0x6F,0x6C,0x6C,0x6F,0x77,0x69,0x6E,0x67,0x20,0x66,
+	0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D,0x61,0x74,0x73,0x3A,
+	0x00,0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x4D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x3A,0x0B,0x3E,0x40,0x58,
+	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x32,0x3E,0x2D,0x20,
+	0x53,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x20,0x6D,0x6F,0x64,
+	0x75,0x6C,0x65,0x73,0x20,0x28,0x31,0x35,0x2F,0x33,0x31,0x20,
+	0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x29,0x2E,0x20,0x28,0x4D,
+	0x4F,0x44,0x2C,0x4E,0x53,0x54,0x2C,0x53,0x54,0x4B,0x29,0x23,
+	0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,
+	0x65,0x72,0x20,0x49,0x49,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,
+	0x73,0x2E,0x20,0x28,0x58,0x4D,0x2C,0x4D,0x4F,0x44,0x29,0x21,
+	0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,
+	0x65,0x72,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,
+	0x28,0x4D,0x4F,0x44,0x2F,0x46,0x53,0x54,0x29,0x00,0x32,0x40,
+	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x70,
+	0x65,0x72,0x69,0x6D,0x65,0x6E,0x74,0x61,0x6C,0x20,0x28,0x69,
+	0x6D,0x70,0x65,0x72,0x66,0x65,0x63,0x74,0x29,0x20,0x6D,0x6F,
+	0x64,0x75,0x6C,0x65,0x20,0x73,0x75,0x70,0x70,0x6F,0x72,0x74,
+	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
+	0x32,0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D,0x20,
+	0x54,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x33,0x20,0x6D,0x6F,
+	0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x33,0x4D,0x29,
+	0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D,0x20,0x54,
+	0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,0x20,0x6D,0x6F,0x64,
+	0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x54,0x4D,0x29,0x29,
+	0x3E,0x2D,0x20,0x44,0x49,0x47,0x49,0x20,0x42,0x6F,0x6F,0x73,
+	0x74,0x65,0x72,0x20,0x28,0x6E,0x6F,0x6E,0x2D,0x50,0x72,0x6F,
+	0x29,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,
+	0x44,0x49,0x47,0x49,0x29,0x00,0x12,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,0x73,
+	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
+	0x32,0x22,0x3E,0x2D,0x20,0x47,0x72,0x61,0x76,0x69,0x73,0x20,
+	0x55,0x6C,0x74,0x72,0x61,0x73,0x6F,0x75,0x6E,0x64,0x20,0x50,
+	0x61,0x74,0x63,0x68,0x65,0x73,0x2C,0x20,0x50,0x41,0x54,0x2E,
+	0x33,0x3E,0x2D,0x20,0x53,0x4D,0x50,0x2F,0x53,0x41,0x4D,0x2F,
+	0x52,0x41,0x57,0x2F,0x53,0x4E,0x44,0x20,0x64,0x61,0x74,0x61,
+	0x20,0x66,0x69,0x6C,0x65,0x73,0x2C,0x20,0x73,0x69,0x67,0x6E,
+	0x65,0x64,0x20,0x61,0x6E,0x64,0x20,0x75,0x6E,0x73,0x69,0x67,
+	0x6E,0x65,0x64,0x2E,0x0D,0x3E,0x2D,0x20,0x57,0x41,0x56,0x20,
+	0x66,0x69,0x6C,0x65,0x73,0x2E,0x0D,0x3E,0x2D,0x20,0x41,0x6D,
+	0x69,0x67,0x61,0x20,0x49,0x46,0x46,0x2E,0x0E,0x3E,0x2D,0x20,
+	0x41,0x70,0x70,0x6C,0x65,0x20,0x41,0x49,0x46,0x46,0x2E,0x00,
+	0x32,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,
+	0x54,0x32,0x20,0x69,0x6E,0x74,0x72,0x6F,0x64,0x75,0x63,0x65,
+	0x73,0x20,0x73,0x65,0x76,0x65,0x72,0x61,0x6C,0x20,0x6E,0x65,
+	0x77,0x20,0x66,0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D,0x61,
+	0x74,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x1C,0x3E,0x2D,0x20,0x58,0x4D,0x20,0x20,0x40,
+	0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,
+	0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x2E,0x20,0x3E,0x2D,0x20,
+	0x58,0x49,0x20,0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74,
+	0x65,0x6E,0x64,0x65,0x64,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
+	0x6D,0x65,0x6E,0x74,0x2E,0x1D,0x3E,0x2D,0x20,0x58,0x50,0x20,
+	0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E,0x64,
+	0x65,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x1B,
+	0x3E,0x2D,0x20,0x58,0x54,0x20,0x20,0x40,0x54,0x31,0x31,0x30,
+	0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x74,0x72,0x61,
+	0x63,0x6B,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
@@ -131,1447 +132,1467 @@
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
+	0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x09,0x40,0x4C,0x45,0x66,
-	0x66,0x65,0x63,0x74,0x73,0x00,0x18,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x53,0x68,0x6F,0x72,0x74,0x20,0x73,
-	0x75,0x6D,0x6D,0x61,0x72,0x79,0x3A,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x0B,0x3E,0x30,0x20,0x41,
-	0x72,0x70,0x65,0x67,0x67,0x69,0x6F,0x10,0x3E,0x31,0x20,0x50,
-	0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,
-	0x12,0x3E,0x32,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,
-	0x74,0x6F,0x20,0x64,0x6F,0x77,0x6E,0x12,0x3E,0x33,0x20,0x54,
-	0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,
-	0x74,0x6F,0x0A,0x3E,0x34,0x20,0x56,0x69,0x62,0x72,0x61,0x74,
-	0x6F,0x1C,0x3E,0x35,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,
-	0x6E,0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,
-	0x20,0x73,0x6C,0x69,0x64,0x65,0x19,0x3E,0x36,0x20,0x56,0x69,
-	0x62,0x72,0x61,0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,
-	0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x37,0x20,
-	0x54,0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x17,0x3E,0x38,0x20,0x53,
-	0x65,0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,
-	0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x39,0x20,0x53,
-	0x61,0x6D,0x70,0x6C,0x65,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,
-	0x0F,0x3E,0x41,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,
-	0x6C,0x69,0x64,0x65,0x10,0x3E,0x42,0x20,0x50,0x6F,0x73,0x69,
-	0x74,0x69,0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0D,0x3E,0x43,
-	0x20,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x10,
-	0x3E,0x44,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x62,
-	0x72,0x65,0x61,0x6B,0x04,0x3E,0x45,0x20,0x2B,0x23,0x3E,0x40,
-	0x58,0x30,0x38,0x30,0x30,0x20,0x46,0x69,0x6C,0x74,0x65,0x72,
-	0x20,0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x20,0x28,0x41,0x6D,0x69,
-	0x67,0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x15,0x3E,0x31,
-	0x20,0x46,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,
-	0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x17,0x3E,0x32,0x20,0x46,
-	0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,
-	0x74,0x6F,0x20,0x64,0x6F,0x77,0x6E,0x18,0x3E,0x33,0x20,0x53,
-	0x65,0x74,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F,
-	0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x16,0x3E,0x34,0x20,
-	0x53,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,
-	0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x10,0x3E,0x35,0x20,0x53,
-	0x65,0x74,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,
-	0x0C,0x3E,0x36,0x20,0x4A,0x75,0x6D,0x70,0x20,0x6C,0x6F,0x6F,
-	0x70,0x16,0x3E,0x37,0x20,0x53,0x65,0x74,0x20,0x74,0x72,0x65,
-	0x6D,0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,
-	0x09,0x3E,0x38,0x20,0x55,0x6E,0x75,0x73,0x65,0x64,0x0E,0x3E,
-	0x39,0x20,0x52,0x65,0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,
-	0x65,0x17,0x3E,0x41,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,
-	0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,
-	0x70,0x19,0x3E,0x42,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,
-	0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x64,
-	0x6F,0x77,0x6E,0x0B,0x3E,0x43,0x20,0x4E,0x6F,0x74,0x65,0x20,
-	0x63,0x75,0x74,0x0D,0x3E,0x44,0x20,0x4E,0x6F,0x74,0x65,0x20,
-	0x64,0x65,0x6C,0x61,0x79,0x10,0x3E,0x45,0x20,0x50,0x61,0x74,
-	0x74,0x65,0x72,0x6E,0x20,0x64,0x65,0x6C,0x61,0x79,0x1D,0x3E,
-	0x46,0x20,0x46,0x75,0x6E,0x6B,0x20,0x69,0x74,0x21,0x20,0x28,
-	0x4E,0x6F,0x74,0x20,0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,
-	0x74,0x65,0x64,0x29,0x06,0x3E,0x40,0x58,0x30,0x36,0x30,0x0B,
-	0x46,0x20,0x53,0x65,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x14,
-	0x3E,0x47,0x20,0x53,0x65,0x74,0x20,0x67,0x6C,0x6F,0x62,0x61,
-	0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x16,0x3E,0x48,0x20,
-	0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,
-	0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x4B,0x20,0x4B,
-	0x65,0x79,0x20,0x6F,0x66,0x66,0x18,0x3E,0x4C,0x20,0x53,0x65,
-	0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70,
-	0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x50,0x20,0x50,
-	0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,
-	0x14,0x3E,0x52,0x20,0x4D,0x75,0x6C,0x74,0x69,0x20,0x72,0x65,
-	0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65,0x09,0x3E,0x54,
-	0x20,0x54,0x72,0x65,0x6D,0x6F,0x72,0x04,0x3E,0x58,0x20,0x2B,
-	0x20,0x3E,0x40,0x58,0x30,0x38,0x30,0x31,0x20,0x45,0x78,0x74,
-	0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,
-	0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x1D,0x3E,0x32,
-	0x20,0x45,0x78,0x74,0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20,
-	0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x64,
-	0x6F,0x77,0x6E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
-	0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F,
-	0x6C,0x75,0x6D,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
-	0x40,0x43,0x30,0x30,0x32,0x17,0x30,0x30,0x2E,0x2E,0x34,0x30,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x6F,
-	0x6C,0x75,0x6D,0x65,0x2E,0x01,0x3E,0x1A,0x3E,0x2D,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,
-	0x6C,0x69,0x64,0x65,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x18,0x3E,
-	0x2B,0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D,
-	0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x35,
-	0x3E,0x44,0x20,0x40,0x54,0x31,0x36,0x30,0x46,0x69,0x6E,0x65,
+	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x09,0x40,0x4C,0x45,0x66,0x66,
+	0x65,0x63,0x74,0x73,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x53,0x68,0x6F,0x72,0x74,0x20,0x73,0x75,
+	0x6D,0x6D,0x61,0x72,0x79,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x0B,0x3E,0x30,0x20,0x41,0x72,
+	0x70,0x65,0x67,0x67,0x69,0x6F,0x10,0x3E,0x31,0x20,0x50,0x6F,
+	0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x12,
+	0x3E,0x32,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,
+	0x6F,0x20,0x64,0x6F,0x77,0x6E,0x12,0x3E,0x33,0x20,0x54,0x6F,
+	0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,
+	0x6F,0x0A,0x3E,0x34,0x20,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,
+	0x1C,0x3E,0x35,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,
+	0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,
+	0x73,0x6C,0x69,0x64,0x65,0x19,0x3E,0x36,0x20,0x56,0x69,0x62,
+	0x72,0x61,0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,
+	0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x37,0x20,0x54,
+	0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x17,0x3E,0x38,0x20,0x53,0x65,
+	0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,
+	0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x39,0x20,0x53,0x61,
+	0x6D,0x70,0x6C,0x65,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x0F,
+	0x3E,0x41,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,
+	0x69,0x64,0x65,0x10,0x3E,0x42,0x20,0x50,0x6F,0x73,0x69,0x74,
+	0x69,0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0D,0x3E,0x43,0x20,
+	0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x10,0x3E,
+	0x44,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x62,0x72,
+	0x65,0x61,0x6B,0x04,0x3E,0x45,0x20,0x2B,0x23,0x3E,0x40,0x58,
+	0x30,0x38,0x30,0x30,0x20,0x46,0x69,0x6C,0x74,0x65,0x72,0x20,
+	0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x20,0x28,0x41,0x6D,0x69,0x67,
+	0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x15,0x3E,0x31,0x20,
+	0x46,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,
+	0x6E,0x74,0x6F,0x20,0x75,0x70,0x17,0x3E,0x32,0x20,0x46,0x69,
+	0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,
+	0x6F,0x20,0x64,0x6F,0x77,0x6E,0x18,0x3E,0x33,0x20,0x53,0x65,
+	0x74,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F,0x20,
+	0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x16,0x3E,0x34,0x20,0x53,
+	0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x63,
+	0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x10,0x3E,0x35,0x20,0x53,0x65,
+	0x74,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x0C,
+	0x3E,0x36,0x20,0x4A,0x75,0x6D,0x70,0x20,0x6C,0x6F,0x6F,0x70,
+	0x16,0x3E,0x37,0x20,0x53,0x65,0x74,0x20,0x74,0x72,0x65,0x6D,
+	0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x09,
+	0x3E,0x38,0x20,0x55,0x6E,0x75,0x73,0x65,0x64,0x0E,0x3E,0x39,
+	0x20,0x52,0x65,0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65,
+	0x17,0x3E,0x41,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,
+	0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,
+	0x19,0x3E,0x42,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,
+	0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x64,0x6F,
+	0x77,0x6E,0x0B,0x3E,0x43,0x20,0x4E,0x6F,0x74,0x65,0x20,0x63,
+	0x75,0x74,0x0D,0x3E,0x44,0x20,0x4E,0x6F,0x74,0x65,0x20,0x64,
+	0x65,0x6C,0x61,0x79,0x10,0x3E,0x45,0x20,0x50,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x20,0x64,0x65,0x6C,0x61,0x79,0x1D,0x3E,0x46,
+	0x20,0x46,0x75,0x6E,0x6B,0x20,0x69,0x74,0x21,0x20,0x28,0x4E,
+	0x6F,0x74,0x20,0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,
+	0x65,0x64,0x29,0x06,0x3E,0x40,0x58,0x30,0x36,0x30,0x0B,0x46,
+	0x20,0x53,0x65,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x14,0x3E,
+	0x47,0x20,0x53,0x65,0x74,0x20,0x67,0x6C,0x6F,0x62,0x61,0x6C,
+	0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x16,0x3E,0x48,0x20,0x47,
+	0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
+	0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x4B,0x20,0x4B,0x65,
+	0x79,0x20,0x6F,0x66,0x66,0x18,0x3E,0x4C,0x20,0x53,0x65,0x74,
+	0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70,0x6F,
+	0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x50,0x20,0x50,0x61,
+	0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x14,
+	0x3E,0x52,0x20,0x4D,0x75,0x6C,0x74,0x69,0x20,0x72,0x65,0x74,
+	0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65,0x09,0x3E,0x54,0x20,
+	0x54,0x72,0x65,0x6D,0x6F,0x72,0x04,0x3E,0x58,0x20,0x2B,0x20,
+	0x3E,0x40,0x58,0x30,0x38,0x30,0x31,0x20,0x45,0x78,0x74,0x72,
+	0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,
+	0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x1D,0x3E,0x32,0x20,
+	0x45,0x78,0x74,0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70,
+	0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x64,0x6F,
+	0x77,0x6E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
+	0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F,0x6C,
+	0x75,0x6D,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
+	0x43,0x30,0x30,0x32,0x17,0x30,0x30,0x2E,0x2E,0x34,0x30,0x20,
+	0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,
+	0x75,0x6D,0x65,0x2E,0x01,0x3E,0x1A,0x3E,0x2D,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,
+	0x69,0x64,0x65,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x18,0x3E,0x2B,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D,0x65,
+	0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x35,0x3E,
+	0x44,0x20,0x40,0x54,0x31,0x36,0x30,0x46,0x69,0x6E,0x65,0x20,
+	0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,
+	0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,
+	0x63,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,
+	0x62,0x6F,0x6C,0x29,0x33,0x3E,0x55,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
+	0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x20,0x28,
+	0x49,0x6E,0x64,0x69,0x63,0x61,0x74,0x65,0x64,0x20,0x62,0x79,
+	0x20,0x73,0x79,0x6D,0x62,0x6F,0x6C,0x29,0x1A,0x3E,0x53,0x20,
+	0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x69,0x62,
+	0x72,0x61,0x74,0x6F,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,0x10,
+	0x3E,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x69,0x62,0x72,
+	0x61,0x74,0x6F,0x2E,0x1D,0x3E,0x50,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x53,0x65,0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,
+	0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x32,0x3E,
+	0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x6E,0x6E,0x69,
+	0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x72,0x69,0x67,
+	0x68,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,0x63,0x61,0x74,
+	0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,0x62,0x6F,0x6C,
+	0x29,0x31,0x3E,0x4C,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,
+	0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,
+	0x6C,0x65,0x66,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,0x63,
+	0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,0x62,
+	0x6F,0x6C,0x29,0x18,0x3E,0x4D,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x54,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,
+	0x6E,0x74,0x6F,0x2E,0x00,0x00,0x1B,0x40,0x4C,0x40,0x58,0x30,
+	0x30,0x30,0x44,0x65,0x74,0x61,0x69,0x6C,0x65,0x64,0x20,0x69,
+	0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E,0x00,0x12,
+	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x72,
+	0x70,0x65,0x67,0x67,0x69,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x27,0x53,0x79,0x6E,0x74,0x61,
+	0x78,0x3A,0x20,0x30,0x20,0x2B,0x20,0x31,0x73,0x74,0x20,0x68,
+	0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x20,0x2B,0x20,0x32,0x6E,
+	0x64,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x00,0x0D,
+	0x45,0x78,0x2E,0x3A,0x20,0x43,0x2D,0x31,0x20,0x20,0x30,0x33,
+	0x37,0x00,0x16,0x3E,0x31,0x30,0x20,0x50,0x6C,0x61,0x79,0x73,
+	0x20,0x43,0x2D,0x31,0x20,0x74,0x69,0x63,0x6B,0x20,0x23,0x31,
+	0x2E,0x26,0x3E,0x32,0x30,0x20,0x50,0x6C,0x61,0x79,0x73,0x20,
+	0x43,0x2D,0x31,0x20,0x2B,0x20,0x33,0x20,0x4E,0x6F,0x74,0x65,
+	0x73,0x20,0x3D,0x20,0x44,0x23,0x31,0x20,0x74,0x69,0x63,0x6B,
+	0x20,0x23,0x32,0x2E,0x26,0x3E,0x33,0x30,0x20,0x50,0x6C,0x61,
+	0x79,0x73,0x20,0x43,0x2D,0x31,0x20,0x2B,0x20,0x37,0x20,0x4E,
+	0x6F,0x74,0x65,0x73,0x20,0x3D,0x20,0x47,0x2D,0x31,0x20,0x74,
+	0x69,0x63,0x6B,0x20,0x23,0x33,0x2E,0x0B,0x3E,0x34,0x30,0x20,
+	0x47,0x6F,0x74,0x6F,0x20,0x31,0x30,0x00,0x1C,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x72,0x74,0x61,
+	0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,
+	0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
+	0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x28,0x31,
+	0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53,0x70,0x65,
+	0x65,0x64,0x00,0x40,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,
+	0x74,0x6F,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20,0x74,
+	0x6F,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x74,0x68,0x65,0x20,
+	0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x69,0x74,0x63,0x68,
+	0x20,0x75,0x70,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x2E,
+	0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x42,0x64,0x6F,0x6E,
+	0x65,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,0x20,
+	0x70,0x65,0x72,0x69,0x6F,0x64,0x20,0x76,0x61,0x6C,0x75,0x65,
+	0x2E,0x20,0x49,0x66,0x20,0x41,0x6D,0x69,0x67,0x61,0x20,0x66,
+	0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62,
+	0x6C,0x65,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x2C,0x20,
+	0x74,0x68,0x65,0x40,0x73,0x6C,0x69,0x64,0x69,0x6E,0x67,0x20,
+	0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x6E,0x6F,0x6E,0x2D,
+	0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x28,0x74,0x68,0x65,0x20,
+	0x73,0x70,0x65,0x65,0x64,0x20,0x64,0x65,0x70,0x65,0x6E,0x64,
+	0x73,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,
+	0x71,0x75,0x65,0x6E,0x63,0x79,0x29,0x2E,0x00,0x19,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E,0x65,
+	0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,
+	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x33,0x20,0x2B,0x20,
+	0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69,0x73,0x20,
+	0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x69,0x73,0x20,0x75,
+	0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74,0x68,0x65,0x72,
+	0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,
+	0x2C,0x20,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,
+	0x6C,0x69,0x64,0x65,0x20,0x74,0x6F,0x20,0x69,0x74,0x73,0x43,
+	0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x2E,0x20,0x49,
+	0x66,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F,0x20,
+	0x28,0x45,0x33,0x29,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,
+	0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,0x71,0x75,0x65,
+	0x6E,0x63,0x79,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,
+	0x72,0x6F,0x75,0x6E,0x64,0x65,0x64,0x18,0x74,0x6F,0x20,0x74,
+	0x68,0x65,0x20,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20,0x68,
+	0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x2E,0x00,0x11,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x62,0x72,
+	0x61,0x74,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,
+	0x34,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20,0x2B,0x20,0x44,
+	0x65,0x70,0x74,0x68,0x00,0x3E,0x41,0x64,0x64,0x73,0x20,0x76,
+	0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74,0x68,
+	0x65,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x77,0x69,
+	0x74,0x68,0x20,0x61,0x20,0x72,0x61,0x74,0x65,0x20,0x61,0x6E,
+	0x64,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,0x20,0x53,0x65,0x74,
+	0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x3C,0x63,0x6F,0x6E,
+	0x74,0x72,0x6F,0x6C,0x20,0x28,0x45,0x34,0x29,0x20,0x63,0x61,
+	0x6E,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F,
+	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x74,0x68,0x65,0x20,
+	0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77,0x61,0x76,0x65,
+	0x66,0x6F,0x72,0x6D,0x20,0x28,0x73,0x65,0x65,0x07,0x62,0x65,
+	0x6C,0x6F,0x77,0x29,0x2E,0x00,0x28,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E,0x65,0x20,0x70,0x6F,
+	0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x2B,0x20,0x76,
+	0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,
+	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x35,0x20,0x2B,0x20,
+	0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69,0x73,0x20,
+	0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,
+	0x20,0x65,0x78,0x65,0x63,0x75,0x74,0x65,0x20,0x62,0x6F,0x74,
+	0x68,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,
+	0x6D,0x65,0x6E,0x74,0x6F,0x20,0x61,0x6E,0x64,0x20,0x76,0x6F,
+	0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x27,
+	0x54,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x69,0x73,
+	0x20,0x75,0x73,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,
+	0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,
+	0x64,0x65,0x2E,0x00,0x20,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x2B,
 	0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,
-	0x65,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x28,0x49,0x6E,0x64,
-	0x69,0x63,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,
-	0x6D,0x62,0x6F,0x6C,0x29,0x33,0x3E,0x55,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,
-	0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x20,
-	0x28,0x49,0x6E,0x64,0x69,0x63,0x61,0x74,0x65,0x64,0x20,0x62,
-	0x79,0x20,0x73,0x79,0x6D,0x62,0x6F,0x6C,0x29,0x1A,0x3E,0x53,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x69,
-	0x62,0x72,0x61,0x74,0x6F,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,
-	0x10,0x3E,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x69,0x62,
-	0x72,0x61,0x74,0x6F,0x2E,0x1D,0x3E,0x50,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x53,0x65,0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,
-	0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x32,
-	0x3E,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x6E,0x6E,
-	0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x72,0x69,
-	0x67,0x68,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,0x63,0x61,
-	0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,0x62,0x6F,
-	0x6C,0x29,0x31,0x3E,0x4C,0x20,0x40,0x54,0x31,0x36,0x30,0x50,
-	0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,
-	0x20,0x6C,0x65,0x66,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,
-	0x63,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,
-	0x62,0x6F,0x6C,0x29,0x18,0x3E,0x4D,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x54,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,
-	0x65,0x6E,0x74,0x6F,0x2E,0x00,0x00,0x1B,0x40,0x4C,0x40,0x58,
-	0x30,0x30,0x30,0x44,0x65,0x74,0x61,0x69,0x6C,0x65,0x64,0x20,
-	0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E,0x00,
-	0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,
-	0x72,0x70,0x65,0x67,0x67,0x69,0x6F,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x27,0x53,0x79,0x6E,0x74,
-	0x61,0x78,0x3A,0x20,0x30,0x20,0x2B,0x20,0x31,0x73,0x74,0x20,
-	0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x20,0x2B,0x20,0x32,
-	0x6E,0x64,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x00,
-	0x0D,0x45,0x78,0x2E,0x3A,0x20,0x43,0x2D,0x31,0x20,0x20,0x30,
-	0x33,0x37,0x00,0x16,0x3E,0x31,0x30,0x20,0x50,0x6C,0x61,0x79,
-	0x73,0x20,0x43,0x2D,0x31,0x20,0x74,0x69,0x63,0x6B,0x20,0x23,
-	0x31,0x2E,0x26,0x3E,0x32,0x30,0x20,0x50,0x6C,0x61,0x79,0x73,
-	0x20,0x43,0x2D,0x31,0x20,0x2B,0x20,0x33,0x20,0x4E,0x6F,0x74,
-	0x65,0x73,0x20,0x3D,0x20,0x44,0x23,0x31,0x20,0x74,0x69,0x63,
-	0x6B,0x20,0x23,0x32,0x2E,0x26,0x3E,0x33,0x30,0x20,0x50,0x6C,
-	0x61,0x79,0x73,0x20,0x43,0x2D,0x31,0x20,0x2B,0x20,0x37,0x20,
-	0x4E,0x6F,0x74,0x65,0x73,0x20,0x3D,0x20,0x47,0x2D,0x31,0x20,
-	0x74,0x69,0x63,0x6B,0x20,0x23,0x33,0x2E,0x0B,0x3E,0x34,0x30,
-	0x20,0x47,0x6F,0x74,0x6F,0x20,0x31,0x30,0x00,0x1C,0x40,0x58,
-	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x72,0x74,
-	0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,
-	0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x28,
-	0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53,0x70,
-	0x65,0x65,0x64,0x00,0x40,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,
-	0x6E,0x74,0x6F,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20,
-	0x74,0x6F,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x74,0x68,0x65,
-	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x69,0x74,0x63,
-	0x68,0x20,0x75,0x70,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,
-	0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x42,0x64,0x6F,
-	0x6E,0x65,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,
-	0x20,0x70,0x65,0x72,0x69,0x6F,0x64,0x20,0x76,0x61,0x6C,0x75,
-	0x65,0x2E,0x20,0x49,0x66,0x20,0x41,0x6D,0x69,0x67,0x61,0x20,
-	0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,
-	0x62,0x6C,0x65,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x2C,
-	0x20,0x74,0x68,0x65,0x40,0x73,0x6C,0x69,0x64,0x69,0x6E,0x67,
-	0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x6E,0x6F,0x6E,
-	0x2D,0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x28,0x74,0x68,0x65,
-	0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x64,0x65,0x70,0x65,0x6E,
-	0x64,0x73,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x66,0x72,
-	0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x29,0x2E,0x00,0x19,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E,
-	0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,
-	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x33,0x20,0x2B,
-	0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69,0x73,
-	0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x69,0x73,0x20,
-	0x75,0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74,0x68,0x65,
-	0x72,0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,
-	0x65,0x2C,0x20,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,
-	0x73,0x6C,0x69,0x64,0x65,0x20,0x74,0x6F,0x20,0x69,0x74,0x73,
-	0x43,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x2E,0x20,
-	0x49,0x66,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F,
-	0x20,0x28,0x45,0x33,0x29,0x20,0x69,0x73,0x20,0x75,0x73,0x65,
-	0x64,0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,0x71,0x75,
-	0x65,0x6E,0x63,0x79,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,
-	0x20,0x72,0x6F,0x75,0x6E,0x64,0x65,0x64,0x18,0x74,0x6F,0x20,
-	0x74,0x68,0x65,0x20,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20,
-	0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x2E,0x00,0x11,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x62,
-	0x72,0x61,0x74,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
-	0x20,0x34,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20,0x2B,0x20,
-	0x44,0x65,0x70,0x74,0x68,0x00,0x3E,0x41,0x64,0x64,0x73,0x20,
-	0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74,
-	0x68,0x65,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x77,
-	0x69,0x74,0x68,0x20,0x61,0x20,0x72,0x61,0x74,0x65,0x20,0x61,
-	0x6E,0x64,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,0x20,0x53,0x65,
-	0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x3D,0x63,0x6F,
-	0x6E,0x74,0x72,0x6F,0x6C,0x20,0x28,0x45,0x34,0x29,0x20,0x63,
-	0x61,0x6E,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,
-	0x6F,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x74,0x68,0x65,
-	0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77,0x61,0x76,
-	0x65,0x20,0x66,0x6F,0x72,0x6D,0x20,0x28,0x73,0x65,0x65,0x07,
-	0x62,0x65,0x6C,0x6F,0x77,0x29,0x2E,0x00,0x28,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E,0x65,0x20,
-	0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x2B,
-	0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,
 	0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
-	0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x35,0x20,
-	0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69,
+	0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x36,0x20,
+	0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3C,0x54,0x68,0x69,
 	0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,
 	0x6C,0x6C,0x20,0x65,0x78,0x65,0x63,0x75,0x74,0x65,0x20,0x62,
-	0x6F,0x74,0x68,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,
-	0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x61,0x6E,0x64,0x20,
-	0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,
-	0x2E,0x27,0x54,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20,
-	0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,
-	0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,
-	0x6C,0x69,0x64,0x65,0x2E,0x00,0x20,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,
-	0x20,0x2B,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,
-	0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
-	0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,
-	0x36,0x20,0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3C,0x54,
-	0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,
-	0x77,0x69,0x6C,0x6C,0x20,0x65,0x78,0x65,0x63,0x75,0x74,0x65,
-	0x20,0x62,0x6F,0x74,0x68,0x20,0x76,0x69,0x62,0x72,0x61,0x74,
-	0x6F,0x20,0x61,0x6E,0x64,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
-	0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x20,0x54,0x68,0x65,0x23,
-	0x73,0x70,0x65,0x65,0x64,0x20,0x69,0x73,0x20,0x75,0x73,0x65,
-	0x64,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x76,0x6F,
-	0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x00,
-	0x11,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,
-	0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,
-	0x30,0x40,0x43,0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,
-	0x78,0x3A,0x20,0x37,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20,
-	0x2B,0x20,0x44,0x65,0x70,0x74,0x68,0x00,0x41,0x54,0x72,0x65,
-	0x6D,0x6F,0x6C,0x6F,0x20,0x61,0x64,0x64,0x73,0x20,0x76,0x69,
-	0x62,0x72,0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,
-	0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C,
-	0x75,0x6D,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x79,0x6E,
-	0x74,0x61,0x78,0x20,0x69,0x73,0x20,0x65,0x78,0x61,0x63,0x74,
-	0x6C,0x79,0x1B,0x61,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,
-	0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x63,0x6F,
-	0x6D,0x6D,0x61,0x6E,0x64,0x2E,0x00,0x1E,0x40,0x58,0x30,0x34,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x70,0x61,
-	0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,
-	0x6F,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x14,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x38,
-	0x20,0x2B,0x20,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,
-	0x3E,0x53,0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x61,
-	0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,
-	0x6F,0x6E,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x63,
-	0x68,0x61,0x6E,0x6E,0x65,0x6C,0x2E,0x20,0x24,0x30,0x30,0x20,
-	0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x6C,0x65,0x66,0x74,0x6D,
-	0x6F,0x73,0x74,0x3F,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,
-	0x20,0x61,0x6E,0x64,0x20,0x24,0x46,0x46,0x20,0x74,0x68,0x65,
-	0x20,0x72,0x69,0x67,0x68,0x74,0x6D,0x6F,0x73,0x74,0x2E,0x20,
-	0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x73,0x6F,
-	0x6D,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x63,0x61,0x72,
-	0x64,0x73,0x20,0x28,0x65,0x78,0x2E,0x30,0x47,0x55,0x53,0x29,
-	0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x75,0x73,0x65,0x20,0x61,
-	0x73,0x20,0x6D,0x61,0x6E,0x79,0x20,0x61,0x73,0x20,0x32,0x35,
-	0x36,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,
-	0x73,0x69,0x74,0x69,0x6F,0x6E,0x73,0x2E,0x00,0x17,0x40,0x58,
-	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,
-	0x6C,0x65,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,
-	0x6E,0x74,0x61,0x78,0x3A,0x20,0x39,0x20,0x2B,0x20,0x4F,0x66,
-	0x66,0x73,0x65,0x74,0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63,
-	0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,
-	0x64,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F,
-	0x67,0x65,0x74,0x68,0x65,0x72,0x20,0x77,0x69,0x74,0x68,0x20,
-	0x61,0x20,0x6E,0x6F,0x74,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,
-	0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x6C,0x6C,0x2D,
-	0x62,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x66,0x72,
-	0x6F,0x6D,0x20,0x28,0x4F,0x66,0x66,0x73,0x65,0x74,0x2A,0x24,
-	0x31,0x30,0x30,0x29,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,
-	0x20,0x6F,0x66,0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x16,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C,
-	0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x21,0x53,0x79,
-	0x6E,0x74,0x61,0x78,0x3A,0x20,0x41,0x20,0x2B,0x20,0x55,0x70,
-	0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x2B,0x20,0x44,0x6F,0x77,
-	0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x00,0x3D,0x53,0x6C,0x69,
-	0x64,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,
-	0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x75,
-	0x70,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x45,
-	0x69,0x74,0x68,0x65,0x72,0x20,0x75,0x70,0x20,0x73,0x70,0x65,
-	0x65,0x64,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x15,0x73,
-	0x70,0x65,0x65,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,
-	0x62,0x65,0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x17,0x40,0x58,
-	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x73,0x69,
-	0x74,0x69,0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x14,0x53,0x79,
-	0x6E,0x74,0x61,0x78,0x3A,0x20,0x42,0x20,0x2B,0x20,0x50,0x6F,
-	0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x41,0x54,0x68,0x69,0x73,
-	0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,
-	0x6C,0x20,0x6A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x68,
-	0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x73,
-	0x6F,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,
-	0x20,0x61,0x6E,0x64,0x20,0x70,0x6C,0x61,0x79,0x20,0x74,0x68,
-	0x65,0x1B,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x66,0x72,
-	0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x67,0x69,0x6E,
-	0x6E,0x69,0x6E,0x67,0x2E,0x00,0x14,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,
-	0x75,0x6D,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
-	0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,
-	0x43,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x00,0x3E,
-	0x53,0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,
-	0x72,0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2E,
-	0x20,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,
-	0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F,0x74,0x20,0x62,
-	0x65,0x20,0x67,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x74,0x68,
-	0x61,0x6E,0x04,0x24,0x34,0x30,0x2E,0x00,0x17,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,
-	0x72,0x6E,0x20,0x62,0x72,0x65,0x61,0x6B,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1C,0x53,0x79,0x6E,
-	0x74,0x61,0x78,0x3A,0x20,0x44,0x20,0x2B,0x20,0x50,0x61,0x74,
-	0x74,0x65,0x72,0x6E,0x2D,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,
-	0x6E,0x00,0x3C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,
-	0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6A,0x75,0x6D,
-	0x70,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x78,
-	0x74,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x61,0x6E,
-	0x64,0x20,0x70,0x6C,0x61,0x79,0x20,0x66,0x72,0x6F,0x6D,0x20,
-	0x74,0x68,0x65,0x13,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,
-	0x64,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x00,
-	0x22,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,
-	0x65,0x74,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x20,0x28,0x41,
-	0x6D,0x69,0x67,0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x0B,
-	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x13,
-	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x30,0x20,0x2B,
-	0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x00,0x38,0x55,0x73,0x65,
-	0x20,0x45,0x30,0x30,0x20,0x61,0x6E,0x64,0x20,0x79,0x6F,0x75,
-	0x72,0x20,0x74,0x75,0x6E,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,
-	0x73,0x6F,0x75,0x6E,0x64,0x20,0x72,0x65,0x61,0x6C,0x6C,0x79,
-	0x20,0x62,0x61,0x64,0x20,0x6F,0x6E,0x20,0x61,0x6E,0x20,0x41,
-	0x6D,0x69,0x67,0x61,0x21,0x00,0x21,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x6E,0x65,0x20,0x70,0x6F,
-	0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,
-	0x64,0x6F,0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x19,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
-	0x20,0x45,0x28,0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B,
-	0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3F,0x54,0x68,0x69,0x73,
-	0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72,
-	0x6B,0x73,0x20,0x61,0x73,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,
-	0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,
-	0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x6F,0x6E,0x6C,
-	0x79,0x20,0x73,0x6C,0x69,0x64,0x65,0x73,0x20,0x75,0x70,0x05,
-	0x6F,0x6E,0x63,0x65,0x2E,0x00,0x1F,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x67,0x6C,0x69,
-	0x73,0x73,0x61,0x6E,0x64,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,
-	0x6F,0x6C,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x13,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,
-	0x33,0x20,0x2B,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x00,0x41,
-	0x49,0x66,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x20,0x69,0x73,
-	0x20,0x3D,0x31,0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,
-	0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,
-	0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70,
-	0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x77,0x69,
-	0x6C,0x6C,0x20,0x62,0x65,0x20,0x72,0x6F,0x75,0x6E,0x64,0x65,
-	0x64,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x61,
-	0x72,0x65,0x73,0x74,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,
-	0x65,0x2E,0x00,0x1D,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
-	0x30,0x31,0x53,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,
-	0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79,
-	0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x34,0x20,0x2B,0x20,0x54,
-	0x79,0x70,0x65,0x00,0x2C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,
-	0x6D,0x6D,0x61,0x6E,0x64,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,
-	0x6C,0x73,0x20,0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72,0x61,
-	0x74,0x6F,0x20,0x77,0x61,0x76,0x65,0x20,0x66,0x6F,0x72,0x6D,
-	0x2E,0x00,0x33,0x54,0x79,0x70,0x65,0x3A,0x20,0x30,0x20,0x3D,
-	0x20,0x53,0x69,0x6E,0x65,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
-	0x31,0x20,0x3D,0x20,0x52,0x61,0x6D,0x70,0x20,0x64,0x6F,0x77,
-	0x6E,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x32,0x20,0x3D,0x20,
-	0x53,0x71,0x75,0x61,0x72,0x65,0x00,0x44,0x49,0x66,0x20,0x79,
-	0x6F,0x75,0x20,0x61,0x64,0x64,0x20,0x34,0x20,0x74,0x6F,0x20,
-	0x74,0x68,0x65,0x20,0x74,0x79,0x70,0x65,0x2C,0x20,0x74,0x68,
-	0x65,0x20,0x77,0x61,0x76,0x65,0x20,0x66,0x6F,0x72,0x6D,0x20,
-	0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,
-	0x72,0x65,0x74,0x72,0x69,0x67,0x67,0x65,0x64,0x20,0x77,0x68,
-	0x65,0x6E,0x20,0x61,0x19,0x6E,0x65,0x77,0x20,0x69,0x6E,0x73,
-	0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x73,0x20,0x70,
-	0x6C,0x61,0x79,0x65,0x64,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x66,0x69,
-	0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,
-	0x61,0x78,0x3A,0x20,0x45,0x35,0x20,0x2B,0x20,0x54,0x75,0x6E,
-	0x65,0x00,0x3F,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,
-	0x61,0x6E,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x62,
-	0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74,
-	0x68,0x65,0x72,0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,
-	0x6F,0x74,0x65,0x2E,0x20,0x49,0x74,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x63,0x61,0x75,0x73,0x65,0x44,0x61,0x6E,0x6F,0x74,0x68,
-	0x65,0x72,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,
-	0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x74,0x6F,0x20,0x62,0x65,
-	0x20,0x75,0x73,0x65,0x64,0x2E,0x20,0x49,0x74,0x20,0x73,0x65,
-	0x65,0x6D,0x73,0x20,0x71,0x75,0x69,0x74,0x65,0x20,0x75,0x6E,
-	0x75,0x73,0x61,0x62,0x6C,0x65,0x20,0x74,0x6F,0x20,0x6D,0x65,
-	0x2E,0x2E,0x2E,0x00,0x16,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
-	0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,
-	0x6F,0x6F,0x70,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
-	0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,
-	0x45,0x36,0x20,0x2B,0x20,0x43,0x6F,0x75,0x6E,0x74,0x00,0x45,
-	0x49,0x66,0x20,0x63,0x6F,0x75,0x6E,0x74,0x20,0x69,0x73,0x20,
-	0x7A,0x65,0x72,0x6F,0x2C,0x20,0x74,0x68,0x65,0x20,0x62,0x65,
-	0x67,0x69,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x6F,0x66,0x20,0x74,
-	0x68,0x65,0x20,0x6C,0x6F,0x6F,0x70,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x62,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,
-	0x64,0x2E,0x20,0x57,0x68,0x65,0x6E,0x20,0x61,0x40,0x6E,0x6F,
-	0x6E,0x2D,0x7A,0x65,0x72,0x6F,0x20,0x76,0x61,0x6C,0x75,0x65,
-	0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x2C,0x20,0x74,0x68,
-	0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x77,0x69,
-	0x6C,0x6C,0x20,0x62,0x65,0x20,0x6C,0x6F,0x6F,0x70,0x65,0x64,
-	0x20,0x66,0x72,0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x6C,0x6F,
-	0x6F,0x70,0x06,0x73,0x74,0x61,0x72,0x74,0x2E,0x00,0x1D,0x40,
+	0x6F,0x74,0x68,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,
+	0x61,0x6E,0x64,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,
+	0x6C,0x69,0x64,0x65,0x2E,0x20,0x54,0x68,0x65,0x23,0x73,0x70,
+	0x65,0x65,0x64,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20,
+	0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,
+	0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x00,0x11,0x40,
+	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x72,0x65,
+	0x6D,0x6F,0x6C,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
+	0x43,0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
+	0x20,0x37,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20,0x2B,0x20,
+	0x44,0x65,0x70,0x74,0x68,0x00,0x41,0x54,0x72,0x65,0x6D,0x6F,
+	0x6C,0x6F,0x20,0x61,0x64,0x64,0x73,0x20,0x76,0x69,0x62,0x72,
+	0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x63,
+	0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,
+	0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x79,0x6E,0x74,0x61,
+	0x78,0x20,0x69,0x73,0x20,0x65,0x78,0x61,0x63,0x74,0x6C,0x79,
+	0x1B,0x61,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,
+	0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x63,0x6F,0x6D,0x6D,
+	0x61,0x6E,0x64,0x2E,0x00,0x1E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x70,0x61,0x6E,0x6E,
+	0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,
+	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
+	0x14,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x38,0x20,0x2B,
+	0x20,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x3E,0x53,
+	0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E,
+	0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,
+	0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x63,0x68,0x61,
+	0x6E,0x6E,0x65,0x6C,0x2E,0x20,0x24,0x30,0x30,0x20,0x69,0x73,
+	0x20,0x74,0x68,0x65,0x20,0x6C,0x65,0x66,0x74,0x6D,0x6F,0x73,
+	0x74,0x3F,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x20,0x61,
+	0x6E,0x64,0x20,0x24,0x46,0x46,0x20,0x74,0x68,0x65,0x20,0x72,
+	0x69,0x67,0x68,0x74,0x6D,0x6F,0x73,0x74,0x2E,0x20,0x4E,0x6F,
+	0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x73,0x6F,0x6D,0x65,
+	0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x63,0x61,0x72,0x64,0x73,
+	0x20,0x28,0x65,0x78,0x2E,0x30,0x47,0x55,0x53,0x29,0x20,0x63,
+	0x61,0x6E,0x27,0x74,0x20,0x75,0x73,0x65,0x20,0x61,0x73,0x20,
+	0x6D,0x61,0x6E,0x79,0x20,0x61,0x73,0x20,0x32,0x35,0x36,0x20,
+	0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,
+	0x74,0x69,0x6F,0x6E,0x73,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,
+	0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,
+	0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,
+	0x61,0x78,0x3A,0x20,0x39,0x20,0x2B,0x20,0x4F,0x66,0x66,0x73,
+	0x65,0x74,0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,
+	0x6D,0x61,0x6E,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,
+	0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65,
+	0x74,0x68,0x65,0x72,0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,
+	0x6E,0x6F,0x74,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x61,
+	0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x6C,0x6C,0x2D,0x62,0x65,
+	0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x66,0x72,0x6F,0x6D,
+	0x20,0x28,0x4F,0x66,0x66,0x73,0x65,0x74,0x2A,0x24,0x31,0x30,
+	0x30,0x29,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x20,0x6F,
+	0x66,0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x16,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,
+	0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x21,0x53,0x79,0x6E,0x74,
+	0x61,0x78,0x3A,0x20,0x41,0x20,0x2B,0x20,0x55,0x70,0x20,0x73,
+	0x70,0x65,0x65,0x64,0x20,0x2B,0x20,0x44,0x6F,0x77,0x6E,0x20,
+	0x73,0x70,0x65,0x65,0x64,0x00,0x3D,0x53,0x6C,0x69,0x64,0x65,
+	0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,
+	0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x75,0x70,0x20,
+	0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x45,0x69,0x74,
+	0x68,0x65,0x72,0x20,0x75,0x70,0x20,0x73,0x70,0x65,0x65,0x64,
+	0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x15,0x73,0x70,0x65,
+	0x65,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65,
+	0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,
+	0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x73,0x69,0x74,0x69,
+	0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x14,0x53,0x79,0x6E,0x74,
+	0x61,0x78,0x3A,0x20,0x42,0x20,0x2B,0x20,0x50,0x6F,0x73,0x69,
+	0x74,0x69,0x6F,0x6E,0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63,
+	0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,
+	0x6A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,
+	0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x73,0x6F,0x6E,
+	0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x20,0x61,
+	0x6E,0x64,0x20,0x70,0x6C,0x61,0x79,0x20,0x74,0x68,0x65,0x1B,
+	0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x66,0x72,0x6F,0x6D,
+	0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x67,0x69,0x6E,0x6E,0x69,
+	0x6E,0x67,0x2E,0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,
+	0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
+	0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x43,0x20,
+	0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x00,0x3E,0x53,0x65,
+	0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,
+	0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2E,0x20,0x54,
+	0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x68,
+	0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,
+	0x67,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x74,0x68,0x61,0x6E,
+	0x04,0x24,0x34,0x30,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,
+	0x20,0x62,0x72,0x65,0x61,0x6B,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x1C,0x53,0x79,0x6E,0x74,0x61,
+	0x78,0x3A,0x20,0x44,0x20,0x2B,0x20,0x50,0x61,0x74,0x74,0x65,
+	0x72,0x6E,0x2D,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,
+	0x3C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,
+	0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6A,0x75,0x6D,0x70,0x20,
+	0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x78,0x74,0x20,
+	0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x61,0x6E,0x64,0x20,
+	0x70,0x6C,0x61,0x79,0x20,0x66,0x72,0x6F,0x6D,0x20,0x74,0x68,
+	0x65,0x13,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,
+	0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x00,0x22,0x40,
 	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,
-	0x20,0x74,0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E,
-	0x74,0x72,0x6F,0x6C,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
-	0x20,0x45,0x37,0x20,0x2B,0x20,0x54,0x79,0x70,0x65,0x00,0x3A,
-	0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,
-	0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x65,0x78,0x61,0x63,0x74,
-	0x6C,0x79,0x20,0x61,0x73,0x20,0x73,0x65,0x74,0x20,0x76,0x69,
-	0x62,0x72,0x61,0x74,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,
-	0x6C,0x2C,0x20,0x62,0x75,0x74,0x20,0x74,0x68,0x65,0x2A,0x74,
-	0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x20,0x77,0x61,0x76,0x65,0x20,
-	0x66,0x6F,0x72,0x6D,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,
-	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x69,0x6E,0x73,
-	0x74,0x65,0x61,0x64,0x2E,0x00,0x15,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x52,0x65,0x74,0x72,0x69,0x67,0x20,
-	0x6E,0x6F,0x74,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x15,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
-	0x20,0x45,0x39,0x20,0x2B,0x20,0x49,0x6E,0x74,0x65,0x72,0x76,
-	0x61,0x6C,0x00,0x2D,0x52,0x65,0x74,0x72,0x69,0x67,0x73,0x20,
-	0x74,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x77,0x69,0x74,
-	0x68,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66,
-	0x69,0x65,0x64,0x20,0x69,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,
-	0x2E,0x00,0x23,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
-	0x31,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
-	0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2F,0x64,0x6F,
+	0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x20,0x28,0x41,0x6D,0x69,
+	0x67,0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x0B,0x3E,0x40,
+	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x13,0x53,0x79,
+	0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x30,0x20,0x2B,0x20,0x53,
+	0x74,0x61,0x74,0x75,0x73,0x00,0x38,0x55,0x73,0x65,0x20,0x45,
+	0x30,0x30,0x20,0x61,0x6E,0x64,0x20,0x79,0x6F,0x75,0x72,0x20,
+	0x74,0x75,0x6E,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x6F,
+	0x75,0x6E,0x64,0x20,0x72,0x65,0x61,0x6C,0x6C,0x79,0x20,0x62,
+	0x61,0x64,0x20,0x6F,0x6E,0x20,0x61,0x6E,0x20,0x41,0x6D,0x69,
+	0x67,0x61,0x21,0x00,0x21,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x46,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,
+	0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,
 	0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
 	0x30,0x32,0x19,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,
-	0x28,0x41,0x20,0x6F,0x72,0x20,0x42,0x29,0x20,0x2B,0x20,0x53,
-	0x70,0x65,0x65,0x64,0x00,0x44,0x54,0x68,0x69,0x73,0x20,0x63,
+	0x28,0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53,
+	0x70,0x65,0x65,0x64,0x00,0x3F,0x54,0x68,0x69,0x73,0x20,0x63,
 	0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,
-	0x20,0x61,0x73,0x20,0x74,0x68,0x65,0x20,0x75,0x73,0x75,0x61,
-	0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,
-	0x64,0x65,0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x77,
-	0x69,0x6C,0x6C,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x73,0x6C,0x69,
-	0x64,0x65,0x05,0x6F,0x6E,0x63,0x65,0x2E,0x00,0x12,0x40,0x58,
-	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,
-	0x20,0x63,0x75,0x74,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
+	0x20,0x61,0x73,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,
+	0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x2C,0x20,
+	0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x6F,0x6E,0x6C,0x79,0x20,
+	0x73,0x6C,0x69,0x64,0x65,0x73,0x20,0x75,0x70,0x05,0x6F,0x6E,
+	0x63,0x65,0x2E,0x00,0x1F,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x67,0x6C,0x69,0x73,0x73,
+	0x61,0x6E,0x64,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,
+	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
+	0x13,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x33,0x20,
+	0x2B,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x00,0x41,0x49,0x66,
+	0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x20,0x69,0x73,0x20,0x3D,
+	0x31,0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,0x71,0x75,
+	0x65,0x6E,0x63,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,0x75,0x73,
+	0x69,0x6E,0x67,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,
+	0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x77,0x69,0x6C,0x6C,
+	0x20,0x62,0x65,0x20,0x72,0x6F,0x75,0x6E,0x64,0x65,0x64,0x20,
+	0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x61,0x72,0x65,
+	0x73,0x74,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x2E,
+	0x00,0x1D,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x53,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,
+	0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,
+	0x61,0x78,0x3A,0x20,0x45,0x34,0x20,0x2B,0x20,0x54,0x79,0x70,
+	0x65,0x00,0x2B,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,
+	0x61,0x6E,0x64,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x73,
+	0x20,0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,
+	0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x2E,0x00,0x33,
+	0x54,0x79,0x70,0x65,0x3A,0x20,0x30,0x20,0x3D,0x20,0x53,0x69,
+	0x6E,0x65,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x20,0x3D,
+	0x20,0x52,0x61,0x6D,0x70,0x20,0x64,0x6F,0x77,0x6E,0x20,0x20,
+	0x20,0x20,0x20,0x20,0x20,0x32,0x20,0x3D,0x20,0x53,0x71,0x75,
+	0x61,0x72,0x65,0x00,0x43,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,
+	0x61,0x64,0x64,0x20,0x34,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,
+	0x20,0x74,0x79,0x70,0x65,0x2C,0x20,0x74,0x68,0x65,0x20,0x77,
+	0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x20,0x77,0x69,0x6C,0x6C,
+	0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,0x72,0x65,0x74,0x72,
+	0x69,0x67,0x67,0x65,0x64,0x20,0x77,0x68,0x65,0x6E,0x20,0x61,
+	0x19,0x6E,0x65,0x77,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
+	0x65,0x6E,0x74,0x20,0x69,0x73,0x20,0x70,0x6C,0x61,0x79,0x65,
+	0x64,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
+	0x30,0x31,0x53,0x65,0x74,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74,
+	0x75,0x6E,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,
+	0x45,0x35,0x20,0x2B,0x20,0x54,0x75,0x6E,0x65,0x00,0x3F,0x54,
+	0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,
+	0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65,0x20,0x75,0x73,
+	0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74,0x68,0x65,0x72,0x20,
+	0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,0x2E,
+	0x20,0x49,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x63,0x61,0x75,
+	0x73,0x65,0x44,0x61,0x6E,0x6F,0x74,0x68,0x65,0x72,0x20,0x66,
+	0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x20,0x76,0x61,0x6C,
+	0x75,0x65,0x20,0x74,0x6F,0x20,0x62,0x65,0x20,0x75,0x73,0x65,
+	0x64,0x2E,0x20,0x49,0x74,0x20,0x73,0x65,0x65,0x6D,0x73,0x20,
+	0x71,0x75,0x69,0x74,0x65,0x20,0x75,0x6E,0x75,0x73,0x61,0x62,
+	0x6C,0x65,0x20,0x74,0x6F,0x20,0x6D,0x65,0x2E,0x2E,0x2E,0x00,
+	0x16,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,
+	0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x6F,0x6F,0x70,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,
+	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x36,0x20,0x2B,
+	0x20,0x43,0x6F,0x75,0x6E,0x74,0x00,0x45,0x49,0x66,0x20,0x63,
+	0x6F,0x75,0x6E,0x74,0x20,0x69,0x73,0x20,0x7A,0x65,0x72,0x6F,
+	0x2C,0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x67,0x69,0x6E,0x6E,
+	0x69,0x6E,0x67,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x6C,
+	0x6F,0x6F,0x70,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,
+	0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x2E,0x20,0x57,
+	0x68,0x65,0x6E,0x20,0x61,0x40,0x6E,0x6F,0x6E,0x2D,0x7A,0x65,
+	0x72,0x6F,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x69,0x73,0x20,
+	0x75,0x73,0x65,0x64,0x2C,0x20,0x74,0x68,0x65,0x20,0x70,0x61,
+	0x74,0x74,0x65,0x72,0x6E,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,
+	0x65,0x20,0x6C,0x6F,0x6F,0x70,0x65,0x64,0x20,0x66,0x72,0x6F,
+	0x6D,0x20,0x74,0x68,0x65,0x20,0x6C,0x6F,0x6F,0x70,0x06,0x73,
+	0x74,0x61,0x72,0x74,0x2E,0x00,0x1D,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x74,0x72,0x65,
+	0x6D,0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,
+	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
+	0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x37,0x20,
+	0x2B,0x20,0x54,0x79,0x70,0x65,0x00,0x3A,0x54,0x68,0x69,0x73,
+	0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72,
+	0x6B,0x73,0x20,0x65,0x78,0x61,0x63,0x74,0x6C,0x79,0x20,0x61,
+	0x73,0x20,0x73,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,
+	0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x2C,0x20,0x62,
+	0x75,0x74,0x20,0x74,0x68,0x65,0x29,0x74,0x72,0x65,0x6D,0x6F,
+	0x6C,0x6F,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x20,
+	0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E,
+	0x67,0x65,0x64,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x2E,
+	0x00,0x15,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x52,0x65,0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x15,
+	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x39,0x20,0x2B,
+	0x20,0x49,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,0x00,0x2D,0x52,
+	0x65,0x74,0x72,0x69,0x67,0x73,0x20,0x74,0x68,0x65,0x20,0x6E,
+	0x6F,0x74,0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,0x65,
+	0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x69,
+	0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,0x2E,0x00,0x23,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x6E,0x65,
+	0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,
+	0x65,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x0B,0x3E,0x40,
+	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x19,0x53,0x79,
+	0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x28,0x41,0x20,0x6F,0x72,
+	0x20,0x42,0x29,0x20,0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,
+	0x44,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,
+	0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x61,0x73,0x20,0x74,
+	0x68,0x65,0x20,0x75,0x73,0x75,0x61,0x6C,0x20,0x76,0x6F,0x6C,
+	0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2C,0x20,0x62,
+	0x75,0x74,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6F,
+	0x6E,0x6C,0x79,0x20,0x73,0x6C,0x69,0x64,0x65,0x05,0x6F,0x6E,
+	0x63,0x65,0x2E,0x00,0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,0x20,0x63,0x75,0x74,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,
+	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x43,0x20,0x2B,
+	0x20,0x54,0x69,0x63,0x6B,0x00,0x43,0x43,0x75,0x74,0x73,0x20,
+	0x74,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x61,0x74,0x20,
+	0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,
+	0x64,0x20,0x74,0x69,0x63,0x6B,0x2E,0x20,0x4E,0x6F,0x74,0x65,
+	0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,
+	0x6C,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x73,0x65,0x74,0x20,0x74,
+	0x68,0x65,0x34,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x74,0x6F,
+	0x20,0x7A,0x65,0x72,0x6F,0x2C,0x20,0x61,0x6E,0x64,0x20,0x74,
+	0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,
+	0x6C,0x6C,0x20,0x73,0x74,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,
+	0x70,0x6C,0x61,0x79,0x65,0x64,0x2E,0x00,0x14,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,0x20,
+	0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
+	0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,
+	0x3A,0x20,0x45,0x44,0x20,0x2B,0x20,0x54,0x69,0x63,0x6B,0x73,
+	0x00,0x3E,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,
+	0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x64,0x65,0x6C,0x61,
+	0x79,0x20,0x74,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x74,
+	0x68,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,
+	0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x74,0x69,
+	0x63,0x6B,0x73,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,
+	0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
+	0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,
+	0x3A,0x20,0x45,0x45,0x20,0x2B,0x20,0x4E,0x6F,0x74,0x65,0x73,
+	0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,
+	0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x64,0x65,0x6C,0x61,
+	0x79,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
+	0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,
+	0x65,0x64,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,
+	0x20,0x6E,0x6F,0x74,0x65,0x73,0x2E,0x00,0x13,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x73,
+	0x70,0x65,0x65,0x64,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
 	0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
-	0x20,0x45,0x43,0x20,0x2B,0x20,0x54,0x69,0x63,0x6B,0x00,0x43,
-	0x43,0x75,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x6E,0x6F,0x74,
-	0x65,0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,
-	0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x74,0x69,0x63,0x6B,0x2E,
-	0x20,0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x69,
-	0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6F,0x6E,0x6C,0x79,0x20,
-	0x73,0x65,0x74,0x20,0x74,0x68,0x65,0x34,0x76,0x6F,0x6C,0x75,
-	0x6D,0x65,0x20,0x74,0x6F,0x20,0x7A,0x65,0x72,0x6F,0x2C,0x20,
-	0x61,0x6E,0x64,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,
-	0x6C,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x74,0x69,0x6C,
-	0x6C,0x20,0x62,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x2E,
-	0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x4E,0x6F,0x74,0x65,0x20,0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E,
-	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,
-	0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x44,0x20,0x2B,0x20,
-	0x54,0x69,0x63,0x6B,0x73,0x00,0x3E,0x54,0x68,0x69,0x73,0x20,
-	0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x64,0x65,0x6C,0x61,0x79,0x20,0x74,0x68,0x65,0x20,0x6E,
-	0x6F,0x74,0x65,0x20,0x74,0x68,0x65,0x20,0x73,0x65,0x6C,0x65,
-	0x63,0x74,0x65,0x64,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,
-	0x6F,0x66,0x20,0x74,0x69,0x63,0x6B,0x73,0x2E,0x00,0x17,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,
-	0x74,0x65,0x72,0x6E,0x20,0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E,
-	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,
-	0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x45,0x20,0x2B,0x20,
-	0x4E,0x6F,0x74,0x65,0x73,0x00,0x41,0x54,0x68,0x69,0x73,0x20,
-	0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x64,0x65,0x6C,0x61,0x79,0x20,0x74,0x68,0x65,0x20,0x70,
-	0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,
-	0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x6E,0x75,0x6D,0x62,
-	0x65,0x72,0x20,0x6F,0x66,0x20,0x6E,0x6F,0x74,0x65,0x73,0x2E,
-	0x00,0x13,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x53,0x65,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79,
-	0x6E,0x74,0x61,0x78,0x3A,0x20,0x46,0x20,0x2B,0x20,0x56,0x61,
-	0x6C,0x75,0x65,0x00,0x42,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,
-	0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,
-	0x65,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,
-	0x20,0x6F,0x72,0x20,0x42,0x50,0x4D,0x20,0x76,0x61,0x6C,0x75,
-	0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x6E,
-	0x67,0x2E,0x20,0x49,0x66,0x20,0x76,0x61,0x6C,0x75,0x65,0x3F,
-	0x69,0x73,0x20,0x6C,0x65,0x73,0x73,0x20,0x74,0x68,0x61,0x6E,
-	0x20,0x24,0x32,0x30,0x2C,0x20,0x74,0x68,0x65,0x20,0x73,0x70,
-	0x65,0x65,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,
-	0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x2E,0x20,0x4F,0x74,0x68,
-	0x65,0x72,0x77,0x69,0x73,0x65,0x2C,0x20,0x74,0x68,0x65,0x20,
-	0x42,0x50,0x4D,0x16,0x76,0x61,0x6C,0x75,0x65,0x20,0x77,0x69,
-	0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,
-	0x64,0x2E,0x00,0x1B,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
-	0x30,0x31,0x53,0x65,0x74,0x20,0x67,0x6C,0x6F,0x62,0x61,0x6C,
-	0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,
-	0x61,0x78,0x3A,0x20,0x47,0x20,0x2B,0x20,0x76,0x6F,0x6C,0x75,
-	0x6D,0x65,0x00,0x42,0x53,0x65,0x74,0x73,0x20,0x74,0x68,0x65,
+	0x20,0x46,0x20,0x2B,0x20,0x56,0x61,0x6C,0x75,0x65,0x00,0x42,
+	0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,
+	0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x65,0x74,0x20,0x74,0x68,
+	0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x6F,0x72,0x20,0x42,
+	0x50,0x4D,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x6F,0x66,0x20,
+	0x74,0x68,0x65,0x20,0x73,0x6F,0x6E,0x67,0x2E,0x20,0x49,0x66,
+	0x20,0x76,0x61,0x6C,0x75,0x65,0x3F,0x69,0x73,0x20,0x6C,0x65,
+	0x73,0x73,0x20,0x74,0x68,0x61,0x6E,0x20,0x24,0x32,0x30,0x2C,
+	0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x77,
+	0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,
+	0x65,0x64,0x2E,0x20,0x4F,0x74,0x68,0x65,0x72,0x77,0x69,0x73,
+	0x65,0x2C,0x20,0x74,0x68,0x65,0x20,0x42,0x50,0x4D,0x16,0x76,
+	0x61,0x6C,0x75,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,
+	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x2E,0x00,0x1B,0x40,
+	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,
 	0x20,0x67,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,
-	0x6D,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,
-	0x6D,0x65,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F,
-	0x74,0x20,0x62,0x65,0x20,0x67,0x72,0x65,0x61,0x74,0x65,0x72,
-	0x20,0x74,0x68,0x61,0x6E,0x20,0x24,0x34,0x30,0x2E,0x00,0x1D,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x47,0x6C,
-	0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,
-	0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
-	0x40,0x43,0x30,0x30,0x32,0x21,0x53,0x79,0x6E,0x74,0x61,0x78,
-	0x3A,0x20,0x48,0x20,0x2B,0x20,0x55,0x70,0x20,0x73,0x70,0x65,
-	0x65,0x64,0x20,0x2B,0x20,0x44,0x6F,0x77,0x6E,0x20,0x73,0x70,
-	0x65,0x65,0x64,0x00,0x3D,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,
-	0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,
-	0x65,0x78,0x61,0x63,0x74,0x6C,0x79,0x20,0x61,0x73,0x20,0x76,
-	0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2C,
-	0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x73,0x6C,0x69,0x64,
-	0x65,0x73,0x20,0x74,0x68,0x65,0x16,0x67,0x6C,0x6F,0x62,0x61,
-	0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x73,
-	0x74,0x65,0x61,0x64,0x2E,0x00,0x11,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x4B,0x65,0x79,0x20,0x6F,0x66,0x66,
+	0x6D,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
+	0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x47,
+	0x20,0x2B,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x00,0x42,0x53,
+	0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x67,0x6C,0x6F,0x62,
+	0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2E,0x20,0x54,
+	0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x68,
+	0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,
+	0x67,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x74,0x68,0x61,0x6E,
+	0x20,0x24,0x34,0x30,0x2E,0x00,0x1D,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,
+	0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,
 	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x10,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x4B,0x20,0x2B,
-	0x20,0x54,0x69,0x63,0x6B,0x00,0x3C,0x54,0x68,0x69,0x73,0x20,
-	0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x74,0x72,0x69,0x67,0x67,0x65,0x72,0x20,0x61,0x20,0x22,
-	0x4B,0x65,0x79,0x20,0x6F,0x66,0x66,0x22,0x20,0x61,0x74,0x20,
-	0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,
-	0x64,0x20,0x74,0x69,0x63,0x6B,0x2E,0x00,0x1F,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x65,
-	0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70,0x6F,0x73,0x69,
-	0x74,0x69,0x6F,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x14,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
-	0x20,0x4C,0x20,0x2B,0x20,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,
-	0x6E,0x00,0x3E,0x43,0x68,0x61,0x6E,0x67,0x65,0x73,0x20,0x74,
-	0x68,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,
-	0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x20,0x4D,0x61,
-	0x67,0x6E,0x75,0x73,0x20,0x74,0x6F,0x6C,0x64,0x20,0x6D,0x65,
-	0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x74,0x20,0x77,0x6F,0x75,
-	0x6C,0x64,0x20,0x62,0x65,0x0C,0x76,0x65,0x72,0x79,0x20,0x75,
-	0x73,0x61,0x62,0x6C,0x65,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x6E,0x6E,0x69,0x6E,
-	0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x24,0x53,0x79,0x6E,0x74,
-	0x61,0x78,0x3A,0x20,0x50,0x20,0x2B,0x20,0x52,0x69,0x67,0x68,
-	0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x2B,0x20,0x4C,0x65,
-	0x66,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x00,0x42,0x54,0x68,
-	0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x73,
-	0x6C,0x69,0x64,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x61,
-	0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,
-	0x6F,0x6E,0x2E,0x20,0x49,0x74,0x20,0x77,0x6F,0x72,0x6B,0x73,
-	0x20,0x6C,0x69,0x6B,0x65,0x20,0x74,0x68,0x65,0x20,0x76,0x6F,
-	0x6C,0x75,0x6D,0x65,0x3C,0x73,0x6C,0x69,0x64,0x65,0x2E,0x20,
-	0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x73,0x6F,
-	0x6D,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x63,0x61,0x72,
-	0x64,0x73,0x20,0x6D,0x61,0x79,0x20,0x6E,0x6F,0x74,0x20,0x68,
-	0x61,0x6E,0x64,0x6C,0x65,0x20,0x32,0x35,0x36,0x20,0x70,0x61,
-	0x6E,0x6E,0x69,0x6E,0x67,0x0A,0x70,0x6F,0x73,0x69,0x74,0x69,
-	0x6F,0x6E,0x73,0x2E,0x00,0x16,0x40,0x58,0x30,0x34,0x30,0x40,
-	0x43,0x30,0x30,0x31,0x4D,0x75,0x6C,0x74,0x69,0x20,0x72,0x65,
-	0x74,0x72,0x69,0x67,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x24,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,
-	0x20,0x52,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,
-	0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x2B,0x20,0x49,0x6E,0x74,
-	0x65,0x72,0x76,0x61,0x6C,0x00,0x32,0x54,0x68,0x69,0x73,0x20,
-	0x69,0x73,0x20,0x61,0x6E,0x20,0x65,0x78,0x74,0x65,0x6E,0x64,
-	0x65,0x64,0x20,0x76,0x65,0x72,0x73,0x69,0x6F,0x6E,0x20,0x6F,
-	0x66,0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x74,0x72,0x69,0x67,
-	0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2E,0x00,0x0E,0x56,
-	0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,
-	0x3A,0x1F,0x3E,0x40,0x58,0x31,0x30,0x30,0x30,0x20,0x3D,0x20,
-	0x4E,0x6F,0x6E,0x65,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x38,
-	0x20,0x3D,0x20,0x55,0x6E,0x75,0x73,0x65,0x64,0x16,0x3E,0x31,
-	0x20,0x3D,0x20,0x2D,0x31,0x20,0x20,0x20,0x20,0x40,0x54,0x33,
-	0x30,0x30,0x39,0x20,0x3D,0x20,0x2B,0x31,0x16,0x3E,0x32,0x20,
-	0x3D,0x20,0x2D,0x32,0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30,
-	0x30,0x41,0x20,0x3D,0x20,0x2B,0x32,0x16,0x3E,0x33,0x20,0x3D,
-	0x20,0x2D,0x34,0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,
-	0x42,0x20,0x3D,0x20,0x2B,0x34,0x16,0x3E,0x34,0x20,0x3D,0x20,
-	0x2D,0x38,0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x43,
-	0x20,0x3D,0x20,0x2B,0x38,0x17,0x3E,0x35,0x20,0x3D,0x20,0x2D,
-	0x31,0x36,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x44,0x20,
-	0x3D,0x20,0x2B,0x31,0x36,0x18,0x3E,0x36,0x20,0x3D,0x20,0x2A,
-	0x32,0x2F,0x33,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x45,0x20,
-	0x3D,0x20,0x2A,0x33,0x2F,0x32,0x16,0x3E,0x37,0x20,0x3D,0x20,
-	0x2A,0x31,0x2F,0x32,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x46,
-	0x20,0x3D,0x20,0x2A,0x32,0x00,0x10,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x54,0x72,0x65,0x6D,0x6F,0x72,0x0B,
-	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E,
-	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x54,0x20,0x2B,0x20,
-	0x4F,0x6E,0x20,0x74,0x69,0x6D,0x65,0x20,0x2B,0x20,0x4F,0x66,
-	0x66,0x20,0x74,0x69,0x6D,0x65,0x00,0x3E,0x54,0x68,0x69,0x73,
-	0x20,0x77,0x65,0x69,0x72,0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61,
-	0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x65,0x74,0x20,
-	0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x74,
-	0x6F,0x20,0x7A,0x65,0x72,0x6F,0x20,0x64,0x75,0x72,0x69,0x6E,
-	0x67,0x20,0x6F,0x66,0x66,0x20,0x74,0x69,0x6D,0x65,0x36,0x6E,
-	0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x74,0x69,0x63,
-	0x6B,0x73,0x2E,0x20,0x49,0x74,0x20,0x69,0x73,0x20,0x69,0x6E,
-	0x63,0x6C,0x75,0x64,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x53,
-	0x54,0x4D,0x20,0x63,0x6F,0x6D,0x70,0x61,0x74,0x69,0x62,0x69,
-	0x6C,0x69,0x74,0x79,0x2E,0x00,0x27,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x74,0x72,0x61,0x20,0x66,
-	0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,
-	0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x0B,0x3E,
-	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x19,0x53,
-	0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x58,0x28,0x31,0x20,0x6F,
-	0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53,0x70,0x65,0x65,0x64,
+	0x21,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x48,0x20,0x2B,
+	0x20,0x55,0x70,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x2B,0x20,
+	0x44,0x6F,0x77,0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x00,0x3D,
+	0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,
+	0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x65,0x78,0x61,0x63,0x74,
+	0x6C,0x79,0x20,0x61,0x73,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
+	0x20,0x73,0x6C,0x69,0x64,0x65,0x2C,0x20,0x62,0x75,0x74,0x20,
+	0x69,0x74,0x20,0x73,0x6C,0x69,0x64,0x65,0x73,0x20,0x74,0x68,
+	0x65,0x16,0x67,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,
+	0x75,0x6D,0x65,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x2E,
+	0x00,0x11,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x4B,0x65,0x79,0x20,0x6F,0x66,0x66,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x10,0x53,0x79,0x6E,0x74,
+	0x61,0x78,0x3A,0x20,0x4B,0x20,0x2B,0x20,0x54,0x69,0x63,0x6B,
 	0x00,0x3C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,
-	0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x61,0x73,0x20,
-	0x66,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,
-	0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x2C,
-	0x20,0x62,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,
-	0x65,0x64,0x18,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x64,
-	0x69,0x76,0x69,0x64,0x65,0x64,0x20,0x62,0x79,0x20,0x66,0x6F,
-	0x75,0x72,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,
+	0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x74,0x72,0x69,0x67,
+	0x67,0x65,0x72,0x20,0x61,0x20,0x22,0x4B,0x65,0x79,0x20,0x6F,
+	0x66,0x66,0x22,0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x73,
+	0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x74,0x69,0x63,
+	0x6B,0x2E,0x00,0x1F,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
+	0x30,0x31,0x53,0x65,0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,
+	0x70,0x65,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x14,
+	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x4C,0x20,0x2B,0x20,
+	0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x3E,0x43,0x68,
+	0x61,0x6E,0x67,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x65,0x6E,
+	0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70,0x6F,0x73,0x69,0x74,
+	0x69,0x6F,0x6E,0x2E,0x20,0x4D,0x61,0x67,0x6E,0x75,0x73,0x20,
+	0x74,0x6F,0x6C,0x64,0x20,0x6D,0x65,0x20,0x74,0x68,0x61,0x74,
+	0x20,0x69,0x74,0x20,0x77,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65,
+	0x0C,0x76,0x65,0x72,0x79,0x20,0x75,0x73,0x61,0x62,0x6C,0x65,
+	0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
+	0x31,0x50,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,
+	0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
+	0x30,0x32,0x24,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x50,
+	0x20,0x2B,0x20,0x52,0x69,0x67,0x68,0x74,0x20,0x73,0x70,0x65,
+	0x65,0x64,0x20,0x2B,0x20,0x4C,0x65,0x66,0x74,0x20,0x73,0x70,
+	0x65,0x65,0x64,0x00,0x42,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,
+	0x6D,0x6D,0x61,0x6E,0x64,0x20,0x73,0x6C,0x69,0x64,0x65,0x73,
+	0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,
+	0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x20,0x49,
+	0x74,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6C,0x69,0x6B,0x65,
+	0x20,0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x3C,
+	0x73,0x6C,0x69,0x64,0x65,0x2E,0x20,0x4E,0x6F,0x74,0x65,0x20,
+	0x74,0x68,0x61,0x74,0x20,0x73,0x6F,0x6D,0x65,0x20,0x73,0x6F,
+	0x75,0x6E,0x64,0x20,0x63,0x61,0x72,0x64,0x73,0x20,0x6D,0x61,
+	0x79,0x20,0x6E,0x6F,0x74,0x20,0x68,0x61,0x6E,0x64,0x6C,0x65,
+	0x20,0x32,0x35,0x36,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,
+	0x0A,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x73,0x2E,0x00,
+	0x16,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,
+	0x75,0x6C,0x74,0x69,0x20,0x72,0x65,0x74,0x72,0x69,0x67,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x24,
+	0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x52,0x20,0x2B,0x20,
+	0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,
+	0x65,0x20,0x2B,0x20,0x49,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,
+	0x00,0x32,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x61,0x6E,
+	0x20,0x65,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x76,0x65,
+	0x72,0x73,0x69,0x6F,0x6E,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,
+	0x20,0x72,0x65,0x74,0x72,0x69,0x67,0x20,0x63,0x6F,0x6D,0x6D,
+	0x61,0x6E,0x64,0x2E,0x00,0x0E,0x56,0x6F,0x6C,0x75,0x6D,0x65,
+	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x3A,0x1F,0x3E,0x40,0x58,
+	0x31,0x30,0x30,0x30,0x20,0x3D,0x20,0x4E,0x6F,0x6E,0x65,0x20,
+	0x20,0x40,0x54,0x33,0x30,0x30,0x38,0x20,0x3D,0x20,0x55,0x6E,
+	0x75,0x73,0x65,0x64,0x16,0x3E,0x31,0x20,0x3D,0x20,0x2D,0x31,
+	0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x39,0x20,0x3D,
+	0x20,0x2B,0x31,0x16,0x3E,0x32,0x20,0x3D,0x20,0x2D,0x32,0x20,
+	0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x41,0x20,0x3D,0x20,
+	0x2B,0x32,0x16,0x3E,0x33,0x20,0x3D,0x20,0x2D,0x34,0x20,0x20,
+	0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x42,0x20,0x3D,0x20,0x2B,
+	0x34,0x16,0x3E,0x34,0x20,0x3D,0x20,0x2D,0x38,0x20,0x20,0x20,
+	0x20,0x40,0x54,0x33,0x30,0x30,0x43,0x20,0x3D,0x20,0x2B,0x38,
+	0x17,0x3E,0x35,0x20,0x3D,0x20,0x2D,0x31,0x36,0x20,0x20,0x20,
+	0x40,0x54,0x33,0x30,0x30,0x44,0x20,0x3D,0x20,0x2B,0x31,0x36,
+	0x18,0x3E,0x36,0x20,0x3D,0x20,0x2A,0x32,0x2F,0x33,0x20,0x20,
+	0x40,0x54,0x33,0x30,0x30,0x45,0x20,0x3D,0x20,0x2A,0x33,0x2F,
+	0x32,0x16,0x3E,0x37,0x20,0x3D,0x20,0x2A,0x31,0x2F,0x32,0x20,
+	0x20,0x40,0x54,0x33,0x30,0x30,0x46,0x20,0x3D,0x20,0x2A,0x32,
+	0x00,0x10,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x54,0x72,0x65,0x6D,0x6F,0x72,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x53,0x79,0x6E,0x74,0x61,
+	0x78,0x3A,0x20,0x54,0x20,0x2B,0x20,0x4F,0x6E,0x20,0x74,0x69,
+	0x6D,0x65,0x20,0x2B,0x20,0x4F,0x66,0x66,0x20,0x74,0x69,0x6D,
+	0x65,0x00,0x3E,0x54,0x68,0x69,0x73,0x20,0x77,0x65,0x69,0x72,
+	0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,
+	0x6C,0x6C,0x20,0x73,0x65,0x74,0x20,0x74,0x68,0x65,0x20,0x76,
+	0x6F,0x6C,0x75,0x6D,0x65,0x20,0x74,0x6F,0x20,0x7A,0x65,0x72,
+	0x6F,0x20,0x64,0x75,0x72,0x69,0x6E,0x67,0x20,0x6F,0x66,0x66,
+	0x20,0x74,0x69,0x6D,0x65,0x36,0x6E,0x75,0x6D,0x62,0x65,0x72,
+	0x20,0x6F,0x66,0x20,0x74,0x69,0x63,0x6B,0x73,0x2E,0x20,0x49,
+	0x74,0x20,0x69,0x73,0x20,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,
+	0x64,0x20,0x66,0x6F,0x72,0x20,0x53,0x54,0x4D,0x20,0x63,0x6F,
+	0x6D,0x70,0x61,0x74,0x69,0x62,0x69,0x6C,0x69,0x74,0x79,0x2E,
+	0x00,0x27,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x45,0x78,0x74,0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70,
+	0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,
+	0x2F,0x64,0x6F,0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
+	0x40,0x43,0x30,0x30,0x32,0x19,0x53,0x79,0x6E,0x74,0x61,0x78,
+	0x3A,0x20,0x58,0x28,0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20,
+	0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3C,0x54,0x68,0x69,
+	0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,
+	0x72,0x6B,0x73,0x20,0x61,0x73,0x20,0x66,0x69,0x6E,0x65,0x20,
+	0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,
+	0x70,0x2F,0x64,0x6F,0x77,0x6E,0x2C,0x20,0x62,0x75,0x74,0x20,
+	0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x18,0x77,0x69,
+	0x6C,0x6C,0x20,0x62,0x65,0x20,0x64,0x69,0x76,0x69,0x64,0x65,
+	0x64,0x20,0x62,0x79,0x20,0x66,0x6F,0x75,0x72,0x2E,0x00,0x03,
+	0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
+	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x0A,0x40,0x4C,0x4B,0x65,0x79,
-	0x62,0x6F,0x61,0x72,0x64,0x00,0x0B,0x3E,0x40,0x58,0x30,0x32,
-	0x30,0x40,0x43,0x30,0x30,0x32,0x4A,0x3E,0x49,0x66,0x20,0x79,
-	0x6F,0x75,0x20,0x68,0x61,0x76,0x65,0x20,0x61,0x6E,0x20,0x61,
-	0x6D,0x62,0x69,0x74,0x69,0x6F,0x6E,0x20,0x74,0x6F,0x20,0x63,
-	0x72,0x65,0x61,0x74,0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20,
-	0x65,0x66,0x66,0x69,0x63,0x69,0x65,0x6E,0x74,0x6C,0x79,0x20,
-	0x77,0x65,0x20,0x73,0x74,0x72,0x6F,0x6E,0x67,0x6C,0x79,0x20,
-	0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x44,0x74,0x68,
-	0x61,0x74,0x20,0x79,0x6F,0x75,0x20,0x6C,0x65,0x61,0x72,0x6E,
-	0x20,0x41,0x4C,0x4C,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,
-	0x62,0x6F,0x61,0x72,0x64,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,
-	0x6F,0x6E,0x73,0x2E,0x20,0x4D,0x61,0x6E,0x79,0x20,0x6F,0x66,
-	0x20,0x74,0x68,0x65,0x6D,0x20,0x61,0x72,0x65,0x20,0x74,0x68,
-	0x65,0x20,0x73,0x61,0x6D,0x65,0x45,0x66,0x72,0x6F,0x6D,0x20,
-	0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,
-	0x31,0x20,0x61,0x6E,0x64,0x20,0x50,0x72,0x6F,0x54,0x72,0x61,
-	0x63,0x6B,0x65,0x72,0x20,0x74,0x6F,0x20,0x65,0x6E,0x73,0x75,
-	0x72,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x79,0x6F,0x75,0x20,
-	0x66,0x65,0x65,0x6C,0x20,0x63,0x6F,0x6D,0x66,0x6F,0x72,0x74,
-	0x61,0x62,0x6C,0x65,0x2E,0x75,0x73,0x69,0x6E,0x67,0x20,0x74,
-	0x68,0x69,0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,
-	0x66,0x72,0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x76,0x65,0x72,
-	0x79,0x20,0x66,0x69,0x72,0x73,0x74,0x20,0x6D,0x69,0x6E,0x75,
-	0x74,0x65,0x2E,0x01,0x3E,0x0B,0x3E,0x40,0x58,0x30,0x32,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x26,0x3E,0x59,0x6F,0x75,0x20,0x73,
-	0x68,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65,0x20,0x61,0x77,0x61,
-	0x72,0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x66,0x61,
-	0x63,0x74,0x20,0x74,0x68,0x61,0x74,0x3A,0x01,0x3E,0x48,0x3E,
-	0x40,0x43,0x30,0x30,0x32,0x54,0x68,0x69,0x73,0x20,0x68,0x65,
-	0x6C,0x70,0x20,0x74,0x65,0x78,0x74,0x20,0x69,0x73,0x20,0x77,
-	0x72,0x69,0x74,0x74,0x65,0x6E,0x20,0x75,0x73,0x69,0x6E,0x67,
-	0x20,0x61,0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x6B,
-	0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x2E,0x20,0x54,0x68,0x65,
-	0x72,0x65,0x66,0x6F,0x72,0x65,0x20,0x73,0x6F,0x6D,0x65,0x2F,
-	0x72,0x65,0x66,0x65,0x72,0x65,0x6E,0x63,0x65,0x73,0x20,0x74,
-	0x6F,0x20,0x6E,0x6F,0x6E,0x2D,0x6F,0x72,0x64,0x69,0x6E,0x61,
-	0x72,0x79,0x20,0x6B,0x65,0x79,0x73,0x20,0x6D,0x69,0x67,0x68,
-	0x74,0x20,0x62,0x65,0x20,0x77,0x72,0x6F,0x6E,0x67,0x2E,0x0F,
-	0x53,0x68,0x20,0x3D,0x20,0x73,0x68,0x69,0x66,0x74,0x20,0x6B,
-	0x65,0x79,0x2E,0x01,0x3E,0x10,0x40,0x58,0x30,0x34,0x30,0x40,
-	0x43,0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x0B,0x3E,
-	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x25,0x41,
-	0x6C,0x74,0x2B,0x45,0x6E,0x74,0x65,0x72,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x54,0x6F,0x67,0x67,0x6C,0x65,0x20,0x66,0x75,0x6C,
-	0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,
-	0x01,0x3E,0x2C,0x3E,0x28,0x4F,0x72,0x20,0x22,0x4C,0x65,0x66,
-	0x74,0x20,0x43,0x74,0x72,0x6C,0x20,0x2B,0x20,0x4C,0x65,0x66,
-	0x74,0x20,0x43,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x2B,0x20,
-	0x46,0x22,0x20,0x6F,0x6E,0x20,0x4D,0x61,0x63,0x73,0x29,0x00,
-	0x17,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,
-	0x75,0x72,0x73,0x6F,0x72,0x20,0x6D,0x6F,0x76,0x65,0x73,0x3A,
-	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x1D,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x69,0x6E,0x20,0x70,0x61,
-	0x74,0x74,0x65,0x72,0x6E,0x2E,0x32,0x3E,0x43,0x74,0x72,0x6C,
-	0x2B,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2D,0x70,0x6C,
-	0x61,0x79,0x20,0x66,0x72,0x6F,0x6D,0x20,0x46,0x39,0x2E,0x2E,
-	0x46,0x31,0x32,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x2F,0x3E,0x53,
-	0x68,0x2B,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x53,0x74,0x6F,0x72,0x65,0x20,0x63,0x75,0x72,
-	0x72,0x65,0x6E,0x74,0x20,0x6C,0x69,0x6E,0x65,0x20,0x69,0x6E,
-	0x20,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x2E,0x24,0x3E,0x50,
-	0x61,0x67,0x65,0x55,0x70,0x20,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x4A,0x75,0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69,0x6E,0x65,
-	0x73,0x20,0x75,0x70,0x77,0x61,0x72,0x64,0x73,0x2E,0x27,0x3E,
-	0x50,0x61,0x67,0x65,0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69,
-	0x6E,0x65,0x73,0x20,0x64,0x6F,0x77,0x6E,0x77,0x61,0x72,0x64,
-	0x73,0x2E,0x1B,0x3E,0x48,0x6F,0x6D,0x65,0x20,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x6C,
-	0x69,0x6E,0x65,0x20,0x30,0x2E,0x1D,0x3E,0x45,0x6E,0x64,0x20,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,
-	0x6F,0x20,0x6C,0x61,0x73,0x74,0x20,0x6C,0x69,0x6E,0x65,0x2E,
-	0x1E,0x3E,0x54,0x61,0x62,0x20,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x6E,0x65,0x78,0x74,
-	0x20,0x74,0x72,0x61,0x63,0x6B,0x2E,0x33,0x3E,0x41,0x6C,0x74,
-	0x2B,0x51,0x2E,0x2E,0x49,0x20,0x40,0x54,0x31,0x36,0x30,0x4A,
-	0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x72,0x61,0x63,0x6B,
-	0x20,0x28,0x30,0x2E,0x2E,0x37,0x29,0x20,0x4D,0x4F,0x44,0x20,
-	0x4E,0x2D,0x43,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x73,0x2E,0x34,
-	0x3E,0x41,0x6C,0x74,0x2B,0x41,0x2E,0x2E,0x4B,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,
-	0x72,0x61,0x63,0x6B,0x20,0x28,0x38,0x2E,0x2E,0x31,0x35,0x29,
-	0x20,0x4D,0x4F,0x44,0x20,0x4E,0x2D,0x43,0x68,0x61,0x6E,0x6E,
-	0x65,0x6C,0x73,0x2E,0x00,0x19,0x40,0x58,0x30,0x34,0x30,0x40,
-	0x43,0x30,0x30,0x31,0x43,0x75,0x74,0x2F,0x43,0x6F,0x70,0x79,
-	0x2F,0x50,0x61,0x73,0x74,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x34,0x44,0x65,0x6C,0x65,
-	0x74,0x65,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,
-	0x65,0x74,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x6F,0x72,0x20,
-	0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F,0x6C,0x75,0x6D,
-	0x6E,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,
-	0x39,0x3E,0x53,0x68,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,
-	0x6E,0x6F,0x74,0x65,0x2C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
-	0x20,0x61,0x6E,0x64,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20,
-	0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x35,0x3E,
-	0x43,0x74,0x72,0x6C,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,
-	0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x6E,0x64,0x20,0x65,
-	0x66,0x66,0x65,0x63,0x74,0x20,0x61,0x74,0x20,0x63,0x75,0x72,
-	0x73,0x6F,0x72,0x2E,0x29,0x3E,0x41,0x6C,0x74,0x2B,0x44,0x65,
-	0x6C,0x65,0x74,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,
-	0x6C,0x65,0x74,0x65,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20,
-	0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x24,0x3E,
-	0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x6E,0x6F,0x74,0x65,
-	0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x27,
-	0x3E,0x53,0x68,0x2B,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,
-	0x6C,0x69,0x6E,0x65,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,
-	0x6F,0x72,0x2E,0x25,0x3E,0x42,0x61,0x63,0x6B,0x73,0x70,0x61,
-	0x63,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,
-	0x74,0x65,0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,
-	0x6E,0x6F,0x74,0x65,0x2E,0x28,0x3E,0x53,0x68,0x2B,0x42,0x61,
-	0x63,0x6B,0x73,0x70,0x61,0x63,0x65,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x70,0x72,0x65,0x76,
-	0x69,0x6F,0x75,0x73,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x1C,0x3E,
-	0x41,0x6C,0x74,0x2B,0x43,0x75,0x72,0x73,0x6F,0x72,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x4D,0x61,0x72,0x6B,0x20,0x62,0x6C,0x6F,
-	0x63,0x6B,0x2E,0x16,0x3E,0x53,0x68,0x2B,0x46,0x33,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x74,0x72,0x61,0x63,
-	0x6B,0x2E,0x17,0x3E,0x53,0x68,0x2B,0x46,0x34,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20,0x74,0x72,0x61,0x63,
-	0x6B,0x2E,0x18,0x3E,0x53,0x68,0x2B,0x46,0x35,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x74,0x72,0x61,
-	0x63,0x6B,0x2E,0x1A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x33,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x70,0x61,
-	0x74,0x74,0x65,0x72,0x6E,0x2E,0x1B,0x3E,0x43,0x74,0x72,0x6C,
-	0x2B,0x46,0x34,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x70,
-	0x79,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x1C,0x3E,
-	0x43,0x74,0x72,0x6C,0x2B,0x46,0x35,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x70,0x61,0x74,0x74,0x65,
-	0x72,0x6E,0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x33,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x62,0x6C,0x6F,
-	0x63,0x6B,0x2E,0x18,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x34,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20,0x62,0x6C,
-	0x6F,0x63,0x6B,0x2E,0x19,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x35,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x73,0x74,0x65,0x20,
-	0x62,0x6C,0x6F,0x63,0x6B,0x2E,0x20,0x3E,0x41,0x6C,0x74,0x2B,
-	0x43,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x72,0x6B,
-	0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x74,0x72,0x61,
-	0x63,0x6B,0x2E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
-	0x30,0x30,0x31,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,
-	0x65,0x6F,0x75,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
-	0x40,0x43,0x30,0x30,0x32,0x1C,0x52,0x69,0x67,0x68,0x74,0x20,
-	0x63,0x74,0x72,0x6C,0x2E,0x20,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x50,0x6C,0x61,0x79,0x20,0x73,0x6F,0x6E,0x67,0x2E,0x1D,0x3E,
-	0x41,0x6C,0x74,0x20,0x47,0x72,0x20,0x20,0x20,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61,0x74,0x74,
-	0x65,0x72,0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,
-	0x73,0x68,0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x52,0x65,0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74,0x74,0x65,
-	0x72,0x6E,0x2E,0x19,0x3E,0x53,0x70,0x61,0x63,0x65,0x20,0x20,
-	0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x74,0x6F,0x70,0x2F,
-	0x45,0x64,0x69,0x74,0x2E,0x1B,0x3E,0x46,0x31,0x2E,0x2E,0x46,
-	0x37,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,
-	0x74,0x20,0x6F,0x63,0x74,0x61,0x76,0x65,0x2E,0x27,0x3E,0x4B,
-	0x65,0x79,0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x45,0x73,0x63,
+	0x2A,0x0D,0x40,0x4C,0x4B,0x65,0x79,0x62,0x69,0x6E,0x64,0x69,
+	0x6E,0x67,0x73,0x00,0x0B,0x3E,0x40,0x58,0x30,0x32,0x30,0x40,
+	0x43,0x30,0x30,0x32,0x4A,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75,
+	0x20,0x68,0x61,0x76,0x65,0x20,0x61,0x6E,0x20,0x61,0x6D,0x62,
+	0x69,0x74,0x69,0x6F,0x6E,0x20,0x74,0x6F,0x20,0x63,0x72,0x65,
+	0x61,0x74,0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20,0x65,0x66,
+	0x66,0x69,0x63,0x69,0x65,0x6E,0x74,0x6C,0x79,0x20,0x77,0x65,
+	0x20,0x73,0x74,0x72,0x6F,0x6E,0x67,0x6C,0x79,0x20,0x72,0x65,
+	0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x44,0x74,0x68,0x61,0x74,
+	0x20,0x79,0x6F,0x75,0x20,0x6C,0x65,0x61,0x72,0x6E,0x20,0x41,
+	0x4C,0x4C,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x6F,
+	0x61,0x72,0x64,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E,
+	0x73,0x2E,0x20,0x4D,0x61,0x6E,0x79,0x20,0x6F,0x66,0x20,0x74,
+	0x68,0x65,0x6D,0x20,0x61,0x72,0x65,0x20,0x74,0x68,0x65,0x20,
+	0x73,0x61,0x6D,0x65,0x45,0x66,0x72,0x6F,0x6D,0x20,0x46,0x61,
+	0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x31,0x20,
+	0x61,0x6E,0x64,0x20,0x50,0x72,0x6F,0x54,0x72,0x61,0x63,0x6B,
+	0x65,0x72,0x20,0x74,0x6F,0x20,0x65,0x6E,0x73,0x75,0x72,0x65,
+	0x20,0x74,0x68,0x61,0x74,0x20,0x79,0x6F,0x75,0x20,0x66,0x65,
+	0x65,0x6C,0x20,0x63,0x6F,0x6D,0x66,0x6F,0x72,0x74,0x61,0x62,
+	0x6C,0x65,0x2E,0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x68,0x69,
+	0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x66,0x72,
+	0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x76,0x65,0x72,0x79,0x20,
+	0x66,0x69,0x72,0x73,0x74,0x20,0x6D,0x69,0x6E,0x75,0x74,0x65,
+	0x2E,0x01,0x3E,0x0B,0x3E,0x40,0x58,0x30,0x32,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x26,0x3E,0x59,0x6F,0x75,0x20,0x73,0x68,0x6F,
+	0x75,0x6C,0x64,0x20,0x62,0x65,0x20,0x61,0x77,0x61,0x72,0x65,
+	0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x66,0x61,0x63,0x74,
+	0x20,0x74,0x68,0x61,0x74,0x3A,0x01,0x3E,0x48,0x3E,0x40,0x43,
+	0x30,0x30,0x32,0x54,0x68,0x69,0x73,0x20,0x68,0x65,0x6C,0x70,
+	0x20,0x74,0x65,0x78,0x74,0x20,0x69,0x73,0x20,0x77,0x72,0x69,
+	0x74,0x74,0x65,0x6E,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x61,
+	0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x6B,0x65,0x79,
+	0x62,0x6F,0x61,0x72,0x64,0x2E,0x20,0x54,0x68,0x65,0x72,0x65,
+	0x66,0x6F,0x72,0x65,0x20,0x73,0x6F,0x6D,0x65,0x2F,0x72,0x65,
+	0x66,0x65,0x72,0x65,0x6E,0x63,0x65,0x73,0x20,0x74,0x6F,0x20,
+	0x6E,0x6F,0x6E,0x2D,0x6F,0x72,0x64,0x69,0x6E,0x61,0x72,0x79,
+	0x20,0x6B,0x65,0x79,0x73,0x20,0x6D,0x69,0x67,0x68,0x74,0x20,
+	0x62,0x65,0x20,0x77,0x72,0x6F,0x6E,0x67,0x2E,0x0F,0x53,0x68,
+	0x20,0x3D,0x20,0x73,0x68,0x69,0x66,0x74,0x20,0x6B,0x65,0x79,
+	0x2E,0x01,0x3E,0x10,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
+	0x30,0x31,0x41,0x75,0x64,0x69,0x6F,0x3A,0x0B,0x3E,0x40,0x58,
+	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x32,0x3E,0x43,0x74,
+	0x72,0x6C,0x20,0x26,0x20,0x6E,0x75,0x6D,0x70,0x61,0x64,0x2B,
 	0x20,0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x63,0x72,0x65,0x61,
-	0x73,0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64,
-	0x2E,0x22,0x3E,0x53,0x68,0x2B,0x28,0x31,0x2F,0x32,0x29,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x63,0x72,0x65,0x61,0x73,
-	0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64,0x2E,
-	0x29,0x3E,0x43,0x61,0x70,0x73,0x4C,0x6F,0x63,0x6B,0x20,0x6F,
-	0x72,0x20,0x3C,0x3E,0x20,0x40,0x54,0x31,0x36,0x30,0x45,0x6E,
-	0x74,0x65,0x72,0x20,0x4B,0x65,0x79,0x6F,0x66,0x66,0x2D,0x22,
-	0x6E,0x6F,0x74,0x65,0x22,0x2E,0x25,0x3E,0x53,0x68,0x2B,0x4C,
-	0x65,0x66,0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x63,
-	0x72,0x65,0x61,0x73,0x65,0x20,0x73,0x6F,0x6E,0x67,0x20,0x70,
-	0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x26,0x3E,0x53,0x68,
-	0x2B,0x52,0x69,0x67,0x68,0x74,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x44,0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x73,0x6F,0x6E,
-	0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x28,
-	0x3E,0x43,0x74,0x72,0x6C,0x2B,0x4C,0x65,0x66,0x74,0x20,0x40,
+	0x73,0x65,0x20,0x6D,0x61,0x73,0x74,0x65,0x72,0x20,0x76,0x6F,
+	0x6C,0x75,0x6D,0x65,0x20,0x62,0x79,0x20,0x31,0x36,0x2E,0x32,
+	0x3E,0x43,0x74,0x72,0x6C,0x20,0x26,0x20,0x6E,0x75,0x6D,0x70,
+	0x61,0x64,0x2D,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x63,
+	0x72,0x65,0x61,0x73,0x65,0x20,0x6D,0x61,0x73,0x74,0x65,0x72,
+	0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x62,0x79,0x20,0x31,
+	0x36,0x2E,0x00,0x10,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
+	0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x0B,0x3E,0x40,0x58,
+	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x25,0x41,0x6C,0x74,
+	0x2B,0x45,0x6E,0x74,0x65,0x72,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x54,0x6F,0x67,0x67,0x6C,0x65,0x20,0x66,0x75,0x6C,0x6C,0x73,
+	0x63,0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x01,0x3E,
+	0x2C,0x3E,0x28,0x4F,0x72,0x20,0x22,0x4C,0x65,0x66,0x74,0x20,
+	0x43,0x74,0x72,0x6C,0x20,0x2B,0x20,0x4C,0x65,0x66,0x74,0x20,
+	0x43,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x2B,0x20,0x46,0x22,
+	0x20,0x6F,0x6E,0x20,0x4D,0x61,0x63,0x73,0x29,0x00,0x17,0x40,
+	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x75,0x72,
+	0x73,0x6F,0x72,0x20,0x6D,0x6F,0x76,0x65,0x73,0x3A,0x0B,0x3E,
+	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1D,0x46,
+	0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x4A,0x75,0x6D,0x70,0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x2E,0x32,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,
+	0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2D,0x70,0x6C,0x61,0x79,
+	0x20,0x66,0x72,0x6F,0x6D,0x20,0x46,0x39,0x2E,0x2E,0x46,0x31,
+	0x32,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x2F,0x3E,0x53,0x68,0x2B,
+	0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x53,0x74,0x6F,0x72,0x65,0x20,0x63,0x75,0x72,0x72,0x65,
+	0x6E,0x74,0x20,0x6C,0x69,0x6E,0x65,0x20,0x69,0x6E,0x20,0x46,
+	0x39,0x2E,0x2E,0x46,0x31,0x32,0x2E,0x24,0x3E,0x50,0x61,0x67,
+	0x65,0x55,0x70,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75,
+	0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69,0x6E,0x65,0x73,0x20,
+	0x75,0x70,0x77,0x61,0x72,0x64,0x73,0x2E,0x27,0x3E,0x50,0x61,
+	0x67,0x65,0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x4A,0x75,0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69,0x6E,0x65,
+	0x73,0x20,0x64,0x6F,0x77,0x6E,0x77,0x61,0x72,0x64,0x73,0x2E,
+	0x1B,0x3E,0x48,0x6F,0x6D,0x65,0x20,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x6C,0x69,0x6E,
+	0x65,0x20,0x30,0x2E,0x1D,0x3E,0x45,0x6E,0x64,0x20,0x20,0x40,
+	0x54,0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,
+	0x6C,0x61,0x73,0x74,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x1E,0x3E,
+	0x54,0x61,0x62,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75,
+	0x6D,0x70,0x20,0x74,0x6F,0x20,0x6E,0x65,0x78,0x74,0x20,0x74,
+	0x72,0x61,0x63,0x6B,0x2E,0x33,0x3E,0x41,0x6C,0x74,0x2B,0x51,
+	0x2E,0x2E,0x49,0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75,0x6D,
+	0x70,0x20,0x74,0x6F,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x28,
+	0x30,0x2E,0x2E,0x37,0x29,0x20,0x4D,0x4F,0x44,0x20,0x4E,0x2D,
+	0x43,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x73,0x2E,0x34,0x3E,0x41,
+	0x6C,0x74,0x2B,0x41,0x2E,0x2E,0x4B,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x72,0x61,
+	0x63,0x6B,0x20,0x28,0x38,0x2E,0x2E,0x31,0x35,0x29,0x20,0x4D,
+	0x4F,0x44,0x20,0x4E,0x2D,0x43,0x68,0x61,0x6E,0x6E,0x65,0x6C,
+	0x73,0x2E,0x00,0x19,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
+	0x30,0x31,0x43,0x75,0x74,0x2F,0x43,0x6F,0x70,0x79,0x2F,0x50,
+	0x61,0x73,0x74,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
+	0x40,0x43,0x30,0x30,0x32,0x34,0x44,0x65,0x6C,0x65,0x74,0x65,
+	0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,
+	0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x6F,0x72,0x20,0x76,0x6F,
+	0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x20,
+	0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x39,0x3E,
+	0x53,0x68,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x6E,0x6F,
+	0x74,0x65,0x2C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,
+	0x6E,0x64,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20,0x61,0x74,
+	0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x35,0x3E,0x43,0x74,
+	0x72,0x6C,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x76,0x6F,
+	0x6C,0x75,0x6D,0x65,0x20,0x61,0x6E,0x64,0x20,0x65,0x66,0x66,
+	0x65,0x63,0x74,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,
+	0x72,0x2E,0x29,0x3E,0x41,0x6C,0x74,0x2B,0x44,0x65,0x6C,0x65,
+	0x74,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,
+	0x74,0x65,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20,0x61,0x74,
+	0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x24,0x3E,0x49,0x6E,
+	0x73,0x65,0x72,0x74,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x49,
+	0x6E,0x73,0x65,0x72,0x74,0x20,0x6E,0x6F,0x74,0x65,0x20,0x61,
+	0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x27,0x3E,0x53,
+	0x68,0x2B,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x6C,0x69,
+	0x6E,0x65,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,
+	0x2E,0x25,0x3E,0x42,0x61,0x63,0x6B,0x73,0x70,0x61,0x63,0x65,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,
+	0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x6E,0x6F,
+	0x74,0x65,0x2E,0x28,0x3E,0x53,0x68,0x2B,0x42,0x61,0x63,0x6B,
+	0x73,0x70,0x61,0x63,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44,
+	0x65,0x6C,0x65,0x74,0x65,0x20,0x70,0x72,0x65,0x76,0x69,0x6F,
+	0x75,0x73,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x1C,0x3E,0x41,0x6C,
+	0x74,0x2B,0x43,0x75,0x72,0x73,0x6F,0x72,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x4D,0x61,0x72,0x6B,0x20,0x62,0x6C,0x6F,0x63,0x6B,
+	0x2E,0x16,0x3E,0x53,0x68,0x2B,0x46,0x33,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x43,0x75,0x74,0x20,0x74,0x72,0x61,0x63,0x6B,0x2E,
+	0x17,0x3E,0x53,0x68,0x2B,0x46,0x34,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x43,0x6F,0x70,0x79,0x20,0x74,0x72,0x61,0x63,0x6B,0x2E,
+	0x18,0x3E,0x53,0x68,0x2B,0x46,0x35,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x74,0x72,0x61,0x63,0x6B,
+	0x2E,0x1A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x33,0x20,0x40,
+	0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x70,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x2E,0x1B,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,
+	0x34,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20,
+	0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x1C,0x3E,0x43,0x74,
+	0x72,0x6C,0x2B,0x46,0x35,0x20,0x40,0x54,0x31,0x36,0x30,0x50,
+	0x61,0x73,0x74,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,
+	0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x33,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B,
+	0x2E,0x18,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x34,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20,0x62,0x6C,0x6F,0x63,
+	0x6B,0x2E,0x19,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x35,0x20,0x40,
+	0x54,0x31,0x36,0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x62,0x6C,
+	0x6F,0x63,0x6B,0x2E,0x20,0x3E,0x41,0x6C,0x74,0x2B,0x43,0x20,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x72,0x6B,0x20,0x63,
+	0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x74,0x72,0x61,0x63,0x6B,
+	0x2E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
+	0x31,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,
+	0x75,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x1C,0x52,0x69,0x67,0x68,0x74,0x20,0x63,0x74,
+	0x72,0x6C,0x2E,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x6C,
+	0x61,0x79,0x20,0x73,0x6F,0x6E,0x67,0x2E,0x1D,0x3E,0x41,0x6C,
+	0x74,0x20,0x47,0x72,0x20,0x20,0x20,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
+	0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x73,0x68,
+	0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x52,0x65,
+	0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,
+	0x2E,0x19,0x3E,0x53,0x70,0x61,0x63,0x65,0x20,0x20,0x20,0x20,
+	0x40,0x54,0x31,0x36,0x30,0x53,0x74,0x6F,0x70,0x2F,0x45,0x64,
+	0x69,0x74,0x2E,0x1B,0x3E,0x46,0x31,0x2E,0x2E,0x46,0x37,0x20,
+	0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,
+	0x6F,0x63,0x74,0x61,0x76,0x65,0x2E,0x27,0x3E,0x4B,0x65,0x79,
+	0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x45,0x73,0x63,0x20,0x40,
 	0x54,0x31,0x36,0x30,0x49,0x6E,0x63,0x72,0x65,0x61,0x73,0x65,
-	0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75,0x6D,
-	0x62,0x65,0x72,0x2E,0x29,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x52,
+	0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64,0x2E,0x22,
+	0x3E,0x53,0x68,0x2B,0x28,0x31,0x2F,0x32,0x29,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x44,0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20,
+	0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64,0x2E,0x29,0x3E,
+	0x43,0x61,0x70,0x73,0x4C,0x6F,0x63,0x6B,0x20,0x6F,0x72,0x20,
+	0x3C,0x3E,0x20,0x40,0x54,0x31,0x36,0x30,0x45,0x6E,0x74,0x65,
+	0x72,0x20,0x4B,0x65,0x79,0x6F,0x66,0x66,0x2D,0x22,0x6E,0x6F,
+	0x74,0x65,0x22,0x2E,0x25,0x3E,0x53,0x68,0x2B,0x4C,0x65,0x66,
+	0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x63,0x72,0x65,
+	0x61,0x73,0x65,0x20,0x73,0x6F,0x6E,0x67,0x20,0x70,0x6F,0x73,
+	0x69,0x74,0x69,0x6F,0x6E,0x2E,0x26,0x3E,0x53,0x68,0x2B,0x52,
 	0x69,0x67,0x68,0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,
-	0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x70,0x61,0x74,0x74,0x65,
-	0x72,0x6E,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x2E,0x00,0x2C,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69,
-	0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x20,
-	0x28,0x6F,0x6E,0x20,0x61,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65,
-	0x79,0x62,0x6F,0x61,0x72,0x64,0x29,0x3A,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x52,0x69,0x67,
-	0x68,0x74,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x20,
-	0x40,0x54,0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x73,0x6F,
-	0x6E,0x67,0x2E,0x32,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x61,
-	0x6C,0x74,0x20,0x28,0x6F,0x72,0x20,0x6C,0x65,0x66,0x74,0x20,
-	0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x29,0x20,0x20,0x20,0x20,
-	0x40,0x54,0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61,
-	0x74,0x74,0x65,0x72,0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68,
-	0x74,0x20,0x73,0x68,0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x32,
-	0x34,0x30,0x52,0x65,0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74,
-	0x74,0x65,0x72,0x6E,0x2E,0x00,0x1B,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x57,0x69,0x6E,0x64,0x6F,0x77,0x20,
-	0x73,0x77,0x69,0x74,0x63,0x68,0x69,0x6E,0x67,0x3A,0x0B,0x3E,
-	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x05,0x43,
-	0x74,0x72,0x6C,0x2B,0x16,0x3E,0x41,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x41,0x64,0x76,0x61,0x6E,0x63,0x65,0x64,0x20,0x65,0x64,
-	0x69,0x74,0x2E,0x0E,0x3E,0x42,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x41,0x62,0x6F,0x75,0x74,0x2E,0x16,0x3E,0x43,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,
-	0x74,0x69,0x6F,0x6E,0x2E,0x18,0x3E,0x44,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x44,0x69,0x73,0x6B,0x20,0x6F,0x70,0x65,0x72,0x61,
-	0x74,0x69,0x6F,0x6E,0x73,0x2E,0x20,0x3E,0x45,0x20,0x40,0x54,
-	0x31,0x36,0x30,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,
-	0x69,0x74,0x6F,0x72,0x20,0x65,0x78,0x74,0x65,0x6E,0x73,0x69,
-	0x6F,0x6E,0x2E,0x0D,0x3E,0x48,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x48,0x65,0x6C,0x70,0x2E,0x1A,0x3E,0x49,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,
-	0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x2E,0x2B,0x3E,0x4D,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,
-	0x65,0x6E,0x74,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x20,0x65,
-	0x78,0x74,0x65,0x6E,0x73,0x69,0x6F,0x6E,0x2E,0x20,0x28,0x4D,
-	0x49,0x44,0x49,0x29,0x10,0x3E,0x4E,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x4E,0x69,0x62,0x62,0x6C,0x65,0x73,0x2E,0x10,0x3E,0x50,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x74,0x74,0x65,0x72,
-	0x6E,0x2E,0x0D,0x3E,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x54,
-	0x72,0x69,0x6D,0x2E,0x16,0x3E,0x53,0x20,0x40,0x54,0x31,0x36,
+	0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x73,0x6F,0x6E,0x67,0x20,
+	0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x28,0x3E,0x43,
+	0x74,0x72,0x6C,0x2B,0x4C,0x65,0x66,0x74,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x49,0x6E,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x70,
+	0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75,0x6D,0x62,0x65,
+	0x72,0x2E,0x29,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x52,0x69,0x67,
+	0x68,0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x63,0x72,
+	0x65,0x61,0x73,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,
+	0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x2E,0x00,0x2C,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69,0x73,0x63,
+	0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x20,0x28,0x6F,
+	0x6E,0x20,0x61,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65,0x79,0x62,
+	0x6F,0x61,0x72,0x64,0x29,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x52,0x69,0x67,0x68,0x74,
+	0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x20,0x40,0x54,
+	0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x73,0x6F,0x6E,0x67,
+	0x2E,0x32,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x61,0x6C,0x74,
+	0x20,0x28,0x6F,0x72,0x20,0x6C,0x65,0x66,0x74,0x20,0x63,0x6F,
+	0x6D,0x6D,0x61,0x6E,0x64,0x29,0x20,0x20,0x20,0x20,0x40,0x54,
+	0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,
+	0x73,0x68,0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x32,0x34,0x30,
+	0x52,0x65,0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74,0x74,0x65,
+	0x72,0x6E,0x2E,0x00,0x1B,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x57,0x69,0x6E,0x64,0x6F,0x77,0x20,0x73,0x77,
+	0x69,0x74,0x63,0x68,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58,
+	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x05,0x43,0x74,0x72,
+	0x6C,0x2B,0x16,0x3E,0x41,0x20,0x40,0x54,0x31,0x36,0x30,0x41,
+	0x64,0x76,0x61,0x6E,0x63,0x65,0x64,0x20,0x65,0x64,0x69,0x74,
+	0x2E,0x0E,0x3E,0x42,0x20,0x40,0x54,0x31,0x36,0x30,0x41,0x62,
+	0x6F,0x75,0x74,0x2E,0x16,0x3E,0x43,0x20,0x40,0x54,0x31,0x36,
+	0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,
+	0x6F,0x6E,0x2E,0x18,0x3E,0x44,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x44,0x69,0x73,0x6B,0x20,0x6F,0x70,0x65,0x72,0x61,0x74,0x69,
+	0x6F,0x6E,0x73,0x2E,0x20,0x3E,0x45,0x20,0x40,0x54,0x31,0x36,
 	0x30,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,0x74,
-	0x6F,0x72,0x2E,0x12,0x3E,0x54,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x2E,0x23,0x3E,
-	0x58,0x20,0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x69,0x6E,0x20,
-	0x73,0x63,0x72,0x65,0x65,0x6E,0x2E,0x20,0x28,0x61,0x6C,0x6D,
-	0x6F,0x73,0x74,0x20,0x61,0x6C,0x74,0x2B,0x58,0x29,0x27,0x3E,
-	0x5A,0x20,0x40,0x54,0x31,0x36,0x30,0x46,0x75,0x6C,0x6C,0x20,
-	0x73,0x63,0x72,0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x2E,
-	0x20,0x28,0x5A,0x20,0x66,0x6F,0x72,0x20,0x73,0x69,0x5A,0x65,
-	0x3F,0x29,0x19,0x3E,0x31,0x20,0x40,0x54,0x31,0x36,0x30,0x43,
+	0x6F,0x72,0x20,0x65,0x78,0x74,0x65,0x6E,0x73,0x69,0x6F,0x6E,
+	0x2E,0x0D,0x3E,0x48,0x20,0x40,0x54,0x31,0x36,0x30,0x48,0x65,
+	0x6C,0x70,0x2E,0x1A,0x3E,0x49,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x65,
+	0x64,0x69,0x74,0x6F,0x72,0x2E,0x2B,0x3E,0x4D,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,
+	0x74,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x20,0x65,0x78,0x74,
+	0x65,0x6E,0x73,0x69,0x6F,0x6E,0x2E,0x20,0x28,0x4D,0x49,0x44,
+	0x49,0x29,0x10,0x3E,0x4E,0x20,0x40,0x54,0x31,0x36,0x30,0x4E,
+	0x69,0x62,0x62,0x6C,0x65,0x73,0x2E,0x10,0x3E,0x50,0x20,0x40,
+	0x54,0x31,0x36,0x30,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,
+	0x0D,0x3E,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x69,
+	0x6D,0x2E,0x16,0x3E,0x53,0x20,0x40,0x54,0x31,0x36,0x30,0x53,
+	0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,
+	0x2E,0x12,0x3E,0x54,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,
+	0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x2E,0x23,0x3E,0x58,0x20,
+	0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x69,0x6E,0x20,0x73,0x63,
+	0x72,0x65,0x65,0x6E,0x2E,0x20,0x28,0x61,0x6C,0x6D,0x6F,0x73,
+	0x74,0x20,0x61,0x6C,0x74,0x2B,0x58,0x29,0x27,0x3E,0x5A,0x20,
+	0x40,0x54,0x31,0x36,0x30,0x46,0x75,0x6C,0x6C,0x20,0x73,0x63,
+	0x72,0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x2E,0x20,0x28,
+	0x5A,0x20,0x66,0x6F,0x72,0x20,0x73,0x69,0x5A,0x65,0x3F,0x29,
+	0x19,0x3E,0x31,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x6E,
+	0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x23,
+	0x31,0x2E,0x19,0x3E,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x43,
 	0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,
-	0x20,0x23,0x31,0x2E,0x19,0x3E,0x32,0x20,0x40,0x54,0x31,0x36,
+	0x20,0x23,0x32,0x2E,0x19,0x3E,0x33,0x20,0x40,0x54,0x31,0x36,
 	0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,
-	0x6F,0x6E,0x20,0x23,0x32,0x2E,0x19,0x3E,0x33,0x20,0x40,0x54,
+	0x6F,0x6E,0x20,0x23,0x33,0x2E,0x19,0x3E,0x34,0x20,0x40,0x54,
 	0x31,0x36,0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,
-	0x74,0x69,0x6F,0x6E,0x20,0x23,0x33,0x2E,0x19,0x3E,0x34,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,
-	0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x23,0x34,0x2E,0x00,0x2D,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6E,
-	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x73,0x65,0x6C,
-	0x65,0x63,0x74,0x20,0x28,0x4E,0x75,0x6D,0x65,0x72,0x69,0x63,
-	0x20,0x6B,0x65,0x79,0x70,0x61,0x64,0x29,0x3A,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x28,0x54,0x6F,
-	0x70,0x20,0x34,0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73,
-	0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63,
-	0x6B,0x2E,0x32,0x3E,0x27,0x2B,0x27,0x20,0x2B,0x54,0x6F,0x70,
-	0x20,0x34,0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74,
-	0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B,
-	0x20,0x2B,0x20,0x34,0x2E,0x23,0x3E,0x45,0x6E,0x74,0x65,0x72,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,
-	0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,
-	0x62,0x61,0x6E,0x6B,0x2E,0x1D,0x3E,0x30,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x6E,0x6F,0x20,
-	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x26,
-	0x3E,0x31,0x2E,0x2E,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x53,
+	0x74,0x69,0x6F,0x6E,0x20,0x23,0x34,0x2E,0x00,0x2D,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6E,0x73,0x74,
+	0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x73,0x65,0x6C,0x65,0x63,
+	0x74,0x20,0x28,0x4E,0x75,0x6D,0x65,0x72,0x69,0x63,0x20,0x6B,
+	0x65,0x79,0x70,0x61,0x64,0x29,0x3A,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x28,0x54,0x6F,0x70,0x20,
+	0x34,0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,
+	0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x2E,
+	0x32,0x3E,0x27,0x2B,0x27,0x20,0x2B,0x54,0x6F,0x70,0x20,0x34,
+	0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31,0x36,0x30,0x53,
 	0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
-	0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,
-	0x6B,0x2E,0x19,0x3E,0x2C,0x20,0x40,0x54,0x31,0x36,0x30,0x43,
-	0x6C,0x65,0x61,0x72,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
-	0x65,0x6E,0x74,0x2E,0x18,0x3E,0x53,0x68,0x2B,0x2C,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x43,0x6C,0x65,0x61,0x72,0x20,0x73,0x61,
-	0x6D,0x70,0x6C,0x65,0x2E,0x27,0x3E,0x53,0x68,0x2B,0x55,0x70,
-	0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,
-	0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x69,0x6E,
-	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x25,0x3E,0x53,
-	0x68,0x2B,0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x6E,0x65,0x78,0x74,0x20,
-	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x00,
-	0x1F,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,
-	0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2F,0x56,0x6F,0x6C,0x75,0x6D,
-	0x65,0x20,0x6D,0x61,0x63,0x72,0x6F,0x3A,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x2D,0x41,0x6C,0x74,
-	0x2B,0x31,0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36,0x30,0x57,
-	0x72,0x69,0x74,0x65,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,
-	0x2F,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20,0x63,
-	0x75,0x72,0x73,0x6F,0x72,0x2E,0x30,0x3E,0x53,0x68,0x2B,0x41,
-	0x6C,0x74,0x2B,0x31,0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x52,0x65,0x61,0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,
-	0x64,0x2F,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20,
-	0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x00,0x1C,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x61,0x6C,0x65,
-	0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
-	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
-	0x32,0x25,0x53,0x68,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x53,0x63,0x61,0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76,
-	0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,
-	0x63,0x6B,0x2E,0x2A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x56,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x53,0x63,0x61,0x6C,0x65,0x2D,0x66,
-	0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,
-	0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x27,0x3E,
-	0x41,0x6C,0x74,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x53,
-	0x63,0x61,0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F,
-	0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,
-	0x6B,0x2E,0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
-	0x30,0x31,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x3A,
-	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x36,0x53,0x68,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36,0x30,
-	0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,
-	0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
-	0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,
-	0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x35,0x3E,0x53,0x68,0x2B,
+	0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x2B,
+	0x20,0x34,0x2E,0x23,0x3E,0x45,0x6E,0x74,0x65,0x72,0x20,0x40,
+	0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69,
+	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x61,
+	0x6E,0x6B,0x2E,0x1D,0x3E,0x30,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x6E,0x6F,0x20,0x69,0x6E,
+	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x26,0x3E,0x31,
+	0x2E,0x2E,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,
+	0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
+	0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x2E,
+	0x19,0x3E,0x2C,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6C,0x65,
+	0x61,0x72,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,
+	0x74,0x2E,0x18,0x3E,0x53,0x68,0x2B,0x2C,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x43,0x6C,0x65,0x61,0x72,0x20,0x73,0x61,0x6D,0x70,
+	0x6C,0x65,0x2E,0x27,0x3E,0x53,0x68,0x2B,0x55,0x70,0x20,0x40,
+	0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x70,
+	0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x69,0x6E,0x73,0x74,
+	0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x25,0x3E,0x53,0x68,0x2B,
+	0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,
+	0x6C,0x65,0x63,0x74,0x20,0x6E,0x65,0x78,0x74,0x20,0x69,0x6E,
+	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x00,0x1F,0x40,
+	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6D,
+	0x6D,0x61,0x6E,0x64,0x2F,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,
+	0x6D,0x61,0x63,0x72,0x6F,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x2D,0x41,0x6C,0x74,0x2B,0x31,
+	0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36,0x30,0x57,0x72,0x69,
+	0x74,0x65,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2F,0x76,
+	0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20,0x63,0x75,0x72,
+	0x73,0x6F,0x72,0x2E,0x30,0x3E,0x53,0x68,0x2B,0x41,0x6C,0x74,
+	0x2B,0x31,0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36,0x30,0x52,
+	0x65,0x61,0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2F,
+	0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20,0x63,0x75,
+	0x72,0x73,0x6F,0x72,0x2E,0x00,0x1C,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x61,0x6C,0x65,0x2D,0x66,
+	0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x3A,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x25,
+	0x53,0x68,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x63,
+	0x61,0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,
+	0x75,0x6D,0x65,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,
+	0x2E,0x2A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x56,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x53,0x63,0x61,0x6C,0x65,0x2D,0x66,0x61,0x64,
+	0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x20,
+	0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x27,0x3E,0x41,0x6C,
+	0x74,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x63,0x61,
+	0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75,
+	0x6D,0x65,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x2E,
+	0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x3A,0x0B,0x3E,
+	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x36,0x53,
+	0x68,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,
+	0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72,
+	0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
+	0x6E,0x74,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,
+	0x64,0x6F,0x77,0x6E,0x2E,0x35,0x3E,0x53,0x68,0x2B,0x46,0x38,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,
+	0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,
+	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,
+	0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70,0x2E,0x3B,
+	0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,
+	0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,
+	0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x70,0x61,
+	0x74,0x74,0x65,0x72,0x6E,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x39,
+	0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x38,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,
+	0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,
+	0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x70,0x61,
+	0x74,0x74,0x65,0x72,0x6E,0x20,0x75,0x70,0x2E,0x38,0x3E,0x41,
+	0x6C,0x74,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36,0x30,0x54,
+	0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72,
+	0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
+	0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,
+	0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x41,0x6C,0x74,0x2B,
 	0x46,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,
 	0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,
 	0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,
-	0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70,
-	0x2E,0x3B,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x37,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,
-	0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,
-	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,
-	0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x64,0x6F,0x77,0x6E,
-	0x2E,0x39,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x38,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,
-	0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,
-	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,
-	0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x75,0x70,0x2E,0x38,
-	0x3E,0x41,0x6C,0x74,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,
-	0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,
-	0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,
-	0x63,0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x41,0x6C,
-	0x74,0x2B,0x46,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,
-	0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72,
-	0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
-	0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,
-	0x75,0x70,0x2E,0x34,0x3E,0x53,0x68,0x2B,0x46,0x31,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,
-	0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
-	0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,
-	0x63,0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x32,0x3E,0x53,0x68,
+	0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x75,0x70,
+	0x2E,0x34,0x3E,0x53,0x68,0x2B,0x46,0x31,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,
+	0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
+	0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,
+	0x20,0x64,0x6F,0x77,0x6E,0x2E,0x32,0x3E,0x53,0x68,0x2B,0x46,
+	0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,
+	0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,
+	0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,
+	0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70,0x2E,0x38,0x3E,0x43,
+	0x74,0x72,0x6C,0x2B,0x46,0x31,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,
+	0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,
+	0x73,0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,
+	0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x43,0x74,0x72,0x6C,
 	0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,
 	0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,
 	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,
-	0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70,0x2E,0x38,
-	0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x31,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,
-	0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
-	0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74,0x65,
-	0x72,0x6E,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x43,0x74,
-	0x72,0x6C,0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,
-	0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,
-	0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,
-	0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,
-	0x75,0x70,0x2E,0x35,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x31,0x20,
-	0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,
-	0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,
-	0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x62,0x6C,
-	0x6F,0x63,0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x33,0x3E,0x41,
-	0x6C,0x74,0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,
-	0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,
-	0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,
-	0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x75,0x70,
-	0x2E,0x01,0x3E,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
-	0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,
-	0x74,0x6F,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x1A,0x41,0x6C,0x74,0x2F,0x43,0x74,0x72,
-	0x6C,0x2B,0x41,0x20,0x40,0x54,0x31,0x36,0x30,0x52,0x61,0x6E,
-	0x67,0x65,0x20,0x61,0x6C,0x6C,0x2E,0x17,0x3E,0x41,0x6C,0x74,
-	0x2B,0x53,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x68,0x6F,0x77,
-	0x20,0x72,0x61,0x6E,0x67,0x65,0x2E,0x15,0x3E,0x41,0x6C,0x74,
-	0x2B,0x5A,0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D,
-	0x20,0x6F,0x75,0x74,0x2E,0x1A,0x3E,0x41,0x6C,0x74,0x2B,0x58,
-	0x20,0x6F,0x72,0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40,
-	0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x2E,0x16,0x3E,0x41,0x6C,
-	0x74,0x2F,0x43,0x74,0x72,0x6C,0x2B,0x43,0x20,0x40,0x54,0x31,
-	0x36,0x30,0x43,0x6F,0x70,0x79,0x2E,0x17,0x3E,0x41,0x6C,0x74,
-	0x2F,0x43,0x74,0x72,0x6C,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,
-	0x30,0x50,0x61,0x73,0x74,0x65,0x2E,0x11,0x3E,0x41,0x6C,0x74,
-	0x2B,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x72,0x6F,0x70,
-	0x2E,0x2A,0x3E,0x4D,0x6F,0x75,0x73,0x65,0x20,0x77,0x68,0x65,
-	0x65,0x6C,0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D,
-	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,
-	0x20,0x69,0x6E,0x2F,0x6F,0x75,0x74,0x2E,0x00,0x21,0x40,0x58,
-	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,
-	0x20,0x66,0x6F,0x72,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65,0x79,
-	0x62,0x6F,0x61,0x72,0x64,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3A,0x55,0x73,0x65,0x20,
-	0x6C,0x65,0x66,0x74,0x20,0x63,0x74,0x72,0x6C,0x2E,0x20,0x66,
-	0x6F,0x72,0x20,0x41,0x2F,0x58,0x2F,0x43,0x2F,0x56,0x20,0x28,
-	0x6D,0x61,0x72,0x6B,0x20,0x61,0x6C,0x6C,0x2F,0x63,0x75,0x74,
-	0x2F,0x63,0x6F,0x70,0x79,0x2F,0x70,0x61,0x73,0x74,0x65,0x29,
-	0x20,0x6B,0x65,0x79,0x73,0x2E,0x4B,0x3E,0x54,0x68,0x69,0x73,
-	0x20,0x61,0x70,0x70,0x6C,0x69,0x65,0x73,0x20,0x74,0x6F,0x20,
-	0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,
-	0x64,0x69,0x74,0x6F,0x72,0x20,0x28,0x41,0x2F,0x43,0x2F,0x56,
-	0x20,0x6F,0x6E,0x6C,0x79,0x29,0x20,0x61,0x6E,0x64,0x20,0x74,
-	0x65,0x78,0x74,0x20,0x6D,0x61,0x72,0x6B,0x69,0x6E,0x67,0x20,
-	0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x55,0x49,0x2E,0x00,0x03,
-	0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
+	0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x75,0x70,
+	0x2E,0x35,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x31,0x20,0x40,0x54,
+	0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,
+	0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
+	0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,
+	0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x33,0x3E,0x41,0x6C,0x74,
+	0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,
+	0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,
+	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,
+	0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x75,0x70,0x2E,0x01,
+	0x3E,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,0x74,0x6F,
+	0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
+	0x30,0x32,0x1A,0x41,0x6C,0x74,0x2F,0x43,0x74,0x72,0x6C,0x2B,
+	0x41,0x20,0x40,0x54,0x31,0x36,0x30,0x52,0x61,0x6E,0x67,0x65,
+	0x20,0x61,0x6C,0x6C,0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2B,0x53,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x68,0x6F,0x77,0x20,0x72,
+	0x61,0x6E,0x67,0x65,0x2E,0x15,0x3E,0x41,0x6C,0x74,0x2B,0x5A,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D,0x20,0x6F,
+	0x75,0x74,0x2E,0x1A,0x3E,0x41,0x6C,0x74,0x2B,0x58,0x20,0x6F,
+	0x72,0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40,0x54,0x31,
+	0x36,0x30,0x43,0x75,0x74,0x2E,0x16,0x3E,0x41,0x6C,0x74,0x2F,
+	0x43,0x74,0x72,0x6C,0x2B,0x43,0x20,0x40,0x54,0x31,0x36,0x30,
+	0x43,0x6F,0x70,0x79,0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2F,0x43,
+	0x74,0x72,0x6C,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x50,
+	0x61,0x73,0x74,0x65,0x2E,0x11,0x3E,0x41,0x6C,0x74,0x2B,0x52,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x72,0x6F,0x70,0x2E,0x2A,
+	0x3E,0x4D,0x6F,0x75,0x73,0x65,0x20,0x77,0x68,0x65,0x65,0x6C,
+	0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D,0x20,0x73,
+	0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,0x20,0x69,
+	0x6E,0x2F,0x6F,0x75,0x74,0x2E,0x00,0x21,0x40,0x58,0x30,0x34,
+	0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,0x20,0x66,
+	0x6F,0x72,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65,0x79,0x62,0x6F,
+	0x61,0x72,0x64,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
+	0x40,0x43,0x30,0x30,0x32,0x3A,0x55,0x73,0x65,0x20,0x6C,0x65,
+	0x66,0x74,0x20,0x63,0x74,0x72,0x6C,0x2E,0x20,0x66,0x6F,0x72,
+	0x20,0x41,0x2F,0x58,0x2F,0x43,0x2F,0x56,0x20,0x28,0x6D,0x61,
+	0x72,0x6B,0x20,0x61,0x6C,0x6C,0x2F,0x63,0x75,0x74,0x2F,0x63,
+	0x6F,0x70,0x79,0x2F,0x70,0x61,0x73,0x74,0x65,0x29,0x20,0x6B,
+	0x65,0x79,0x73,0x2E,0x4B,0x3E,0x54,0x68,0x69,0x73,0x20,0x61,
+	0x70,0x70,0x6C,0x69,0x65,0x73,0x20,0x74,0x6F,0x20,0x74,0x68,
+	0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,
+	0x74,0x6F,0x72,0x20,0x28,0x41,0x2F,0x43,0x2F,0x56,0x20,0x6F,
+	0x6E,0x6C,0x79,0x29,0x20,0x61,0x6E,0x64,0x20,0x74,0x65,0x78,
+	0x74,0x20,0x6D,0x61,0x72,0x6B,0x69,0x6E,0x67,0x20,0x69,0x6E,
+	0x20,0x74,0x68,0x65,0x20,0x55,0x49,0x2E,0x00,0x03,0x45,0x4E,
+	0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,
+	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x1B,0x40,0x4C,0x48,0x6F,0x77,0x20,0x74,0x6F,0x20,0x75,
-	0x73,0x65,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,
-	0x65,0x72,0x20,0x49,0x49,0x0B,0x3E,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x32,0x40,0x3E,0x41,0x6C,0x6C,0x20,0x22,
-	0x6E,0x6F,0x74,0x2D,0x74,0x6F,0x6F,0x2D,0x74,0x72,0x69,0x76,
-	0x69,0x61,0x6C,0x22,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,
-	0x6E,0x73,0x20,0x61,0x72,0x65,0x20,0x70,0x72,0x65,0x73,0x65,
-	0x6E,0x74,0x65,0x64,0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x28,
-	0x6F,0x72,0x64,0x65,0x72,0x65,0x64,0x20,0x69,0x6E,0x22,0x77,
-	0x69,0x6E,0x64,0x6F,0x77,0x73,0x29,0x20,0x77,0x69,0x74,0x68,
-	0x20,0x61,0x20,0x73,0x68,0x6F,0x72,0x74,0x20,0x64,0x65,0x73,
-	0x63,0x72,0x69,0x70,0x74,0x69,0x6F,0x6E,0x2E,0x00,0x17,0x3E,
-	0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x61,
-	0x69,0x6E,0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3A,0x01,0x3E,
-	0x22,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x42,0x50,0x4D,0x20,0x28,0x42,0x65,0x61,0x74,0x73,0x20,0x70,
-	0x65,0x72,0x20,0x6D,0x69,0x6E,0x75,0x74,0x65,0x29,0x3A,0x0B,
-	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x40,
-	0x54,0x68,0x65,0x20,0x42,0x50,0x4D,0x20,0x73,0x65,0x74,0x74,
-	0x69,0x6E,0x67,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,
-	0x68,0x6F,0x77,0x20,0x66,0x61,0x73,0x74,0x20,0x28,0x74,0x69,
-	0x63,0x6B,0x73,0x2F,0x73,0x65,0x63,0x6F,0x6E,0x64,0x29,0x20,
-	0x74,0x68,0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20,0x70,0x6C,
-	0x61,0x79,0x65,0x72,0x1C,0x77,0x69,0x6C,0x6C,0x20,0x72,0x75,
-	0x6E,0x2E,0x20,0x31,0x32,0x35,0x20,0x42,0x50,0x4D,0x20,0x3C,
-	0x2D,0x3E,0x20,0x35,0x30,0x20,0x48,0x7A,0x2E,0x28,0x3E,0x4E,
-	0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61,
-	0x79,0x65,0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x73,0x65,
-	0x63,0x6F,0x6E,0x64,0x20,0x3D,0x20,0x42,0x50,0x4D,0x2A,0x32,
-	0x2F,0x35,0x00,0x16,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
-	0x30,0x30,0x31,0x53,0x70,0x64,0x2C,0x20,0x53,0x70,0x65,0x65,
-	0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x2C,0x53,0x70,0x65,0x65,0x64,0x20,0x3D,0x20,0x6E,
-	0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61,
-	0x79,0x65,0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x70,0x61,
-	0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x00,
-	0x0F,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x41,0x64,0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
-	0x43,0x30,0x30,0x32,0x3E,0x22,0x41,0x64,0x64,0x22,0x20,0x69,
-	0x73,0x20,0x74,0x68,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,
-	0x20,0x6F,0x66,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,
-	0x6C,0x69,0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,
-	0x72,0x73,0x6F,0x72,0x20,0x6A,0x75,0x6D,0x70,0x73,0x20,0x77,
-	0x68,0x65,0x6E,0x20,0x79,0x6F,0x75,0x0C,0x65,0x64,0x69,0x74,
-	0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,0x2E,0x00,0x0F,0x3E,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x74,0x6E,
-	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
-	0x32,0x1B,0x54,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,
-	0x74,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75,
-	0x6D,0x62,0x65,0x72,0x2E,0x00,0x0E,0x3E,0x40,0x58,0x30,0x34,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x4C,0x6E,0x3A,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x43,0x54,0x68,
-	0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,
-	0x6C,0x69,0x6E,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,
-	0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x70,0x61,
-	0x74,0x74,0x65,0x72,0x6E,0x2E,0x20,0x55,0x70,0x20,0x74,0x6F,
-	0x20,0x24,0x31,0x30,0x30,0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E,
-	0x20,0x4E,0x6F,0x74,0x65,0x40,0x74,0x68,0x61,0x74,0x20,0x46,
-	0x54,0x32,0x20,0x77,0x6F,0x6E,0x27,0x74,0x20,0x77,0x61,0x72,
-	0x6E,0x20,0x79,0x6F,0x75,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,
-	0x20,0x64,0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x74,0x68,
-	0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x2E,0x20,0x54,0x68,
-	0x65,0x20,0x6E,0x6F,0x74,0x65,0x73,0x20,0x61,0x74,0x37,0x74,
-	0x68,0x65,0x20,0x62,0x6F,0x74,0x74,0x6F,0x6D,0x20,0x6C,0x69,
-	0x6E,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x74,
-	0x68,0x72,0x6F,0x77,0x6E,0x20,0x6F,0x75,0x74,0x20,0x74,0x6F,
-	0x20,0x74,0x68,0x65,0x20,0x62,0x69,0x6E,0x61,0x72,0x79,0x20,
-	0x73,0x70,0x61,0x63,0x65,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x70,0x64,0x3A,
+	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x1B,
+	0x40,0x4C,0x48,0x6F,0x77,0x20,0x74,0x6F,0x20,0x75,0x73,0x65,
+	0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,
+	0x20,0x49,0x49,0x0B,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x40,0x3E,0x41,0x6C,0x6C,0x20,0x22,0x6E,0x6F,
+	0x74,0x2D,0x74,0x6F,0x6F,0x2D,0x74,0x72,0x69,0x76,0x69,0x61,
+	0x6C,0x22,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E,0x73,
+	0x20,0x61,0x72,0x65,0x20,0x70,0x72,0x65,0x73,0x65,0x6E,0x74,
+	0x65,0x64,0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x28,0x6F,0x72,
+	0x64,0x65,0x72,0x65,0x64,0x20,0x69,0x6E,0x22,0x77,0x69,0x6E,
+	0x64,0x6F,0x77,0x73,0x29,0x20,0x77,0x69,0x74,0x68,0x20,0x61,
+	0x20,0x73,0x68,0x6F,0x72,0x74,0x20,0x64,0x65,0x73,0x63,0x72,
+	0x69,0x70,0x74,0x69,0x6F,0x6E,0x2E,0x00,0x17,0x3E,0x40,0x58,
+	0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x61,0x69,0x6E,
+	0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3A,0x01,0x3E,0x22,0x3E,
+	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x42,0x50,
+	0x4D,0x20,0x28,0x42,0x65,0x61,0x74,0x73,0x20,0x70,0x65,0x72,
+	0x20,0x6D,0x69,0x6E,0x75,0x74,0x65,0x29,0x3A,0x0B,0x3E,0x40,
+	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x54,0x68,
+	0x65,0x20,0x42,0x50,0x4D,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,
+	0x67,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x68,0x6F,
+	0x77,0x20,0x66,0x61,0x73,0x74,0x20,0x28,0x74,0x69,0x63,0x6B,
+	0x73,0x2F,0x73,0x65,0x63,0x6F,0x6E,0x64,0x29,0x20,0x74,0x68,
+	0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20,0x70,0x6C,0x61,0x79,
+	0x65,0x72,0x1C,0x77,0x69,0x6C,0x6C,0x20,0x72,0x75,0x6E,0x2E,
+	0x20,0x31,0x32,0x35,0x20,0x42,0x50,0x4D,0x20,0x3C,0x2D,0x3E,
+	0x20,0x35,0x30,0x20,0x48,0x7A,0x2E,0x28,0x3E,0x4E,0x75,0x6D,
+	0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61,0x79,0x65,
+	0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x73,0x65,0x63,0x6F,
+	0x6E,0x64,0x20,0x3D,0x20,0x42,0x50,0x4D,0x2A,0x32,0x2F,0x35,
+	0x00,0x16,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
+	0x31,0x53,0x70,0x64,0x2C,0x20,0x53,0x70,0x65,0x65,0x64,0x3A,
 	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x44,0x45,0x78,0x70,0x61,0x6E,0x64,0x20,0x70,0x61,0x74,0x74,
-	0x65,0x72,0x6E,0x2E,0x20,0x49,0x6E,0x73,0x65,0x72,0x74,0x73,
-	0x20,0x61,0x20,0x62,0x6C,0x61,0x6E,0x6B,0x20,0x6C,0x69,0x6E,
-	0x65,0x20,0x61,0x66,0x74,0x65,0x72,0x20,0x65,0x61,0x63,0x68,
-	0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E,
-	0x65,0x2E,0x20,0x55,0x73,0x65,0x66,0x75,0x6C,0x3C,0x69,0x66,
-	0x20,0x79,0x6F,0x75,0x20,0x77,0x61,0x6E,0x74,0x20,0x74,0x6F,
-	0x20,0x63,0x6F,0x6E,0x76,0x65,0x72,0x74,0x20,0x61,0x20,0x70,
-	0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20,
-	0x72,0x75,0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65,
-	0x64,0x20,0x32,0x2A,0x78,0x20,0x74,0x6F,0x20,0x61,0x1D,0x70,
-	0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20,
-	0x72,0x75,0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65,
-	0x64,0x20,0x78,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x53,0x68,0x6E,0x6B,0x3A,0x0B,0x3E,
-	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x2E,0x53,
-	0x68,0x72,0x69,0x6E,0x6B,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
-	0x6E,0x2E,0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x73,0x20,0x61,
-	0x6C,0x6C,0x20,0x6F,0x64,0x64,0x20,0x70,0x61,0x74,0x74,0x65,
-	0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E,0x00,0x2A,0x3E,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,
-	0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,
-	0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x73,0x65,0x6C,0x65,
-	0x63,0x74,0x6F,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
-	0x40,0x43,0x30,0x30,0x32,0x3A,0x54,0x68,0x65,0x20,0x69,0x6E,
-	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61,
-	0x74,0x20,0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B,
-	0x20,0x6F,0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x61,0x6D,
-	0x65,0x20,0x73,0x74,0x72,0x69,0x6E,0x67,0x2C,0x20,0x69,0x73,
-	0x20,0x74,0x68,0x65,0x17,0x64,0x65,0x73,0x74,0x69,0x6E,0x61,
-	0x74,0x69,0x6F,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
-	0x65,0x6E,0x74,0x2E,0x3D,0x3E,0x54,0x68,0x65,0x20,0x69,0x6E,
-	0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61,
-	0x74,0x20,0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B,
-	0x20,0x6F,0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x75,0x6D,
-	0x62,0x65,0x72,0x2C,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,
-	0x73,0x6F,0x75,0x72,0x63,0x65,0x0B,0x69,0x6E,0x73,0x74,0x72,
-	0x75,0x6D,0x65,0x6E,0x74,0x2E,0x1F,0x3E,0x54,0x68,0x65,0x20,
-	0x73,0x61,0x6D,0x65,0x20,0x67,0x6F,0x65,0x73,0x20,0x66,0x6F,
-	0x72,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,
-	0x73,0x2E,0x42,0x3E,0x59,0x6F,0x75,0x20,0x63,0x68,0x61,0x6E,
-	0x67,0x65,0x20,0x74,0x68,0x65,0x20,0x6E,0x61,0x6D,0x65,0x20,
-	0x6F,0x6E,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
-	0x6D,0x65,0x6E,0x74,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,
-	0x62,0x79,0x20,0x63,0x6C,0x69,0x63,0x6B,0x69,0x6E,0x67,0x20,
-	0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x07,0x62,0x75,
-	0x74,0x74,0x6F,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,0x32,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65,0x73,
-	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
-	0x32,0x22,0x3E,0x4C,0x65,0x66,0x74,0x20,0x62,0x75,0x74,0x74,
-	0x6F,0x6E,0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61,
-	0x6E,0x6E,0x65,0x6C,0x20,0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x2E,
-	0x35,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74,
-	0x6F,0x6E,0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61,
-	0x6E,0x6E,0x65,0x6C,0x20,0x6D,0x75,0x6C,0x74,0x69,0x2D,0x72,
-	0x65,0x63,0x6F,0x72,0x64,0x2F,0x65,0x64,0x69,0x74,0x20,0x6F,
-	0x6E,0x2F,0x6F,0x66,0x66,0x2E,0x42,0x3E,0x4C,0x65,0x66,0x74,
-	0x2B,0x72,0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74,0x6F,
-	0x6E,0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x61,0x6C,0x6C,0x20,
-	0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x73,0x20,0x6F,0x66,0x66,
-	0x20,0x65,0x78,0x63,0x65,0x70,0x74,0x20,0x74,0x68,0x65,0x20,
-	0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x6F,0x6E,0x65,
-	0x2E,0x00,0x1C,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,
-	0x31,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,
-	0x45,0x64,0x69,0x74,0x6F,0x72,0x3A,0x01,0x3E,0x22,0x3E,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x57,0x68,0x61,
-	0x74,0x20,0x69,0x73,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,
-	0x72,0x75,0x6D,0x65,0x6E,0x74,0x3F,0x3A,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x41,0x20,0x46,
+	0x2C,0x53,0x70,0x65,0x65,0x64,0x20,0x3D,0x20,0x6E,0x75,0x6D,
+	0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61,0x79,0x65,
+	0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x70,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x00,0x0F,0x3E,
+	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x64,
+	0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
+	0x30,0x32,0x3E,0x22,0x41,0x64,0x64,0x22,0x20,0x69,0x73,0x20,
+	0x74,0x68,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,
+	0x66,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69,
+	0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x73,
+	0x6F,0x72,0x20,0x6A,0x75,0x6D,0x70,0x73,0x20,0x77,0x68,0x65,
+	0x6E,0x20,0x79,0x6F,0x75,0x0C,0x65,0x64,0x69,0x74,0x20,0x61,
+	0x20,0x6E,0x6F,0x74,0x65,0x2E,0x00,0x0F,0x3E,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x74,0x6E,0x3A,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1B,
+	0x54,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,
+	0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75,0x6D,0x62,
+	0x65,0x72,0x2E,0x00,0x0E,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x4C,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x43,0x54,0x68,0x65,0x20,
+	0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x6C,0x69,
+	0x6E,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,
+	0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x70,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x2E,0x20,0x55,0x70,0x20,0x74,0x6F,0x20,0x24,
+	0x31,0x30,0x30,0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E,0x20,0x4E,
+	0x6F,0x74,0x65,0x40,0x74,0x68,0x61,0x74,0x20,0x46,0x54,0x32,
+	0x20,0x77,0x6F,0x6E,0x27,0x74,0x20,0x77,0x61,0x72,0x6E,0x20,
+	0x79,0x6F,0x75,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x64,
+	0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x74,0x68,0x69,0x73,
+	0x20,0x76,0x61,0x6C,0x75,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,
+	0x6E,0x6F,0x74,0x65,0x73,0x20,0x61,0x74,0x37,0x74,0x68,0x65,
+	0x20,0x62,0x6F,0x74,0x74,0x6F,0x6D,0x20,0x6C,0x69,0x6E,0x65,
+	0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x74,0x68,0x72,
+	0x6F,0x77,0x6E,0x20,0x6F,0x75,0x74,0x20,0x74,0x6F,0x20,0x74,
+	0x68,0x65,0x20,0x62,0x69,0x6E,0x61,0x72,0x79,0x20,0x73,0x70,
+	0x61,0x63,0x65,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x70,0x64,0x3A,0x0B,0x3E,
+	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x44,0x45,
+	0x78,0x70,0x61,0x6E,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
+	0x6E,0x2E,0x20,0x49,0x6E,0x73,0x65,0x72,0x74,0x73,0x20,0x61,
+	0x20,0x62,0x6C,0x61,0x6E,0x6B,0x20,0x6C,0x69,0x6E,0x65,0x20,
+	0x61,0x66,0x74,0x65,0x72,0x20,0x65,0x61,0x63,0x68,0x20,0x70,
+	0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x2E,
+	0x20,0x55,0x73,0x65,0x66,0x75,0x6C,0x3C,0x69,0x66,0x20,0x79,
+	0x6F,0x75,0x20,0x77,0x61,0x6E,0x74,0x20,0x74,0x6F,0x20,0x63,
+	0x6F,0x6E,0x76,0x65,0x72,0x74,0x20,0x61,0x20,0x70,0x61,0x74,
+	0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20,0x72,0x75,
+	0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x20,
+	0x32,0x2A,0x78,0x20,0x74,0x6F,0x20,0x61,0x1D,0x70,0x61,0x74,
+	0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20,0x72,0x75,
+	0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x20,
+	0x78,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x53,0x68,0x6E,0x6B,0x3A,0x0B,0x3E,0x40,0x58,
+	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x2E,0x53,0x68,0x72,
+	0x69,0x6E,0x6B,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,
+	0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x73,0x20,0x61,0x6C,0x6C,
+	0x20,0x6F,0x64,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,
+	0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E,0x00,0x2A,0x3E,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20,
+	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2F,0x73,
+	0x61,0x6D,0x70,0x6C,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,
+	0x6F,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x3A,0x54,0x68,0x65,0x20,0x69,0x6E,0x73,0x74,
+	0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61,0x74,0x20,
+	0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B,0x20,0x6F,
+	0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x61,0x6D,0x65,0x20,
+	0x73,0x74,0x72,0x69,0x6E,0x67,0x2C,0x20,0x69,0x73,0x20,0x74,
+	0x68,0x65,0x17,0x64,0x65,0x73,0x74,0x69,0x6E,0x61,0x74,0x69,
+	0x6F,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,
+	0x74,0x2E,0x3D,0x3E,0x54,0x68,0x65,0x20,0x69,0x6E,0x73,0x74,
+	0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61,0x74,0x20,
+	0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B,0x20,0x6F,
+	0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x75,0x6D,0x62,0x65,
+	0x72,0x2C,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,
+	0x75,0x72,0x63,0x65,0x0B,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
+	0x65,0x6E,0x74,0x2E,0x1F,0x3E,0x54,0x68,0x65,0x20,0x73,0x61,
+	0x6D,0x65,0x20,0x67,0x6F,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,
+	0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x2E,
+	0x42,0x3E,0x59,0x6F,0x75,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,
+	0x20,0x74,0x68,0x65,0x20,0x6E,0x61,0x6D,0x65,0x20,0x6F,0x6E,
+	0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
+	0x6E,0x74,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x62,0x79,
+	0x20,0x63,0x6C,0x69,0x63,0x6B,0x69,0x6E,0x67,0x20,0x74,0x68,
+	0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x07,0x62,0x75,0x74,0x74,
+	0x6F,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,0x32,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65,0x73,0x3A,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x22,
+	0x3E,0x4C,0x65,0x66,0x74,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,
+	0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61,0x6E,0x6E,
+	0x65,0x6C,0x20,0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x2E,0x35,0x3E,
+	0x52,0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,
+	0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61,0x6E,0x6E,
+	0x65,0x6C,0x20,0x6D,0x75,0x6C,0x74,0x69,0x2D,0x72,0x65,0x63,
+	0x6F,0x72,0x64,0x2F,0x65,0x64,0x69,0x74,0x20,0x6F,0x6E,0x2F,
+	0x6F,0x66,0x66,0x2E,0x42,0x3E,0x4C,0x65,0x66,0x74,0x2B,0x72,
+	0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x3A,
+	0x20,0x54,0x75,0x72,0x6E,0x20,0x61,0x6C,0x6C,0x20,0x63,0x68,
+	0x61,0x6E,0x6E,0x65,0x6C,0x73,0x20,0x6F,0x66,0x66,0x20,0x65,
+	0x78,0x63,0x65,0x70,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x65,
+	0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x6F,0x6E,0x65,0x2E,0x00,
+	0x1C,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x49,
+	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x45,0x64,
+	0x69,0x74,0x6F,0x72,0x3A,0x01,0x3E,0x22,0x3E,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x57,0x68,0x61,0x74,0x20,
+	0x69,0x73,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
+	0x6D,0x65,0x6E,0x74,0x3F,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x41,0x20,0x46,0x61,0x73,
+	0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,0x20,0x69,
+	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x73,
+	0x3A,0x15,0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F,0x6C,0x75,
+	0x6D,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x16,
+	0x3E,0x20,0x20,0x20,0x31,0x20,0x50,0x61,0x6E,0x6E,0x69,0x6E,
+	0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x1D,0x3E,
+	0x20,0x20,0x20,0x31,0x20,0x41,0x75,0x74,0x6F,0x2D,0x76,0x69,
+	0x62,0x72,0x61,0x74,0x6F,0x20,0x64,0x65,0x66,0x69,0x6E,0x69,
+	0x74,0x69,0x6F,0x6E,0x13,0x3E,0x20,0x20,0x20,0x31,0x2E,0x2E,
+	0x31,0x36,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x28,0x73,0x29,
+	0x1F,0x3E,0x20,0x20,0x20,0x31,0x20,0x4B,0x65,0x79,0x62,0x6F,
+	0x61,0x72,0x64,0x20,0x73,0x70,0x6C,0x69,0x74,0x20,0x64,0x65,
+	0x66,0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x15,0x3E,0x20,0x20,
+	0x20,0x31,0x20,0x4D,0x49,0x44,0x49,0x20,0x64,0x65,0x66,0x69,
+	0x6E,0x69,0x74,0x69,0x6F,0x6E,0x00,0x1B,0x3E,0x41,0x20,0x46,
 	0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,
-	0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,
-	0x69,0x73,0x3A,0x15,0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F,
-	0x6C,0x75,0x6D,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,
-	0x65,0x16,0x3E,0x20,0x20,0x20,0x31,0x20,0x50,0x61,0x6E,0x6E,
-	0x69,0x6E,0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,
-	0x1D,0x3E,0x20,0x20,0x20,0x31,0x20,0x41,0x75,0x74,0x6F,0x2D,
-	0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x64,0x65,0x66,0x69,
-	0x6E,0x69,0x74,0x69,0x6F,0x6E,0x13,0x3E,0x20,0x20,0x20,0x31,
-	0x2E,0x2E,0x31,0x36,0x20,0x53,0x61,0x6D,0x70,0x6C,0x65,0x28,
-	0x73,0x29,0x1F,0x3E,0x20,0x20,0x20,0x31,0x20,0x4B,0x65,0x79,
-	0x62,0x6F,0x61,0x72,0x64,0x20,0x73,0x70,0x6C,0x69,0x74,0x20,
-	0x64,0x65,0x66,0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x15,0x3E,
-	0x20,0x20,0x20,0x31,0x20,0x4D,0x49,0x44,0x49,0x20,0x64,0x65,
-	0x66,0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x00,0x1B,0x3E,0x41,
-	0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,
-	0x20,0x32,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x69,0x73,
-	0x3A,0x29,0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F,0x6C,0x75,
-	0x6D,0x65,0x2F,0x50,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2F,0x46,
-	0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x20,0x64,0x65,0x66,
-	0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x14,0x3E,0x20,0x20,0x20,
-	0x31,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x74,
-	0x6F,0x6E,0x65,0x2E,0x10,0x3E,0x20,0x20,0x20,0x31,0x20,0x57,
-	0x61,0x76,0x65,0x20,0x66,0x6F,0x72,0x6D,0x2E,0x00,0x1F,0x3E,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,
-	0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x65,0x6E,0x76,
-	0x65,0x6C,0x6F,0x70,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
-	0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x3E,0x41,0x6E,0x20,0x69,
-	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x27,0x73,0x20,
-	0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x73,0x20,0x64,0x65,
-	0x66,0x69,0x6E,0x65,0x64,0x20,0x62,0x79,0x20,0x69,0x74,0x73,
-	0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x63,0x75,
-	0x72,0x76,0x65,0x2E,0x20,0x49,0x66,0x20,0x74,0x68,0x65,0x3E,
-	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x68,
-	0x61,0x73,0x20,0x61,0x20,0x73,0x75,0x73,0x74,0x61,0x69,0x6E,
-	0x20,0x70,0x6F,0x69,0x6E,0x74,0x2C,0x20,0x74,0x68,0x65,0x20,
-	0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x77,0x69,0x6C,
-	0x6C,0x20,0x73,0x74,0x6F,0x70,0x20,0x61,0x74,0x20,0x74,0x68,
-	0x61,0x74,0x42,0x70,0x6F,0x69,0x6E,0x74,0x20,0x75,0x6E,0x74,
-	0x69,0x6C,0x20,0x61,0x20,0x6B,0x65,0x79,0x2D,0x6F,0x66,0x66,
-	0x20,0x6E,0x6F,0x74,0x65,0x20,0x68,0x61,0x73,0x20,0x62,0x65,
-	0x65,0x6E,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x2E,0x20,0x57,
-	0x68,0x65,0x6E,0x20,0x61,0x20,0x6B,0x65,0x79,0x2D,0x6F,0x66,
-	0x66,0x20,0x6E,0x6F,0x74,0x65,0x20,0x69,0x73,0x1D,0x70,0x6C,
-	0x61,0x79,0x65,0x64,0x2C,0x20,0x74,0x68,0x65,0x20,0x22,0x66,
-	0x61,0x64,0x65,0x6F,0x75,0x74,0x22,0x20,0x62,0x65,0x67,0x69,
-	0x6E,0x73,0x2E,0x44,0x3E,0x4F,0x6E,0x65,0x20,0x70,0x69,0x78,
-	0x65,0x6C,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x65,0x6E,
-	0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,
-	0x77,0x20,0x63,0x6F,0x72,0x72,0x65,0x73,0x70,0x6F,0x6E,0x64,
-	0x73,0x20,0x74,0x6F,0x20,0x6F,0x6E,0x65,0x20,0x70,0x6C,0x61,
-	0x79,0x65,0x72,0x2D,0x74,0x69,0x63,0x6B,0x2E,0x20,0x49,0x66,
-	0x3C,0x74,0x68,0x65,0x20,0x42,0x50,0x4D,0x20,0x69,0x73,0x20,
-	0x31,0x32,0x35,0x2C,0x20,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x20,
-	0x63,0x6F,0x6E,0x73,0x75,0x6D,0x65,0x20,0x35,0x30,0x20,0x70,
-	0x69,0x78,0x65,0x6C,0x2F,0x73,0x65,0x63,0x6F,0x6E,0x64,0x2E,
-	0x20,0x54,0x68,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x27,
-	0x73,0x1A,0x22,0x73,0x69,0x7A,0x65,0x22,0x20,0x69,0x73,0x20,
-	0x61,0x62,0x6F,0x75,0x74,0x20,0x36,0x20,0x73,0x65,0x63,0x6F,
-	0x6E,0x64,0x73,0x2E,0x3E,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75,
-	0x20,0x70,0x72,0x65,0x73,0x73,0x20,0x74,0x68,0x65,0x20,0x72,
-	0x69,0x67,0x68,0x74,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x62,
-	0x75,0x74,0x74,0x6F,0x6E,0x20,0x61,0x74,0x20,0x74,0x68,0x65,
-	0x20,0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20,0x62,
-	0x75,0x74,0x74,0x6F,0x6E,0x73,0x2C,0x3F,0x79,0x6F,0x75,0x27,
-	0x6C,0x6C,0x20,0x73,0x74,0x6F,0x72,0x65,0x20,0x74,0x68,0x65,
-	0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x65,0x6E,0x76,
-	0x65,0x6C,0x6F,0x70,0x65,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74,
-	0x68,0x61,0x74,0x20,0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,
-	0x65,0x20,0x63,0x65,0x6C,0x6C,0x2E,0x20,0x54,0x68,0x65,0x30,
-	0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x61,
-	0x72,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64,0x20,0x69,0x6E,
-	0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,
-	0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,0x69,0x6C,0x65,0x2E,
-	0x43,0x3E,0x50,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20,
-	0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x31,0x20,0x69,0x73,0x20,
-	0x74,0x68,0x65,0x20,0x64,0x65,0x66,0x61,0x75,0x6C,0x74,0x20,
-	0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x2E,0x20,0x54,0x68,
-	0x69,0x73,0x20,0x6D,0x65,0x61,0x6E,0x73,0x20,0x74,0x68,0x61,
-	0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x42,0x6C,0x6F,0x61,
-	0x64,0x20,0x61,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x2C,0x20,
-	0x69,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x67,0x65,0x74,0x20,
-	0x61,0x6C,0x6C,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,
-	0x20,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E,
-	0x20,0x66,0x72,0x6F,0x6D,0x20,0x70,0x72,0x65,0x64,0x65,0x66,
-	0x69,0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x31,
-	0x2C,0x20,0x69,0x6E,0x63,0x6C,0x75,0x64,0x69,0x6E,0x67,0x20,
-	0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x2E,
-	0x42,0x3E,0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,
-	0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x74,0x75,0x72,0x6E,0x20,
-	0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2D,0x65,
-	0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x6F,0x66,0x66,0x2C,
-	0x20,0x79,0x6F,0x75,0x20,0x64,0x6F,0x6E,0x27,0x74,0x20,0x74,
-	0x75,0x72,0x6E,0x20,0x74,0x68,0x65,0x0C,0x76,0x69,0x62,0x72,
-	0x61,0x74,0x6F,0x20,0x6F,0x66,0x66,0x2E,0x00,0x20,0x3E,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65,
-	0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x65,0x6E,0x76,
-	0x65,0x6C,0x6F,0x70,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
-	0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x3E,0x53,0x61,0x6D,0x65,
-	0x20,0x61,0x73,0x20,0x61,0x62,0x6F,0x76,0x65,0x2C,0x20,0x65,
-	0x78,0x63,0x65,0x70,0x74,0x20,0x66,0x72,0x6F,0x6D,0x20,0x74,
-	0x68,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72,
-	0x61,0x74,0x6F,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x63,
-	0x6F,0x6E,0x6E,0x65,0x63,0x74,0x65,0x64,0x20,0x74,0x6F,0x15,
-	0x74,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,
-	0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x2E,0x00,0x10,0x3E,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x75,
-	0x6E,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
-	0x30,0x30,0x32,0x3F,0x3E,0x54,0x68,0x65,0x20,0x66,0x69,0x6E,
-	0x65,0x2D,0x74,0x75,0x6E,0x65,0x20,0x72,0x65,0x73,0x6F,0x6C,
-	0x75,0x74,0x69,0x6F,0x6E,0x20,0x68,0x61,0x73,0x20,0x62,0x65,
-	0x65,0x6E,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x66,
-	0x72,0x6F,0x6D,0x20,0x61,0x20,0x73,0x69,0x67,0x6E,0x65,0x64,
-	0x20,0x6E,0x69,0x62,0x62,0x6C,0x65,0x27,0x28,0x2D,0x38,0x2E,
-	0x2E,0x2B,0x37,0x29,0x20,0x74,0x6F,0x20,0x61,0x20,0x73,0x69,
-	0x67,0x6E,0x65,0x64,0x20,0x62,0x79,0x74,0x65,0x20,0x28,0x2D,
-	0x31,0x32,0x38,0x2E,0x2E,0x2B,0x31,0x32,0x37,0x29,0x2E,0x00,
-	0x13,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x46,0x61,0x64,0x65,0x6F,0x75,0x74,0x3A,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1B,0x3E,0x54,0x68,
-	0x69,0x73,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x66,0x61,
-	0x64,0x65,0x6F,0x75,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,
-	0x00,0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
-	0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x73,0x77,0x65,
-	0x65,0x70,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
-	0x30,0x30,0x32,0x3E,0x3E,0x54,0x68,0x69,0x73,0x20,0x69,0x73,
-	0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x28,0x69,
-	0x6E,0x20,0x70,0x6C,0x61,0x79,0x65,0x72,0x20,0x74,0x69,0x63,
-	0x6B,0x73,0x29,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x69,0x6C,
-	0x6C,0x20,0x62,0x79,0x70,0x61,0x73,0x73,0x20,0x75,0x6E,0x74,
-	0x69,0x6C,0x20,0x74,0x68,0x65,0x2D,0x61,0x75,0x74,0x6F,0x2D,
-	0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x72,0x65,0x61,0x63,0x68,0x20,0x69,0x74,0x27,0x73,0x20,
-	0x66,0x69,0x6E,0x61,0x6C,0x20,0x61,0x6D,0x70,0x6C,0x69,0x74,
-	0x75,0x64,0x65,0x2E,0x00,0x1E,0x3E,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20,0x70,0x69,0x61,
-	0x6E,0x6F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x3A,
+	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x69,0x73,0x3A,0x28,
+	0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,
+	0x2F,0x50,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2F,0x46,0x69,0x6E,
+	0x65,0x74,0x75,0x6E,0x65,0x20,0x64,0x65,0x66,0x69,0x6E,0x69,
+	0x74,0x69,0x6F,0x6E,0x13,0x3E,0x20,0x20,0x20,0x31,0x20,0x52,
+	0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x6E,0x6F,0x74,0x65,
+	0x0E,0x3E,0x20,0x20,0x20,0x31,0x20,0x57,0x61,0x76,0x65,0x66,
+	0x6F,0x72,0x6D,0x00,0x1F,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,
+	0x6D,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x3A,
 	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x3F,0x3E,0x54,0x68,0x65,0x20,0x70,0x69,0x61,0x6E,0x6F,0x20,
-	0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x20,0x64,0x65,0x66,
-	0x69,0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,
-	0x20,0x73,0x70,0x6C,0x69,0x74,0x20,0x66,0x6F,0x72,0x20,0x61,
-	0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,
-	0x2E,0x20,0x54,0x6F,0x3F,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,
-	0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x20,0x73,0x70,0x6C,0x69,
-	0x74,0x2C,0x20,0x63,0x68,0x6F,0x6F,0x73,0x65,0x20,0x61,0x20,
-	0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x74,0x68,0x69,
-	0x6E,0x20,0x74,0x68,0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
-	0x6D,0x65,0x6E,0x74,0x20,0x61,0x6E,0x64,0x1C,0x74,0x68,0x65,
-	0x6E,0x20,0x22,0x64,0x72,0x61,0x77,0x22,0x20,0x6F,0x6E,0x20,
-	0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,
-	0x2E,0x42,0x3E,0x54,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x73,
-	0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x77,0x69,0x74,0x68,
-	0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,
-	0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,
-	0x61,0x72,0x65,0x20,0x69,0x6E,0x64,0x69,0x63,0x61,0x74,0x65,
-	0x64,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x09,0x6B,0x65,0x79,
-	0x62,0x6F,0x61,0x72,0x64,0x2E,0x00,0x1A,0x3E,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6D,0x70,0x6F,0x72,
-	0x74,0x61,0x6E,0x74,0x20,0x6E,0x6F,0x74,0x65,0x3A,0x0B,0x3E,
-	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x3E,
-	0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2C,0x20,
-	0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2C,0x20,0x74,0x75,0x6E,
-	0x65,0x20,0x61,0x6E,0x64,0x20,0x72,0x65,0x6C,0x61,0x74,0x69,
-	0x76,0x65,0x20,0x74,0x6F,0x6E,0x65,0x20,0x69,0x73,0x20,0x64,
-	0x65,0x66,0x69,0x6E,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x45,
-	0x41,0x43,0x48,0x41,0x53,0x41,0x4D,0x50,0x4C,0x45,0x20,0x69,
-	0x6E,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
-	0x65,0x6E,0x74,0x2E,0x20,0x41,0x6C,0x6C,0x20,0x6F,0x74,0x68,
-	0x65,0x72,0x20,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,
-	0x6F,0x6E,0x20,0x69,0x73,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,
-	0x64,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x12,0x65,0x6E,
-	0x74,0x69,0x72,0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
-	0x65,0x6E,0x74,0x2E,0x00,0x31,0x40,0x58,0x30,0x32,0x30,0x40,
-	0x43,0x30,0x30,0x31,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
-	0x6E,0x74,0x20,0x45,0x64,0x69,0x74,0x6F,0x72,0x20,0x45,0x78,
-	0x74,0x65,0x6E,0x73,0x69,0x6F,0x6E,0x3A,0x20,0x28,0x49,0x2E,
-	0x45,0x2E,0x45,0x78,0x74,0x2E,0x29,0x01,0x3E,0x10,0x3E,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x49,0x44,
-	0x49,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x28,0x3E,0x27,0x70,0x2E,0x27,0x20,0x73,0x74,0x61,
-	0x6E,0x64,0x73,0x20,0x66,0x6F,0x72,0x20,0x22,0x70,0x72,0x6F,
-	0x67,0x72,0x61,0x6D,0x22,0x20,0x28,0x69,0x6E,0x73,0x74,0x72,
-	0x75,0x6D,0x65,0x6E,0x74,0x29,0x2E,0x40,0x3E,0x53,0x65,0x76,
-	0x65,0x72,0x61,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
-	0x65,0x6E,0x74,0x73,0x20,0x63,0x61,0x6E,0x20,0x68,0x61,0x76,
-	0x65,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x74,
-	0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,0x63,0x68,0x61,0x6E,
-	0x6E,0x65,0x6C,0x20,0x62,0x75,0x74,0x20,0x77,0x69,0x74,0x68,
-	0x33,0x64,0x69,0x66,0x66,0x65,0x72,0x65,0x6E,0x74,0x20,0x70,
-	0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x2E,0x20,0x46,0x54,0x32,
-	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x73,0x20,0x74,0x68,0x65,
-	0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x20,0x6F,0x6E,
-	0x20,0x74,0x68,0x65,0x43,0x4D,0x49,0x44,0x49,0x2D,0x63,0x68,
-	0x61,0x6E,0x6E,0x65,0x6C,0x73,0x20,0x69,0x6E,0x73,0x74,0x61,
-	0x6E,0x74,0x6C,0x79,0x20,0x64,0x75,0x72,0x69,0x6E,0x67,0x20,
-	0x70,0x6C,0x61,0x79,0x20,0x69,0x66,0x20,0x64,0x69,0x66,0x66,
+	0x40,0x3E,0x41,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
+	0x65,0x6E,0x74,0x27,0x73,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,
+	0x20,0x69,0x73,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x64,0x20,
+	0x62,0x79,0x20,0x69,0x74,0x73,0x20,0x65,0x6E,0x76,0x65,0x6C,
+	0x6F,0x70,0x65,0x20,0x63,0x75,0x72,0x76,0x65,0x2E,0x20,0x49,
+	0x66,0x20,0x74,0x68,0x65,0x3E,0x69,0x6E,0x73,0x74,0x72,0x75,
+	0x6D,0x65,0x6E,0x74,0x20,0x68,0x61,0x73,0x20,0x61,0x20,0x73,
+	0x75,0x73,0x74,0x61,0x69,0x6E,0x20,0x70,0x6F,0x69,0x6E,0x74,
+	0x2C,0x20,0x74,0x68,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,
+	0x70,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x74,0x6F,0x70,
+	0x20,0x61,0x74,0x20,0x74,0x68,0x61,0x74,0x42,0x70,0x6F,0x69,
+	0x6E,0x74,0x20,0x75,0x6E,0x74,0x69,0x6C,0x20,0x61,0x20,0x6B,
+	0x65,0x79,0x2D,0x6F,0x66,0x66,0x20,0x6E,0x6F,0x74,0x65,0x20,
+	0x68,0x61,0x73,0x20,0x62,0x65,0x65,0x6E,0x20,0x70,0x6C,0x61,
+	0x79,0x65,0x64,0x2E,0x20,0x57,0x68,0x65,0x6E,0x20,0x61,0x20,
+	0x6B,0x65,0x79,0x2D,0x6F,0x66,0x66,0x20,0x6E,0x6F,0x74,0x65,
+	0x20,0x69,0x73,0x1D,0x70,0x6C,0x61,0x79,0x65,0x64,0x2C,0x20,
+	0x74,0x68,0x65,0x20,0x22,0x66,0x61,0x64,0x65,0x6F,0x75,0x74,
+	0x22,0x20,0x62,0x65,0x67,0x69,0x6E,0x73,0x2E,0x44,0x3E,0x4F,
+	0x6E,0x65,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x69,0x6E,0x20,
+	0x74,0x68,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,
+	0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x20,0x63,0x6F,0x72,0x72,
+	0x65,0x73,0x70,0x6F,0x6E,0x64,0x73,0x20,0x74,0x6F,0x20,0x6F,
+	0x6E,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x72,0x2D,0x74,0x69,
+	0x63,0x6B,0x2E,0x20,0x49,0x66,0x3C,0x74,0x68,0x65,0x20,0x42,
+	0x50,0x4D,0x20,0x69,0x73,0x20,0x31,0x32,0x35,0x2C,0x20,0x79,
+	0x6F,0x75,0x27,0x6C,0x6C,0x20,0x63,0x6F,0x6E,0x73,0x75,0x6D,
+	0x65,0x20,0x35,0x30,0x20,0x70,0x69,0x78,0x65,0x6C,0x2F,0x73,
+	0x65,0x63,0x6F,0x6E,0x64,0x2E,0x20,0x54,0x68,0x65,0x20,0x77,
+	0x69,0x6E,0x64,0x6F,0x77,0x27,0x73,0x1A,0x22,0x73,0x69,0x7A,
+	0x65,0x22,0x20,0x69,0x73,0x20,0x61,0x62,0x6F,0x75,0x74,0x20,
+	0x36,0x20,0x73,0x65,0x63,0x6F,0x6E,0x64,0x73,0x2E,0x3E,0x3E,
+	0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x70,0x72,0x65,0x73,0x73,
+	0x20,0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x20,0x6D,
+	0x6F,0x75,0x73,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x20,
+	0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x65,0x64,0x65,
+	0x66,0x69,0x6E,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x73,
+	0x2C,0x3F,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x20,0x73,0x74,0x6F,
+	0x72,0x65,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,
+	0x6E,0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,
+	0x69,0x6E,0x74,0x6F,0x20,0x74,0x68,0x61,0x74,0x20,0x70,0x72,
+	0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20,0x63,0x65,0x6C,0x6C,
+	0x2E,0x20,0x54,0x68,0x65,0x30,0x70,0x72,0x65,0x64,0x65,0x66,
+	0x69,0x6E,0x65,0x73,0x20,0x61,0x72,0x65,0x20,0x73,0x74,0x6F,
+	0x72,0x65,0x64,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x63,
+	0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,
+	0x20,0x66,0x69,0x6C,0x65,0x2E,0x43,0x3E,0x50,0x72,0x65,0x64,
+	0x65,0x66,0x69,0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,
+	0x20,0x31,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x64,0x65,
+	0x66,0x61,0x75,0x6C,0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,
+	0x70,0x65,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x6D,0x65,0x61,
+	0x6E,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x66,0x20,0x79,
+	0x6F,0x75,0x42,0x6C,0x6F,0x61,0x64,0x20,0x61,0x20,0x73,0x61,
+	0x6D,0x70,0x6C,0x65,0x2C,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,
+	0x6C,0x20,0x67,0x65,0x74,0x20,0x61,0x6C,0x6C,0x20,0x65,0x6E,
+	0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x69,0x6E,0x66,0x6F,0x72,
+	0x6D,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,0x72,0x6F,0x6D,0x20,
+	0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20,0x6E,0x75,
+	0x6D,0x62,0x65,0x72,0x20,0x31,0x2C,0x20,0x69,0x6E,0x63,0x6C,
+	0x75,0x64,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,0x20,0x76,0x69,
+	0x62,0x72,0x61,0x74,0x6F,0x2E,0x42,0x3E,0x4E,0x6F,0x74,0x65,
+	0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,
+	0x20,0x74,0x75,0x72,0x6E,0x20,0x74,0x68,0x65,0x20,0x76,0x6F,
+	0x6C,0x75,0x6D,0x65,0x2D,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,
+	0x65,0x20,0x6F,0x66,0x66,0x2C,0x20,0x79,0x6F,0x75,0x20,0x64,
+	0x6F,0x6E,0x27,0x74,0x20,0x74,0x75,0x72,0x6E,0x20,0x74,0x68,
+	0x65,0x0C,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x6F,0x66,
+	0x66,0x2E,0x00,0x20,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x54,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E,0x69,
+	0x6E,0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x3A,
+	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
+	0x40,0x3E,0x53,0x61,0x6D,0x65,0x20,0x61,0x73,0x20,0x61,0x62,
+	0x6F,0x76,0x65,0x2C,0x20,0x65,0x78,0x63,0x65,0x70,0x74,0x20,
+	0x66,0x72,0x6F,0x6D,0x20,0x74,0x68,0x61,0x74,0x20,0x74,0x68,
+	0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x69,0x73,
+	0x20,0x6E,0x6F,0x74,0x20,0x63,0x6F,0x6E,0x6E,0x65,0x63,0x74,
+	0x65,0x64,0x20,0x74,0x6F,0x15,0x74,0x68,0x65,0x20,0x70,0x61,
+	0x6E,0x6E,0x69,0x6E,0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,
+	0x70,0x65,0x2E,0x00,0x1B,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x54,0x75,0x6E,0x65,0x20,0x28,0x66,0x69,
+	0x6E,0x65,0x74,0x75,0x6E,0x65,0x29,0x3A,0x0B,0x3E,0x40,0x58,
+	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3E,0x3E,0x54,0x68,
+	0x65,0x20,0x66,0x69,0x6E,0x65,0x74,0x75,0x6E,0x65,0x20,0x72,
+	0x65,0x73,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x20,0x68,0x61,
+	0x73,0x20,0x62,0x65,0x65,0x6E,0x20,0x63,0x68,0x61,0x6E,0x67,
+	0x65,0x64,0x20,0x66,0x72,0x6F,0x6D,0x20,0x61,0x20,0x73,0x69,
+	0x67,0x6E,0x65,0x64,0x20,0x6E,0x69,0x62,0x62,0x6C,0x65,0x27,
+	0x28,0x2D,0x38,0x2E,0x2E,0x2B,0x37,0x29,0x20,0x74,0x6F,0x20,
+	0x61,0x20,0x73,0x69,0x67,0x6E,0x65,0x64,0x20,0x62,0x79,0x74,
+	0x65,0x20,0x28,0x2D,0x31,0x32,0x38,0x2E,0x2E,0x2B,0x31,0x32,
+	0x37,0x29,0x2E,0x46,0x3E,0x4E,0x4F,0x54,0x45,0x3A,0x20,0x54,
+	0x68,0x65,0x20,0x6C,0x61,0x73,0x74,0x20,0x33,0x20,0x62,0x69,
+	0x74,0x73,0x20,0x61,0x72,0x65,0x20,0x64,0x69,0x73,0x63,0x61,
+	0x72,0x64,0x65,0x64,0x20,0x64,0x75,0x72,0x69,0x6E,0x67,0x20,
+	0x70,0x6C,0x61,0x79,0x62,0x61,0x63,0x6B,0x2C,0x20,0x73,0x6F,
+	0x20,0x74,0x68,0x65,0x20,0x74,0x72,0x75,0x65,0x20,0x73,0x74,
+	0x65,0x70,0x17,0x73,0x69,0x7A,0x65,0x20,0x69,0x73,0x20,0x38,
+	0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x20,0x6F,0x66,0x20,
+	0x31,0x2E,0x00,0x13,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x46,0x61,0x64,0x65,0x6F,0x75,0x74,0x3A,0x0B,
+	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1B,
+	0x3E,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x74,0x68,0x65,
+	0x20,0x66,0x61,0x64,0x65,0x6F,0x75,0x74,0x20,0x73,0x70,0x65,
+	0x65,0x64,0x2E,0x00,0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,
+	0x73,0x77,0x65,0x65,0x70,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x3E,0x3E,0x54,0x68,0x69,0x73,
+	0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65,
+	0x20,0x28,0x69,0x6E,0x20,0x70,0x6C,0x61,0x79,0x65,0x72,0x20,
+	0x74,0x69,0x63,0x6B,0x73,0x29,0x20,0x74,0x68,0x61,0x74,0x20,
+	0x77,0x69,0x6C,0x6C,0x20,0x62,0x79,0x70,0x61,0x73,0x73,0x20,
+	0x75,0x6E,0x74,0x69,0x6C,0x20,0x74,0x68,0x65,0x2D,0x61,0x75,
+	0x74,0x6F,0x2D,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77,
+	0x69,0x6C,0x6C,0x20,0x72,0x65,0x61,0x63,0x68,0x20,0x69,0x74,
+	0x27,0x73,0x20,0x66,0x69,0x6E,0x61,0x6C,0x20,0x61,0x6D,0x70,
+	0x6C,0x69,0x74,0x75,0x64,0x65,0x2E,0x00,0x1E,0x3E,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20,
+	0x70,0x69,0x61,0x6E,0x6F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,
+	0x72,0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x3F,0x3E,0x54,0x68,0x65,0x20,0x70,0x69,0x61,
+	0x6E,0x6F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x20,
+	0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20,
+	0x6B,0x65,0x79,0x20,0x73,0x70,0x6C,0x69,0x74,0x20,0x66,0x6F,
+	0x72,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,
+	0x65,0x6E,0x74,0x2E,0x20,0x54,0x6F,0x3F,0x63,0x68,0x61,0x6E,
+	0x67,0x65,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x20,0x73,
+	0x70,0x6C,0x69,0x74,0x2C,0x20,0x63,0x68,0x6F,0x6F,0x73,0x65,
+	0x20,0x61,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,
+	0x74,0x68,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x69,0x6E,0x73,
+	0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x61,0x6E,0x64,0x1C,
+	0x74,0x68,0x65,0x6E,0x20,0x22,0x64,0x72,0x61,0x77,0x22,0x20,
+	0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x6F,
+	0x61,0x72,0x64,0x2E,0x42,0x3E,0x54,0x68,0x65,0x20,0x6E,0x6F,
+	0x74,0x65,0x73,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x77,
+	0x69,0x74,0x68,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,
+	0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
+	0x6E,0x74,0x20,0x61,0x72,0x65,0x20,0x69,0x6E,0x64,0x69,0x63,
+	0x61,0x74,0x65,0x64,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x09,
+	0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x2E,0x00,0x1A,0x3E,
+	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6D,
+	0x70,0x6F,0x72,0x74,0x61,0x6E,0x74,0x20,0x6E,0x6F,0x74,0x65,
+	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
+	0x32,0x44,0x3E,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,
+	0x65,0x2C,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2C,0x20,
+	0x66,0x69,0x6E,0x65,0x74,0x75,0x6E,0x65,0x20,0x61,0x6E,0x64,
+	0x20,0x72,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x6E,0x6F,
+	0x74,0x65,0x20,0x69,0x73,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,
+	0x64,0x20,0x66,0x6F,0x72,0x20,0x45,0x41,0x43,0x48,0x41,0x53,
+	0x41,0x4D,0x50,0x4C,0x45,0x20,0x69,0x6E,0x20,0x61,0x6E,0x20,
+	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x20,
+	0x41,0x6C,0x6C,0x20,0x6F,0x74,0x68,0x65,0x72,0x20,0x69,0x6E,
+	0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E,0x20,0x69,0x73,
+	0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x64,0x20,0x66,0x6F,0x72,
+	0x20,0x74,0x68,0x65,0x12,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,
+	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x00,
+	0x31,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x49,
+	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x45,0x64,
+	0x69,0x74,0x6F,0x72,0x20,0x45,0x78,0x74,0x65,0x6E,0x73,0x69,
+	0x6F,0x6E,0x3A,0x20,0x28,0x49,0x2E,0x45,0x2E,0x45,0x78,0x74,
+	0x2E,0x29,0x01,0x3E,0x10,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x4D,0x49,0x44,0x49,0x3A,0x0B,0x3E,0x40,
+	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x28,0x3E,0x27,
+	0x70,0x2E,0x27,0x20,0x73,0x74,0x61,0x6E,0x64,0x73,0x20,0x66,
+	0x6F,0x72,0x20,0x22,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x22,
+	0x20,0x28,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,
+	0x29,0x2E,0x40,0x3E,0x53,0x65,0x76,0x65,0x72,0x61,0x6C,0x20,
+	0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,
+	0x63,0x61,0x6E,0x20,0x68,0x61,0x76,0x65,0x20,0x74,0x68,0x65,
+	0x20,0x73,0x61,0x6D,0x65,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,
+	0x69,0x74,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x62,
+	0x75,0x74,0x20,0x77,0x69,0x74,0x68,0x33,0x64,0x69,0x66,0x66,
 	0x65,0x72,0x65,0x6E,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,
-	0x6D,0x73,0x20,0x61,0x72,0x65,0x20,0x75,0x73,0x65,0x64,0x2E,
-	0x3E,0x44,0x69,0x66,0x66,0x65,0x72,0x65,0x6E,0x74,0x20,0x70,
-	0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x20,0x63,0x61,0x6E,0x6E,
-	0x6F,0x74,0x20,0x62,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,
-	0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65,
-	0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x61,0x74,0x20,
-	0x74,0x68,0x65,0x11,0x73,0x61,0x6D,0x65,0x20,0x74,0x69,0x6D,
-	0x65,0x20,0x74,0x68,0x6F,0x75,0x67,0x68,0x2E,0x44,0x3E,0x49,
-	0x66,0x20,0x79,0x6F,0x75,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,
-	0x20,0x74,0x68,0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x2C,
-	0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,
-	0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x62,0x65,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,
-	0x74,0x65,0x64,0x20,0x74,0x6F,0x1C,0x74,0x68,0x65,0x20,0x73,
-	0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x20,0x69,
-	0x6D,0x6D,0x65,0x64,0x69,0x61,0x74,0x65,0x6C,0x79,0x2E,0x3E,
-	0x3E,0x53,0x6F,0x6D,0x65,0x20,0x73,0x79,0x6E,0x74,0x68,0x65,
-	0x73,0x69,0x7A,0x65,0x72,0x73,0x20,0x74,0x72,0x61,0x6E,0x73,
-	0x6D,0x69,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,
-	0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x69,0x6E,0x66,0x6F,0x72,
-	0x6D,0x61,0x74,0x69,0x6F,0x6E,0x2E,0x20,0x49,0x66,0x20,0x74,
-	0x68,0x65,0x43,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,
-	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,
-	0x20,0x46,0x54,0x32,0x20,0x69,0x73,0x20,0x61,0x20,0x4D,0x49,
-	0x44,0x49,0x2D,0x69,0x6E,0x73,0x74,0x72,0x2E,0x20,0x77,0x69,
-	0x74,0x68,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20,
-	0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x61,0x73,0x3F,0x74,
-	0x68,0x65,0x20,0x72,0x65,0x63,0x65,0x69,0x76,0x65,0x64,0x20,
+	0x6D,0x73,0x2E,0x20,0x46,0x54,0x32,0x20,0x63,0x68,0x61,0x6E,
+	0x67,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,
+	0x72,0x61,0x6D,0x73,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x43,
+	0x4D,0x49,0x44,0x49,0x2D,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,
+	0x73,0x20,0x69,0x6E,0x73,0x74,0x61,0x6E,0x74,0x6C,0x79,0x20,
+	0x64,0x75,0x72,0x69,0x6E,0x67,0x20,0x70,0x6C,0x61,0x79,0x20,
+	0x69,0x66,0x20,0x64,0x69,0x66,0x66,0x65,0x72,0x65,0x6E,0x74,
+	0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x20,0x61,0x72,
+	0x65,0x20,0x75,0x73,0x65,0x64,0x2E,0x3E,0x44,0x69,0x66,0x66,
+	0x65,0x72,0x65,0x6E,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,
+	0x6D,0x73,0x20,0x63,0x61,0x6E,0x6E,0x6F,0x74,0x20,0x62,0x65,
+	0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x61,0x74,0x20,0x74,
+	0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E,
+	0x6E,0x65,0x6C,0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x11,0x73,
+	0x61,0x6D,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x74,0x68,0x6F,
+	0x75,0x67,0x68,0x2E,0x44,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75,
+	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x74,0x68,0x69,0x73,
+	0x20,0x76,0x61,0x6C,0x75,0x65,0x2C,0x20,0x74,0x68,0x65,0x20,
+	0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x6E,0x75,0x6D,0x62,
+	0x65,0x72,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x74,
+	0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x74,0x65,0x64,0x20,0x74,
+	0x6F,0x1C,0x74,0x68,0x65,0x20,0x73,0x79,0x6E,0x74,0x68,0x65,
+	0x73,0x69,0x7A,0x65,0x72,0x20,0x69,0x6D,0x6D,0x65,0x64,0x69,
+	0x61,0x74,0x65,0x6C,0x79,0x2E,0x3E,0x3E,0x53,0x6F,0x6D,0x65,
+	0x20,0x73,0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,
+	0x73,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,0x70,
+	0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x63,0x68,0x61,0x6E,0x67,
+	0x65,0x20,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,
+	0x6E,0x2E,0x20,0x49,0x66,0x20,0x74,0x68,0x65,0x43,0x63,0x75,
+	0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,
+	0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x46,0x54,0x32,0x20,
+	0x69,0x73,0x20,0x61,0x20,0x4D,0x49,0x44,0x49,0x2D,0x69,0x6E,
+	0x73,0x74,0x72,0x2E,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,
+	0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E,0x6E,
+	0x65,0x6C,0x20,0x61,0x73,0x3F,0x74,0x68,0x65,0x20,0x72,0x65,
+	0x63,0x65,0x69,0x76,0x65,0x64,0x20,0x70,0x72,0x6F,0x67,0x72,
+	0x61,0x6D,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2C,0x20,0x69,
+	0x74,0x27,0x73,0x20,0x4D,0x49,0x44,0x49,0x2D,0x70,0x72,0x6F,
+	0x67,0x72,0x61,0x6D,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,
+	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x2E,0x40,0x3E,0x49,
+	0x66,0x20,0x79,0x6F,0x75,0x72,0x20,0x73,0x79,0x6E,0x74,0x68,
+	0x65,0x73,0x69,0x7A,0x65,0x72,0x20,0x64,0x6F,0x65,0x73,0x6E,
+	0x27,0x74,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,
 	0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x63,0x68,0x61,0x6E,
-	0x67,0x65,0x2C,0x20,0x69,0x74,0x27,0x73,0x20,0x4D,0x49,0x44,
-	0x49,0x2D,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x77,0x69,
-	0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,
-	0x64,0x2E,0x40,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75,0x72,0x20,
-	0x73,0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x20,
-	0x64,0x6F,0x65,0x73,0x6E,0x27,0x74,0x20,0x74,0x72,0x61,0x6E,
-	0x73,0x6D,0x69,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,
-	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2C,0x20,0x74,0x68,0x65,
-	0x72,0x65,0x27,0x73,0x20,0x6E,0x6F,0x3E,0x70,0x6F,0x69,0x6E,
-	0x74,0x20,0x69,0x6E,0x20,0x63,0x68,0x61,0x6E,0x67,0x69,0x6E,
-	0x67,0x20,0x69,0x74,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,
-	0x73,0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x2C,
-	0x20,0x64,0x6F,0x20,0x69,0x74,0x20,0x69,0x6E,0x20,0x46,0x54,
-	0x32,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x2E,0x00,0x18,
-	0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x42,
-	0x65,0x6E,0x64,0x65,0x72,0x20,0x72,0x61,0x6E,0x67,0x65,0x3A,
-	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x38,0x3E,0x54,0x68,0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65,
-	0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x68,0x6F,0x77,
-	0x20,0x6D,0x61,0x6E,0x79,0x20,0x6E,0x6F,0x74,0x65,0x73,0x20,
-	0x74,0x68,0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,
-	0x6E,0x74,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x37,0x73,0x79,
-	0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x20,0x63,0x61,
-	0x6E,0x20,0x62,0x65,0x20,0x70,0x69,0x74,0x63,0x68,0x62,0x65,
-	0x6E,0x64,0x65,0x64,0x2E,0x20,0x46,0x54,0x32,0x20,0x75,0x73,
-	0x65,0x73,0x20,0x74,0x68,0x69,0x73,0x20,0x76,0x61,0x6C,0x75,
-	0x65,0x20,0x66,0x6F,0x72,0x37,0x74,0x72,0x61,0x6E,0x73,0x6D,
-	0x69,0x74,0x74,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,0x20,0x70,
-	0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,
-	0x2F,0x64,0x6F,0x77,0x6E,0x20,0x61,0x6E,0x64,0x20,0x74,0x6F,
-	0x6E,0x65,0x2D,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,
-	0x6F,0x13,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x73,0x20,0x63,
-	0x6F,0x72,0x72,0x65,0x63,0x74,0x6C,0x79,0x2E,0x45,0x3E,0x54,
-	0x68,0x65,0x20,0x4D,0x49,0x44,0x49,0x2D,0x70,0x69,0x74,0x63,
-	0x68,0x62,0x65,0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,
-	0x63,0x6F,0x72,0x72,0x65,0x63,0x74,0x6C,0x79,0x20,0x6F,0x6E,
-	0x6C,0x79,0x20,0x77,0x69,0x74,0x68,0x20,0x6C,0x69,0x6E,0x65,
-	0x61,0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,
-	0x20,0x74,0x61,0x62,0x6C,0x65,0x2E,0x00,0x18,0x40,0x58,0x30,
-	0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,
-	0x65,0x20,0x45,0x64,0x69,0x74,0x6F,0x72,0x3A,0x01,0x3E,0x2C,
-	0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,
-	0x6C,0x61,0x79,0x20,0x28,0x57,0x61,0x76,0x65,0x20,0x66,0x6F,
-	0x72,0x6D,0x2C,0x20,0x72,0x61,0x6E,0x67,0x65,0x2C,0x20,0x64,
-	0x69,0x73,0x70,0x6C,0x61,0x79,0x29,0x3A,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3C,0x3E,0x50,0x6C,
-	0x61,0x79,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,
-	0x65,0x6E,0x74,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,
-	0x69,0x74,0x68,0x20,0x74,0x6F,0x6E,0x65,0x20,0x64,0x69,0x73,
-	0x70,0x6C,0x61,0x79,0x20,0x61,0x62,0x6F,0x76,0x65,0x20,0x74,
+	0x67,0x65,0x2C,0x20,0x74,0x68,0x65,0x72,0x65,0x27,0x73,0x20,
+	0x6E,0x6F,0x3E,0x70,0x6F,0x69,0x6E,0x74,0x20,0x69,0x6E,0x20,
+	0x63,0x68,0x61,0x6E,0x67,0x69,0x6E,0x67,0x20,0x69,0x74,0x20,
+	0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x79,0x6E,0x74,0x68,
+	0x65,0x73,0x69,0x7A,0x65,0x72,0x2C,0x20,0x64,0x6F,0x20,0x69,
+	0x74,0x20,0x69,0x6E,0x20,0x46,0x54,0x32,0x20,0x69,0x6E,0x73,
+	0x74,0x65,0x61,0x64,0x2E,0x00,0x18,0x3E,0x40,0x58,0x30,0x34,
+	0x30,0x40,0x43,0x30,0x30,0x31,0x42,0x65,0x6E,0x64,0x65,0x72,
+	0x20,0x72,0x61,0x6E,0x67,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,
+	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x38,0x3E,0x54,0x68,0x69,
+	0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x64,0x65,0x66,0x69,
+	0x6E,0x65,0x73,0x20,0x68,0x6F,0x77,0x20,0x6D,0x61,0x6E,0x79,
+	0x20,0x6E,0x6F,0x74,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x69,
+	0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x6F,0x6E,
+	0x20,0x74,0x68,0x65,0x37,0x73,0x79,0x6E,0x74,0x68,0x65,0x73,
+	0x69,0x7A,0x65,0x72,0x20,0x63,0x61,0x6E,0x20,0x62,0x65,0x20,
+	0x70,0x69,0x74,0x63,0x68,0x62,0x65,0x6E,0x64,0x65,0x64,0x2E,
+	0x20,0x46,0x54,0x32,0x20,0x75,0x73,0x65,0x73,0x20,0x74,0x68,
+	0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x66,0x6F,0x72,
+	0x37,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x74,0x69,0x6E,
+	0x67,0x20,0x74,0x68,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,
+	0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,
+	0x20,0x61,0x6E,0x64,0x20,0x74,0x6F,0x6E,0x65,0x2D,0x70,0x6F,
+	0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x13,0x63,0x6F,0x6D,
+	0x6D,0x61,0x6E,0x64,0x73,0x20,0x63,0x6F,0x72,0x72,0x65,0x63,
+	0x74,0x6C,0x79,0x2E,0x45,0x3E,0x54,0x68,0x65,0x20,0x4D,0x49,
+	0x44,0x49,0x2D,0x70,0x69,0x74,0x63,0x68,0x62,0x65,0x6E,0x64,
+	0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x63,0x6F,0x72,0x72,0x65,
+	0x63,0x74,0x6C,0x79,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x77,0x69,
+	0x74,0x68,0x20,0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x66,0x72,
+	0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62,0x6C,
+	0x65,0x2E,0x00,0x18,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,
+	0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x45,0x64,0x69,
+	0x74,0x6F,0x72,0x3A,0x01,0x3E,0x2B,0x3E,0x40,0x58,0x30,0x34,
+	0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6C,0x61,0x79,0x20,0x28,
+	0x57,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x2C,0x20,0x72,0x61,
+	0x6E,0x67,0x65,0x2C,0x20,0x64,0x69,0x73,0x70,0x6C,0x61,0x79,
+	0x29,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
+	0x30,0x32,0x42,0x3E,0x50,0x6C,0x61,0x79,0x73,0x20,0x74,0x68,
+	0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x73,0x61,
+	0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,
+	0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x64,0x69,0x73,0x70,0x6C,
+	0x61,0x79,0x65,0x64,0x20,0x61,0x62,0x6F,0x76,0x65,0x20,0x74,
 	0x68,0x65,0x20,0x22,0x73,0x74,0x6F,0x70,0x22,0x3D,0x62,0x75,
 	0x74,0x74,0x6F,0x6E,0x2E,0x20,0x4E,0x6F,0x74,0x65,0x20,0x74,
 	0x68,0x61,0x74,0x20,0x72,0x65,0x73,0x70,0x65,0x63,0x74,0x20,
@@ -1578,7 +1599,7 @@
 	0x69,0x73,0x20,0x74,0x61,0x6B,0x65,0x6E,0x20,0x74,0x6F,0x20,
 	0x74,0x68,0x65,0x20,0x70,0x61,0x72,0x74,0x69,0x63,0x75,0x6C,
 	0x61,0x72,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x27,0x73,0x0E,
-	0x72,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x74,0x6F,0x6E,
+	0x72,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x6E,0x6F,0x74,
 	0x65,0x2E,0x00,0x16,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
 	0x30,0x30,0x31,0x53,0x61,0x76,0x65,0x20,0x72,0x61,0x6E,0x67,
 	0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
@@ -1689,543 +1710,542 @@
 	0x61,0x6E,0x67,0x65,0x20,0x28,0x6F,0x72,0x20,0x74,0x68,0x65,
 	0x20,0x77,0x68,0x6F,0x6C,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,
 	0x65,0x20,0x69,0x66,0x20,0x6E,0x6F,0x20,0x72,0x61,0x6E,0x67,
-	0x65,0x20,0x69,0x73,0x20,0x73,0x65,0x74,0x29,0x2E,0x00,0x13,
-	0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,
-	0x6F,0x6E,0x76,0x65,0x72,0x74,0x3A,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x34,0x3E,0x43,0x6F,0x6E,
-	0x76,0x65,0x72,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x65,0x6E,
-	0x74,0x69,0x72,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,
-	0x66,0x72,0x6F,0x6D,0x2F,0x74,0x6F,0x20,0x73,0x69,0x67,0x6E,
-	0x65,0x64,0x2F,0x75,0x6E,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,
-	0x00,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
-	0x31,0x43,0x6F,0x6E,0x76,0x65,0x72,0x74,0x20,0x57,0x3A,0x0B,
-	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3F,
-	0x53,0x77,0x61,0x70,0x73,0x20,0x74,0x68,0x65,0x20,0x62,0x79,
-	0x74,0x65,0x20,0x6F,0x72,0x64,0x65,0x72,0x20,0x74,0x6F,0x2F,
-	0x66,0x72,0x6F,0x6D,0x20,0x49,0x6E,0x74,0x65,0x6C,0x20,0x66,
-	0x72,0x6F,0x6D,0x2F,0x74,0x6F,0x20,0x4D,0x6F,0x74,0x6F,0x72,
-	0x6F,0x6C,0x61,0x20,0x73,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,
-	0x20,0x6F,0x6E,0x12,0x74,0x68,0x65,0x20,0x65,0x6E,0x74,0x69,
-	0x72,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x2E,0x44,0x59,
-	0x6F,0x75,0x27,0x6C,0x6C,0x20,0x6E,0x65,0x65,0x64,0x20,0x74,
-	0x68,0x69,0x73,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E,
-	0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x69,0x6D,0x70,0x6F,
-	0x72,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74,0x20,0x73,0x61,
-	0x6D,0x70,0x6C,0x65,0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x4D,
-	0x6F,0x74,0x6F,0x72,0x6F,0x6C,0x61,0x2D,0x62,0x79,0x74,0x65,
-	0x2D,0x6F,0x72,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,0x28,0x66,
-	0x2E,0x65,0x78,0x2E,0x20,0x4B,0x75,0x72,0x7A,0x77,0x65,0x69,
-	0x6C,0x20,0x4B,0x32,0x30,0x30,0x30,0x20,0x73,0x61,0x6D,0x70,
-	0x6C,0x65,0x73,0x2E,0x29,0x00,0x10,0x3E,0x40,0x58,0x30,0x34,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x63,0x68,0x6F,0x3A,0x0B,
-	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E,
-	0x4F,0x70,0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E,0x20,
-	0x74,0x68,0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,0x73,
-	0x61,0x6D,0x70,0x6C,0x65,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x78,0x20,0x44,
-	0x43,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x3D,0x41,0x74,0x74,0x65,0x6D,0x70,0x74,0x73,0x20,
-	0x74,0x6F,0x20,0x63,0x65,0x6E,0x74,0x65,0x72,0x20,0x61,0x20,
-	0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x74,0x68,0x61,0x74,0x20,
-	0x68,0x61,0x73,0x20,0x75,0x6E,0x77,0x61,0x6E,0x74,0x65,0x64,
-	0x20,0x44,0x43,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x2F,0x62,
-	0x69,0x61,0x73,0x2E,0x43,0x50,0x6C,0x65,0x61,0x73,0x65,0x20,
-	0x6E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x74,
-	0x20,0x69,0x73,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x61,0x20,
-	0x63,0x72,0x75,0x64,0x65,0x20,0x61,0x6C,0x67,0x6F,0x72,0x69,
-	0x74,0x68,0x6D,0x2C,0x20,0x73,0x6F,0x20,0x69,0x74,0x20,0x63,
-	0x61,0x6E,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69,0x6D,0x65,0x73,
-	0x22,0x66,0x61,0x69,0x6C,0x20,0x64,0x65,0x70,0x65,0x6E,0x64,
-	0x69,0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,
-	0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,0x2E,0x00,
-	0x14,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x52,0x65,0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3C,0x4F,0x70,
-	0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E,0x20,0x74,0x68,
-	0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,0x73,0x61,0x6D,
-	0x70,0x6C,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x61,0x6D,
-	0x70,0x6C,0x65,0x27,0x73,0x20,0x72,0x65,0x6C,0x61,0x74,0x69,
-	0x76,0x65,0x20,0x74,0x6F,0x6E,0x65,0x20,0x69,0x73,0x2C,0x63,
-	0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,
-	0x72,0x65,0x73,0x70,0x65,0x63,0x74,0x20,0x74,0x6F,0x20,0x74,
-	0x68,0x65,0x20,0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69,0x6E,
-	0x67,0x20,0x72,0x61,0x74,0x65,0x2E,0x00,0x16,0x3E,0x40,0x58,
-	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69,0x78,0x20,
-	0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,
-	0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x35,0x3E,0x4D,0x69,0x78,
-	0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72,0x63,
-	0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,0x65,0x20,0x64,
-	0x65,0x73,0x74,0x69,0x6E,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,
-	0x6F,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72,0x63,0x65,
-	0x2E,0x00,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,
-	0x30,0x31,0x44,0x72,0x61,0x77,0x20,0x6D,0x6F,0x64,0x65,0x3A,
+	0x65,0x20,0x69,0x73,0x20,0x73,0x65,0x74,0x29,0x2E,0x00,0x10,
+	0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,
+	0x69,0x67,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,
+	0x43,0x30,0x30,0x32,0x22,0x3E,0x43,0x6F,0x6E,0x76,0x65,0x72,
+	0x74,0x73,0x20,0x62,0x65,0x74,0x77,0x65,0x65,0x6E,0x20,0x73,
+	0x69,0x67,0x6E,0x65,0x64,0x2F,0x75,0x6E,0x73,0x69,0x67,0x6E,
+	0x65,0x64,0x2E,0x00,0x1F,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x42,0x2E,0x20,0x73,0x77,0x61,0x70,0x20,
+	0x28,0x62,0x79,0x74,0x65,0x20,0x73,0x77,0x61,0x70,0x29,0x3A,
 	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
-	0x40,0x42,0x79,0x20,0x70,0x72,0x65,0x73,0x73,0x69,0x6E,0x67,
-	0x20,0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x20,0x6D,
-	0x6F,0x75,0x73,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x20,
-	0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,
-	0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x2C,0x20,0x79,0x6F,
-	0x75,0x20,0x63,0x61,0x6E,0x1D,0x64,0x72,0x61,0x77,0x20,0x79,
-	0x6F,0x75,0x72,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,
-	0x73,0x20,0x6D,0x61,0x6E,0x75,0x61,0x6C,0x6C,0x79,0x2E,0x00,
-	0x18,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,
-	0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,
-	0x3A,0x01,0x3E,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
-	0x30,0x30,0x31,0x41,0x75,0x74,0x6F,0x20,0x73,0x61,0x76,0x65,
+	0x3F,0x53,0x77,0x61,0x70,0x73,0x20,0x74,0x68,0x65,0x20,0x62,
+	0x79,0x74,0x65,0x20,0x6F,0x72,0x64,0x65,0x72,0x20,0x74,0x6F,
+	0x2F,0x66,0x72,0x6F,0x6D,0x20,0x49,0x6E,0x74,0x65,0x6C,0x20,
+	0x66,0x72,0x6F,0x6D,0x2F,0x74,0x6F,0x20,0x4D,0x6F,0x74,0x6F,
+	0x72,0x6F,0x6C,0x61,0x20,0x73,0x74,0x61,0x6E,0x64,0x61,0x72,
+	0x64,0x20,0x6F,0x6E,0x12,0x74,0x68,0x65,0x20,0x65,0x6E,0x74,
+	0x69,0x72,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x2E,0x44,
+	0x59,0x6F,0x75,0x27,0x6C,0x6C,0x20,0x6E,0x65,0x65,0x64,0x20,
+	0x74,0x68,0x69,0x73,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,
+	0x6E,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x69,0x6D,0x70,
+	0x6F,0x72,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74,0x20,0x73,
+	0x61,0x6D,0x70,0x6C,0x65,0x73,0x20,0x77,0x69,0x74,0x68,0x20,
+	0x4D,0x6F,0x74,0x6F,0x72,0x6F,0x6C,0x61,0x2D,0x62,0x79,0x74,
+	0x65,0x2D,0x6F,0x72,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,0x28,
+	0x66,0x2E,0x65,0x78,0x2E,0x20,0x4B,0x75,0x72,0x7A,0x77,0x65,
+	0x69,0x6C,0x20,0x4B,0x32,0x30,0x30,0x30,0x20,0x73,0x61,0x6D,
+	0x70,0x6C,0x65,0x73,0x2E,0x29,0x00,0x10,0x3E,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x63,0x68,0x6F,0x3A,
+	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
+	0x1E,0x4F,0x70,0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E,
+	0x20,0x74,0x68,0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,
+	0x73,0x61,0x6D,0x70,0x6C,0x65,0x2E,0x00,0x12,0x3E,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x78,0x20,
+	0x44,0x43,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x3D,0x41,0x74,0x74,0x65,0x6D,0x70,0x74,0x73,
+	0x20,0x74,0x6F,0x20,0x63,0x65,0x6E,0x74,0x65,0x72,0x20,0x61,
+	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x74,0x68,0x61,0x74,
+	0x20,0x68,0x61,0x73,0x20,0x75,0x6E,0x77,0x61,0x6E,0x74,0x65,
+	0x64,0x20,0x44,0x43,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x2F,
+	0x62,0x69,0x61,0x73,0x2E,0x43,0x50,0x6C,0x65,0x61,0x73,0x65,
+	0x20,0x6E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x69,
+	0x74,0x20,0x69,0x73,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x61,
+	0x20,0x63,0x72,0x75,0x64,0x65,0x20,0x61,0x6C,0x67,0x6F,0x72,
+	0x69,0x74,0x68,0x6D,0x2C,0x20,0x73,0x6F,0x20,0x69,0x74,0x20,
+	0x63,0x61,0x6E,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69,0x6D,0x65,
+	0x73,0x22,0x66,0x61,0x69,0x6C,0x20,0x64,0x65,0x70,0x65,0x6E,
+	0x64,0x69,0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,
+	0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,0x2E,
+	0x00,0x14,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
+	0x31,0x52,0x65,0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E,
+	0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3C,0x4F,
+	0x70,0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E,0x20,0x74,
+	0x68,0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,0x73,0x61,
+	0x6D,0x70,0x6C,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x61,
+	0x6D,0x70,0x6C,0x65,0x27,0x73,0x20,0x72,0x65,0x6C,0x61,0x74,
+	0x69,0x76,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x69,0x73,0x2C,
+	0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x77,0x69,0x74,0x68,
+	0x20,0x72,0x65,0x73,0x70,0x65,0x63,0x74,0x20,0x74,0x6F,0x20,
+	0x74,0x68,0x65,0x20,0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69,
+	0x6E,0x67,0x20,0x72,0x61,0x74,0x65,0x2E,0x00,0x16,0x3E,0x40,
+	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69,0x78,
+	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E,0x40,0x58,
+	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x35,0x3E,0x4D,0x69,
+	0x78,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72,
+	0x63,0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,0x65,0x20,
+	0x64,0x65,0x73,0x74,0x69,0x6E,0x61,0x74,0x69,0x6F,0x6E,0x20,
+	0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72,0x63,
+	0x65,0x2E,0x00,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,
+	0x30,0x30,0x31,0x44,0x72,0x61,0x77,0x20,0x6D,0x6F,0x64,0x65,
 	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
-	0x32,0x43,0x49,0x66,0x20,0x74,0x68,0x65,0x20,0x61,0x75,0x74,
-	0x6F,0x20,0x73,0x61,0x76,0x65,0x20,0x69,0x73,0x20,0x6F,0x6E,
-	0x2C,0x20,0x46,0x54,0x32,0x20,0x77,0x69,0x6C,0x6C,0x20,0x75,
-	0x70,0x64,0x61,0x74,0x65,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,
-	0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,
-	0x66,0x69,0x6C,0x65,0x20,0x77,0x68,0x65,0x6E,0x15,0x79,0x6F,
-	0x75,0x20,0x65,0x78,0x69,0x74,0x20,0x74,0x68,0x65,0x20,0x70,
-	0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x25,0x40,0x58,0x30,
-	0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,0x69,
-	0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x49,0x2F,
-	0x4F,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x73,0x3A,0x01,0x3E,
-	0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,
-	0x49,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,
-	0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x35,0x53,0x65,0x6C,0x65,0x63,0x74,0x73,0x20,0x77,
-	0x68,0x61,0x74,0x20,0x74,0x79,0x70,0x65,0x20,0x6F,0x66,0x20,
-	0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69,0x6E,0x67,0x20,0x69,
-	0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,
-	0x20,0x74,0x6F,0x20,0x75,0x73,0x65,0x2E,0x45,0x22,0x4E,0x6F,
-	0x6E,0x65,0x22,0x20,0x75,0x73,0x65,0x73,0x20,0x6E,0x6F,0x20,
-	0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,
-	0x6E,0x20,0x28,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20,0x6E,
-	0x65,0x69,0x67,0x68,0x62,0x6F,0x72,0x29,0x2C,0x20,0x77,0x68,
-	0x69,0x63,0x68,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x73,
-	0x75,0x6C,0x74,0x20,0x69,0x6E,0x49,0x61,0x6C,0x69,0x61,0x73,
-	0x69,0x6E,0x67,0x20,0x28,0x6E,0x6F,0x69,0x73,0x65,0x29,0x20,
-	0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64,
-	0x2E,0x20,0x22,0x4C,0x69,0x6E,0x65,0x61,0x72,0x22,0x20,0x69,
-	0x73,0x20,0x77,0x68,0x61,0x74,0x20,0x72,0x65,0x61,0x6C,0x20,
-	0x46,0x54,0x32,0x20,0x75,0x73,0x65,0x73,0x2C,0x20,0x77,0x68,
-	0x69,0x63,0x68,0x20,0x69,0x73,0x20,0x61,0x47,0x6D,0x65,0x64,
-	0x69,0x6F,0x63,0x72,0x65,0x20,0x69,0x6E,0x74,0x65,0x72,0x70,
-	0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,0x79,0x70,0x65,
-	0x2E,0x20,0x22,0x57,0x69,0x6E,0x64,0x6F,0x77,0x65,0x64,0x2D,
-	0x73,0x69,0x6E,0x63,0x22,0x20,0x69,0x73,0x20,0x74,0x68,0x65,
-	0x20,0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x65,0x64,
-	0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x48,0x66,0x6F,0x72,
-	0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x73,0x74,0x20,0x61,0x75,
-	0x64,0x69,0x6F,0x20,0x71,0x75,0x61,0x6C,0x69,0x74,0x79,0x2C,
-	0x20,0x61,0x6C,0x74,0x68,0x6F,0x75,0x67,0x68,0x20,0x69,0x74,
-	0x20,0x6D,0x61,0x79,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69,0x6D,
-	0x65,0x73,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x74,0x6F,0x6F,
-	0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x65,0x64,0x2A,0x6F,0x6E,
-	0x20,0x6C,0x6F,0x77,0x2D,0x71,0x75,0x61,0x6C,0x69,0x74,0x79,
-	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x20,0x28,0x66,0x2E,
-	0x65,0x78,0x2E,0x20,0x41,0x6D,0x69,0x67,0x61,0x20,0x4D,0x4F,
-	0x44,0x73,0x29,0x2E,0x00,0x1A,0x3E,0x40,0x58,0x30,0x34,0x30,
-	0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,
-	0x72,0x61,0x6D,0x70,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3B,0x45,0x6E,0x61,
-	0x62,0x6C,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x61,0x6E,0x74,
-	0x69,0x2D,0x63,0x6C,0x69,0x63,0x6B,0x20,0x73,0x79,0x73,0x74,
-	0x65,0x6D,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x61,0x75,
-	0x64,0x69,0x6F,0x20,0x6D,0x69,0x78,0x65,0x72,0x20,0x28,0x46,
-	0x54,0x32,0x2E,0x30,0x38,0x2B,0x29,0x2E,0x3B,0x50,0x6C,0x65,
-	0x61,0x73,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,
-	0x74,0x20,0x6F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,0x20,0x46,
-	0x54,0x32,0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x6C,0x6F,0x61,
-	0x64,0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6F,0x6E,0x66,0x69,
-	0x67,0x20,0x65,0x6E,0x74,0x72,0x79,0x2C,0x0B,0x63,0x6C,0x6F,
-	0x6E,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x2E,0x00,0x19,0x3E,0x40,
-	0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x6D,0x70,
-	0x6C,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x3A,0x0B,
-	0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x46,
-	0x41,0x6D,0x70,0x6C,0x69,0x66,0x69,0x65,0x73,0x20,0x74,0x68,
-	0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x77,0x68,0x65,
-	0x6E,0x20,0x6D,0x69,0x78,0x69,0x6E,0x67,0x2E,0x20,0x49,0x66,
-	0x20,0x79,0x6F,0x75,0x20,0x73,0x65,0x74,0x20,0x74,0x68,0x69,
-	0x73,0x20,0x6F,0x6E,0x65,0x20,0x74,0x6F,0x6F,0x20,0x68,0x69,
-	0x67,0x68,0x2C,0x20,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x3A,0x67,
-	0x65,0x74,0x20,0x64,0x69,0x73,0x74,0x6F,0x72,0x74,0x69,0x6F,
-	0x6E,0x2E,0x20,0x33,0x32,0x58,0x20,0x65,0x71,0x75,0x61,0x6C,
-	0x73,0x20,0x66,0x75,0x6C,0x6C,0x20,0x61,0x6D,0x70,0x6C,0x69,
-	0x74,0x75,0x64,0x65,0x20,0x66,0x6F,0x72,0x20,0x6F,0x6E,0x65,
-	0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x2E,0x00,0x1B,0x3E,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x72,
-	0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62,0x6C,
+	0x32,0x40,0x42,0x79,0x20,0x70,0x72,0x65,0x73,0x73,0x69,0x6E,
+	0x67,0x20,0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x20,
+	0x6D,0x6F,0x75,0x73,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,
+	0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,
+	0x6C,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x2C,0x20,0x79,
+	0x6F,0x75,0x20,0x63,0x61,0x6E,0x1D,0x64,0x72,0x61,0x77,0x20,
+	0x79,0x6F,0x75,0x72,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,
+	0x6D,0x73,0x20,0x6D,0x61,0x6E,0x75,0x61,0x6C,0x6C,0x79,0x2E,
+	0x00,0x18,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,
+	0x6E,0x3A,0x01,0x3E,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,
+	0x43,0x30,0x30,0x31,0x41,0x75,0x74,0x6F,0x20,0x73,0x61,0x76,
 	0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
-	0x30,0x32,0x40,0x54,0x68,0x65,0x20,0x6C,0x69,0x6E,0x65,0x61,
-	0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,
-	0x74,0x61,0x62,0x6C,0x65,0x20,0x6D,0x61,0x6B,0x65,0x73,0x20,
-	0x61,0x6C,0x6C,0x20,0x70,0x69,0x74,0x63,0x68,0x20,0x62,0x65,
-	0x6E,0x64,0x73,0x20,0x72,0x75,0x6E,0x20,0x69,0x6E,0x20,0x63,
-	0x6F,0x6E,0x73,0x74,0x61,0x6E,0x74,0x3F,0x73,0x70,0x65,0x65,
-	0x64,0x2C,0x20,0x69,0x6E,0x64,0x65,0x70,0x65,0x6E,0x64,0x65,
-	0x6E,0x74,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63,0x75,
-	0x72,0x72,0x65,0x6E,0x74,0x20,0x66,0x72,0x65,0x71,0x75,0x65,
-	0x6E,0x63,0x79,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,
-	0x73,0x77,0x69,0x74,0x63,0x68,0x20,0x74,0x68,0x69,0x73,0x41,
-	0x6F,0x6E,0x65,0x2C,0x20,0x6F,0x6E,0x20,0x61,0x20,0x66,0x69,
-	0x6E,0x69,0x73,0x68,0x65,0x64,0x20,0x73,0x6F,0x6E,0x67,0x2C,
-	0x20,0x69,0x74,0x20,0x6D,0x69,0x67,0x68,0x74,0x20,0x73,0x6F,
-	0x75,0x6E,0x64,0x20,0x73,0x74,0x72,0x61,0x6E,0x67,0x65,0x20,
-	0x69,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64,
-	0x20,0x75,0x73,0x65,0x73,0x0D,0x70,0x6F,0x72,0x74,0x61,0x6D,
-	0x65,0x6E,0x74,0x6F,0x65,0x73,0x2E,0x00,0x20,0x40,0x58,0x30,
-	0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,0x69,
-	0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x4C,0x61,
-	0x79,0x6F,0x75,0x74,0x3A,0x01,0x3E,0x29,0x3E,0x40,0x58,0x30,
-	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,
-	0x72,0x6E,0x20,0x6C,0x61,0x79,0x6F,0x75,0x74,0x2C,0x20,0x68,
-	0x65,0x78,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x69,0x6E,0x67,
-	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
-	0x32,0x41,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x75,0x73,0x65,
-	0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x73,0x20,0x74,0x68,
-	0x61,0x74,0x20,0x61,0x72,0x65,0x20,0x6C,0x6F,0x6E,0x67,0x65,
-	0x72,0x20,0x74,0x68,0x61,0x6E,0x20,0x39,0x39,0x20,0x6C,0x69,
-	0x6E,0x65,0x73,0x2C,0x20,0x79,0x6F,0x75,0x20,0x73,0x68,0x6F,
-	0x75,0x6C,0x64,0x20,0x75,0x73,0x65,0x45,0x68,0x65,0x78,0x20,
-	0x63,0x6F,0x75,0x6E,0x74,0x69,0x6E,0x67,0x20,0x73,0x69,0x6E,
-	0x63,0x65,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61,0x72,0x65,
-	0x20,0x6F,0x6E,0x6C,0x79,0x20,0x32,0x20,0x64,0x69,0x67,0x69,
-	0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6C,0x69,
-	0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x63,0x6F,
-	0x6C,0x75,0x6D,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,0x34,
-	0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65,0x73,
-	0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,
-	0x32,0x43,0x22,0x53,0x74,0x64,0x2E,0x22,0x20,0x28,0x73,0x74,
-	0x61,0x6E,0x64,0x61,0x72,0x64,0x29,0x20,0x77,0x69,0x6C,0x6C,
-	0x20,0x73,0x68,0x6F,0x77,0x20,0x74,0x68,0x65,0x20,0x73,0x61,
-	0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,0x69,0x6E,0x74,0x73,0x20,
-	0x61,0x73,0x20,0x70,0x69,0x78,0x65,0x6C,0x73,0x20,0x28,0x6C,
-	0x69,0x6B,0x65,0x20,0x46,0x54,0x32,0x29,0x2E,0x41,0x22,0x4C,
-	0x69,0x6E,0x65,0x64,0x22,0x20,0x77,0x69,0x6C,0x6C,0x20,0x64,
-	0x72,0x61,0x77,0x20,0x6C,0x69,0x6E,0x65,0x73,0x20,0x62,0x65,
-	0x74,0x77,0x65,0x65,0x6E,0x20,0x74,0x68,0x65,0x20,0x70,0x6F,
-	0x69,0x6E,0x74,0x73,0x2C,0x20,0x6C,0x69,0x6B,0x65,0x20,0x61,
-	0x6E,0x20,0x6F,0x73,0x63,0x69,0x6C,0x6C,0x6F,0x73,0x63,0x6F,
-	0x70,0x65,0x2E,0x00,0x27,0x40,0x58,0x30,0x32,0x30,0x40,0x43,
-	0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,
-	0x74,0x69,0x6F,0x6E,0x2C,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,
-	0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x3A,0x01,0x3E,0x15,0x3E,
-	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x53,
-	0x79,0x6E,0x63,0x20,0x6F,0x66,0x66,0x3A,0x0B,0x3E,0x40,0x58,
-	0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3F,0x54,0x65,0x6C,
-	0x6C,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72,
-	0x61,0x6D,0x20,0x74,0x6F,0x20,0x6E,0x6F,0x74,0x20,0x75,0x73,
-	0x65,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x66,0x6F,0x72,0x20,
-	0x76,0x69,0x64,0x65,0x6F,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,
-	0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,
-	0x40,0x72,0x65,0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,
-	0x65,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x36,0x30,0x48,
-	0x7A,0x20,0x28,0x6F,0x72,0x20,0x35,0x39,0x48,0x7A,0x29,0x2C,
-	0x20,0x74,0x68,0x65,0x6E,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,
-	0x69,0x73,0x20,0x61,0x6C,0x77,0x61,0x79,0x73,0x20,0x6F,0x66,
-	0x66,0x20,0x66,0x6F,0x72,0x45,0x74,0x68,0x69,0x73,0x20,0x70,
-	0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x20,0x4E,0x6F,0x74,0x20,
-	0x68,0x61,0x76,0x69,0x6E,0x67,0x20,0x56,0x53,0x79,0x6E,0x63,
-	0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,
-	0x20,0x69,0x6E,0x20,0x6C,0x65,0x73,0x73,0x20,0x69,0x6E,0x70,
-	0x75,0x74,0x2F,0x76,0x69,0x64,0x65,0x6F,0x20,0x64,0x65,0x6C,
-	0x61,0x79,0x2C,0x1E,0x62,0x75,0x74,0x20,0x61,0x6C,0x73,0x6F,
-	0x20,0x70,0x6F,0x74,0x65,0x6E,0x74,0x69,0x61,0x6C,0x20,0x73,
-	0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,0x67,0x2E,0x00,0x15,
-	0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,
-	0x74,0x72,0x65,0x74,0x63,0x68,0x65,0x64,0x3A,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x4A,0x4D,0x61,
-	0x6B,0x65,0x73,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,
-	0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x20,0x63,0x6F,0x6D,0x70,
-	0x6C,0x65,0x74,0x65,0x6C,0x79,0x20,0x73,0x74,0x72,0x65,0x74,
-	0x63,0x68,0x20,0x6F,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x69,
-	0x6D,0x61,0x67,0x65,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x63,
-	0x61,0x6E,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,
-	0x4E,0x61,0x6C,0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x28,0x75,
-	0x6E,0x65,0x76,0x65,0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,
-	0x77,0x69,0x64,0x74,0x68,0x73,0x29,0x20,0x69,0x66,0x20,0x74,
-	0x68,0x65,0x20,0x61,0x73,0x70,0x65,0x63,0x74,0x20,0x72,0x61,
-	0x74,0x69,0x6F,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x73,
-	0x63,0x72,0x65,0x65,0x6E,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,
-	0x20,0x31,0x36,0x3A,0x31,0x30,0x2E,0x52,0x54,0x68,0x65,0x20,
-	0x22,0x50,0x69,0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,
-	0x72,0x22,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x20,0x63,
-	0x61,0x6E,0x20,0x68,0x65,0x6C,0x70,0x20,0x77,0x69,0x74,0x68,
-	0x20,0x74,0x68,0x69,0x73,0x2C,0x20,0x62,0x75,0x74,0x20,0x69,
-	0x74,0x20,0x6D,0x61,0x6B,0x65,0x73,0x20,0x74,0x68,0x65,0x20,
-	0x69,0x6D,0x61,0x67,0x65,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62,
-	0x6C,0x75,0x72,0x72,0x79,0x2E,0x01,0x20,0x18,0x3E,0x40,0x58,
-	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x69,0x78,0x65,
-	0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x3A,0x0B,0x3E,0x40,
-	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x52,0x41,0x70,
-	0x70,0x6C,0x69,0x65,0x73,0x20,0x61,0x6E,0x20,0x61,0x6E,0x74,
-	0x69,0x2D,0x61,0x6C,0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x73,
-	0x75,0x62,0x70,0x69,0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,
-	0x65,0x72,0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x73,0x20,0x75,
-	0x73,0x65,0x64,0x20,0x77,0x68,0x65,0x6E,0x20,0x74,0x68,0x65,
-	0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x20,0x69,0x73,0x20,0x75,
-	0x70,0x73,0x63,0x61,0x6C,0x65,0x64,0x2E,0x3B,0x50,0x6C,0x65,
-	0x61,0x73,0x65,0x20,0x6B,0x65,0x65,0x70,0x20,0x69,0x6E,0x20,
-	0x6D,0x69,0x6E,0x64,0x20,0x74,0x68,0x61,0x74,0x20,0x74,0x68,
-	0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6D,0x61,0x6B,0x65,
-	0x20,0x70,0x69,0x78,0x65,0x6C,0x73,0x20,0x6C,0x6F,0x6F,0x6B,
-	0x20,0x62,0x6C,0x75,0x72,0x72,0x79,0x2E,0x00,0x23,0x40,0x58,
-	0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x64,0x76,0x61,
-	0x6E,0x63,0x65,0x64,0x20,0x65,0x64,0x69,0x74,0x20,0x66,0x75,
-	0x6E,0x63,0x74,0x69,0x6F,0x6E,0x73,0x3A,0x20,0x01,0x3E,0x1E,
-	0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,
-	0x6F,0x70,0x79,0x2F,0x50,0x61,0x73,0x74,0x65,0x20,0x6D,0x61,
-	0x73,0x6B,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
-	0x30,0x40,0x43,0x30,0x30,0x32,0x37,0x54,0x68,0x65,0x20,0x6D,
-	0x61,0x73,0x6B,0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x75,0x73,
-	0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x63,0x6F,0x70,0x79,0x69,
-	0x6E,0x67,0x2F,0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x6F,
-	0x6E,0x6C,0x79,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x6F,0x66,
-	0x20,0x61,0x46,0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63,0x65,0x6C,
-	0x6C,0x22,0x2E,0x20,0x54,0x68,0x65,0x20,0x64,0x69,0x66,0x66,
-	0x65,0x72,0x65,0x6E,0x74,0x20,0x70,0x61,0x72,0x74,0x73,0x20,
-	0x6F,0x66,0x20,0x61,0x20,0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63,
-	0x65,0x6C,0x6C,0x22,0x20,0x69,0x73,0x20,0x4E,0x6F,0x74,0x65,
-	0x2C,0x20,0x49,0x6E,0x73,0x74,0x72,0x2E,0x20,0x6E,0x72,0x2E,
-	0x2C,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x2C,0x20,0x45,0x66,
-	0x66,0x65,0x63,0x74,0x20,0x6E,0x72,0x20,0x26,0x20,0x45,0x66,
-	0x66,0x65,0x63,0x74,0x20,0x64,0x61,0x74,0x61,0x2E,0x34,0x3E,
-	0x41,0x73,0x20,0x79,0x6F,0x75,0x20,0x63,0x61,0x6E,0x20,0x73,
-	0x65,0x65,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x69,
-	0x6E,0x64,0x6F,0x77,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61,
-	0x72,0x65,0x20,0x33,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x73,
-	0x20,0x6F,0x66,0x3D,0x22,0x65,0x6E,0x61,0x62,0x6C,0x65,0x2F,
-	0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x20,0x62,0x75,0x74,0x74,
-	0x6F,0x6E,0x73,0x22,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x68,
-	0x61,0x73,0x20,0x74,0x68,0x65,0x20,0x6C,0x65,0x74,0x74,0x65,
-	0x72,0x73,0x20,0x43,0x2C,0x50,0x20,0x26,0x20,0x54,0x20,0x61,
-	0x62,0x6F,0x76,0x65,0x2E,0x45,0x3E,0x43,0x20,0x6D,0x65,0x61,
-	0x6E,0x73,0x20,0x63,0x6F,0x70,0x79,0x2C,0x20,0x69,0x74,0x20,
-	0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69,
-	0x63,0x68,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61,
-	0x74,0x20,0x67,0x6F,0x65,0x73,0x20,0x69,0x6E,0x74,0x6F,0x20,
-	0x74,0x68,0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,
-	0x65,0x72,0x2E,0x3E,0x3E,0x50,0x20,0x6D,0x65,0x61,0x6E,0x73,
-	0x20,0x70,0x61,0x73,0x74,0x65,0x20,0x61,0x6E,0x64,0x20,0x63,
-	0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69,0x63,
-	0x68,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61,0x74,
-	0x20,0x67,0x6F,0x65,0x73,0x20,0x6F,0x75,0x74,0x20,0x66,0x72,
-	0x6F,0x6D,0x20,0x74,0x68,0x65,0x0B,0x63,0x6F,0x70,0x79,0x62,
-	0x75,0x66,0x66,0x65,0x72,0x2E,0x45,0x3E,0x54,0x20,0x6D,0x65,
-	0x61,0x6E,0x73,0x20,0x74,0x72,0x61,0x6E,0x73,0x70,0x61,0x72,
-	0x65,0x6E,0x63,0x79,0x2E,0x20,0x49,0x66,0x20,0x69,0x74,0x27,
-	0x73,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x64,0x2C,0x20,0x74,
-	0x68,0x65,0x20,0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x64,
-	0x6F,0x65,0x73,0x6E,0x27,0x74,0x20,0x6F,0x76,0x65,0x72,0x77,
-	0x72,0x69,0x74,0x65,0x3D,0x64,0x61,0x74,0x61,0x20,0x77,0x69,
-	0x74,0x68,0x20,0x6E,0x69,0x6C,0x2D,0x69,0x6E,0x66,0x6F,0x72,
-	0x6D,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x6F,0x6E,0x6C,0x79,
-	0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,
-	0x20,0x6F,0x72,0x20,0x61,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,
-	0x20,0x3C,0x3E,0x20,0x30,0x2E,0x01,0x3E,0x40,0x3E,0x54,0x68,
-	0x65,0x20,0x63,0x75,0x74,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,
-	0x6F,0x6E,0x73,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6C,0x69,
-	0x6B,0x65,0x20,0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x77,
-	0x69,0x74,0x68,0x20,0x7A,0x65,0x72,0x6F,0x2D,0x64,0x61,0x74,
-	0x61,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x6D,0x65,0x61,0x6E,
-	0x73,0x3B,0x74,0x68,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x63,
-	0x75,0x74,0x74,0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x63,0x6F,
-	0x6E,0x74,0x72,0x6F,0x6C,0x6C,0x65,0x64,0x20,0x77,0x69,0x74,
-	0x68,0x20,0x50,0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x20,0x28,
-	0x6F,0x72,0x20,0x54,0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x29,
-	0x2E,0x3C,0x3E,0x57,0x68,0x65,0x6E,0x20,0x79,0x6F,0x75,0x20,
-	0x63,0x6F,0x70,0x79,0x20,0x64,0x61,0x74,0x61,0x20,0x77,0x69,
-	0x74,0x68,0x20,0x6D,0x61,0x73,0x6B,0x69,0x6E,0x67,0x2C,0x20,
-	0x74,0x68,0x65,0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x64,
-	0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x61,0x72,0x65,0x20,0x6E,
-	0x6F,0x74,0x43,0x63,0x6C,0x65,0x61,0x72,0x65,0x64,0x20,0x69,
-	0x6E,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75,
-	0x66,0x66,0x65,0x72,0x2E,0x20,0x28,0x4D,0x61,0x6B,0x69,0x6E,
-	0x67,0x20,0x69,0x74,0x20,0x70,0x6F,0x73,0x73,0x69,0x62,0x6C,
-	0x65,0x20,0x74,0x6F,0x20,0x63,0x6F,0x6C,0x6C,0x65,0x63,0x74,
-	0x20,0x64,0x61,0x74,0x61,0x20,0x66,0x72,0x6F,0x6D,0x27,0x73,
-	0x65,0x76,0x65,0x72,0x61,0x6C,0x20,0x6C,0x6F,0x63,0x61,0x74,
-	0x69,0x6F,0x6E,0x73,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74,0x68,
-	0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65,0x72,
-	0x2E,0x29,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,
+	0x30,0x32,0x43,0x49,0x66,0x20,0x74,0x68,0x65,0x20,0x61,0x75,
+	0x74,0x6F,0x20,0x73,0x61,0x76,0x65,0x20,0x69,0x73,0x20,0x6F,
+	0x6E,0x2C,0x20,0x46,0x54,0x32,0x20,0x77,0x69,0x6C,0x6C,0x20,
+	0x75,0x70,0x64,0x61,0x74,0x65,0x20,0x74,0x68,0x65,0x20,0x63,
+	0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,
+	0x20,0x66,0x69,0x6C,0x65,0x20,0x77,0x68,0x65,0x6E,0x15,0x79,
+	0x6F,0x75,0x20,0x65,0x78,0x69,0x74,0x20,0x74,0x68,0x65,0x20,
+	0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x25,0x40,0x58,
+	0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,
+	0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x49,
+	0x2F,0x4F,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x73,0x3A,0x01,
+	0x3E,0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,
+	0x31,0x49,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,
+	0x6F,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x35,0x53,0x65,0x6C,0x65,0x63,0x74,0x73,0x20,
+	0x77,0x68,0x61,0x74,0x20,0x74,0x79,0x70,0x65,0x20,0x6F,0x66,
+	0x20,0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69,0x6E,0x67,0x20,
+	0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,
+	0x6E,0x20,0x74,0x6F,0x20,0x75,0x73,0x65,0x2E,0x45,0x22,0x4E,
+	0x6F,0x6E,0x65,0x22,0x20,0x75,0x73,0x65,0x73,0x20,0x6E,0x6F,
+	0x20,0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,
+	0x6F,0x6E,0x20,0x28,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20,
+	0x6E,0x65,0x69,0x67,0x68,0x62,0x6F,0x72,0x29,0x2C,0x20,0x77,
+	0x68,0x69,0x63,0x68,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,
+	0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,0x49,0x61,0x6C,0x69,0x61,
+	0x73,0x69,0x6E,0x67,0x20,0x28,0x6E,0x6F,0x69,0x73,0x65,0x29,
+	0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E,
+	0x64,0x2E,0x20,0x22,0x4C,0x69,0x6E,0x65,0x61,0x72,0x22,0x20,
+	0x69,0x73,0x20,0x77,0x68,0x61,0x74,0x20,0x72,0x65,0x61,0x6C,
+	0x20,0x46,0x54,0x32,0x20,0x75,0x73,0x65,0x73,0x2C,0x20,0x77,
+	0x68,0x69,0x63,0x68,0x20,0x69,0x73,0x20,0x61,0x47,0x6D,0x65,
+	0x64,0x69,0x6F,0x63,0x72,0x65,0x20,0x69,0x6E,0x74,0x65,0x72,
+	0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,0x79,0x70,
+	0x65,0x2E,0x20,0x22,0x57,0x69,0x6E,0x64,0x6F,0x77,0x65,0x64,
+	0x2D,0x73,0x69,0x6E,0x63,0x22,0x20,0x69,0x73,0x20,0x74,0x68,
+	0x65,0x20,0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x65,
+	0x64,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x48,0x66,0x6F,
+	0x72,0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x73,0x74,0x20,0x61,
+	0x75,0x64,0x69,0x6F,0x20,0x71,0x75,0x61,0x6C,0x69,0x74,0x79,
+	0x2C,0x20,0x61,0x6C,0x74,0x68,0x6F,0x75,0x67,0x68,0x20,0x69,
+	0x74,0x20,0x6D,0x61,0x79,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69,
+	0x6D,0x65,0x73,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x74,0x6F,
+	0x6F,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x65,0x64,0x2A,0x6F,
+	0x6E,0x20,0x6C,0x6F,0x77,0x2D,0x71,0x75,0x61,0x6C,0x69,0x74,
+	0x79,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x20,0x28,0x66,
+	0x2E,0x65,0x78,0x2E,0x20,0x41,0x6D,0x69,0x67,0x61,0x20,0x4D,
+	0x4F,0x44,0x73,0x29,0x2E,0x00,0x1A,0x3E,0x40,0x58,0x30,0x34,
+	0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65,
+	0x20,0x72,0x61,0x6D,0x70,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40,
+	0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3B,0x45,0x6E,
+	0x61,0x62,0x6C,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x61,0x6E,
+	0x74,0x69,0x2D,0x63,0x6C,0x69,0x63,0x6B,0x20,0x73,0x79,0x73,
+	0x74,0x65,0x6D,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x61,
+	0x75,0x64,0x69,0x6F,0x20,0x6D,0x69,0x78,0x65,0x72,0x20,0x28,
+	0x46,0x54,0x32,0x2E,0x30,0x38,0x2B,0x29,0x2E,0x3B,0x50,0x6C,
+	0x65,0x61,0x73,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x74,0x68,
+	0x61,0x74,0x20,0x6F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,0x20,
+	0x46,0x54,0x32,0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x6C,0x6F,
+	0x61,0x64,0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6F,0x6E,0x66,
+	0x69,0x67,0x20,0x65,0x6E,0x74,0x72,0x79,0x2C,0x0B,0x63,0x6C,
+	0x6F,0x6E,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x2E,0x00,0x19,0x3E,
+	0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x6D,
+	0x70,0x6C,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x3A,
+	0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,
+	0x46,0x41,0x6D,0x70,0x6C,0x69,0x66,0x69,0x65,0x73,0x20,0x74,
+	0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x77,0x68,
+	0x65,0x6E,0x20,0x6D,0x69,0x78,0x69,0x6E,0x67,0x2E,0x20,0x49,
+	0x66,0x20,0x79,0x6F,0x75,0x20,0x73,0x65,0x74,0x20,0x74,0x68,
+	0x69,0x73,0x20,0x6F,0x6E,0x65,0x20,0x74,0x6F,0x6F,0x20,0x68,
+	0x69,0x67,0x68,0x2C,0x20,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x3A,
+	0x67,0x65,0x74,0x20,0x64,0x69,0x73,0x74,0x6F,0x72,0x74,0x69,
+	0x6F,0x6E,0x2E,0x20,0x33,0x32,0x58,0x20,0x65,0x71,0x75,0x61,
+	0x6C,0x73,0x20,0x66,0x75,0x6C,0x6C,0x20,0x61,0x6D,0x70,0x6C,
+	0x69,0x74,0x75,0x64,0x65,0x20,0x66,0x6F,0x72,0x20,0x6F,0x6E,
+	0x65,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x2E,0x00,0x1B,
+	0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,
+	0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62,
+	0x6C,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x40,0x54,0x68,0x65,0x20,0x6C,0x69,0x6E,0x65,
+	0x61,0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,
+	0x20,0x74,0x61,0x62,0x6C,0x65,0x20,0x6D,0x61,0x6B,0x65,0x73,
+	0x20,0x61,0x6C,0x6C,0x20,0x70,0x69,0x74,0x63,0x68,0x20,0x62,
+	0x65,0x6E,0x64,0x73,0x20,0x72,0x75,0x6E,0x20,0x69,0x6E,0x20,
+	0x63,0x6F,0x6E,0x73,0x74,0x61,0x6E,0x74,0x3F,0x73,0x70,0x65,
+	0x65,0x64,0x2C,0x20,0x69,0x6E,0x64,0x65,0x70,0x65,0x6E,0x64,
+	0x65,0x6E,0x74,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63,
+	0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x66,0x72,0x65,0x71,0x75,
+	0x65,0x6E,0x63,0x79,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75,
+	0x20,0x73,0x77,0x69,0x74,0x63,0x68,0x20,0x74,0x68,0x69,0x73,
+	0x41,0x6F,0x6E,0x65,0x2C,0x20,0x6F,0x6E,0x20,0x61,0x20,0x66,
+	0x69,0x6E,0x69,0x73,0x68,0x65,0x64,0x20,0x73,0x6F,0x6E,0x67,
+	0x2C,0x20,0x69,0x74,0x20,0x6D,0x69,0x67,0x68,0x74,0x20,0x73,
+	0x6F,0x75,0x6E,0x64,0x20,0x73,0x74,0x72,0x61,0x6E,0x67,0x65,
+	0x20,0x69,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E,
+	0x64,0x20,0x75,0x73,0x65,0x73,0x0D,0x70,0x6F,0x72,0x74,0x61,
+	0x6D,0x65,0x6E,0x74,0x6F,0x65,0x73,0x2E,0x00,0x20,0x40,0x58,
+	0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,
+	0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x4C,
+	0x61,0x79,0x6F,0x75,0x74,0x3A,0x01,0x3E,0x29,0x3E,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x20,0x6C,0x61,0x79,0x6F,0x75,0x74,0x2C,0x20,
+	0x68,0x65,0x78,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x69,0x6E,
+	0x67,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
+	0x30,0x32,0x41,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x75,0x73,
+	0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x73,0x20,0x74,
+	0x68,0x61,0x74,0x20,0x61,0x72,0x65,0x20,0x6C,0x6F,0x6E,0x67,
+	0x65,0x72,0x20,0x74,0x68,0x61,0x6E,0x20,0x39,0x39,0x20,0x6C,
+	0x69,0x6E,0x65,0x73,0x2C,0x20,0x79,0x6F,0x75,0x20,0x73,0x68,
+	0x6F,0x75,0x6C,0x64,0x20,0x75,0x73,0x65,0x45,0x68,0x65,0x78,
+	0x20,0x63,0x6F,0x75,0x6E,0x74,0x69,0x6E,0x67,0x20,0x73,0x69,
+	0x6E,0x63,0x65,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61,0x72,
+	0x65,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x32,0x20,0x64,0x69,0x67,
+	0x69,0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6C,
+	0x69,0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x63,
+	0x6F,0x6C,0x75,0x6D,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65,
+	0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,
+	0x30,0x32,0x43,0x22,0x53,0x74,0x64,0x2E,0x22,0x20,0x28,0x73,
+	0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x29,0x20,0x77,0x69,0x6C,
+	0x6C,0x20,0x73,0x68,0x6F,0x77,0x20,0x74,0x68,0x65,0x20,0x73,
+	0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,0x69,0x6E,0x74,0x73,
+	0x20,0x61,0x73,0x20,0x70,0x69,0x78,0x65,0x6C,0x73,0x20,0x28,
+	0x6C,0x69,0x6B,0x65,0x20,0x46,0x54,0x32,0x29,0x2E,0x3D,0x22,
+	0x4C,0x69,0x6E,0x65,0x64,0x22,0x20,0x77,0x69,0x6C,0x6C,0x20,
+	0x64,0x72,0x61,0x77,0x20,0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,
+	0x6C,0x61,0x74,0x65,0x64,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,
+	0x73,0x20,0x28,0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x69,0x6E,
+	0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x2E,
+	0x00,0x27,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,
+	0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,
+	0x6E,0x2C,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,
+	0x65,0x6F,0x75,0x73,0x3A,0x01,0x3E,0x15,0x3E,0x40,0x58,0x30,
+	0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x53,0x79,0x6E,0x63,
+	0x20,0x6F,0x66,0x66,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,
+	0x40,0x43,0x30,0x30,0x32,0x3F,0x54,0x65,0x6C,0x6C,0x73,0x20,
+	0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,
+	0x74,0x6F,0x20,0x6E,0x6F,0x74,0x20,0x75,0x73,0x65,0x20,0x56,
+	0x53,0x79,0x6E,0x63,0x20,0x66,0x6F,0x72,0x20,0x76,0x69,0x64,
+	0x65,0x6F,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75,0x72,0x20,
+	0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,0x40,0x72,0x65,
+	0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,0x65,0x20,0x69,
+	0x73,0x20,0x6E,0x6F,0x74,0x20,0x36,0x30,0x48,0x7A,0x20,0x28,
+	0x6F,0x72,0x20,0x35,0x39,0x48,0x7A,0x29,0x2C,0x20,0x74,0x68,
+	0x65,0x6E,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x69,0x73,0x20,
+	0x61,0x6C,0x77,0x61,0x79,0x73,0x20,0x6F,0x66,0x66,0x20,0x66,
+	0x6F,0x72,0x45,0x74,0x68,0x69,0x73,0x20,0x70,0x72,0x6F,0x67,
+	0x72,0x61,0x6D,0x2E,0x20,0x4E,0x6F,0x74,0x20,0x68,0x61,0x76,
+	0x69,0x6E,0x67,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x77,0x69,
+	0x6C,0x6C,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,
+	0x20,0x6C,0x65,0x73,0x73,0x20,0x69,0x6E,0x70,0x75,0x74,0x2F,
+	0x76,0x69,0x64,0x65,0x6F,0x20,0x64,0x65,0x6C,0x61,0x79,0x2C,
+	0x1E,0x62,0x75,0x74,0x20,0x61,0x6C,0x73,0x6F,0x20,0x70,0x6F,
+	0x74,0x65,0x6E,0x74,0x69,0x61,0x6C,0x20,0x73,0x74,0x75,0x74,
+	0x74,0x65,0x72,0x69,0x6E,0x67,0x2E,0x00,0x15,0x3E,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x74,0x72,0x65,
+	0x74,0x63,0x68,0x65,0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x4A,0x4D,0x61,0x6B,0x65,0x73,
+	0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20,
+	0x6D,0x6F,0x64,0x65,0x20,0x63,0x6F,0x6D,0x70,0x6C,0x65,0x74,
+	0x65,0x6C,0x79,0x20,0x73,0x74,0x72,0x65,0x74,0x63,0x68,0x20,
+	0x6F,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61,0x67,
+	0x65,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x63,0x61,0x6E,0x20,
+	0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,0x4E,0x61,0x6C,
+	0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x28,0x75,0x6E,0x65,0x76,
+	0x65,0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x77,0x69,0x64,
+	0x74,0x68,0x73,0x29,0x20,0x69,0x66,0x20,0x74,0x68,0x65,0x20,
+	0x61,0x73,0x70,0x65,0x63,0x74,0x20,0x72,0x61,0x74,0x69,0x6F,
+	0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x63,0x72,0x65,
+	0x65,0x6E,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x31,0x36,
+	0x3A,0x31,0x30,0x2E,0x52,0x54,0x68,0x65,0x20,0x22,0x50,0x69,
+	0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x22,0x20,
+	0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x20,0x63,0x61,0x6E,0x20,
+	0x68,0x65,0x6C,0x70,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,
+	0x69,0x73,0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x6D,
+	0x61,0x6B,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61,
+	0x67,0x65,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C,0x75,0x72,
+	0x72,0x79,0x2E,0x01,0x20,0x18,0x3E,0x40,0x58,0x30,0x34,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x50,0x69,0x78,0x65,0x6C,0x20,0x66,
+	0x69,0x6C,0x74,0x65,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,
+	0x30,0x40,0x43,0x30,0x30,0x32,0x52,0x41,0x70,0x70,0x6C,0x69,
+	0x65,0x73,0x20,0x61,0x6E,0x20,0x61,0x6E,0x74,0x69,0x2D,0x61,
+	0x6C,0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x73,0x75,0x62,0x70,
+	0x69,0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x20,
+	0x74,0x68,0x61,0x74,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,
+	0x20,0x77,0x68,0x65,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x69,
+	0x6E,0x64,0x6F,0x77,0x20,0x69,0x73,0x20,0x75,0x70,0x73,0x63,
+	0x61,0x6C,0x65,0x64,0x2E,0x3B,0x50,0x6C,0x65,0x61,0x73,0x65,
+	0x20,0x6B,0x65,0x65,0x70,0x20,0x69,0x6E,0x20,0x6D,0x69,0x6E,
+	0x64,0x20,0x74,0x68,0x61,0x74,0x20,0x74,0x68,0x69,0x73,0x20,
+	0x77,0x69,0x6C,0x6C,0x20,0x6D,0x61,0x6B,0x65,0x20,0x70,0x69,
+	0x78,0x65,0x6C,0x73,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C,
+	0x75,0x72,0x72,0x79,0x2E,0x00,0x23,0x40,0x58,0x30,0x32,0x30,
+	0x40,0x43,0x30,0x30,0x31,0x41,0x64,0x76,0x61,0x6E,0x63,0x65,
+	0x64,0x20,0x65,0x64,0x69,0x74,0x20,0x66,0x75,0x6E,0x63,0x74,
+	0x69,0x6F,0x6E,0x73,0x3A,0x20,0x01,0x3E,0x1E,0x3E,0x40,0x58,
+	0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x70,0x79,
+	0x2F,0x50,0x61,0x73,0x74,0x65,0x20,0x6D,0x61,0x73,0x6B,0x69,
+	0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,
+	0x30,0x30,0x32,0x37,0x54,0x68,0x65,0x20,0x6D,0x61,0x73,0x6B,
+	0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20,
+	0x66,0x6F,0x72,0x20,0x63,0x6F,0x70,0x79,0x69,0x6E,0x67,0x2F,
+	0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x6F,0x6E,0x6C,0x79,
+	0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x6F,0x66,0x20,0x61,0x46,
+	0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63,0x65,0x6C,0x6C,0x22,0x2E,
+	0x20,0x54,0x68,0x65,0x20,0x64,0x69,0x66,0x66,0x65,0x72,0x65,
+	0x6E,0x74,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x6F,0x66,0x20,
+	0x61,0x20,0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63,0x65,0x6C,0x6C,
+	0x22,0x20,0x69,0x73,0x20,0x4E,0x6F,0x74,0x65,0x2C,0x20,0x49,
+	0x6E,0x73,0x74,0x72,0x2E,0x20,0x6E,0x72,0x2E,0x2C,0x20,0x56,
+	0x6F,0x6C,0x75,0x6D,0x65,0x2C,0x20,0x45,0x66,0x66,0x65,0x63,
+	0x74,0x20,0x6E,0x72,0x20,0x26,0x20,0x45,0x66,0x66,0x65,0x63,
+	0x74,0x20,0x64,0x61,0x74,0x61,0x2E,0x34,0x3E,0x41,0x73,0x20,
+	0x79,0x6F,0x75,0x20,0x63,0x61,0x6E,0x20,0x73,0x65,0x65,0x20,
+	0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,
+	0x77,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61,0x72,0x65,0x20,
+	0x33,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x73,0x20,0x6F,0x66,
+	0x3D,0x22,0x65,0x6E,0x61,0x62,0x6C,0x65,0x2F,0x64,0x69,0x73,
+	0x61,0x62,0x6C,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x73,
+	0x22,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x68,0x61,0x73,0x20,
+	0x74,0x68,0x65,0x20,0x6C,0x65,0x74,0x74,0x65,0x72,0x73,0x20,
+	0x43,0x2C,0x50,0x20,0x26,0x20,0x54,0x20,0x61,0x62,0x6F,0x76,
+	0x65,0x2E,0x45,0x3E,0x43,0x20,0x6D,0x65,0x61,0x6E,0x73,0x20,
+	0x63,0x6F,0x70,0x79,0x2C,0x20,0x69,0x74,0x20,0x63,0x6F,0x6E,
+	0x74,0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69,0x63,0x68,0x20,
+	0x70,0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x67,
+	0x6F,0x65,0x73,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74,0x68,0x65,
+	0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65,0x72,0x2E,
+	0x3E,0x3E,0x50,0x20,0x6D,0x65,0x61,0x6E,0x73,0x20,0x70,0x61,
+	0x73,0x74,0x65,0x20,0x61,0x6E,0x64,0x20,0x63,0x6F,0x6E,0x74,
+	0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x70,
+	0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x67,0x6F,
+	0x65,0x73,0x20,0x6F,0x75,0x74,0x20,0x66,0x72,0x6F,0x6D,0x20,
+	0x74,0x68,0x65,0x0B,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,
+	0x65,0x72,0x2E,0x45,0x3E,0x54,0x20,0x6D,0x65,0x61,0x6E,0x73,
+	0x20,0x74,0x72,0x61,0x6E,0x73,0x70,0x61,0x72,0x65,0x6E,0x63,
+	0x79,0x2E,0x20,0x49,0x66,0x20,0x69,0x74,0x27,0x73,0x20,0x65,
+	0x6E,0x61,0x62,0x6C,0x65,0x64,0x2C,0x20,0x74,0x68,0x65,0x20,
+	0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x64,0x6F,0x65,0x73,
+	0x6E,0x27,0x74,0x20,0x6F,0x76,0x65,0x72,0x77,0x72,0x69,0x74,
+	0x65,0x3D,0x64,0x61,0x74,0x61,0x20,0x77,0x69,0x74,0x68,0x20,
+	0x6E,0x69,0x6C,0x2D,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,
+	0x69,0x6F,0x6E,0x2C,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x77,0x69,
+	0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,0x20,0x6F,0x72,
+	0x20,0x61,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x3C,0x3E,
+	0x20,0x30,0x2E,0x01,0x3E,0x40,0x3E,0x54,0x68,0x65,0x20,0x63,
+	0x75,0x74,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E,0x73,
+	0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6C,0x69,0x6B,0x65,0x20,
+	0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x77,0x69,0x74,0x68,
+	0x20,0x7A,0x65,0x72,0x6F,0x2D,0x64,0x61,0x74,0x61,0x2E,0x20,
+	0x54,0x68,0x69,0x73,0x20,0x6D,0x65,0x61,0x6E,0x73,0x3B,0x74,
+	0x68,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x74,0x74,
+	0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x63,0x6F,0x6E,0x74,0x72,
+	0x6F,0x6C,0x6C,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x50,
+	0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x20,0x28,0x6F,0x72,0x20,
+	0x54,0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x29,0x2E,0x3C,0x3E,
+	0x57,0x68,0x65,0x6E,0x20,0x79,0x6F,0x75,0x20,0x63,0x6F,0x70,
+	0x79,0x20,0x64,0x61,0x74,0x61,0x20,0x77,0x69,0x74,0x68,0x20,
+	0x6D,0x61,0x73,0x6B,0x69,0x6E,0x67,0x2C,0x20,0x74,0x68,0x65,
+	0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x64,0x20,0x70,0x61,
+	0x72,0x74,0x73,0x20,0x61,0x72,0x65,0x20,0x6E,0x6F,0x74,0x43,
+	0x63,0x6C,0x65,0x61,0x72,0x65,0x64,0x20,0x69,0x6E,0x20,0x74,
+	0x68,0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65,
+	0x72,0x2E,0x20,0x28,0x4D,0x61,0x6B,0x69,0x6E,0x67,0x20,0x69,
+	0x74,0x20,0x70,0x6F,0x73,0x73,0x69,0x62,0x6C,0x65,0x20,0x74,
+	0x6F,0x20,0x63,0x6F,0x6C,0x6C,0x65,0x63,0x74,0x20,0x64,0x61,
+	0x74,0x61,0x20,0x66,0x72,0x6F,0x6D,0x27,0x73,0x65,0x76,0x65,
+	0x72,0x61,0x6C,0x20,0x6C,0x6F,0x63,0x61,0x74,0x69,0x6F,0x6E,
+	0x73,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x63,
+	0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65,0x72,0x2E,0x29,0x00,
+	0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
+	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x2A,0x2A,0x2A,0x0E,0x40,0x4C,0x50,0x72,0x6F,0x62,
-	0x6C,0x65,0x6D,0x73,0x2F,0x46,0x41,0x51,0x06,0x3E,0x40,0x58,
-	0x30,0x32,0x30,0x2A,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,
-	0x20,0x48,0x6F,0x77,0x20,0x63,0x61,0x6E,0x20,0x49,0x20,0x74,
-	0x6F,0x67,0x67,0x6C,0x65,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,
-	0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x3F,0x37,0x3E,
-	0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x50,0x72,0x65,0x73,
-	0x73,0x20,0x41,0x6C,0x74,0x2B,0x45,0x6E,0x74,0x65,0x72,0x20,
-	0x28,0x43,0x74,0x72,0x6C,0x2B,0x43,0x6D,0x64,0x2B,0x46,0x20,
-	0x61,0x6C,0x73,0x6F,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6F,
-	0x6E,0x20,0x4D,0x61,0x63,0x29,0x06,0x3E,0x40,0x58,0x30,0x32,
-	0x30,0x45,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x48,
-	0x6F,0x77,0x20,0x63,0x61,0x6E,0x20,0x49,0x20,0x6D,0x61,0x6B,
-	0x65,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,
-	0x20,0x6D,0x6F,0x64,0x65,0x20,0x73,0x74,0x72,0x65,0x74,0x63,
-	0x68,0x20,0x6F,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x77,0x68,
-	0x6F,0x6C,0x65,0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3F,0x37,
-	0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x45,0x6E,0x61,
-	0x62,0x6C,0x65,0x20,0x22,0x53,0x74,0x72,0x65,0x74,0x63,0x68,
-	0x65,0x64,0x22,0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,
-	0x67,0x20,0x2D,0x3E,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,
-	0x61,0x6E,0x65,0x6F,0x75,0x73,0x2E,0x4E,0x3E,0x40,0x58,0x30,
-	0x33,0x35,0x54,0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,
-	0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,0x20,0x75,0x6E,
-	0x65,0x76,0x65,0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x77,
-	0x69,0x64,0x74,0x68,0x73,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,
-	0x75,0x20,0x77,0x61,0x6E,0x74,0x20,0x74,0x6F,0x20,0x66,0x69,
-	0x78,0x20,0x74,0x68,0x69,0x73,0x2C,0x20,0x65,0x6E,0x61,0x62,
-	0x6C,0x65,0x3D,0x22,0x50,0x69,0x78,0x65,0x6C,0x20,0x66,0x69,
-	0x6C,0x74,0x65,0x72,0x22,0x20,0x28,0x74,0x68,0x6F,0x75,0x67,
-	0x68,0x20,0x74,0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,
-	0x6D,0x61,0x6B,0x65,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61,
-	0x67,0x65,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C,0x75,0x72,
-	0x72,0x79,0x29,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x27,
-	0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20,0x63,
-	0x61,0x6E,0x27,0x74,0x20,0x75,0x73,0x65,0x20,0x41,0x6C,0x74,
-	0x2B,0x46,0x34,0x20,0x61,0x6E,0x64,0x20,0x41,0x6C,0x74,0x2B,
-	0x46,0x35,0x21,0x4E,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,
-	0x20,0x57,0x69,0x6E,0x64,0x6F,0x77,0x73,0x3A,0x20,0x49,0x66,
-	0x20,0x79,0x6F,0x75,0x20,0x68,0x61,0x76,0x65,0x20,0x47,0x65,
-	0x46,0x6F,0x72,0x63,0x65,0x20,0x45,0x78,0x70,0x65,0x72,0x69,
-	0x65,0x6E,0x63,0x65,0x20,0x69,0x6E,0x73,0x74,0x61,0x6C,0x6C,
-	0x65,0x64,0x2C,0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65,0x64,
-	0x20,0x74,0x6F,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2B,0x3E,
-	0x40,0x58,0x30,0x33,0x35,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,
-	0x62,0x69,0x6E,0x64,0x69,0x6E,0x67,0x73,0x20,0x69,0x6E,0x20,
-	0x69,0x74,0x73,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,
-	0x20,0x70,0x61,0x67,0x65,0x2E,0x57,0x3E,0x6D,0x61,0x63,0x4F,
-	0x53,0x2F,0x4F,0x53,0x20,0x58,0x3A,0x20,0x43,0x68,0x61,0x6E,
-	0x67,0x65,0x20,0x41,0x6C,0x74,0x2B,0x46,0x34,0x2F,0x41,0x6C,
-	0x74,0x2B,0x46,0x35,0x20,0x6B,0x65,0x79,0x73,0x20,0x69,0x6E,
-	0x20,0x74,0x68,0x65,0x20,0x4F,0x53,0x20,0x74,0x6F,0x20,0x73,
-	0x6F,0x6D,0x65,0x74,0x68,0x69,0x6E,0x67,0x20,0x65,0x6C,0x73,
-	0x65,0x2E,0x20,0x41,0x6C,0x73,0x6F,0x20,0x66,0x6F,0x72,0x20,
-	0x47,0x4E,0x55,0x2F,0x4C,0x69,0x6E,0x75,0x78,0x2E,0x06,0x3E,
-	0x40,0x58,0x30,0x32,0x30,0x2B,0x3E,0x40,0x43,0x30,0x30,0x31,
-	0x51,0x3A,0x20,0x54,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,
-	0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x20,0x69,0x73,0x20,0x64,
-	0x65,0x6C,0x61,0x79,0x65,0x64,0x2F,0x6C,0x61,0x67,0x67,0x79,
-	0x21,0x44,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x4D,
-	0x61,0x6B,0x65,0x20,0x73,0x75,0x72,0x65,0x20,0x22,0x53,0x6F,
-	0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,
-	0x22,0x20,0x69,0x73,0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,
-	0x64,0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20,
-	0x2D,0x3E,0x20,0x4C,0x61,0x79,0x6F,0x75,0x74,0x2E,0x4B,0x3E,
-	0x40,0x58,0x30,0x33,0x35,0x41,0x6C,0x74,0x65,0x72,0x6E,0x61,
-	0x74,0x69,0x76,0x65,0x6C,0x79,0x2C,0x20,0x79,0x6F,0x75,0x20,
-	0x63,0x61,0x6E,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x20,0x22,
-	0x56,0x53,0x79,0x6E,0x63,0x20,0x6F,0x66,0x66,0x22,0x20,0x69,
+	0x2A,0x2A,0x0E,0x40,0x4C,0x50,0x72,0x6F,0x62,0x6C,0x65,0x6D,
+	0x73,0x2F,0x46,0x41,0x51,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,
+	0x2A,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x48,0x6F,
+	0x77,0x20,0x63,0x61,0x6E,0x20,0x49,0x20,0x74,0x6F,0x67,0x67,
+	0x6C,0x65,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,
+	0x6E,0x20,0x6D,0x6F,0x64,0x65,0x3F,0x37,0x3E,0x40,0x43,0x30,
+	0x30,0x32,0x41,0x3A,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x41,
+	0x6C,0x74,0x2B,0x45,0x6E,0x74,0x65,0x72,0x20,0x28,0x43,0x74,
+	0x72,0x6C,0x2B,0x43,0x6D,0x64,0x2B,0x46,0x20,0x61,0x6C,0x73,
+	0x6F,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6F,0x6E,0x20,0x4D,
+	0x61,0x63,0x29,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x45,0x3E,
+	0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x48,0x6F,0x77,0x20,
+	0x63,0x61,0x6E,0x20,0x49,0x20,0x6D,0x61,0x6B,0x65,0x20,0x66,
+	0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F,
+	0x64,0x65,0x20,0x73,0x74,0x72,0x65,0x74,0x63,0x68,0x20,0x6F,
+	0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x77,0x68,0x6F,0x6C,0x65,
+	0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3F,0x37,0x3E,0x40,0x43,
+	0x30,0x30,0x32,0x41,0x3A,0x20,0x45,0x6E,0x61,0x62,0x6C,0x65,
+	0x20,0x22,0x53,0x74,0x72,0x65,0x74,0x63,0x68,0x65,0x64,0x22,
+	0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D,
+	0x3E,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,
+	0x6F,0x75,0x73,0x2E,0x4E,0x3E,0x40,0x58,0x30,0x33,0x35,0x54,
+	0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x73,
+	0x75,0x6C,0x74,0x20,0x69,0x6E,0x20,0x75,0x6E,0x65,0x76,0x65,
+	0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x77,0x69,0x64,0x74,
+	0x68,0x73,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x77,
+	0x61,0x6E,0x74,0x20,0x74,0x6F,0x20,0x66,0x69,0x78,0x20,0x74,
+	0x68,0x69,0x73,0x2C,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x3D,
+	0x22,0x50,0x69,0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,
+	0x72,0x22,0x20,0x28,0x74,0x68,0x6F,0x75,0x67,0x68,0x20,0x74,
+	0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6D,0x61,0x6B,
+	0x65,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61,0x67,0x65,0x20,
+	0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C,0x75,0x72,0x72,0x79,0x29,
+	0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x27,0x3E,0x40,0x43,
+	0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20,0x63,0x61,0x6E,0x27,
+	0x74,0x20,0x75,0x73,0x65,0x20,0x41,0x6C,0x74,0x2B,0x46,0x34,
+	0x20,0x61,0x6E,0x64,0x20,0x41,0x6C,0x74,0x2B,0x46,0x35,0x21,
+	0x4E,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x57,0x69,
+	0x6E,0x64,0x6F,0x77,0x73,0x3A,0x20,0x49,0x66,0x20,0x79,0x6F,
+	0x75,0x20,0x68,0x61,0x76,0x65,0x20,0x47,0x65,0x46,0x6F,0x72,
+	0x63,0x65,0x20,0x45,0x78,0x70,0x65,0x72,0x69,0x65,0x6E,0x63,
+	0x65,0x20,0x69,0x6E,0x73,0x74,0x61,0x6C,0x6C,0x65,0x64,0x2C,
+	0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65,0x64,0x20,0x74,0x6F,
+	0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2B,0x3E,0x40,0x58,0x30,
+	0x33,0x35,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x69,0x6E,
+	0x64,0x69,0x6E,0x67,0x73,0x20,0x69,0x6E,0x20,0x69,0x74,0x73,
+	0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x20,0x70,0x61,
+	0x67,0x65,0x2E,0x57,0x3E,0x6D,0x61,0x63,0x4F,0x53,0x2F,0x4F,
+	0x53,0x20,0x58,0x3A,0x20,0x43,0x68,0x61,0x6E,0x67,0x65,0x20,
+	0x41,0x6C,0x74,0x2B,0x46,0x34,0x2F,0x41,0x6C,0x74,0x2B,0x46,
+	0x35,0x20,0x6B,0x65,0x79,0x73,0x20,0x69,0x6E,0x20,0x74,0x68,
+	0x65,0x20,0x4F,0x53,0x20,0x74,0x6F,0x20,0x73,0x6F,0x6D,0x65,
+	0x74,0x68,0x69,0x6E,0x67,0x20,0x65,0x6C,0x73,0x65,0x2E,0x20,
+	0x41,0x6C,0x73,0x6F,0x20,0x66,0x6F,0x72,0x20,0x47,0x4E,0x55,
+	0x2F,0x4C,0x69,0x6E,0x75,0x78,0x2E,0x06,0x3E,0x40,0x58,0x30,
+	0x32,0x30,0x2B,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,
+	0x54,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x63,0x75,
+	0x72,0x73,0x6F,0x72,0x20,0x69,0x73,0x20,0x64,0x65,0x6C,0x61,
+	0x79,0x65,0x64,0x2F,0x6C,0x61,0x67,0x67,0x79,0x21,0x44,0x3E,
+	0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x4D,0x61,0x6B,0x65,
+	0x20,0x73,0x75,0x72,0x65,0x20,0x22,0x53,0x6F,0x66,0x74,0x77,
+	0x61,0x72,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x22,0x20,0x69,
+	0x73,0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x64,0x20,0x69,
 	0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D,0x3E,0x20,
-	0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,
-	0x73,0x2E,0x46,0x3E,0x54,0x68,0x69,0x73,0x20,0x68,0x6F,0x77,
-	0x65,0x76,0x65,0x72,0x2C,0x20,0x77,0x69,0x6C,0x6C,0x20,0x69,
-	0x6E,0x74,0x72,0x6F,0x64,0x75,0x63,0x65,0x20,0x73,0x74,0x75,
-	0x74,0x74,0x65,0x72,0x69,0x6E,0x67,0x20,0x62,0x65,0x63,0x61,
-	0x75,0x73,0x65,0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x6E,0x64,
-	0x65,0x72,0x69,0x6E,0x67,0x20,0x72,0x61,0x74,0x65,0x20,0x69,
-	0x73,0x22,0x3E,0x6E,0x6F,0x74,0x20,0x65,0x78,0x61,0x63,0x74,
-	0x20,0x74,0x6F,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,
-	0x69,0x74,0x6F,0x72,0x27,0x73,0x20,0x72,0x61,0x74,0x65,0x2E,
-	0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x33,0x3E,0x40,0x43,0x30,
-	0x30,0x31,0x51,0x3A,0x20,0x57,0x69,0x6C,0x6C,0x20,0x79,0x6F,
-	0x75,0x20,0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x20,
-	0x4D,0x49,0x44,0x49,0x20,0x6F,0x75,0x74,0x20,0x66,0x75,0x6E,
-	0x63,0x74,0x69,0x6F,0x6E,0x61,0x6C,0x69,0x74,0x79,0x3F,0x4D,
-	0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x4E,0x6F,0x2C,
-	0x20,0x73,0x6F,0x72,0x72,0x79,0x2E,0x20,0x54,0x68,0x69,0x73,
-	0x20,0x69,0x73,0x20,0x76,0x65,0x72,0x79,0x20,0x64,0x69,0x66,
-	0x66,0x69,0x63,0x75,0x6C,0x74,0x20,0x74,0x6F,0x20,0x69,0x6D,
-	0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x20,0x63,0x6F,0x72,0x72,
-	0x65,0x63,0x74,0x6C,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,0x68,
-	0x61,0x76,0x69,0x6E,0x67,0x3C,0x3E,0x40,0x58,0x30,0x33,0x35,
-	0x68,0x69,0x67,0x68,0x65,0x72,0x20,0x61,0x75,0x64,0x69,0x6F,
-	0x20,0x62,0x75,0x66,0x66,0x65,0x72,0x20,0x73,0x69,0x7A,0x65,
-	0x73,0x20,0x28,0x62,0x75,0x66,0x66,0x65,0x72,0x65,0x64,0x20,
-	0x72,0x65,0x70,0x6C,0x61,0x79,0x65,0x72,0x20,0x74,0x69,0x63,
-	0x6B,0x73,0x29,0x2E,0x2E,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,
-	0x30,0x30,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x57,
-	0x68,0x65,0x72,0x65,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,
-	0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,
-	0x6E,0x20,0x66,0x69,0x6C,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,
-	0x64,0x3F,0x3F,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,
-	0x57,0x69,0x6E,0x64,0x6F,0x77,0x73,0x3A,0x20,0x5C,0x55,0x73,
-	0x65,0x72,0x73,0x5C,0x55,0x53,0x45,0x52,0x5C,0x41,0x70,0x70,
-	0x44,0x61,0x74,0x61,0x5C,0x52,0x6F,0x61,0x6D,0x69,0x6E,0x67,
-	0x5C,0x46,0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x5C,0x46,
-	0x54,0x32,0x2E,0x43,0x46,0x47,0x45,0x3E,0x40,0x58,0x30,0x33,
-	0x35,0x4F,0x53,0x20,0x58,0x3A,0x20,0x2F,0x55,0x73,0x65,0x72,
-	0x73,0x2F,0x55,0x53,0x45,0x52,0x2F,0x4C,0x69,0x62,0x72,0x61,
-	0x72,0x79,0x2F,0x41,0x70,0x70,0x6C,0x69,0x63,0x61,0x74,0x69,
-	0x6F,0x6E,0x20,0x53,0x75,0x70,0x70,0x6F,0x72,0x74,0x2F,0x46,
-	0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32,
-	0x2E,0x43,0x46,0x47,0x2F,0x47,0x4E,0x55,0x2F,0x4C,0x69,0x6E,
-	0x75,0x78,0x3A,0x20,0x2F,0x68,0x6F,0x6D,0x65,0x2F,0x55,0x53,
-	0x45,0x52,0x2F,0x2E,0x63,0x6F,0x6E,0x66,0x69,0x67,0x2F,0x46,
-	0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32,
-	0x2E,0x43,0x46,0x47,0x01,0x3E,0x48,0x49,0x74,0x20,0x77,0x69,
-	0x6C,0x6C,0x20,0x62,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64,
-	0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,
-	0x72,0x61,0x6D,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x6F,0x72,
-	0x79,0x20,0x69,0x66,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x74,
-	0x68,0x20,0x63,0x6F,0x75,0x6C,0x64,0x6E,0x27,0x74,0x20,0x62,
-	0x65,0x20,0x75,0x73,0x65,0x64,0x2E,0x4D,0x49,0x66,0x20,0x79,
-	0x6F,0x75,0x20,0x70,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x63,
-	0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,
-	0x20,0x66,0x69,0x6C,0x65,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,
-	0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x64,0x69,0x72,
-	0x65,0x63,0x74,0x6F,0x72,0x79,0x2C,0x20,0x69,0x74,0x20,0x77,
-	0x69,0x6C,0x6C,0x20,0x72,0x65,0x61,0x64,0x20,0x74,0x68,0x61,
-	0x74,0x4A,0x6F,0x6E,0x65,0x20,0x61,0x6E,0x64,0x20,0x6E,0x6F,
-	0x74,0x20,0x61,0x74,0x74,0x65,0x6D,0x70,0x74,0x20,0x74,0x6F,
-	0x20,0x63,0x72,0x65,0x61,0x74,0x65,0x20,0x63,0x6F,0x6E,0x66,
-	0x69,0x67,0x20,0x64,0x69,0x72,0x73,0x20,0x66,0x6F,0x72,0x20,
-	0x74,0x68,0x65,0x20,0x4F,0x53,0x20,0x75,0x73,0x65,0x72,0x2E,
-	0x20,0x28,0x70,0x6F,0x72,0x74,0x61,0x62,0x6C,0x65,0x20,0x6D,
-	0x6F,0x64,0x65,0x29,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x42,
-	0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x43,0x61,0x6E,
-	0x20,0x74,0x68,0x65,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x20,0x72,
-	0x65,0x61,0x64,0x20,0x46,0x54,0x32,0x2E,0x43,0x46,0x47,0x20,
-	0x66,0x72,0x6F,0x6D,0x20,0x72,0x65,0x61,0x6C,0x20,0x46,0x54,
-	0x32,0x2C,0x20,0x61,0x6E,0x64,0x20,0x76,0x69,0x63,0x65,0x20,
-	0x76,0x65,0x72,0x73,0x61,0x3F,0x4C,0x3E,0x40,0x43,0x30,0x30,
-	0x32,0x41,0x3A,0x20,0x59,0x65,0x73,0x2C,0x20,0x69,0x74,0x20,
-	0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x77,0x6F,0x72,0x6B,0x20,
-	0x6A,0x75,0x73,0x74,0x20,0x66,0x69,0x6E,0x65,0x2E,0x20,0x50,
-	0x75,0x74,0x20,0x69,0x74,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,
-	0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x6F,0x72,0x79,0x20,0x73,
-	0x68,0x6F,0x77,0x6E,0x20,0x61,0x62,0x6F,0x76,0x65,0x2E,0x06,
-	0x3E,0x40,0x58,0x30,0x32,0x30,0x52,0x3E,0x40,0x43,0x30,0x30,
-	0x31,0x51,0x3A,0x20,0x53,0x6D,0x70,0x2E,0x20,0x45,0x64,0x2E,
-	0x3A,0x20,0x57,0x68,0x69,0x6C,0x65,0x20,0x7A,0x6F,0x6F,0x6D,
-	0x69,0x6E,0x67,0x20,0x69,0x6E,0x2C,0x20,0x49,0x20,0x73,0x6F,
-	0x6D,0x65,0x74,0x69,0x6D,0x65,0x73,0x20,0x63,0x61,0x6E,0x27,
-	0x74,0x20,0x6D,0x61,0x72,0x6B,0x20,0x74,0x68,0x65,0x20,0x6C,
-	0x61,0x73,0x74,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,
-	0x6F,0x69,0x6E,0x74,0x21,0x47,0x3E,0x40,0x43,0x30,0x30,0x32,
-	0x41,0x3A,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x6E,
-	0x6F,0x72,0x6D,0x61,0x6C,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,
-	0x69,0x73,0x20,0x61,0x20,0x6C,0x69,0x6D,0x69,0x74,0x61,0x74,
-	0x69,0x6F,0x6E,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6E,
-	0x61,0x74,0x75,0x72,0x65,0x20,0x6F,0x66,0x20,0x73,0x63,0x61,
-	0x6C,0x69,0x6E,0x67,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,
-	0x17,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20,
-	0x66,0x6F,0x75,0x6E,0x64,0x20,0x61,0x20,0x62,0x75,0x67,0x21,
-	0x4C,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x50,0x6C,
-	0x65,0x61,0x73,0x65,0x20,0x73,0x65,0x6E,0x64,0x20,0x6D,0x65,
-	0x20,0x61,0x20,0x6D,0x61,0x69,0x6C,0x20,0x28,0x66,0x6F,0x75,
-	0x6E,0x64,0x20,0x61,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74,
-	0x73,0x2E,0x6F,0x72,0x67,0x29,0x20,0x61,0x6E,0x64,0x20,0x74,
-	0x72,0x79,0x20,0x74,0x6F,0x20,0x65,0x78,0x70,0x6C,0x61,0x69,
-	0x6E,0x20,0x69,0x74,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,
+	0x4C,0x61,0x79,0x6F,0x75,0x74,0x2E,0x4B,0x3E,0x40,0x58,0x30,
+	0x33,0x35,0x41,0x6C,0x74,0x65,0x72,0x6E,0x61,0x74,0x69,0x76,
+	0x65,0x6C,0x79,0x2C,0x20,0x79,0x6F,0x75,0x20,0x63,0x61,0x6E,
+	0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x20,0x22,0x56,0x53,0x79,
+	0x6E,0x63,0x20,0x6F,0x66,0x66,0x22,0x20,0x69,0x6E,0x20,0x43,
+	0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D,0x3E,0x20,0x4D,0x69,0x73,
+	0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x2E,0x46,
+	0x3E,0x54,0x68,0x69,0x73,0x20,0x68,0x6F,0x77,0x65,0x76,0x65,
+	0x72,0x2C,0x20,0x77,0x69,0x6C,0x6C,0x20,0x69,0x6E,0x74,0x72,
+	0x6F,0x64,0x75,0x63,0x65,0x20,0x73,0x74,0x75,0x74,0x74,0x65,
+	0x72,0x69,0x6E,0x67,0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,
+	0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x6E,0x64,0x65,0x72,0x69,
+	0x6E,0x67,0x20,0x72,0x61,0x74,0x65,0x20,0x69,0x73,0x22,0x3E,
+	0x6E,0x6F,0x74,0x20,0x65,0x78,0x61,0x63,0x74,0x20,0x74,0x6F,
+	0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,
+	0x72,0x27,0x73,0x20,0x72,0x61,0x74,0x65,0x2E,0x06,0x3E,0x40,
+	0x58,0x30,0x32,0x30,0x33,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,
+	0x3A,0x20,0x57,0x69,0x6C,0x6C,0x20,0x79,0x6F,0x75,0x20,0x69,
+	0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x20,0x4D,0x49,0x44,
+	0x49,0x20,0x6F,0x75,0x74,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,
+	0x6F,0x6E,0x61,0x6C,0x69,0x74,0x79,0x3F,0x4D,0x3E,0x40,0x43,
+	0x30,0x30,0x32,0x41,0x3A,0x20,0x4E,0x6F,0x2C,0x20,0x73,0x6F,
+	0x72,0x72,0x79,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,
+	0x20,0x76,0x65,0x72,0x79,0x20,0x64,0x69,0x66,0x66,0x69,0x63,
+	0x75,0x6C,0x74,0x20,0x74,0x6F,0x20,0x69,0x6D,0x70,0x6C,0x65,
+	0x6D,0x65,0x6E,0x74,0x20,0x63,0x6F,0x72,0x72,0x65,0x63,0x74,
+	0x6C,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,0x68,0x61,0x76,0x69,
+	0x6E,0x67,0x3C,0x3E,0x40,0x58,0x30,0x33,0x35,0x68,0x69,0x67,
+	0x68,0x65,0x72,0x20,0x61,0x75,0x64,0x69,0x6F,0x20,0x62,0x75,
+	0x66,0x66,0x65,0x72,0x20,0x73,0x69,0x7A,0x65,0x73,0x20,0x28,
+	0x62,0x75,0x66,0x66,0x65,0x72,0x65,0x64,0x20,0x72,0x65,0x70,
+	0x6C,0x61,0x79,0x65,0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x29,
+	0x2E,0x2E,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x30,0x3E,
+	0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x57,0x68,0x65,0x72,
+	0x65,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6E,
+	0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,
+	0x69,0x6C,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64,0x3F,0x3F,
+	0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x57,0x69,0x6E,
+	0x64,0x6F,0x77,0x73,0x3A,0x20,0x5C,0x55,0x73,0x65,0x72,0x73,
+	0x5C,0x55,0x53,0x45,0x52,0x5C,0x41,0x70,0x70,0x44,0x61,0x74,
+	0x61,0x5C,0x52,0x6F,0x61,0x6D,0x69,0x6E,0x67,0x5C,0x46,0x54,
+	0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x5C,0x46,0x54,0x32,0x2E,
+	0x43,0x46,0x47,0x45,0x3E,0x40,0x58,0x30,0x33,0x35,0x4F,0x53,
+	0x20,0x58,0x3A,0x20,0x2F,0x55,0x73,0x65,0x72,0x73,0x2F,0x55,
+	0x53,0x45,0x52,0x2F,0x4C,0x69,0x62,0x72,0x61,0x72,0x79,0x2F,
+	0x41,0x70,0x70,0x6C,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
+	0x53,0x75,0x70,0x70,0x6F,0x72,0x74,0x2F,0x46,0x54,0x32,0x20,
+	0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32,0x2E,0x43,0x46,
+	0x47,0x2F,0x47,0x4E,0x55,0x2F,0x4C,0x69,0x6E,0x75,0x78,0x3A,
+	0x20,0x2F,0x68,0x6F,0x6D,0x65,0x2F,0x55,0x53,0x45,0x52,0x2F,
+	0x2E,0x63,0x6F,0x6E,0x66,0x69,0x67,0x2F,0x46,0x54,0x32,0x20,
+	0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32,0x2E,0x43,0x46,
+	0x47,0x01,0x3E,0x48,0x49,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,
+	0x62,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64,0x20,0x69,0x6E,
+	0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,
+	0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x6F,0x72,0x79,0x20,0x69,
+	0x66,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x74,0x68,0x20,0x63,
+	0x6F,0x75,0x6C,0x64,0x6E,0x27,0x74,0x20,0x62,0x65,0x20,0x75,
+	0x73,0x65,0x64,0x2E,0x4D,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,
+	0x70,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6E,0x66,
+	0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,0x69,
+	0x6C,0x65,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x70,0x72,
+	0x6F,0x67,0x72,0x61,0x6D,0x20,0x64,0x69,0x72,0x65,0x63,0x74,
+	0x6F,0x72,0x79,0x2C,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,0x6C,
+	0x20,0x72,0x65,0x61,0x64,0x20,0x74,0x68,0x61,0x74,0x4A,0x6F,
+	0x6E,0x65,0x20,0x61,0x6E,0x64,0x20,0x6E,0x6F,0x74,0x20,0x61,
+	0x74,0x74,0x65,0x6D,0x70,0x74,0x20,0x74,0x6F,0x20,0x63,0x72,
+	0x65,0x61,0x74,0x65,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x20,
+	0x64,0x69,0x72,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,
+	0x20,0x4F,0x53,0x20,0x75,0x73,0x65,0x72,0x2E,0x20,0x28,0x70,
+	0x6F,0x72,0x74,0x61,0x62,0x6C,0x65,0x20,0x6D,0x6F,0x64,0x65,
+	0x29,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x42,0x3E,0x40,0x43,
+	0x30,0x30,0x31,0x51,0x3A,0x20,0x43,0x61,0x6E,0x20,0x74,0x68,
+	0x65,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x20,0x72,0x65,0x61,0x64,
+	0x20,0x46,0x54,0x32,0x2E,0x43,0x46,0x47,0x20,0x66,0x72,0x6F,
+	0x6D,0x20,0x72,0x65,0x61,0x6C,0x20,0x46,0x54,0x32,0x2C,0x20,
+	0x61,0x6E,0x64,0x20,0x76,0x69,0x63,0x65,0x20,0x76,0x65,0x72,
+	0x73,0x61,0x3F,0x4C,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,
+	0x20,0x59,0x65,0x73,0x2C,0x20,0x69,0x74,0x20,0x73,0x68,0x6F,
+	0x75,0x6C,0x64,0x20,0x77,0x6F,0x72,0x6B,0x20,0x6A,0x75,0x73,
+	0x74,0x20,0x66,0x69,0x6E,0x65,0x2E,0x20,0x50,0x75,0x74,0x20,
+	0x69,0x74,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x64,0x69,
+	0x72,0x65,0x63,0x74,0x6F,0x72,0x79,0x20,0x73,0x68,0x6F,0x77,
+	0x6E,0x20,0x61,0x62,0x6F,0x76,0x65,0x2E,0x06,0x3E,0x40,0x58,
+	0x30,0x32,0x30,0x52,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,
+	0x20,0x53,0x6D,0x70,0x2E,0x20,0x45,0x64,0x2E,0x3A,0x20,0x57,
+	0x68,0x69,0x6C,0x65,0x20,0x7A,0x6F,0x6F,0x6D,0x69,0x6E,0x67,
+	0x20,0x69,0x6E,0x2C,0x20,0x49,0x20,0x73,0x6F,0x6D,0x65,0x74,
+	0x69,0x6D,0x65,0x73,0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x6D,
+	0x61,0x72,0x6B,0x20,0x74,0x68,0x65,0x20,0x6C,0x61,0x73,0x74,
+	0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,0x69,0x6E,
+	0x74,0x21,0x47,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,
+	0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x6E,0x6F,0x72,0x6D,
+	0x61,0x6C,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,
+	0x61,0x20,0x6C,0x69,0x6D,0x69,0x74,0x61,0x74,0x69,0x6F,0x6E,
+	0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6E,0x61,0x74,0x75,
+	0x72,0x65,0x20,0x6F,0x66,0x20,0x73,0x63,0x61,0x6C,0x69,0x6E,
+	0x67,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x17,0x3E,0x40,
+	0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20,0x66,0x6F,0x75,
+	0x6E,0x64,0x20,0x61,0x20,0x62,0x75,0x67,0x21,0x4C,0x3E,0x40,
+	0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x50,0x6C,0x65,0x61,0x73,
+	0x65,0x20,0x73,0x65,0x6E,0x64,0x20,0x6D,0x65,0x20,0x61,0x20,
+	0x6D,0x61,0x69,0x6C,0x20,0x28,0x66,0x6F,0x75,0x6E,0x64,0x20,
+	0x61,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74,0x73,0x2E,0x6F,
+	0x72,0x67,0x29,0x20,0x61,0x6E,0x64,0x20,0x74,0x72,0x79,0x20,
+	0x74,0x6F,0x20,0x65,0x78,0x70,0x6C,0x61,0x69,0x6E,0x20,0x69,
+	0x74,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
@@ -2232,64 +2252,63 @@
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
+	0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
 	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,
-	0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x0C,0x40,0x4C,0x4B,
-	0x6E,0x6F,0x77,0x6E,0x20,0x62,0x75,0x67,0x73,0x06,0x3E,0x40,
-	0x58,0x30,0x31,0x30,0x2C,0x3E,0x40,0x43,0x30,0x30,0x31,0x57,
-	0x41,0x56,0x20,0x65,0x78,0x70,0x6F,0x72,0x74,0x69,0x6E,0x67,
-	0x20,0x28,0x72,0x65,0x6E,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,
-	0x73,0x6F,0x6E,0x67,0x20,0x74,0x6F,0x20,0x57,0x41,0x56,0x29,
-	0x3A,0x01,0x3E,0x50,0x3E,0x40,0x43,0x30,0x30,0x32,0x2D,0x20,
-	0x53,0x6F,0x6E,0x67,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x6A,
-	0x75,0x6D,0x70,0x20,0x62,0x61,0x63,0x6B,0x20,0x74,0x6F,0x20,
-	0x61,0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x70,
-	0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x77,0x69,0x6C,0x6C,0x20,
-	0x72,0x65,0x6E,0x64,0x65,0x72,0x20,0x66,0x6F,0x72,0x65,0x76,
-	0x65,0x72,0x20,0x61,0x6E,0x64,0x20,0x65,0x76,0x65,0x72,0x2C,
-	0x50,0x61,0x6E,0x64,0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65,
-	0x64,0x20,0x74,0x6F,0x20,0x70,0x72,0x65,0x73,0x73,0x20,0x61,
-	0x20,0x6B,0x65,0x79,0x20,0x6F,0x72,0x20,0x63,0x6C,0x69,0x63,
-	0x6B,0x20,0x74,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,
-	0x74,0x6F,0x20,0x61,0x62,0x6F,0x72,0x74,0x20,0x74,0x68,0x65,
-	0x20,0x72,0x65,0x6E,0x64,0x65,0x72,0x20,0x77,0x68,0x65,0x6E,
-	0x20,0x79,0x6F,0x75,0x20,0x77,0x61,0x6E,0x74,0x06,0x69,0x74,
-	0x20,0x74,0x6F,0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x0C,
-	0x3E,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,
-	0x06,0x3E,0x40,0x43,0x30,0x30,0x32,0x50,0x3E,0x40,0x58,0x30,
-	0x31,0x30,0x2D,0x20,0x46,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,
-	0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x20,0x63,0x61,0x6E,0x20,
-	0x62,0x65,0x20,0x75,0x6E,0x62,0x65,0x61,0x72,0x61,0x62,0x6C,
-	0x79,0x20,0x73,0x6C,0x6F,0x77,0x20,0x6F,0x6E,0x20,0x61,0x20,
-	0x52,0x61,0x73,0x70,0x62,0x65,0x72,0x72,0x79,0x20,0x50,0x69,
-	0x20,0x28,0x65,0x76,0x65,0x6E,0x20,0x6F,0x6E,0x20,0x52,0x50,
-	0x69,0x20,0x34,0x29,0x01,0x3E,0x52,0x3E,0x40,0x58,0x30,0x31,
-	0x30,0x2D,0x20,0x4E,0x6F,0x74,0x20,0x61,0x20,0x62,0x75,0x67,
-	0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,
-	0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,0x20,
-	0x72,0x65,0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,0x65,
-	0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x73,0x65,0x74,0x20,
-	0x74,0x6F,0x20,0x36,0x30,0x48,0x7A,0x20,0x28,0x6F,0x72,0x20,
-	0x35,0x39,0x48,0x7A,0x29,0x4F,0x3E,0x40,0x58,0x30,0x32,0x31,
-	0x79,0x6F,0x75,0x20,0x6D,0x61,0x79,0x20,0x65,0x78,0x70,0x65,
-	0x72,0x69,0x65,0x6E,0x63,0x65,0x20,0x76,0x69,0x73,0x75,0x61,
-	0x6C,0x20,0x73,0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,0x67,
-	0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x56,0x53,0x79,
-	0x6E,0x63,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74,0x20,
-	0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x68,0x65,0x6E,
-	0x2E,0x51,0x49,0x20,0x68,0x69,0x67,0x68,0x6C,0x79,0x20,0x72,
-	0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x20,0x72,0x75,0x6E,
-	0x6E,0x69,0x6E,0x67,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,
-	0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x74,0x20,0x36,0x30,0x48,
-	0x7A,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x27,0x72,0x65,0x20,
-	0x61,0x20,0x68,0x61,0x72,0x64,0x63,0x6F,0x72,0x65,0x20,0x75,
-	0x73,0x65,0x72,0x20,0x6F,0x66,0x20,0x74,0x68,0x69,0x73,0x08,
-	0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x03,0x45,0x4E,
-	0x44
+	0x2A,0x2A,0x2A,0x2A,0x2A,0x0C,0x40,0x4C,0x4B,0x6E,0x6F,0x77,
+	0x6E,0x20,0x62,0x75,0x67,0x73,0x06,0x3E,0x40,0x58,0x30,0x31,
+	0x30,0x2C,0x3E,0x40,0x43,0x30,0x30,0x31,0x57,0x41,0x56,0x20,
+	0x65,0x78,0x70,0x6F,0x72,0x74,0x69,0x6E,0x67,0x20,0x28,0x72,
+	0x65,0x6E,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,0x73,0x6F,0x6E,
+	0x67,0x20,0x74,0x6F,0x20,0x57,0x41,0x56,0x29,0x3A,0x01,0x3E,
+	0x50,0x3E,0x40,0x43,0x30,0x30,0x32,0x2D,0x20,0x53,0x6F,0x6E,
+	0x67,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x6A,0x75,0x6D,0x70,
+	0x20,0x62,0x61,0x63,0x6B,0x20,0x74,0x6F,0x20,0x61,0x20,0x70,
+	0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x70,0x61,0x74,0x74,
+	0x65,0x72,0x6E,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x6E,
+	0x64,0x65,0x72,0x20,0x66,0x6F,0x72,0x65,0x76,0x65,0x72,0x20,
+	0x61,0x6E,0x64,0x20,0x65,0x76,0x65,0x72,0x2C,0x50,0x61,0x6E,
+	0x64,0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65,0x64,0x20,0x74,
+	0x6F,0x20,0x70,0x72,0x65,0x73,0x73,0x20,0x61,0x20,0x6B,0x65,
+	0x79,0x20,0x6F,0x72,0x20,0x63,0x6C,0x69,0x63,0x6B,0x20,0x74,
+	0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x74,0x6F,0x20,
+	0x61,0x62,0x6F,0x72,0x74,0x20,0x74,0x68,0x65,0x20,0x72,0x65,
+	0x6E,0x64,0x65,0x72,0x20,0x77,0x68,0x65,0x6E,0x20,0x79,0x6F,
+	0x75,0x20,0x77,0x61,0x6E,0x74,0x06,0x69,0x74,0x20,0x74,0x6F,
+	0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x0C,0x3E,0x40,0x43,
+	0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x06,0x3E,0x40,
+	0x43,0x30,0x30,0x32,0x50,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,
+	0x20,0x46,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20,
+	0x6D,0x6F,0x64,0x65,0x20,0x63,0x61,0x6E,0x20,0x62,0x65,0x20,
+	0x75,0x6E,0x62,0x65,0x61,0x72,0x61,0x62,0x6C,0x79,0x20,0x73,
+	0x6C,0x6F,0x77,0x20,0x6F,0x6E,0x20,0x61,0x20,0x52,0x61,0x73,
+	0x70,0x62,0x65,0x72,0x72,0x79,0x20,0x50,0x69,0x20,0x28,0x65,
+	0x76,0x65,0x6E,0x20,0x6F,0x6E,0x20,0x52,0x50,0x69,0x20,0x34,
+	0x29,0x01,0x3E,0x52,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,0x20,
+	0x4E,0x6F,0x74,0x20,0x61,0x20,0x62,0x75,0x67,0x2C,0x20,0x62,
+	0x75,0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,
+	0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,0x20,0x72,0x65,0x66,
+	0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,0x65,0x20,0x69,0x73,
+	0x20,0x6E,0x6F,0x74,0x20,0x73,0x65,0x74,0x20,0x74,0x6F,0x20,
+	0x36,0x30,0x48,0x7A,0x20,0x28,0x6F,0x72,0x20,0x35,0x39,0x48,
+	0x7A,0x29,0x4F,0x3E,0x40,0x58,0x30,0x32,0x31,0x79,0x6F,0x75,
+	0x20,0x6D,0x61,0x79,0x20,0x65,0x78,0x70,0x65,0x72,0x69,0x65,
+	0x6E,0x63,0x65,0x20,0x76,0x69,0x73,0x75,0x61,0x6C,0x20,0x73,
+	0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,0x67,0x20,0x62,0x65,
+	0x63,0x61,0x75,0x73,0x65,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,
+	0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,
+	0x75,0x73,0x65,0x64,0x20,0x74,0x68,0x65,0x6E,0x2E,0x51,0x49,
+	0x20,0x68,0x69,0x67,0x68,0x6C,0x79,0x20,0x72,0x65,0x63,0x6F,
+	0x6D,0x6D,0x65,0x6E,0x64,0x20,0x72,0x75,0x6E,0x6E,0x69,0x6E,
+	0x67,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,
+	0x6F,0x72,0x20,0x61,0x74,0x20,0x36,0x30,0x48,0x7A,0x20,0x69,
+	0x66,0x20,0x79,0x6F,0x75,0x27,0x72,0x65,0x20,0x61,0x20,0x68,
+	0x61,0x72,0x64,0x63,0x6F,0x72,0x65,0x20,0x75,0x73,0x65,0x72,
+	0x20,0x6F,0x66,0x20,0x74,0x68,0x69,0x73,0x08,0x70,0x72,0x6F,
+	0x67,0x72,0x61,0x6D,0x2E,0x00,0x03,0x45,0x4E,0x44
 };
 
 #endif
--- /dev/null
+++ b/src/libflac/COPYING.txt
@@ -1,0 +1,29 @@
+Copyright (C) 2000-2009  Josh Coalson
+Copyright (C) 2011-2016  Xiph.Org Foundation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
--- /dev/null
+++ b/src/libflac/FLAC/callback.h
@@ -1,0 +1,185 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2004-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__CALLBACK_H
+#define FLAC__CALLBACK_H
+
+#include "ordinals.h"
+#include <stdlib.h> /* for size_t */
+
+/** \file include/FLAC/callback.h
+ *
+ *  \brief
+ *  This module defines the structures for describing I/O callbacks
+ *  to the other FLAC interfaces.
+ *
+ *  See the detailed documentation for callbacks in the
+ *  \link flac_callbacks callbacks \endlink module.
+ */
+
+/** \defgroup flac_callbacks FLAC/callback.h: I/O callback structures
+ *  \ingroup flac
+ *
+ *  \brief
+ *  This module defines the structures for describing I/O callbacks
+ *  to the other FLAC interfaces.
+ *
+ *  The purpose of the I/O callback functions is to create a common way
+ *  for the metadata interfaces to handle I/O.
+ *
+ *  Originally the metadata interfaces required filenames as the way of
+ *  specifying FLAC files to operate on.  This is problematic in some
+ *  environments so there is an additional option to specify a set of
+ *  callbacks for doing I/O on the FLAC file, instead of the filename.
+ *
+ *  In addition to the callbacks, a FLAC__IOHandle type is defined as an
+ *  opaque structure for a data source.
+ *
+ *  The callback function prototypes are similar (but not identical) to the
+ *  stdio functions fread, fwrite, fseek, ftell, feof, and fclose.  If you use
+ *  stdio streams to implement the callbacks, you can pass fread, fwrite, and
+ *  fclose anywhere a FLAC__IOCallback_Read, FLAC__IOCallback_Write, or
+ *  FLAC__IOCallback_Close is required, and a FILE* anywhere a FLAC__IOHandle
+ *  is required.  \warning You generally CANNOT directly use fseek or ftell
+ *  for FLAC__IOCallback_Seek or FLAC__IOCallback_Tell since on most systems
+ *  these use 32-bit offsets and FLAC requires 64-bit offsets to deal with
+ *  large files.  You will have to find an equivalent function (e.g. ftello),
+ *  or write a wrapper.  The same is true for feof() since this is usually
+ *  implemented as a macro, not as a function whose address can be taken.
+ *
+ * \{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This is the opaque handle type used by the callbacks.  Typically
+ *  this is a \c FILE* or address of a file descriptor.
+ */
+typedef void* FLAC__IOHandle;
+
+/** Signature for the read callback.
+ *  The signature and semantics match POSIX fread() implementations
+ *  and can generally be used interchangeably.
+ *
+ * \param  ptr      The address of the read buffer.
+ * \param  size     The size of the records to be read.
+ * \param  nmemb    The number of records to be read.
+ * \param  handle   The handle to the data source.
+ * \retval size_t
+ *    The number of records read.
+ */
+typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle);
+
+/** Signature for the write callback.
+ *  The signature and semantics match POSIX fwrite() implementations
+ *  and can generally be used interchangeably.
+ *
+ * \param  ptr      The address of the write buffer.
+ * \param  size     The size of the records to be written.
+ * \param  nmemb    The number of records to be written.
+ * \param  handle   The handle to the data source.
+ * \retval size_t
+ *    The number of records written.
+ */
+typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle);
+
+/** Signature for the seek callback.
+ *  The signature and semantics mostly match POSIX fseek() WITH ONE IMPORTANT
+ *  EXCEPTION: the offset is a 64-bit type whereas fseek() is generally 'long'
+ *  and 32-bits wide.
+ *
+ * \param  handle   The handle to the data source.
+ * \param  offset   The new position, relative to \a whence
+ * \param  whence   \c SEEK_SET, \c SEEK_CUR, or \c SEEK_END
+ * \retval int
+ *    \c 0 on success, \c -1 on error.
+ */
+typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence);
+
+/** Signature for the tell callback.
+ *  The signature and semantics mostly match POSIX ftell() WITH ONE IMPORTANT
+ *  EXCEPTION: the offset is a 64-bit type whereas ftell() is generally 'long'
+ *  and 32-bits wide.
+ *
+ * \param  handle   The handle to the data source.
+ * \retval FLAC__int64
+ *    The current position on success, \c -1 on error.
+ */
+typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle);
+
+/** Signature for the EOF callback.
+ *  The signature and semantics mostly match POSIX feof() but WATCHOUT:
+ *  on many systems, feof() is a macro, so in this case a wrapper function
+ *  must be provided instead.
+ *
+ * \param  handle   The handle to the data source.
+ * \retval int
+ *    \c 0 if not at end of file, nonzero if at end of file.
+ */
+typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle);
+
+/** Signature for the close callback.
+ *  The signature and semantics match POSIX fclose() implementations
+ *  and can generally be used interchangeably.
+ *
+ * \param  handle   The handle to the data source.
+ * \retval int
+ *    \c 0 on success, \c EOF on error.
+ */
+typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle);
+
+/** A structure for holding a set of callbacks.
+ *  Each FLAC interface that requires a FLAC__IOCallbacks structure will
+ *  describe which of the callbacks are required.  The ones that are not
+ *  required may be set to NULL.
+ *
+ *  If the seek requirement for an interface is optional, you can signify that
+ *  a data source is not seekable by setting the \a seek field to \c NULL.
+ */
+typedef struct {
+	FLAC__IOCallback_Read read;
+	FLAC__IOCallback_Write write;
+	FLAC__IOCallback_Seek seek;
+	FLAC__IOCallback_Tell tell;
+	FLAC__IOCallback_Eof eof;
+	FLAC__IOCallback_Close close;
+} FLAC__IOCallbacks;
+
+/* \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+++ b/src/libflac/FLAC/export.h
@@ -1,0 +1,70 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__EXPORT_H
+#define FLAC__EXPORT_H
+
+/** \file include/FLAC/export.h
+ *
+ *  \brief
+ *  This module contains #defines and symbols for exporting function
+ *  calls, and providing version information and compiled-in features.
+ *
+ *  See the \link flac_export export \endlink module.
+ */
+
+/** \defgroup flac_export FLAC/export.h: export symbols
+ *  \ingroup flac
+ *
+ *  \brief
+ *  This module contains #defines and symbols for exporting function
+ *  calls, and providing version information and compiled-in features.
+ *
+ *  If you are compiling with MSVC and will link to the static library
+ *  (libFLAC.lib) you should define FLAC__NO_DLL in your project to
+ *  make sure the symbols are exported properly.
+ *
+ * \{
+ */
+
+#define FLAC_API
+
+/** These #defines will mirror the libtool-based library version number, see
+ * http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning
+ */
+#define FLAC_API_VERSION_CURRENT 11
+#define FLAC_API_VERSION_REVISION 0 /**< see above */
+#define FLAC_API_VERSION_AGE 3 /**< see above */
+
+/* \} */
+
+#endif
--- /dev/null
+++ b/src/libflac/FLAC/format.h
@@ -1,0 +1,1025 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__FORMAT_H
+#define FLAC__FORMAT_H
+
+#include "export.h"
+#include "ordinals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file include/FLAC/format.h
+ *
+ *  \brief
+ *  This module contains structure definitions for the representation
+ *  of FLAC format components in memory.  These are the basic
+ *  structures used by the rest of the interfaces.
+ *
+ *  See the detailed documentation in the
+ *  \link flac_format format \endlink module.
+ */
+
+/** \defgroup flac_format FLAC/format.h: format components
+ *  \ingroup flac
+ *
+ *  \brief
+ *  This module contains structure definitions for the representation
+ *  of FLAC format components in memory.  These are the basic
+ *  structures used by the rest of the interfaces.
+ *
+ *  First, you should be familiar with the
+ *  <A HREF="../format.html">FLAC format</A>.  Many of the values here
+ *  follow directly from the specification.  As a user of libFLAC, the
+ *  interesting parts really are the structures that describe the frame
+ *  header and metadata blocks.
+ *
+ *  The format structures here are very primitive, designed to store
+ *  information in an efficient way.  Reading information from the
+ *  structures is easy but creating or modifying them directly is
+ *  more complex.  For the most part, as a user of a library, editing
+ *  is not necessary; however, for metadata blocks it is, so there are
+ *  convenience functions provided in the \link flac_metadata metadata
+ *  module \endlink to simplify the manipulation of metadata blocks.
+ *
+ * \note
+ * It's not the best convention, but symbols ending in _LEN are in bits
+ * and _LENGTH are in bytes.  _LENGTH symbols are \#defines instead of
+ * global variables because they are usually used when declaring byte
+ * arrays and some compilers require compile-time knowledge of array
+ * sizes when declared on the stack.
+ *
+ * \{
+ */
+
+
+/*
+	Most of the values described in this file are defined by the FLAC
+	format specification.  There is nothing to tune here.
+*/
+
+/** The largest legal metadata type code. */
+#define FLAC__MAX_METADATA_TYPE_CODE (126u)
+
+/** The minimum block size, in samples, permitted by the format. */
+#define FLAC__MIN_BLOCK_SIZE (16u)
+
+/** The maximum block size, in samples, permitted by the format. */
+#define FLAC__MAX_BLOCK_SIZE (65535u)
+
+/** The maximum block size, in samples, permitted by the FLAC subset for
+ *  sample rates up to 48kHz. */
+#define FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ (4608u)
+
+/** The maximum number of channels permitted by the format. */
+#define FLAC__MAX_CHANNELS (8u)
+
+/** The minimum sample resolution permitted by the format. */
+#define FLAC__MIN_BITS_PER_SAMPLE (4u)
+
+/** The maximum sample resolution permitted by the format. */
+#define FLAC__MAX_BITS_PER_SAMPLE (32u)
+
+/** The maximum sample resolution permitted by libFLAC.
+ *
+ * \warning
+ * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format.  However,
+ * the reference encoder/decoder is currently limited to 24 bits because
+ * of prevalent 32-bit math, so make sure and use this value when
+ * appropriate.
+ */
+#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u)
+
+/** The maximum sample rate permitted by the format.  The value is
+ *  ((2 ^ 16) - 1) * 10; see <A HREF="../format.html">FLAC format</A>
+ *  as to why.
+ */
+#define FLAC__MAX_SAMPLE_RATE (655350u)
+
+/** The maximum LPC order permitted by the format. */
+#define FLAC__MAX_LPC_ORDER (32u)
+
+/** The maximum LPC order permitted by the FLAC subset for sample rates
+ *  up to 48kHz. */
+#define FLAC__SUBSET_MAX_LPC_ORDER_48000HZ (12u)
+
+/** The minimum quantized linear predictor coefficient precision
+ *  permitted by the format.
+ */
+#define FLAC__MIN_QLP_COEFF_PRECISION (5u)
+
+/** The maximum quantized linear predictor coefficient precision
+ *  permitted by the format.
+ */
+#define FLAC__MAX_QLP_COEFF_PRECISION (15u)
+
+/** The maximum order of the fixed predictors permitted by the format. */
+#define FLAC__MAX_FIXED_ORDER (4u)
+
+/** The maximum Rice partition order permitted by the format. */
+#define FLAC__MAX_RICE_PARTITION_ORDER (15u)
+
+/** The maximum Rice partition order permitted by the FLAC Subset. */
+#define FLAC__SUBSET_MAX_RICE_PARTITION_ORDER (8u)
+
+/** The version string of the release, stamped onto the libraries and binaries.
+ *
+ * \note
+ * This does not correspond to the shared library version number, which
+ * is used to determine binary compatibility.
+ */
+extern FLAC_API const char *FLAC__VERSION_STRING;
+
+/** The vendor string inserted by the encoder into the VORBIS_COMMENT block.
+ *  This is a NUL-terminated ASCII string; when inserted into the
+ *  VORBIS_COMMENT the trailing null is stripped.
+ */
+extern FLAC_API const char *FLAC__VENDOR_STRING;
+
+/** The byte string representation of the beginning of a FLAC stream. */
+extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */
+
+/** The 32-bit integer big-endian representation of the beginning of
+ *  a FLAC stream.
+ */
+extern FLAC_API const uint32_t FLAC__STREAM_SYNC; /* = 0x664C6143 */
+
+/** The length of the FLAC signature in bits. */
+extern FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN; /* = 32 bits */
+
+/** The length of the FLAC signature in bytes. */
+#define FLAC__STREAM_SYNC_LENGTH (4u)
+
+
+/*****************************************************************************
+ *
+ * Subframe structures
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+
+/** An enumeration of the available entropy coding methods. */
+typedef enum {
+	FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0,
+	/**< Residual is coded by partitioning into contexts, each with it's own
+	 * 4-bit Rice parameter. */
+
+	FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1
+	/**< Residual is coded by partitioning into contexts, each with it's own
+	 * 5-bit Rice parameter. */
+} FLAC__EntropyCodingMethodType;
+
+/** Maps a FLAC__EntropyCodingMethodType to a C string.
+ *
+ *  Using a FLAC__EntropyCodingMethodType as the index to this array will
+ *  give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[];
+
+
+/** Contents of a Rice partitioned residual
+ */
+typedef struct {
+
+	uint32_t *parameters;
+	/**< The Rice parameters for each context. */
+
+	uint32_t *raw_bits;
+	/**< Widths for escape-coded partitions.  Will be non-zero for escaped
+	 * partitions and zero for unescaped partitions.
+	 */
+
+	uint32_t capacity_by_order;
+	/**< The capacity of the \a parameters and \a raw_bits arrays
+	 * specified as an order, i.e. the number of array elements
+	 * allocated is 2 ^ \a capacity_by_order.
+	 */
+} FLAC__EntropyCodingMethod_PartitionedRiceContents;
+
+/** Header for a Rice partitioned residual.  (c.f. <A HREF="../format.html#partitioned_rice">format specification</A>)
+ */
+typedef struct {
+
+	uint32_t order;
+	/**< The partition order, i.e. # of contexts = 2 ^ \a order. */
+
+	const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents;
+	/**< The context's Rice parameters and/or raw bits. */
+
+} FLAC__EntropyCodingMethod_PartitionedRice;
+
+extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */
+extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */
+extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */
+extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */
+
+extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER;
+/**< == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)-1 */
+extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER;
+/**< == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN)-1 */
+
+/** Header for the entropy coding method.  (c.f. <A HREF="../format.html#residual">format specification</A>)
+ */
+typedef struct {
+	FLAC__EntropyCodingMethodType type;
+	union {
+		FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice;
+	} data;
+} FLAC__EntropyCodingMethod;
+
+extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */
+
+/*****************************************************************************/
+
+/** An enumeration of the available subframe types. */
+typedef enum {
+	FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */
+	FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */
+	FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */
+	FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */
+} FLAC__SubframeType;
+
+/** Maps a FLAC__SubframeType to a C string.
+ *
+ *  Using a FLAC__SubframeType as the index to this array will
+ *  give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__SubframeTypeString[];
+
+
+/** CONSTANT subframe.  (c.f. <A HREF="../format.html#subframe_constant">format specification</A>)
+ */
+typedef struct {
+	FLAC__int32 value; /**< The constant signal value. */
+} FLAC__Subframe_Constant;
+
+
+/** VERBATIM subframe.  (c.f. <A HREF="../format.html#subframe_verbatim">format specification</A>)
+ */
+typedef struct {
+	const FLAC__int32 *data; /**< A pointer to verbatim signal. */
+} FLAC__Subframe_Verbatim;
+
+
+/** FIXED subframe.  (c.f. <A HREF="../format.html#subframe_fixed">format specification</A>)
+ */
+typedef struct {
+	FLAC__EntropyCodingMethod entropy_coding_method;
+	/**< The residual coding method. */
+
+	uint32_t order;
+	/**< The polynomial order. */
+
+	FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER];
+	/**< Warmup samples to prime the predictor, length == order. */
+
+	const FLAC__int32 *residual;
+	/**< The residual signal, length == (blocksize minus order) samples. */
+} FLAC__Subframe_Fixed;
+
+
+/** LPC subframe.  (c.f. <A HREF="../format.html#subframe_lpc">format specification</A>)
+ */
+typedef struct {
+	FLAC__EntropyCodingMethod entropy_coding_method;
+	/**< The residual coding method. */
+
+	uint32_t order;
+	/**< The FIR order. */
+
+	uint32_t qlp_coeff_precision;
+	/**< Quantized FIR filter coefficient precision in bits. */
+
+	int quantization_level;
+	/**< The qlp coeff shift needed. */
+
+	FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER];
+	/**< FIR filter coefficients. */
+
+	FLAC__int32 warmup[FLAC__MAX_LPC_ORDER];
+	/**< Warmup samples to prime the predictor, length == order. */
+
+	const FLAC__int32 *residual;
+	/**< The residual signal, length == (blocksize minus order) samples. */
+} FLAC__Subframe_LPC;
+
+extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */
+extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */
+
+
+/** FLAC subframe structure.  (c.f. <A HREF="../format.html#subframe">format specification</A>)
+ */
+typedef struct {
+	FLAC__SubframeType type;
+	union {
+		FLAC__Subframe_Constant constant;
+		FLAC__Subframe_Fixed fixed;
+		FLAC__Subframe_LPC lpc;
+		FLAC__Subframe_Verbatim verbatim;
+	} data;
+	uint32_t wasted_bits;
+} FLAC__Subframe;
+
+/** == 1 (bit)
+ *
+ * This used to be a zero-padding bit (hence the name
+ * FLAC__SUBFRAME_ZERO_PAD_LEN) but is now a reserved bit.  It still has a
+ * mandatory value of \c 0 but in the future may take on the value \c 0 or \c 1
+ * to mean something else.
+ */
+extern FLAC_API const uint32_t FLAC__SUBFRAME_ZERO_PAD_LEN;
+extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */
+extern FLAC_API const uint32_t FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */
+
+extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */
+extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */
+extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */
+extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */
+
+/*****************************************************************************/
+
+
+/*****************************************************************************
+ *
+ * Frame structures
+ *
+ *****************************************************************************/
+
+/** An enumeration of the available channel assignments. */
+typedef enum {
+	FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */
+	FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */
+	FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */
+	FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */
+} FLAC__ChannelAssignment;
+
+/** Maps a FLAC__ChannelAssignment to a C string.
+ *
+ *  Using a FLAC__ChannelAssignment as the index to this array will
+ *  give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__ChannelAssignmentString[];
+
+/** An enumeration of the possible frame numbering methods. */
+typedef enum {
+	FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */
+	FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */
+} FLAC__FrameNumberType;
+
+/** Maps a FLAC__FrameNumberType to a C string.
+ *
+ *  Using a FLAC__FrameNumberType as the index to this array will
+ *  give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__FrameNumberTypeString[];
+
+
+/** FLAC frame header structure.  (c.f. <A HREF="../format.html#frame_header">format specification</A>)
+ */
+typedef struct {
+	uint32_t blocksize;
+	/**< The number of samples per subframe. */
+
+	uint32_t sample_rate;
+	/**< The sample rate in Hz. */
+
+	uint32_t channels;
+	/**< The number of channels (== number of subframes). */
+
+	FLAC__ChannelAssignment channel_assignment;
+	/**< The channel assignment for the frame. */
+
+	uint32_t bits_per_sample;
+	/**< The sample resolution. */
+
+	FLAC__FrameNumberType number_type;
+	/**< The numbering scheme used for the frame.  As a convenience, the
+	 * decoder will always convert a frame number to a sample number because
+	 * the rules are complex. */
+
+	union {
+		FLAC__uint32 frame_number;
+		FLAC__uint64 sample_number;
+	} number;
+	/**< The frame number or sample number of first sample in frame;
+	 * use the \a number_type value to determine which to use. */
+
+	FLAC__uint8 crc;
+	/**< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0)
+	 * of the raw frame header bytes, meaning everything before the CRC byte
+	 * including the sync code.
+	 */
+} FLAC__FrameHeader;
+
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */
+extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */
+
+
+/** FLAC frame footer structure.  (c.f. <A HREF="../format.html#frame_footer">format specification</A>)
+ */
+typedef struct {
+	FLAC__uint16 crc;
+	/**< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with
+	 * 0) of the bytes before the crc, back to and including the frame header
+	 * sync code.
+	 */
+} FLAC__FrameFooter;
+
+extern FLAC_API const uint32_t FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */
+
+
+/** FLAC frame structure.  (c.f. <A HREF="../format.html#frame">format specification</A>)
+ */
+typedef struct {
+	FLAC__FrameHeader header;
+	FLAC__Subframe subframes[FLAC__MAX_CHANNELS];
+	FLAC__FrameFooter footer;
+} FLAC__Frame;
+
+/*****************************************************************************/
+
+
+/*****************************************************************************
+ *
+ * Meta-data structures
+ *
+ *****************************************************************************/
+
+/** An enumeration of the available metadata block types. */
+typedef enum {
+
+	FLAC__METADATA_TYPE_STREAMINFO = 0,
+	/**< <A HREF="../format.html#metadata_block_streaminfo">STREAMINFO</A> block */
+
+	FLAC__METADATA_TYPE_PADDING = 1,
+	/**< <A HREF="../format.html#metadata_block_padding">PADDING</A> block */
+
+	FLAC__METADATA_TYPE_APPLICATION = 2,
+	/**< <A HREF="../format.html#metadata_block_application">APPLICATION</A> block */
+
+	FLAC__METADATA_TYPE_SEEKTABLE = 3,
+	/**< <A HREF="../format.html#metadata_block_seektable">SEEKTABLE</A> block */
+
+	FLAC__METADATA_TYPE_VORBIS_COMMENT = 4,
+	/**< <A HREF="../format.html#metadata_block_vorbis_comment">VORBISCOMMENT</A> block (a.k.a. FLAC tags) */
+
+	FLAC__METADATA_TYPE_CUESHEET = 5,
+	/**< <A HREF="../format.html#metadata_block_cuesheet">CUESHEET</A> block */
+
+	FLAC__METADATA_TYPE_PICTURE = 6,
+	/**< <A HREF="../format.html#metadata_block_picture">PICTURE</A> block */
+
+	FLAC__METADATA_TYPE_UNDEFINED = 7,
+	/**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */
+
+	FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE,
+	/**< No type will ever be greater than this. There is not enough room in the protocol block. */
+} FLAC__MetadataType;
+
+/** Maps a FLAC__MetadataType to a C string.
+ *
+ *  Using a FLAC__MetadataType as the index to this array will
+ *  give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__MetadataTypeString[];
+
+
+/** FLAC STREAMINFO structure.  (c.f. <A HREF="../format.html#metadata_block_streaminfo">format specification</A>)
+ */
+typedef struct {
+	uint32_t min_blocksize, max_blocksize;
+	uint32_t min_framesize, max_framesize;
+	uint32_t sample_rate;
+	uint32_t channels;
+	uint32_t bits_per_sample;
+	FLAC__uint64 total_samples;
+	FLAC__byte md5sum[16];
+} FLAC__StreamMetadata_StreamInfo;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */
+
+/** The total stream length of the STREAMINFO block in bytes. */
+#define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u)
+
+/** FLAC PADDING structure.  (c.f. <A HREF="../format.html#metadata_block_padding">format specification</A>)
+ */
+typedef struct {
+	int dummy;
+	/**< Conceptually this is an empty struct since we don't store the
+	 * padding bytes.  Empty structs are not allowed by some C compilers,
+	 * hence the dummy.
+	 */
+} FLAC__StreamMetadata_Padding;
+
+
+/** FLAC APPLICATION structure.  (c.f. <A HREF="../format.html#metadata_block_application">format specification</A>)
+ */
+typedef struct {
+	FLAC__byte id[4];
+	FLAC__byte *data;
+} FLAC__StreamMetadata_Application;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */
+
+/** SeekPoint structure used in SEEKTABLE blocks.  (c.f. <A HREF="../format.html#seekpoint">format specification</A>)
+ */
+typedef struct {
+	FLAC__uint64 sample_number;
+	/**<  The sample number of the target frame. */
+
+	FLAC__uint64 stream_offset;
+	/**< The offset, in bytes, of the target frame with respect to
+	 * beginning of the first frame. */
+
+	uint32_t frame_samples;
+	/**< The number of samples in the target frame. */
+} FLAC__StreamMetadata_SeekPoint;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */
+
+/** The total stream length of a seek point in bytes. */
+#define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u)
+
+/** The value used in the \a sample_number field of
+ *  FLAC__StreamMetadataSeekPoint used to indicate a placeholder
+ *  point (== 0xffffffffffffffff).
+ */
+extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
+
+
+/** FLAC SEEKTABLE structure.  (c.f. <A HREF="../format.html#metadata_block_seektable">format specification</A>)
+ *
+ * \note From the format specification:
+ * - The seek points must be sorted by ascending sample number.
+ * - Each seek point's sample number must be the first sample of the
+ *   target frame.
+ * - Each seek point's sample number must be unique within the table.
+ * - Existence of a SEEKTABLE block implies a correct setting of
+ *   total_samples in the stream_info block.
+ * - Behavior is undefined when more than one SEEKTABLE block is
+ *   present in a stream.
+ */
+typedef struct {
+	uint32_t num_points;
+	FLAC__StreamMetadata_SeekPoint *points;
+} FLAC__StreamMetadata_SeekTable;
+
+
+/** Vorbis comment entry structure used in VORBIS_COMMENT blocks.  (c.f. <A HREF="../format.html#metadata_block_vorbis_comment">format specification</A>)
+ *
+ *  For convenience, the APIs maintain a trailing NUL character at the end of
+ *  \a entry which is not counted toward \a length, i.e.
+ *  \code strlen(entry) == length \endcode
+ */
+typedef struct {
+	FLAC__uint32 length;
+	FLAC__byte *entry;
+} FLAC__StreamMetadata_VorbisComment_Entry;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */
+
+
+/** FLAC VORBIS_COMMENT structure.  (c.f. <A HREF="../format.html#metadata_block_vorbis_comment">format specification</A>)
+ */
+typedef struct {
+	FLAC__StreamMetadata_VorbisComment_Entry vendor_string;
+	FLAC__uint32 num_comments;
+	FLAC__StreamMetadata_VorbisComment_Entry *comments;
+} FLAC__StreamMetadata_VorbisComment;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */
+
+
+/** FLAC CUESHEET track index structure.  (See the
+ * <A HREF="../format.html#cuesheet_track_index">format specification</A> for
+ * the full description of each field.)
+ */
+typedef struct {
+	FLAC__uint64 offset;
+	/**< Offset in samples, relative to the track offset, of the index
+	 * point.
+	 */
+
+	FLAC__byte number;
+	/**< The index point number. */
+} FLAC__StreamMetadata_CueSheet_Index;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */
+
+
+/** FLAC CUESHEET track structure.  (See the
+ * <A HREF="../format.html#cuesheet_track">format specification</A> for
+ * the full description of each field.)
+ */
+typedef struct {
+	FLAC__uint64 offset;
+	/**< Track offset in samples, relative to the beginning of the FLAC audio stream. */
+
+	FLAC__byte number;
+	/**< The track number. */
+
+	char isrc[13];
+	/**< Track ISRC.  This is a 12-digit alphanumeric code plus a trailing \c NUL byte */
+
+	uint32_t type:1;
+	/**< The track type: 0 for audio, 1 for non-audio. */
+
+	uint32_t pre_emphasis:1;
+	/**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */
+
+	FLAC__byte num_indices;
+	/**< The number of track index points. */
+
+	FLAC__StreamMetadata_CueSheet_Index *indices;
+	/**< NULL if num_indices == 0, else pointer to array of index points. */
+
+} FLAC__StreamMetadata_CueSheet_Track;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */
+
+
+/** FLAC CUESHEET structure.  (See the
+ * <A HREF="../format.html#metadata_block_cuesheet">format specification</A>
+ * for the full description of each field.)
+ */
+typedef struct {
+	char media_catalog_number[129];
+	/**< Media catalog number, in ASCII printable characters 0x20-0x7e.  In
+	 * general, the media catalog number may be 0 to 128 bytes long; any
+	 * unused characters should be right-padded with NUL characters.
+	 */
+
+	FLAC__uint64 lead_in;
+	/**< The number of lead-in samples. */
+
+	FLAC__bool is_cd;
+	/**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */
+
+	uint32_t num_tracks;
+	/**< The number of tracks. */
+
+	FLAC__StreamMetadata_CueSheet_Track *tracks;
+	/**< NULL if num_tracks == 0, else pointer to array of tracks. */
+
+} FLAC__StreamMetadata_CueSheet;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */
+
+
+/** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */
+typedef enum {
+	FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */
+	FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED
+} FLAC__StreamMetadata_Picture_Type;
+
+/** Maps a FLAC__StreamMetadata_Picture_Type to a C string.
+ *
+ *  Using a FLAC__StreamMetadata_Picture_Type as the index to this array
+ *  will give the string equivalent.  The contents should not be
+ *  modified.
+ */
+extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[];
+
+/** FLAC PICTURE structure.  (See the
+ * <A HREF="../format.html#metadata_block_picture">format specification</A>
+ * for the full description of each field.)
+ */
+typedef struct {
+	FLAC__StreamMetadata_Picture_Type type;
+	/**< The kind of picture stored. */
+
+	char *mime_type;
+	/**< Picture data's MIME type, in ASCII printable characters
+	 * 0x20-0x7e, NUL terminated.  For best compatibility with players,
+	 * use picture data of MIME type \c image/jpeg or \c image/png.  A
+	 * MIME type of '-->' is also allowed, in which case the picture
+	 * data should be a complete URL.  In file storage, the MIME type is
+	 * stored as a 32-bit length followed by the ASCII string with no NUL
+	 * terminator, but is converted to a plain C string in this structure
+	 * for convenience.
+	 */
+
+	FLAC__byte *description;
+	/**< Picture's description in UTF-8, NUL terminated.  In file storage,
+	 * the description is stored as a 32-bit length followed by the UTF-8
+	 * string with no NUL terminator, but is converted to a plain C string
+	 * in this structure for convenience.
+	 */
+
+	FLAC__uint32 width;
+	/**< Picture's width in pixels. */
+
+	FLAC__uint32 height;
+	/**< Picture's height in pixels. */
+
+	FLAC__uint32 depth;
+	/**< Picture's color depth in bits-per-pixel. */
+
+	FLAC__uint32 colors;
+	/**< For indexed palettes (like GIF), picture's number of colors (the
+	 * number of palette entries), or \c 0 for non-indexed (i.e. 2^depth).
+	 */
+
+	FLAC__uint32 data_length;
+	/**< Length of binary picture data in bytes. */
+
+	FLAC__byte *data;
+	/**< Binary picture data. */
+
+} FLAC__StreamMetadata_Picture;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */
+
+
+/** Structure that is used when a metadata block of unknown type is loaded.
+ *  The contents are opaque.  The structure is used only internally to
+ *  correctly handle unknown metadata.
+ */
+typedef struct {
+	FLAC__byte *data;
+} FLAC__StreamMetadata_Unknown;
+
+
+/** FLAC metadata block structure.  (c.f. <A HREF="../format.html#metadata_block">format specification</A>)
+ */
+typedef struct {
+	FLAC__MetadataType type;
+	/**< The type of the metadata block; used determine which member of the
+	 * \a data union to dereference.  If type >= FLAC__METADATA_TYPE_UNDEFINED
+	 * then \a data.unknown must be used. */
+
+	FLAC__bool is_last;
+	/**< \c true if this metadata block is the last, else \a false */
+
+	uint32_t length;
+	/**< Length, in bytes, of the block data as it appears in the stream. */
+
+	union {
+		FLAC__StreamMetadata_StreamInfo stream_info;
+		FLAC__StreamMetadata_Padding padding;
+		FLAC__StreamMetadata_Application application;
+		FLAC__StreamMetadata_SeekTable seek_table;
+		FLAC__StreamMetadata_VorbisComment vorbis_comment;
+		FLAC__StreamMetadata_CueSheet cue_sheet;
+		FLAC__StreamMetadata_Picture picture;
+		FLAC__StreamMetadata_Unknown unknown;
+	} data;
+	/**< Polymorphic block data; use the \a type value to determine which
+	 * to use. */
+} FLAC__StreamMetadata;
+
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */
+extern FLAC_API const uint32_t FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */
+
+/** The total stream length of a metadata block header in bytes. */
+#define FLAC__STREAM_METADATA_HEADER_LENGTH (4u)
+
+/*****************************************************************************/
+
+
+/*****************************************************************************
+ *
+ * Utility functions
+ *
+ *****************************************************************************/
+
+/** Tests that a sample rate is valid for FLAC.
+ *
+ * \param sample_rate  The sample rate to test for compliance.
+ * \retval FLAC__bool
+ *    \c true if the given sample rate conforms to the specification, else
+ *    \c false.
+ */
+FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(uint32_t sample_rate);
+
+/** Tests that a blocksize at the given sample rate is valid for the FLAC
+ *  subset.
+ *
+ * \param blocksize    The blocksize to test for compliance.
+ * \param sample_rate  The sample rate is needed, since the valid subset
+ *                     blocksize depends on the sample rate.
+ * \retval FLAC__bool
+ *    \c true if the given blocksize conforms to the specification for the
+ *    subset at the given sample rate, else \c false.
+ */
+FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(uint32_t blocksize, uint32_t sample_rate);
+
+/** Tests that a sample rate is valid for the FLAC subset.  The subset rules
+ *  for valid sample rates are slightly more complex since the rate has to
+ *  be expressible completely in the frame header.
+ *
+ * \param sample_rate  The sample rate to test for compliance.
+ * \retval FLAC__bool
+ *    \c true if the given sample rate conforms to the specification for the
+ *    subset, else \c false.
+ */
+FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(uint32_t sample_rate);
+
+/** Check a Vorbis comment entry name to see if it conforms to the Vorbis
+ *  comment specification.
+ *
+ *  Vorbis comment names must be composed only of characters from
+ *  [0x20-0x3C,0x3E-0x7D].
+ *
+ * \param name       A NUL-terminated string to be checked.
+ * \assert
+ *    \code name != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if entry name is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name);
+
+/** Check a Vorbis comment entry value to see if it conforms to the Vorbis
+ *  comment specification.
+ *
+ *  Vorbis comment values must be valid UTF-8 sequences.
+ *
+ * \param value      A string to be checked.
+ * \param length     A the length of \a value in bytes.  May be
+ *                   \c (uint32_t)(-1) to indicate that \a value is a plain
+ *                   UTF-8 NUL-terminated string.
+ * \assert
+ *    \code value != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if entry name is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, uint32_t length);
+
+/** Check a Vorbis comment entry to see if it conforms to the Vorbis
+ *  comment specification.
+ *
+ *  Vorbis comment entries must be of the form 'name=value', and 'name' and
+ *  'value' must be legal according to
+ *  FLAC__format_vorbiscomment_entry_name_is_legal() and
+ *  FLAC__format_vorbiscomment_entry_value_is_legal() respectively.
+ *
+ * \param entry      An entry to be checked.
+ * \param length     The length of \a entry in bytes.
+ * \assert
+ *    \code value != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if entry name is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, uint32_t length);
+
+/** Check a seek table to see if it conforms to the FLAC specification.
+ *  See the format specification for limits on the contents of the
+ *  seek table.
+ *
+ * \param seek_table  A pointer to a seek table to be checked.
+ * \assert
+ *    \code seek_table != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if seek table is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table);
+
+/** Sort a seek table's seek points according to the format specification.
+ *  This includes a "unique-ification" step to remove duplicates, i.e.
+ *  seek points with identical \a sample_number values.  Duplicate seek
+ *  points are converted into placeholder points and sorted to the end of
+ *  the table.
+ *
+ * \param seek_table  A pointer to a seek table to be sorted.
+ * \assert
+ *    \code seek_table != NULL \endcode
+ * \retval uint32_t
+ *    The number of duplicate seek points converted into placeholders.
+ */
+FLAC_API uint32_t FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table);
+
+/** Check a cue sheet to see if it conforms to the FLAC specification.
+ *  See the format specification for limits on the contents of the
+ *  cue sheet.
+ *
+ * \param cue_sheet  A pointer to an existing cue sheet to be checked.
+ * \param check_cd_da_subset  If \c true, check CUESHEET against more
+ *                   stringent requirements for a CD-DA (audio) disc.
+ * \param violation  Address of a pointer to a string.  If there is a
+ *                   violation, a pointer to a string explanation of the
+ *                   violation will be returned here. \a violation may be
+ *                   \c NULL if you don't need the returned string.  Do not
+ *                   free the returned string; it will always point to static
+ *                   data.
+ * \assert
+ *    \code cue_sheet != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if cue sheet is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation);
+
+/** Check picture data to see if it conforms to the FLAC specification.
+ *  See the format specification for limits on the contents of the
+ *  PICTURE block.
+ *
+ * \param picture    A pointer to existing picture data to be checked.
+ * \param violation  Address of a pointer to a string.  If there is a
+ *                   violation, a pointer to a string explanation of the
+ *                   violation will be returned here. \a violation may be
+ *                   \c NULL if you don't need the returned string.  Do not
+ *                   free the returned string; it will always point to static
+ *                   data.
+ * \assert
+ *    \code picture != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if picture data is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation);
+
+/* \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+++ b/src/libflac/FLAC/metadata.h
@@ -1,0 +1,2182 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__METADATA_H
+#define FLAC__METADATA_H
+
+#include <sys/types.h> /* for off_t */
+#include "export.h"
+#include "callback.h"
+#include "format.h"
+
+/* --------------------------------------------------------------------
+   (For an example of how all these routines are used, see the source
+   code for the unit tests in src/test_libFLAC/metadata_*.c, or
+   metaflac in src/metaflac/)
+   ------------------------------------------------------------------*/
+
+/** \file include/FLAC/metadata.h
+ *
+ *  \brief
+ *  This module provides functions for creating and manipulating FLAC
+ *  metadata blocks in memory, and three progressively more powerful
+ *  interfaces for traversing and editing metadata in FLAC files.
+ *
+ *  See the detailed documentation for each interface in the
+ *  \link flac_metadata metadata \endlink module.
+ */
+
+/** \defgroup flac_metadata FLAC/metadata.h: metadata interfaces
+ *  \ingroup flac
+ *
+ *  \brief
+ *  This module provides functions for creating and manipulating FLAC
+ *  metadata blocks in memory, and three progressively more powerful
+ *  interfaces for traversing and editing metadata in native FLAC files.
+ *  Note that currently only the Chain interface (level 2) supports Ogg
+ *  FLAC files, and it is read-only i.e. no writing back changed
+ *  metadata to file.
+ *
+ *  There are three metadata interfaces of increasing complexity:
+ *
+ *  Level 0:
+ *  Read-only access to the STREAMINFO, VORBIS_COMMENT, CUESHEET, and
+ *  PICTURE blocks.
+ *
+ *  Level 1:
+ *  Read-write access to all metadata blocks.  This level is write-
+ *  efficient in most cases (more on this below), and uses less memory
+ *  than level 2.
+ *
+ *  Level 2:
+ *  Read-write access to all metadata blocks.  This level is write-
+ *  efficient in all cases, but uses more memory since all metadata for
+ *  the whole file is read into memory and manipulated before writing
+ *  out again.
+ *
+ *  What do we mean by efficient?  Since FLAC metadata appears at the
+ *  beginning of the file, when writing metadata back to a FLAC file
+ *  it is possible to grow or shrink the metadata such that the entire
+ *  file must be rewritten.  However, if the size remains the same during
+ *  changes or PADDING blocks are utilized, only the metadata needs to be
+ *  overwritten, which is much faster.
+ *
+ *  Efficient means the whole file is rewritten at most one time, and only
+ *  when necessary.  Level 1 is not efficient only in the case that you
+ *  cause more than one metadata block to grow or shrink beyond what can
+ *  be accommodated by padding.  In this case you should probably use level
+ *  2, which allows you to edit all the metadata for a file in memory and
+ *  write it out all at once.
+ *
+ *  All levels know how to skip over and not disturb an ID3v2 tag at the
+ *  front of the file.
+ *
+ *  All levels access files via their filenames.  In addition, level 2
+ *  has additional alternative read and write functions that take an I/O
+ *  handle and callbacks, for situations where access by filename is not
+ *  possible.
+ *
+ *  In addition to the three interfaces, this module defines functions for
+ *  creating and manipulating various metadata objects in memory.  As we see
+ *  from the Format module, FLAC metadata blocks in memory are very primitive
+ *  structures for storing information in an efficient way.  Reading
+ *  information from the structures is easy but creating or modifying them
+ *  directly is more complex.  The metadata object routines here facilitate
+ *  this by taking care of the consistency and memory management drudgery.
+ *
+ *  Unless you will be using the level 1 or 2 interfaces to modify existing
+ *  metadata however, you will not probably not need these.
+ *
+ *  From a dependency standpoint, none of the encoders or decoders require
+ *  the metadata module.  This is so that embedded users can strip out the
+ *  metadata module from libFLAC to reduce the size and complexity.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** \defgroup flac_metadata_level0 FLAC/metadata.h: metadata level 0 interface
+ *  \ingroup flac_metadata
+ *
+ *  \brief
+ *  The level 0 interface consists of individual routines to read the
+ *  STREAMINFO, VORBIS_COMMENT, CUESHEET, and PICTURE blocks, requiring
+ *  only a filename.
+ *
+ *  They try to skip any ID3v2 tag at the head of the file.
+ *
+ * \{
+ */
+
+/** Read the STREAMINFO metadata block of the given FLAC file.  This function
+ *  will try to skip any ID3v2 tag at the head of the file.
+ *
+ * \param filename    The path to the FLAC file to read.
+ * \param streaminfo  A pointer to space for the STREAMINFO block.  Since
+ *                    FLAC__StreamMetadata is a simple structure with no
+ *                    memory allocation involved, you pass the address of
+ *                    an existing structure.  It need not be initialized.
+ * \assert
+ *    \code filename != NULL \endcode
+ *    \code streaminfo != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid STREAMINFO block was read from \a filename.  Returns
+ *    \c false if there was a memory allocation error, a file decoder error,
+ *    or the file contained no STREAMINFO block.  (A memory allocation error
+ *    is possible because this function must set up a file decoder.)
+ */
+FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo);
+
+/** Read the VORBIS_COMMENT metadata block of the given FLAC file.  This
+ *  function will try to skip any ID3v2 tag at the head of the file.
+ *
+ * \param filename    The path to the FLAC file to read.
+ * \param tags        The address where the returned pointer will be
+ *                    stored.  The \a tags object must be deleted by
+ *                    the caller using FLAC__metadata_object_delete().
+ * \assert
+ *    \code filename != NULL \endcode
+ *    \code tags != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid VORBIS_COMMENT block was read from \a filename,
+ *    and \a *tags will be set to the address of the metadata structure.
+ *    Returns \c false if there was a memory allocation error, a file
+ *    decoder error, or the file contained no VORBIS_COMMENT block, and
+ *    \a *tags will be set to \c NULL.
+ */
+FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags);
+
+/** Read the CUESHEET metadata block of the given FLAC file.  This
+ *  function will try to skip any ID3v2 tag at the head of the file.
+ *
+ * \param filename    The path to the FLAC file to read.
+ * \param cuesheet    The address where the returned pointer will be
+ *                    stored.  The \a cuesheet object must be deleted by
+ *                    the caller using FLAC__metadata_object_delete().
+ * \assert
+ *    \code filename != NULL \endcode
+ *    \code cuesheet != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid CUESHEET block was read from \a filename,
+ *    and \a *cuesheet will be set to the address of the metadata
+ *    structure.  Returns \c false if there was a memory allocation
+ *    error, a file decoder error, or the file contained no CUESHEET
+ *    block, and \a *cuesheet will be set to \c NULL.
+ */
+FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet);
+
+/** Read a PICTURE metadata block of the given FLAC file.  This
+ *  function will try to skip any ID3v2 tag at the head of the file.
+ *  Since there can be more than one PICTURE block in a file, this
+ *  function takes a number of parameters that act as constraints to
+ *  the search.  The PICTURE block with the largest area matching all
+ *  the constraints will be returned, or \a *picture will be set to
+ *  \c NULL if there was no such block.
+ *
+ * \param filename    The path to the FLAC file to read.
+ * \param picture     The address where the returned pointer will be
+ *                    stored.  The \a picture object must be deleted by
+ *                    the caller using FLAC__metadata_object_delete().
+ * \param type        The desired picture type.  Use \c -1 to mean
+ *                    "any type".
+ * \param mime_type   The desired MIME type, e.g. "image/jpeg".  The
+ *                    string will be matched exactly.  Use \c NULL to
+ *                    mean "any MIME type".
+ * \param description The desired description.  The string will be
+ *                    matched exactly.  Use \c NULL to mean "any
+ *                    description".
+ * \param max_width   The maximum width in pixels desired.  Use
+ *                    \c (uint32_t)(-1) to mean "any width".
+ * \param max_height  The maximum height in pixels desired.  Use
+ *                    \c (uint32_t)(-1) to mean "any height".
+ * \param max_depth   The maximum color depth in bits-per-pixel desired.
+ *                    Use \c (uint32_t)(-1) to mean "any depth".
+ * \param max_colors  The maximum number of colors desired.  Use
+ *                    \c (uint32_t)(-1) to mean "any number of colors".
+ * \assert
+ *    \code filename != NULL \endcode
+ *    \code picture != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid PICTURE block was read from \a filename,
+ *    and \a *picture will be set to the address of the metadata
+ *    structure.  Returns \c false if there was a memory allocation
+ *    error, a file decoder error, or the file contained no PICTURE
+ *    block, and \a *picture will be set to \c NULL.
+ */
+FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, uint32_t max_width, uint32_t max_height, uint32_t max_depth, uint32_t max_colors);
+
+/* \} */
+
+
+/** \defgroup flac_metadata_level1 FLAC/metadata.h: metadata level 1 interface
+ *  \ingroup flac_metadata
+ *
+ * \brief
+ * The level 1 interface provides read-write access to FLAC file metadata and
+ * operates directly on the FLAC file.
+ *
+ * The general usage of this interface is:
+ *
+ * - Create an iterator using FLAC__metadata_simple_iterator_new()
+ * - Attach it to a file using FLAC__metadata_simple_iterator_init() and check
+ *   the exit code.  Call FLAC__metadata_simple_iterator_is_writable() to
+ *   see if the file is writable, or only read access is allowed.
+ * - Use FLAC__metadata_simple_iterator_next() and
+ *   FLAC__metadata_simple_iterator_prev() to traverse the blocks.
+ *   This is does not read the actual blocks themselves.
+ *   FLAC__metadata_simple_iterator_next() is relatively fast.
+ *   FLAC__metadata_simple_iterator_prev() is slower since it needs to search
+ *   forward from the front of the file.
+ * - Use FLAC__metadata_simple_iterator_get_block_type() or
+ *   FLAC__metadata_simple_iterator_get_block() to access the actual data at
+ *   the current iterator position.  The returned object is yours to modify
+ *   and free.
+ * - Use FLAC__metadata_simple_iterator_set_block() to write a modified block
+ *   back.  You must have write permission to the original file.  Make sure to
+ *   read the whole comment to FLAC__metadata_simple_iterator_set_block()
+ *   below.
+ * - Use FLAC__metadata_simple_iterator_insert_block_after() to add new blocks.
+ *   Use the object creation functions from
+ *   \link flac_metadata_object here \endlink to generate new objects.
+ * - Use FLAC__metadata_simple_iterator_delete_block() to remove the block
+ *   currently referred to by the iterator, or replace it with padding.
+ * - Destroy the iterator with FLAC__metadata_simple_iterator_delete() when
+ *   finished.
+ *
+ * \note
+ * The FLAC file remains open the whole time between
+ * FLAC__metadata_simple_iterator_init() and
+ * FLAC__metadata_simple_iterator_delete(), so make sure you are not altering
+ * the file during this time.
+ *
+ * \note
+ * Do not modify the \a is_last, \a length, or \a type fields of returned
+ * FLAC__StreamMetadata objects.  These are managed automatically.
+ *
+ * \note
+ * If any of the modification functions
+ * (FLAC__metadata_simple_iterator_set_block(),
+ * FLAC__metadata_simple_iterator_delete_block(),
+ * FLAC__metadata_simple_iterator_insert_block_after(), etc.) return \c false,
+ * you should delete the iterator as it may no longer be valid.
+ *
+ * \{
+ */
+
+struct FLAC__Metadata_SimpleIterator;
+/** The opaque structure definition for the level 1 iterator type.
+ *  See the
+ *  \link flac_metadata_level1 metadata level 1 module \endlink
+ *  for a detailed description.
+ */
+typedef struct FLAC__Metadata_SimpleIterator FLAC__Metadata_SimpleIterator;
+
+/** Status type for FLAC__Metadata_SimpleIterator.
+ *
+ *  The iterator's current status can be obtained by calling FLAC__metadata_simple_iterator_status().
+ */
+typedef enum {
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0,
+	/**< The iterator is in the normal OK state */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT,
+	/**< The data passed into a function violated the function's usage criteria */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE,
+	/**< The iterator could not open the target file */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE,
+	/**< The iterator could not find the FLAC signature at the start of the file */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE,
+	/**< The iterator tried to write to a file that was not writable */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA,
+	/**< The iterator encountered input that does not conform to the FLAC metadata specification */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR,
+	/**< The iterator encountered an error while reading the FLAC file */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR,
+	/**< The iterator encountered an error while seeking in the FLAC file */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR,
+	/**< The iterator encountered an error while writing the FLAC file */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR,
+	/**< The iterator encountered an error renaming the FLAC file */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR,
+	/**< The iterator encountered an error removing the temporary file */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR,
+	/**< Memory allocation failed */
+
+	FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR
+	/**< The caller violated an assertion or an unexpected error occurred */
+
+} FLAC__Metadata_SimpleIteratorStatus;
+
+/** Maps a FLAC__Metadata_SimpleIteratorStatus to a C string.
+ *
+ *  Using a FLAC__Metadata_SimpleIteratorStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[];
+
+
+/** Create a new iterator instance.
+ *
+ * \retval FLAC__Metadata_SimpleIterator*
+ *    \c NULL if there was an error allocating memory, else the new instance.
+ */
+FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void);
+
+/** Free an iterator instance.  Deletes the object pointed to by \a iterator.
+ *
+ * \param iterator  A pointer to an existing iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator);
+
+/** Get the current status of the iterator.  Call this after a function
+ *  returns \c false to get the reason for the error.  Also resets the status
+ *  to FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK.
+ *
+ * \param iterator  A pointer to an existing iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ * \retval FLAC__Metadata_SimpleIteratorStatus
+ *    The current status of the iterator.
+ */
+FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator);
+
+/** Initialize the iterator to point to the first metadata block in the
+ *  given FLAC file.
+ *
+ * \param iterator             A pointer to an existing iterator.
+ * \param filename             The path to the FLAC file.
+ * \param read_only            If \c true, the FLAC file will be opened
+ *                             in read-only mode; if \c false, the FLAC
+ *                             file will be opened for edit even if no
+ *                             edits are performed.
+ * \param preserve_file_stats  If \c true, the owner and modification
+ *                             time will be preserved even if the FLAC
+ *                             file is written to.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \code filename != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if a memory allocation error occurs, the file can't be
+ *    opened, or another error occurs, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats);
+
+/** Returns \c true if the FLAC file is writable.  If \c false, calls to
+ *  FLAC__metadata_simple_iterator_set_block() and
+ *  FLAC__metadata_simple_iterator_insert_block_after() will fail.
+ *
+ * \param iterator             A pointer to an existing iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ * \retval FLAC__bool
+ *    See above.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator);
+
+/** Moves the iterator forward one metadata block, returning \c false if
+ *  already at the end.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval FLAC__bool
+ *    \c false if already at the last metadata block of the chain, else
+ *    \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator);
+
+/** Moves the iterator backward one metadata block, returning \c false if
+ *  already at the beginning.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval FLAC__bool
+ *    \c false if already at the first metadata block of the chain, else
+ *    \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator);
+
+/** Returns a flag telling if the current metadata block is the last.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval FLAC__bool
+ *    \c true if the current metadata block is the last in the file,
+ *    else \c false.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator);
+
+/** Get the offset of the metadata block at the current position.  This
+ *  avoids reading the actual block data which can save time for large
+ *  blocks.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval off_t
+ *    The offset of the metadata block at the current iterator position.
+ *    This is the byte offset relative to the beginning of the file of
+ *    the current metadata block's header.
+ */
+FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator);
+
+/** Get the type of the metadata block at the current position.  This
+ *  avoids reading the actual block data which can save time for large
+ *  blocks.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval FLAC__MetadataType
+ *    The type of the metadata block at the current iterator position.
+ */
+FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator);
+
+/** Get the length of the metadata block at the current position.  This
+ *  avoids reading the actual block data which can save time for large
+ *  blocks.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval uint32_t
+ *    The length of the metadata block at the current iterator position.
+ *    The is same length as that in the
+ *    <a href="http://xiph.org/flac/format.html#metadata_block_header">metadata block header</a>,
+ *    i.e. the length of the metadata body that follows the header.
+ */
+FLAC_API uint32_t FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator);
+
+/** Get the application ID of the \c APPLICATION block at the current
+ *  position.  This avoids reading the actual block data which can save
+ *  time for large blocks.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \param id        A pointer to a buffer of at least \c 4 bytes where
+ *                  the ID will be stored.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \code id != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval FLAC__bool
+ *    \c true if the ID was successfully read, else \c false, in which
+ *    case you should check FLAC__metadata_simple_iterator_status() to
+ *    find out why.  If the status is
+ *    \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, then the
+ *    current metadata block is not an \c APPLICATION block.  Otherwise
+ *    if the status is
+ *    \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR or
+ *    \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, an I/O error
+ *    occurred and the iterator can no longer be used.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id);
+
+/** Get the metadata block at the current position.  You can modify the
+ *  block but must use FLAC__metadata_simple_iterator_set_block() to
+ *  write it back to the FLAC file.
+ *
+ *  You must call FLAC__metadata_object_delete() on the returned object
+ *  when you are finished with it.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval FLAC__StreamMetadata*
+ *    The current metadata block, or \c NULL if there was a memory
+ *    allocation error.
+ */
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator);
+
+/** Write a block back to the FLAC file.  This function tries to be
+ *  as efficient as possible; how the block is actually written is
+ *  shown by the following:
+ *
+ *  Existing block is a STREAMINFO block and the new block is a
+ *  STREAMINFO block: the new block is written in place.  Make sure
+ *  you know what you're doing when changing the values of a
+ *  STREAMINFO block.
+ *
+ *  Existing block is a STREAMINFO block and the new block is a
+ *  not a STREAMINFO block: this is an error since the first block
+ *  must be a STREAMINFO block.  Returns \c false without altering the
+ *  file.
+ *
+ *  Existing block is not a STREAMINFO block and the new block is a
+ *  STREAMINFO block: this is an error since there may be only one
+ *  STREAMINFO block.  Returns \c false without altering the file.
+ *
+ *  Existing block and new block are the same length: the existing
+ *  block will be replaced by the new block, written in place.
+ *
+ *  Existing block is longer than new block: if use_padding is \c true,
+ *  the existing block will be overwritten in place with the new
+ *  block followed by a PADDING block, if possible, to make the total
+ *  size the same as the existing block.  Remember that a padding
+ *  block requires at least four bytes so if the difference in size
+ *  between the new block and existing block is less than that, the
+ *  entire file will have to be rewritten, using the new block's
+ *  exact size.  If use_padding is \c false, the entire file will be
+ *  rewritten, replacing the existing block by the new block.
+ *
+ *  Existing block is shorter than new block: if use_padding is \c true,
+ *  the function will try and expand the new block into the following
+ *  PADDING block, if it exists and doing so won't shrink the PADDING
+ *  block to less than 4 bytes.  If there is no following PADDING
+ *  block, or it will shrink to less than 4 bytes, or use_padding is
+ *  \c false, the entire file is rewritten, replacing the existing block
+ *  with the new block.  Note that in this case any following PADDING
+ *  block is preserved as is.
+ *
+ *  After writing the block, the iterator will remain in the same
+ *  place, i.e. pointing to the new block.
+ *
+ * \param iterator     A pointer to an existing initialized iterator.
+ * \param block        The block to set.
+ * \param use_padding  See above.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ *    \code block != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if successful, else \c false.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding);
+
+/** This is similar to FLAC__metadata_simple_iterator_set_block()
+ *  except that instead of writing over an existing block, it appends
+ *  a block after the existing block.  \a use_padding is again used to
+ *  tell the function to try an expand into following padding in an
+ *  attempt to avoid rewriting the entire file.
+ *
+ *  This function will fail and return \c false if given a STREAMINFO
+ *  block.
+ *
+ *  After writing the block, the iterator will be pointing to the
+ *  new block.
+ *
+ * \param iterator     A pointer to an existing initialized iterator.
+ * \param block        The block to set.
+ * \param use_padding  See above.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ *    \code block != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if successful, else \c false.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding);
+
+/** Deletes the block at the current position.  This will cause the
+ *  entire FLAC file to be rewritten, unless \a use_padding is \c true,
+ *  in which case the block will be replaced by an equal-sized PADDING
+ *  block.  The iterator will be left pointing to the block before the
+ *  one just deleted.
+ *
+ *  You may not delete the STREAMINFO block.
+ *
+ * \param iterator     A pointer to an existing initialized iterator.
+ * \param use_padding  See above.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_simple_iterator_init()
+ * \retval FLAC__bool
+ *    \c true if successful, else \c false.
+ */
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding);
+
+/* \} */
+
+
+/** \defgroup flac_metadata_level2 FLAC/metadata.h: metadata level 2 interface
+ *  \ingroup flac_metadata
+ *
+ * \brief
+ * The level 2 interface provides read-write access to FLAC file metadata;
+ * all metadata is read into memory, operated on in memory, and then written
+ * to file, which is more efficient than level 1 when editing multiple blocks.
+ *
+ * Currently Ogg FLAC is supported for read only, via
+ * FLAC__metadata_chain_read_ogg() but a subsequent
+ * FLAC__metadata_chain_write() will fail.
+ *
+ * The general usage of this interface is:
+ *
+ * - Create a new chain using FLAC__metadata_chain_new().  A chain is a
+ *   linked list of FLAC metadata blocks.
+ * - Read all metadata into the chain from a FLAC file using
+ *   FLAC__metadata_chain_read() or FLAC__metadata_chain_read_ogg() and
+ *   check the status.
+ * - Optionally, consolidate the padding using
+ *   FLAC__metadata_chain_merge_padding() or
+ *   FLAC__metadata_chain_sort_padding().
+ * - Create a new iterator using FLAC__metadata_iterator_new()
+ * - Initialize the iterator to point to the first element in the chain
+ *   using FLAC__metadata_iterator_init()
+ * - Traverse the chain using FLAC__metadata_iterator_next and
+ *   FLAC__metadata_iterator_prev().
+ * - Get a block for reading or modification using
+ *   FLAC__metadata_iterator_get_block().  The pointer to the object
+ *   inside the chain is returned, so the block is yours to modify.
+ *   Changes will be reflected in the FLAC file when you write the
+ *   chain.  You can also add and delete blocks (see functions below).
+ * - When done, write out the chain using FLAC__metadata_chain_write().
+ *   Make sure to read the whole comment to the function below.
+ * - Delete the chain using FLAC__metadata_chain_delete().
+ *
+ * \note
+ * Even though the FLAC file is not open while the chain is being
+ * manipulated, you must not alter the file externally during
+ * this time.  The chain assumes the FLAC file will not change
+ * between the time of FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg()
+ * and FLAC__metadata_chain_write().
+ *
+ * \note
+ * Do not modify the is_last, length, or type fields of returned
+ * FLAC__StreamMetadata objects.  These are managed automatically.
+ *
+ * \note
+ * The metadata objects returned by FLAC__metadata_iterator_get_block()
+ * are owned by the chain; do not FLAC__metadata_object_delete() them.
+ * In the same way, blocks passed to FLAC__metadata_iterator_set_block()
+ * become owned by the chain and they will be deleted when the chain is
+ * deleted.
+ *
+ * \{
+ */
+
+struct FLAC__Metadata_Chain;
+/** The opaque structure definition for the level 2 chain type.
+ */
+typedef struct FLAC__Metadata_Chain FLAC__Metadata_Chain;
+
+struct FLAC__Metadata_Iterator;
+/** The opaque structure definition for the level 2 iterator type.
+ */
+typedef struct FLAC__Metadata_Iterator FLAC__Metadata_Iterator;
+
+typedef enum {
+	FLAC__METADATA_CHAIN_STATUS_OK = 0,
+	/**< The chain is in the normal OK state */
+
+	FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT,
+	/**< The data passed into a function violated the function's usage criteria */
+
+	FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE,
+	/**< The chain could not open the target file */
+
+	FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE,
+	/**< The chain could not find the FLAC signature at the start of the file */
+
+	FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE,
+	/**< The chain tried to write to a file that was not writable */
+
+	FLAC__METADATA_CHAIN_STATUS_BAD_METADATA,
+	/**< The chain encountered input that does not conform to the FLAC metadata specification */
+
+	FLAC__METADATA_CHAIN_STATUS_READ_ERROR,
+	/**< The chain encountered an error while reading the FLAC file */
+
+	FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR,
+	/**< The chain encountered an error while seeking in the FLAC file */
+
+	FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR,
+	/**< The chain encountered an error while writing the FLAC file */
+
+	FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR,
+	/**< The chain encountered an error renaming the FLAC file */
+
+	FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR,
+	/**< The chain encountered an error removing the temporary file */
+
+	FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR,
+	/**< Memory allocation failed */
+
+	FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR,
+	/**< The caller violated an assertion or an unexpected error occurred */
+
+	FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS,
+	/**< One or more of the required callbacks was NULL */
+
+	FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH,
+	/**< FLAC__metadata_chain_write() was called on a chain read by
+	 *   FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(),
+	 *   or
+	 *   FLAC__metadata_chain_write_with_callbacks()/FLAC__metadata_chain_write_with_callbacks_and_tempfile()
+	 *   was called on a chain read by
+	 *   FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg().
+	 *   Matching read/write methods must always be used. */
+
+	FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL
+	/**< FLAC__metadata_chain_write_with_callbacks() was called when the
+	 *   chain write requires a tempfile; use
+	 *   FLAC__metadata_chain_write_with_callbacks_and_tempfile() instead.
+	 *   Or, FLAC__metadata_chain_write_with_callbacks_and_tempfile() was
+	 *   called when the chain write does not require a tempfile; use
+	 *   FLAC__metadata_chain_write_with_callbacks() instead.
+	 *   Always check FLAC__metadata_chain_check_if_tempfile_needed()
+	 *   before writing via callbacks. */
+
+} FLAC__Metadata_ChainStatus;
+
+/** Maps a FLAC__Metadata_ChainStatus to a C string.
+ *
+ *  Using a FLAC__Metadata_ChainStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__Metadata_ChainStatusString[];
+
+/*********** FLAC__Metadata_Chain ***********/
+
+/** Create a new chain instance.
+ *
+ * \retval FLAC__Metadata_Chain*
+ *    \c NULL if there was an error allocating memory, else the new instance.
+ */
+FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void);
+
+/** Free a chain instance.  Deletes the object pointed to by \a chain.
+ *
+ * \param chain  A pointer to an existing chain.
+ * \assert
+ *    \code chain != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain);
+
+/** Get the current status of the chain.  Call this after a function
+ *  returns \c false to get the reason for the error.  Also resets the
+ *  status to FLAC__METADATA_CHAIN_STATUS_OK.
+ *
+ * \param chain    A pointer to an existing chain.
+ * \assert
+ *    \code chain != NULL \endcode
+ * \retval FLAC__Metadata_ChainStatus
+ *    The current status of the chain.
+ */
+FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain);
+
+/** Read all metadata from a FLAC file into the chain.
+ *
+ * \param chain    A pointer to an existing chain.
+ * \param filename The path to the FLAC file to read.
+ * \assert
+ *    \code chain != NULL \endcode
+ *    \code filename != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid list of metadata blocks was read from
+ *    \a filename, else \c false.  On failure, check the status with
+ *    FLAC__metadata_chain_status().
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename);
+
+/** Read all metadata from an Ogg FLAC file into the chain.
+ *
+ * \note Ogg FLAC metadata data writing is not supported yet and
+ * FLAC__metadata_chain_write() will fail.
+ *
+ * \param chain    A pointer to an existing chain.
+ * \param filename The path to the Ogg FLAC file to read.
+ * \assert
+ *    \code chain != NULL \endcode
+ *    \code filename != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid list of metadata blocks was read from
+ *    \a filename, else \c false.  On failure, check the status with
+ *    FLAC__metadata_chain_status().
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename);
+
+/** Read all metadata from a FLAC stream into the chain via I/O callbacks.
+ *
+ *  The \a handle need only be open for reading, but must be seekable.
+ *  The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb"
+ *  for Windows).
+ *
+ * \param chain    A pointer to an existing chain.
+ * \param handle   The I/O handle of the FLAC stream to read.  The
+ *                 handle will NOT be closed after the metadata is read;
+ *                 that is the duty of the caller.
+ * \param callbacks
+ *                 A set of callbacks to use for I/O.  The mandatory
+ *                 callbacks are \a read, \a seek, and \a tell.
+ * \assert
+ *    \code chain != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid list of metadata blocks was read from
+ *    \a handle, else \c false.  On failure, check the status with
+ *    FLAC__metadata_chain_status().
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks);
+
+/** Read all metadata from an Ogg FLAC stream into the chain via I/O callbacks.
+ *
+ *  The \a handle need only be open for reading, but must be seekable.
+ *  The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb"
+ *  for Windows).
+ *
+ * \note Ogg FLAC metadata data writing is not supported yet and
+ * FLAC__metadata_chain_write() will fail.
+ *
+ * \param chain    A pointer to an existing chain.
+ * \param handle   The I/O handle of the Ogg FLAC stream to read.  The
+ *                 handle will NOT be closed after the metadata is read;
+ *                 that is the duty of the caller.
+ * \param callbacks
+ *                 A set of callbacks to use for I/O.  The mandatory
+ *                 callbacks are \a read, \a seek, and \a tell.
+ * \assert
+ *    \code chain != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if a valid list of metadata blocks was read from
+ *    \a handle, else \c false.  On failure, check the status with
+ *    FLAC__metadata_chain_status().
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks);
+
+/** Checks if writing the given chain would require the use of a
+ *  temporary file, or if it could be written in place.
+ *
+ *  Under certain conditions, padding can be utilized so that writing
+ *  edited metadata back to the FLAC file does not require rewriting the
+ *  entire file.  If rewriting is required, then a temporary workfile is
+ *  required.  When writing metadata using callbacks, you must check
+ *  this function to know whether to call
+ *  FLAC__metadata_chain_write_with_callbacks() or
+ *  FLAC__metadata_chain_write_with_callbacks_and_tempfile().  When
+ *  writing with FLAC__metadata_chain_write(), the temporary file is
+ *  handled internally.
+ *
+ * \param chain    A pointer to an existing chain.
+ * \param use_padding
+ *                 Whether or not padding will be allowed to be used
+ *                 during the write.  The value of \a use_padding given
+ *                 here must match the value later passed to
+ *                 FLAC__metadata_chain_write_with_callbacks() or
+ *                 FLAC__metadata_chain_write_with_callbacks_with_tempfile().
+ * \assert
+ *    \code chain != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if writing the current chain would require a tempfile, or
+ *    \c false if metadata can be written in place.
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding);
+
+/** Write all metadata out to the FLAC file.  This function tries to be as
+ *  efficient as possible; how the metadata is actually written is shown by
+ *  the following:
+ *
+ *  If the current chain is the same size as the existing metadata, the new
+ *  data is written in place.
+ *
+ *  If the current chain is longer than the existing metadata, and
+ *  \a use_padding is \c true, and the last block is a PADDING block of
+ *  sufficient length, the function will truncate the final padding block
+ *  so that the overall size of the metadata is the same as the existing
+ *  metadata, and then just rewrite the metadata.  Otherwise, if not all of
+ *  the above conditions are met, the entire FLAC file must be rewritten.
+ *  If you want to use padding this way it is a good idea to call
+ *  FLAC__metadata_chain_sort_padding() first so that you have the maximum
+ *  amount of padding to work with, unless you need to preserve ordering
+ *  of the PADDING blocks for some reason.
+ *
+ *  If the current chain is shorter than the existing metadata, and
+ *  \a use_padding is \c true, and the final block is a PADDING block, the padding
+ *  is extended to make the overall size the same as the existing data.  If
+ *  \a use_padding is \c true and the last block is not a PADDING block, a new
+ *  PADDING block is added to the end of the new data to make it the same
+ *  size as the existing data (if possible, see the note to
+ *  FLAC__metadata_simple_iterator_set_block() about the four byte limit)
+ *  and the new data is written in place.  If none of the above apply or
+ *  \a use_padding is \c false, the entire FLAC file is rewritten.
+ *
+ *  If \a preserve_file_stats is \c true, the owner and modification time will
+ *  be preserved even if the FLAC file is written.
+ *
+ *  For this write function to be used, the chain must have been read with
+ *  FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(), not
+ *  FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks().
+ *
+ * \param chain               A pointer to an existing chain.
+ * \param use_padding         See above.
+ * \param preserve_file_stats See above.
+ * \assert
+ *    \code chain != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if the write succeeded, else \c false.  On failure,
+ *    check the status with FLAC__metadata_chain_status().
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats);
+
+/** Write all metadata out to a FLAC stream via callbacks.
+ *
+ *  (See FLAC__metadata_chain_write() for the details on how padding is
+ *  used to write metadata in place if possible.)
+ *
+ *  The \a handle must be open for updating and be seekable.  The
+ *  equivalent minimum stdio fopen() file mode is \c "r+" (or \c "r+b"
+ *  for Windows).
+ *
+ *  For this write function to be used, the chain must have been read with
+ *  FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(),
+ *  not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg().
+ *  Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned
+ *  \c false.
+ *
+ * \param chain        A pointer to an existing chain.
+ * \param use_padding  See FLAC__metadata_chain_write()
+ * \param handle       The I/O handle of the FLAC stream to write.  The
+ *                     handle will NOT be closed after the metadata is
+ *                     written; that is the duty of the caller.
+ * \param callbacks    A set of callbacks to use for I/O.  The mandatory
+ *                     callbacks are \a write and \a seek.
+ * \assert
+ *    \code chain != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if the write succeeded, else \c false.  On failure,
+ *    check the status with FLAC__metadata_chain_status().
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks);
+
+/** Write all metadata out to a FLAC stream via callbacks.
+ *
+ *  (See FLAC__metadata_chain_write() for the details on how padding is
+ *  used to write metadata in place if possible.)
+ *
+ *  This version of the write-with-callbacks function must be used when
+ *  FLAC__metadata_chain_check_if_tempfile_needed() returns true.  In
+ *  this function, you must supply an I/O handle corresponding to the
+ *  FLAC file to edit, and a temporary handle to which the new FLAC
+ *  file will be written.  It is the caller's job to move this temporary
+ *  FLAC file on top of the original FLAC file to complete the metadata
+ *  edit.
+ *
+ *  The \a handle must be open for reading and be seekable.  The
+ *  equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb"
+ *  for Windows).
+ *
+ *  The \a temp_handle must be open for writing.  The
+ *  equivalent minimum stdio fopen() file mode is \c "w" (or \c "wb"
+ *  for Windows).  It should be an empty stream, or at least positioned
+ *  at the start-of-file (in which case it is the caller's duty to
+ *  truncate it on return).
+ *
+ *  For this write function to be used, the chain must have been read with
+ *  FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(),
+ *  not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg().
+ *  Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned
+ *  \c true.
+ *
+ * \param chain        A pointer to an existing chain.
+ * \param use_padding  See FLAC__metadata_chain_write()
+ * \param handle       The I/O handle of the original FLAC stream to read.
+ *                     The handle will NOT be closed after the metadata is
+ *                     written; that is the duty of the caller.
+ * \param callbacks    A set of callbacks to use for I/O on \a handle.
+ *                     The mandatory callbacks are \a read, \a seek, and
+ *                     \a eof.
+ * \param temp_handle  The I/O handle of the FLAC stream to write.  The
+ *                     handle will NOT be closed after the metadata is
+ *                     written; that is the duty of the caller.
+ * \param temp_callbacks
+ *                     A set of callbacks to use for I/O on temp_handle.
+ *                     The only mandatory callback is \a write.
+ * \assert
+ *    \code chain != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if the write succeeded, else \c false.  On failure,
+ *    check the status with FLAC__metadata_chain_status().
+ */
+FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks);
+
+/** Merge adjacent PADDING blocks into a single block.
+ *
+ * \note This function does not write to the FLAC file, it only
+ * modifies the chain.
+ *
+ * \warning Any iterator on the current chain will become invalid after this
+ * call.  You should delete the iterator and get a new one.
+ *
+ * \param chain               A pointer to an existing chain.
+ * \assert
+ *    \code chain != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain);
+
+/** This function will move all PADDING blocks to the end on the metadata,
+ *  then merge them into a single block.
+ *
+ * \note This function does not write to the FLAC file, it only
+ * modifies the chain.
+ *
+ * \warning Any iterator on the current chain will become invalid after this
+ * call.  You should delete the iterator and get a new one.
+ *
+ * \param chain  A pointer to an existing chain.
+ * \assert
+ *    \code chain != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain);
+
+
+/*********** FLAC__Metadata_Iterator ***********/
+
+/** Create a new iterator instance.
+ *
+ * \retval FLAC__Metadata_Iterator*
+ *    \c NULL if there was an error allocating memory, else the new instance.
+ */
+FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void);
+
+/** Free an iterator instance.  Deletes the object pointed to by \a iterator.
+ *
+ * \param iterator  A pointer to an existing iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator);
+
+/** Initialize the iterator to point to the first metadata block in the
+ *  given chain.
+ *
+ * \param iterator  A pointer to an existing iterator.
+ * \param chain     A pointer to an existing and initialized (read) chain.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \code chain != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain);
+
+/** Moves the iterator forward one metadata block, returning \c false if
+ *  already at the end.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ * \retval FLAC__bool
+ *    \c false if already at the last metadata block of the chain, else
+ *    \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator);
+
+/** Moves the iterator backward one metadata block, returning \c false if
+ *  already at the beginning.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ * \retval FLAC__bool
+ *    \c false if already at the first metadata block of the chain, else
+ *    \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator);
+
+/** Get the type of the metadata block at the current position.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ * \retval FLAC__MetadataType
+ *    The type of the metadata block at the current iterator position.
+ */
+FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator);
+
+/** Get the metadata block at the current position.  You can modify
+ *  the block in place but must write the chain before the changes
+ *  are reflected to the FLAC file.  You do not need to call
+ *  FLAC__metadata_iterator_set_block() to reflect the changes;
+ *  the pointer returned by FLAC__metadata_iterator_get_block()
+ *  points directly into the chain.
+ *
+ * \warning
+ * Do not call FLAC__metadata_object_delete() on the returned object;
+ * to delete a block use FLAC__metadata_iterator_delete_block().
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ * \retval FLAC__StreamMetadata*
+ *    The current metadata block.
+ */
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator);
+
+/** Set the metadata block at the current position, replacing the existing
+ *  block.  The new block passed in becomes owned by the chain and it will be
+ *  deleted when the chain is deleted.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \param block     A pointer to a metadata block.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ *    \code block != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the conditions in the above description are not met, or
+ *    a memory allocation error occurs, otherwise \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block);
+
+/** Removes the current block from the chain.  If \a replace_with_padding is
+ *  \c true, the block will instead be replaced with a padding block of equal
+ *  size.  You can not delete the STREAMINFO block.  The iterator will be
+ *  left pointing to the block before the one just "deleted", even if
+ *  \a replace_with_padding is \c true.
+ *
+ * \param iterator              A pointer to an existing initialized iterator.
+ * \param replace_with_padding  See above.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ * \retval FLAC__bool
+ *    \c false if the conditions in the above description are not met,
+ *    otherwise \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding);
+
+/** Insert a new block before the current block.  You cannot insert a block
+ *  before the first STREAMINFO block.  You cannot insert a STREAMINFO block
+ *  as there can be only one, the one that already exists at the head when you
+ *  read in a chain.  The chain takes ownership of the new block and it will be
+ *  deleted when the chain is deleted.  The iterator will be left pointing to
+ *  the new block.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \param block     A pointer to a metadata block to insert.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ * \retval FLAC__bool
+ *    \c false if the conditions in the above description are not met, or
+ *    a memory allocation error occurs, otherwise \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block);
+
+/** Insert a new block after the current block.  You cannot insert a STREAMINFO
+ *  block as there can be only one, the one that already exists at the head when
+ *  you read in a chain.  The chain takes ownership of the new block and it will
+ *  be deleted when the chain is deleted.  The iterator will be left pointing to
+ *  the new block.
+ *
+ * \param iterator  A pointer to an existing initialized iterator.
+ * \param block     A pointer to a metadata block to insert.
+ * \assert
+ *    \code iterator != NULL \endcode
+ *    \a iterator has been successfully initialized with
+ *    FLAC__metadata_iterator_init()
+ * \retval FLAC__bool
+ *    \c false if the conditions in the above description are not met, or
+ *    a memory allocation error occurs, otherwise \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block);
+
+/* \} */
+
+
+/** \defgroup flac_metadata_object FLAC/metadata.h: metadata object methods
+ *  \ingroup flac_metadata
+ *
+ * \brief
+ * This module contains methods for manipulating FLAC metadata objects.
+ *
+ * Since many are variable length we have to be careful about the memory
+ * management.  We decree that all pointers to data in the object are
+ * owned by the object and memory-managed by the object.
+ *
+ * Use the FLAC__metadata_object_new() and FLAC__metadata_object_delete()
+ * functions to create all instances.  When using the
+ * FLAC__metadata_object_set_*() functions to set pointers to data, set
+ * \a copy to \c true to have the function make it's own copy of the data, or
+ * to \c false to give the object ownership of your data.  In the latter case
+ * your pointer must be freeable by free() and will be free()d when the object
+ * is FLAC__metadata_object_delete()d.  It is legal to pass a null pointer as
+ * the data pointer to a FLAC__metadata_object_set_*() function as long as
+ * the length argument is 0 and the \a copy argument is \c false.
+ *
+ * The FLAC__metadata_object_new() and FLAC__metadata_object_clone() function
+ * will return \c NULL in the case of a memory allocation error, otherwise a new
+ * object.  The FLAC__metadata_object_set_*() functions return \c false in the
+ * case of a memory allocation error.
+ *
+ * We don't have the convenience of C++ here, so note that the library relies
+ * on you to keep the types straight.  In other words, if you pass, for
+ * example, a FLAC__StreamMetadata* that represents a STREAMINFO block to
+ * FLAC__metadata_object_application_set_data(), you will get an assertion
+ * failure.
+ *
+ * For convenience the FLAC__metadata_object_vorbiscomment_*() functions
+ * maintain a trailing NUL on each Vorbis comment entry.  This is not counted
+ * toward the length or stored in the stream, but it can make working with plain
+ * comments (those that don't contain embedded-NULs in the value) easier.
+ * Entries passed into these functions have trailing NULs added if missing, and
+ * returned entries are guaranteed to have a trailing NUL.
+ *
+ * The FLAC__metadata_object_vorbiscomment_*() functions that take a Vorbis
+ * comment entry/name/value will first validate that it complies with the Vorbis
+ * comment specification and return false if it does not.
+ *
+ * There is no need to recalculate the length field on metadata blocks you
+ * have modified.  They will be calculated automatically before they  are
+ * written back to a file.
+ *
+ * \{
+ */
+
+
+/** Create a new metadata object instance of the given type.
+ *
+ *  The object will be "empty"; i.e. values and data pointers will be \c 0,
+ *  with the exception of FLAC__METADATA_TYPE_VORBIS_COMMENT, which will have
+ *  the vendor string set (but zero comments).
+ *
+ *  Do not pass in a value greater than or equal to
+ *  \a FLAC__METADATA_TYPE_UNDEFINED unless you really know what you're
+ *  doing.
+ *
+ * \param type  Type of object to create
+ * \retval FLAC__StreamMetadata*
+ *    \c NULL if there was an error allocating memory or the type code is
+ *    greater than FLAC__MAX_METADATA_TYPE_CODE, else the new instance.
+ */
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type);
+
+/** Create a copy of an existing metadata object.
+ *
+ *  The copy is a "deep" copy, i.e. dynamically allocated data within the
+ *  object is also copied.  The caller takes ownership of the new block and
+ *  is responsible for freeing it with FLAC__metadata_object_delete().
+ *
+ * \param object  Pointer to object to copy.
+ * \assert
+ *    \code object != NULL \endcode
+ * \retval FLAC__StreamMetadata*
+ *    \c NULL if there was an error allocating memory, else the new instance.
+ */
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object);
+
+/** Free a metadata object.  Deletes the object pointed to by \a object.
+ *
+ *  The delete is a "deep" delete, i.e. dynamically allocated data within the
+ *  object is also deleted.
+ *
+ * \param object  A pointer to an existing object.
+ * \assert
+ *    \code object != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object);
+
+/** Compares two metadata objects.
+ *
+ *  The compare is "deep", i.e. dynamically allocated data within the
+ *  object is also compared.
+ *
+ * \param block1  A pointer to an existing object.
+ * \param block2  A pointer to an existing object.
+ * \assert
+ *    \code block1 != NULL \endcode
+ *    \code block2 != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if objects are identical, else \c false.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2);
+
+/** Sets the application data of an APPLICATION block.
+ *
+ *  If \a copy is \c true, a copy of the data is stored; otherwise, the object
+ *  takes ownership of the pointer.  The existing data will be freed if this
+ *  function is successful, otherwise the original data will remain if \a copy
+ *  is \c true and malloc() fails.
+ *
+ * \note It is safe to pass a const pointer to \a data if \a copy is \c true.
+ *
+ * \param object  A pointer to an existing APPLICATION object.
+ * \param data    A pointer to the data to set.
+ * \param length  The length of \a data in bytes.
+ * \param copy    See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_APPLICATION \endcode
+ *    \code (data != NULL && length > 0) ||
+ * (data == NULL && length == 0 && copy == false) \endcode
+ * \retval FLAC__bool
+ *    \c false if \a copy is \c true and malloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, uint32_t length, FLAC__bool copy);
+
+/** Resize the seekpoint array.
+ *
+ *  If the size shrinks, elements will truncated; if it grows, new placeholder
+ *  points will be added to the end.
+ *
+ * \param object          A pointer to an existing SEEKTABLE object.
+ * \param new_num_points  The desired length of the array; may be \c 0.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ *    \code (object->data.seek_table.points == NULL && object->data.seek_table.num_points == 0) ||
+ * (object->data.seek_table.points != NULL && object->data.seek_table.num_points > 0) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation error, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, uint32_t new_num_points);
+
+/** Set a seekpoint in a seektable.
+ *
+ * \param object     A pointer to an existing SEEKTABLE object.
+ * \param point_num  Index into seekpoint array to set.
+ * \param point      The point to set.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ *    \code object->data.seek_table.num_points > point_num \endcode
+ */
+FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point);
+
+/** Insert a seekpoint into a seektable.
+ *
+ * \param object     A pointer to an existing SEEKTABLE object.
+ * \param point_num  Index into seekpoint array to set.
+ * \param point      The point to set.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ *    \code object->data.seek_table.num_points >= point_num \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation error, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point);
+
+/** Delete a seekpoint from a seektable.
+ *
+ * \param object     A pointer to an existing SEEKTABLE object.
+ * \param point_num  Index into seekpoint array to set.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ *    \code object->data.seek_table.num_points > point_num \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation error, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, uint32_t point_num);
+
+/** Check a seektable to see if it conforms to the FLAC specification.
+ *  See the format specification for limits on the contents of the
+ *  seektable.
+ *
+ * \param object  A pointer to an existing SEEKTABLE object.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ * \retval FLAC__bool
+ *    \c false if seek table is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object);
+
+/** Append a number of placeholder points to the end of a seek table.
+ *
+ * \note
+ * As with the other ..._seektable_template_... functions, you should
+ * call FLAC__metadata_object_seektable_template_sort() when finished
+ * to make the seek table legal.
+ *
+ * \param object  A pointer to an existing SEEKTABLE object.
+ * \param num     The number of placeholder points to append.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, uint32_t num);
+
+/** Append a specific seek point template to the end of a seek table.
+ *
+ * \note
+ * As with the other ..._seektable_template_... functions, you should
+ * call FLAC__metadata_object_seektable_template_sort() when finished
+ * to make the seek table legal.
+ *
+ * \param object  A pointer to an existing SEEKTABLE object.
+ * \param sample_number  The sample number of the seek point template.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number);
+
+/** Append specific seek point templates to the end of a seek table.
+ *
+ * \note
+ * As with the other ..._seektable_template_... functions, you should
+ * call FLAC__metadata_object_seektable_template_sort() when finished
+ * to make the seek table legal.
+ *
+ * \param object  A pointer to an existing SEEKTABLE object.
+ * \param sample_numbers  An array of sample numbers for the seek points.
+ * \param num     The number of seek point templates to append.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], uint32_t num);
+
+/** Append a set of evenly-spaced seek point templates to the end of a
+ *  seek table.
+ *
+ * \note
+ * As with the other ..._seektable_template_... functions, you should
+ * call FLAC__metadata_object_seektable_template_sort() when finished
+ * to make the seek table legal.
+ *
+ * \param object  A pointer to an existing SEEKTABLE object.
+ * \param num     The number of placeholder points to append.
+ * \param total_samples  The total number of samples to be encoded;
+ *                       the seekpoints will be spaced approximately
+ *                       \a total_samples / \a num samples apart.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ *    \code total_samples > 0 \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, uint32_t num, FLAC__uint64 total_samples);
+
+/** Append a set of evenly-spaced seek point templates to the end of a
+ *  seek table.
+ *
+ * \note
+ * As with the other ..._seektable_template_... functions, you should
+ * call FLAC__metadata_object_seektable_template_sort() when finished
+ * to make the seek table legal.
+ *
+ * \param object  A pointer to an existing SEEKTABLE object.
+ * \param samples The number of samples apart to space the placeholder
+ *                points.  The first point will be at sample \c 0, the
+ *                second at sample \a samples, then 2*\a samples, and
+ *                so on.  As long as \a samples and \a total_samples
+ *                are greater than \c 0, there will always be at least
+ *                one seekpoint at sample \c 0.
+ * \param total_samples  The total number of samples to be encoded;
+ *                       the seekpoints will be spaced
+ *                       \a samples samples apart.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ *    \code samples > 0 \endcode
+ *    \code total_samples > 0 \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, uint32_t samples, FLAC__uint64 total_samples);
+
+/** Sort a seek table's seek points according to the format specification,
+ *  removing duplicates.
+ *
+ * \param object   A pointer to a seek table to be sorted.
+ * \param compact  If \c false, behaves like FLAC__format_seektable_sort().
+ *                 If \c true, duplicates are deleted and the seek table is
+ *                 shrunk appropriately; the number of placeholder points
+ *                 present in the seek table will be the same after the call
+ *                 as before.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode
+ * \retval FLAC__bool
+ *    \c false if realloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact);
+
+/** Sets the vendor string in a VORBIS_COMMENT block.
+ *
+ *  For convenience, a trailing NUL is added to the entry if it doesn't have
+ *  one already.
+ *
+ *  If \a copy is \c true, a copy of the entry is stored; otherwise, the object
+ *  takes ownership of the \c entry.entry pointer.
+ *
+ *  \note If this function returns \c false, the caller still owns the
+ *  pointer.
+ *
+ * \param object  A pointer to an existing VORBIS_COMMENT object.
+ * \param entry   The entry to set the vendor string to.
+ * \param copy    See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code (entry.entry != NULL && entry.length > 0) ||
+ * (entry.entry == NULL && entry.length == 0) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails or \a entry does not comply with the
+ *    Vorbis comment specification, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy);
+
+/** Resize the comment array.
+ *
+ *  If the size shrinks, elements will truncated; if it grows, new empty
+ *  fields will be added to the end.
+ *
+ * \param object            A pointer to an existing VORBIS_COMMENT object.
+ * \param new_num_comments  The desired length of the array; may be \c 0.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code (object->data.vorbis_comment.comments == NULL && object->data.vorbis_comment.num_comments == 0) ||
+ * (object->data.vorbis_comment.comments != NULL && object->data.vorbis_comment.num_comments > 0) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, uint32_t new_num_comments);
+
+/** Sets a comment in a VORBIS_COMMENT block.
+ *
+ *  For convenience, a trailing NUL is added to the entry if it doesn't have
+ *  one already.
+ *
+ *  If \a copy is \c true, a copy of the entry is stored; otherwise, the object
+ *  takes ownership of the \c entry.entry pointer.
+ *
+ *  \note If this function returns \c false, the caller still owns the
+ *  pointer.
+ *
+ * \param object       A pointer to an existing VORBIS_COMMENT object.
+ * \param comment_num  Index into comment array to set.
+ * \param entry        The entry to set the comment to.
+ * \param copy         See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code comment_num < object->data.vorbis_comment.num_comments \endcode
+ *    \code (entry.entry != NULL && entry.length > 0) ||
+ * (entry.entry == NULL && entry.length == 0) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails or \a entry does not comply with the
+ *    Vorbis comment specification, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy);
+
+/** Insert a comment in a VORBIS_COMMENT block at the given index.
+ *
+ *  For convenience, a trailing NUL is added to the entry if it doesn't have
+ *  one already.
+ *
+ *  If \a copy is \c true, a copy of the entry is stored; otherwise, the object
+ *  takes ownership of the \c entry.entry pointer.
+ *
+ *  \note If this function returns \c false, the caller still owns the
+ *  pointer.
+ *
+ * \param object       A pointer to an existing VORBIS_COMMENT object.
+ * \param comment_num  The index at which to insert the comment.  The comments
+ *                     at and after \a comment_num move right one position.
+ *                     To append a comment to the end, set \a comment_num to
+ *                     \c object->data.vorbis_comment.num_comments .
+ * \param entry        The comment to insert.
+ * \param copy         See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code object->data.vorbis_comment.num_comments >= comment_num \endcode
+ *    \code (entry.entry != NULL && entry.length > 0) ||
+ * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails or \a entry does not comply with the
+ *    Vorbis comment specification, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy);
+
+/** Appends a comment to a VORBIS_COMMENT block.
+ *
+ *  For convenience, a trailing NUL is added to the entry if it doesn't have
+ *  one already.
+ *
+ *  If \a copy is \c true, a copy of the entry is stored; otherwise, the object
+ *  takes ownership of the \c entry.entry pointer.
+ *
+ *  \note If this function returns \c false, the caller still owns the
+ *  pointer.
+ *
+ * \param object       A pointer to an existing VORBIS_COMMENT object.
+ * \param entry        The comment to insert.
+ * \param copy         See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code (entry.entry != NULL && entry.length > 0) ||
+ * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails or \a entry does not comply with the
+ *    Vorbis comment specification, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy);
+
+/** Replaces comments in a VORBIS_COMMENT block with a new one.
+ *
+ *  For convenience, a trailing NUL is added to the entry if it doesn't have
+ *  one already.
+ *
+ *  Depending on the value of \a all, either all or just the first comment
+ *  whose field name(s) match the given entry's name will be replaced by the
+ *  given entry.  If no comments match, \a entry will simply be appended.
+ *
+ *  If \a copy is \c true, a copy of the entry is stored; otherwise, the object
+ *  takes ownership of the \c entry.entry pointer.
+ *
+ *  \note If this function returns \c false, the caller still owns the
+ *  pointer.
+ *
+ * \param object       A pointer to an existing VORBIS_COMMENT object.
+ * \param entry        The comment to insert.
+ * \param all          If \c true, all comments whose field name matches
+ *                     \a entry's field name will be removed, and \a entry will
+ *                     be inserted at the position of the first matching
+ *                     comment.  If \c false, only the first comment whose
+ *                     field name matches \a entry's field name will be
+ *                     replaced with \a entry.
+ * \param copy         See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code (entry.entry != NULL && entry.length > 0) ||
+ * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails or \a entry does not comply with the
+ *    Vorbis comment specification, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy);
+
+/** Delete a comment in a VORBIS_COMMENT block at the given index.
+ *
+ * \param object       A pointer to an existing VORBIS_COMMENT object.
+ * \param comment_num  The index of the comment to delete.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code object->data.vorbis_comment.num_comments > comment_num \endcode
+ * \retval FLAC__bool
+ *    \c false if realloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, uint32_t comment_num);
+
+/** Creates a Vorbis comment entry from NUL-terminated name and value strings.
+ *
+ *  On return, the filled-in \a entry->entry pointer will point to malloc()ed
+ *  memory and shall be owned by the caller.  For convenience the entry will
+ *  have a terminating NUL.
+ *
+ * \param entry              A pointer to a Vorbis comment entry.  The entry's
+ *                           \c entry pointer should not point to allocated
+ *                           memory as it will be overwritten.
+ * \param field_name         The field name in ASCII, \c NUL terminated.
+ * \param field_value        The field value in UTF-8, \c NUL terminated.
+ * \assert
+ *    \code entry != NULL \endcode
+ *    \code field_name != NULL \endcode
+ *    \code field_value != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if malloc() fails, or if \a field_name or \a field_value does
+ *    not comply with the Vorbis comment specification, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value);
+
+/** Splits a Vorbis comment entry into NUL-terminated name and value strings.
+ *
+ *  The returned pointers to name and value will be allocated by malloc()
+ *  and shall be owned by the caller.
+ *
+ * \param entry              An existing Vorbis comment entry.
+ * \param field_name         The address of where the returned pointer to the
+ *                           field name will be stored.
+ * \param field_value        The address of where the returned pointer to the
+ *                           field value will be stored.
+ * \assert
+ *    \code (entry.entry != NULL && entry.length > 0) \endcode
+ *    \code memchr(entry.entry, '=', entry.length) != NULL \endcode
+ *    \code field_name != NULL \endcode
+ *    \code field_value != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation fails or \a entry does not comply with the
+ *    Vorbis comment specification, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value);
+
+/** Check if the given Vorbis comment entry's field name matches the given
+ *  field name.
+ *
+ * \param entry              An existing Vorbis comment entry.
+ * \param field_name         The field name to check.
+ * \param field_name_length  The length of \a field_name, not including the
+ *                           terminating \c NUL.
+ * \assert
+ *    \code (entry.entry != NULL && entry.length > 0) \endcode
+ * \retval FLAC__bool
+ *    \c true if the field names match, else \c false
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, uint32_t field_name_length);
+
+/** Find a Vorbis comment with the given field name.
+ *
+ *  The search begins at entry number \a offset; use an offset of 0 to
+ *  search from the beginning of the comment array.
+ *
+ * \param object      A pointer to an existing VORBIS_COMMENT object.
+ * \param offset      The offset into the comment array from where to start
+ *                    the search.
+ * \param field_name  The field name of the comment to find.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ *    \code field_name != NULL \endcode
+ * \retval int
+ *    The offset in the comment array of the first comment whose field
+ *    name matches \a field_name, or \c -1 if no match was found.
+ */
+FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, uint32_t offset, const char *field_name);
+
+/** Remove first Vorbis comment matching the given field name.
+ *
+ * \param object      A pointer to an existing VORBIS_COMMENT object.
+ * \param field_name  The field name of comment to delete.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ * \retval int
+ *    \c -1 for memory allocation error, \c 0 for no matching entries,
+ *    \c 1 for one matching entry deleted.
+ */
+FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name);
+
+/** Remove all Vorbis comments matching the given field name.
+ *
+ * \param object      A pointer to an existing VORBIS_COMMENT object.
+ * \param field_name  The field name of comments to delete.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode
+ * \retval int
+ *    \c -1 for memory allocation error, \c 0 for no matching entries,
+ *    else the number of matching entries deleted.
+ */
+FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name);
+
+/** Create a new CUESHEET track instance.
+ *
+ *  The object will be "empty"; i.e. values and data pointers will be \c 0.
+ *
+ * \retval FLAC__StreamMetadata_CueSheet_Track*
+ *    \c NULL if there was an error allocating memory, else the new instance.
+ */
+FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void);
+
+/** Create a copy of an existing CUESHEET track object.
+ *
+ *  The copy is a "deep" copy, i.e. dynamically allocated data within the
+ *  object is also copied.  The caller takes ownership of the new object and
+ *  is responsible for freeing it with
+ *  FLAC__metadata_object_cuesheet_track_delete().
+ *
+ * \param object  Pointer to object to copy.
+ * \assert
+ *    \code object != NULL \endcode
+ * \retval FLAC__StreamMetadata_CueSheet_Track*
+ *    \c NULL if there was an error allocating memory, else the new instance.
+ */
+FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object);
+
+/** Delete a CUESHEET track object
+ *
+ * \param object       A pointer to an existing CUESHEET track object.
+ * \assert
+ *    \code object != NULL \endcode
+ */
+FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object);
+
+/** Resize a track's index point array.
+ *
+ *  If the size shrinks, elements will truncated; if it grows, new blank
+ *  indices will be added to the end.
+ *
+ * \param object           A pointer to an existing CUESHEET object.
+ * \param track_num        The index of the track to modify.  NOTE: this is not
+ *                         necessarily the same as the track's \a number field.
+ * \param new_num_indices  The desired length of the array; may be \c 0.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code object->data.cue_sheet.num_tracks > track_num \endcode
+ *    \code (object->data.cue_sheet.tracks[track_num].indices == NULL && object->data.cue_sheet.tracks[track_num].num_indices == 0) ||
+ * (object->data.cue_sheet.tracks[track_num].indices != NULL && object->data.cue_sheet.tracks[track_num].num_indices > 0) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation error, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t new_num_indices);
+
+/** Insert an index point in a CUESHEET track at the given index.
+ *
+ * \param object       A pointer to an existing CUESHEET object.
+ * \param track_num    The index of the track to modify.  NOTE: this is not
+ *                     necessarily the same as the track's \a number field.
+ * \param index_num    The index into the track's index array at which to
+ *                     insert the index point.  NOTE: this is not necessarily
+ *                     the same as the index point's \a number field.  The
+ *                     indices at and after \a index_num move right one
+ *                     position.  To append an index point to the end, set
+ *                     \a index_num to
+ *                     \c object->data.cue_sheet.tracks[track_num].num_indices .
+ * \param index        The index point to insert.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code object->data.cue_sheet.num_tracks > track_num \endcode
+ *    \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode
+ * \retval FLAC__bool
+ *    \c false if realloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num, FLAC__StreamMetadata_CueSheet_Index index);
+
+/** Insert a blank index point in a CUESHEET track at the given index.
+ *
+ *  A blank index point is one in which all field values are zero.
+ *
+ * \param object       A pointer to an existing CUESHEET object.
+ * \param track_num    The index of the track to modify.  NOTE: this is not
+ *                     necessarily the same as the track's \a number field.
+ * \param index_num    The index into the track's index array at which to
+ *                     insert the index point.  NOTE: this is not necessarily
+ *                     the same as the index point's \a number field.  The
+ *                     indices at and after \a index_num move right one
+ *                     position.  To append an index point to the end, set
+ *                     \a index_num to
+ *                     \c object->data.cue_sheet.tracks[track_num].num_indices .
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code object->data.cue_sheet.num_tracks > track_num \endcode
+ *    \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode
+ * \retval FLAC__bool
+ *    \c false if realloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num);
+
+/** Delete an index point in a CUESHEET track at the given index.
+ *
+ * \param object       A pointer to an existing CUESHEET object.
+ * \param track_num    The index into the track array of the track to
+ *                     modify.  NOTE: this is not necessarily the same
+ *                     as the track's \a number field.
+ * \param index_num    The index into the track's index array of the index
+ *                     to delete.  NOTE: this is not necessarily the same
+ *                     as the index's \a number field.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code object->data.cue_sheet.num_tracks > track_num \endcode
+ *    \code object->data.cue_sheet.tracks[track_num].num_indices > index_num \endcode
+ * \retval FLAC__bool
+ *    \c false if realloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num);
+
+/** Resize the track array.
+ *
+ *  If the size shrinks, elements will truncated; if it grows, new blank
+ *  tracks will be added to the end.
+ *
+ * \param object            A pointer to an existing CUESHEET object.
+ * \param new_num_tracks    The desired length of the array; may be \c 0.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code (object->data.cue_sheet.tracks == NULL && object->data.cue_sheet.num_tracks == 0) ||
+ * (object->data.cue_sheet.tracks != NULL && object->data.cue_sheet.num_tracks > 0) \endcode
+ * \retval FLAC__bool
+ *    \c false if memory allocation error, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, uint32_t new_num_tracks);
+
+/** Sets a track in a CUESHEET block.
+ *
+ *  If \a copy is \c true, a copy of the track is stored; otherwise, the object
+ *  takes ownership of the \a track pointer.
+ *
+ * \param object       A pointer to an existing CUESHEET object.
+ * \param track_num    Index into track array to set.  NOTE: this is not
+ *                     necessarily the same as the track's \a number field.
+ * \param track        The track to set the track to.  You may safely pass in
+ *                     a const pointer if \a copy is \c true.
+ * \param copy         See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code track_num < object->data.cue_sheet.num_tracks \endcode
+ *    \code (track->indices != NULL && track->num_indices > 0) ||
+ * (track->indices == NULL && track->num_indices == 0) \endcode
+ * \retval FLAC__bool
+ *    \c false if \a copy is \c true and malloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy);
+
+/** Insert a track in a CUESHEET block at the given index.
+ *
+ *  If \a copy is \c true, a copy of the track is stored; otherwise, the object
+ *  takes ownership of the \a track pointer.
+ *
+ * \param object       A pointer to an existing CUESHEET object.
+ * \param track_num    The index at which to insert the track.  NOTE: this
+ *                     is not necessarily the same as the track's \a number
+ *                     field.  The tracks at and after \a track_num move right
+ *                     one position.  To append a track to the end, set
+ *                     \a track_num to \c object->data.cue_sheet.num_tracks .
+ * \param track        The track to insert.  You may safely pass in a const
+ *                     pointer if \a copy is \c true.
+ * \param copy         See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code object->data.cue_sheet.num_tracks >= track_num \endcode
+ * \retval FLAC__bool
+ *    \c false if \a copy is \c true and malloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy);
+
+/** Insert a blank track in a CUESHEET block at the given index.
+ *
+ *  A blank track is one in which all field values are zero.
+ *
+ * \param object       A pointer to an existing CUESHEET object.
+ * \param track_num    The index at which to insert the track.  NOTE: this
+ *                     is not necessarily the same as the track's \a number
+ *                     field.  The tracks at and after \a track_num move right
+ *                     one position.  To append a track to the end, set
+ *                     \a track_num to \c object->data.cue_sheet.num_tracks .
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code object->data.cue_sheet.num_tracks >= track_num \endcode
+ * \retval FLAC__bool
+ *    \c false if \a copy is \c true and malloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, uint32_t track_num);
+
+/** Delete a track in a CUESHEET block at the given index.
+ *
+ * \param object       A pointer to an existing CUESHEET object.
+ * \param track_num    The index into the track array of the track to
+ *                     delete.  NOTE: this is not necessarily the same
+ *                     as the track's \a number field.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ *    \code object->data.cue_sheet.num_tracks > track_num \endcode
+ * \retval FLAC__bool
+ *    \c false if realloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, uint32_t track_num);
+
+/** Check a cue sheet to see if it conforms to the FLAC specification.
+ *  See the format specification for limits on the contents of the
+ *  cue sheet.
+ *
+ * \param object     A pointer to an existing CUESHEET object.
+ * \param check_cd_da_subset  If \c true, check CUESHEET against more
+ *                   stringent requirements for a CD-DA (audio) disc.
+ * \param violation  Address of a pointer to a string.  If there is a
+ *                   violation, a pointer to a string explanation of the
+ *                   violation will be returned here. \a violation may be
+ *                   \c NULL if you don't need the returned string.  Do not
+ *                   free the returned string; it will always point to static
+ *                   data.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ * \retval FLAC__bool
+ *    \c false if cue sheet is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation);
+
+/** Calculate and return the CDDB/freedb ID for a cue sheet.  The function
+ *  assumes the cue sheet corresponds to a CD; the result is undefined
+ *  if the cuesheet's is_cd bit is not set.
+ *
+ * \param object     A pointer to an existing CUESHEET object.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode
+ * \retval FLAC__uint32
+ *    The unsigned integer representation of the CDDB/freedb ID
+ */
+FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object);
+
+/** Sets the MIME type of a PICTURE block.
+ *
+ *  If \a copy is \c true, a copy of the string is stored; otherwise, the object
+ *  takes ownership of the pointer.  The existing string will be freed if this
+ *  function is successful, otherwise the original string will remain if \a copy
+ *  is \c true and malloc() fails.
+ *
+ * \note It is safe to pass a const pointer to \a mime_type if \a copy is \c true.
+ *
+ * \param object      A pointer to an existing PICTURE object.
+ * \param mime_type   A pointer to the MIME type string.  The string must be
+ *                    ASCII characters 0x20-0x7e, NUL-terminated.  No validation
+ *                    is done.
+ * \param copy        See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode
+ *    \code (mime_type != NULL) \endcode
+ * \retval FLAC__bool
+ *    \c false if \a copy is \c true and malloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy);
+
+/** Sets the description of a PICTURE block.
+ *
+ *  If \a copy is \c true, a copy of the string is stored; otherwise, the object
+ *  takes ownership of the pointer.  The existing string will be freed if this
+ *  function is successful, otherwise the original string will remain if \a copy
+ *  is \c true and malloc() fails.
+ *
+ * \note It is safe to pass a const pointer to \a description if \a copy is \c true.
+ *
+ * \param object      A pointer to an existing PICTURE object.
+ * \param description A pointer to the description string.  The string must be
+ *                    valid UTF-8, NUL-terminated.  No validation is done.
+ * \param copy        See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode
+ *    \code (description != NULL) \endcode
+ * \retval FLAC__bool
+ *    \c false if \a copy is \c true and malloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy);
+
+/** Sets the picture data of a PICTURE block.
+ *
+ *  If \a copy is \c true, a copy of the data is stored; otherwise, the object
+ *  takes ownership of the pointer.  Also sets the \a data_length field of the
+ *  metadata object to what is passed in as the \a length parameter.  The
+ *  existing data will be freed if this function is successful, otherwise the
+ *  original data and data_length will remain if \a copy is \c true and
+ *  malloc() fails.
+ *
+ * \note It is safe to pass a const pointer to \a data if \a copy is \c true.
+ *
+ * \param object  A pointer to an existing PICTURE object.
+ * \param data    A pointer to the data to set.
+ * \param length  The length of \a data in bytes.
+ * \param copy    See above.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode
+ *    \code (data != NULL && length > 0) ||
+ * (data == NULL && length == 0 && copy == false) \endcode
+ * \retval FLAC__bool
+ *    \c false if \a copy is \c true and malloc() fails, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy);
+
+/** Check a PICTURE block to see if it conforms to the FLAC specification.
+ *  See the format specification for limits on the contents of the
+ *  PICTURE block.
+ *
+ * \param object     A pointer to existing PICTURE block to be checked.
+ * \param violation  Address of a pointer to a string.  If there is a
+ *                   violation, a pointer to a string explanation of the
+ *                   violation will be returned here. \a violation may be
+ *                   \c NULL if you don't need the returned string.  Do not
+ *                   free the returned string; it will always point to static
+ *                   data.
+ * \assert
+ *    \code object != NULL \endcode
+ *    \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode
+ * \retval FLAC__bool
+ *    \c false if PICTURE block is illegal, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation);
+
+/* \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+++ b/src/libflac/FLAC/ordinals.h
@@ -1,0 +1,85 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__ORDINALS_H
+#define FLAC__ORDINALS_H
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+
+/* Microsoft Visual Studio earlier than the 2010 version did not provide
+ * the 1999 ISO C Standard header file <stdint.h>.
+ */
+
+typedef signed __int8 FLAC__int8;
+typedef signed __int16 FLAC__int16;
+typedef signed __int32 FLAC__int32;
+typedef signed __int64 FLAC__int64;
+typedef unsigned __int8 FLAC__uint8;
+typedef unsigned __int16 FLAC__uint16;
+typedef unsigned __int32 FLAC__uint32;
+typedef unsigned __int64 FLAC__uint64;
+
+#else
+
+/* For MSVC 2010 and everything else which provides <stdint.h>. */
+
+#include <stdint.h>
+
+typedef int8_t FLAC__int8;
+typedef uint8_t FLAC__uint8;
+
+typedef int16_t FLAC__int16;
+typedef int32_t FLAC__int32;
+typedef int64_t FLAC__int64;
+typedef uint16_t FLAC__uint16;
+typedef uint32_t FLAC__uint32;
+typedef uint64_t FLAC__uint64;
+
+#endif
+
+typedef int FLAC__bool;
+
+typedef FLAC__uint8 FLAC__byte;
+
+
+#ifdef true
+#undef true
+#endif
+#ifdef false
+#undef false
+#endif
+#ifndef __cplusplus
+#define true 1
+#define false 0
+#endif
+
+#endif
--- /dev/null
+++ b/src/libflac/FLAC/stream_decoder.h
@@ -1,0 +1,1559 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__STREAM_DECODER_H
+#define FLAC__STREAM_DECODER_H
+
+#include <stdio.h> /* for FILE */
+#include "export.h"
+#include "format.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** \file include/FLAC/stream_decoder.h
+ *
+ *  \brief
+ *  This module contains the functions which implement the stream
+ *  decoder.
+ *
+ *  See the detailed documentation in the
+ *  \link flac_stream_decoder stream decoder \endlink module.
+ */
+
+/** \defgroup flac_decoder FLAC/ \*_decoder.h: decoder interfaces
+ *  \ingroup flac
+ *
+ *  \brief
+ *  This module describes the decoder layers provided by libFLAC.
+ *
+ * The stream decoder can be used to decode complete streams either from
+ * the client via callbacks, or directly from a file, depending on how
+ * it is initialized.  When decoding via callbacks, the client provides
+ * callbacks for reading FLAC data and writing decoded samples, and
+ * handling metadata and errors.  If the client also supplies seek-related
+ * callback, the decoder function for sample-accurate seeking within the
+ * FLAC input is also available.  When decoding from a file, the client
+ * needs only supply a filename or open \c FILE* and write/metadata/error
+ * callbacks; the rest of the callbacks are supplied internally.  For more
+ * info see the \link flac_stream_decoder stream decoder \endlink module.
+ */
+
+/** \defgroup flac_stream_decoder FLAC/stream_decoder.h: stream decoder interface
+ *  \ingroup flac_decoder
+ *
+ *  \brief
+ *  This module contains the functions which implement the stream
+ *  decoder.
+ *
+ * The stream decoder can decode native FLAC, and optionally Ogg FLAC
+ * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files.
+ *
+ * The basic usage of this decoder is as follows:
+ * - The program creates an instance of a decoder using
+ *   FLAC__stream_decoder_new().
+ * - The program overrides the default settings using
+ *   FLAC__stream_decoder_set_*() functions.
+ * - The program initializes the instance to validate the settings and
+ *   prepare for decoding using
+ *   - FLAC__stream_decoder_init_stream() or FLAC__stream_decoder_init_FILE()
+ *     or FLAC__stream_decoder_init_file() for native FLAC,
+ *   - FLAC__stream_decoder_init_ogg_stream() or FLAC__stream_decoder_init_ogg_FILE()
+ *     or FLAC__stream_decoder_init_ogg_file() for Ogg FLAC
+ * - The program calls the FLAC__stream_decoder_process_*() functions
+ *   to decode data, which subsequently calls the callbacks.
+ * - The program finishes the decoding with FLAC__stream_decoder_finish(),
+ *   which flushes the input and output and resets the decoder to the
+ *   uninitialized state.
+ * - The instance may be used again or deleted with
+ *   FLAC__stream_decoder_delete().
+ *
+ * In more detail, the program will create a new instance by calling
+ * FLAC__stream_decoder_new(), then call FLAC__stream_decoder_set_*()
+ * functions to override the default decoder options, and call
+ * one of the FLAC__stream_decoder_init_*() functions.
+ *
+ * There are three initialization functions for native FLAC, one for
+ * setting up the decoder to decode FLAC data from the client via
+ * callbacks, and two for decoding directly from a FLAC file.
+ *
+ * For decoding via callbacks, use FLAC__stream_decoder_init_stream().
+ * You must also supply several callbacks for handling I/O.  Some (like
+ * seeking) are optional, depending on the capabilities of the input.
+ *
+ * For decoding directly from a file, use FLAC__stream_decoder_init_FILE()
+ * or FLAC__stream_decoder_init_file().  Then you must only supply an open
+ * \c FILE* or filename and fewer callbacks; the decoder will handle
+ * the other callbacks internally.
+ *
+ * There are three similarly-named init functions for decoding from Ogg
+ * FLAC streams.  Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the
+ * library has been built with Ogg support.
+ *
+ * Once the decoder is initialized, your program will call one of several
+ * functions to start the decoding process:
+ *
+ * - FLAC__stream_decoder_process_single() - Tells the decoder to process at
+ *   most one metadata block or audio frame and return, calling either the
+ *   metadata callback or write callback, respectively, once.  If the decoder
+ *   loses sync it will return with only the error callback being called.
+ * - FLAC__stream_decoder_process_until_end_of_metadata() - Tells the decoder
+ *   to process the stream from the current location and stop upon reaching
+ *   the first audio frame.  The client will get one metadata, write, or error
+ *   callback per metadata block, audio frame, or sync error, respectively.
+ * - FLAC__stream_decoder_process_until_end_of_stream() - Tells the decoder
+ *   to process the stream from the current location until the read callback
+ *   returns FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM or
+ *   FLAC__STREAM_DECODER_READ_STATUS_ABORT.  The client will get one metadata,
+ *   write, or error callback per metadata block, audio frame, or sync error,
+ *   respectively.
+ *
+ * When the decoder has finished decoding (normally or through an abort),
+ * the instance is finished by calling FLAC__stream_decoder_finish(), which
+ * ensures the decoder is in the correct state and frees memory.  Then the
+ * instance may be deleted with FLAC__stream_decoder_delete() or initialized
+ * again to decode another stream.
+ *
+ * Seeking is exposed through the FLAC__stream_decoder_seek_absolute() method.
+ * At any point after the stream decoder has been initialized, the client can
+ * call this function to seek to an exact sample within the stream.
+ * Subsequently, the first time the write callback is called it will be
+ * passed a (possibly partial) block starting at that sample.
+ *
+ * If the client cannot seek via the callback interface provided, but still
+ * has another way of seeking, it can flush the decoder using
+ * FLAC__stream_decoder_flush() and start feeding data from the new position
+ * through the read callback.
+ *
+ * The stream decoder also provides MD5 signature checking.  If this is
+ * turned on before initialization, FLAC__stream_decoder_finish() will
+ * report when the decoded MD5 signature does not match the one stored
+ * in the STREAMINFO block.  MD5 checking is automatically turned off
+ * (until the next FLAC__stream_decoder_reset()) if there is no signature
+ * in the STREAMINFO block or when a seek is attempted.
+ *
+ * The FLAC__stream_decoder_set_metadata_*() functions deserve special
+ * attention.  By default, the decoder only calls the metadata_callback for
+ * the STREAMINFO block.  These functions allow you to tell the decoder
+ * explicitly which blocks to parse and return via the metadata_callback
+ * and/or which to skip.  Use a FLAC__stream_decoder_set_metadata_respond_all(),
+ * FLAC__stream_decoder_set_metadata_ignore() ... or FLAC__stream_decoder_set_metadata_ignore_all(),
+ * FLAC__stream_decoder_set_metadata_respond() ... sequence to exactly specify
+ * which blocks to return.  Remember that metadata blocks can potentially
+ * be big (for example, cover art) so filtering out the ones you don't
+ * use can reduce the memory requirements of the decoder.  Also note the
+ * special forms FLAC__stream_decoder_set_metadata_respond_application(id)
+ * and FLAC__stream_decoder_set_metadata_ignore_application(id) for
+ * filtering APPLICATION blocks based on the application ID.
+ *
+ * STREAMINFO and SEEKTABLE blocks are always parsed and used internally, but
+ * they still can legally be filtered from the metadata_callback.
+ *
+ * \note
+ * The "set" functions may only be called when the decoder is in the
+ * state FLAC__STREAM_DECODER_UNINITIALIZED, i.e. after
+ * FLAC__stream_decoder_new() or FLAC__stream_decoder_finish(), but
+ * before FLAC__stream_decoder_init_*().  If this is the case they will
+ * return \c true, otherwise \c false.
+ *
+ * \note
+ * FLAC__stream_decoder_finish() resets all settings to the constructor
+ * defaults, including the callbacks.
+ *
+ * \{
+ */
+
+
+/** State values for a FLAC__StreamDecoder
+ *
+ * The decoder's state can be obtained by calling FLAC__stream_decoder_get_state().
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0,
+	/**< The decoder is ready to search for metadata. */
+
+	FLAC__STREAM_DECODER_READ_METADATA,
+	/**< The decoder is ready to or is in the process of reading metadata. */
+
+	FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC,
+	/**< The decoder is ready to or is in the process of searching for the
+	 * frame sync code.
+	 */
+
+	FLAC__STREAM_DECODER_READ_FRAME,
+	/**< The decoder is ready to or is in the process of reading a frame. */
+
+	FLAC__STREAM_DECODER_END_OF_STREAM,
+	/**< The decoder has reached the end of the stream. */
+
+	FLAC__STREAM_DECODER_OGG_ERROR,
+	/**< An error occurred in the underlying Ogg layer.  */
+
+	FLAC__STREAM_DECODER_SEEK_ERROR,
+	/**< An error occurred while seeking.  The decoder must be flushed
+	 * with FLAC__stream_decoder_flush() or reset with
+	 * FLAC__stream_decoder_reset() before decoding can continue.
+	 */
+
+	FLAC__STREAM_DECODER_ABORTED,
+	/**< The decoder was aborted by the read or write callback. */
+
+	FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR,
+	/**< An error occurred allocating memory.  The decoder is in an invalid
+	 * state and can no longer be used.
+	 */
+
+	FLAC__STREAM_DECODER_UNINITIALIZED
+	/**< The decoder is in the uninitialized state; one of the
+	 * FLAC__stream_decoder_init_*() functions must be called before samples
+	 * can be processed.
+	 */
+
+} FLAC__StreamDecoderState;
+
+/** Maps a FLAC__StreamDecoderState to a C string.
+ *
+ *  Using a FLAC__StreamDecoderState as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderStateString[];
+
+
+/** Possible return values for the FLAC__stream_decoder_init_*() functions.
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_INIT_STATUS_OK = 0,
+	/**< Initialization was successful. */
+
+	FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER,
+	/**< The library was not compiled with support for the given container
+	 * format.
+	 */
+
+	FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS,
+	/**< A required callback was not supplied. */
+
+	FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR,
+	/**< An error occurred allocating memory. */
+
+	FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE,
+	/**< fopen() failed in FLAC__stream_decoder_init_file() or
+	 * FLAC__stream_decoder_init_ogg_file(). */
+
+	FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED
+	/**< FLAC__stream_decoder_init_*() was called when the decoder was
+	 * already initialized, usually because
+	 * FLAC__stream_decoder_finish() was not called.
+	 */
+
+} FLAC__StreamDecoderInitStatus;
+
+/** Maps a FLAC__StreamDecoderInitStatus to a C string.
+ *
+ *  Using a FLAC__StreamDecoderInitStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderInitStatusString[];
+
+
+/** Return values for the FLAC__StreamDecoder read callback.
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_READ_STATUS_CONTINUE,
+	/**< The read was OK and decoding can continue. */
+
+	FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM,
+	/**< The read was attempted while at the end of the stream.  Note that
+	 * the client must only return this value when the read callback was
+	 * called when already at the end of the stream.  Otherwise, if the read
+	 * itself moves to the end of the stream, the client should still return
+	 * the data and \c FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, and then on
+	 * the next read callback it should return
+	 * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM with a byte count
+	 * of \c 0.
+	 */
+
+	FLAC__STREAM_DECODER_READ_STATUS_ABORT
+	/**< An unrecoverable error occurred.  The decoder will return from the process call. */
+
+} FLAC__StreamDecoderReadStatus;
+
+/** Maps a FLAC__StreamDecoderReadStatus to a C string.
+ *
+ *  Using a FLAC__StreamDecoderReadStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderReadStatusString[];
+
+
+/** Return values for the FLAC__StreamDecoder seek callback.
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_SEEK_STATUS_OK,
+	/**< The seek was OK and decoding can continue. */
+
+	FLAC__STREAM_DECODER_SEEK_STATUS_ERROR,
+	/**< An unrecoverable error occurred.  The decoder will return from the process call. */
+
+	FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED
+	/**< Client does not support seeking. */
+
+} FLAC__StreamDecoderSeekStatus;
+
+/** Maps a FLAC__StreamDecoderSeekStatus to a C string.
+ *
+ *  Using a FLAC__StreamDecoderSeekStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[];
+
+
+/** Return values for the FLAC__StreamDecoder tell callback.
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_TELL_STATUS_OK,
+	/**< The tell was OK and decoding can continue. */
+
+	FLAC__STREAM_DECODER_TELL_STATUS_ERROR,
+	/**< An unrecoverable error occurred.  The decoder will return from the process call. */
+
+	FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED
+	/**< Client does not support telling the position. */
+
+} FLAC__StreamDecoderTellStatus;
+
+/** Maps a FLAC__StreamDecoderTellStatus to a C string.
+ *
+ *  Using a FLAC__StreamDecoderTellStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderTellStatusString[];
+
+
+/** Return values for the FLAC__StreamDecoder length callback.
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_LENGTH_STATUS_OK,
+	/**< The length call was OK and decoding can continue. */
+
+	FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR,
+	/**< An unrecoverable error occurred.  The decoder will return from the process call. */
+
+	FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED
+	/**< Client does not support reporting the length. */
+
+} FLAC__StreamDecoderLengthStatus;
+
+/** Maps a FLAC__StreamDecoderLengthStatus to a C string.
+ *
+ *  Using a FLAC__StreamDecoderLengthStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[];
+
+
+/** Return values for the FLAC__StreamDecoder write callback.
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE,
+	/**< The write was OK and decoding can continue. */
+
+	FLAC__STREAM_DECODER_WRITE_STATUS_ABORT
+	/**< An unrecoverable error occurred.  The decoder will return from the process call. */
+
+} FLAC__StreamDecoderWriteStatus;
+
+/** Maps a FLAC__StreamDecoderWriteStatus to a C string.
+ *
+ *  Using a FLAC__StreamDecoderWriteStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[];
+
+
+/** Possible values passed back to the FLAC__StreamDecoder error callback.
+ *  \c FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC is the generic catch-
+ *  all.  The rest could be caused by bad sync (false synchronization on
+ *  data that is not the start of a frame) or corrupted data.  The error
+ *  itself is the decoder's best guess at what happened assuming a correct
+ *  sync.  For example \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER
+ *  could be caused by a correct sync on the start of a frame, but some
+ *  data in the frame header was corrupted.  Or it could be the result of
+ *  syncing on a point the stream that looked like the starting of a frame
+ *  but was not.  \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM
+ *  could be because the decoder encountered a valid frame made by a future
+ *  version of the encoder which it cannot parse, or because of a false
+ *  sync making it appear as though an encountered frame was generated by
+ *  a future encoder.
+ */
+typedef enum {
+
+	FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC,
+	/**< An error in the stream caused the decoder to lose synchronization. */
+
+	FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER,
+	/**< The decoder encountered a corrupted frame header. */
+
+	FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH,
+	/**< The frame's data did not match the CRC in the footer. */
+
+	FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM
+	/**< The decoder encountered reserved fields in use in the stream. */
+
+} FLAC__StreamDecoderErrorStatus;
+
+/** Maps a FLAC__StreamDecoderErrorStatus to a C string.
+ *
+ *  Using a FLAC__StreamDecoderErrorStatus as the index to this array
+ *  will give the string equivalent.  The contents should not be modified.
+ */
+extern FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[];
+
+
+/***********************************************************************
+ *
+ * class FLAC__StreamDecoder
+ *
+ ***********************************************************************/
+
+struct FLAC__StreamDecoderProtected;
+struct FLAC__StreamDecoderPrivate;
+/** The opaque structure definition for the stream decoder type.
+ *  See the \link flac_stream_decoder stream decoder module \endlink
+ *  for a detailed description.
+ */
+typedef struct {
+	struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */
+	struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */
+} FLAC__StreamDecoder;
+
+/** Signature for the read callback.
+ *
+ *  A function pointer matching this signature must be passed to
+ *  FLAC__stream_decoder_init*_stream(). The supplied function will be
+ *  called when the decoder needs more input data.  The address of the
+ *  buffer to be filled is supplied, along with the number of bytes the
+ *  buffer can hold.  The callback may choose to supply less data and
+ *  modify the byte count but must be careful not to overflow the buffer.
+ *  The callback then returns a status code chosen from
+ *  FLAC__StreamDecoderReadStatus.
+ *
+ * Here is an example of a read callback for stdio streams:
+ * \code
+ * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+ * {
+ *   FILE *file = ((MyClientData*)client_data)->file;
+ *   if(*bytes > 0) {
+ *     *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file);
+ *     if(ferror(file))
+ *       return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ *     else if(*bytes == 0)
+ *       return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ *     else
+ *       return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ *   }
+ *   else
+ *     return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ * }
+ * \endcode
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  buffer   A pointer to a location for the callee to store
+ *                  data to be decoded.
+ * \param  bytes    A pointer to the size of the buffer.  On entry
+ *                  to the callback, it contains the maximum number
+ *                  of bytes that may be stored in \a buffer.  The
+ *                  callee must set it to the actual number of bytes
+ *                  stored (0 in case of error or end-of-stream) before
+ *                  returning.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ * \retval FLAC__StreamDecoderReadStatus
+ *    The callee's return status.  Note that the callback should return
+ *    \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if
+ *    zero bytes were read and there is no more data to be read.
+ */
+typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
+
+/** Signature for the seek callback.
+ *
+ *  A function pointer matching this signature may be passed to
+ *  FLAC__stream_decoder_init*_stream().  The supplied function will be
+ *  called when the decoder needs to seek the input stream.  The decoder
+ *  will pass the absolute byte offset to seek to, 0 meaning the
+ *  beginning of the stream.
+ *
+ * Here is an example of a seek callback for stdio streams:
+ * \code
+ * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+ * {
+ *   FILE *file = ((MyClientData*)client_data)->file;
+ *   if(file == stdin)
+ *     return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+ *   else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0)
+ *     return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+ *   else
+ *     return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+ * }
+ * \endcode
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  absolute_byte_offset  The offset from the beginning of the stream
+ *                               to seek to.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ * \retval FLAC__StreamDecoderSeekStatus
+ *    The callee's return status.
+ */
+typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+
+/** Signature for the tell callback.
+ *
+ *  A function pointer matching this signature may be passed to
+ *  FLAC__stream_decoder_init*_stream().  The supplied function will be
+ *  called when the decoder wants to know the current position of the
+ *  stream.  The callback should return the byte offset from the
+ *  beginning of the stream.
+ *
+ * Here is an example of a tell callback for stdio streams:
+ * \code
+ * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+ * {
+ *   FILE *file = ((MyClientData*)client_data)->file;
+ *   off_t pos;
+ *   if(file == stdin)
+ *     return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+ *   else if((pos = ftello(file)) < 0)
+ *     return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+ *   else {
+ *     *absolute_byte_offset = (FLAC__uint64)pos;
+ *     return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+ *   }
+ * }
+ * \endcode
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  absolute_byte_offset  A pointer to storage for the current offset
+ *                               from the beginning of the stream.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ * \retval FLAC__StreamDecoderTellStatus
+ *    The callee's return status.
+ */
+typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+
+/** Signature for the length callback.
+ *
+ *  A function pointer matching this signature may be passed to
+ *  FLAC__stream_decoder_init*_stream().  The supplied function will be
+ *  called when the decoder wants to know the total length of the stream
+ *  in bytes.
+ *
+ * Here is an example of a length callback for stdio streams:
+ * \code
+ * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+ * {
+ *   FILE *file = ((MyClientData*)client_data)->file;
+ *   struct stat filestats;
+ *
+ *   if(file == stdin)
+ *     return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+ *   else if(fstat(fileno(file), &filestats) != 0)
+ *     return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+ *   else {
+ *     *stream_length = (FLAC__uint64)filestats.st_size;
+ *     return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+ *   }
+ * }
+ * \endcode
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  stream_length  A pointer to storage for the length of the stream
+ *                        in bytes.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ * \retval FLAC__StreamDecoderLengthStatus
+ *    The callee's return status.
+ */
+typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
+
+/** Signature for the EOF callback.
+ *
+ *  A function pointer matching this signature may be passed to
+ *  FLAC__stream_decoder_init*_stream().  The supplied function will be
+ *  called when the decoder needs to know if the end of the stream has
+ *  been reached.
+ *
+ * Here is an example of a EOF callback for stdio streams:
+ * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data)
+ * \code
+ * {
+ *   FILE *file = ((MyClientData*)client_data)->file;
+ *   return feof(file)? true : false;
+ * }
+ * \endcode
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ * \retval FLAC__bool
+ *    \c true if the currently at the end of the stream, else \c false.
+ */
+typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data);
+
+/** Signature for the write callback.
+ *
+ *  A function pointer matching this signature must be passed to one of
+ *  the FLAC__stream_decoder_init_*() functions.
+ *  The supplied function will be called when the decoder has decoded a
+ *  single audio frame.  The decoder will pass the frame metadata as well
+ *  as an array of pointers (one for each channel) pointing to the
+ *  decoded audio.
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  frame    The description of the decoded frame.  See
+ *                  FLAC__Frame.
+ * \param  buffer   An array of pointers to decoded channels of data.
+ *                  Each pointer will point to an array of signed
+ *                  samples of length \a frame->header.blocksize.
+ *                  Channels will be ordered according to the FLAC
+ *                  specification; see the documentation for the
+ *                  <A HREF="../format.html#frame_header">frame header</A>.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ * \retval FLAC__StreamDecoderWriteStatus
+ *    The callee's return status.
+ */
+typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+
+/** Signature for the metadata callback.
+ *
+ *  A function pointer matching this signature must be passed to one of
+ *  the FLAC__stream_decoder_init_*() functions.
+ *  The supplied function will be called when the decoder has decoded a
+ *  metadata block.  In a valid FLAC file there will always be one
+ *  \c STREAMINFO block, followed by zero or more other metadata blocks.
+ *  These will be supplied by the decoder in the same order as they
+ *  appear in the stream and always before the first audio frame (i.e.
+ *  write callback).  The metadata block that is passed in must not be
+ *  modified, and it doesn't live beyond the callback, so you should make
+ *  a copy of it with FLAC__metadata_object_clone() if you will need it
+ *  elsewhere.  Since metadata blocks can potentially be large, by
+ *  default the decoder only calls the metadata callback for the
+ *  \c STREAMINFO block; you can instruct the decoder to pass or filter
+ *  other blocks with FLAC__stream_decoder_set_metadata_*() calls.
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  metadata The decoded metadata block.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ */
+typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+
+/** Signature for the error callback.
+ *
+ *  A function pointer matching this signature must be passed to one of
+ *  the FLAC__stream_decoder_init_*() functions.
+ *  The supplied function will be called whenever an error occurs during
+ *  decoding.
+ *
+ * \note In general, FLAC__StreamDecoder functions which change the
+ * state should not be called on the \a decoder while in the callback.
+ *
+ * \param  decoder  The decoder instance calling the callback.
+ * \param  status   The error encountered by the decoder.
+ * \param  client_data  The callee's client data set through
+ *                      FLAC__stream_decoder_init_*().
+ */
+typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+
+
+/***********************************************************************
+ *
+ * Class constructor/destructor
+ *
+ ***********************************************************************/
+
+/** Create a new stream decoder instance.  The instance is created with
+ *  default settings; see the individual FLAC__stream_decoder_set_*()
+ *  functions for each setting's default.
+ *
+ * \retval FLAC__StreamDecoder*
+ *    \c NULL if there was an error allocating memory, else the new instance.
+ */
+FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void);
+
+/** Free a decoder instance.  Deletes the object pointed to by \a decoder.
+ *
+ * \param decoder  A pointer to an existing decoder.
+ * \assert
+ *    \code decoder != NULL \endcode
+ */
+FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder);
+
+
+/***********************************************************************
+ *
+ * Public class method prototypes
+ *
+ ***********************************************************************/
+
+/** Set the serial number for the FLAC stream within the Ogg container.
+ *  The default behavior is to use the serial number of the first Ogg
+ *  page.  Setting a serial number here will explicitly specify which
+ *  stream is to be decoded.
+ *
+ * \note
+ * This does not need to be set for native FLAC decoding.
+ *
+ * \default \c use serial number of first page
+ * \param  decoder        A decoder instance to set.
+ * \param  serial_number  See above.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number);
+
+/** Set the "MD5 signature checking" flag.  If \c true, the decoder will
+ *  compute the MD5 signature of the unencoded audio data while decoding
+ *  and compare it to the signature from the STREAMINFO block, if it
+ *  exists, during FLAC__stream_decoder_finish().
+ *
+ *  MD5 signature checking will be turned off (until the next
+ *  FLAC__stream_decoder_reset()) if there is no signature in the
+ *  STREAMINFO block or when a seek is attempted.
+ *
+ *  Clients that do not use the MD5 check should leave this off to speed
+ *  up decoding.
+ *
+ * \default \c false
+ * \param  decoder  A decoder instance to set.
+ * \param  value    Flag value (see above).
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value);
+
+/** Direct the decoder to pass on all metadata blocks of type \a type.
+ *
+ * \default By default, only the \c STREAMINFO block is returned via the
+ *          metadata callback.
+ * \param  decoder  A decoder instance to set.
+ * \param  type     See above.
+ * \assert
+ *    \code decoder != NULL \endcode
+ *    \a type is valid
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type);
+
+/** Direct the decoder to pass on all APPLICATION metadata blocks of the
+ *  given \a id.
+ *
+ * \default By default, only the \c STREAMINFO block is returned via the
+ *          metadata callback.
+ * \param  decoder  A decoder instance to set.
+ * \param  id       See above.
+ * \assert
+ *    \code decoder != NULL \endcode
+ *    \code id != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]);
+
+/** Direct the decoder to pass on all metadata blocks of any type.
+ *
+ * \default By default, only the \c STREAMINFO block is returned via the
+ *          metadata callback.
+ * \param  decoder  A decoder instance to set.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder);
+
+/** Direct the decoder to filter out all metadata blocks of type \a type.
+ *
+ * \default By default, only the \c STREAMINFO block is returned via the
+ *          metadata callback.
+ * \param  decoder  A decoder instance to set.
+ * \param  type     See above.
+ * \assert
+ *    \code decoder != NULL \endcode
+ *    \a type is valid
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type);
+
+/** Direct the decoder to filter out all APPLICATION metadata blocks of
+ *  the given \a id.
+ *
+ * \default By default, only the \c STREAMINFO block is returned via the
+ *          metadata callback.
+ * \param  decoder  A decoder instance to set.
+ * \param  id       See above.
+ * \assert
+ *    \code decoder != NULL \endcode
+ *    \code id != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]);
+
+/** Direct the decoder to filter out all metadata blocks of any type.
+ *
+ * \default By default, only the \c STREAMINFO block is returned via the
+ *          metadata callback.
+ * \param  decoder  A decoder instance to set.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if the decoder is already initialized, else \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder);
+
+/** Get the current decoder state.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__StreamDecoderState
+ *    The current decoder state.
+ */
+FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder);
+
+/** Get the current decoder state as a C string.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval const char *
+ *    The decoder state as a C string.  Do not modify the contents.
+ */
+FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder);
+
+/** Get the "MD5 signature checking" flag.
+ *  This is the value of the setting, not whether or not the decoder is
+ *  currently checking the MD5 (remember, it can be turned off automatically
+ *  by a seek).  When the decoder is reset the flag will be restored to the
+ *  value returned by this function.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    See above.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder);
+
+/** Get the total number of samples in the stream being decoded.
+ *  Will only be valid after decoding has started and will contain the
+ *  value from the \c STREAMINFO block.  A value of \c 0 means "unknown".
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval uint32_t
+ *    See above.
+ */
+FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder);
+
+/** Get the current number of channels in the stream being decoded.
+ *  Will only be valid after decoding has started and will contain the
+ *  value from the most recently decoded frame header.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval uint32_t
+ *    See above.
+ */
+FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder);
+
+/** Get the current channel assignment in the stream being decoded.
+ *  Will only be valid after decoding has started and will contain the
+ *  value from the most recently decoded frame header.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__ChannelAssignment
+ *    See above.
+ */
+FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder);
+
+/** Get the current sample resolution in the stream being decoded.
+ *  Will only be valid after decoding has started and will contain the
+ *  value from the most recently decoded frame header.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval uint32_t
+ *    See above.
+ */
+FLAC_API uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder);
+
+/** Get the current sample rate in Hz of the stream being decoded.
+ *  Will only be valid after decoding has started and will contain the
+ *  value from the most recently decoded frame header.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval uint32_t
+ *    See above.
+ */
+FLAC_API uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder);
+
+/** Get the current blocksize of the stream being decoded.
+ *  Will only be valid after decoding has started and will contain the
+ *  value from the most recently decoded frame header.
+ *
+ * \param  decoder  A decoder instance to query.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval uint32_t
+ *    See above.
+ */
+FLAC_API uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder);
+
+/** Returns the decoder's current read position within the stream.
+ *  The position is the byte offset from the start of the stream.
+ *  Bytes before this position have been fully decoded.  Note that
+ *  there may still be undecoded bytes in the decoder's read FIFO.
+ *  The returned position is correct even after a seek.
+ *
+ *  \warning This function currently only works for native FLAC,
+ *           not Ogg FLAC streams.
+ *
+ * \param  decoder   A decoder instance to query.
+ * \param  position  Address at which to return the desired position.
+ * \assert
+ *    \code decoder != NULL \endcode
+ *    \code position != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if successful, \c false if the stream is not native FLAC,
+ *    or there was an error from the 'tell' callback or it returned
+ *    \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position);
+
+/** Initialize the decoder instance to decode native FLAC streams.
+ *
+ *  This flavor of initialization sets up the decoder to decode from a
+ *  native FLAC stream. I/O is performed via callbacks to the client.
+ *  For decoding from a plain file via filename or open FILE*,
+ *  FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE()
+ *  provide a simpler interface.
+ *
+ *  This function should be called after FLAC__stream_decoder_new() and
+ *  FLAC__stream_decoder_set_*() but before any of the
+ *  FLAC__stream_decoder_process_*() functions.  Will set and return the
+ *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
+ *  if initialization succeeded.
+ *
+ * \param  decoder            An uninitialized decoder instance.
+ * \param  read_callback      See FLAC__StreamDecoderReadCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  seek_callback      See FLAC__StreamDecoderSeekCallback.  This
+ *                            pointer may be \c NULL if seeking is not
+ *                            supported.  If \a seek_callback is not \c NULL then a
+ *                            \a tell_callback, \a length_callback, and \a eof_callback must also be supplied.
+ *                            Alternatively, a dummy seek callback that just
+ *                            returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  tell_callback      See FLAC__StreamDecoderTellCallback.  This
+ *                            pointer may be \c NULL if not supported by the client.  If
+ *                            \a seek_callback is not \c NULL then a
+ *                            \a tell_callback must also be supplied.
+ *                            Alternatively, a dummy tell callback that just
+ *                            returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  length_callback    See FLAC__StreamDecoderLengthCallback.  This
+ *                            pointer may be \c NULL if not supported by the client.  If
+ *                            \a seek_callback is not \c NULL then a
+ *                            \a length_callback must also be supplied.
+ *                            Alternatively, a dummy length callback that just
+ *                            returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  eof_callback       See FLAC__StreamDecoderEofCallback.  This
+ *                            pointer may be \c NULL if not supported by the client.  If
+ *                            \a seek_callback is not \c NULL then a
+ *                            \a eof_callback must also be supplied.
+ *                            Alternatively, a dummy length callback that just
+ *                            returns \c false
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
+ *                            pointer may be \c NULL if the callback is not
+ *                            desired.
+ * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  client_data        This value will be supplied to callbacks in their
+ *                            \a client_data argument.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__StreamDecoderInitStatus
+ *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
+ *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
+ */
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream(
+	FLAC__StreamDecoder *decoder,
+	FLAC__StreamDecoderReadCallback read_callback,
+	FLAC__StreamDecoderSeekCallback seek_callback,
+	FLAC__StreamDecoderTellCallback tell_callback,
+	FLAC__StreamDecoderLengthCallback length_callback,
+	FLAC__StreamDecoderEofCallback eof_callback,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+);
+
+/** Initialize the decoder instance to decode Ogg FLAC streams.
+ *
+ *  This flavor of initialization sets up the decoder to decode from a
+ *  FLAC stream in an Ogg container. I/O is performed via callbacks to the
+ *  client.  For decoding from a plain file via filename or open FILE*,
+ *  FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE()
+ *  provide a simpler interface.
+ *
+ *  This function should be called after FLAC__stream_decoder_new() and
+ *  FLAC__stream_decoder_set_*() but before any of the
+ *  FLAC__stream_decoder_process_*() functions.  Will set and return the
+ *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
+ *  if initialization succeeded.
+ *
+ *  \note Support for Ogg FLAC in the library is optional.  If this
+ *  library has been built without support for Ogg FLAC, this function
+ *  will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER.
+ *
+ * \param  decoder            An uninitialized decoder instance.
+ * \param  read_callback      See FLAC__StreamDecoderReadCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  seek_callback      See FLAC__StreamDecoderSeekCallback.  This
+ *                            pointer may be \c NULL if seeking is not
+ *                            supported.  If \a seek_callback is not \c NULL then a
+ *                            \a tell_callback, \a length_callback, and \a eof_callback must also be supplied.
+ *                            Alternatively, a dummy seek callback that just
+ *                            returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  tell_callback      See FLAC__StreamDecoderTellCallback.  This
+ *                            pointer may be \c NULL if not supported by the client.  If
+ *                            \a seek_callback is not \c NULL then a
+ *                            \a tell_callback must also be supplied.
+ *                            Alternatively, a dummy tell callback that just
+ *                            returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  length_callback    See FLAC__StreamDecoderLengthCallback.  This
+ *                            pointer may be \c NULL if not supported by the client.  If
+ *                            \a seek_callback is not \c NULL then a
+ *                            \a length_callback must also be supplied.
+ *                            Alternatively, a dummy length callback that just
+ *                            returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  eof_callback       See FLAC__StreamDecoderEofCallback.  This
+ *                            pointer may be \c NULL if not supported by the client.  If
+ *                            \a seek_callback is not \c NULL then a
+ *                            \a eof_callback must also be supplied.
+ *                            Alternatively, a dummy length callback that just
+ *                            returns \c false
+ *                            may also be supplied, all though this is slightly
+ *                            less efficient for the decoder.
+ * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
+ *                            pointer may be \c NULL if the callback is not
+ *                            desired.
+ * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  client_data        This value will be supplied to callbacks in their
+ *                            \a client_data argument.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__StreamDecoderInitStatus
+ *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
+ *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
+ */
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream(
+	FLAC__StreamDecoder *decoder,
+	FLAC__StreamDecoderReadCallback read_callback,
+	FLAC__StreamDecoderSeekCallback seek_callback,
+	FLAC__StreamDecoderTellCallback tell_callback,
+	FLAC__StreamDecoderLengthCallback length_callback,
+	FLAC__StreamDecoderEofCallback eof_callback,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+);
+
+/** Initialize the decoder instance to decode native FLAC files.
+ *
+ *  This flavor of initialization sets up the decoder to decode from a
+ *  plain native FLAC file.  For non-stdio streams, you must use
+ *  FLAC__stream_decoder_init_stream() and provide callbacks for the I/O.
+ *
+ *  This function should be called after FLAC__stream_decoder_new() and
+ *  FLAC__stream_decoder_set_*() but before any of the
+ *  FLAC__stream_decoder_process_*() functions.  Will set and return the
+ *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
+ *  if initialization succeeded.
+ *
+ * \param  decoder            An uninitialized decoder instance.
+ * \param  file               An open FLAC file.  The file should have been
+ *                            opened with mode \c "rb" and rewound.  The file
+ *                            becomes owned by the decoder and should not be
+ *                            manipulated by the client while decoding.
+ *                            Unless \a file is \c stdin, it will be closed
+ *                            when FLAC__stream_decoder_finish() is called.
+ *                            Note however that seeking will not work when
+ *                            decoding from \c stdin since it is not seekable.
+ * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
+ *                            pointer may be \c NULL if the callback is not
+ *                            desired.
+ * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  client_data        This value will be supplied to callbacks in their
+ *                            \a client_data argument.
+ * \assert
+ *    \code decoder != NULL \endcode
+ *    \code file != NULL \endcode
+ * \retval FLAC__StreamDecoderInitStatus
+ *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
+ *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
+ */
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE(
+	FLAC__StreamDecoder *decoder,
+	FILE *file,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+);
+
+/** Initialize the decoder instance to decode Ogg FLAC files.
+ *
+ *  This flavor of initialization sets up the decoder to decode from a
+ *  plain Ogg FLAC file.  For non-stdio streams, you must use
+ *  FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O.
+ *
+ *  This function should be called after FLAC__stream_decoder_new() and
+ *  FLAC__stream_decoder_set_*() but before any of the
+ *  FLAC__stream_decoder_process_*() functions.  Will set and return the
+ *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
+ *  if initialization succeeded.
+ *
+ *  \note Support for Ogg FLAC in the library is optional.  If this
+ *  library has been built without support for Ogg FLAC, this function
+ *  will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER.
+ *
+ * \param  decoder            An uninitialized decoder instance.
+ * \param  file               An open FLAC file.  The file should have been
+ *                            opened with mode \c "rb" and rewound.  The file
+ *                            becomes owned by the decoder and should not be
+ *                            manipulated by the client while decoding.
+ *                            Unless \a file is \c stdin, it will be closed
+ *                            when FLAC__stream_decoder_finish() is called.
+ *                            Note however that seeking will not work when
+ *                            decoding from \c stdin since it is not seekable.
+ * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
+ *                            pointer may be \c NULL if the callback is not
+ *                            desired.
+ * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  client_data        This value will be supplied to callbacks in their
+ *                            \a client_data argument.
+ * \assert
+ *    \code decoder != NULL \endcode
+ *    \code file != NULL \endcode
+ * \retval FLAC__StreamDecoderInitStatus
+ *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
+ *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
+ */
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE(
+	FLAC__StreamDecoder *decoder,
+	FILE *file,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+);
+
+/** Initialize the decoder instance to decode native FLAC files.
+ *
+ *  This flavor of initialization sets up the decoder to decode from a plain
+ *  native FLAC file.  If POSIX fopen() semantics are not sufficient, (for
+ *  example, with Unicode filenames on Windows), you must use
+ *  FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream()
+ *  and provide callbacks for the I/O.
+ *
+ *  This function should be called after FLAC__stream_decoder_new() and
+ *  FLAC__stream_decoder_set_*() but before any of the
+ *  FLAC__stream_decoder_process_*() functions.  Will set and return the
+ *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
+ *  if initialization succeeded.
+ *
+ * \param  decoder            An uninitialized decoder instance.
+ * \param  filename           The name of the file to decode from.  The file will
+ *                            be opened with fopen().  Use \c NULL to decode from
+ *                            \c stdin.  Note that \c stdin is not seekable.
+ * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
+ *                            pointer may be \c NULL if the callback is not
+ *                            desired.
+ * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  client_data        This value will be supplied to callbacks in their
+ *                            \a client_data argument.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__StreamDecoderInitStatus
+ *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
+ *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
+ */
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file(
+	FLAC__StreamDecoder *decoder,
+	const char *filename,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+);
+
+/** Initialize the decoder instance to decode Ogg FLAC files.
+ *
+ *  This flavor of initialization sets up the decoder to decode from a plain
+ *  Ogg FLAC file.  If POSIX fopen() semantics are not sufficient, (for
+ *  example, with Unicode filenames on Windows), you must use
+ *  FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream()
+ *  and provide callbacks for the I/O.
+ *
+ *  This function should be called after FLAC__stream_decoder_new() and
+ *  FLAC__stream_decoder_set_*() but before any of the
+ *  FLAC__stream_decoder_process_*() functions.  Will set and return the
+ *  decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA
+ *  if initialization succeeded.
+ *
+ *  \note Support for Ogg FLAC in the library is optional.  If this
+ *  library has been built without support for Ogg FLAC, this function
+ *  will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER.
+ *
+ * \param  decoder            An uninitialized decoder instance.
+ * \param  filename           The name of the file to decode from.  The file will
+ *                            be opened with fopen().  Use \c NULL to decode from
+ *                            \c stdin.  Note that \c stdin is not seekable.
+ * \param  write_callback     See FLAC__StreamDecoderWriteCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  metadata_callback  See FLAC__StreamDecoderMetadataCallback.  This
+ *                            pointer may be \c NULL if the callback is not
+ *                            desired.
+ * \param  error_callback     See FLAC__StreamDecoderErrorCallback.  This
+ *                            pointer must not be \c NULL.
+ * \param  client_data        This value will be supplied to callbacks in their
+ *                            \a client_data argument.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__StreamDecoderInitStatus
+ *    \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful;
+ *    see FLAC__StreamDecoderInitStatus for the meanings of other return values.
+ */
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file(
+	FLAC__StreamDecoder *decoder,
+	const char *filename,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+);
+
+/** Finish the decoding process.
+ *  Flushes the decoding buffer, releases resources, resets the decoder
+ *  settings to their defaults, and returns the decoder state to
+ *  FLAC__STREAM_DECODER_UNINITIALIZED.
+ *
+ *  In the event of a prematurely-terminated decode, it is not strictly
+ *  necessary to call this immediately before FLAC__stream_decoder_delete()
+ *  but it is good practice to match every FLAC__stream_decoder_init_*()
+ *  with a FLAC__stream_decoder_finish().
+ *
+ * \param  decoder  An uninitialized decoder instance.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if MD5 checking is on AND a STREAMINFO block was available
+ *    AND the MD5 signature in the STREAMINFO block was non-zero AND the
+ *    signature does not match the one computed by the decoder; else
+ *    \c true.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder);
+
+/** Flush the stream input.
+ *  The decoder's input buffer will be cleared and the state set to
+ *  \c FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC.  This will also turn
+ *  off MD5 checking.
+ *
+ * \param  decoder  A decoder instance.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if successful, else \c false if a memory allocation
+ *    error occurs (in which case the state will be set to
+ *    \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR).
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder);
+
+/** Reset the decoding process.
+ *  The decoder's input buffer will be cleared and the state set to
+ *  \c FLAC__STREAM_DECODER_SEARCH_FOR_METADATA.  This is similar to
+ *  FLAC__stream_decoder_finish() except that the settings are
+ *  preserved; there is no need to call FLAC__stream_decoder_init_*()
+ *  before decoding again.  MD5 checking will be restored to its original
+ *  setting.
+ *
+ *  If the decoder is seekable, or was initialized with
+ *  FLAC__stream_decoder_init*_FILE() or FLAC__stream_decoder_init*_file(),
+ *  the decoder will also attempt to seek to the beginning of the file.
+ *  If this rewind fails, this function will return \c false.  It follows
+ *  that FLAC__stream_decoder_reset() cannot be used when decoding from
+ *  \c stdin.
+ *
+ *  If the decoder was initialized with FLAC__stream_encoder_init*_stream()
+ *  and is not seekable (i.e. no seek callback was provided or the seek
+ *  callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it
+ *  is the duty of the client to start feeding data from the beginning of
+ *  the stream on the next FLAC__stream_decoder_process_*() call.
+ *
+ * \param  decoder  A decoder instance.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if successful, else \c false if a memory allocation occurs
+ *    (in which case the state will be set to
+ *    \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR) or a seek error
+ *    occurs (the state will be unchanged).
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder);
+
+/** Decode one metadata block or audio frame.
+ *  This version instructs the decoder to decode a either a single metadata
+ *  block or a single frame and stop, unless the callbacks return a fatal
+ *  error or the read callback returns
+ *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM.
+ *
+ *  As the decoder needs more input it will call the read callback.
+ *  Depending on what was decoded, the metadata or write callback will be
+ *  called with the decoded metadata block or audio frame.
+ *
+ *  Unless there is a fatal read error or end of stream, this function
+ *  will return once one whole frame is decoded.  In other words, if the
+ *  stream is not synchronized or points to a corrupt frame header, the
+ *  decoder will continue to try and resync until it gets to a valid
+ *  frame, then decode one frame, then return.  If the decoder points to
+ *  a frame whose frame CRC in the frame footer does not match the
+ *  computed frame CRC, this function will issue a
+ *  FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the
+ *  error callback, and return, having decoded one complete, although
+ *  corrupt, frame.  (Such corrupted frames are sent as silence of the
+ *  correct length to the write callback.)
+ *
+ * \param  decoder  An initialized decoder instance.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if any fatal read, write, or memory allocation error
+ *    occurred (meaning decoding must stop), else \c true; for more
+ *    information about the decoder, check the decoder state with
+ *    FLAC__stream_decoder_get_state().
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder);
+
+/** Decode until the end of the metadata.
+ *  This version instructs the decoder to decode from the current position
+ *  and continue until all the metadata has been read, or until the
+ *  callbacks return a fatal error or the read callback returns
+ *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM.
+ *
+ *  As the decoder needs more input it will call the read callback.
+ *  As each metadata block is decoded, the metadata callback will be called
+ *  with the decoded metadata.
+ *
+ * \param  decoder  An initialized decoder instance.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if any fatal read, write, or memory allocation error
+ *    occurred (meaning decoding must stop), else \c true; for more
+ *    information about the decoder, check the decoder state with
+ *    FLAC__stream_decoder_get_state().
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder);
+
+/** Decode until the end of the stream.
+ *  This version instructs the decoder to decode from the current position
+ *  and continue until the end of stream (the read callback returns
+ *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the
+ *  callbacks return a fatal error.
+ *
+ *  As the decoder needs more input it will call the read callback.
+ *  As each metadata block and frame is decoded, the metadata or write
+ *  callback will be called with the decoded metadata or frame.
+ *
+ * \param  decoder  An initialized decoder instance.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if any fatal read, write, or memory allocation error
+ *    occurred (meaning decoding must stop), else \c true; for more
+ *    information about the decoder, check the decoder state with
+ *    FLAC__stream_decoder_get_state().
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder);
+
+/** Skip one audio frame.
+ *  This version instructs the decoder to 'skip' a single frame and stop,
+ *  unless the callbacks return a fatal error or the read callback returns
+ *  \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM.
+ *
+ *  The decoding flow is the same as what occurs when
+ *  FLAC__stream_decoder_process_single() is called to process an audio
+ *  frame, except that this function does not decode the parsed data into
+ *  PCM or call the write callback.  The integrity of the frame is still
+ *  checked the same way as in the other process functions.
+ *
+ *  This function will return once one whole frame is skipped, in the
+ *  same way that FLAC__stream_decoder_process_single() will return once
+ *  one whole frame is decoded.
+ *
+ *  This function can be used in more quickly determining FLAC frame
+ *  boundaries when decoding of the actual data is not needed, for
+ *  example when an application is separating a FLAC stream into frames
+ *  for editing or storing in a container.  To do this, the application
+ *  can use FLAC__stream_decoder_skip_single_frame() to quickly advance
+ *  to the next frame, then use
+ *  FLAC__stream_decoder_get_decode_position() to find the new frame
+ *  boundary.
+ *
+ *  This function should only be called when the stream has advanced
+ *  past all the metadata, otherwise it will return \c false.
+ *
+ * \param  decoder  An initialized decoder instance not in a metadata
+ *                  state.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c false if any fatal read, write, or memory allocation error
+ *    occurred (meaning decoding must stop), or if the decoder
+ *    is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or
+ *    FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more
+ *    information about the decoder, check the decoder state with
+ *    FLAC__stream_decoder_get_state().
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder);
+
+/** Flush the input and seek to an absolute sample.
+ *  Decoding will resume at the given sample.  Note that because of
+ *  this, the next write callback may contain a partial block.  The
+ *  client must support seeking the input or this function will fail
+ *  and return \c false.  Furthermore, if the decoder state is
+ *  \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed
+ *  with FLAC__stream_decoder_flush() or reset with
+ *  FLAC__stream_decoder_reset() before decoding can continue.
+ *
+ * \param  decoder  A decoder instance.
+ * \param  sample   The target sample number to seek to.
+ * \assert
+ *    \code decoder != NULL \endcode
+ * \retval FLAC__bool
+ *    \c true if successful, else \c false.
+ */
+FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample);
+
+/* \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+++ b/src/libflac/NOTE.txt
@@ -1,0 +1,4 @@
+8bitbubsy:
+This is a cut down and modified version of libFLAC v1.3.3.
+I have removed stuff I don't want, and only left the decoder in. Please don't
+use this outside of the FT2 clone project!
--- /dev/null
+++ b/src/libflac/bitmath.c
@@ -1,0 +1,69 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "private/bitmath.h"
+
+/* An example of what FLAC__bitmath_silog2() computes:
+ *
+ * silog2(-10) = 5
+ * silog2(- 9) = 5
+ * silog2(- 8) = 4
+ * silog2(- 7) = 4
+ * silog2(- 6) = 4
+ * silog2(- 5) = 4
+ * silog2(- 4) = 3
+ * silog2(- 3) = 3
+ * silog2(- 2) = 2
+ * silog2(- 1) = 2
+ * silog2(  0) = 0
+ * silog2(  1) = 2
+ * silog2(  2) = 3
+ * silog2(  3) = 3
+ * silog2(  4) = 4
+ * silog2(  5) = 4
+ * silog2(  6) = 4
+ * silog2(  7) = 4
+ * silog2(  8) = 5
+ * silog2(  9) = 5
+ * silog2( 10) = 5
+ */
+uint32_t FLAC__bitmath_silog2(FLAC__int64 v)
+{
+	if(v == 0)
+		return 0;
+
+	if(v == -1)
+		return 2;
+
+	v = (v < 0) ? (-(v+1)) : v;
+	return FLAC__bitmath_ilog2_wide(v)+2;
+}
--- /dev/null
+++ b/src/libflac/bitreader.c
@@ -1,0 +1,914 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2018  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "private/bitmath.h"
+#include "private/bitreader.h"
+#include "private/crc.h"
+#include "private/macros.h"
+#include "share/compat.h"
+#include "share/endswap.h"
+
+/* Things should be fastest when this matches the machine word size */
+/* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS2 below to match */
+/* WATCHOUT: there are a few places where the code will not work unless brword is >= 32 bits wide */
+/*           also, some sections currently only have fast versions for 4 or 8 bytes per word */
+
+#if (ENABLE_64_BIT_WORDS == 0)
+
+typedef FLAC__uint32 brword;
+#define FLAC__BYTES_PER_WORD 4		/* sizeof brword */
+#define FLAC__BITS_PER_WORD 32
+#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff)
+/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */
+#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_32(x)
+/* counts the # of zero MSBs in a word */
+#define COUNT_ZERO_MSBS(word) FLAC__clz_uint32(word)
+#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint32(word)
+
+#else
+
+typedef FLAC__uint64 brword;
+#define FLAC__BYTES_PER_WORD 8		/* sizeof brword */
+#define FLAC__BITS_PER_WORD 64
+#define FLAC__WORD_ALL_ONES ((FLAC__uint64)FLAC__U64L(0xffffffffffffffff))
+/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */
+#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_64(x)
+/* counts the # of zero MSBs in a word */
+#define COUNT_ZERO_MSBS(word) FLAC__clz_uint64(word)
+#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint64(word)
+
+#endif
+
+/*
+ * This should be at least twice as large as the largest number of words
+ * required to represent any 'number' (in any encoding) you are going to
+ * read.  With FLAC this is on the order of maybe a few hundred bits.
+ * If the buffer is smaller than that, the decoder won't be able to read
+ * in a whole number that is in a variable length encoding (e.g. Rice).
+ * But to be practical it should be at least 1K bytes.
+ *
+ * Increase this number to decrease the number of read callbacks, at the
+ * expense of using more memory.  Or decrease for the reverse effect,
+ * keeping in mind the limit from the first paragraph.  The optimal size
+ * also depends on the CPU cache size and other factors; some twiddling
+ * may be necessary to squeeze out the best performance.
+ */
+static const uint32_t FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */
+
+struct FLAC__BitReader {
+	/* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */
+	/* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */
+	brword *buffer;
+	uint32_t capacity; /* in words */
+	uint32_t words; /* # of completed words in buffer */
+	uint32_t bytes; /* # of bytes in incomplete word at buffer[words] */
+	uint32_t consumed_words; /* #words ... */
+	uint32_t consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */
+	uint32_t read_crc16; /* the running frame CRC */
+	uint32_t crc16_offset; /* the number of words in the current buffer that should not be CRC'd */
+	uint32_t crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */
+	FLAC__BitReaderReadCallback read_callback;
+	void *client_data;
+};
+
+static inline void crc16_update_word_(FLAC__BitReader *br, brword word)
+{
+	register uint32_t crc = br->read_crc16;
+
+	for( ; br->crc16_align < FLAC__BITS_PER_WORD; br->crc16_align += 8)
+		crc = FLAC__CRC16_UPDATE((uint32_t)((word >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), crc);
+
+	br->read_crc16 = crc;
+	br->crc16_align = 0;
+}
+
+static inline void crc16_update_block_(FLAC__BitReader *br)
+{
+	if(br->consumed_words > br->crc16_offset && br->crc16_align)
+		crc16_update_word_(br, br->buffer[br->crc16_offset++]);
+
+#if FLAC__BYTES_PER_WORD == 4
+	br->read_crc16 = FLAC__crc16_update_words32(br->buffer + br->crc16_offset, br->consumed_words - br->crc16_offset, (FLAC__uint16)br->read_crc16);
+#elif FLAC__BYTES_PER_WORD == 8
+	br->read_crc16 = FLAC__crc16_update_words64(br->buffer + br->crc16_offset, br->consumed_words - br->crc16_offset, br->read_crc16);
+#else
+	unsigned i;
+
+	for(i = br->crc16_offset; i < br->consumed_words; i++)
+		crc16_update_word_(br, br->buffer[i]);
+#endif
+
+	br->crc16_offset = 0;
+}
+
+static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br)
+{
+	uint32_t start, end;
+	size_t bytes;
+	FLAC__byte *target;
+
+	/* first shift the unconsumed buffer data toward the front as much as possible */
+	if(br->consumed_words > 0) {
+		crc16_update_block_(br); /* CRC consumed words */
+
+		start = br->consumed_words;
+		end = br->words + (br->bytes? 1:0);
+		memmove(br->buffer, br->buffer+start, FLAC__BYTES_PER_WORD * (end - start));
+
+		br->words -= start;
+		br->consumed_words = 0;
+	}
+
+	/*
+	 * set the target for reading, taking into account word alignment and endianness
+	 */
+	bytes = (br->capacity - br->words) * FLAC__BYTES_PER_WORD - br->bytes;
+	if(bytes == 0)
+		return false; /* no space left, buffer is too small; see note for FLAC__BITREADER_DEFAULT_CAPACITY  */
+	target = ((FLAC__byte*)(br->buffer+br->words)) + br->bytes;
+
+	/* before reading, if the existing reader looks like this (say brword is 32 bits wide)
+	 *   bitstream :  11 22 33 44 55            br->words=1 br->bytes=1 (partial tail word is left-justified)
+	 *   buffer[BE]:  11 22 33 44 55 ?? ?? ??   (shown laid out as bytes sequentially in memory)
+	 *   buffer[LE]:  44 33 22 11 ?? ?? ?? 55   (?? being don't-care)
+	 *                               ^^-------target, bytes=3
+	 * on LE machines, have to byteswap the odd tail word so nothing is
+	 * overwritten:
+	 */
+	if(br->bytes)
+		br->buffer[br->words] = SWAP_BE_WORD_TO_HOST(br->buffer[br->words]);
+
+	/* now it looks like:
+	 *   bitstream :  11 22 33 44 55            br->words=1 br->bytes=1
+	 *   buffer[BE]:  11 22 33 44 55 ?? ?? ??
+	 *   buffer[LE]:  44 33 22 11 55 ?? ?? ??
+	 *                               ^^-------target, bytes=3
+	 */
+
+	/* read in the data; note that the callback may return a smaller number of bytes */
+	if(!br->read_callback(target, &bytes, br->client_data))
+		return false;
+
+	/* after reading bytes 66 77 88 99 AA BB CC DD EE FF from the client:
+	 *   bitstream :  11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
+	 *   buffer[BE]:  11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ??
+	 *   buffer[LE]:  44 33 22 11 55 66 77 88 99 AA BB CC DD EE FF ??
+	 * now have to byteswap on LE machines:
+	 */
+	end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + (uint32_t)bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD;
+	for(start = br->words; start < end; start++)
+		br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]);
+
+	/* now it looks like:
+	 *   bitstream :  11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
+	 *   buffer[BE]:  11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ??
+	 *   buffer[LE]:  44 33 22 11 88 77 66 55 CC BB AA 99 ?? FF EE DD
+	 * finally we'll update the reader values:
+	 */
+	end = br->words*FLAC__BYTES_PER_WORD + br->bytes + (uint32_t)bytes;
+	br->words = end / FLAC__BYTES_PER_WORD;
+	br->bytes = end % FLAC__BYTES_PER_WORD;
+
+	return true;
+}
+
+/***********************************************************************
+ *
+ * Class constructor/destructor
+ *
+ ***********************************************************************/
+
+FLAC__BitReader *FLAC__bitreader_new(void)
+{
+	FLAC__BitReader *br = calloc(1, sizeof(FLAC__BitReader));
+
+	/* calloc() implies:
+		memset(br, 0, sizeof(FLAC__BitReader));
+		br->buffer = 0;
+		br->capacity = 0;
+		br->words = br->bytes = 0;
+		br->consumed_words = br->consumed_bits = 0;
+		br->read_callback = 0;
+		br->client_data = 0;
+	*/
+	return br;
+}
+
+void FLAC__bitreader_delete(FLAC__BitReader *br)
+{
+	FLAC__bitreader_free(br);
+	free(br);
+}
+
+/***********************************************************************
+ *
+ * Public class methods
+ *
+ ***********************************************************************/
+
+FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd)
+{
+	br->words = br->bytes = 0;
+	br->consumed_words = br->consumed_bits = 0;
+	br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY;
+	br->buffer = malloc(sizeof(brword) * br->capacity);
+	if(br->buffer == 0)
+		return false;
+	br->read_callback = rcb;
+	br->client_data = cd;
+
+	return true;
+}
+
+void FLAC__bitreader_free(FLAC__BitReader *br)
+{
+	if(0 != br->buffer)
+		free(br->buffer);
+	br->buffer = 0;
+	br->capacity = 0;
+	br->words = br->bytes = 0;
+	br->consumed_words = br->consumed_bits = 0;
+	br->read_callback = 0;
+	br->client_data = 0;
+}
+
+FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br)
+{
+	br->words = br->bytes = 0;
+	br->consumed_words = br->consumed_bits = 0;
+	return true;
+}
+
+void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out)
+{
+	uint32_t i, j;
+	if(br == 0) {
+		fprintf(out, "bitreader is NULL\n");
+	}
+	else {
+		fprintf(out, "bitreader: capacity=%u words=%u bytes=%u consumed: words=%u, bits=%u\n", br->capacity, br->words, br->bytes, br->consumed_words, br->consumed_bits);
+
+		for(i = 0; i < br->words; i++) {
+			fprintf(out, "%08X: ", i);
+			for(j = 0; j < FLAC__BITS_PER_WORD; j++)
+				if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits))
+					fprintf(out, ".");
+				else
+					fprintf(out, "%01d", br->buffer[i] & ((brword)1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0);
+			fprintf(out, "\n");
+		}
+		if(br->bytes > 0) {
+			fprintf(out, "%08X: ", i);
+			for(j = 0; j < br->bytes*8; j++)
+				if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits))
+					fprintf(out, ".");
+				else
+					fprintf(out, "%01d", br->buffer[i] & ((brword)1 << (br->bytes*8-j-1)) ? 1:0);
+			fprintf(out, "\n");
+		}
+	}
+}
+
+void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed)
+{
+	br->read_crc16 = (uint32_t)seed;
+	br->crc16_offset = br->consumed_words;
+	br->crc16_align = br->consumed_bits;
+}
+
+FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br)
+{
+	/* CRC consumed words up to here */
+	crc16_update_block_(br);
+
+	/* CRC any tail bytes in a partially-consumed word */
+	if(br->consumed_bits) {
+		const brword tail = br->buffer[br->consumed_words];
+		for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8)
+			br->read_crc16 = FLAC__CRC16_UPDATE((uint32_t)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16);
+	}
+	return (FLAC__uint16)br->read_crc16;
+}
+
+inline FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br)
+{
+	return ((br->consumed_bits & 7) == 0);
+}
+
+inline uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br)
+{
+	return 8 - (br->consumed_bits & 7);
+}
+
+inline uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br)
+{
+	return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits;
+}
+
+FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, uint32_t bits)
+{
+	if(bits == 0) { /* OPT: investigate if this can ever happen, maybe change to assertion */
+		*val = 0;
+		return true;
+	}
+
+	while((br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits < bits) {
+		if(!bitreader_read_from_client_(br))
+			return false;
+	}
+	if(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */
+		/* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */
+		if(br->consumed_bits) {
+			/* this also works when consumed_bits==0, it's just a little slower than necessary for that case */
+			const uint32_t n = FLAC__BITS_PER_WORD - br->consumed_bits;
+			const brword word = br->buffer[br->consumed_words];
+			if(bits < n) {
+				*val = (FLAC__uint32)((word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits)); /* The result has <= 32 non-zero bits */
+				br->consumed_bits += bits;
+				return true;
+			}
+			/* (FLAC__BITS_PER_WORD - br->consumed_bits <= bits) ==> (FLAC__WORD_ALL_ONES >> br->consumed_bits) has no more than 'bits' non-zero bits */
+			*val = (FLAC__uint32)(word & (FLAC__WORD_ALL_ONES >> br->consumed_bits));
+			bits -= n;
+			br->consumed_words++;
+			br->consumed_bits = 0;
+			if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */
+				*val <<= bits;
+				*val |= (FLAC__uint32)(br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits));
+				br->consumed_bits = bits;
+			}
+			return true;
+		}
+		else { /* br->consumed_bits == 0 */
+			const brword word = br->buffer[br->consumed_words];
+			if(bits < FLAC__BITS_PER_WORD) {
+				*val = (FLAC__uint32)(word >> (FLAC__BITS_PER_WORD-bits));
+				br->consumed_bits = bits;
+				return true;
+			}
+			/* at this point bits == FLAC__BITS_PER_WORD == 32; because of previous assertions, it can't be larger */
+			*val = (FLAC__uint32)word;
+			br->consumed_words++;
+			return true;
+		}
+	}
+	else {
+		/* in this case we're starting our read at a partial tail word;
+		 * the reader has guaranteed that we have at least 'bits' bits
+		 * available to read, which makes this case simpler.
+		 */
+		/* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */
+		if(br->consumed_bits) {
+			/* this also works when consumed_bits==0, it's just a little slower than necessary for that case */
+			*val = (FLAC__uint32)((br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits));
+			br->consumed_bits += bits;
+			return true;
+		}
+		else {
+			*val = (FLAC__uint32)(br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits));
+			br->consumed_bits += bits;
+			return true;
+		}
+	}
+}
+
+FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, uint32_t bits)
+{
+	FLAC__uint32 uval, mask;
+	/* OPT: inline raw uint32 code here, or make into a macro if possible in the .h file */
+	if(!FLAC__bitreader_read_raw_uint32(br, &uval, bits))
+		return false;
+	/* sign-extend *val assuming it is currently bits wide. */
+	/* From: https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend */
+	mask = 1u << (bits - 1);
+	*val = (uval ^ mask) - mask;
+	return true;
+}
+
+FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, uint32_t bits)
+{
+	FLAC__uint32 hi, lo;
+
+	if(bits > 32) {
+		if(!FLAC__bitreader_read_raw_uint32(br, &hi, bits-32))
+			return false;
+		if(!FLAC__bitreader_read_raw_uint32(br, &lo, 32))
+			return false;
+		*val = hi;
+		*val <<= 32;
+		*val |= lo;
+	}
+	else {
+		if(!FLAC__bitreader_read_raw_uint32(br, &lo, bits))
+			return false;
+		*val = lo;
+	}
+	return true;
+}
+
+inline FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val)
+{
+	FLAC__uint32 x8, x32 = 0;
+
+	/* this doesn't need to be that fast as currently it is only used for vorbis comments */
+
+	if(!FLAC__bitreader_read_raw_uint32(br, &x32, 8))
+		return false;
+
+	if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8))
+		return false;
+	x32 |= (x8 << 8);
+
+	if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8))
+		return false;
+	x32 |= (x8 << 16);
+
+	if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8))
+		return false;
+	x32 |= (x8 << 24);
+
+	*val = x32;
+	return true;
+}
+
+FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, uint32_t bits)
+{
+	/*
+	 * OPT: a faster implementation is possible but probably not that useful
+	 * since this is only called a couple of times in the metadata readers.
+	 */
+
+	if(bits > 0) {
+		const uint32_t n = br->consumed_bits & 7;
+		uint32_t m;
+		FLAC__uint32 x;
+
+		if(n != 0) {
+			m = flac_min(8-n, bits);
+			if(!FLAC__bitreader_read_raw_uint32(br, &x, m))
+				return false;
+			bits -= m;
+		}
+		m = bits / 8;
+		if(m > 0) {
+			if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(br, m))
+				return false;
+			bits %= 8;
+		}
+		if(bits > 0) {
+			if(!FLAC__bitreader_read_raw_uint32(br, &x, bits))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, uint32_t nvals)
+{
+	FLAC__uint32 x;
+
+	/* step 1: skip over partial head word to get word aligned */
+	while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */
+		if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+			return false;
+		nvals--;
+	}
+	if(0 == nvals)
+		return true;
+	/* step 2: skip whole words in chunks */
+	while(nvals >= FLAC__BYTES_PER_WORD) {
+		if(br->consumed_words < br->words) {
+			br->consumed_words++;
+			nvals -= FLAC__BYTES_PER_WORD;
+		}
+		else if(!bitreader_read_from_client_(br))
+			return false;
+	}
+	/* step 3: skip any remainder from partial tail bytes */
+	while(nvals) {
+		if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+			return false;
+		nvals--;
+	}
+
+	return true;
+}
+
+FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, uint32_t nvals)
+{
+	FLAC__uint32 x;
+
+	/* step 1: read from partial head word to get word aligned */
+	while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */
+		if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+			return false;
+		*val++ = (FLAC__byte)x;
+		nvals--;
+	}
+	if(0 == nvals)
+		return true;
+	/* step 2: read whole words in chunks */
+	while(nvals >= FLAC__BYTES_PER_WORD) {
+		if(br->consumed_words < br->words) {
+			const brword word = br->buffer[br->consumed_words++];
+#if FLAC__BYTES_PER_WORD == 4
+			val[0] = (FLAC__byte)(word >> 24);
+			val[1] = (FLAC__byte)(word >> 16);
+			val[2] = (FLAC__byte)(word >> 8);
+			val[3] = (FLAC__byte)word;
+#elif FLAC__BYTES_PER_WORD == 8
+			val[0] = (FLAC__byte)(word >> 56);
+			val[1] = (FLAC__byte)(word >> 48);
+			val[2] = (FLAC__byte)(word >> 40);
+			val[3] = (FLAC__byte)(word >> 32);
+			val[4] = (FLAC__byte)(word >> 24);
+			val[5] = (FLAC__byte)(word >> 16);
+			val[6] = (FLAC__byte)(word >> 8);
+			val[7] = (FLAC__byte)word;
+#else
+			for(x = 0; x < FLAC__BYTES_PER_WORD; x++)
+				val[x] = (FLAC__byte)(word >> (8*(FLAC__BYTES_PER_WORD-x-1)));
+#endif
+			val += FLAC__BYTES_PER_WORD;
+			nvals -= FLAC__BYTES_PER_WORD;
+		}
+		else if(!bitreader_read_from_client_(br))
+			return false;
+	}
+	/* step 3: read any remainder from partial tail bytes */
+	while(nvals) {
+		if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+			return false;
+		*val++ = (FLAC__byte)x;
+		nvals--;
+	}
+
+	return true;
+}
+
+FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, uint32_t *val)
+{
+	uint32_t i;
+
+	*val = 0;
+	while(1) {
+		while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */
+			brword b = br->buffer[br->consumed_words] << br->consumed_bits;
+			if(b) {
+				i = COUNT_ZERO_MSBS(b);
+				*val += i;
+				i++;
+				br->consumed_bits += i;
+				if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */
+					br->consumed_words++;
+					br->consumed_bits = 0;
+				}
+				return true;
+			}
+			else {
+				*val += FLAC__BITS_PER_WORD - br->consumed_bits;
+				br->consumed_words++;
+				br->consumed_bits = 0;
+				/* didn't find stop bit yet, have to keep going... */
+			}
+		}
+		/* at this point we've eaten up all the whole words; have to try
+		 * reading through any tail bytes before calling the read callback.
+		 * this is a repeat of the above logic adjusted for the fact we
+		 * don't have a whole word.  note though if the client is feeding
+		 * us data a byte at a time (unlikely), br->consumed_bits may not
+		 * be zero.
+		 */
+		if(br->bytes*8 > br->consumed_bits) {
+			const uint32_t end = br->bytes * 8;
+			brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits;
+			if(b) {
+				i = COUNT_ZERO_MSBS(b);
+				*val += i;
+				i++;
+				br->consumed_bits += i;
+				return true;
+			}
+			else {
+				*val += end - br->consumed_bits;
+				br->consumed_bits = end;
+				/* didn't find stop bit yet, have to keep going... */
+			}
+		}
+		if(!bitreader_read_from_client_(br))
+			return false;
+	}
+}
+
+FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, uint32_t parameter)
+{
+	FLAC__uint32 lsbs = 0, msbs = 0;
+	uint32_t uval;
+
+	/* read the unary MSBs and end bit */
+	if(!FLAC__bitreader_read_unary_unsigned(br, &msbs))
+		return false;
+
+	/* read the binary LSBs */
+	if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter))
+		return false;
+
+	/* compose the value */
+	uval = (msbs << parameter) | lsbs;
+	if(uval & 1)
+		*val = -((int)(uval >> 1)) - 1;
+	else
+		*val = (int)(uval >> 1);
+
+	return true;
+}
+
+/* this is by far the most heavily used reader call.  it ain't pretty but it's fast */
+FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter)
+{
+	/* try and get br->consumed_words and br->consumed_bits into register;
+	 * must remember to flush them back to *br before calling other
+	 * bitreader functions that use them, and before returning */
+	uint32_t cwords, words, lsbs, msbs, x, y;
+	uint32_t ucbits; /* keep track of the number of unconsumed bits in word */
+	brword b;
+	int *val, *end;
+
+	val = vals;
+	end = vals + nvals;
+
+	if(parameter == 0) {
+		while(val < end) {
+			/* read the unary MSBs and end bit */
+			if(!FLAC__bitreader_read_unary_unsigned(br, &msbs))
+				return false;
+
+			*val++ = (int)(msbs >> 1) ^ -(int)(msbs & 1);
+		}
+
+		return true;
+	}
+
+	cwords = br->consumed_words;
+	words = br->words;
+
+	/* if we've not consumed up to a partial tail word... */
+	if(cwords >= words) {
+		x = 0;
+		goto process_tail;
+	}
+
+	ucbits = FLAC__BITS_PER_WORD - br->consumed_bits;
+	b = br->buffer[cwords] << br->consumed_bits;  /* keep unconsumed bits aligned to left */
+
+	while(val < end) {
+		/* read the unary MSBs and end bit */
+		x = y = COUNT_ZERO_MSBS2(b);
+		if(x == FLAC__BITS_PER_WORD) {
+			x = ucbits;
+			do {
+				/* didn't find stop bit yet, have to keep going... */
+				cwords++;
+				if (cwords >= words)
+					goto incomplete_msbs;
+				b = br->buffer[cwords];
+				y = COUNT_ZERO_MSBS2(b);
+				x += y;
+			} while(y == FLAC__BITS_PER_WORD);
+		}
+		b <<= y;
+		b <<= 1; /* account for stop bit */
+		ucbits = (ucbits - x - 1) % FLAC__BITS_PER_WORD;
+		msbs = x;
+
+		/* read the binary LSBs */
+		x = (FLAC__uint32)(b >> (FLAC__BITS_PER_WORD - parameter)); /* parameter < 32, so we can cast to 32-bit uint32_t */
+		if(parameter <= ucbits) {
+			ucbits -= parameter;
+			b <<= parameter;
+		} else {
+			/* there are still bits left to read, they will all be in the next word */
+			cwords++;
+			if (cwords >= words)
+				goto incomplete_lsbs;
+			b = br->buffer[cwords];
+			ucbits += FLAC__BITS_PER_WORD - parameter;
+			x |= (FLAC__uint32)(b >> ucbits);
+			b <<= FLAC__BITS_PER_WORD - ucbits;
+		}
+		lsbs = x;
+
+		/* compose the value */
+		x = (msbs << parameter) | lsbs;
+		*val++ = (int)(x >> 1) ^ -(int)(x & 1);
+
+		continue;
+
+		/* at this point we've eaten up all the whole words */
+process_tail:
+		do {
+			if(0) {
+incomplete_msbs:
+				br->consumed_bits = 0;
+				br->consumed_words = cwords;
+			}
+
+			/* read the unary MSBs and end bit */
+			if(!FLAC__bitreader_read_unary_unsigned(br, &msbs))
+				return false;
+			msbs += x;
+			x = ucbits = 0;
+
+			if(0) {
+incomplete_lsbs:
+				br->consumed_bits = 0;
+				br->consumed_words = cwords;
+			}
+
+			/* read the binary LSBs */
+			if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter - ucbits))
+				return false;
+			lsbs = x | lsbs;
+
+			/* compose the value */
+			x = (msbs << parameter) | lsbs;
+			*val++ = (int)(x >> 1) ^ -(int)(x & 1);
+			x = 0;
+
+			cwords = br->consumed_words;
+			words = br->words;
+			ucbits = FLAC__BITS_PER_WORD - br->consumed_bits;
+			b = br->buffer[cwords] << br->consumed_bits;
+		} while(cwords >= words && val < end);
+	}
+
+	if(ucbits == 0 && cwords < words) {
+		/* don't leave the head word with no unconsumed bits */
+		cwords++;
+		ucbits = FLAC__BITS_PER_WORD;
+	}
+
+	br->consumed_bits = FLAC__BITS_PER_WORD - ucbits;
+	br->consumed_words = cwords;
+
+	return true;
+}
+
+/* on return, if *val == 0xffffffff then the utf-8 sequence was invalid, but the return value will be true */
+FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, uint32_t *rawlen)
+{
+	FLAC__uint32 v = 0;
+	FLAC__uint32 x;
+	uint32_t i;
+
+	if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+		return false;
+	if(raw)
+		raw[(*rawlen)++] = (FLAC__byte)x;
+	if(!(x & 0x80)) { /* 0xxxxxxx */
+		v = x;
+		i = 0;
+	}
+	else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */
+		v = x & 0x1F;
+		i = 1;
+	}
+	else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */
+		v = x & 0x0F;
+		i = 2;
+	}
+	else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */
+		v = x & 0x07;
+		i = 3;
+	}
+	else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */
+		v = x & 0x03;
+		i = 4;
+	}
+	else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */
+		v = x & 0x01;
+		i = 5;
+	}
+	else {
+		*val = 0xffffffff;
+		return true;
+	}
+	for( ; i; i--) {
+		if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+			return false;
+		if(raw)
+			raw[(*rawlen)++] = (FLAC__byte)x;
+		if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */
+			*val = 0xffffffff;
+			return true;
+		}
+		v <<= 6;
+		v |= (x & 0x3F);
+	}
+	*val = v;
+	return true;
+}
+
+/* on return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but the return value will be true */
+FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, uint32_t *rawlen)
+{
+	FLAC__uint64 v = 0;
+	FLAC__uint32 x;
+	uint32_t i;
+
+	if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+		return false;
+	if(raw)
+		raw[(*rawlen)++] = (FLAC__byte)x;
+	if(!(x & 0x80)) { /* 0xxxxxxx */
+		v = x;
+		i = 0;
+	}
+	else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */
+		v = x & 0x1F;
+		i = 1;
+	}
+	else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */
+		v = x & 0x0F;
+		i = 2;
+	}
+	else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */
+		v = x & 0x07;
+		i = 3;
+	}
+	else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */
+		v = x & 0x03;
+		i = 4;
+	}
+	else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */
+		v = x & 0x01;
+		i = 5;
+	}
+	else if(x & 0xFE && !(x & 0x01)) { /* 11111110 */
+		v = 0;
+		i = 6;
+	}
+	else {
+		*val = FLAC__U64L(0xffffffffffffffff);
+		return true;
+	}
+	for( ; i; i--) {
+		if(!FLAC__bitreader_read_raw_uint32(br, &x, 8))
+			return false;
+		if(raw)
+			raw[(*rawlen)++] = (FLAC__byte)x;
+		if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */
+			*val = FLAC__U64L(0xffffffffffffffff);
+			return true;
+		}
+		v <<= 6;
+		v |= (x & 0x3F);
+	}
+	*val = v;
+	return true;
+}
+
+/* These functions are declared inline in this file but are also callable as
+ * externs from elsewhere.
+ * According to the C99 spec, section 6.7.4, simply providing a function
+ * prototype in a header file without 'inline' and making the function inline
+ * in this file should be sufficient.
+ * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To
+ * fix that we add extern declarations here.
+ */
+extern FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br);
+extern uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br);
+extern uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br);
+extern FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val);
--- /dev/null
+++ b/src/libflac/crc.c
@@ -1,0 +1,410 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2018  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "private/crc.h"
+
+/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */
+
+FLAC__uint8 const FLAC__crc8_table[256] = {
+	0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
+	0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+	0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
+	0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
+	0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
+	0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+	0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
+	0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
+	0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
+	0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
+	0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
+	0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+	0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
+	0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
+	0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
+	0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
+	0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
+	0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+	0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
+	0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
+	0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
+	0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
+	0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
+	0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+	0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
+	0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
+	0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
+	0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
+	0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
+	0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+	0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
+	0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
+};
+
+/* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */
+
+FLAC__uint16 const FLAC__crc16_table[8][256] = {
+  { 0x0000,  0x8005,  0x800f,  0x000a,  0x801b,  0x001e,  0x0014,  0x8011,
+	0x8033,  0x0036,  0x003c,  0x8039,  0x0028,  0x802d,  0x8027,  0x0022,
+	0x8063,  0x0066,  0x006c,  0x8069,  0x0078,  0x807d,  0x8077,  0x0072,
+	0x0050,  0x8055,  0x805f,  0x005a,  0x804b,  0x004e,  0x0044,  0x8041,
+	0x80c3,  0x00c6,  0x00cc,  0x80c9,  0x00d8,  0x80dd,  0x80d7,  0x00d2,
+	0x00f0,  0x80f5,  0x80ff,  0x00fa,  0x80eb,  0x00ee,  0x00e4,  0x80e1,
+	0x00a0,  0x80a5,  0x80af,  0x00aa,  0x80bb,  0x00be,  0x00b4,  0x80b1,
+	0x8093,  0x0096,  0x009c,  0x8099,  0x0088,  0x808d,  0x8087,  0x0082,
+	0x8183,  0x0186,  0x018c,  0x8189,  0x0198,  0x819d,  0x8197,  0x0192,
+	0x01b0,  0x81b5,  0x81bf,  0x01ba,  0x81ab,  0x01ae,  0x01a4,  0x81a1,
+	0x01e0,  0x81e5,  0x81ef,  0x01ea,  0x81fb,  0x01fe,  0x01f4,  0x81f1,
+	0x81d3,  0x01d6,  0x01dc,  0x81d9,  0x01c8,  0x81cd,  0x81c7,  0x01c2,
+	0x0140,  0x8145,  0x814f,  0x014a,  0x815b,  0x015e,  0x0154,  0x8151,
+	0x8173,  0x0176,  0x017c,  0x8179,  0x0168,  0x816d,  0x8167,  0x0162,
+	0x8123,  0x0126,  0x012c,  0x8129,  0x0138,  0x813d,  0x8137,  0x0132,
+	0x0110,  0x8115,  0x811f,  0x011a,  0x810b,  0x010e,  0x0104,  0x8101,
+	0x8303,  0x0306,  0x030c,  0x8309,  0x0318,  0x831d,  0x8317,  0x0312,
+	0x0330,  0x8335,  0x833f,  0x033a,  0x832b,  0x032e,  0x0324,  0x8321,
+	0x0360,  0x8365,  0x836f,  0x036a,  0x837b,  0x037e,  0x0374,  0x8371,
+	0x8353,  0x0356,  0x035c,  0x8359,  0x0348,  0x834d,  0x8347,  0x0342,
+	0x03c0,  0x83c5,  0x83cf,  0x03ca,  0x83db,  0x03de,  0x03d4,  0x83d1,
+	0x83f3,  0x03f6,  0x03fc,  0x83f9,  0x03e8,  0x83ed,  0x83e7,  0x03e2,
+	0x83a3,  0x03a6,  0x03ac,  0x83a9,  0x03b8,  0x83bd,  0x83b7,  0x03b2,
+	0x0390,  0x8395,  0x839f,  0x039a,  0x838b,  0x038e,  0x0384,  0x8381,
+	0x0280,  0x8285,  0x828f,  0x028a,  0x829b,  0x029e,  0x0294,  0x8291,
+	0x82b3,  0x02b6,  0x02bc,  0x82b9,  0x02a8,  0x82ad,  0x82a7,  0x02a2,
+	0x82e3,  0x02e6,  0x02ec,  0x82e9,  0x02f8,  0x82fd,  0x82f7,  0x02f2,
+	0x02d0,  0x82d5,  0x82df,  0x02da,  0x82cb,  0x02ce,  0x02c4,  0x82c1,
+	0x8243,  0x0246,  0x024c,  0x8249,  0x0258,  0x825d,  0x8257,  0x0252,
+	0x0270,  0x8275,  0x827f,  0x027a,  0x826b,  0x026e,  0x0264,  0x8261,
+	0x0220,  0x8225,  0x822f,  0x022a,  0x823b,  0x023e,  0x0234,  0x8231,
+	0x8213,  0x0216,  0x021c,  0x8219,  0x0208,  0x820d,  0x8207,  0x0202 },
+
+  { 0x0000,  0x8603,  0x8c03,  0x0a00,  0x9803,  0x1e00,  0x1400,  0x9203,
+	0xb003,  0x3600,  0x3c00,  0xba03,  0x2800,  0xae03,  0xa403,  0x2200,
+	0xe003,  0x6600,  0x6c00,  0xea03,  0x7800,  0xfe03,  0xf403,  0x7200,
+	0x5000,  0xd603,  0xdc03,  0x5a00,  0xc803,  0x4e00,  0x4400,  0xc203,
+	0x4003,  0xc600,  0xcc00,  0x4a03,  0xd800,  0x5e03,  0x5403,  0xd200,
+	0xf000,  0x7603,  0x7c03,  0xfa00,  0x6803,  0xee00,  0xe400,  0x6203,
+	0xa000,  0x2603,  0x2c03,  0xaa00,  0x3803,  0xbe00,  0xb400,  0x3203,
+	0x1003,  0x9600,  0x9c00,  0x1a03,  0x8800,  0x0e03,  0x0403,  0x8200,
+	0x8006,  0x0605,  0x0c05,  0x8a06,  0x1805,  0x9e06,  0x9406,  0x1205,
+	0x3005,  0xb606,  0xbc06,  0x3a05,  0xa806,  0x2e05,  0x2405,  0xa206,
+	0x6005,  0xe606,  0xec06,  0x6a05,  0xf806,  0x7e05,  0x7405,  0xf206,
+	0xd006,  0x5605,  0x5c05,  0xda06,  0x4805,  0xce06,  0xc406,  0x4205,
+	0xc005,  0x4606,  0x4c06,  0xca05,  0x5806,  0xde05,  0xd405,  0x5206,
+	0x7006,  0xf605,  0xfc05,  0x7a06,  0xe805,  0x6e06,  0x6406,  0xe205,
+	0x2006,  0xa605,  0xac05,  0x2a06,  0xb805,  0x3e06,  0x3406,  0xb205,
+	0x9005,  0x1606,  0x1c06,  0x9a05,  0x0806,  0x8e05,  0x8405,  0x0206,
+	0x8009,  0x060a,  0x0c0a,  0x8a09,  0x180a,  0x9e09,  0x9409,  0x120a,
+	0x300a,  0xb609,  0xbc09,  0x3a0a,  0xa809,  0x2e0a,  0x240a,  0xa209,
+	0x600a,  0xe609,  0xec09,  0x6a0a,  0xf809,  0x7e0a,  0x740a,  0xf209,
+	0xd009,  0x560a,  0x5c0a,  0xda09,  0x480a,  0xce09,  0xc409,  0x420a,
+	0xc00a,  0x4609,  0x4c09,  0xca0a,  0x5809,  0xde0a,  0xd40a,  0x5209,
+	0x7009,  0xf60a,  0xfc0a,  0x7a09,  0xe80a,  0x6e09,  0x6409,  0xe20a,
+	0x2009,  0xa60a,  0xac0a,  0x2a09,  0xb80a,  0x3e09,  0x3409,  0xb20a,
+	0x900a,  0x1609,  0x1c09,  0x9a0a,  0x0809,  0x8e0a,  0x840a,  0x0209,
+	0x000f,  0x860c,  0x8c0c,  0x0a0f,  0x980c,  0x1e0f,  0x140f,  0x920c,
+	0xb00c,  0x360f,  0x3c0f,  0xba0c,  0x280f,  0xae0c,  0xa40c,  0x220f,
+	0xe00c,  0x660f,  0x6c0f,  0xea0c,  0x780f,  0xfe0c,  0xf40c,  0x720f,
+	0x500f,  0xd60c,  0xdc0c,  0x5a0f,  0xc80c,  0x4e0f,  0x440f,  0xc20c,
+	0x400c,  0xc60f,  0xcc0f,  0x4a0c,  0xd80f,  0x5e0c,  0x540c,  0xd20f,
+	0xf00f,  0x760c,  0x7c0c,  0xfa0f,  0x680c,  0xee0f,  0xe40f,  0x620c,
+	0xa00f,  0x260c,  0x2c0c,  0xaa0f,  0x380c,  0xbe0f,  0xb40f,  0x320c,
+	0x100c,  0x960f,  0x9c0f,  0x1a0c,  0x880f,  0x0e0c,  0x040c,  0x820f },
+
+  { 0x0000,  0x8017,  0x802b,  0x003c,  0x8053,  0x0044,  0x0078,  0x806f,
+	0x80a3,  0x00b4,  0x0088,  0x809f,  0x00f0,  0x80e7,  0x80db,  0x00cc,
+	0x8143,  0x0154,  0x0168,  0x817f,  0x0110,  0x8107,  0x813b,  0x012c,
+	0x01e0,  0x81f7,  0x81cb,  0x01dc,  0x81b3,  0x01a4,  0x0198,  0x818f,
+	0x8283,  0x0294,  0x02a8,  0x82bf,  0x02d0,  0x82c7,  0x82fb,  0x02ec,
+	0x0220,  0x8237,  0x820b,  0x021c,  0x8273,  0x0264,  0x0258,  0x824f,
+	0x03c0,  0x83d7,  0x83eb,  0x03fc,  0x8393,  0x0384,  0x03b8,  0x83af,
+	0x8363,  0x0374,  0x0348,  0x835f,  0x0330,  0x8327,  0x831b,  0x030c,
+	0x8503,  0x0514,  0x0528,  0x853f,  0x0550,  0x8547,  0x857b,  0x056c,
+	0x05a0,  0x85b7,  0x858b,  0x059c,  0x85f3,  0x05e4,  0x05d8,  0x85cf,
+	0x0440,  0x8457,  0x846b,  0x047c,  0x8413,  0x0404,  0x0438,  0x842f,
+	0x84e3,  0x04f4,  0x04c8,  0x84df,  0x04b0,  0x84a7,  0x849b,  0x048c,
+	0x0780,  0x8797,  0x87ab,  0x07bc,  0x87d3,  0x07c4,  0x07f8,  0x87ef,
+	0x8723,  0x0734,  0x0708,  0x871f,  0x0770,  0x8767,  0x875b,  0x074c,
+	0x86c3,  0x06d4,  0x06e8,  0x86ff,  0x0690,  0x8687,  0x86bb,  0x06ac,
+	0x0660,  0x8677,  0x864b,  0x065c,  0x8633,  0x0624,  0x0618,  0x860f,
+	0x8a03,  0x0a14,  0x0a28,  0x8a3f,  0x0a50,  0x8a47,  0x8a7b,  0x0a6c,
+	0x0aa0,  0x8ab7,  0x8a8b,  0x0a9c,  0x8af3,  0x0ae4,  0x0ad8,  0x8acf,
+	0x0b40,  0x8b57,  0x8b6b,  0x0b7c,  0x8b13,  0x0b04,  0x0b38,  0x8b2f,
+	0x8be3,  0x0bf4,  0x0bc8,  0x8bdf,  0x0bb0,  0x8ba7,  0x8b9b,  0x0b8c,
+	0x0880,  0x8897,  0x88ab,  0x08bc,  0x88d3,  0x08c4,  0x08f8,  0x88ef,
+	0x8823,  0x0834,  0x0808,  0x881f,  0x0870,  0x8867,  0x885b,  0x084c,
+	0x89c3,  0x09d4,  0x09e8,  0x89ff,  0x0990,  0x8987,  0x89bb,  0x09ac,
+	0x0960,  0x8977,  0x894b,  0x095c,  0x8933,  0x0924,  0x0918,  0x890f,
+	0x0f00,  0x8f17,  0x8f2b,  0x0f3c,  0x8f53,  0x0f44,  0x0f78,  0x8f6f,
+	0x8fa3,  0x0fb4,  0x0f88,  0x8f9f,  0x0ff0,  0x8fe7,  0x8fdb,  0x0fcc,
+	0x8e43,  0x0e54,  0x0e68,  0x8e7f,  0x0e10,  0x8e07,  0x8e3b,  0x0e2c,
+	0x0ee0,  0x8ef7,  0x8ecb,  0x0edc,  0x8eb3,  0x0ea4,  0x0e98,  0x8e8f,
+	0x8d83,  0x0d94,  0x0da8,  0x8dbf,  0x0dd0,  0x8dc7,  0x8dfb,  0x0dec,
+	0x0d20,  0x8d37,  0x8d0b,  0x0d1c,  0x8d73,  0x0d64,  0x0d58,  0x8d4f,
+	0x0cc0,  0x8cd7,  0x8ceb,  0x0cfc,  0x8c93,  0x0c84,  0x0cb8,  0x8caf,
+	0x8c63,  0x0c74,  0x0c48,  0x8c5f,  0x0c30,  0x8c27,  0x8c1b,  0x0c0c },
+
+  { 0x0000,  0x9403,  0xa803,  0x3c00,  0xd003,  0x4400,  0x7800,  0xec03,
+	0x2003,  0xb400,  0x8800,  0x1c03,  0xf000,  0x6403,  0x5803,  0xcc00,
+	0x4006,  0xd405,  0xe805,  0x7c06,  0x9005,  0x0406,  0x3806,  0xac05,
+	0x6005,  0xf406,  0xc806,  0x5c05,  0xb006,  0x2405,  0x1805,  0x8c06,
+	0x800c,  0x140f,  0x280f,  0xbc0c,  0x500f,  0xc40c,  0xf80c,  0x6c0f,
+	0xa00f,  0x340c,  0x080c,  0x9c0f,  0x700c,  0xe40f,  0xd80f,  0x4c0c,
+	0xc00a,  0x5409,  0x6809,  0xfc0a,  0x1009,  0x840a,  0xb80a,  0x2c09,
+	0xe009,  0x740a,  0x480a,  0xdc09,  0x300a,  0xa409,  0x9809,  0x0c0a,
+	0x801d,  0x141e,  0x281e,  0xbc1d,  0x501e,  0xc41d,  0xf81d,  0x6c1e,
+	0xa01e,  0x341d,  0x081d,  0x9c1e,  0x701d,  0xe41e,  0xd81e,  0x4c1d,
+	0xc01b,  0x5418,  0x6818,  0xfc1b,  0x1018,  0x841b,  0xb81b,  0x2c18,
+	0xe018,  0x741b,  0x481b,  0xdc18,  0x301b,  0xa418,  0x9818,  0x0c1b,
+	0x0011,  0x9412,  0xa812,  0x3c11,  0xd012,  0x4411,  0x7811,  0xec12,
+	0x2012,  0xb411,  0x8811,  0x1c12,  0xf011,  0x6412,  0x5812,  0xcc11,
+	0x4017,  0xd414,  0xe814,  0x7c17,  0x9014,  0x0417,  0x3817,  0xac14,
+	0x6014,  0xf417,  0xc817,  0x5c14,  0xb017,  0x2414,  0x1814,  0x8c17,
+	0x803f,  0x143c,  0x283c,  0xbc3f,  0x503c,  0xc43f,  0xf83f,  0x6c3c,
+	0xa03c,  0x343f,  0x083f,  0x9c3c,  0x703f,  0xe43c,  0xd83c,  0x4c3f,
+	0xc039,  0x543a,  0x683a,  0xfc39,  0x103a,  0x8439,  0xb839,  0x2c3a,
+	0xe03a,  0x7439,  0x4839,  0xdc3a,  0x3039,  0xa43a,  0x983a,  0x0c39,
+	0x0033,  0x9430,  0xa830,  0x3c33,  0xd030,  0x4433,  0x7833,  0xec30,
+	0x2030,  0xb433,  0x8833,  0x1c30,  0xf033,  0x6430,  0x5830,  0xcc33,
+	0x4035,  0xd436,  0xe836,  0x7c35,  0x9036,  0x0435,  0x3835,  0xac36,
+	0x6036,  0xf435,  0xc835,  0x5c36,  0xb035,  0x2436,  0x1836,  0x8c35,
+	0x0022,  0x9421,  0xa821,  0x3c22,  0xd021,  0x4422,  0x7822,  0xec21,
+	0x2021,  0xb422,  0x8822,  0x1c21,  0xf022,  0x6421,  0x5821,  0xcc22,
+	0x4024,  0xd427,  0xe827,  0x7c24,  0x9027,  0x0424,  0x3824,  0xac27,
+	0x6027,  0xf424,  0xc824,  0x5c27,  0xb024,  0x2427,  0x1827,  0x8c24,
+	0x802e,  0x142d,  0x282d,  0xbc2e,  0x502d,  0xc42e,  0xf82e,  0x6c2d,
+	0xa02d,  0x342e,  0x082e,  0x9c2d,  0x702e,  0xe42d,  0xd82d,  0x4c2e,
+	0xc028,  0x542b,  0x682b,  0xfc28,  0x102b,  0x8428,  0xb828,  0x2c2b,
+	0xe02b,  0x7428,  0x4828,  0xdc2b,  0x3028,  0xa42b,  0x982b,  0x0c28 },
+
+  { 0x0000,  0x807b,  0x80f3,  0x0088,  0x81e3,  0x0198,  0x0110,  0x816b,
+	0x83c3,  0x03b8,  0x0330,  0x834b,  0x0220,  0x825b,  0x82d3,  0x02a8,
+	0x8783,  0x07f8,  0x0770,  0x870b,  0x0660,  0x861b,  0x8693,  0x06e8,
+	0x0440,  0x843b,  0x84b3,  0x04c8,  0x85a3,  0x05d8,  0x0550,  0x852b,
+	0x8f03,  0x0f78,  0x0ff0,  0x8f8b,  0x0ee0,  0x8e9b,  0x8e13,  0x0e68,
+	0x0cc0,  0x8cbb,  0x8c33,  0x0c48,  0x8d23,  0x0d58,  0x0dd0,  0x8dab,
+	0x0880,  0x88fb,  0x8873,  0x0808,  0x8963,  0x0918,  0x0990,  0x89eb,
+	0x8b43,  0x0b38,  0x0bb0,  0x8bcb,  0x0aa0,  0x8adb,  0x8a53,  0x0a28,
+	0x9e03,  0x1e78,  0x1ef0,  0x9e8b,  0x1fe0,  0x9f9b,  0x9f13,  0x1f68,
+	0x1dc0,  0x9dbb,  0x9d33,  0x1d48,  0x9c23,  0x1c58,  0x1cd0,  0x9cab,
+	0x1980,  0x99fb,  0x9973,  0x1908,  0x9863,  0x1818,  0x1890,  0x98eb,
+	0x9a43,  0x1a38,  0x1ab0,  0x9acb,  0x1ba0,  0x9bdb,  0x9b53,  0x1b28,
+	0x1100,  0x917b,  0x91f3,  0x1188,  0x90e3,  0x1098,  0x1010,  0x906b,
+	0x92c3,  0x12b8,  0x1230,  0x924b,  0x1320,  0x935b,  0x93d3,  0x13a8,
+	0x9683,  0x16f8,  0x1670,  0x960b,  0x1760,  0x971b,  0x9793,  0x17e8,
+	0x1540,  0x953b,  0x95b3,  0x15c8,  0x94a3,  0x14d8,  0x1450,  0x942b,
+	0xbc03,  0x3c78,  0x3cf0,  0xbc8b,  0x3de0,  0xbd9b,  0xbd13,  0x3d68,
+	0x3fc0,  0xbfbb,  0xbf33,  0x3f48,  0xbe23,  0x3e58,  0x3ed0,  0xbeab,
+	0x3b80,  0xbbfb,  0xbb73,  0x3b08,  0xba63,  0x3a18,  0x3a90,  0xbaeb,
+	0xb843,  0x3838,  0x38b0,  0xb8cb,  0x39a0,  0xb9db,  0xb953,  0x3928,
+	0x3300,  0xb37b,  0xb3f3,  0x3388,  0xb2e3,  0x3298,  0x3210,  0xb26b,
+	0xb0c3,  0x30b8,  0x3030,  0xb04b,  0x3120,  0xb15b,  0xb1d3,  0x31a8,
+	0xb483,  0x34f8,  0x3470,  0xb40b,  0x3560,  0xb51b,  0xb593,  0x35e8,
+	0x3740,  0xb73b,  0xb7b3,  0x37c8,  0xb6a3,  0x36d8,  0x3650,  0xb62b,
+	0x2200,  0xa27b,  0xa2f3,  0x2288,  0xa3e3,  0x2398,  0x2310,  0xa36b,
+	0xa1c3,  0x21b8,  0x2130,  0xa14b,  0x2020,  0xa05b,  0xa0d3,  0x20a8,
+	0xa583,  0x25f8,  0x2570,  0xa50b,  0x2460,  0xa41b,  0xa493,  0x24e8,
+	0x2640,  0xa63b,  0xa6b3,  0x26c8,  0xa7a3,  0x27d8,  0x2750,  0xa72b,
+	0xad03,  0x2d78,  0x2df0,  0xad8b,  0x2ce0,  0xac9b,  0xac13,  0x2c68,
+	0x2ec0,  0xaebb,  0xae33,  0x2e48,  0xaf23,  0x2f58,  0x2fd0,  0xafab,
+	0x2a80,  0xaafb,  0xaa73,  0x2a08,  0xab63,  0x2b18,  0x2b90,  0xabeb,
+	0xa943,  0x2938,  0x29b0,  0xa9cb,  0x28a0,  0xa8db,  0xa853,  0x2828 },
+
+  { 0x0000,  0xf803,  0x7003,  0x8800,  0xe006,  0x1805,  0x9005,  0x6806,
+	0x4009,  0xb80a,  0x300a,  0xc809,  0xa00f,  0x580c,  0xd00c,  0x280f,
+	0x8012,  0x7811,  0xf011,  0x0812,  0x6014,  0x9817,  0x1017,  0xe814,
+	0xc01b,  0x3818,  0xb018,  0x481b,  0x201d,  0xd81e,  0x501e,  0xa81d,
+	0x8021,  0x7822,  0xf022,  0x0821,  0x6027,  0x9824,  0x1024,  0xe827,
+	0xc028,  0x382b,  0xb02b,  0x4828,  0x202e,  0xd82d,  0x502d,  0xa82e,
+	0x0033,  0xf830,  0x7030,  0x8833,  0xe035,  0x1836,  0x9036,  0x6835,
+	0x403a,  0xb839,  0x3039,  0xc83a,  0xa03c,  0x583f,  0xd03f,  0x283c,
+	0x8047,  0x7844,  0xf044,  0x0847,  0x6041,  0x9842,  0x1042,  0xe841,
+	0xc04e,  0x384d,  0xb04d,  0x484e,  0x2048,  0xd84b,  0x504b,  0xa848,
+	0x0055,  0xf856,  0x7056,  0x8855,  0xe053,  0x1850,  0x9050,  0x6853,
+	0x405c,  0xb85f,  0x305f,  0xc85c,  0xa05a,  0x5859,  0xd059,  0x285a,
+	0x0066,  0xf865,  0x7065,  0x8866,  0xe060,  0x1863,  0x9063,  0x6860,
+	0x406f,  0xb86c,  0x306c,  0xc86f,  0xa069,  0x586a,  0xd06a,  0x2869,
+	0x8074,  0x7877,  0xf077,  0x0874,  0x6072,  0x9871,  0x1071,  0xe872,
+	0xc07d,  0x387e,  0xb07e,  0x487d,  0x207b,  0xd878,  0x5078,  0xa87b,
+	0x808b,  0x7888,  0xf088,  0x088b,  0x608d,  0x988e,  0x108e,  0xe88d,
+	0xc082,  0x3881,  0xb081,  0x4882,  0x2084,  0xd887,  0x5087,  0xa884,
+	0x0099,  0xf89a,  0x709a,  0x8899,  0xe09f,  0x189c,  0x909c,  0x689f,
+	0x4090,  0xb893,  0x3093,  0xc890,  0xa096,  0x5895,  0xd095,  0x2896,
+	0x00aa,  0xf8a9,  0x70a9,  0x88aa,  0xe0ac,  0x18af,  0x90af,  0x68ac,
+	0x40a3,  0xb8a0,  0x30a0,  0xc8a3,  0xa0a5,  0x58a6,  0xd0a6,  0x28a5,
+	0x80b8,  0x78bb,  0xf0bb,  0x08b8,  0x60be,  0x98bd,  0x10bd,  0xe8be,
+	0xc0b1,  0x38b2,  0xb0b2,  0x48b1,  0x20b7,  0xd8b4,  0x50b4,  0xa8b7,
+	0x00cc,  0xf8cf,  0x70cf,  0x88cc,  0xe0ca,  0x18c9,  0x90c9,  0x68ca,
+	0x40c5,  0xb8c6,  0x30c6,  0xc8c5,  0xa0c3,  0x58c0,  0xd0c0,  0x28c3,
+	0x80de,  0x78dd,  0xf0dd,  0x08de,  0x60d8,  0x98db,  0x10db,  0xe8d8,
+	0xc0d7,  0x38d4,  0xb0d4,  0x48d7,  0x20d1,  0xd8d2,  0x50d2,  0xa8d1,
+	0x80ed,  0x78ee,  0xf0ee,  0x08ed,  0x60eb,  0x98e8,  0x10e8,  0xe8eb,
+	0xc0e4,  0x38e7,  0xb0e7,  0x48e4,  0x20e2,  0xd8e1,  0x50e1,  0xa8e2,
+	0x00ff,  0xf8fc,  0x70fc,  0x88ff,  0xe0f9,  0x18fa,  0x90fa,  0x68f9,
+	0x40f6,  0xb8f5,  0x30f5,  0xc8f6,  0xa0f0,  0x58f3,  0xd0f3,  0x28f0 },
+
+  { 0x0000,  0x8113,  0x8223,  0x0330,  0x8443,  0x0550,  0x0660,  0x8773,
+	0x8883,  0x0990,  0x0aa0,  0x8bb3,  0x0cc0,  0x8dd3,  0x8ee3,  0x0ff0,
+	0x9103,  0x1010,  0x1320,  0x9233,  0x1540,  0x9453,  0x9763,  0x1670,
+	0x1980,  0x9893,  0x9ba3,  0x1ab0,  0x9dc3,  0x1cd0,  0x1fe0,  0x9ef3,
+	0xa203,  0x2310,  0x2020,  0xa133,  0x2640,  0xa753,  0xa463,  0x2570,
+	0x2a80,  0xab93,  0xa8a3,  0x29b0,  0xaec3,  0x2fd0,  0x2ce0,  0xadf3,
+	0x3300,  0xb213,  0xb123,  0x3030,  0xb743,  0x3650,  0x3560,  0xb473,
+	0xbb83,  0x3a90,  0x39a0,  0xb8b3,  0x3fc0,  0xbed3,  0xbde3,  0x3cf0,
+	0xc403,  0x4510,  0x4620,  0xc733,  0x4040,  0xc153,  0xc263,  0x4370,
+	0x4c80,  0xcd93,  0xcea3,  0x4fb0,  0xc8c3,  0x49d0,  0x4ae0,  0xcbf3,
+	0x5500,  0xd413,  0xd723,  0x5630,  0xd143,  0x5050,  0x5360,  0xd273,
+	0xdd83,  0x5c90,  0x5fa0,  0xdeb3,  0x59c0,  0xd8d3,  0xdbe3,  0x5af0,
+	0x6600,  0xe713,  0xe423,  0x6530,  0xe243,  0x6350,  0x6060,  0xe173,
+	0xee83,  0x6f90,  0x6ca0,  0xedb3,  0x6ac0,  0xebd3,  0xe8e3,  0x69f0,
+	0xf703,  0x7610,  0x7520,  0xf433,  0x7340,  0xf253,  0xf163,  0x7070,
+	0x7f80,  0xfe93,  0xfda3,  0x7cb0,  0xfbc3,  0x7ad0,  0x79e0,  0xf8f3,
+	0x0803,  0x8910,  0x8a20,  0x0b33,  0x8c40,  0x0d53,  0x0e63,  0x8f70,
+	0x8080,  0x0193,  0x02a3,  0x83b0,  0x04c3,  0x85d0,  0x86e0,  0x07f3,
+	0x9900,  0x1813,  0x1b23,  0x9a30,  0x1d43,  0x9c50,  0x9f60,  0x1e73,
+	0x1183,  0x9090,  0x93a0,  0x12b3,  0x95c0,  0x14d3,  0x17e3,  0x96f0,
+	0xaa00,  0x2b13,  0x2823,  0xa930,  0x2e43,  0xaf50,  0xac60,  0x2d73,
+	0x2283,  0xa390,  0xa0a0,  0x21b3,  0xa6c0,  0x27d3,  0x24e3,  0xa5f0,
+	0x3b03,  0xba10,  0xb920,  0x3833,  0xbf40,  0x3e53,  0x3d63,  0xbc70,
+	0xb380,  0x3293,  0x31a3,  0xb0b0,  0x37c3,  0xb6d0,  0xb5e0,  0x34f3,
+	0xcc00,  0x4d13,  0x4e23,  0xcf30,  0x4843,  0xc950,  0xca60,  0x4b73,
+	0x4483,  0xc590,  0xc6a0,  0x47b3,  0xc0c0,  0x41d3,  0x42e3,  0xc3f0,
+	0x5d03,  0xdc10,  0xdf20,  0x5e33,  0xd940,  0x5853,  0x5b63,  0xda70,
+	0xd580,  0x5493,  0x57a3,  0xd6b0,  0x51c3,  0xd0d0,  0xd3e0,  0x52f3,
+	0x6e03,  0xef10,  0xec20,  0x6d33,  0xea40,  0x6b53,  0x6863,  0xe970,
+	0xe680,  0x6793,  0x64a3,  0xe5b0,  0x62c3,  0xe3d0,  0xe0e0,  0x61f3,
+	0xff00,  0x7e13,  0x7d23,  0xfc30,  0x7b43,  0xfa50,  0xf960,  0x7873,
+	0x7783,  0xf690,  0xf5a0,  0x74b3,  0xf3c0,  0x72d3,  0x71e3,  0xf0f0 },
+
+  { 0x0000,  0x1006,  0x200c,  0x300a,  0x4018,  0x501e,  0x6014,  0x7012,
+	0x8030,  0x9036,  0xa03c,  0xb03a,  0xc028,  0xd02e,  0xe024,  0xf022,
+	0x8065,  0x9063,  0xa069,  0xb06f,  0xc07d,  0xd07b,  0xe071,  0xf077,
+	0x0055,  0x1053,  0x2059,  0x305f,  0x404d,  0x504b,  0x6041,  0x7047,
+	0x80cf,  0x90c9,  0xa0c3,  0xb0c5,  0xc0d7,  0xd0d1,  0xe0db,  0xf0dd,
+	0x00ff,  0x10f9,  0x20f3,  0x30f5,  0x40e7,  0x50e1,  0x60eb,  0x70ed,
+	0x00aa,  0x10ac,  0x20a6,  0x30a0,  0x40b2,  0x50b4,  0x60be,  0x70b8,
+	0x809a,  0x909c,  0xa096,  0xb090,  0xc082,  0xd084,  0xe08e,  0xf088,
+	0x819b,  0x919d,  0xa197,  0xb191,  0xc183,  0xd185,  0xe18f,  0xf189,
+	0x01ab,  0x11ad,  0x21a7,  0x31a1,  0x41b3,  0x51b5,  0x61bf,  0x71b9,
+	0x01fe,  0x11f8,  0x21f2,  0x31f4,  0x41e6,  0x51e0,  0x61ea,  0x71ec,
+	0x81ce,  0x91c8,  0xa1c2,  0xb1c4,  0xc1d6,  0xd1d0,  0xe1da,  0xf1dc,
+	0x0154,  0x1152,  0x2158,  0x315e,  0x414c,  0x514a,  0x6140,  0x7146,
+	0x8164,  0x9162,  0xa168,  0xb16e,  0xc17c,  0xd17a,  0xe170,  0xf176,
+	0x8131,  0x9137,  0xa13d,  0xb13b,  0xc129,  0xd12f,  0xe125,  0xf123,
+	0x0101,  0x1107,  0x210d,  0x310b,  0x4119,  0x511f,  0x6115,  0x7113,
+	0x8333,  0x9335,  0xa33f,  0xb339,  0xc32b,  0xd32d,  0xe327,  0xf321,
+	0x0303,  0x1305,  0x230f,  0x3309,  0x431b,  0x531d,  0x6317,  0x7311,
+	0x0356,  0x1350,  0x235a,  0x335c,  0x434e,  0x5348,  0x6342,  0x7344,
+	0x8366,  0x9360,  0xa36a,  0xb36c,  0xc37e,  0xd378,  0xe372,  0xf374,
+	0x03fc,  0x13fa,  0x23f0,  0x33f6,  0x43e4,  0x53e2,  0x63e8,  0x73ee,
+	0x83cc,  0x93ca,  0xa3c0,  0xb3c6,  0xc3d4,  0xd3d2,  0xe3d8,  0xf3de,
+	0x8399,  0x939f,  0xa395,  0xb393,  0xc381,  0xd387,  0xe38d,  0xf38b,
+	0x03a9,  0x13af,  0x23a5,  0x33a3,  0x43b1,  0x53b7,  0x63bd,  0x73bb,
+	0x02a8,  0x12ae,  0x22a4,  0x32a2,  0x42b0,  0x52b6,  0x62bc,  0x72ba,
+	0x8298,  0x929e,  0xa294,  0xb292,  0xc280,  0xd286,  0xe28c,  0xf28a,
+	0x82cd,  0x92cb,  0xa2c1,  0xb2c7,  0xc2d5,  0xd2d3,  0xe2d9,  0xf2df,
+	0x02fd,  0x12fb,  0x22f1,  0x32f7,  0x42e5,  0x52e3,  0x62e9,  0x72ef,
+	0x8267,  0x9261,  0xa26b,  0xb26d,  0xc27f,  0xd279,  0xe273,  0xf275,
+	0x0257,  0x1251,  0x225b,  0x325d,  0x424f,  0x5249,  0x6243,  0x7245,
+	0x0202,  0x1204,  0x220e,  0x3208,  0x421a,  0x521c,  0x6216,  0x7210,
+	0x8232,  0x9234,  0xa23e,  0xb238,  0xc22a,  0xd22c,  0xe226,  0xf220 }
+};
+
+FLAC__uint8 FLAC__crc8(const FLAC__byte *data, uint32_t len)
+{
+	FLAC__uint8 crc = 0;
+
+	while(len--)
+		crc = FLAC__crc8_table[crc ^ *data++];
+
+	return crc;
+}
+
+FLAC__uint16 FLAC__crc16(const FLAC__byte *data, uint32_t len)
+{
+	FLAC__uint16 crc = 0;
+
+	while(len >= 8){
+		crc ^= data[0] << 8 | data[1];
+
+		crc = FLAC__crc16_table[7][crc >> 8] ^ FLAC__crc16_table[6][crc & 0xFF] ^
+		      FLAC__crc16_table[5][data[2] ] ^ FLAC__crc16_table[4][data[3]   ] ^
+		      FLAC__crc16_table[3][data[4] ] ^ FLAC__crc16_table[2][data[5]   ] ^
+		      FLAC__crc16_table[1][data[6] ] ^ FLAC__crc16_table[0][data[7]   ];
+
+		data += 8;
+		len -= 8;
+	}
+
+	while(len--)
+		crc = (crc<<8) ^ FLAC__crc16_table[0][(crc>>8) ^ *data++];
+
+	return crc;
+}
+
+FLAC__uint16 FLAC__crc16_update_words32(const FLAC__uint32 *words, uint32_t len, FLAC__uint16 crc)
+{
+	while (len >= 2) {
+		crc ^= words[0] >> 16;
+
+		crc = FLAC__crc16_table[7][crc >> 8               ] ^ FLAC__crc16_table[6][crc & 0xFF             ] ^
+		      FLAC__crc16_table[5][(words[0] >>  8) & 0xFF] ^ FLAC__crc16_table[4][ words[0]        & 0xFF] ^
+		      FLAC__crc16_table[3][ words[1] >> 24        ] ^ FLAC__crc16_table[2][(words[1] >> 16) & 0xFF] ^
+		      FLAC__crc16_table[1][(words[1] >>  8) & 0xFF] ^ FLAC__crc16_table[0][ words[1]        & 0xFF];
+
+		words += 2;
+		len -= 2;
+	}
+
+	if (len) {
+		crc ^= words[0] >> 16;
+
+		crc = FLAC__crc16_table[3][crc >> 8               ] ^ FLAC__crc16_table[2][crc & 0xFF             ] ^
+		      FLAC__crc16_table[1][(words[0] >>  8) & 0xFF] ^ FLAC__crc16_table[0][words[0]         & 0xFF];
+	}
+
+	return crc;
+}
+
+FLAC__uint16 FLAC__crc16_update_words64(const FLAC__uint64 *words, uint32_t len, FLAC__uint16 crc)
+{
+	while (len--) {
+		crc ^= words[0] >> 48;
+
+		crc = FLAC__crc16_table[7][crc >> 8               ] ^ FLAC__crc16_table[6][crc & 0xFF             ] ^
+		      FLAC__crc16_table[5][(words[0] >> 40) & 0xFF] ^ FLAC__crc16_table[4][(words[0] >> 32) & 0xFF] ^
+		      FLAC__crc16_table[3][(words[0] >> 24) & 0xFF] ^ FLAC__crc16_table[2][(words[0] >> 16) & 0xFF] ^
+		      FLAC__crc16_table[1][(words[0] >>  8) & 0xFF] ^ FLAC__crc16_table[0][ words[0]        & 0xFF];
+
+		words++;
+	}
+
+	return crc;
+}
--- /dev/null
+++ b/src/libflac/fixed.c
@@ -1,0 +1,100 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <string.h>
+#include "share/compat.h"
+#include "private/bitmath.h"
+#include "private/fixed.h"
+#include "private/macros.h"
+
+#ifdef local_abs
+#undef local_abs
+#endif
+#define local_abs(x) ((uint32_t)((x)<0? -(x) : (x)))
+
+void FLAC__fixed_compute_residual(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[])
+{
+	const int idata_len = (int)data_len;
+	int i;
+
+	switch(order) {
+		case 0:
+			memcpy(residual, data, sizeof(residual[0])*data_len);
+			break;
+		case 1:
+			for(i = 0; i < idata_len; i++)
+				residual[i] = data[i] - data[i-1];
+			break;
+		case 2:
+			for(i = 0; i < idata_len; i++)
+				residual[i] = data[i] - 2*data[i-1] + data[i-2];
+			break;
+		case 3:
+			for(i = 0; i < idata_len; i++)
+				residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3];
+			break;
+		case 4:
+			for(i = 0; i < idata_len; i++)
+				residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4];
+			break;
+		default: break;
+	}
+}
+
+void FLAC__fixed_restore_signal(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[])
+{
+	int i, idata_len = (int)data_len;
+
+	switch(order) {
+		case 0:
+			memcpy(data, residual, sizeof(residual[0])*data_len);
+			break;
+		case 1:
+			for(i = 0; i < idata_len; i++)
+				data[i] = residual[i] + data[i-1];
+			break;
+		case 2:
+			for(i = 0; i < idata_len; i++)
+				data[i] = residual[i] + 2*data[i-1] - data[i-2];
+			break;
+		case 3:
+			for(i = 0; i < idata_len; i++)
+				data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3];
+			break;
+		case 4:
+			for(i = 0; i < idata_len; i++)
+				data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4];
+			break;
+		default: break;
+	}
+}
--- /dev/null
+++ b/src/libflac/format.c
@@ -1,0 +1,566 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h> /* for qsort() */
+#include <string.h> /* for memset() */
+#include "FLAC/format.h"
+#include "share/alloc.h"
+#include "share/compat.h"
+#include "private/format.h"
+#include "private/macros.h"
+
+/* PACKAGE_VERSION should come from configure */
+FLAC_API const char *FLAC__VERSION_STRING = "1.3.3";
+
+FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " "1.3.3" " 20190804";
+
+FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' };
+FLAC_API const uint32_t FLAC__STREAM_SYNC = 0x664C6143;
+FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN = 32; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */
+
+FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff);
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */
+
+FLAC_API const uint32_t FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */
+FLAC_API const uint32_t FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */
+
+FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC = 0x3ffe;
+FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */
+FLAC_API const uint32_t FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */
+
+FLAC_API const uint32_t FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */
+
+FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */
+FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */
+FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */
+FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */
+FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */
+
+FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)-1 */
+FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER = 31; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN)-1 */
+
+FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[] = {
+	"PARTITIONED_RICE",
+	"PARTITIONED_RICE2"
+};
+
+FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN = 4; /* bits */
+FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN = 5; /* bits */
+
+FLAC_API const uint32_t FLAC__SUBFRAME_ZERO_PAD_LEN = 1; /* bits */
+FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LEN = 6; /* bits */
+FLAC_API const uint32_t FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN = 1; /* bits */
+
+FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK = 0x00;
+FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK = 0x02;
+FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK = 0x10;
+FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK = 0x40;
+
+FLAC_API const char * const FLAC__SubframeTypeString[] = {
+	"CONSTANT",
+	"VERBATIM",
+	"FIXED",
+	"LPC"
+};
+
+FLAC_API const char * const FLAC__ChannelAssignmentString[] = {
+	"INDEPENDENT",
+	"LEFT_SIDE",
+	"RIGHT_SIDE",
+	"MID_SIDE"
+};
+
+FLAC_API const char * const FLAC__FrameNumberTypeString[] = {
+	"FRAME_NUMBER_TYPE_FRAME_NUMBER",
+	"FRAME_NUMBER_TYPE_SAMPLE_NUMBER"
+};
+
+FLAC_API const char * const FLAC__MetadataTypeString[] = {
+	"STREAMINFO",
+	"PADDING",
+	"APPLICATION",
+	"SEEKTABLE",
+	"VORBIS_COMMENT",
+	"CUESHEET",
+	"PICTURE"
+};
+
+FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[] = {
+	"Other",
+	"32x32 pixels 'file icon' (PNG only)",
+	"Other file icon",
+	"Cover (front)",
+	"Cover (back)",
+	"Leaflet page",
+	"Media (e.g. label side of CD)",
+	"Lead artist/lead performer/soloist",
+	"Artist/performer",
+	"Conductor",
+	"Band/Orchestra",
+	"Composer",
+	"Lyricist/text writer",
+	"Recording Location",
+	"During recording",
+	"During performance",
+	"Movie/video screen capture",
+	"A bright coloured fish",
+	"Illustration",
+	"Band/artist logotype",
+	"Publisher/Studio logotype"
+};
+
+FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(uint32_t sample_rate)
+{
+	if(sample_rate == 0 || sample_rate > FLAC__MAX_SAMPLE_RATE) {
+		return false;
+	}
+	else
+		return true;
+}
+
+FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(uint32_t blocksize, uint32_t sample_rate)
+{
+	if(blocksize > 16384)
+		return false;
+	else if(sample_rate <= 48000 && blocksize > 4608)
+		return false;
+	else
+		return true;
+}
+
+FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(uint32_t sample_rate)
+{
+	if(
+		!FLAC__format_sample_rate_is_valid(sample_rate) ||
+		(
+			sample_rate >= (1u << 16) &&
+			!(sample_rate % 1000 == 0 || sample_rate % 10 == 0)
+		)
+	) {
+		return false;
+	}
+	else
+		return true;
+}
+
+/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
+FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table)
+{
+	uint32_t i;
+	FLAC__uint64 prev_sample_number = 0;
+	FLAC__bool got_prev = false;
+
+	for(i = 0; i < seek_table->num_points; i++) {
+		if(got_prev) {
+			if(
+				seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER &&
+				seek_table->points[i].sample_number <= prev_sample_number
+			)
+				return false;
+		}
+		prev_sample_number = seek_table->points[i].sample_number;
+		got_prev = true;
+	}
+
+	return true;
+}
+
+/* used as the sort predicate for qsort() */
+static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r)
+{
+	/* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */
+	if(l->sample_number == r->sample_number)
+		return 0;
+	else if(l->sample_number < r->sample_number)
+		return -1;
+	else
+		return 1;
+}
+
+/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
+FLAC_API uint32_t FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table)
+{
+	uint32_t i, j;
+	FLAC__bool first;
+
+	if (seek_table->num_points == 0)
+		return 0;
+
+	/* sort the seekpoints */
+	qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_);
+
+	/* uniquify the seekpoints */
+	first = true;
+	for(i = j = 0; i < seek_table->num_points; i++) {
+		if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) {
+			if(!first) {
+				if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number)
+					continue;
+			}
+		}
+		first = false;
+		seek_table->points[j++] = seek_table->points[i];
+	}
+
+	for(i = j; i < seek_table->num_points; i++) {
+		seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
+		seek_table->points[i].stream_offset = 0;
+		seek_table->points[i].frame_samples = 0;
+	}
+
+	return j;
+}
+
+/*
+ * also disallows non-shortest-form encodings, c.f.
+ *   http://www.unicode.org/versions/corrigendum1.html
+ * and a more clear explanation at the end of this section:
+ *   http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ */
+static uint32_t utf8len_(const FLAC__byte *utf8)
+{
+	if ((utf8[0] & 0x80) == 0) {
+		return 1;
+	}
+	else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) {
+		if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */
+			return 0;
+		return 2;
+	}
+	else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) {
+		if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */
+			return 0;
+		/* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */
+		if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */
+			return 0;
+		if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */
+			return 0;
+		return 3;
+	}
+	else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) {
+		if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */
+			return 0;
+		return 4;
+	}
+	else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) {
+		if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */
+			return 0;
+		return 5;
+	}
+	else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) {
+		if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */
+			return 0;
+		return 6;
+	}
+	else {
+		return 0;
+	}
+}
+
+FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name)
+{
+	char c;
+	for(c = *name; c; c = *(++name))
+		if(c < 0x20 || c == 0x3d || c > 0x7d)
+			return false;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, uint32_t length)
+{
+	if(length == (uint32_t)(-1)) {
+		while(*value) {
+			uint32_t n = utf8len_(value);
+			if(n == 0)
+				return false;
+			value += n;
+		}
+	}
+	else {
+		const FLAC__byte *end = value + length;
+		while(value < end) {
+			uint32_t n = utf8len_(value);
+			if(n == 0)
+				return false;
+			value += n;
+		}
+		if(value != end)
+			return false;
+	}
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, uint32_t length)
+{
+	const FLAC__byte *s, *end;
+
+	for(s = entry, end = s + length; s < end && *s != '='; s++) {
+		if(*s < 0x20 || *s > 0x7D)
+			return false;
+	}
+	if(s == end)
+		return false;
+
+	s++; /* skip '=' */
+
+	while(s < end) {
+		uint32_t n = utf8len_(s);
+		if(n == 0)
+			return false;
+		s += n;
+	}
+	if(s != end)
+		return false;
+
+	return true;
+}
+
+/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
+FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation)
+{
+	uint32_t i, j;
+
+	if(check_cd_da_subset) {
+		if(cue_sheet->lead_in < 2 * 44100) {
+			if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds";
+			return false;
+		}
+		if(cue_sheet->lead_in % 588 != 0) {
+			if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples";
+			return false;
+		}
+	}
+
+	if(cue_sheet->num_tracks == 0) {
+		if(violation) *violation = "cue sheet must have at least one track (the lead-out)";
+		return false;
+	}
+
+	if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) {
+		if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)";
+		return false;
+	}
+
+	for(i = 0; i < cue_sheet->num_tracks; i++) {
+		if(cue_sheet->tracks[i].number == 0) {
+			if(violation) *violation = "cue sheet may not have a track number 0";
+			return false;
+		}
+
+		if(check_cd_da_subset) {
+			if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) {
+				if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170";
+				return false;
+			}
+		}
+
+		if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) {
+			if(violation) {
+				if(i == cue_sheet->num_tracks-1) /* the lead-out track... */
+					*violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples";
+				else
+					*violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples";
+			}
+			return false;
+		}
+
+		if(i < cue_sheet->num_tracks - 1) {
+			if(cue_sheet->tracks[i].num_indices == 0) {
+				if(violation) *violation = "cue sheet track must have at least one index point";
+				return false;
+			}
+
+			if(cue_sheet->tracks[i].indices[0].number > 1) {
+				if(violation) *violation = "cue sheet track's first index number must be 0 or 1";
+				return false;
+			}
+		}
+
+		for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) {
+			if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) {
+				if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples";
+				return false;
+			}
+
+			if(j > 0) {
+				if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) {
+					if(violation) *violation = "cue sheet track index numbers must increase by 1";
+					return false;
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
+FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation)
+{
+	char *p;
+	FLAC__byte *b;
+
+	for(p = picture->mime_type; *p; p++) {
+		if(*p < 0x20 || *p > 0x7e) {
+			if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)";
+			return false;
+		}
+	}
+
+	for(b = picture->description; *b; ) {
+		uint32_t n = utf8len_(b);
+		if(n == 0) {
+			if(violation) *violation = "description string must be valid UTF-8";
+			return false;
+		}
+		b += n;
+	}
+
+	return true;
+}
+
+/*
+ * These routines are private to libFLAC
+ */
+uint32_t FLAC__format_get_max_rice_partition_order(uint32_t blocksize, uint32_t predictor_order)
+{
+	return
+		FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(
+			FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize),
+			blocksize,
+			predictor_order
+		);
+}
+
+uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize(uint32_t blocksize)
+{
+	uint32_t max_rice_partition_order = 0;
+	while(!(blocksize & 1)) {
+		max_rice_partition_order++;
+		blocksize >>= 1;
+	}
+	return flac_min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order);
+}
+
+uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(uint32_t limit, uint32_t blocksize, uint32_t predictor_order)
+{
+	uint32_t max_rice_partition_order = limit;
+
+	while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order)
+		max_rice_partition_order--;
+
+	return max_rice_partition_order;
+}
+
+void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object)
+{
+	object->parameters = 0;
+	object->raw_bits = 0;
+	object->capacity_by_order = 0;
+}
+
+void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object)
+{
+	if(0 != object->parameters)
+		free(object->parameters);
+	if(0 != object->raw_bits)
+		free(object->raw_bits);
+	FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object);
+}
+
+FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, uint32_t max_partition_order)
+{
+	if(object->capacity_by_order < max_partition_order) {
+		if(0 == (object->parameters = safe_realloc_(object->parameters, sizeof(uint32_t)*(1ULL << max_partition_order))))
+			return false;
+		if(0 == (object->raw_bits = safe_realloc_(object->raw_bits, sizeof(uint32_t)*(1ULL << max_partition_order))))
+			return false;
+		memset(object->raw_bits, 0, sizeof(uint32_t)*(1ULL << max_partition_order));
+		object->capacity_by_order = max_partition_order;
+	}
+
+	return true;
+}
--- /dev/null
+++ b/src/libflac/lpc.c
@@ -1,0 +1,1233 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include "FLAC/format.h"
+#include "share/compat.h"
+#include "private/bitmath.h"
+#include "private/lpc.h"
+#include "private/macros.h"
+
+/* OPT: #undef'ing this may improve the speed on some architectures */
+#define FLAC__LPC_UNROLLED_FILTER_LOOPS
+
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+#include <float.h>
+static inline long int lround(double x) {
+	return (long)(x + _copysign(0.5, x));
+}
+#endif
+
+void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len)
+{
+	uint32_t i;
+	for(i = 0; i < data_len; i++)
+		out[i] = in[i] * window[i];
+}
+
+void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[])
+{
+	/*
+	 * this version tends to run faster because of better data locality
+	 * ('data_len' is usually much larger than 'lag')
+	 */
+	FLAC__real d;
+	uint32_t sample, coeff;
+	const uint32_t limit = data_len - lag;
+
+	for(coeff = 0; coeff < lag; coeff++)
+		autoc[coeff] = 0.0;
+	for(sample = 0; sample <= limit; sample++) {
+		d = data[sample];
+		for(coeff = 0; coeff < lag; coeff++)
+			autoc[coeff] += d * data[sample+coeff];
+	}
+	for(; sample < data_len; sample++) {
+		d = data[sample];
+		for(coeff = 0; coeff < data_len - sample; coeff++)
+			autoc[coeff] += d * data[sample+coeff];
+	}
+}
+
+void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], uint32_t *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], double error[])
+{
+	uint32_t i, j;
+	double r, err, lpc[FLAC__MAX_LPC_ORDER];
+
+	err = autoc[0];
+
+	for(i = 0; i < *max_order; i++) {
+		/* Sum up this iteration's reflection coefficient. */
+		r = -autoc[i+1];
+		for(j = 0; j < i; j++)
+			r -= lpc[j] * autoc[i-j];
+		r /= err;
+
+		/* Update LPC coefficients and total error. */
+		lpc[i]=r;
+		for(j = 0; j < (i>>1); j++) {
+			double tmp = lpc[j];
+			lpc[j] += r * lpc[i-1-j];
+			lpc[i-1-j] += r * tmp;
+		}
+		if(i & 1)
+			lpc[j] += lpc[j] * r;
+
+		err *= (1.0 - r * r);
+
+		/* save this order */
+		for(j = 0; j <= i; j++)
+			lp_coeff[i][j] = (FLAC__real)(-lpc[j]); /* negate FIR filter coeff to get predictor coeff */
+		error[i] = err;
+
+		/* see SF bug https://sourceforge.net/p/flac/bugs/234/ */
+		if(err == 0.0) {
+			*max_order = i+1;
+			return;
+		}
+	}
+}
+
+int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], uint32_t order, uint32_t precision, FLAC__int32 qlp_coeff[], int *shift)
+{
+	uint32_t i;
+	double cmax;
+	FLAC__int32 qmax, qmin;
+
+	/* drop one bit for the sign; from here on out we consider only |lp_coeff[i]| */
+	precision--;
+	qmax = 1 << precision;
+	qmin = -qmax;
+	qmax--;
+
+	/* calc cmax = max( |lp_coeff[i]| ) */
+	cmax = 0.0;
+	for(i = 0; i < order; i++) {
+		const double d = fabs(lp_coeff[i]);
+		if(d > cmax)
+			cmax = d;
+	}
+
+	if(cmax <= 0.0) {
+		/* => coefficients are all 0, which means our constant-detect didn't work */
+		return 2;
+	}
+	else {
+		const int max_shiftlimit = (1 << (FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN-1)) - 1;
+		const int min_shiftlimit = -max_shiftlimit - 1;
+		int log2cmax;
+
+		(void)frexp(cmax, &log2cmax);
+		log2cmax--;
+		*shift = (int)precision - log2cmax - 1;
+
+		if(*shift > max_shiftlimit)
+			*shift = max_shiftlimit;
+		else if(*shift < min_shiftlimit)
+			return 1;
+	}
+
+	if(*shift >= 0) {
+		double error = 0.0;
+		FLAC__int32 q;
+		for(i = 0; i < order; i++) {
+			error += lp_coeff[i] * (1 << *shift);
+			q = lround(error);
+
+			if(q > qmax)
+				q = qmax;
+			else if(q < qmin)
+				q = qmin;
+			error -= q;
+			qlp_coeff[i] = q;
+		}
+	}
+	/* negative shift is very rare but due to design flaw, negative shift is
+	 * not allowed in the decoder, so it must be handled specially by scaling
+	 * down coeffs
+	 */
+	else {
+		const int nshift = -(*shift);
+		double error = 0.0;
+		FLAC__int32 q;
+
+		for(i = 0; i < order; i++) {
+			error += lp_coeff[i] / (1 << nshift);
+			q = lround(error);
+			if(q > qmax)
+				q = qmax;
+			else if(q < qmin)
+				q = qmin;
+			error -= q;
+			qlp_coeff[i] = q;
+		}
+		*shift = 0;
+	}
+
+	return 0;
+}
+
+#if defined(_MSC_VER)
+// silence MSVC warnings about __restrict modifier
+#pragma warning ( disable : 4028 )
+#endif
+
+void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual)
+#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS)
+{
+	FLAC__int64 sumo;
+	uint32_t i, j;
+	FLAC__int32 sum;
+	const FLAC__int32 *history;
+
+	FLAC__ASSERT(order > 0);
+
+	for(i = 0; i < data_len; i++) {
+		sumo = 0;
+		sum = 0;
+		history = data;
+		for(j = 0; j < order; j++) {
+			sum += qlp_coeff[j] * (*(--history));
+			sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history);
+			if(sumo > 2147483647ll || sumo < -2147483648ll)
+				fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo);
+		}
+		*(residual++) = *(data++) - (sum >> lp_quantization);
+	}
+}
+#else /* fully unrolled version for normal use */
+{
+	int i;
+	FLAC__int32 sum;
+
+	/*
+	 * We do unique versions up to 12th order since that's the subset limit.
+	 * Also they are roughly ordered to match frequency of occurrence to
+	 * minimize branching.
+	 */
+	if(order <= 12) {
+		if(order > 8) {
+			if(order > 10) {
+				if(order == 12) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[11] * data[i-12];
+						sum += qlp_coeff[10] * data[i-11];
+						sum += qlp_coeff[9] * data[i-10];
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 11 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[10] * data[i-11];
+						sum += qlp_coeff[9] * data[i-10];
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 10) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[9] * data[i-10];
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 9 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else if(order > 4) {
+			if(order > 6) {
+				if(order == 8) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 7 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 6) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 5 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else {
+			if(order > 2) {
+				if(order == 4) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 3 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 2) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						residual[i] = data[i] - (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 1 */
+					for(i = 0; i < (int)data_len; i++)
+						residual[i] = data[i] - ((qlp_coeff[0] * data[i-1]) >> lp_quantization);
+				}
+			}
+		}
+	}
+	else { /* order > 12 */
+		for(i = 0; i < (int)data_len; i++) {
+			sum = 0;
+			switch(order) {
+				default:
+				case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */
+				case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */
+				case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */
+				case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */
+				case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */
+				case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */
+				case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */
+				case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */
+				case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */
+				case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */
+				case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */
+				case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */
+				case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */
+				case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */
+				case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */
+				case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */
+				case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */
+				case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */
+				case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */
+				case 13: sum += qlp_coeff[12] * data[i-13];
+				         sum += qlp_coeff[11] * data[i-12];
+				         sum += qlp_coeff[10] * data[i-11];
+				         sum += qlp_coeff[ 9] * data[i-10];
+				         sum += qlp_coeff[ 8] * data[i- 9];
+				         sum += qlp_coeff[ 7] * data[i- 8];
+				         sum += qlp_coeff[ 6] * data[i- 7];
+				         sum += qlp_coeff[ 5] * data[i- 6];
+				         sum += qlp_coeff[ 4] * data[i- 5];
+				         sum += qlp_coeff[ 3] * data[i- 4];
+				         sum += qlp_coeff[ 2] * data[i- 3];
+				         sum += qlp_coeff[ 1] * data[i- 2];
+				         sum += qlp_coeff[ 0] * data[i- 1];
+			}
+			residual[i] = data[i] - (sum >> lp_quantization);
+		}
+	}
+}
+#endif
+
+void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual)
+#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS)
+{
+	uint32_t i, j;
+	FLAC__int64 sum;
+	const FLAC__int32 *history;
+
+	FLAC__ASSERT(order > 0);
+
+	for(i = 0; i < data_len; i++) {
+		sum = 0;
+		history = data;
+		for(j = 0; j < order; j++)
+			sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history));
+		if(FLAC__bitmath_silog2(sum >> lp_quantization) > 32) {
+			fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization));
+			break;
+		}
+		if(FLAC__bitmath_silog2((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) {
+			fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%" PRId64 ", residual=%" PRId64 "\n", i, *data, (int64_t)(sum >> lp_quantization), ((FLAC__int64)(*data) - (sum >> lp_quantization)));
+			break;
+		}
+		*(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization);
+	}
+}
+#else /* fully unrolled version for normal use */
+{
+	int i;
+	FLAC__int64 sum;
+
+	/*
+	 * We do unique versions up to 12th order since that's the subset limit.
+	 * Also they are roughly ordered to match frequency of occurrence to
+	 * minimize branching.
+	 */
+	if(order <= 12) {
+		if(order > 8) {
+			if(order > 10) {
+				if(order == 12) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[11] * (FLAC__int64)data[i-12];
+						sum += qlp_coeff[10] * (FLAC__int64)data[i-11];
+						sum += qlp_coeff[9] * (FLAC__int64)data[i-10];
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 11 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[10] * (FLAC__int64)data[i-11];
+						sum += qlp_coeff[9] * (FLAC__int64)data[i-10];
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 10) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[9] * (FLAC__int64)data[i-10];
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 9 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else if(order > 4) {
+			if(order > 6) {
+				if(order == 8) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 7 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 6) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 5 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else {
+			if(order > 2) {
+				if(order == 4) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 3 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 2) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 1 */
+					for(i = 0; i < (int)data_len; i++)
+						residual[i] = data[i] - (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization);
+				}
+			}
+		}
+	}
+	else { /* order > 12 */
+		for(i = 0; i < (int)data_len; i++) {
+			sum = 0;
+			switch(order) {
+				default:
+				case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; /* Falls through. */
+				case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; /* Falls through. */
+				case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; /* Falls through. */
+				case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; /* Falls through. */
+				case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; /* Falls through. */
+				case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; /* Falls through. */
+				case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; /* Falls through. */
+				case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; /* Falls through. */
+				case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; /* Falls through. */
+				case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; /* Falls through. */
+				case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; /* Falls through. */
+				case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; /* Falls through. */
+				case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; /* Falls through. */
+				case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; /* Falls through. */
+				case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; /* Falls through. */
+				case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; /* Falls through. */
+				case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; /* Falls through. */
+				case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; /* Falls through. */
+				case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; /* Falls through. */
+				case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13];
+				         sum += qlp_coeff[11] * (FLAC__int64)data[i-12];
+				         sum += qlp_coeff[10] * (FLAC__int64)data[i-11];
+				         sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10];
+				         sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9];
+				         sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8];
+				         sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7];
+				         sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6];
+				         sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5];
+				         sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4];
+				         sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3];
+				         sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2];
+				         sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1];
+			}
+			residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization);
+		}
+	}
+}
+#endif
+
+void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict data)
+#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS)
+{
+	FLAC__int64 sumo;
+	uint32_t i, j;
+	FLAC__int32 sum;
+	const FLAC__int32 *r = residual, *history;
+
+	FLAC__ASSERT(order > 0);
+
+	for(i = 0; i < data_len; i++) {
+		sumo = 0;
+		sum = 0;
+		history = data;
+		for(j = 0; j < order; j++) {
+			sum += qlp_coeff[j] * (*(--history));
+			sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history);
+			if(sumo > 2147483647ll || sumo < -2147483648ll)
+				fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo);
+		}
+		*(data++) = *(r++) + (sum >> lp_quantization);
+	}
+}
+#else /* fully unrolled version for normal use */
+{
+	int i;
+	FLAC__int32 sum;
+
+	/*
+	 * We do unique versions up to 12th order since that's the subset limit.
+	 * Also they are roughly ordered to match frequency of occurrence to
+	 * minimize branching.
+	 */
+	if(order <= 12) {
+		if(order > 8) {
+			if(order > 10) {
+				if(order == 12) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[11] * data[i-12];
+						sum += qlp_coeff[10] * data[i-11];
+						sum += qlp_coeff[9] * data[i-10];
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 11 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[10] * data[i-11];
+						sum += qlp_coeff[9] * data[i-10];
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 10) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[9] * data[i-10];
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 9 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[8] * data[i-9];
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else if(order > 4) {
+			if(order > 6) {
+				if(order == 8) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[7] * data[i-8];
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 7 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[6] * data[i-7];
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 6) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[5] * data[i-6];
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 5 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[4] * data[i-5];
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else {
+			if(order > 2) {
+				if(order == 4) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[3] * data[i-4];
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 3 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[2] * data[i-3];
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 2) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[1] * data[i-2];
+						sum += qlp_coeff[0] * data[i-1];
+						data[i] = residual[i] + (sum >> lp_quantization);
+					}
+				}
+				else { /* order == 1 */
+					for(i = 0; i < (int)data_len; i++)
+						data[i] = residual[i] + ((qlp_coeff[0] * data[i-1]) >> lp_quantization);
+				}
+			}
+		}
+	}
+	else { /* order > 12 */
+		for(i = 0; i < (int)data_len; i++) {
+			sum = 0;
+			switch(order) {
+				default:
+				case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */
+				case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */
+				case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */
+				case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */
+				case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */
+				case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */
+				case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */
+				case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */
+				case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */
+				case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */
+				case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */
+				case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */
+				case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */
+				case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */
+				case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */
+				case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */
+				case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */
+				case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */
+				case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */
+				case 13: sum += qlp_coeff[12] * data[i-13];
+				         sum += qlp_coeff[11] * data[i-12];
+				         sum += qlp_coeff[10] * data[i-11];
+				         sum += qlp_coeff[ 9] * data[i-10];
+				         sum += qlp_coeff[ 8] * data[i- 9];
+				         sum += qlp_coeff[ 7] * data[i- 8];
+				         sum += qlp_coeff[ 6] * data[i- 7];
+				         sum += qlp_coeff[ 5] * data[i- 6];
+				         sum += qlp_coeff[ 4] * data[i- 5];
+				         sum += qlp_coeff[ 3] * data[i- 4];
+				         sum += qlp_coeff[ 2] * data[i- 3];
+				         sum += qlp_coeff[ 1] * data[i- 2];
+				         sum += qlp_coeff[ 0] * data[i- 1];
+			}
+			data[i] = residual[i] + (sum >> lp_quantization);
+		}
+	}
+}
+#endif
+
+void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict data)
+#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS)
+{
+	uint32_t i, j;
+	FLAC__int64 sum;
+	const FLAC__int32 *r = residual, *history;
+
+	FLAC__ASSERT(order > 0);
+
+	for(i = 0; i < data_len; i++) {
+		sum = 0;
+		history = data;
+		for(j = 0; j < order; j++)
+			sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history));
+		if(FLAC__bitmath_silog2(sum >> lp_quantization) > 32) {
+			fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization));
+			break;
+		}
+		if(FLAC__bitmath_silog2((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) {
+			fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%" PRId64 ", data=%" PRId64 "\n", i, *r, (sum >> lp_quantization), ((FLAC__int64)(*r) + (sum >> lp_quantization)));
+			break;
+		}
+		*(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization);
+	}
+}
+#else /* fully unrolled version for normal use */
+{
+	int i;
+	FLAC__int64 sum;
+
+	/*
+	 * We do unique versions up to 12th order since that's the subset limit.
+	 * Also they are roughly ordered to match frequency of occurrence to
+	 * minimize branching.
+	 */
+	if(order <= 12) {
+		if(order > 8) {
+			if(order > 10) {
+				if(order == 12) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[11] * (FLAC__int64)data[i-12];
+						sum += qlp_coeff[10] * (FLAC__int64)data[i-11];
+						sum += qlp_coeff[9] * (FLAC__int64)data[i-10];
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 11 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[10] * (FLAC__int64)data[i-11];
+						sum += qlp_coeff[9] * (FLAC__int64)data[i-10];
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 10) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[9] * (FLAC__int64)data[i-10];
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 9 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[8] * (FLAC__int64)data[i-9];
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else if(order > 4) {
+			if(order > 6) {
+				if(order == 8) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[7] * (FLAC__int64)data[i-8];
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 7 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[6] * (FLAC__int64)data[i-7];
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 6) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[5] * (FLAC__int64)data[i-6];
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 5 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[4] * (FLAC__int64)data[i-5];
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+		}
+		else {
+			if(order > 2) {
+				if(order == 4) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[3] * (FLAC__int64)data[i-4];
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 3 */
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[2] * (FLAC__int64)data[i-3];
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+			}
+			else {
+				if(order == 2) {
+					for(i = 0; i < (int)data_len; i++) {
+						sum = 0;
+						sum += qlp_coeff[1] * (FLAC__int64)data[i-2];
+						sum += qlp_coeff[0] * (FLAC__int64)data[i-1];
+						data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+					}
+				}
+				else { /* order == 1 */
+					for(i = 0; i < (int)data_len; i++)
+						data[i] = residual[i] + (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization);
+				}
+			}
+		}
+	}
+	else { /* order > 12 */
+		for(i = 0; i < (int)data_len; i++) {
+			sum = 0;
+			switch(order) {
+				default:
+				case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; /* Falls through. */
+				case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; /* Falls through. */
+				case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; /* Falls through. */
+				case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; /* Falls through. */
+				case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; /* Falls through. */
+				case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; /* Falls through. */
+				case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; /* Falls through. */
+				case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; /* Falls through. */
+				case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; /* Falls through. */
+				case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; /* Falls through. */
+				case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; /* Falls through. */
+				case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; /* Falls through. */
+				case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; /* Falls through. */
+				case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; /* Falls through. */
+				case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; /* Falls through. */
+				case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; /* Falls through. */
+				case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; /* Falls through. */
+				case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; /* Falls through. */
+				case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; /* Falls through. */
+				case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13];
+				         sum += qlp_coeff[11] * (FLAC__int64)data[i-12];
+				         sum += qlp_coeff[10] * (FLAC__int64)data[i-11];
+				         sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10];
+				         sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9];
+				         sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8];
+				         sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7];
+				         sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6];
+				         sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5];
+				         sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4];
+				         sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3];
+				         sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2];
+				         sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1];
+			}
+			data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization);
+		}
+	}
+}
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning ( default : 4028 )
+#endif
+
+double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, uint32_t total_samples)
+{
+	double error_scale;
+
+	error_scale = 0.5 / (double)total_samples;
+
+	return FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error, error_scale);
+}
+
+double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale)
+{
+	if(lpc_error > 0.0) {
+		double bps = (double)0.5 * log(error_scale * lpc_error) / M_LN2;
+		if(bps >= 0.0)
+			return bps;
+		else
+			return 0.0;
+	}
+	else if(lpc_error < 0.0) { /* error should not be negative but can happen due to inadequate floating-point resolution */
+		return 1e32;
+	}
+	else {
+		return 0.0;
+	}
+}
+
+uint32_t FLAC__lpc_compute_best_order(const double lpc_error[], uint32_t max_order, uint32_t total_samples, uint32_t overhead_bits_per_order)
+{
+	uint32_t order, indx, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */
+	double bits, best_bits, error_scale;
+
+	error_scale = 0.5 / (double)total_samples;
+
+	best_index = 0;
+	best_bits = (uint32_t)(-1);
+
+	for(indx = 0, order = 1; indx < max_order; indx++, order++) {
+		bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[indx], error_scale) * (double)(total_samples - order) + (double)(order * overhead_bits_per_order);
+		if(bits < best_bits) {
+			best_index = indx;
+			best_bits = bits;
+		}
+	}
+
+	return best_index+1; /* +1 since indx of lpc_error[] is order-1 */
+}
--- /dev/null
+++ b/src/libflac/md5.c
@@ -1,0 +1,479 @@
+#include <stdlib.h>		/* for malloc() */
+#include <string.h>		/* for memcpy() */
+
+#include "private/md5.h"
+#include "share/alloc.h"
+#include "share/compat.h"
+#include "share/endswap.h"
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ * definitions; now uses stuff from dpkg's config.h.
+ *  - Ian Jackson <[email protected]>.
+ * Still in the public domain.
+ *
+ * Josh Coalson: made some changes to integrate with libFLAC.
+ * Still in the public domain.
+ */
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+	 (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16])
+{
+	register FLAC__uint32 a, b, c, d;
+
+	a = buf[0];
+	b = buf[1];
+	c = buf[2];
+	d = buf[3];
+
+	MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+	MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+	MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+	MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+	MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+	MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+	MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+	MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+	MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+	MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+	MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+	MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+	MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+	MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+	MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+	MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+	MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+	MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+	MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+	MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+	MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+	MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+	MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+	MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+	MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+	MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+	MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+	MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+	MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+	MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+	MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+	MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+	MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+	MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+	MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+	MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+	MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+	MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+	MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+	MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+	MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+	MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+	MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+	MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+	MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+	MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+	MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+	MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+	MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+	MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+	MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+	MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+	MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+	MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+	MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+	MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+	MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+	MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+	MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+	MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+	MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+	MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+	MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+	MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+	buf[0] += a;
+	buf[1] += b;
+	buf[2] += c;
+	buf[3] += d;
+}
+
+#define byteSwap(buf, words)
+#define byteSwapX16(buf)
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, uint32_t len)
+{
+	FLAC__uint32 t;
+
+	/* Update byte count */
+
+	t = ctx->bytes[0];
+	if ((ctx->bytes[0] = t + len) < t)
+		ctx->bytes[1]++;	/* Carry from low to high */
+
+	t = 64 - (t & 0x3f);	/* Space available in ctx->in (at least 1) */
+	if (t > len) {
+		memcpy((FLAC__byte *)ctx->in + 64 - t, buf, len);
+		return;
+	}
+	/* First chunk is an odd size */
+	memcpy((FLAC__byte *)ctx->in + 64 - t, buf, t);
+	byteSwapX16(ctx->in);
+	FLAC__MD5Transform(ctx->buf, ctx->in);
+	buf += t;
+	len -= t;
+
+	/* Process data in 64-byte chunks */
+	while (len >= 64) {
+		memcpy(ctx->in, buf, 64);
+		byteSwapX16(ctx->in);
+		FLAC__MD5Transform(ctx->buf, ctx->in);
+		buf += 64;
+		len -= 64;
+	}
+
+	/* Handle any remaining bytes of data. */
+	memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void FLAC__MD5Init(FLAC__MD5Context *ctx)
+{
+	ctx->buf[0] = 0x67452301;
+	ctx->buf[1] = 0xefcdab89;
+	ctx->buf[2] = 0x98badcfe;
+	ctx->buf[3] = 0x10325476;
+
+	ctx->bytes[0] = 0;
+	ctx->bytes[1] = 0;
+
+	ctx->internal_buf.p8 = 0;
+	ctx->capacity = 0;
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx)
+{
+	int count = ctx->bytes[0] & 0x3f;	/* Number of bytes in ctx->in */
+	FLAC__byte *p = (FLAC__byte *)ctx->in + count;
+
+	/* Set the first char of padding to 0x80.  There is always room. */
+	*p++ = 0x80;
+
+	/* Bytes of padding needed to make 56 bytes (-8..55) */
+	count = 56 - 1 - count;
+
+	if (count < 0) {	/* Padding forces an extra block */
+		memset(p, 0, count + 8);
+		byteSwapX16(ctx->in);
+		FLAC__MD5Transform(ctx->buf, ctx->in);
+		p = (FLAC__byte *)ctx->in;
+		count = 56;
+	}
+	memset(p, 0, count);
+	byteSwap(ctx->in, 14);
+
+	/* Append length in bits and transform */
+	ctx->in[14] = ctx->bytes[0] << 3;
+	ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+	FLAC__MD5Transform(ctx->buf, ctx->in);
+
+	byteSwap(ctx->buf, 4);
+	memcpy(digest, ctx->buf, 16);
+	if (0 != ctx->internal_buf.p8) {
+		free(ctx->internal_buf.p8);
+		ctx->internal_buf.p8 = 0;
+		ctx->capacity = 0;
+	}
+	memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
+}
+
+/*
+ * Convert the incoming audio signal to a byte stream
+ */
+static void format_input_(FLAC__multibyte *mbuf, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample)
+{
+	FLAC__byte *buf_ = mbuf->p8;
+	FLAC__int16 *buf16 = mbuf->p16;
+	FLAC__int32 *buf32 = mbuf->p32;
+	FLAC__int32 a_word;
+	uint32_t channel, sample;
+
+	/* Storage in the output buffer, buf, is little endian. */
+
+#define BYTES_CHANNEL_SELECTOR(bytes, channels)   (bytes * 100 + channels)
+
+	/* First do the most commonly used combinations. */
+	switch (BYTES_CHANNEL_SELECTOR (bytes_per_sample, channels)) {
+		/* One byte per sample. */
+		case (BYTES_CHANNEL_SELECTOR (1, 1)):
+			for (sample = 0; sample < samples; sample++)
+				*buf_++ = (FLAC__byte)signal[0][sample];
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (1, 2)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf_++ = (FLAC__byte)signal[0][sample];
+				*buf_++ = (FLAC__byte)signal[1][sample];
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (1, 4)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf_++ = (FLAC__byte)signal[0][sample];
+				*buf_++ = (FLAC__byte)signal[1][sample];
+				*buf_++ = (FLAC__byte)signal[2][sample];
+				*buf_++ = (FLAC__byte)signal[3][sample];
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (1, 6)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf_++ = (FLAC__byte)signal[0][sample];
+				*buf_++ = (FLAC__byte)signal[1][sample];
+				*buf_++ = (FLAC__byte)signal[2][sample];
+				*buf_++ = (FLAC__byte)signal[3][sample];
+				*buf_++ = (FLAC__byte)signal[4][sample];
+				*buf_++ = (FLAC__byte)signal[5][sample];
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (1, 8)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf_++ = (FLAC__byte)signal[0][sample];
+				*buf_++ = (FLAC__byte)signal[1][sample];
+				*buf_++ = (FLAC__byte)signal[2][sample];
+				*buf_++ = (FLAC__byte)signal[3][sample];
+				*buf_++ = (FLAC__byte)signal[4][sample];
+				*buf_++ = (FLAC__byte)signal[5][sample];
+				*buf_++ = (FLAC__byte)signal[6][sample];
+				*buf_++ = (FLAC__byte)signal[7][sample];
+			}
+			return;
+
+		/* Two bytes per sample. */
+		case (BYTES_CHANNEL_SELECTOR (2, 1)):
+			for (sample = 0; sample < samples; sample++)
+				*buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]);
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (2, 2)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]);
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (2, 4)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[2][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[3][sample]);
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (2, 6)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[2][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[3][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[4][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[5][sample]);
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (2, 8)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[2][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[3][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[4][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[5][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[6][sample]);
+				*buf16++ = (FLAC__int16)H2LE_16(signal[7][sample]);
+			}
+			return;
+
+		/* Three bytes per sample. */
+		case (BYTES_CHANNEL_SELECTOR (3, 1)):
+			for (sample = 0; sample < samples; sample++) {
+				a_word = signal[0][sample];
+				*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+				*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+				*buf_++ = (FLAC__byte)a_word;
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (3, 2)):
+			for (sample = 0; sample < samples; sample++) {
+				a_word = signal[0][sample];
+				*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+				*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+				*buf_++ = (FLAC__byte)a_word;
+				a_word = signal[1][sample];
+				*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+				*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+				*buf_++ = (FLAC__byte)a_word;
+			}
+			return;
+
+		/* Four bytes per sample. */
+		case (BYTES_CHANNEL_SELECTOR (4, 1)):
+			for (sample = 0; sample < samples; sample++)
+				*buf32++ = H2LE_32(signal[0][sample]);
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (4, 2)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf32++ = H2LE_32(signal[0][sample]);
+				*buf32++ = H2LE_32(signal[1][sample]);
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (4, 4)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf32++ = H2LE_32(signal[0][sample]);
+				*buf32++ = H2LE_32(signal[1][sample]);
+				*buf32++ = H2LE_32(signal[2][sample]);
+				*buf32++ = H2LE_32(signal[3][sample]);
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (4, 6)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf32++ = H2LE_32(signal[0][sample]);
+				*buf32++ = H2LE_32(signal[1][sample]);
+				*buf32++ = H2LE_32(signal[2][sample]);
+				*buf32++ = H2LE_32(signal[3][sample]);
+				*buf32++ = H2LE_32(signal[4][sample]);
+				*buf32++ = H2LE_32(signal[5][sample]);
+			}
+			return;
+
+		case (BYTES_CHANNEL_SELECTOR (4, 8)):
+			for (sample = 0; sample < samples; sample++) {
+				*buf32++ = H2LE_32(signal[0][sample]);
+				*buf32++ = H2LE_32(signal[1][sample]);
+				*buf32++ = H2LE_32(signal[2][sample]);
+				*buf32++ = H2LE_32(signal[3][sample]);
+				*buf32++ = H2LE_32(signal[4][sample]);
+				*buf32++ = H2LE_32(signal[5][sample]);
+				*buf32++ = H2LE_32(signal[6][sample]);
+				*buf32++ = H2LE_32(signal[7][sample]);
+			}
+			return;
+
+		default:
+			break;
+	}
+
+	/* General version. */
+	switch (bytes_per_sample) {
+		case 1:
+			for (sample = 0; sample < samples; sample++)
+				for (channel = 0; channel < channels; channel++)
+					*buf_++ = (FLAC__byte)signal[channel][sample];
+			return;
+
+		case 2:
+			for (sample = 0; sample < samples; sample++)
+				for (channel = 0; channel < channels; channel++)
+					*buf16++ = (FLAC__int16)H2LE_16(signal[channel][sample]);
+			return;
+
+		case 3:
+			for (sample = 0; sample < samples; sample++)
+				for (channel = 0; channel < channels; channel++) {
+					a_word = signal[channel][sample];
+					*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+					*buf_++ = (FLAC__byte)a_word; a_word >>= 8;
+					*buf_++ = (FLAC__byte)a_word;
+				}
+			return;
+
+		case 4:
+			for (sample = 0; sample < samples; sample++)
+				for (channel = 0; channel < channels; channel++)
+					*buf32++ = H2LE_32(signal[channel][sample]);
+			return;
+
+		default:
+			break;
+	}
+}
+
+/*
+ * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it.
+ */
+FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample)
+{
+	const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample;
+
+	/* overflow check */
+	if ((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample)
+		return false;
+	if ((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples)
+		return false;
+
+	if (ctx->capacity < bytes_needed) {
+		if (0 == (ctx->internal_buf.p8 = safe_realloc_(ctx->internal_buf.p8, bytes_needed))) {
+			if (0 == (ctx->internal_buf.p8 = safe_malloc_(bytes_needed))) {
+				ctx->capacity = 0;
+				return false;
+			}
+		}
+		ctx->capacity = bytes_needed;
+	}
+
+	format_input_(&ctx->internal_buf, signal, channels, samples, bytes_per_sample);
+
+	FLAC__MD5Update(ctx, ctx->internal_buf.p8, (uint32_t)bytes_needed);
+
+	return true;
+}
--- /dev/null
+++ b/src/libflac/memory.c
@@ -1,0 +1,175 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include "private/memory.h"
+#include "share/compat.h"
+#include "share/alloc.h"
+
+void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address)
+{
+	void *x;
+
+	x = safe_malloc_(bytes);
+	*aligned_address = x;
+
+	return x;
+}
+
+FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer)
+{
+	FLAC__int32 *pu; /* unaligned pointer */
+	union { /* union needed to comply with C99 pointer aliasing rules */
+		FLAC__int32 *pa; /* aligned pointer */
+		void        *pv; /* aligned pointer alias */
+	} u;
+
+	if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */
+		return false;
+
+	pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv);
+	if(0 == pu) {
+		return false;
+	}
+	else {
+		if(*unaligned_pointer != 0)
+			free(*unaligned_pointer);
+		*unaligned_pointer = pu;
+		*aligned_pointer = u.pa;
+		return true;
+	}
+}
+
+FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer)
+{
+	FLAC__uint32 *pu; /* unaligned pointer */
+	union { /* union needed to comply with C99 pointer aliasing rules */
+		FLAC__uint32 *pa; /* aligned pointer */
+		void         *pv; /* aligned pointer alias */
+	} u;
+
+	if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */
+		return false;
+
+	pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv);
+	if(0 == pu) {
+		return false;
+	}
+	else {
+		if(*unaligned_pointer != 0)
+			free(*unaligned_pointer);
+		*unaligned_pointer = pu;
+		*aligned_pointer = u.pa;
+		return true;
+	}
+}
+
+FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer)
+{
+	FLAC__uint64 *pu; /* unaligned pointer */
+	union { /* union needed to comply with C99 pointer aliasing rules */
+		FLAC__uint64 *pa; /* aligned pointer */
+		void         *pv; /* aligned pointer alias */
+	} u;
+
+	if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */
+		return false;
+
+	pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv);
+	if(0 == pu) {
+		return false;
+	}
+	else {
+		if(*unaligned_pointer != 0)
+			free(*unaligned_pointer);
+		*unaligned_pointer = pu;
+		*aligned_pointer = u.pa;
+		return true;
+	}
+}
+
+FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, uint32_t **unaligned_pointer, uint32_t **aligned_pointer)
+{
+	uint32_t *pu; /* unaligned pointer */
+	union { /* union needed to comply with C99 pointer aliasing rules */
+		uint32_t *pa; /* aligned pointer */
+		void     *pv; /* aligned pointer alias */
+	} u;
+
+	if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */
+		return false;
+
+	pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv);
+	if(0 == pu) {
+		return false;
+	}
+	else {
+		if(*unaligned_pointer != 0)
+			free(*unaligned_pointer);
+		*unaligned_pointer = pu;
+		*aligned_pointer = u.pa;
+		return true;
+	}
+}
+
+FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer)
+{
+	FLAC__real *pu; /* unaligned pointer */
+	union { /* union needed to comply with C99 pointer aliasing rules */
+		FLAC__real *pa; /* aligned pointer */
+		void       *pv; /* aligned pointer alias */
+	} u;
+
+	if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */
+		return false;
+
+	pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv);
+	if(0 == pu) {
+		return false;
+	}
+	else {
+		if(*unaligned_pointer != 0)
+			free(*unaligned_pointer);
+		*unaligned_pointer = pu;
+		*aligned_pointer = u.pa;
+		return true;
+	}
+}
+
+void *safe_malloc_mul_2op_p(size_t size1, size_t size2)
+{
+	if(!size1 || !size2)
+		return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */
+	if(size1 > SIZE_MAX / size2)
+		return 0;
+	return malloc(size1*size2);
+}
--- /dev/null
+++ b/src/libflac/metadata_iterators.c
@@ -1,0 +1,3147 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <sys/stat.h> /* for stat(), maybe chmod() */
+
+#include "private/metadata.h"
+
+#include "FLAC/stream_decoder.h"
+#include "share/alloc.h"
+#include "share/compat.h"
+#include "share/macros.h"
+#include "share/safe_str.h"
+#include "private/macros.h"
+#include "private/memory.h"
+
+// 8bb: hide POSIX warnings
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+
+/* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */
+#define safe_malloc_mul_2op_ safe_malloc_mul_2op_p
+
+/****************************************************************************
+ *
+ * Local function declarations
+ *
+ ***************************************************************************/
+
+static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes);
+static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes);
+static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, uint32_t bytes);
+static FLAC__uint32 unpack_uint32_(FLAC__byte *b, uint32_t bytes);
+static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, uint32_t bytes);
+static FLAC__uint64 unpack_uint64_(FLAC__byte *b, uint32_t bytes);
+
+static FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator);
+static FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block);
+static FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, uint32_t *length);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, uint32_t block_length);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, uint32_t block_length);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, uint32_t block_length);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, uint32_t max_length);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, uint32_t block_length);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block);
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, uint32_t block_length);
+
+static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block);
+static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block);
+static FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block);
+static FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block);
+static FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block);
+static FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, uint32_t block_length);
+static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, uint32_t block_length);
+static FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block);
+static FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block);
+static FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block);
+static FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block);
+static FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, uint32_t block_length);
+
+static FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block);
+static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, uint32_t padding_length, FLAC__bool padding_is_last);
+static FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append);
+
+static void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator);
+static FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator);
+
+static uint32_t seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb);
+static uint32_t seek_to_first_metadata_block_(FILE *f);
+
+static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append);
+static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup);
+
+static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status);
+static FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status);
+static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status);
+static FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status);
+
+static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status);
+static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status);
+static void cleanup_tempfile_(FILE **tempfile, char **tempfilename);
+
+static FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats);
+static void set_file_stats_(const char *filename, struct flac_stat_s *stats);
+
+static int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence);
+static FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle);
+
+static FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status);
+
+
+#ifdef FLAC__VALGRIND_TESTING
+static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+	size_t ret = fwrite(ptr, size, nmemb, stream);
+	if(!ferror(stream))
+		fflush(stream);
+	return ret;
+}
+#else
+#define local__fwrite fwrite
+#endif
+
+/****************************************************************************
+ *
+ * Level 0 implementation
+ *
+ ***************************************************************************/
+
+static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+
+typedef struct {
+	FLAC__bool got_error;
+	FLAC__StreamMetadata *object;
+} level0_client_data;
+
+static FLAC__StreamMetadata *get_one_metadata_block_(const char *filename, FLAC__MetadataType type)
+{
+	level0_client_data cd;
+	FLAC__StreamDecoder *decoder;
+
+	cd.got_error = false;
+	cd.object = 0;
+
+	decoder = FLAC__stream_decoder_new();
+
+	if(0 == decoder)
+		return 0;
+
+	FLAC__stream_decoder_set_md5_checking(decoder, false);
+	FLAC__stream_decoder_set_metadata_ignore_all(decoder);
+	FLAC__stream_decoder_set_metadata_respond(decoder, type);
+
+	if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &cd) != FLAC__STREAM_DECODER_INIT_STATUS_OK || cd.got_error) {
+		(void)FLAC__stream_decoder_finish(decoder);
+		FLAC__stream_decoder_delete(decoder);
+		return 0;
+	}
+
+	if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder) || cd.got_error) {
+		(void)FLAC__stream_decoder_finish(decoder);
+		FLAC__stream_decoder_delete(decoder);
+		if(0 != cd.object)
+			FLAC__metadata_object_delete(cd.object);
+		return 0;
+	}
+
+	(void)FLAC__stream_decoder_finish(decoder);
+	FLAC__stream_decoder_delete(decoder);
+
+	return cd.object;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo)
+{
+	FLAC__StreamMetadata *object;
+
+	object = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_STREAMINFO);
+
+	if (object) {
+		/* can just copy the contents since STREAMINFO has no internal structure */
+		*streaminfo = *object;
+		FLAC__metadata_object_delete(object);
+		return true;
+	}
+	else {
+		return false;
+	}
+}
+
+FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags)
+{
+	*tags = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+	return 0 != *tags;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet)
+{
+	*cuesheet = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_CUESHEET);
+
+	return 0 != *cuesheet;
+}
+
+FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+	(void)decoder, (void)frame, (void)buffer, (void)client_data;
+
+	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+	level0_client_data *cd = (level0_client_data *)client_data;
+	(void)decoder;
+
+	/*
+	 * we assume we only get here when the one metadata block we were
+	 * looking for was passed to us
+	 */
+	if(!cd->got_error && 0 == cd->object) {
+		if(0 == (cd->object = FLAC__metadata_object_clone(metadata)))
+			cd->got_error = true;
+	}
+}
+
+void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+	level0_client_data *cd = (level0_client_data *)client_data;
+	(void)decoder;
+
+	if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
+		cd->got_error = true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, uint32_t max_width, uint32_t max_height, uint32_t max_depth, uint32_t max_colors)
+{
+	FLAC__Metadata_SimpleIterator *it;
+	FLAC__uint64 max_area_seen = 0;
+	FLAC__uint64 max_depth_seen = 0;
+
+	*picture = 0;
+
+	it = FLAC__metadata_simple_iterator_new();
+	if(0 == it)
+		return false;
+	if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) {
+		FLAC__metadata_simple_iterator_delete(it);
+		return false;
+	}
+	do {
+		if(FLAC__metadata_simple_iterator_get_block_type(it) == FLAC__METADATA_TYPE_PICTURE) {
+			FLAC__StreamMetadata *obj = FLAC__metadata_simple_iterator_get_block(it);
+			FLAC__uint64 area = (FLAC__uint64)obj->data.picture.width * (FLAC__uint64)obj->data.picture.height;
+			/* check constraints */
+			if(
+				(type == (FLAC__StreamMetadata_Picture_Type)(-1) || type == obj->data.picture.type) &&
+				(mime_type == 0 || !strcmp(mime_type, obj->data.picture.mime_type)) &&
+				(description == 0 || !strcmp((const char *)description, (const char *)obj->data.picture.description)) &&
+				obj->data.picture.width <= max_width &&
+				obj->data.picture.height <= max_height &&
+				obj->data.picture.depth <= max_depth &&
+				obj->data.picture.colors <= max_colors &&
+				(area > max_area_seen || (area == max_area_seen && obj->data.picture.depth > max_depth_seen))
+			) {
+				if(*picture)
+					FLAC__metadata_object_delete(*picture);
+				*picture = obj;
+				max_area_seen = area;
+				max_depth_seen = obj->data.picture.depth;
+			}
+			else {
+				FLAC__metadata_object_delete(obj);
+			}
+		}
+	} while(FLAC__metadata_simple_iterator_next(it));
+
+	FLAC__metadata_simple_iterator_delete(it);
+
+	return (0 != *picture);
+}
+
+
+/****************************************************************************
+ *
+ * Level 1 implementation
+ *
+ ***************************************************************************/
+
+#define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4)
+/* 1 for initial offset, +4 for our own personal use */
+
+struct FLAC__Metadata_SimpleIterator {
+	FILE *file;
+	char *filename, *tempfile_path_prefix;
+	struct flac_stat_s stats;
+	FLAC__bool has_stats;
+	FLAC__bool is_writable;
+	FLAC__Metadata_SimpleIteratorStatus status;
+	FLAC__off_t offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH];
+	FLAC__off_t first_offset; /* this is the offset to the STREAMINFO block */
+	uint32_t depth;
+	/* this is the metadata block header of the current block we are pointing to: */
+	FLAC__bool is_last;
+	FLAC__MetadataType type;
+	uint32_t length;
+};
+
+FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[] = {
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR",
+	"FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR"
+};
+
+
+FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void)
+{
+	FLAC__Metadata_SimpleIterator *iterator = calloc(1, sizeof(FLAC__Metadata_SimpleIterator));
+
+	if(0 != iterator) {
+		iterator->file = 0;
+		iterator->filename = 0;
+		iterator->tempfile_path_prefix = 0;
+		iterator->has_stats = false;
+		iterator->is_writable = false;
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+		iterator->first_offset = iterator->offset[0] = -1;
+		iterator->depth = 0;
+	}
+
+	return iterator;
+}
+
+static void simple_iterator_free_guts_(FLAC__Metadata_SimpleIterator *iterator)
+{
+	if(0 != iterator->file) {
+		fclose(iterator->file);
+		iterator->file = 0;
+		if(iterator->has_stats)
+			set_file_stats_(iterator->filename, &iterator->stats);
+	}
+	if(0 != iterator->filename) {
+		free(iterator->filename);
+		iterator->filename = 0;
+	}
+	if(0 != iterator->tempfile_path_prefix) {
+		free(iterator->tempfile_path_prefix);
+		iterator->tempfile_path_prefix = 0;
+	}
+}
+
+FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator)
+{
+	simple_iterator_free_guts_(iterator);
+	free(iterator);
+}
+
+FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator)
+{
+	FLAC__Metadata_SimpleIteratorStatus status;
+
+	status = iterator->status;
+	iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+	return status;
+}
+
+static FLAC__bool simple_iterator_prime_input_(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool read_only)
+{
+	uint32_t ret;
+
+	if(read_only || 0 == (iterator->file = flac_fopen(iterator->filename, "r+b"))) {
+		iterator->is_writable = false;
+		if(read_only || errno == EACCES) {
+			if(0 == (iterator->file = flac_fopen(iterator->filename, "rb"))) {
+				iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
+				return false;
+			}
+		}
+		else {
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
+			return false;
+		}
+	}
+	else {
+		iterator->is_writable = true;
+	}
+
+	ret = seek_to_first_metadata_block_(iterator->file);
+	switch(ret) {
+		case 0:
+			iterator->depth = 0;
+			iterator->first_offset = iterator->offset[iterator->depth] = ftello(iterator->file);
+			return read_metadata_block_header_(iterator);
+		case 1:
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+			return false;
+		case 2:
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+			return false;
+		case 3:
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE;
+			return false;
+		default:
+			return false;
+	}
+}
+
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats)
+{
+	const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'flac_rename(...)' for what it will take to finish implementing this */
+
+	simple_iterator_free_guts_(iterator);
+
+	if(!read_only && preserve_file_stats)
+		iterator->has_stats = get_file_stats_(filename, &iterator->stats);
+
+	if(0 == (iterator->filename = strdup(filename))) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+	if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+
+	return simple_iterator_prime_input_(iterator, read_only);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator)
+{
+	return iterator->is_writable;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator)
+{
+	if(iterator->is_last)
+		return false;
+
+	if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	iterator->offset[iterator->depth] = ftello(iterator->file);
+
+	return read_metadata_block_header_(iterator);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator)
+{
+	FLAC__off_t this_offset;
+
+	if(iterator->offset[iterator->depth] == iterator->first_offset)
+		return false;
+
+	if(0 != fseeko(iterator->file, iterator->first_offset, SEEK_SET)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+	this_offset = iterator->first_offset;
+	if(!read_metadata_block_header_(iterator))
+		return false;
+
+	/* we ignore any error from ftello() and catch it in fseeko() */
+	while(ftello(iterator->file) + (FLAC__off_t)iterator->length < iterator->offset[iterator->depth]) {
+		if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) {
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+			return false;
+		}
+		this_offset = ftello(iterator->file);
+		if(!read_metadata_block_header_(iterator))
+			return false;
+	}
+
+	iterator->offset[iterator->depth] = this_offset;
+
+	return true;
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator)
+{
+	return iterator->is_last;
+}
+
+/*@@@@add to tests*/
+FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator)
+{
+	return (off_t)iterator->offset[iterator->depth];
+}
+
+FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator)
+{
+	return iterator->type;
+}
+
+/*@@@@add to tests*/
+FLAC_API uint32_t FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator)
+{
+	return iterator->length;
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id)
+{
+	const uint32_t id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
+
+	if(iterator->type != FLAC__METADATA_TYPE_APPLICATION) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
+		return false;
+	}
+
+	if(fread(id, 1, id_bytes, iterator->file) != id_bytes) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+		return false;
+	}
+
+	/* back up */
+	if(0 != fseeko(iterator->file, -((int)id_bytes), SEEK_CUR)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	return true;
+}
+
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator)
+{
+	FLAC__StreamMetadata *block = FLAC__metadata_object_new(iterator->type);
+
+	if(0 != block) {
+		block->is_last = iterator->is_last;
+		block->length = iterator->length;
+
+		if(!read_metadata_block_data_(iterator, block)) {
+			FLAC__metadata_object_delete(block);
+			return 0;
+		}
+
+		/* back up to the beginning of the block data to stay consistent */
+		if(0 != fseeko(iterator->file, iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH, SEEK_SET)) {
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+			FLAC__metadata_object_delete(block);
+			return 0;
+		}
+	}
+	else
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+	return block;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding)
+{
+	FLAC__bool ret;
+
+	if(!iterator->is_writable) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE;
+		return false;
+	}
+
+	if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO || block->type == FLAC__METADATA_TYPE_STREAMINFO) {
+		if(iterator->type != block->type) {
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
+			return false;
+		}
+	}
+
+	block->is_last = iterator->is_last;
+
+	if(iterator->length == block->length)
+		return write_metadata_block_stationary_(iterator, block);
+	else if(iterator->length > block->length) {
+		if(use_padding && iterator->length >= FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) {
+			ret = write_metadata_block_stationary_with_padding_(iterator, block, iterator->length - FLAC__STREAM_METADATA_HEADER_LENGTH - block->length, block->is_last);
+			return ret;
+		}
+		else {
+			ret = rewrite_whole_file_(iterator, block, /*append=*/false);
+			return ret;
+		}
+	}
+	else /* iterator->length < block->length */ {
+		uint32_t padding_leftover = 0;
+		FLAC__bool padding_is_last = false;
+		if(use_padding) {
+			/* first see if we can even use padding */
+			if(iterator->is_last) {
+				use_padding = false;
+			}
+			else {
+				const uint32_t extra_padding_bytes_required = block->length - iterator->length;
+				simple_iterator_push_(iterator);
+				if(!FLAC__metadata_simple_iterator_next(iterator)) {
+					(void)simple_iterator_pop_(iterator);
+					return false;
+				}
+				if(iterator->type != FLAC__METADATA_TYPE_PADDING) {
+					use_padding = false;
+				}
+				else {
+					if(FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length == extra_padding_bytes_required) {
+						padding_leftover = 0;
+						block->is_last = iterator->is_last;
+					}
+					else if(iterator->length < extra_padding_bytes_required)
+						use_padding = false;
+					else {
+						padding_leftover = FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length - extra_padding_bytes_required;
+						padding_is_last = iterator->is_last;
+						block->is_last = false;
+					}
+				}
+				if(!simple_iterator_pop_(iterator))
+					return false;
+			}
+		}
+		if(use_padding) {
+			if(padding_leftover == 0) {
+				ret = write_metadata_block_stationary_(iterator, block);
+				return ret;
+			}
+			else {
+				ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last);
+				return ret;
+			}
+		}
+		else {
+			ret = rewrite_whole_file_(iterator, block, /*append=*/false);
+			return ret;
+		}
+	}
+}
+
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding)
+{
+	uint32_t padding_leftover = 0;
+	FLAC__bool padding_is_last = false;
+
+	FLAC__bool ret;
+
+	if(!iterator->is_writable) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE;
+		return false;
+	}
+
+	if(block->type == FLAC__METADATA_TYPE_STREAMINFO) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
+		return false;
+	}
+
+	block->is_last = iterator->is_last;
+
+	if(use_padding) {
+		/* first see if we can even use padding */
+		if(iterator->is_last) {
+			use_padding = false;
+		}
+		else {
+			simple_iterator_push_(iterator);
+			if(!FLAC__metadata_simple_iterator_next(iterator)) {
+				(void)simple_iterator_pop_(iterator);
+				return false;
+			}
+			if(iterator->type != FLAC__METADATA_TYPE_PADDING) {
+				use_padding = false;
+			}
+			else {
+				if(iterator->length == block->length) {
+					padding_leftover = 0;
+					block->is_last = iterator->is_last;
+				}
+				else if(iterator->length < FLAC__STREAM_METADATA_HEADER_LENGTH + block->length)
+					use_padding = false;
+				else {
+					padding_leftover = iterator->length - block->length;
+					padding_is_last = iterator->is_last;
+					block->is_last = false;
+				}
+			}
+			if(!simple_iterator_pop_(iterator))
+				return false;
+		}
+	}
+	if(use_padding) {
+		/* move to the next block, which is suitable padding */
+		if(!FLAC__metadata_simple_iterator_next(iterator))
+			return false;
+		if(padding_leftover == 0) {
+			ret = write_metadata_block_stationary_(iterator, block);
+			return ret;
+		}
+		else {
+			ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last);
+			return ret;
+		}
+	}
+	else {
+		ret = rewrite_whole_file_(iterator, block, /*append=*/true);
+		return ret;
+	}
+}
+
+FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding)
+{
+	FLAC__bool ret;
+
+	if(!iterator->is_writable) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE;
+		return false;
+	}
+
+	if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
+		return false;
+	}
+
+	if(use_padding) {
+		FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+		if(0 == padding) {
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+		padding->length = iterator->length;
+		if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) {
+			FLAC__metadata_object_delete(padding);
+			return false;
+		}
+		FLAC__metadata_object_delete(padding);
+		if(!FLAC__metadata_simple_iterator_prev(iterator))
+			return false;
+		return true;
+	}
+	else {
+		ret = rewrite_whole_file_(iterator, 0, /*append=*/false);
+		return ret;
+	}
+}
+
+
+
+/****************************************************************************
+ *
+ * Level 2 implementation
+ *
+ ***************************************************************************/
+
+
+typedef struct FLAC__Metadata_Node {
+	FLAC__StreamMetadata *data;
+	struct FLAC__Metadata_Node *prev, *next;
+} FLAC__Metadata_Node;
+
+struct FLAC__Metadata_Chain {
+	char *filename; /* will be NULL if using callbacks */
+	FLAC__bool is_ogg;
+	FLAC__Metadata_Node *head;
+	FLAC__Metadata_Node *tail;
+	uint32_t nodes;
+	FLAC__Metadata_ChainStatus status;
+	FLAC__off_t first_offset, last_offset;
+	/*
+	 * This is the length of the chain initially read from the FLAC file.
+	 * it is used to compare against the current length to decide whether
+	 * or not the whole file has to be rewritten.
+	 */
+	FLAC__off_t initial_length;
+	/* @@@ hacky, these are currently only needed by ogg reader */
+	FLAC__IOHandle handle;
+	FLAC__IOCallback_Read read_cb;
+};
+
+struct FLAC__Metadata_Iterator {
+	FLAC__Metadata_Chain *chain;
+	FLAC__Metadata_Node *current;
+};
+
+FLAC_API const char * const FLAC__Metadata_ChainStatusString[] = {
+	"FLAC__METADATA_CHAIN_STATUS_OK",
+	"FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT",
+	"FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE",
+	"FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE",
+	"FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE",
+	"FLAC__METADATA_CHAIN_STATUS_BAD_METADATA",
+	"FLAC__METADATA_CHAIN_STATUS_READ_ERROR",
+	"FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR",
+	"FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR",
+	"FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR",
+	"FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR",
+	"FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR",
+	"FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR",
+	"FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS",
+	"FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH",
+	"FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL"
+};
+
+
+static FLAC__Metadata_Node *node_new_(void)
+{
+	return calloc(1, sizeof(FLAC__Metadata_Node));
+}
+
+static void node_delete_(FLAC__Metadata_Node *node)
+{
+	if(0 != node->data)
+		FLAC__metadata_object_delete(node->data);
+	free(node);
+}
+
+static void chain_init_(FLAC__Metadata_Chain *chain)
+{
+	chain->filename = 0;
+	chain->is_ogg = false;
+	chain->head = chain->tail = 0;
+	chain->nodes = 0;
+	chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
+	chain->initial_length = 0;
+	chain->read_cb = 0;
+}
+
+static void chain_clear_(FLAC__Metadata_Chain *chain)
+{
+	FLAC__Metadata_Node *node, *next;
+
+	for(node = chain->head; node; ) {
+		next = node->next;
+		node_delete_(node);
+		node = next;
+	}
+
+	if(0 != chain->filename)
+		free(chain->filename);
+
+	chain_init_(chain);
+}
+
+static void chain_append_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node)
+{
+	node->next = node->prev = 0;
+	node->data->is_last = true;
+	if(0 != chain->tail)
+		chain->tail->data->is_last = false;
+
+	if(0 == chain->head)
+		chain->head = node;
+	else {
+		chain->tail->next = node;
+		node->prev = chain->tail;
+	}
+	chain->tail = node;
+	chain->nodes++;
+}
+
+static void chain_remove_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node)
+{
+	if(node == chain->head)
+		chain->head = node->next;
+	else
+		node->prev->next = node->next;
+
+	if(node == chain->tail)
+		chain->tail = node->prev;
+	else
+		node->next->prev = node->prev;
+
+	if(0 != chain->tail)
+		chain->tail->data->is_last = true;
+
+	chain->nodes--;
+}
+
+static void chain_delete_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node)
+{
+	chain_remove_node_(chain, node);
+	node_delete_(node);
+}
+
+static FLAC__off_t chain_calculate_length_(FLAC__Metadata_Chain *chain)
+{
+	const FLAC__Metadata_Node *node;
+	FLAC__off_t length = 0;
+	for(node = chain->head; node; node = node->next)
+		length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
+	return length;
+}
+
+static void iterator_insert_node_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node)
+{
+	node->data->is_last = false;
+
+	node->prev = iterator->current->prev;
+	node->next = iterator->current;
+
+	if(0 == node->prev)
+		iterator->chain->head = node;
+	else
+		node->prev->next = node;
+
+	iterator->current->prev = node;
+
+	iterator->chain->nodes++;
+}
+
+static void iterator_insert_node_after_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node)
+{
+	iterator->current->data->is_last = false;
+
+	node->prev = iterator->current;
+	node->next = iterator->current->next;
+
+	if(0 == node->next)
+		iterator->chain->tail = node;
+	else
+		node->next->prev = node;
+
+	node->prev->next = node;
+
+	iterator->chain->tail->data->is_last = true;
+
+	iterator->chain->nodes++;
+}
+
+/* return true iff node and node->next are both padding */
+static FLAC__bool chain_merge_adjacent_padding_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node)
+{
+	if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) {
+		const uint32_t growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length;
+		node->data->length += growth; /* new block size can be greater than max metadata block size, but it'll be fixed later in chain_prepare_for_write_() */
+
+		chain_delete_node_(chain, node->next);
+		return true;
+	}
+	else
+		return false;
+}
+
+/* Returns the new length of the chain, or 0 if there was an error. */
+/* WATCHOUT: This can get called multiple times before a write, so
+ * it should still work when this happens.
+ */
+/* WATCHOUT: Make sure to also update the logic in
+ * FLAC__metadata_chain_check_if_tempfile_needed() if the logic here changes.
+ */
+static FLAC__off_t chain_prepare_for_write_(FLAC__Metadata_Chain *chain, FLAC__bool use_padding)
+{
+	FLAC__off_t current_length = chain_calculate_length_(chain);
+
+	if(use_padding) {
+		/* if the metadata shrank and the last block is padding, we just extend the last padding block */
+		if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
+			const FLAC__off_t delta = chain->initial_length - current_length;
+			chain->tail->data->length += (uint32_t)delta;
+			current_length += delta;
+		}
+		/* if the metadata shrank more than 4 bytes then there's room to add another padding block */
+		else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) {
+			FLAC__StreamMetadata *padding;
+			FLAC__Metadata_Node *node;
+			if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) {
+				chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+				return 0;
+			}
+			padding->length = (uint32_t)(chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + current_length));
+			if(0 == (node = node_new_())) {
+				FLAC__metadata_object_delete(padding);
+				chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+				return 0;
+			}
+			node->data = padding;
+			chain_append_node_(chain, node);
+			current_length = chain_calculate_length_(chain);
+		}
+		/* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */
+		else if(current_length > chain->initial_length) {
+			const FLAC__off_t delta = current_length - chain->initial_length;
+			if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
+				/* if the delta is exactly the size of the last padding block, remove the padding block */
+				if((FLAC__off_t)chain->tail->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) {
+					chain_delete_node_(chain, chain->tail);
+					current_length = chain_calculate_length_(chain);
+				}
+				/* if there is at least 'delta' bytes of padding, trim the padding down */
+				else if((FLAC__off_t)chain->tail->data->length >= delta) {
+					chain->tail->data->length -= (uint32_t)delta;
+					current_length -= delta;
+				}
+			}
+		}
+	}
+
+	/* check sizes of all metadata blocks; reduce padding size if necessary */
+	{
+		FLAC__Metadata_Node *node;
+		for (node = chain->head; node; node = node->next) {
+			if(node->data->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+				if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
+					node->data->length = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+					current_length = chain_calculate_length_(chain);
+				} else {
+					chain->status = FLAC__METADATA_CHAIN_STATUS_BAD_METADATA;
+					return 0;
+				}
+			}
+		}
+	}
+
+	return current_length;
+}
+
+static FLAC__bool chain_read_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Tell tell_cb)
+{
+	FLAC__Metadata_Node *node;
+
+	/* we assume we're already at the beginning of the file */
+
+	switch(seek_to_first_metadata_block_cb_(handle, read_cb, seek_cb)) {
+		case 0:
+			break;
+		case 1:
+			chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
+			return false;
+		case 2:
+			chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
+			return false;
+		case 3:
+			chain->status = FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE;
+			return false;
+		default:
+			return false;
+	}
+
+	{
+		FLAC__int64 pos = tell_cb(handle);
+		if(pos < 0) {
+			chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
+			return false;
+		}
+		chain->first_offset = (FLAC__off_t)pos;
+	}
+
+	{
+		FLAC__bool is_last;
+		FLAC__MetadataType type;
+		uint32_t length;
+
+		do {
+			node = node_new_();
+			if(0 == node) {
+				chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+				return false;
+			}
+
+			if(!read_metadata_block_header_cb_(handle, read_cb, &is_last, &type, &length)) {
+				node_delete_(node);
+				chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
+				return false;
+			}
+
+			node->data = FLAC__metadata_object_new(type);
+			if(0 == node->data) {
+				node_delete_(node);
+				chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+				return false;
+			}
+
+			node->data->is_last = is_last;
+			node->data->length = length;
+
+			chain->status = get_equivalent_status_(read_metadata_block_data_cb_(handle, read_cb, seek_cb, node->data));
+			if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) {
+				node_delete_(node);
+				return false;
+			}
+			chain_append_node_(chain, node);
+		} while(!is_last);
+	}
+
+	{
+		FLAC__int64 pos = tell_cb(handle);
+		if(pos < 0) {
+			chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
+			return false;
+		}
+		chain->last_offset = (FLAC__off_t)pos;
+	}
+
+	chain->initial_length = chain_calculate_length_(chain);
+
+	return true;
+}
+
+static FLAC__StreamDecoderReadStatus chain_read_ogg_read_cb_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+	FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data;
+	(void)decoder;
+	if(*bytes > 0 && chain->status == FLAC__METADATA_CHAIN_STATUS_OK) {
+		*bytes = chain->read_cb(buffer, sizeof(FLAC__byte), *bytes, chain->handle);
+		if(*bytes == 0)
+			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+		else
+			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+	}
+	else
+		return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+}
+
+static FLAC__StreamDecoderWriteStatus chain_read_ogg_write_cb_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+	(void)decoder, (void)frame, (void)buffer, (void)client_data;
+	return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+}
+
+static void chain_read_ogg_metadata_cb_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+	FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data;
+	FLAC__Metadata_Node *node;
+
+	(void)decoder;
+
+	node = node_new_();
+	if(0 == node) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+		return;
+	}
+
+	node->data = FLAC__metadata_object_clone(metadata);
+	if(0 == node->data) {
+		node_delete_(node);
+		chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+		return;
+	}
+
+	chain_append_node_(chain, node);
+}
+
+static void chain_read_ogg_error_cb_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+	FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data;
+	(void)decoder, (void)status;
+	chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */
+}
+
+static FLAC__bool chain_read_ogg_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb)
+{
+	FLAC__StreamDecoder *decoder;
+
+	/* we assume we're already at the beginning of the file */
+
+	chain->handle = handle;
+	chain->read_cb = read_cb;
+	if(0 == (decoder = FLAC__stream_decoder_new())) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+	FLAC__stream_decoder_set_metadata_respond_all(decoder);
+	if(FLAC__stream_decoder_init_ogg_stream(decoder, chain_read_ogg_read_cb_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, chain_read_ogg_write_cb_, chain_read_ogg_metadata_cb_, chain_read_ogg_error_cb_, chain) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+		FLAC__stream_decoder_delete(decoder);
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */
+		return false;
+	}
+
+	chain->first_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */
+
+	if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */
+	if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) {
+		FLAC__stream_decoder_delete(decoder);
+		return false;
+	}
+
+	FLAC__stream_decoder_delete(decoder);
+
+	chain->last_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */
+
+	chain->initial_length = chain_calculate_length_(chain);
+
+	return true;
+}
+
+static FLAC__bool chain_rewrite_metadata_in_place_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, FLAC__IOCallback_Seek seek_cb)
+{
+	FLAC__Metadata_Node *node;
+
+	if(0 != seek_cb(handle, chain->first_offset, SEEK_SET)) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	for(node = chain->head; node; node = node->next) {
+		if(!write_metadata_block_header_cb_(handle, write_cb, node->data)) {
+			chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
+			return false;
+		}
+		if(!write_metadata_block_data_cb_(handle, write_cb, node->data)) {
+			chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
+			return false;
+		}
+	}
+
+	chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
+	return true;
+}
+
+static FLAC__bool chain_rewrite_metadata_in_place_(FLAC__Metadata_Chain *chain)
+{
+	FILE *file;
+	FLAC__bool ret;
+
+	if(0 == (file = flac_fopen(chain->filename, "r+b"))) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
+		return false;
+	}
+
+	/* chain_rewrite_metadata_in_place_cb_() sets chain->status for us */
+	ret = chain_rewrite_metadata_in_place_cb_(chain, (FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, fseek_wrapper_);
+
+	fclose(file);
+
+	return ret;
+}
+
+static FLAC__bool chain_rewrite_file_(FLAC__Metadata_Chain *chain, const char *tempfile_path_prefix)
+{
+	FILE *f, *tempfile = NULL;
+	char *tempfilename;
+	FLAC__Metadata_SimpleIteratorStatus status;
+	const FLAC__Metadata_Node *node;
+
+	/* copy the file prefix (data up to first metadata block */
+	if(0 == (f = flac_fopen(chain->filename, "rb"))) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
+		return false;
+	}
+	if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) {
+		chain->status = get_equivalent_status_(status);
+		goto err;
+	}
+	if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) {
+		chain->status = get_equivalent_status_(status);
+		goto err;
+	}
+
+	/* write the metadata */
+	for(node = chain->head; node; node = node->next) {
+		if(!write_metadata_block_header_(tempfile, &status, node->data)) {
+			chain->status = get_equivalent_status_(status);
+			goto err;
+		}
+		if(!write_metadata_block_data_(tempfile, &status, node->data)) {
+			chain->status = get_equivalent_status_(status);
+			goto err;
+		}
+	}
+	/*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/
+
+	/* copy the file postfix (everything after the metadata) */
+	if(0 != fseeko(f, chain->last_offset, SEEK_SET)) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
+		goto err;
+	}
+	if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) {
+		chain->status = get_equivalent_status_(status);
+		goto err;
+	}
+
+	/* move the tempfile on top of the original */
+	(void)fclose(f);
+	if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status))
+		return false;
+
+	return true;
+
+err:
+	(void)fclose(f);
+	cleanup_tempfile_(&tempfile, &tempfilename);
+	return false;
+}
+
+/* assumes 'handle' is already at beginning of file */
+static FLAC__bool chain_rewrite_file_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb)
+{
+	FLAC__Metadata_SimpleIteratorStatus status;
+	const FLAC__Metadata_Node *node;
+
+	/* copy the file prefix (data up to first metadata block */
+	if(!copy_n_bytes_from_file_cb_(handle, read_cb, temp_handle, temp_write_cb, chain->first_offset, &status)) {
+		chain->status = get_equivalent_status_(status);
+		return false;
+	}
+
+	/* write the metadata */
+	for(node = chain->head; node; node = node->next) {
+		if(!write_metadata_block_header_cb_(temp_handle, temp_write_cb, node->data)) {
+			chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
+			return false;
+		}
+		if(!write_metadata_block_data_cb_(temp_handle, temp_write_cb, node->data)) {
+			chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
+			return false;
+		}
+	}
+
+	/* copy the file postfix (everything after the metadata) */
+	if(0 != seek_cb(handle, chain->last_offset, SEEK_SET)) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
+		return false;
+	}
+	if(!copy_remaining_bytes_from_file_cb_(handle, read_cb, eof_cb, temp_handle, temp_write_cb, &status)) {
+		chain->status = get_equivalent_status_(status);
+		return false;
+	}
+
+	return true;
+}
+
+FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void)
+{
+	FLAC__Metadata_Chain *chain = calloc(1, sizeof(FLAC__Metadata_Chain));
+
+	if(0 != chain)
+		chain_init_(chain);
+
+	return chain;
+}
+
+FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain)
+{
+	chain_clear_(chain);
+
+	free(chain);
+}
+
+FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain)
+{
+	FLAC__Metadata_ChainStatus status;
+
+	status = chain->status;
+	chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
+	return status;
+}
+
+static FLAC__bool chain_read_(FLAC__Metadata_Chain *chain, const char *filename, FLAC__bool is_ogg)
+{
+	FILE *file;
+	FLAC__bool ret;
+
+	chain_clear_(chain);
+
+	if(0 == (chain->filename = strdup(filename))) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+
+	chain->is_ogg = is_ogg;
+
+	if(0 == (file = flac_fopen(filename, "rb"))) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
+		return false;
+	}
+
+	/* the function also sets chain->status for us */
+	ret = is_ogg?
+		chain_read_ogg_cb_(chain, file, (FLAC__IOCallback_Read)fread) :
+		chain_read_cb_(chain, file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, ftell_wrapper_)
+	;
+
+	fclose(file);
+
+	return ret;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename)
+{
+	return chain_read_(chain, filename, /*is_ogg=*/false);
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename)
+{
+	return chain_read_(chain, filename, /*is_ogg=*/true);
+}
+
+static FLAC__bool chain_read_with_callbacks_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__bool is_ogg)
+{
+	FLAC__bool ret;
+
+	chain_clear_(chain);
+
+	if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.tell) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS;
+		return false;
+	}
+
+	chain->is_ogg = is_ogg;
+
+	/* rewind */
+	if(0 != callbacks.seek(handle, 0, SEEK_SET)) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	/* the function also sets chain->status for us */
+	ret = is_ogg?
+		chain_read_ogg_cb_(chain, handle, callbacks.read) :
+		chain_read_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.tell)
+	;
+
+	return ret;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks)
+{
+	return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/false);
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks)
+{
+	return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/true);
+}
+
+typedef enum {
+	LBS_NONE = 0,
+	LBS_SIZE_CHANGED,
+	LBS_BLOCK_ADDED,
+	LBS_BLOCK_REMOVED
+} LastBlockState;
+
+FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding)
+{
+	/* This does all the same checks that are in chain_prepare_for_write_()
+	 * but doesn't actually alter the chain.  Make sure to update the logic
+	 * here if chain_prepare_for_write_() changes.
+	 */
+	FLAC__off_t current_length;
+	LastBlockState lbs_state = LBS_NONE;
+	uint32_t lbs_size = 0;
+
+	current_length = chain_calculate_length_(chain);
+
+	if(use_padding) {
+		const FLAC__Metadata_Node * const node = chain->tail;
+		/* if the metadata shrank and the last block is padding, we just extend the last padding block */
+		if(current_length < chain->initial_length && node->data->type == FLAC__METADATA_TYPE_PADDING) {
+			lbs_state = LBS_SIZE_CHANGED;
+			lbs_size = (uint32_t)(node->data->length + (chain->initial_length - current_length));
+		}
+		/* if the metadata shrank more than 4 bytes then there's room to add another padding block */
+		else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) {
+			lbs_state = LBS_BLOCK_ADDED;
+			lbs_size = (uint32_t)(chain->initial_length - (current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH));
+		}
+		/* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */
+		else if(current_length > chain->initial_length) {
+			const FLAC__off_t delta = current_length - chain->initial_length;
+			if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
+				/* if the delta is exactly the size of the last padding block, remove the padding block */
+				if((FLAC__off_t)node->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) {
+					lbs_state = LBS_BLOCK_REMOVED;
+					lbs_size = 0;
+				}
+				/* if there is at least 'delta' bytes of padding, trim the padding down */
+				else if((FLAC__off_t)node->data->length >= delta) {
+					lbs_state = LBS_SIZE_CHANGED;
+					lbs_size = (uint32_t)(node->data->length - delta);
+				}
+			}
+		}
+	}
+
+	current_length = 0;
+	/* check sizes of all metadata blocks; reduce padding size if necessary */
+	{
+		const FLAC__Metadata_Node *node;
+		for(node = chain->head; node; node = node->next) {
+			uint32_t block_len = node->data->length;
+			if(node == chain->tail) {
+				if(lbs_state == LBS_BLOCK_REMOVED)
+					continue;
+				else if(lbs_state == LBS_SIZE_CHANGED)
+					block_len = lbs_size;
+			}
+			if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) {
+				if(node->data->type == FLAC__METADATA_TYPE_PADDING)
+					block_len = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+				else
+					return false /* the return value doesn't matter */;
+			}
+			current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + block_len);
+		}
+
+		if(lbs_state == LBS_BLOCK_ADDED) {
+			/* test added padding block */
+			uint32_t block_len = lbs_size;
+			if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN))
+				block_len = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1;
+			current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + block_len);
+		}
+	}
+
+	return (current_length != chain->initial_length);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats)
+{
+	struct flac_stat_s stats;
+	const char *tempfile_path_prefix = 0;
+	FLAC__off_t current_length;
+
+	if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
+		return false;
+	}
+
+	if (0 == chain->filename) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH;
+		return false;
+	}
+
+	current_length = chain_prepare_for_write_(chain, use_padding);
+
+	/* a return value of 0 means there was an error; chain->status is already set */
+	if (0 == current_length)
+		return false;
+
+	if(preserve_file_stats)
+		get_file_stats_(chain->filename, &stats);
+
+	if(current_length == chain->initial_length) {
+		if(!chain_rewrite_metadata_in_place_(chain))
+			return false;
+	}
+	else {
+		if(!chain_rewrite_file_(chain, tempfile_path_prefix))
+			return false;
+
+		/* recompute lengths and offsets */
+		{
+			const FLAC__Metadata_Node *node;
+			chain->initial_length = current_length;
+			chain->last_offset = chain->first_offset;
+			for(node = chain->head; node; node = node->next)
+				chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
+		}
+	}
+
+	if(preserve_file_stats)
+		set_file_stats_(chain->filename, &stats);
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks)
+{
+	FLAC__off_t current_length;
+
+	if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
+		return false;
+	}
+
+	if (0 != chain->filename) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH;
+		return false;
+	}
+
+	if (0 == callbacks.write || 0 == callbacks.seek) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS;
+		return false;
+	}
+
+	if (FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL;
+		return false;
+	}
+
+	current_length = chain_prepare_for_write_(chain, use_padding);
+
+	/* a return value of 0 means there was an error; chain->status is already set */
+	if (0 == current_length)
+		return false;
+
+	return chain_rewrite_metadata_in_place_cb_(chain, handle, callbacks.write, callbacks.seek);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks)
+{
+	FLAC__off_t current_length;
+
+	if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
+		return false;
+	}
+
+	if (0 != chain->filename) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH;
+		return false;
+	}
+
+	if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.eof) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS;
+		return false;
+	}
+	if (0 == temp_callbacks.write) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS;
+		return false;
+	}
+
+	if (!FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL;
+		return false;
+	}
+
+	current_length = chain_prepare_for_write_(chain, use_padding);
+
+	/* a return value of 0 means there was an error; chain->status is already set */
+	if (0 == current_length)
+		return false;
+
+	/* rewind */
+	if(0 != callbacks.seek(handle, 0, SEEK_SET)) {
+		chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	if(!chain_rewrite_file_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.eof, temp_handle, temp_callbacks.write))
+		return false;
+
+	/* recompute lengths and offsets */
+	{
+		const FLAC__Metadata_Node *node;
+		chain->initial_length = current_length;
+		chain->last_offset = chain->first_offset;
+		for(node = chain->head; node; node = node->next)
+			chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
+	}
+
+	return true;
+}
+
+FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain)
+{
+	FLAC__Metadata_Node *node;
+
+	for(node = chain->head; node; ) {
+		if(!chain_merge_adjacent_padding_(chain, node))
+			node = node->next;
+	}
+}
+
+FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain)
+{
+	FLAC__Metadata_Node *node, *save;
+	uint32_t i;
+
+	/*
+	 * Don't try and be too smart... this simple algo is good enough for
+	 * the small number of nodes that we deal with.
+	 */
+	for(i = 0, node = chain->head; i < chain->nodes; i++) {
+		if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
+			save = node->next;
+			chain_remove_node_(chain, node);
+			chain_append_node_(chain, node);
+			node = save;
+		}
+		else {
+			node = node->next;
+		}
+	}
+
+	FLAC__metadata_chain_merge_padding(chain);
+}
+
+
+FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void)
+{
+	FLAC__Metadata_Iterator *iterator = calloc(1, sizeof(FLAC__Metadata_Iterator));
+
+	/* calloc() implies:
+		iterator->current = 0;
+		iterator->chain = 0;
+	*/
+
+	return iterator;
+}
+
+FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator)
+{
+	free(iterator);
+}
+
+FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain)
+{
+	iterator->chain = chain;
+	iterator->current = chain->head;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator)
+{
+	if(0 == iterator->current || 0 == iterator->current->next)
+		return false;
+
+	iterator->current = iterator->current->next;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator)
+{
+	if(0 == iterator->current || 0 == iterator->current->prev)
+		return false;
+
+	iterator->current = iterator->current->prev;
+	return true;
+}
+
+FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator)
+{
+	return iterator->current->data->type;
+}
+
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator)
+{
+	return iterator->current->data;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block)
+{
+	return FLAC__metadata_iterator_delete_block(iterator, false) && FLAC__metadata_iterator_insert_block_after(iterator, block);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding)
+{
+	FLAC__Metadata_Node *save;
+
+	if(0 == iterator->current->prev) {
+		return false;
+	}
+
+	save = iterator->current->prev;
+
+	if(replace_with_padding) {
+		FLAC__metadata_object_delete_data(iterator->current->data);
+		iterator->current->data->type = FLAC__METADATA_TYPE_PADDING;
+	}
+	else {
+		chain_delete_node_(iterator->chain, iterator->current);
+	}
+
+	iterator->current = save;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block)
+{
+	FLAC__Metadata_Node *node;
+
+	if(block->type == FLAC__METADATA_TYPE_STREAMINFO)
+		return false;
+
+	if(0 == iterator->current->prev) {
+		return false;
+	}
+
+	if(0 == (node = node_new_()))
+		return false;
+
+	node->data = block;
+	iterator_insert_node_(iterator, node);
+	iterator->current = node;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block)
+{
+	FLAC__Metadata_Node *node;
+
+	if(block->type == FLAC__METADATA_TYPE_STREAMINFO)
+		return false;
+
+	if(0 == (node = node_new_()))
+		return false;
+
+	node->data = block;
+	iterator_insert_node_after_(iterator, node);
+	iterator->current = node;
+	return true;
+}
+
+
+/****************************************************************************
+ *
+ * Local function definitions
+ *
+ ***************************************************************************/
+
+void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes)
+{
+	uint32_t i;
+
+	b += bytes;
+
+	for(i = 0; i < bytes; i++) {
+		*(--b) = (FLAC__byte)(val & 0xff);
+		val >>= 8;
+	}
+}
+
+void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes)
+{
+	uint32_t i;
+
+	for(i = 0; i < bytes; i++) {
+		*(b++) = (FLAC__byte)(val & 0xff);
+		val >>= 8;
+	}
+}
+
+void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, uint32_t bytes)
+{
+	uint32_t i;
+
+	b += bytes;
+
+	for(i = 0; i < bytes; i++) {
+		*(--b) = (FLAC__byte)(val & 0xff);
+		val >>= 8;
+	}
+}
+
+FLAC__uint32 unpack_uint32_(FLAC__byte *b, uint32_t bytes)
+{
+	FLAC__uint32 ret = 0;
+	uint32_t i;
+
+	for(i = 0; i < bytes; i++)
+		ret = (ret << 8) | (FLAC__uint32)(*b++);
+
+	return ret;
+}
+
+FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, uint32_t bytes)
+{
+	FLAC__uint32 ret = 0;
+	uint32_t i;
+
+	b += bytes;
+
+	for(i = 0; i < bytes; i++)
+		ret = (ret << 8) | (FLAC__uint32)(*--b);
+
+	return ret;
+}
+
+FLAC__uint64 unpack_uint64_(FLAC__byte *b, uint32_t bytes)
+{
+	FLAC__uint64 ret = 0;
+	uint32_t i;
+
+	for(i = 0; i < bytes; i++)
+		ret = (ret << 8) | (FLAC__uint64)(*b++);
+
+	return ret;
+}
+
+FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator)
+{
+	if(!read_metadata_block_header_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, &iterator->is_last, &iterator->type, &iterator->length)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+		return false;
+	}
+
+	return true;
+}
+
+FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block)
+{
+	iterator->status = read_metadata_block_data_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, block);
+
+	return (iterator->status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK);
+}
+
+FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, uint32_t *length)
+{
+	FLAC__byte raw_header[FLAC__STREAM_METADATA_HEADER_LENGTH];
+
+	if(read_cb(raw_header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH)
+		return false;
+
+	*is_last = raw_header[0] & 0x80? true : false;
+	*type = (FLAC__MetadataType)(raw_header[0] & 0x7f);
+	*length = unpack_uint32_(raw_header + 1, 3);
+
+	/* Note that we don't check:
+	 *    if(iterator->type >= FLAC__METADATA_TYPE_UNDEFINED)
+	 * we just will read in an opaque block
+	 */
+
+	return true;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block)
+{
+	switch(block->type) {
+		case FLAC__METADATA_TYPE_STREAMINFO:
+			return read_metadata_block_data_streaminfo_cb_(handle, read_cb, &block->data.stream_info);
+		case FLAC__METADATA_TYPE_PADDING:
+			return read_metadata_block_data_padding_cb_(handle, seek_cb, &block->data.padding, block->length);
+		case FLAC__METADATA_TYPE_APPLICATION:
+			return read_metadata_block_data_application_cb_(handle, read_cb, &block->data.application, block->length);
+		case FLAC__METADATA_TYPE_SEEKTABLE:
+			return read_metadata_block_data_seektable_cb_(handle, read_cb, &block->data.seek_table, block->length);
+		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+			return read_metadata_block_data_vorbis_comment_cb_(handle, read_cb, seek_cb, &block->data.vorbis_comment, block->length);
+		case FLAC__METADATA_TYPE_CUESHEET:
+			return read_metadata_block_data_cuesheet_cb_(handle, read_cb, &block->data.cue_sheet);
+		case FLAC__METADATA_TYPE_PICTURE:
+			return read_metadata_block_data_picture_cb_(handle, read_cb, &block->data.picture);
+		default:
+			return read_metadata_block_data_unknown_cb_(handle, read_cb, &block->data.unknown, block->length);
+	}
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block)
+{
+	FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH], *b;
+
+	if(read_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+
+	b = buffer;
+
+	/* we are using hardcoded numbers for simplicity but we should
+	 * probably eventually write a bit-level unpacker and use the
+	 * _STREAMINFO_ constants.
+	 */
+	block->min_blocksize = unpack_uint32_(b, 2); b += 2;
+	block->max_blocksize = unpack_uint32_(b, 2); b += 2;
+	block->min_framesize = unpack_uint32_(b, 3); b += 3;
+	block->max_framesize = unpack_uint32_(b, 3); b += 3;
+	block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((uint32_t)(b[2] & 0xf0) >> 4);
+	block->channels = (uint32_t)((b[2] & 0x0e) >> 1) + 1;
+	block->bits_per_sample = ((((uint32_t)(b[2] & 0x01)) << 4) | (((uint32_t)(b[3] & 0xf0)) >> 4)) + 1;
+	block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4);
+	memcpy(block->md5sum, b+8, 16);
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, uint32_t block_length)
+{
+	(void)block; /* nothing to do; we don't care about reading the padding bytes */
+
+	if(0 != seek_cb(handle, block_length, SEEK_CUR))
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, uint32_t block_length)
+{
+	const uint32_t id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
+
+	if(read_cb(block->id, 1, id_bytes, handle) != id_bytes)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+
+	if(block_length < id_bytes)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+
+	block_length -= id_bytes;
+
+	if(block_length == 0) {
+		block->data = 0;
+	}
+	else {
+		if(0 == (block->data = malloc(block_length)))
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+		if(read_cb(block->data, 1, block_length, handle) != block_length)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	}
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, uint32_t block_length)
+{
+	uint32_t i;
+	FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH];
+
+	block->num_points = block_length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
+
+	if(block->num_points == 0)
+		block->points = 0;
+	else if(0 == (block->points = safe_malloc_mul_2op_p(block->num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint))))
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+	for(i = 0; i < block->num_points; i++) {
+		if(read_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+		/* some MAGIC NUMBERs here */
+		block->points[i].sample_number = unpack_uint64_(buffer, 8);
+		block->points[i].stream_offset = unpack_uint64_(buffer+8, 8);
+		block->points[i].frame_samples = unpack_uint32_(buffer+16, 2);
+	}
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, uint32_t max_length)
+{
+	const uint32_t entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8;
+	FLAC__byte buffer[4]; /* magic number is asserted below */
+
+	if(max_length < entry_length_len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA;
+
+	max_length -= entry_length_len;
+	if(read_cb(buffer, 1, entry_length_len, handle) != entry_length_len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	entry->length = unpack_uint32_little_endian_(buffer, entry_length_len);
+	if(max_length < entry->length) {
+		entry->length = 0;
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA;
+	} else max_length -= entry->length;
+
+	if(0 != entry->entry)
+		free(entry->entry);
+
+	if(entry->length == 0) {
+		entry->entry = 0;
+	}
+	else {
+		if(0 == (entry->entry = safe_malloc_add_2op_(entry->length, /*+*/1)))
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+		if(read_cb(entry->entry, 1, entry->length, handle) != entry->length)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+
+		entry->entry[entry->length] = '\0';
+	}
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, uint32_t block_length)
+{
+	uint32_t i;
+	FLAC__Metadata_SimpleIteratorStatus status;
+	const uint32_t num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8;
+	FLAC__byte buffer[4]; /* magic number is asserted below */
+
+	status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, &(block->vendor_string), block_length);
+	if(block_length >= 4)
+		block_length -= 4;
+	if(status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA)
+		goto skip;
+	else if(status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK)
+		return status;
+	block_length -= block->vendor_string.length;
+
+	if(block_length < num_comments_len) goto skip; else block_length -= num_comments_len;
+	if(read_cb(buffer, 1, num_comments_len, handle) != num_comments_len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->num_comments = unpack_uint32_little_endian_(buffer, num_comments_len);
+
+	if(block->num_comments == 0) {
+		block->comments = 0;
+	}
+	else if(0 == (block->comments = calloc(block->num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) {
+		block->num_comments = 0;
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+	}
+
+	for(i = 0; i < block->num_comments; i++) {
+		status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, block->comments + i, block_length);
+		if(block_length >= 4) block_length -= 4;
+		if(status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA) {
+			block->num_comments = i;
+			goto skip;
+		}
+		else if(status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) return status;
+		block_length -= block->comments[i].length;
+	}
+
+  skip:
+	if(block_length > 0) {
+		/* bad metadata */
+		if(0 != seek_cb(handle, block_length, SEEK_CUR))
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+	}
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track)
+{
+	uint32_t i, len;
+	FLAC__byte buffer[32]; /* asserted below that this is big enough */
+
+	len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	track->offset = unpack_uint64_(buffer, len);
+
+	len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	track->number = (FLAC__byte)unpack_uint32_(buffer, len);
+
+	len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8;
+	if(read_cb(track->isrc, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+
+	len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	track->type = buffer[0] >> 7;
+	track->pre_emphasis = (buffer[0] >> 6) & 1;
+
+	len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	track->num_indices = (FLAC__byte)unpack_uint32_(buffer, len);
+
+	if(track->num_indices == 0) {
+		track->indices = 0;
+	}
+	else if(0 == (track->indices = calloc(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index))))
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+	for(i = 0; i < track->num_indices; i++) {
+		len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8;
+		if(read_cb(buffer, 1, len, handle) != len)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+		track->indices[i].offset = unpack_uint64_(buffer, len);
+
+		len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8;
+		if(read_cb(buffer, 1, len, handle) != len)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+		track->indices[i].number = (FLAC__byte)unpack_uint32_(buffer, len);
+
+		len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8;
+		if(read_cb(buffer, 1, len, handle) != len)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	}
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block)
+{
+	uint32_t i, len;
+	FLAC__Metadata_SimpleIteratorStatus status;
+	FLAC__byte buffer[1024]; /* MSVC needs a constant expression so we put a magic number and assert */
+
+	len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8;
+	if(read_cb(block->media_catalog_number, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+
+	len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->lead_in = unpack_uint64_(buffer, len);
+
+	len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->is_cd = buffer[0]&0x80? true : false;
+
+	len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->num_tracks = unpack_uint32_(buffer, len);
+
+	if(block->num_tracks == 0) {
+		block->tracks = 0;
+	}
+	else if(0 == (block->tracks = calloc(block->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track))))
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+	for(i = 0; i < block->num_tracks; i++) {
+		if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_cuesheet_track_cb_(handle, read_cb, block->tracks + i)))
+			return status;
+	}
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cstring_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__byte **data, FLAC__uint32 *length, FLAC__uint32 length_len)
+{
+	FLAC__byte buffer[sizeof(FLAC__uint32)];
+
+	length_len /= 8; /* convert to bytes */
+
+	if(read_cb(buffer, 1, length_len, handle) != length_len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	*length = unpack_uint32_(buffer, length_len);
+
+	if(0 != *data)
+		free(*data);
+
+	if(0 == (*data = safe_malloc_add_2op_(*length, /*+*/1)))
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+	if(*length > 0) {
+		if(read_cb(*data, 1, *length, handle) != *length)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	}
+
+	(*data)[*length] = '\0';
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block)
+{
+	FLAC__Metadata_SimpleIteratorStatus status;
+	FLAC__byte buffer[4]; /* asserted below that this is big enough */
+	FLAC__uint32 len;
+
+	len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->type = (FLAC__StreamMetadata_Picture_Type)unpack_uint32_(buffer, len);
+
+	if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, (FLAC__byte**)(&(block->mime_type)), &len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK)
+		return status;
+
+	if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->description), &len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK)
+		return status;
+
+	len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->width = unpack_uint32_(buffer, len);
+
+	len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->height = unpack_uint32_(buffer, len);
+
+	len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->depth = unpack_uint32_(buffer, len);
+
+	len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN / 8;
+	if(read_cb(buffer, 1, len, handle) != len)
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	block->colors = unpack_uint32_(buffer, len);
+
+	/* for convenience we use read_metadata_block_data_picture_cstring_cb_() even though it adds an extra terminating NUL we don't use */
+	if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->data), &(block->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK)
+		return status;
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, uint32_t block_length)
+{
+	if(block_length == 0) {
+		block->data = 0;
+	}
+	else {
+		if(0 == (block->data = malloc(block_length)))
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+		if(read_cb(block->data, 1, block_length, handle) != block_length)
+			return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+	}
+
+	return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+}
+
+FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block)
+{
+	if(!write_metadata_block_header_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) {
+		*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
+		return false;
+	}
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block)
+{
+	if (write_metadata_block_data_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) {
+		*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
+		return true;
+	}
+	else {
+		*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
+		return false;
+	}
+}
+
+FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block)
+{
+	FLAC__byte buffer[FLAC__STREAM_METADATA_HEADER_LENGTH];
+
+	/* double protection */
+	if(block->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN))
+		return false;
+
+	buffer[0] = (block->is_last? 0x80 : 0) | (FLAC__byte)block->type;
+	pack_uint32_(block->length, buffer + 1, 3);
+
+	if(write_cb(buffer, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH)
+		return false;
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block)
+{
+	switch(block->type) {
+		case FLAC__METADATA_TYPE_STREAMINFO:
+			return write_metadata_block_data_streaminfo_cb_(handle, write_cb, &block->data.stream_info);
+		case FLAC__METADATA_TYPE_PADDING:
+			return write_metadata_block_data_padding_cb_(handle, write_cb, &block->data.padding, block->length);
+		case FLAC__METADATA_TYPE_APPLICATION:
+			return write_metadata_block_data_application_cb_(handle, write_cb, &block->data.application, block->length);
+		case FLAC__METADATA_TYPE_SEEKTABLE:
+			return write_metadata_block_data_seektable_cb_(handle, write_cb, &block->data.seek_table);
+		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+			return write_metadata_block_data_vorbis_comment_cb_(handle, write_cb, &block->data.vorbis_comment);
+		case FLAC__METADATA_TYPE_CUESHEET:
+			return write_metadata_block_data_cuesheet_cb_(handle, write_cb, &block->data.cue_sheet);
+		case FLAC__METADATA_TYPE_PICTURE:
+			return write_metadata_block_data_picture_cb_(handle, write_cb, &block->data.picture);
+		default:
+			return write_metadata_block_data_unknown_cb_(handle, write_cb, &block->data.unknown, block->length);
+	}
+}
+
+FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block)
+{
+	FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH];
+	const uint32_t channels1 = block->channels - 1;
+	const uint32_t bps1 = block->bits_per_sample - 1;
+
+	/* we are using hardcoded numbers for simplicity but we should
+	 * probably eventually write a bit-level packer and use the
+	 * _STREAMINFO_ constants.
+	 */
+	pack_uint32_(block->min_blocksize, buffer, 2);
+	pack_uint32_(block->max_blocksize, buffer+2, 2);
+	pack_uint32_(block->min_framesize, buffer+4, 3);
+	pack_uint32_(block->max_framesize, buffer+7, 3);
+	buffer[10] = (block->sample_rate >> 12) & 0xff;
+	buffer[11] = (block->sample_rate >> 4) & 0xff;
+	buffer[12] = (FLAC__byte)(((block->sample_rate & 0x0f) << 4) | (channels1 << 1) | (bps1 >> 4));
+	buffer[13] = (FLAC__byte)(((bps1 & 0x0f) << 4) | ((block->total_samples >> 32) & 0x0f));
+	pack_uint32_((FLAC__uint32)block->total_samples, buffer+14, 4);
+	memcpy(buffer+18, block->md5sum, 16);
+
+	if(write_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
+		return false;
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, uint32_t block_length)
+{
+	uint32_t i, n = block_length;
+	FLAC__byte buffer[1024];
+
+	(void)block;
+
+	memset(buffer, 0, 1024);
+
+	for(i = 0; i < n/1024; i++)
+		if(write_cb(buffer, 1, 1024, handle) != 1024)
+			return false;
+
+	n %= 1024;
+
+	if(write_cb(buffer, 1, n, handle) != n)
+		return false;
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, uint32_t block_length)
+{
+	const uint32_t id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
+
+	if(write_cb(block->id, 1, id_bytes, handle) != id_bytes)
+		return false;
+
+	block_length -= id_bytes;
+
+	if(write_cb(block->data, 1, block_length, handle) != block_length)
+		return false;
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block)
+{
+	uint32_t i;
+	FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH];
+
+	for(i = 0; i < block->num_points; i++) {
+		/* some MAGIC NUMBERs here */
+		pack_uint64_(block->points[i].sample_number, buffer, 8);
+		pack_uint64_(block->points[i].stream_offset, buffer+8, 8);
+		pack_uint32_(block->points[i].frame_samples, buffer+16, 2);
+		if(write_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)
+			return false;
+	}
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block)
+{
+	uint32_t i;
+	const uint32_t entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8;
+	const uint32_t num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8;
+	FLAC__byte buffer[4]; /* magic number is asserted below */
+
+	pack_uint32_little_endian_(block->vendor_string.length, buffer, entry_length_len);
+	if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len)
+		return false;
+	if(write_cb(block->vendor_string.entry, 1, block->vendor_string.length, handle) != block->vendor_string.length)
+		return false;
+
+	pack_uint32_little_endian_(block->num_comments, buffer, num_comments_len);
+	if(write_cb(buffer, 1, num_comments_len, handle) != num_comments_len)
+		return false;
+
+	for(i = 0; i < block->num_comments; i++) {
+		pack_uint32_little_endian_(block->comments[i].length, buffer, entry_length_len);
+		if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len)
+			return false;
+		if(write_cb(block->comments[i].entry, 1, block->comments[i].length, handle) != block->comments[i].length)
+			return false;
+	}
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block)
+{
+	uint32_t i, j, len;
+	FLAC__byte buffer[1024]; /* asserted below that this is big enough */
+
+	len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8;
+	if(write_cb(block->media_catalog_number, 1, len, handle) != len)
+		return false;
+
+	len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8;
+	pack_uint64_(block->lead_in, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8;
+	memset(buffer, 0, len);
+	if(block->is_cd)
+		buffer[0] |= 0x80;
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8;
+	pack_uint32_(block->num_tracks, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	for(i = 0; i < block->num_tracks; i++) {
+		FLAC__StreamMetadata_CueSheet_Track *track = block->tracks + i;
+
+		len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8;
+		pack_uint64_(track->offset, buffer, len);
+		if(write_cb(buffer, 1, len, handle) != len)
+			return false;
+
+		len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8;
+		pack_uint32_(track->number, buffer, len);
+		if(write_cb(buffer, 1, len, handle) != len)
+			return false;
+
+		len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8;
+		if(write_cb(track->isrc, 1, len, handle) != len)
+			return false;
+
+		len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8;
+		memset(buffer, 0, len);
+		buffer[0] = (FLAC__byte)((track->type << 7) | (track->pre_emphasis << 6));
+		if(write_cb(buffer, 1, len, handle) != len)
+			return false;
+
+		len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8;
+		pack_uint32_(track->num_indices, buffer, len);
+		if(write_cb(buffer, 1, len, handle) != len)
+			return false;
+
+		for(j = 0; j < track->num_indices; j++) {
+			FLAC__StreamMetadata_CueSheet_Index *indx = track->indices + j;
+
+			len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8;
+			pack_uint64_(indx->offset, buffer, len);
+			if(write_cb(buffer, 1, len, handle) != len)
+				return false;
+
+			len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8;
+			pack_uint32_(indx->number, buffer, len);
+			if(write_cb(buffer, 1, len, handle) != len)
+				return false;
+
+			len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8;
+			memset(buffer, 0, len);
+			if(write_cb(buffer, 1, len, handle) != len)
+				return false;
+		}
+	}
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block)
+{
+	uint32_t len;
+	size_t slen;
+	FLAC__byte buffer[4]; /* magic number is asserted below */
+
+	len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8;
+	pack_uint32_(block->type, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	len = FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8;
+	slen = strlen(block->mime_type);
+	pack_uint32_((FLAC__uint32)slen, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+	if(write_cb(block->mime_type, 1, slen, handle) != slen)
+		return false;
+
+	len = FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8;
+	slen = strlen((const char *)block->description);
+	pack_uint32_((FLAC__uint32)slen, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+	if(write_cb(block->description, 1, slen, handle) != slen)
+		return false;
+
+	len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8;
+	pack_uint32_(block->width, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8;
+	pack_uint32_(block->height, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8;
+	pack_uint32_(block->depth, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8;
+	pack_uint32_(block->colors, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+
+	len = FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8;
+	pack_uint32_(block->data_length, buffer, len);
+	if(write_cb(buffer, 1, len, handle) != len)
+		return false;
+	if(write_cb(block->data, 1, block->data_length, handle) != block->data_length)
+		return false;
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, uint32_t block_length)
+{
+	if(write_cb(block->data, 1, block_length, handle) != block_length)
+		return false;
+
+	return true;
+}
+
+FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block)
+{
+	if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	if(!write_metadata_block_header_(iterator->file, &iterator->status, block))
+		return false;
+
+	if(!write_metadata_block_data_(iterator->file, &iterator->status, block))
+		return false;
+
+	if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	return read_metadata_block_header_(iterator);
+}
+
+FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, uint32_t padding_length, FLAC__bool padding_is_last)
+{
+	FLAC__StreamMetadata *padding;
+
+	if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	block->is_last = false;
+
+	if(!write_metadata_block_header_(iterator->file, &iterator->status, block))
+		return false;
+
+	if(!write_metadata_block_data_(iterator->file, &iterator->status, block))
+		return false;
+
+	if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)))
+		return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+
+	padding->is_last = padding_is_last;
+	padding->length = padding_length;
+
+	if(!write_metadata_block_header_(iterator->file, &iterator->status, padding)) {
+		FLAC__metadata_object_delete(padding);
+		return false;
+	}
+
+	if(!write_metadata_block_data_(iterator->file, &iterator->status, padding)) {
+		FLAC__metadata_object_delete(padding);
+		return false;
+	}
+
+	FLAC__metadata_object_delete(padding);
+
+	if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	return read_metadata_block_header_(iterator);
+}
+
+FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append)
+{
+	FILE *tempfile = NULL;
+	char *tempfilename = NULL;
+	int fixup_is_last_code = 0; /* 0 => no need to change any is_last flags */
+	FLAC__off_t fixup_is_last_flag_offset = -1;
+
+	if(iterator->is_last) {
+		if(append) {
+			fixup_is_last_code = 1; /* 1 => clear the is_last flag at the following offset */
+			fixup_is_last_flag_offset = iterator->offset[iterator->depth];
+		}
+		else if(0 == block) {
+			simple_iterator_push_(iterator);
+			if(!FLAC__metadata_simple_iterator_prev(iterator)) {
+				(void)simple_iterator_pop_(iterator);
+				return false;
+			}
+			fixup_is_last_code = -1; /* -1 => set the is_last the flag at the following offset */
+			fixup_is_last_flag_offset = iterator->offset[iterator->depth];
+			if(!simple_iterator_pop_(iterator))
+				return false;
+		}
+	}
+
+	if(!simple_iterator_copy_file_prefix_(iterator, &tempfile, &tempfilename, append))
+		return false;
+
+	if(0 != block) {
+		if(!write_metadata_block_header_(tempfile, &iterator->status, block)) {
+			cleanup_tempfile_(&tempfile, &tempfilename);
+			return false;
+		}
+
+		if(!write_metadata_block_data_(tempfile, &iterator->status, block)) {
+			cleanup_tempfile_(&tempfile, &tempfilename);
+			return false;
+		}
+	}
+
+	if(!simple_iterator_copy_file_postfix_(iterator, &tempfile, &tempfilename, fixup_is_last_code, fixup_is_last_flag_offset, block==0))
+		return false;
+
+	if(append)
+		return FLAC__metadata_simple_iterator_next(iterator);
+
+	return true;
+}
+
+void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator)
+{
+	iterator->offset[iterator->depth+1] = iterator->offset[iterator->depth];
+	iterator->depth++;
+}
+
+FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator)
+{
+	iterator->depth--;
+	if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+
+	return read_metadata_block_header_(iterator);
+}
+
+/* return meanings:
+ * 0: ok
+ * 1: read error
+ * 2: seek error
+ * 3: not a FLAC file
+ */
+uint32_t seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb)
+{
+	FLAC__byte buffer[4];
+	size_t n;
+	uint32_t i;
+
+	/* skip any id3v2 tag */
+	errno = 0;
+	n = read_cb(buffer, 1, 4, handle);
+	if(errno)
+		return 1;
+	else if(n != 4)
+		return 3;
+	else if(0 == memcmp(buffer, "ID3", 3)) {
+		uint32_t tag_length = 0;
+
+		/* skip to the tag length */
+		if(seek_cb(handle, 2, SEEK_CUR) < 0)
+			return 2;
+
+		/* read the length */
+		for(i = 0; i < 4; i++) {
+			if(read_cb(buffer, 1, 1, handle) < 1 || buffer[0] & 0x80)
+				return 1;
+			tag_length <<= 7;
+			tag_length |= (buffer[0] & 0x7f);
+		}
+
+		/* skip the rest of the tag */
+		if(seek_cb(handle, tag_length, SEEK_CUR) < 0)
+			return 2;
+
+		/* read the stream sync code */
+		errno = 0;
+		n = read_cb(buffer, 1, 4, handle);
+		if(errno)
+			return 1;
+		else if(n != 4)
+			return 3;
+	}
+
+	/* check for the fLaC signature */
+	if(0 == memcmp(FLAC__STREAM_SYNC_STRING, buffer, FLAC__STREAM_SYNC_LENGTH))
+		return 0;
+	else
+		return 3;
+}
+
+uint32_t seek_to_first_metadata_block_(FILE *f)
+{
+	return seek_to_first_metadata_block_cb_((FLAC__IOHandle)f, (FLAC__IOCallback_Read)fread, fseek_wrapper_);
+}
+
+FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append)
+{
+	const FLAC__off_t offset_end = append? iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length : iterator->offset[iterator->depth];
+
+	if(0 != fseeko(iterator->file, 0, SEEK_SET)) {
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+	if(!open_tempfile_(iterator->filename, iterator->tempfile_path_prefix, tempfile, tempfilename, &iterator->status)) {
+		cleanup_tempfile_(tempfile, tempfilename);
+		return false;
+	}
+	if(!copy_n_bytes_from_file_(iterator->file, *tempfile, offset_end, &iterator->status)) {
+		cleanup_tempfile_(tempfile, tempfilename);
+		return false;
+	}
+
+	return true;
+}
+
+FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup)
+{
+	FLAC__off_t save_offset = iterator->offset[iterator->depth];
+
+	if(0 != fseeko(iterator->file, save_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length, SEEK_SET)) {
+		cleanup_tempfile_(tempfile, tempfilename);
+		iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+		return false;
+	}
+	if(!copy_remaining_bytes_from_file_(iterator->file, *tempfile, &iterator->status)) {
+		cleanup_tempfile_(tempfile, tempfilename);
+		return false;
+	}
+
+	if(fixup_is_last_code != 0) {
+		/*
+		 * if code == 1, it means a block was appended to the end so
+		 *   we have to clear the is_last flag of the previous block
+		 * if code == -1, it means the last block was deleted so
+		 *   we have to set the is_last flag of the previous block
+		 */
+		/* MAGIC NUMBERs here; we know the is_last flag is the high bit of the byte at this location */
+		FLAC__byte x;
+		if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) {
+			cleanup_tempfile_(tempfile, tempfilename);
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+			return false;
+		}
+		if(fread(&x, 1, 1, *tempfile) != 1) {
+			cleanup_tempfile_(tempfile, tempfilename);
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+			return false;
+		}
+		if(fixup_is_last_code > 0) {
+			x &= 0x7f;
+		}
+		else {
+			x |= 0x80;
+		}
+		if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) {
+			cleanup_tempfile_(tempfile, tempfilename);
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
+			return false;
+		}
+		if(local__fwrite(&x, 1, 1, *tempfile) != 1) {
+			cleanup_tempfile_(tempfile, tempfilename);
+			iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
+			return false;
+		}
+	}
+
+	(void)fclose(iterator->file);
+
+	if(!transport_tempfile_(iterator->filename, tempfile, tempfilename, &iterator->status))
+		return false;
+
+	if(iterator->has_stats)
+		set_file_stats_(iterator->filename, &iterator->stats);
+
+	if(!simple_iterator_prime_input_(iterator, !iterator->is_writable))
+		return false;
+	if(backup) {
+		while(iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length < save_offset)
+			if(!FLAC__metadata_simple_iterator_next(iterator))
+				return false;
+		return true;
+	}
+	else {
+		/* move the iterator to it's original block faster by faking a push, then doing a pop_ */
+		iterator->offset[0] = save_offset;
+		iterator->depth++;
+		return simple_iterator_pop_(iterator);
+	}
+}
+
+FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status)
+{
+	FLAC__byte buffer[8192];
+	size_t n;
+
+	while(bytes > 0) {
+		n = flac_min(sizeof(buffer), (size_t)bytes);
+		if(fread(buffer, 1, n, file) != n) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+			return false;
+		}
+		if(local__fwrite(buffer, 1, n, tempfile) != n) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
+			return false;
+		}
+		bytes -= n;
+	}
+
+	return true;
+}
+
+FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status)
+{
+	FLAC__byte buffer[8192];
+	size_t n;
+
+	while(bytes > 0) {
+		n = flac_min(sizeof(buffer), (size_t)bytes);
+		if(read_cb(buffer, 1, n, handle) != n) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+			return false;
+		}
+		if(temp_write_cb(buffer, 1, n, temp_handle) != n) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
+			return false;
+		}
+		bytes -= n;
+	}
+
+	return true;
+}
+
+FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status)
+{
+	FLAC__byte buffer[8192];
+	size_t n;
+
+	while(!feof(file)) {
+		n = fread(buffer, 1, sizeof(buffer), file);
+		if(n == 0 && !feof(file)) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+			return false;
+		}
+		if(n > 0 && local__fwrite(buffer, 1, n, tempfile) != n) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
+			return false;
+		}
+	}
+
+	return true;
+}
+
+FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status)
+{
+	FLAC__byte buffer[8192];
+	size_t n;
+
+	while(!eof_cb(handle)) {
+		n = read_cb(buffer, 1, sizeof(buffer), handle);
+		if(n == 0 && !eof_cb(handle)) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
+			return false;
+		}
+		if(n > 0 && temp_write_cb(buffer, 1, n, temp_handle) != n) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int
+local_snprintf(char *str, size_t size, const char *fmt, ...)
+{
+	va_list va;
+	int rc;
+
+#if defined _MSC_VER
+	if (size == 0)
+		return 1024;
+#endif
+
+	va_start (va, fmt);
+
+#if defined _MSC_VER
+	rc = vsnprintf_s (str, size, _TRUNCATE, fmt, va);
+	if (rc < 0)
+		rc = (int)(size - 1);
+#elif defined __MINGW32__
+	rc = __mingw_vsnprintf (str, size, fmt, va);
+#else
+	rc = vsnprintf (str, size, fmt, va);
+#endif
+	va_end (va);
+
+	return rc;
+}
+
+FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status)
+{
+	static const char *tempfile_suffix = ".metadata_edit";
+	if(0 == tempfile_path_prefix) {
+		size_t dest_len = strlen(filename) + strlen(tempfile_suffix) + 1;
+		if(0 == (*tempfilename = safe_malloc_(dest_len))) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+		local_snprintf(*tempfilename, dest_len, "%s%s", filename, tempfile_suffix);
+	}
+	else {
+		const char *p = strrchr(filename, '/');
+		size_t dest_len;
+		if(0 == p)
+			p = filename;
+		else
+			p++;
+
+		dest_len = strlen(tempfile_path_prefix) + strlen(p) + strlen(tempfile_suffix) + 2;
+
+		if(0 == (*tempfilename = safe_malloc_(dest_len))) {
+			*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+		local_snprintf(*tempfilename, dest_len, "%s/%s%s", tempfile_path_prefix, p, tempfile_suffix);
+	}
+
+	if(0 == (*tempfile = flac_fopen(*tempfilename, "w+b"))) {
+		*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
+		return false;
+	}
+
+	return true;
+}
+
+FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status)
+{
+	(void)fclose(*tempfile);
+	*tempfile = 0;
+
+#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__
+	/* on some flavors of windows, flac_rename() will fail if the destination already exists */
+	if(flac_unlink(filename) < 0) {
+		cleanup_tempfile_(tempfile, tempfilename);
+		*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR;
+		return false;
+	}
+#endif
+
+	/*@@@ to fully support the tempfile_path_prefix we need to update this piece to actually copy across filesystems instead of just flac_rename(): */
+	if(0 != flac_rename(*tempfilename, filename)) {
+		cleanup_tempfile_(tempfile, tempfilename);
+		*status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR;
+		return false;
+	}
+
+	cleanup_tempfile_(tempfile, tempfilename);
+
+	return true;
+}
+
+void cleanup_tempfile_(FILE **tempfile, char **tempfilename)
+{
+	if(0 != *tempfile) {
+		(void)fclose(*tempfile);
+		*tempfile = 0;
+	}
+
+	if(0 != *tempfilename) {
+		(void)flac_unlink(*tempfilename);
+		free(*tempfilename);
+		*tempfilename = 0;
+	}
+}
+
+FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats)
+{
+	return (0 == flac_stat(filename, stats));
+}
+
+void set_file_stats_(const char *filename, struct flac_stat_s *stats)
+{
+	struct utimbuf srctime;
+
+	srctime.actime = stats->st_atime;
+	srctime.modtime = stats->st_mtime;
+	(void)flac_chmod(filename, stats->st_mode);
+	(void)flac_utime(filename, &srctime);
+#if !defined _MSC_VER && !defined __BORLANDC__ && !defined __MINGW32__
+	FLAC_CHECK_RETURN(chown(filename, stats->st_uid, -1));
+	FLAC_CHECK_RETURN(chown(filename, -1, stats->st_gid));
+#endif
+}
+
+int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
+{
+	return fseeko((FILE*)handle, (FLAC__off_t)offset, whence);
+}
+
+FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle)
+{
+	return ftello((FILE*)handle);
+}
+
+FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status)
+{
+	switch(status) {
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK:
+			return FLAC__METADATA_CHAIN_STATUS_OK;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
+			return FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
+			return FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
+			return FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE:
+			return FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA:
+			return FLAC__METADATA_CHAIN_STATUS_BAD_METADATA;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR:
+			return FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR:
+			return FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR:
+			return FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR:
+			return FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR:
+			return FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR:
+			return FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
+		case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR:
+		default:
+			return FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
+	}
+}
--- /dev/null
+++ b/src/libflac/metadata_object.c
@@ -1,0 +1,1588 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "private/metadata.h"
+#include "private/memory.h"
+
+#include "share/alloc.h"
+#include "share/compat.h"
+
+// 8bb: hide POSIX warnings
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+
+/* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */
+#define safe_malloc_mul_2op_ safe_malloc_mul_2op_p
+
+
+/****************************************************************************
+ *
+ * Local routines
+ *
+ ***************************************************************************/
+
+/* copy bytes:
+ *  from = NULL  && bytes = 0
+ *       to <- NULL
+ *  from != NULL && bytes > 0
+ *       to <- copy of from
+ *  else ASSERT
+ * malloc error leaves 'to' unchanged
+ */
+static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, uint32_t bytes)
+{
+	if (bytes > 0 && from != NULL) {
+		FLAC__byte *x;
+		if ((x = safe_malloc_(bytes)) == NULL)
+			return false;
+		memcpy(x, from, bytes);
+		*to = x;
+	}
+	else {
+		*to = 0;
+	}
+	return true;
+}
+
+/* reallocate entry to 1 byte larger and add a terminating NUL */
+/* realloc() failure leaves entry unchanged */
+static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, uint32_t length)
+{
+	FLAC__byte *x = safe_realloc_add_2op_(*entry, length, /*+*/1);
+	if (x != NULL) {
+		x[length] = '\0';
+		*entry = x;
+		return true;
+	}
+	else
+		return false;
+}
+
+/* copies the NUL-terminated C-string 'from' to '*to', leaving '*to'
+ * unchanged if malloc fails, free()ing the original '*to' if it
+ * succeeds and the original '*to' was not NULL
+ */
+static FLAC__bool copy_cstring_(char **to, const char *from)
+{
+	char *copy = strdup(from);
+	if (copy) {
+		free(*to);
+		*to = copy;
+		return true;
+	}
+	else
+		return false;
+}
+
+static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from)
+{
+	to->length = from->length;
+	if (from->entry == 0) {
+		to->entry = 0;
+	}
+	else {
+		FLAC__byte *x;
+		if ((x = safe_malloc_add_2op_(from->length, /*+*/1)) == NULL)
+			return false;
+		memcpy(x, from->entry, from->length);
+		x[from->length] = '\0';
+		to->entry = x;
+	}
+	return true;
+}
+
+static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from)
+{
+	memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track));
+	if (from->indices == 0) {
+	}
+	else {
+		FLAC__StreamMetadata_CueSheet_Index *x;
+		if ((x = safe_malloc_mul_2op_p(from->num_indices, /*times*/sizeof(FLAC__StreamMetadata_CueSheet_Index))) == NULL)
+			return false;
+		memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index));
+		to->indices = x;
+	}
+	return true;
+}
+
+static void seektable_calculate_length_(FLAC__StreamMetadata *object)
+{
+	object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
+}
+
+static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(uint32_t num_points)
+{
+	FLAC__StreamMetadata_SeekPoint *object_array;
+
+	object_array = safe_malloc_mul_2op_p(num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint));
+
+	if (object_array != NULL) {
+		uint32_t i;
+		for (i = 0; i < num_points; i++) {
+			object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
+			object_array[i].stream_offset = 0;
+			object_array[i].frame_samples = 0;
+		}
+	}
+
+	return object_array;
+}
+
+static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object)
+{
+	uint32_t i;
+
+	object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
+	object->length += object->data.vorbis_comment.vendor_string.length;
+	object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
+	for (i = 0; i < object->data.vorbis_comment.num_comments; i++) {
+		object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
+		object->length += object->data.vorbis_comment.comments[i].length;
+	}
+}
+
+static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(uint32_t num_comments)
+{
+	return safe_calloc_(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry));
+}
+
+static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, uint32_t num_comments)
+{
+	uint32_t i;
+
+	for (i = 0; i < num_comments; i++)
+		free(object_array[i].entry);
+
+	free(object_array);
+}
+
+static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, uint32_t num_comments)
+{
+	FLAC__StreamMetadata_VorbisComment_Entry *return_array;
+
+	return_array = vorbiscomment_entry_array_new_(num_comments);
+
+	if (return_array != NULL) {
+		uint32_t i;
+
+		for (i = 0; i < num_comments; i++) {
+			if (!copy_vcentry_(return_array+i, object_array+i)) {
+				vorbiscomment_entry_array_delete_(return_array, num_comments);
+				return 0;
+			}
+		}
+	}
+
+	return return_array;
+}
+
+static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy)
+{
+	FLAC__byte *save;
+
+	save = dest->entry;
+
+	if (src->entry != NULL) {
+		if (copy) {
+			/* do the copy first so that if we fail we leave the dest object untouched */
+			if (!copy_vcentry_(dest, src))
+				return false;
+		}
+		else {
+			/* we have to make sure that the string we're taking over is null-terminated */
+
+			/*
+			 * Stripping the const from src->entry is OK since we're taking
+			 * ownership of the pointer.  This is a hack around a deficiency
+			 * in the API where the same function is used for 'copy' and
+			 * 'own', but the source entry is a const pointer.  If we were
+			 * precise, the 'own' flavor would be a separate function with a
+			 * non-const source pointer.  But it's not, so we hack away.
+			 */
+			if (!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length))
+				return false;
+			*dest = *src;
+		}
+	}
+	else {
+		/* the src is null */
+		*dest = *src;
+	}
+
+	free(save);
+
+	vorbiscomment_calculate_length_(object);
+	return true;
+}
+
+static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, uint32_t offset, const char *field_name, uint32_t field_name_length)
+{
+	uint32_t i;
+
+	for (i = offset; i < object->data.vorbis_comment.num_comments; i++) {
+		if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length))
+			return (int)i;
+	}
+
+	return -1;
+}
+
+static void cuesheet_calculate_length_(FLAC__StreamMetadata *object)
+{
+	uint32_t i;
+
+	object->length = (
+		FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN
+	) / 8;
+
+	object->length += object->data.cue_sheet.num_tracks * (
+		FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN +
+		FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN
+	) / 8;
+
+	for (i = 0; i < object->data.cue_sheet.num_tracks; i++) {
+		object->length += object->data.cue_sheet.tracks[i].num_indices * (
+			FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN +
+			FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN +
+			FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN
+		) / 8;
+	}
+}
+
+static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(uint32_t num_indices)
+{
+	return safe_calloc_(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index));
+}
+
+static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(uint32_t num_tracks)
+{
+	return safe_calloc_(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track));
+}
+
+static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, uint32_t num_tracks)
+{
+	uint32_t i;
+
+	for (i = 0; i < num_tracks; i++) {
+		if (object_array[i].indices != 0) {
+			free(object_array[i].indices);
+		}
+	}
+
+	free(object_array);
+}
+
+static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, uint32_t num_tracks)
+{
+	FLAC__StreamMetadata_CueSheet_Track *return_array;
+
+	return_array = cuesheet_track_array_new_(num_tracks);
+
+	if (return_array != NULL) {
+		uint32_t i;
+
+		for (i = 0; i < num_tracks; i++) {
+			if (!copy_track_(return_array+i, object_array+i)) {
+				cuesheet_track_array_delete_(return_array, num_tracks);
+				return 0;
+			}
+		}
+	}
+
+	return return_array;
+}
+
+static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy)
+{
+	FLAC__StreamMetadata_CueSheet_Index *save;
+
+	save = dest->indices;
+
+	/* do the copy first so that if we fail we leave the object untouched */
+	if (copy) {
+		if (!copy_track_(dest, src))
+			return false;
+	}
+	else {
+		*dest = *src;
+	}
+
+	free(save);
+
+	cuesheet_calculate_length_(object);
+	return true;
+}
+
+
+/****************************************************************************
+ *
+ * Metadata object routines
+ *
+ ***************************************************************************/
+
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type)
+{
+	FLAC__StreamMetadata *object;
+
+	if (type > FLAC__MAX_METADATA_TYPE)
+		return 0;
+
+	object = calloc(1, sizeof(FLAC__StreamMetadata));
+	if (object != NULL) {
+		object->is_last = false;
+		object->type = type;
+		switch(type) {
+			case FLAC__METADATA_TYPE_STREAMINFO:
+				object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
+				break;
+			case FLAC__METADATA_TYPE_PADDING:
+				/* calloc() took care of this for us:
+				object->length = 0;
+				*/
+				break;
+			case FLAC__METADATA_TYPE_APPLICATION:
+				object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
+				/* calloc() took care of this for us:
+				object->data.application.data = 0;
+				*/
+				break;
+			case FLAC__METADATA_TYPE_SEEKTABLE:
+				/* calloc() took care of this for us:
+				object->length = 0;
+				object->data.seek_table.num_points = 0;
+				object->data.seek_table.points = 0;
+				*/
+				break;
+			case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+				object->data.vorbis_comment.vendor_string.length = (uint32_t)strlen(FLAC__VENDOR_STRING);
+				if (!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) {
+					free(object);
+					return 0;
+				}
+				vorbiscomment_calculate_length_(object);
+				break;
+			case FLAC__METADATA_TYPE_CUESHEET:
+				cuesheet_calculate_length_(object);
+				break;
+			case FLAC__METADATA_TYPE_PICTURE:
+				object->length = (
+					FLAC__STREAM_METADATA_PICTURE_TYPE_LEN +
+					FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* empty mime_type string */
+					FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* empty description string */
+					FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN +
+					FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN +
+					FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN +
+					FLAC__STREAM_METADATA_PICTURE_COLORS_LEN +
+					FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN +
+					0 /* no data */
+				) / 8;
+				object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER;
+				object->data.picture.mime_type = 0;
+				object->data.picture.description = 0;
+				/* calloc() took care of this for us:
+				object->data.picture.width = 0;
+				object->data.picture.height = 0;
+				object->data.picture.depth = 0;
+				object->data.picture.colors = 0;
+				object->data.picture.data_length = 0;
+				object->data.picture.data = 0;
+				*/
+				/* now initialize mime_type and description with empty strings to make things easier on the client */
+				if (!copy_cstring_(&object->data.picture.mime_type, "")) {
+					free(object);
+					return 0;
+				}
+				if (!copy_cstring_((char**)(&object->data.picture.description), "")) {
+					free(object->data.picture.mime_type);
+					free(object);
+					return 0;
+				}
+				break;
+			default:
+				/* calloc() took care of this for us:
+				object->length = 0;
+				object->data.unknown.data = 0;
+				*/
+				break;
+		}
+	}
+
+	return object;
+}
+
+FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
+{
+	FLAC__StreamMetadata *to;
+
+	if ((to = FLAC__metadata_object_new(object->type)) != NULL) {
+		to->is_last = object->is_last;
+		to->type = object->type;
+		to->length = object->length;
+		switch(to->type) {
+			case FLAC__METADATA_TYPE_STREAMINFO:
+				memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
+				break;
+			case FLAC__METADATA_TYPE_PADDING:
+				break;
+			case FLAC__METADATA_TYPE_APPLICATION:
+				if (to->length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8) { /* underflow check */
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
+				if (!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				break;
+			case FLAC__METADATA_TYPE_SEEKTABLE:
+				to->data.seek_table.num_points = object->data.seek_table.num_points;
+				if (to->data.seek_table.num_points > UINT32_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) { /* overflow check */
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				if (!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint))) {
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				break;
+			case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+				if (to->data.vorbis_comment.vendor_string.entry != NULL) {
+					free(to->data.vorbis_comment.vendor_string.entry);
+					to->data.vorbis_comment.vendor_string.entry = 0;
+				}
+				if (!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				if (object->data.vorbis_comment.num_comments == 0) {
+					to->data.vorbis_comment.comments = 0;
+				}
+				else {
+					to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
+					if (to->data.vorbis_comment.comments == NULL) {
+						to->data.vorbis_comment.num_comments = 0;
+						FLAC__metadata_object_delete(to);
+						return 0;
+					}
+				}
+				to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
+				break;
+			case FLAC__METADATA_TYPE_CUESHEET:
+				memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
+				if (object->data.cue_sheet.num_tracks == 0) {
+				}
+				else {
+					to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
+					if (to->data.cue_sheet.tracks == NULL) {
+						FLAC__metadata_object_delete(to);
+						return 0;
+					}
+				}
+				break;
+			case FLAC__METADATA_TYPE_PICTURE:
+				to->data.picture.type = object->data.picture.type;
+				if (!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) {
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				if (!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) {
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				to->data.picture.width = object->data.picture.width;
+				to->data.picture.height = object->data.picture.height;
+				to->data.picture.depth = object->data.picture.depth;
+				to->data.picture.colors = object->data.picture.colors;
+				to->data.picture.data_length = object->data.picture.data_length;
+				if (!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) {
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				break;
+			default:
+				if (!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) {
+					FLAC__metadata_object_delete(to);
+					return 0;
+				}
+				break;
+		}
+	}
+
+	return to;
+}
+
+void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
+{
+	switch(object->type) {
+		case FLAC__METADATA_TYPE_STREAMINFO:
+		case FLAC__METADATA_TYPE_PADDING:
+			break;
+		case FLAC__METADATA_TYPE_APPLICATION:
+			if (object->data.application.data != NULL) {
+				free(object->data.application.data);
+				object->data.application.data = NULL;
+			}
+			break;
+		case FLAC__METADATA_TYPE_SEEKTABLE:
+			if (object->data.seek_table.points != NULL) {
+				free(object->data.seek_table.points);
+				object->data.seek_table.points = NULL;
+			}
+			break;
+		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+			if (object->data.vorbis_comment.vendor_string.entry != NULL) {
+				free(object->data.vorbis_comment.vendor_string.entry);
+				object->data.vorbis_comment.vendor_string.entry = 0;
+			}
+			if (object->data.vorbis_comment.comments != NULL) {
+				vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
+				object->data.vorbis_comment.comments = NULL;
+				object->data.vorbis_comment.num_comments = 0;
+			}
+			break;
+		case FLAC__METADATA_TYPE_CUESHEET:
+			if (object->data.cue_sheet.tracks != NULL) {
+				cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
+				object->data.cue_sheet.tracks = NULL;
+				object->data.cue_sheet.num_tracks = 0;
+			}
+			break;
+		case FLAC__METADATA_TYPE_PICTURE:
+			if (object->data.picture.mime_type != NULL) {
+				free(object->data.picture.mime_type);
+				object->data.picture.mime_type = NULL;
+			}
+			if (object->data.picture.description != NULL) {
+				free(object->data.picture.description);
+				object->data.picture.description = NULL;
+			}
+			if (object->data.picture.data != NULL) {
+				free(object->data.picture.data);
+				object->data.picture.data = NULL;
+			}
+			break;
+		default:
+			if (object->data.unknown.data != NULL) {
+				free(object->data.unknown.data);
+				object->data.unknown.data = NULL;
+			}
+			break;
+	}
+}
+
+FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
+{
+	FLAC__metadata_object_delete_data(object);
+	free(object);
+}
+
+static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
+{
+	if (block1->min_blocksize != block2->min_blocksize)
+		return false;
+	if (block1->max_blocksize != block2->max_blocksize)
+		return false;
+	if (block1->min_framesize != block2->min_framesize)
+		return false;
+	if (block1->max_framesize != block2->max_framesize)
+		return false;
+	if (block1->sample_rate != block2->sample_rate)
+		return false;
+	if (block1->channels != block2->channels)
+		return false;
+	if (block1->bits_per_sample != block2->bits_per_sample)
+		return false;
+	if (block1->total_samples != block2->total_samples)
+		return false;
+	if (memcmp(block1->md5sum, block2->md5sum, 16) != 0)
+		return false;
+	return true;
+}
+
+static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, uint32_t block_length)
+{
+	if (memcmp(block1->id, block2->id, sizeof(block1->id)) != 0)
+		return false;
+	if (block1->data != NULL && block2->data != NULL)
+		return memcmp(block1->data, block2->data, block_length - sizeof(block1->id)) == 0;
+	else
+		return block1->data == block2->data;
+}
+
+static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
+{
+	uint32_t i;
+
+	if (block1->num_points != block2->num_points)
+		return false;
+
+	if (block1->points != NULL && block2->points != NULL) {
+		for (i = 0; i < block1->num_points; i++) {
+			if (block1->points[i].sample_number != block2->points[i].sample_number)
+				return false;
+			if (block1->points[i].stream_offset != block2->points[i].stream_offset)
+				return false;
+			if (block1->points[i].frame_samples != block2->points[i].frame_samples)
+				return false;
+		}
+		return true;
+	}
+	else
+		return block1->points == block2->points;
+}
+
+static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
+{
+	uint32_t i;
+
+	if (block1->vendor_string.length != block2->vendor_string.length)
+		return false;
+
+	if (block1->vendor_string.entry != NULL && block2->vendor_string.entry != NULL) {
+		if (memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length) != 0)
+			return false;
+	}
+	else if (block1->vendor_string.entry != block2->vendor_string.entry)
+		return false;
+
+	if (block1->num_comments != block2->num_comments)
+		return false;
+
+	for (i = 0; i < block1->num_comments; i++) {
+		if (block1->comments[i].entry != NULL && block2->comments[i].entry != NULL) {
+			if (memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length) != 0)
+				return false;
+		}
+		else if (block1->comments[i].entry != block2->comments[i].entry)
+			return false;
+	}
+	return true;
+}
+
+static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
+{
+	uint32_t i, j;
+
+	if (strcmp(block1->media_catalog_number, block2->media_catalog_number) != 0)
+		return false;
+
+	if (block1->lead_in != block2->lead_in)
+		return false;
+
+	if (block1->is_cd != block2->is_cd)
+		return false;
+
+	if (block1->num_tracks != block2->num_tracks)
+		return false;
+
+	if (block1->tracks != NULL && block2->tracks != NULL) {
+		for (i = 0; i < block1->num_tracks; i++) {
+			if (block1->tracks[i].offset != block2->tracks[i].offset)
+				return false;
+			if (block1->tracks[i].number != block2->tracks[i].number)
+				return false;
+			if (memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)) != 0)
+				return false;
+			if (block1->tracks[i].type != block2->tracks[i].type)
+				return false;
+			if (block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
+				return false;
+			if (block1->tracks[i].num_indices != block2->tracks[i].num_indices)
+				return false;
+			if (block1->tracks[i].indices != NULL && block2->tracks[i].indices != NULL) {
+				for (j = 0; j < block1->tracks[i].num_indices; j++) {
+					if (block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
+						return false;
+					if (block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
+						return false;
+				}
+			}
+			else if (block1->tracks[i].indices != block2->tracks[i].indices)
+				return false;
+		}
+	}
+	else if (block1->tracks != block2->tracks)
+		return false;
+	return true;
+}
+
+static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2)
+{
+	if (block1->type != block2->type)
+		return false;
+	if (block1->mime_type != block2->mime_type && (block1->mime_type == 0 || block2->mime_type == 0 || strcmp(block1->mime_type, block2->mime_type)))
+		return false;
+	if (block1->description != block2->description && (block1->description == 0 || block2->description == 0 || strcmp((const char *)block1->description, (const char *)block2->description)))
+		return false;
+	if (block1->width != block2->width)
+		return false;
+	if (block1->height != block2->height)
+		return false;
+	if (block1->depth != block2->depth)
+		return false;
+	if (block1->colors != block2->colors)
+		return false;
+	if (block1->data_length != block2->data_length)
+		return false;
+	if (block1->data != block2->data && (block1->data == NULL || block2->data == NULL || memcmp(block1->data, block2->data, block1->data_length)))
+		return false;
+	return true;
+}
+
+static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, uint32_t block_length)
+{
+	if (block1->data != NULL && block2->data != NULL)
+		return memcmp(block1->data, block2->data, block_length) == 0;
+	else
+		return block1->data == block2->data;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
+{
+	if (block1->type != block2->type) {
+		return false;
+	}
+	if (block1->is_last != block2->is_last) {
+		return false;
+	}
+	if (block1->length != block2->length) {
+		return false;
+	}
+	switch(block1->type) {
+		case FLAC__METADATA_TYPE_STREAMINFO:
+			return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
+		case FLAC__METADATA_TYPE_PADDING:
+			return true; /* we don't compare the padding guts */
+		case FLAC__METADATA_TYPE_APPLICATION:
+			return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
+		case FLAC__METADATA_TYPE_SEEKTABLE:
+			return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
+		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+			return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
+		case FLAC__METADATA_TYPE_CUESHEET:
+			return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
+		case FLAC__METADATA_TYPE_PICTURE:
+			return compare_block_data_picture_(&block1->data.picture, &block2->data.picture);
+		default:
+			return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length);
+	}
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, uint32_t length, FLAC__bool copy)
+{
+	FLAC__byte *save;
+
+	save = object->data.application.data;
+
+	/* do the copy first so that if we fail we leave the object untouched */
+	if (copy) {
+		if (!copy_bytes_(&object->data.application.data, data, length))
+			return false;
+	}
+	else {
+		object->data.application.data = data;
+	}
+
+	free(save);
+
+	object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, uint32_t new_num_points)
+{
+	if (object->data.seek_table.points == 0) {
+		if (new_num_points == 0)
+			return true;
+		else if ((object->data.seek_table.points = seekpoint_array_new_(new_num_points)) == 0)
+			return false;
+	}
+	else {
+		const size_t old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
+		const size_t new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
+
+		/* overflow check */
+		if (new_num_points > UINT32_MAX / sizeof(FLAC__StreamMetadata_SeekPoint))
+			return false;
+
+		if (new_size == 0) {
+			free(object->data.seek_table.points);
+			object->data.seek_table.points = 0;
+		}
+		else if ((object->data.seek_table.points = safe_realloc_(object->data.seek_table.points, new_size)) == NULL)
+			return false;
+
+		/* if growing, set new elements to placeholders */
+		if (new_size > old_size) {
+			uint32_t i;
+			for (i = object->data.seek_table.num_points; i < new_num_points; i++) {
+				object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
+				object->data.seek_table.points[i].stream_offset = 0;
+				object->data.seek_table.points[i].frame_samples = 0;
+			}
+		}
+	}
+
+	object->data.seek_table.num_points = new_num_points;
+
+	seektable_calculate_length_(object);
+	return true;
+}
+
+FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point)
+{
+	object->data.seek_table.points[point_num] = point;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point)
+{
+	int i;
+
+	if (!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
+		return false;
+
+	/* move all points >= point_num forward one space */
+	for (i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
+		object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
+
+	FLAC__metadata_object_seektable_set_point(object, point_num, point);
+	seektable_calculate_length_(object);
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, uint32_t point_num)
+{
+	uint32_t i;
+
+	/* move all points > point_num backward one space */
+	for (i = point_num; i < object->data.seek_table.num_points-1; i++)
+		object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
+
+	return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
+{
+	return FLAC__format_seektable_is_legal(&object->data.seek_table);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, uint32_t num)
+{
+	if (num > 0)
+		/* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
+		return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
+	else
+		return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
+{
+	FLAC__StreamMetadata_SeekTable *seek_table;
+
+	seek_table = &object->data.seek_table;
+
+	if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
+		return false;
+
+	seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
+	seek_table->points[seek_table->num_points - 1].stream_offset = 0;
+	seek_table->points[seek_table->num_points - 1].frame_samples = 0;
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], uint32_t num)
+{
+	if (num > 0) {
+		FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
+		uint32_t i, j;
+
+		i = seek_table->num_points;
+
+		if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
+			return false;
+
+		for (j = 0; j < num; i++, j++) {
+			seek_table->points[i].sample_number = sample_numbers[j];
+			seek_table->points[i].stream_offset = 0;
+			seek_table->points[i].frame_samples = 0;
+		}
+	}
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, uint32_t num, FLAC__uint64 total_samples)
+{
+	if (num > 0 && total_samples > 0) {
+		FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
+		uint32_t i, j;
+
+		i = seek_table->num_points;
+
+		if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
+			return false;
+
+		for (j = 0; j < num; i++, j++) {
+			seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
+			seek_table->points[i].stream_offset = 0;
+			seek_table->points[i].frame_samples = 0;
+		}
+	}
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, uint32_t samples, FLAC__uint64 total_samples)
+{
+	if (samples > 0 && total_samples > 0) {
+		FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
+		uint32_t i, j;
+		FLAC__uint64 num, sample;
+
+		num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */
+		/* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */
+		if (total_samples % samples == 0)
+			num--;
+
+		/* Put a strict upper bound on the number of allowed seek points. */
+		if (num > 32768) {
+			/* Set the bound and recalculate samples accordingly. */
+			num = 32768;
+			samples = (uint32_t)(total_samples / num);
+		}
+
+		i = seek_table->num_points;
+
+		if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + (uint32_t)num))
+			return false;
+
+		sample = 0;
+		for (j = 0; j < num; i++, j++, sample += samples) {
+			seek_table->points[i].sample_number = sample;
+			seek_table->points[i].stream_offset = 0;
+			seek_table->points[i].frame_samples = 0;
+		}
+	}
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
+{
+	uint32_t unique;
+
+	unique = FLAC__format_seektable_sort(&object->data.seek_table);
+
+	return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
+{
+	if (!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length))
+		return false;
+	return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, uint32_t new_num_comments)
+{
+	if (object->data.vorbis_comment.comments == NULL) {
+		if (new_num_comments == 0)
+			return true;
+		else if ((object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)) == NULL)
+			return false;
+	}
+	else {
+		const size_t old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
+		const size_t new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
+
+		/* overflow check */
+		if (new_num_comments > UINT32_MAX / sizeof(FLAC__StreamMetadata_VorbisComment_Entry))
+			return false;
+
+		/* if shrinking, free the truncated entries */
+		if (new_num_comments < object->data.vorbis_comment.num_comments) {
+			uint32_t i;
+			for (i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
+				if (object->data.vorbis_comment.comments[i].entry != NULL)
+					free(object->data.vorbis_comment.comments[i].entry);
+		}
+
+		if (new_size == 0) {
+			free(object->data.vorbis_comment.comments);
+			object->data.vorbis_comment.comments = 0;
+		}
+		else {
+			FLAC__StreamMetadata_VorbisComment_Entry *oldptr = object->data.vorbis_comment.comments;
+			if ((object->data.vorbis_comment.comments = realloc(object->data.vorbis_comment.comments, new_size)) == NULL) {
+				vorbiscomment_entry_array_delete_(oldptr, object->data.vorbis_comment.num_comments);
+				object->data.vorbis_comment.num_comments = 0;
+				return false;
+			}
+		}
+
+		/* if growing, zero all the length/pointers of new elements */
+		if (new_size > old_size)
+			memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
+	}
+
+	object->data.vorbis_comment.num_comments = new_num_comments;
+
+	vorbiscomment_calculate_length_(object);
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
+{
+	if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
+		return false;
+	return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
+{
+	FLAC__StreamMetadata_VorbisComment *vc;
+
+	if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
+		return false;
+
+	vc = &object->data.vorbis_comment;
+
+	if (!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
+		return false;
+
+	/* move all comments >= comment_num forward one space */
+	memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
+	vc->comments[comment_num].length = 0;
+	vc->comments[comment_num].entry = 0;
+
+	return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
+{
+	return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy)
+{
+	if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
+		return false;
+
+	{
+		int i;
+		size_t field_name_length;
+		const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
+
+		if (eq == NULL)
+			return false; /* double protection */
+
+		field_name_length = eq-entry.entry;
+
+		i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, (uint32_t)field_name_length);
+		if (i >= 0) {
+			uint32_t indx = (uint32_t)i;
+			if (!FLAC__metadata_object_vorbiscomment_set_comment(object, indx, entry, copy))
+				return false;
+			entry = object->data.vorbis_comment.comments[indx];
+			indx++; /* skip over replaced comment */
+			if (all && indx < object->data.vorbis_comment.num_comments) {
+				i = vorbiscomment_find_entry_from_(object, indx, (const char *)entry.entry, (uint32_t)field_name_length);
+				while (i >= 0) {
+					indx = (uint32_t)i;
+					if (!FLAC__metadata_object_vorbiscomment_delete_comment(object, indx))
+						return false;
+					if (indx < object->data.vorbis_comment.num_comments)
+						i = vorbiscomment_find_entry_from_(object, indx, (const char *)entry.entry, (uint32_t)field_name_length);
+					else
+						i = -1;
+				}
+			}
+			return true;
+		}
+		else
+			return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy);
+	}
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, uint32_t comment_num)
+{
+	FLAC__StreamMetadata_VorbisComment *vc;
+
+	vc = &object->data.vorbis_comment;
+
+	/* free the comment at comment_num */
+	free(vc->comments[comment_num].entry);
+
+	/* move all comments > comment_num backward one space */
+	memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
+	vc->comments[vc->num_comments-1].length = 0;
+	vc->comments[vc->num_comments-1].entry = 0;
+
+	return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value)
+{
+	if (!FLAC__format_vorbiscomment_entry_name_is_legal(field_name))
+		return false;
+	if (!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (uint32_t)(-1)))
+		return false;
+
+	{
+		const size_t nn = strlen(field_name);
+		const size_t nv = strlen(field_value);
+		entry->length = (FLAC__uint32)(nn + 1 /*=*/ + nv);
+		if ((entry->entry = safe_malloc_add_4op_(nn, /*+*/1, /*+*/nv, /*+*/1)) == NULL)
+			return false;
+		memcpy(entry->entry, field_name, nn);
+		entry->entry[nn] = '=';
+		memcpy(entry->entry+nn+1, field_value, nv);
+		entry->entry[entry->length] = '\0';
+	}
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value)
+{
+	if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
+		return false;
+
+	{
+		const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
+		const size_t nn = eq-entry.entry;
+		const size_t nv = entry.length-nn-1; /* -1 for the '=' */
+
+		if (eq == NULL)
+			return false; /* double protection */
+		if ((*field_name = safe_malloc_add_2op_(nn, /*+*/1)) == NULL)
+			return false;
+		if ((*field_value = safe_malloc_add_2op_(nv, /*+*/1)) == NULL) {
+			free(*field_name);
+			return false;
+		}
+		memcpy(*field_name, entry.entry, nn);
+		memcpy(*field_value, entry.entry+nn+1, nv);
+		(*field_name)[nn] = '\0';
+		(*field_value)[nv] = '\0';
+	}
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, uint32_t field_name_length)
+{
+	const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
+	return (eq != NULL && (uint32_t)(eq-entry.entry) == field_name_length && FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length) == 0);
+}
+
+FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, uint32_t offset, const char *field_name)
+{
+	return vorbiscomment_find_entry_from_(object, offset, field_name, (uint32_t)strlen(field_name));
+}
+
+FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
+{
+	const uint32_t field_name_length = (const uint32_t)strlen(field_name);
+	uint32_t i;
+
+	for (i = 0; i < object->data.vorbis_comment.num_comments; i++) {
+		if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
+			if (!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
+				return -1;
+			else
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
+{
+	FLAC__bool ok = true;
+	uint32_t matching = 0;
+	const uint32_t field_name_length = (const uint32_t)strlen(field_name);
+	int i;
+
+	/* must delete from end to start otherwise it will interfere with our iteration */
+	for (i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
+		if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
+			matching++;
+			ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (uint32_t)i);
+		}
+	}
+
+	return ok? (int)matching : -1;
+}
+
+FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void)
+{
+	return calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track));
+}
+
+FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object)
+{
+	FLAC__StreamMetadata_CueSheet_Track *to;
+
+	if ((to = FLAC__metadata_object_cuesheet_track_new()) != NULL) {
+		if (!copy_track_(to, object)) {
+			FLAC__metadata_object_cuesheet_track_delete(to);
+			return 0;
+		}
+	}
+
+	return to;
+}
+
+void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object)
+{
+	if (object->indices != NULL) {
+		free(object->indices);
+	}
+}
+
+FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object)
+{
+	FLAC__metadata_object_cuesheet_track_delete_data(object);
+	free(object);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t new_num_indices)
+{
+	FLAC__StreamMetadata_CueSheet_Track *track;
+
+	track = &object->data.cue_sheet.tracks[track_num];
+
+	if (track->indices == NULL) {
+		if (new_num_indices == 0)
+			return true;
+		else if ((track->indices = cuesheet_track_index_array_new_(new_num_indices)) == NULL)
+			return false;
+	}
+	else {
+		const size_t old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
+		const size_t new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
+
+		/* overflow check */
+		if (new_num_indices > UINT32_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Index))
+			return false;
+
+		if (new_size == 0) {
+			free(track->indices);
+			track->indices = 0;
+		}
+		else if ((track->indices = safe_realloc_(track->indices, new_size)) == NULL)
+			return false;
+
+		/* if growing, zero all the lengths/pointers of new elements */
+		if (new_size > old_size)
+			memset(track->indices + track->num_indices, 0, new_size - old_size);
+	}
+
+	track->num_indices = (FLAC__byte)new_num_indices;
+
+	cuesheet_calculate_length_(object);
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num, FLAC__StreamMetadata_CueSheet_Index indx)
+{
+	FLAC__StreamMetadata_CueSheet_Track *track;
+
+	track = &object->data.cue_sheet.tracks[track_num];
+
+	if (!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
+		return false;
+
+	/* move all indices >= index_num forward one space */
+	memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
+
+	track->indices[index_num] = indx;
+	cuesheet_calculate_length_(object);
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num)
+{
+	FLAC__StreamMetadata_CueSheet_Index indx;
+	memset(&indx, 0, sizeof(indx));
+	return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, indx);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num)
+{
+	FLAC__StreamMetadata_CueSheet_Track *track;
+
+	track = &object->data.cue_sheet.tracks[track_num];
+
+	/* move all indices > index_num backward one space */
+	memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1));
+
+	FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
+	cuesheet_calculate_length_(object);
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, uint32_t new_num_tracks)
+{
+	if (object->data.cue_sheet.tracks == NULL) {
+		if (new_num_tracks == 0)
+			return true;
+		else if ((object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)) == NULL)
+			return false;
+	}
+	else {
+		const size_t old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
+		const size_t new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
+
+		/* overflow check */
+		if (new_num_tracks > UINT32_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Track))
+			return false;
+
+		/* if shrinking, free the truncated entries */
+		if (new_num_tracks < object->data.cue_sheet.num_tracks) {
+			uint32_t i;
+			for (i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
+				free(object->data.cue_sheet.tracks[i].indices);
+		}
+
+		if (new_size == 0) {
+			free(object->data.cue_sheet.tracks);
+			object->data.cue_sheet.tracks = 0;
+		}
+		else if ((object->data.cue_sheet.tracks = safe_realloc_(object->data.cue_sheet.tracks, new_size)) == NULL)
+			return false;
+
+		/* if growing, zero all the lengths/pointers of new elements */
+		if (new_size > old_size)
+			memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
+	}
+
+	object->data.cue_sheet.num_tracks = new_num_tracks;
+
+	cuesheet_calculate_length_(object);
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
+{
+	return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
+{
+	FLAC__StreamMetadata_CueSheet *cs;
+
+	cs = &object->data.cue_sheet;
+
+	if (!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
+		return false;
+
+	/* move all tracks >= track_num forward one space */
+	memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
+	cs->tracks[track_num].num_indices = 0;
+	cs->tracks[track_num].indices = 0;
+
+	return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, uint32_t track_num)
+{
+	FLAC__StreamMetadata_CueSheet_Track track;
+	memset(&track, 0, sizeof(track));
+	return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, uint32_t track_num)
+{
+	FLAC__StreamMetadata_CueSheet *cs;
+
+	cs = &object->data.cue_sheet;
+
+	/* free the track at track_num */
+	free(cs->tracks[track_num].indices);
+
+	/* move all tracks > track_num backward one space */
+	memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
+	cs->tracks[cs->num_tracks-1].num_indices = 0;
+	cs->tracks[cs->num_tracks-1].indices = 0;
+
+	return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
+{
+	return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
+}
+
+static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, uint32_t track)
+{
+	if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1)
+		return 0;
+	else if (cs->tracks[track].indices[0].number == 1)
+		return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in;
+	else if (cs->tracks[track].num_indices < 2)
+		return 0;
+	else if (cs->tracks[track].indices[1].number == 1)
+		return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in;
+	else
+		return 0;
+}
+
+static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x)
+{
+	FLAC__uint32 n = 0;
+	while (x) {
+		n += (x%10);
+		x /= 10;
+	}
+	return n;
+}
+
+/*@@@@add to tests*/
+FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object)
+{
+	const FLAC__StreamMetadata_CueSheet *cs;
+
+	cs = &object->data.cue_sheet;
+
+	if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */
+		return 0;
+
+	{
+		FLAC__uint32 i, length, sum = 0;
+		for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */
+			sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100));
+		length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100);
+
+		return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1);
+	}
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy)
+{
+	char *old;
+	size_t old_length, new_length;
+
+	old = object->data.picture.mime_type;
+	old_length = old? strlen(old) : 0;
+	new_length = strlen(mime_type);
+
+	/* do the copy first so that if we fail we leave the object untouched */
+	if (copy) {
+		if (new_length >= SIZE_MAX) /* overflow check */
+			return false;
+		if (!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, (uint32_t)(new_length+1)))
+			return false;
+	}
+	else {
+		object->data.picture.mime_type = mime_type;
+	}
+
+	free(old);
+
+	object->length -= (uint32_t)old_length;
+	object->length += (uint32_t)new_length;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy)
+{
+	FLAC__byte *old;
+	size_t old_length, new_length;
+
+	old = object->data.picture.description;
+	old_length = old? strlen((const char *)old) : 0;
+	new_length = strlen((const char *)description);
+
+	/* do the copy first so that if we fail we leave the object untouched */
+	if (copy) {
+		if (new_length >= SIZE_MAX) /* overflow check */
+			return false;
+		if (!copy_bytes_(&object->data.picture.description, description, (uint32_t)(new_length+1)))
+			return false;
+	}
+	else {
+		object->data.picture.description = description;
+	}
+
+	free(old);
+
+	object->length -= (uint32_t)old_length;
+	object->length += (uint32_t)new_length;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy)
+{
+	FLAC__byte *old;
+
+	old = object->data.picture.data;
+
+	/* do the copy first so that if we fail we leave the object untouched */
+	if (copy) {
+		if (!copy_bytes_(&object->data.picture.data, data, length))
+			return false;
+	}
+	else {
+		object->data.picture.data = data;
+	}
+
+	free(old);
+
+	object->length -= object->data.picture.data_length;
+	object->data.picture.data_length = length;
+	object->length += length;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation)
+{
+	return FLAC__format_picture_is_legal(&object->data.picture, violation);
+}
--- /dev/null
+++ b/src/libflac/private/bitmath.h
@@ -1,0 +1,205 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__BITMATH_H
+#define FLAC__PRIVATE__BITMATH_H
+
+#include "../FLAC/ordinals.h"
+
+#include "../share/compat.h"
+
+#if defined(_MSC_VER)
+#include <intrin.h> /* for _BitScanReverse* */
+#endif
+
+/* Will never be emitted for MSVC, GCC, Intel compilers */
+static inline uint32_t FLAC__clz_soft_uint32(FLAC__uint32 word)
+{
+	static const uint8_t byte_to_unary_table[] = {
+	8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	};
+
+	return word > 0xffffff ? byte_to_unary_table[word >> 24] :
+		word > 0xffff ? byte_to_unary_table[word >> 16] + 8 :
+		word > 0xff ? byte_to_unary_table[word >> 8] + 16 :
+		byte_to_unary_table[word] + 24;
+}
+
+static inline uint32_t FLAC__clz_uint32(FLAC__uint32 v)
+{
+/* Never used with input 0 */
+#if defined(__INTEL_COMPILER)
+	return _bit_scan_reverse(v) ^ 31U;
+#elif defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+/* This will translate either to (bsr ^ 31U), clz , ctlz, cntlz, lzcnt depending on
+ * -march= setting or to a software routine in exotic machines. */
+	return __builtin_clz(v);
+#elif defined(_MSC_VER)
+	{
+		DWORD idx;
+		_BitScanReverse(&idx, v);
+		return (uint32_t)(idx ^ 31U);
+	}
+#else
+	return FLAC__clz_soft_uint32(v);
+#endif
+}
+
+/* Used when 64-bit bsr/clz is unavailable; can use 32-bit bsr/clz when possible */
+static inline uint32_t FLAC__clz_soft_uint64(FLAC__uint64 word)
+{
+	return (FLAC__uint32)(word>>32) ? FLAC__clz_uint32((FLAC__uint32)(word>>32)) :
+		FLAC__clz_uint32((FLAC__uint32)word) + 32;
+}
+
+static inline uint32_t FLAC__clz_uint64(FLAC__uint64 v)
+{
+	/* Never used with input 0 */
+#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+	return __builtin_clzll(v);
+#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64))
+	{
+		DWORD idx;
+		_BitScanReverse64(&idx, v);
+		return (uint32_t)(idx ^ 63U);
+	}
+#else
+	return FLAC__clz_soft_uint64(v);
+#endif
+}
+
+/* These two functions work with input 0 */
+static inline uint32_t FLAC__clz2_uint32(FLAC__uint32 v)
+{
+	if (!v)
+		return 32;
+	return FLAC__clz_uint32(v);
+}
+
+static inline uint32_t FLAC__clz2_uint64(FLAC__uint64 v)
+{
+	if (!v)
+		return 64;
+	return FLAC__clz_uint64(v);
+}
+
+/* An example of what FLAC__bitmath_ilog2() computes:
+ *
+ * ilog2( 0) = assertion failure
+ * ilog2( 1) = 0
+ * ilog2( 2) = 1
+ * ilog2( 3) = 1
+ * ilog2( 4) = 2
+ * ilog2( 5) = 2
+ * ilog2( 6) = 2
+ * ilog2( 7) = 2
+ * ilog2( 8) = 3
+ * ilog2( 9) = 3
+ * ilog2(10) = 3
+ * ilog2(11) = 3
+ * ilog2(12) = 3
+ * ilog2(13) = 3
+ * ilog2(14) = 3
+ * ilog2(15) = 3
+ * ilog2(16) = 4
+ * ilog2(17) = 4
+ * ilog2(18) = 4
+ */
+
+static inline uint32_t FLAC__bitmath_ilog2(FLAC__uint32 v)
+{
+#if defined(__INTEL_COMPILER)
+	return _bit_scan_reverse(v);
+#elif defined(_MSC_VER)
+	{
+		DWORD idx;
+		_BitScanReverse(&idx, v);
+		return (uint32_t)idx;
+	}
+#else
+	return FLAC__clz_uint32(v) ^ 31U;
+#endif
+}
+
+static inline uint32_t FLAC__bitmath_ilog2_wide(FLAC__uint64 v)
+{
+#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+	return __builtin_clzll(v) ^ 63U;
+/* Sorry, only supported in x64/Itanium.. and both have fast FPU which makes integer-only encoder pointless */
+#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64))
+	{
+		DWORD idx;
+		_BitScanReverse64(&idx, v);
+		return (uint32_t)idx;
+	}
+#else
+/*  Brain-damaged compilers will use the fastest possible way that is,
+	de Bruijn sequences (http://supertech.csail.mit.edu/papers/debruijn.pdf)
+	(C) Timothy B. Terriberry ([email protected]) 2001-2009 CC0 (Public domain).
+*/
+	{
+		static const uint8_t DEBRUIJN_IDX64[64]={
+			0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40,
+			5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57,
+			63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56,
+			62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58
+		};
+		v|= v>>1;
+		v|= v>>2;
+		v|= v>>4;
+		v|= v>>8;
+		v|= v>>16;
+		v|= v>>32;
+		v= (v>>1)+1;
+		return DEBRUIJN_IDX64[v*FLAC__U64L(0x218A392CD3D5DBF)>>58&0x3F];
+	}
+#endif
+}
+
+uint32_t FLAC__bitmath_silog2(FLAC__int64 v);
+
+#endif
--- /dev/null
+++ b/src/libflac/private/bitreader.h
@@ -1,0 +1,90 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__BITREADER_H
+#define FLAC__PRIVATE__BITREADER_H
+
+#include <stdio.h> /* for FILE */
+#include "../FLAC/ordinals.h"
+
+/*
+ * opaque structure definition
+ */
+struct FLAC__BitReader;
+typedef struct FLAC__BitReader FLAC__BitReader;
+
+typedef FLAC__bool (*FLAC__BitReaderReadCallback)(FLAC__byte buffer[], size_t *bytes, void *client_data);
+
+/*
+ * construction, deletion, initialization, etc functions
+ */
+FLAC__BitReader *FLAC__bitreader_new(void);
+void FLAC__bitreader_delete(FLAC__BitReader *br);
+FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd);
+void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */
+FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br);
+void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out);
+
+/*
+ * CRC functions
+ */
+void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed);
+FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br);
+
+/*
+ * info functions
+ */
+FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br);
+uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br);
+uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br);
+
+/*
+ * read functions
+ */
+
+FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, uint32_t bits);
+FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, uint32_t bits);
+FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, uint32_t bits);
+FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/
+FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, uint32_t bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */
+FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, uint32_t nvals); /* WATCHOUT: does not CRC the read data! */
+FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, uint32_t nvals); /* WATCHOUT: does not CRC the read data! */
+FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, uint32_t *val);
+FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, uint32_t parameter);
+FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter);
+#if 0 /* UNUSED */
+FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uint32_t parameter);
+FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, uint32_t *val, uint32_t parameter);
+#endif
+FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, uint32_t *rawlen);
+FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, uint32_t *rawlen);
+#endif
--- /dev/null
+++ b/src/libflac/private/crc.h
@@ -1,0 +1,60 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2018  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__CRC_H
+#define FLAC__PRIVATE__CRC_H
+
+#include "../FLAC/ordinals.h"
+
+/* 8 bit CRC generator, MSB shifted first
+** polynomial = x^8 + x^2 + x^1 + x^0
+** init = 0
+*/
+FLAC__uint8 FLAC__crc8(const FLAC__byte *data, uint32_t len);
+
+/* 16 bit CRC generator, MSB shifted first
+** polynomial = x^16 + x^15 + x^2 + x^0
+** init = 0
+*/
+extern FLAC__uint16 const FLAC__crc16_table[8][256];
+
+#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[0][((crc)>>8) ^ (data)])
+/* this alternate may be faster on some systems/compilers */
+#if 0
+#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[0][((crc)>>8) ^ (data)]) & 0xffff)
+#endif
+
+FLAC__uint16 FLAC__crc16(const FLAC__byte *data, uint32_t len);
+FLAC__uint16 FLAC__crc16_update_words32(const FLAC__uint32 *words, uint32_t len, FLAC__uint16 crc);
+FLAC__uint16 FLAC__crc16_update_words64(const FLAC__uint64 *words, uint32_t len, FLAC__uint16 crc);
+
+#endif
--- /dev/null
+++ b/src/libflac/private/fixed.h
@@ -1,0 +1,82 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__FIXED_H
+#define FLAC__PRIVATE__FIXED_H
+
+#include "../private/float.h"
+#include "../FLAC/format.h"
+
+/*
+ *	FLAC__fixed_compute_best_predictor()
+ *	--------------------------------------------------------------------
+ *	Compute the best fixed predictor and the expected bits-per-sample
+ *  of the residual signal for each order.  The _wide() version uses
+ *  64-bit integers which is statistically necessary when bits-per-
+ *  sample + log2(blocksize) > 30
+ *
+ *	IN data[0,data_len-1]
+ *	IN data_len
+ *	OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER]
+ */
+uint32_t FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]);
+uint32_t FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]);
+
+/*
+ *	FLAC__fixed_compute_residual()
+ *	--------------------------------------------------------------------
+ *	Compute the residual signal obtained from sutracting the predicted
+ *	signal from the original.
+ *
+ *	IN data[-order,data_len-1]        original signal (NOTE THE INDICES!)
+ *	IN data_len                       length of original signal
+ *	IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order
+ *	OUT residual[0,data_len-1]        residual signal
+ */
+void FLAC__fixed_compute_residual(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]);
+
+/*
+ *	FLAC__fixed_restore_signal()
+ *	--------------------------------------------------------------------
+ *	Restore the original signal by summing the residual and the
+ *	predictor.
+ *
+ *	IN residual[0,data_len-1]         residual signal
+ *	IN data_len                       length of original signal
+ *	IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order
+ *	*** IMPORTANT: the caller must pass in the historical samples:
+ *	IN  data[-order,-1]               previously-reconstructed historical samples
+ *	OUT data[0,data_len-1]            original signal
+ */
+void FLAC__fixed_restore_signal(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[]);
+
+#endif
--- /dev/null
+++ b/src/libflac/private/float.h
@@ -1,0 +1,46 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2004-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__FLOAT_H
+#define FLAC__PRIVATE__FLOAT_H
+
+#include "../FLAC/ordinals.h"
+
+/*
+ * FLAC__real is the basic floating point type used in LPC analysis.
+ *
+ * WATCHOUT: changing FLAC__real will change the signatures of many
+ * functions that have assembly language equivalents and break them.
+ */
+typedef float FLAC__real;
+
+#endif
--- /dev/null
+++ b/src/libflac/private/format.h
@@ -1,0 +1,45 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__FORMAT_H
+#define FLAC__PRIVATE__FORMAT_H
+
+#include "../FLAC/format.h"
+
+uint32_t FLAC__format_get_max_rice_partition_order(uint32_t blocksize, uint32_t predictor_order);
+uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize(uint32_t blocksize);
+uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(uint32_t limit, uint32_t blocksize, uint32_t predictor_order);
+void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object);
+void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object);
+FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, uint32_t max_partition_order);
+
+#endif
--- /dev/null
+++ b/src/libflac/private/lpc.h
@@ -1,0 +1,258 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__LPC_H
+#define FLAC__PRIVATE__LPC_H
+
+#include "../private/float.h"
+#include "../FLAC/format.h"
+
+#ifndef FLAC__INTEGER_ONLY_LIBRARY
+
+/*
+ *	FLAC__lpc_window_data()
+ *	--------------------------------------------------------------------
+ *	Applies the given window to the data.
+ *  OPT: asm implementation
+ *
+ *	IN in[0,data_len-1]
+ *	IN window[0,data_len-1]
+ *	OUT out[0,lag-1]
+ *	IN data_len
+ */
+void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len);
+
+/*
+ *	FLAC__lpc_compute_autocorrelation()
+ *	--------------------------------------------------------------------
+ *	Compute the autocorrelation for lags between 0 and lag-1.
+ *	Assumes data[] outside of [0,data_len-1] == 0.
+ *	Asserts that lag > 0.
+ *
+ *	IN data[0,data_len-1]
+ *	IN data_len
+ *	IN 0 < lag <= data_len
+ *	OUT autoc[0,lag-1]
+ */
+void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+#ifndef FLAC__NO_ASM
+#  ifdef FLAC__CPU_IA32
+#    ifdef FLAC__HAS_NASM
+void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_16_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+#    endif
+#  endif
+#  if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN
+#    ifdef FLAC__SSE_SUPPORTED
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+#    endif
+#  endif
+#if defined(FLAC__CPU_PPC64) && defined(FLAC__USE_VSX)
+#ifdef FLAC__HAS_TARGET_POWER9
+void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_4(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_8(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_12(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_16(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+#endif
+#ifdef FLAC__HAS_TARGET_POWER8
+void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_4(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_8(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_12(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_16(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]);
+#endif
+#endif
+#endif
+
+/*
+ *	FLAC__lpc_compute_lp_coefficients()
+ *	--------------------------------------------------------------------
+ *	Computes LP coefficients for orders 1..max_order.
+ *	Do not call if autoc[0] == 0.0.  This means the signal is zero
+ *	and there is no point in calculating a predictor.
+ *
+ *	IN autoc[0,max_order]                      autocorrelation values
+ *	IN 0 < max_order <= FLAC__MAX_LPC_ORDER    max LP order to compute
+ *	OUT lp_coeff[0,max_order-1][0,max_order-1] LP coefficients for each order
+ *	*** IMPORTANT:
+ *	*** lp_coeff[0,max_order-1][max_order,FLAC__MAX_LPC_ORDER-1] are untouched
+ *	OUT error[0,max_order-1]                   error for each order (more
+ *	                                           specifically, the variance of
+ *	                                           the error signal times # of
+ *	                                           samples in the signal)
+ *
+ *	Example: if max_order is 9, the LP coefficients for order 9 will be
+ *	         in lp_coeff[8][0,8], the LP coefficients for order 8 will be
+ *			 in lp_coeff[7][0,7], etc.
+ */
+void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], uint32_t *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], double error[]);
+
+/*
+ *	FLAC__lpc_quantize_coefficients()
+ *	--------------------------------------------------------------------
+ *	Quantizes the LP coefficients.  NOTE: precision + bits_per_sample
+ *	must be less than 32 (sizeof(FLAC__int32)*8).
+ *
+ *	IN lp_coeff[0,order-1]    LP coefficients
+ *	IN order                  LP order
+ *	IN FLAC__MIN_QLP_COEFF_PRECISION < precision
+ *	                          desired precision (in bits, including sign
+ *	                          bit) of largest coefficient
+ *	OUT qlp_coeff[0,order-1]  quantized coefficients
+ *	OUT shift                 # of bits to shift right to get approximated
+ *	                          LP coefficients.  NOTE: could be negative.
+ *	RETURN 0 => quantization OK
+ *	       1 => coefficients require too much shifting for *shift to
+ *              fit in the LPC subframe header.  'shift' is unset.
+ *         2 => coefficients are all zero, which is bad.  'shift' is
+ *              unset.
+ */
+int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], uint32_t order, uint32_t precision, FLAC__int32 qlp_coeff[], int *shift);
+
+/*
+ *	FLAC__lpc_compute_residual_from_qlp_coefficients()
+ *	--------------------------------------------------------------------
+ *	Compute the residual signal obtained from sutracting the predicted
+ *	signal from the original.
+ *
+ *	IN data[-order,data_len-1] original signal (NOTE THE INDICES!)
+ *	IN data_len                length of original signal
+ *	IN qlp_coeff[0,order-1]    quantized LP coefficients
+ *	IN order > 0               LP order
+ *	IN lp_quantization         quantization of LP coefficients in bits
+ *	OUT residual[0,data_len-1] residual signal
+ */
+void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+#ifndef FLAC__NO_ASM
+#  ifdef FLAC__CPU_IA32
+#    ifdef FLAC__HAS_NASM
+void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_asm_ia32(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+#    endif
+#  endif
+#  if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN
+#    ifdef FLAC__SSE2_SUPPORTED
+void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+#    endif
+#    ifdef FLAC__SSE4_1_SUPPORTED
+void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+#    endif
+#    ifdef FLAC__AVX2_SUPPORTED
+void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]);
+#    endif
+#  endif
+#endif
+
+#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */
+
+/*
+ *	FLAC__lpc_restore_signal()
+ *	--------------------------------------------------------------------
+ *	Restore the original signal by summing the residual and the
+ *	predictor.
+ *
+ *	IN residual[0,data_len-1]  residual signal
+ *	IN data_len                length of original signal
+ *	IN qlp_coeff[0,order-1]    quantized LP coefficients
+ *	IN order > 0               LP order
+ *	IN lp_quantization         quantization of LP coefficients in bits
+ *	*** IMPORTANT: the caller must pass in the historical samples:
+ *	IN  data[-order,-1]        previously-reconstructed historical samples
+ *	OUT data[0,data_len-1]     original signal
+ */
+void FLAC__lpc_restore_signal(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+#ifndef FLAC__NO_ASM
+#  ifdef FLAC__CPU_IA32
+#    ifdef FLAC__HAS_NASM
+void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+void FLAC__lpc_restore_signal_wide_asm_ia32(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+#    endif /* FLAC__HAS_NASM */
+#  endif /* FLAC__CPU_IA32 */
+#  if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN
+#    ifdef FLAC__SSE4_1_SUPPORTED
+void FLAC__lpc_restore_signal_intrin_sse41(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+void FLAC__lpc_restore_signal_16_intrin_sse41(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+void FLAC__lpc_restore_signal_wide_intrin_sse41(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+#    endif
+#  endif
+#endif /* FLAC__NO_ASM */
+
+#ifndef FLAC__INTEGER_ONLY_LIBRARY
+
+/*
+ *	FLAC__lpc_compute_expected_bits_per_residual_sample()
+ *	--------------------------------------------------------------------
+ *	Compute the expected number of bits per residual signal sample
+ *	based on the LP error (which is related to the residual variance).
+ *
+ *	IN lpc_error >= 0.0   error returned from calculating LP coefficients
+ *	IN total_samples > 0  # of samples in residual signal
+ *	RETURN                expected bits per sample
+ */
+double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, uint32_t total_samples);
+double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale);
+
+/*
+ *	FLAC__lpc_compute_best_order()
+ *	--------------------------------------------------------------------
+ *	Compute the best order from the array of signal errors returned
+ *	during coefficient computation.
+ *
+ *	IN lpc_error[0,max_order-1] >= 0.0  error returned from calculating LP coefficients
+ *	IN max_order > 0                    max LP order
+ *	IN total_samples > 0                # of samples in residual signal
+ *	IN overhead_bits_per_order          # of bits overhead for each increased LP order
+ *	                                    (includes warmup sample size and quantized LP coefficient)
+ *	RETURN [1,max_order]                best order
+ */
+uint32_t FLAC__lpc_compute_best_order(const double lpc_error[], uint32_t max_order, uint32_t total_samples, uint32_t overhead_bits_per_order);
+
+#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */
+
+#endif
--- /dev/null
+++ b/src/libflac/private/macros.h
@@ -1,0 +1,72 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2012-2016  Xiph.org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__MACROS_H
+#define FLAC__PRIVATE__MACROS_H
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+
+#define flac_max(a,b) \
+	({ __typeof__ (a) _a = (a); \
+	__typeof__ (b) _b = (b); \
+	_a > _b ? _a : _b; })
+
+#define MIN_PASTE(A,B) A##B
+#define MIN_IMPL(A,B,L) ({ \
+	__typeof__(A) MIN_PASTE(__a,L) = (A); \
+	__typeof__(B) MIN_PASTE(__b,L) = (B); \
+	MIN_PASTE(__a,L) < MIN_PASTE(__b,L) ? MIN_PASTE(__a,L) : MIN_PASTE(__b,L); \
+	})
+
+#define flac_min(A,B) MIN_IMPL(A,B,__COUNTER__)
+
+/* Whatever other unix that has sys/param.h */
+#elif defined(HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#define flac_max(a,b) MAX(a,b)
+#define flac_min(a,b) MIN(a,b)
+
+/* Windows VS has them in stdlib.h.. XXX:Untested */
+#elif defined(_MSC_VER)
+#include <stdlib.h>
+#define flac_max(a,b) __max(a,b)
+#define flac_min(a,b) __min(a,b)
+#endif
+
+#ifndef flac_min
+#define flac_min(x,y)	((x) <= (y) ? (x) : (y))
+#endif
+
+#ifndef flac_max
+#define flac_max(x,y)	((x) >= (y) ? (x) : (y))
+#endif
+
+#endif
--- /dev/null
+++ b/src/libflac/private/md5.h
@@ -1,0 +1,50 @@
+#ifndef FLAC__PRIVATE__MD5_H
+#define FLAC__PRIVATE__MD5_H
+
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h'
+ * header definitions; now uses stuff from dpkg's config.h
+ *  - Ian Jackson <[email protected]>.
+ * Still in the public domain.
+ *
+ * Josh Coalson: made some changes to integrate with libFLAC.
+ * Still in the public domain, with no warranty.
+ */
+
+#include "../FLAC/ordinals.h"
+
+typedef union {
+	FLAC__byte *p8;
+	FLAC__int16 *p16;
+	FLAC__int32 *p32;
+} FLAC__multibyte;
+
+typedef struct {
+	FLAC__uint32 in[16];
+	FLAC__uint32 buf[4];
+	FLAC__uint32 bytes[2];
+	FLAC__multibyte internal_buf;
+	size_t capacity;
+} FLAC__MD5Context;
+
+void FLAC__MD5Init(FLAC__MD5Context *context);
+void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context);
+
+FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample);
+
+#endif
--- /dev/null
+++ b/src/libflac/private/memory.h
@@ -1,0 +1,55 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__MEMORY_H
+#define FLAC__PRIVATE__MEMORY_H
+
+
+#include <stdlib.h> /* for size_t */
+
+#include "../private/float.h"
+#include "../FLAC/ordinals.h" /* for FLAC__bool */
+
+/* Returns the unaligned address returned by malloc.
+ * Use free() on this address to deallocate.
+ */
+void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address);
+FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer);
+FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer);
+FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer);
+FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, uint32_t **unaligned_pointer, uint32_t **aligned_pointer);
+#ifndef FLAC__INTEGER_ONLY_LIBRARY
+FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer);
+#endif
+void *safe_malloc_mul_2op_p(size_t size1, size_t size2);
+
+#endif
--- /dev/null
+++ b/src/libflac/private/metadata.h
@@ -1,0 +1,46 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2002-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__METADATA_H
+#define FLAC__PRIVATE__METADATA_H
+
+#include "../FLAC/metadata.h"
+
+/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not
+ * be a consistent state (e.g. PICTURE) or equivalent to the initial
+ * state after FLAC__metadata_object_new()
+ */
+void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object);
+
+void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object);
+
+#endif
--- /dev/null
+++ b/src/libflac/private/window.h
@@ -1,0 +1,70 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2006-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PRIVATE__WINDOW_H
+#define FLAC__PRIVATE__WINDOW_H
+
+#include "../private/float.h"
+#include "../FLAC/format.h"
+
+#ifndef FLAC__INTEGER_ONLY_LIBRARY
+
+/*
+ *	FLAC__window_*()
+ *	--------------------------------------------------------------------
+ *	Calculates window coefficients according to different apodization
+ *	functions.
+ *
+ *	OUT window[0,L-1]
+ *	IN L (number of points in window)
+ */
+void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev); /* 0.0 < stddev <= 0.5 */
+void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L);
+void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p);
+void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end);
+void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end);
+void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L);
+
+#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */
+
+#endif
--- /dev/null
+++ b/src/libflac/protected/stream_decoder.h
@@ -1,0 +1,60 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__PROTECTED__STREAM_DECODER_H
+#define FLAC__PROTECTED__STREAM_DECODER_H
+
+#include "../FLAC/stream_decoder.h"
+
+typedef struct FLAC__StreamDecoderProtected {
+	FLAC__StreamDecoderState state;
+	FLAC__StreamDecoderInitStatus initstate;
+	uint32_t channels;
+	FLAC__ChannelAssignment channel_assignment;
+	uint32_t bits_per_sample;
+	uint32_t sample_rate; /* in Hz */
+	uint32_t blocksize; /* in samples (per channel) */
+	FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */
+
+} FLAC__StreamDecoderProtected;
+
+/*
+ * return the number of input bytes consumed
+ */
+uint32_t FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder);
+
+/*
+ * return client_data from decoder
+ */
+FLAC_API void *get_client_data_from_decoder(FLAC__StreamDecoder *decoder);
+
+#endif
--- /dev/null
+++ b/src/libflac/share/alloc.h
@@ -1,0 +1,213 @@
+/* alloc - Convenience routines for safely allocating memory
+ * Copyright (C) 2007-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FLAC__SHARE__ALLOC_H
+#define FLAC__SHARE__ALLOC_H
+
+/* WATCHOUT: for c++ you may have to #define __STDC_LIMIT_MACROS 1 real early
+ * before #including this file,  otherwise SIZE_MAX might not be defined
+ */
+
+#include <limits.h> /* for SIZE_MAX */
+#include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */
+#include <stdlib.h> /* for size_t, malloc(), etc */
+#include "../share/compat.h"
+
+#ifndef SIZE_MAX
+# ifndef SIZE_T_MAX
+#  ifdef _MSC_VER
+#   ifdef _WIN64
+#    define SIZE_T_MAX FLAC__U64L(0xffffffffffffffff)
+#   else
+#    define SIZE_T_MAX 0xffffffff
+#   endif
+#  else
+#   error
+#  endif
+# endif
+# define SIZE_MAX SIZE_T_MAX
+#endif
+
+/* avoid malloc()ing 0 bytes, see:
+ * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003
+*/
+static inline void *safe_malloc_(size_t size)
+{
+	/* malloc(0) is undefined; FLAC src convention is to always allocate */
+	if(!size)
+		size++;
+	return malloc(size);
+}
+
+static inline void *safe_calloc_(size_t nmemb, size_t size)
+{
+	if(!nmemb || !size)
+		return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */
+	return calloc(nmemb, size);
+}
+
+/*@@@@ there's probably a better way to prevent overflows when allocating untrusted sums but this works for now */
+
+static inline void *safe_malloc_add_2op_(size_t size1, size_t size2)
+{
+	size2 += size1;
+	if(size2 < size1)
+		return 0;
+	return safe_malloc_(size2);
+}
+
+static inline void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3)
+{
+	size2 += size1;
+	if(size2 < size1)
+		return 0;
+	size3 += size2;
+	if(size3 < size2)
+		return 0;
+	return safe_malloc_(size3);
+}
+
+static inline void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4)
+{
+	size2 += size1;
+	if(size2 < size1)
+		return 0;
+	size3 += size2;
+	if(size3 < size2)
+		return 0;
+	size4 += size3;
+	if(size4 < size3)
+		return 0;
+	return safe_malloc_(size4);
+}
+
+void *safe_malloc_mul_2op_(size_t size1, size_t size2) ;
+
+static inline void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3)
+{
+	if(!size1 || !size2 || !size3)
+		return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */
+	if(size1 > SIZE_MAX / size2)
+		return 0;
+	size1 *= size2;
+	if(size1 > SIZE_MAX / size3)
+		return 0;
+	return malloc(size1*size3);
+}
+
+/* size1*size2 + size3 */
+static inline void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3)
+{
+	if(!size1 || !size2)
+		return safe_malloc_(size3);
+	if(size1 > SIZE_MAX / size2)
+		return 0;
+	return safe_malloc_add_2op_(size1*size2, size3);
+}
+
+/* size1 * (size2 + size3) */
+static inline void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3)
+{
+	if(!size1 || (!size2 && !size3))
+		return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */
+	size2 += size3;
+	if(size2 < size3)
+		return 0;
+	if(size1 > SIZE_MAX / size2)
+		return 0;
+	return malloc(size1*size2);
+}
+
+static inline void *safe_realloc_(void *ptr, size_t size)
+{
+	void *oldptr = ptr;
+	void *newptr = realloc(ptr, size);
+	if(size > 0 && newptr == 0)
+		free(oldptr);
+	return newptr;
+}
+static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2)
+{
+	size2 += size1;
+	if(size2 < size1) {
+		free(ptr);
+		return 0;
+	}
+	return realloc(ptr, size2);
+}
+
+static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3)
+{
+	size2 += size1;
+	if(size2 < size1)
+		return 0;
+	size3 += size2;
+	if(size3 < size2)
+		return 0;
+	return realloc(ptr, size3);
+}
+
+static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4)
+{
+	size2 += size1;
+	if(size2 < size1)
+		return 0;
+	size3 += size2;
+	if(size3 < size2)
+		return 0;
+	size4 += size3;
+	if(size4 < size3)
+		return 0;
+	return realloc(ptr, size4);
+}
+
+static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2)
+{
+	if(!size1 || !size2)
+		return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */
+	if(size1 > SIZE_MAX / size2)
+		return 0;
+	return safe_realloc_(ptr, size1*size2);
+}
+
+/* size1 * (size2 + size3) */
+static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3)
+{
+	if(!size1 || (!size2 && !size3))
+		return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */
+	size2 += size3;
+	if(size2 < size3)
+		return 0;
+	return safe_realloc_mul_2op_(ptr, size1, size2);
+}
+
+#endif
--- /dev/null
+++ b/src/libflac/share/compat.h
@@ -1,0 +1,205 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2012-2016  Xiph.org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+ */
+
+/* This is the preferred location of all CPP hackery to make $random_compiler
+ * work like something approaching a C99 (or maybe more accurately GNU99)
+ * compiler.
+ *
+ * It is assumed that this header will be included after "config.h".
+ */
+
+#ifndef FLAC__SHARE__COMPAT_H
+#define FLAC__SHARE__COMPAT_H
+
+#if defined _WIN32 && !defined __CYGWIN__
+/* where MSVC puts unlink() */
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
+#include <sys/types.h> /* for off_t */
+#define FLAC__off_t __int64 /* use this instead of off_t to fix the 2 GB limit */
+#if !defined __MINGW32__
+#define fseeko _fseeki64
+#define ftello _ftelli64
+#else /* MinGW */
+#if !defined(HAVE_FSEEKO)
+#define fseeko fseeko64
+#define ftello ftello64
+#endif
+#endif
+#else
+#define FLAC__off_t off_t
+#endif
+
+#if HAVE_INTTYPES_H
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#endif
+
+#if defined(_MSC_VER)
+#define strtoll _strtoi64
+#define strtoull _strtoui64
+#endif
+
+#if defined(_MSC_VER) && !defined(__cplusplus)
+#define inline __inline
+#endif
+
+#if defined __INTEL_COMPILER || (defined _MSC_VER && defined _WIN64)
+/* MSVS generates VERY slow 32-bit code with __restrict */
+#define flac_restrict __restrict
+#elif defined __GNUC__
+#define flac_restrict __restrict__
+#else
+#define flac_restrict
+#endif
+
+#define FLAC__U64L(x) x##ULL
+
+#if defined _MSC_VER || defined __MINGW32__
+#define FLAC__STRCASECMP _stricmp
+#define FLAC__STRNCASECMP _strnicmp
+#elif defined __BORLANDC__
+#define FLAC__STRCASECMP stricmp
+#define FLAC__STRNCASECMP strnicmp
+#else
+#define FLAC__STRCASECMP strcasecmp
+#define FLAC__STRNCASECMP strncasecmp
+#endif
+
+#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__
+#include <io.h> /* for _setmode(), chmod() */
+#include <fcntl.h> /* for _O_BINARY */
+#else
+#include <unistd.h> /* for chown(), unlink() */
+#endif
+
+#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
+#if defined __BORLANDC__
+#include <utime.h> /* for utime() */
+#else
+#include <sys/utime.h> /* for utime() */
+#endif
+#else
+#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
+#include <utime.h> /* for utime() */
+#endif
+
+#if defined _MSC_VER
+#  if _MSC_VER >= 1800
+#    include <inttypes.h>
+#  elif _MSC_VER >= 1600
+/* Visual Studio 2010 has decent C99 support */
+#    include <stdint.h>
+#    define PRIu64 "llu"
+#    define PRId64 "lld"
+#    define PRIx64 "llx"
+#  else
+#    include <limits.h>
+#    ifndef UINT32_MAX
+#      define UINT32_MAX _UI32_MAX
+#    endif
+#    define PRIu64 "I64u"
+#    define PRId64 "I64d"
+#    define PRIx64 "I64x"
+#  endif
+#endif /* defined _MSC_VER */
+
+#ifdef _WIN32
+/* All char* strings are in UTF-8 format. Added to support Unicode files on Windows */
+
+#include "../share/win_utf8_io.h"
+#define flac_printf printf_utf8
+#define flac_fprintf fprintf_utf8
+#define flac_vfprintf vfprintf_utf8
+
+#include "../share/windows_unicode_filenames.h"
+#define flac_fopen flac_internal_fopen_utf8
+#define flac_chmod flac_internal_chmod_utf8
+#define flac_utime flac_internal_utime_utf8
+#define flac_unlink flac_internal_unlink_utf8
+#define flac_rename flac_internal_rename_utf8
+#define flac_stat flac_internal_stat64_utf8
+
+#else
+
+#define flac_printf printf
+#define flac_fprintf fprintf
+#define flac_vfprintf vfprintf
+
+#define flac_fopen fopen
+#define flac_chmod chmod
+#define flac_utime utime
+#define flac_unlink unlink
+#define flac_rename rename
+#define flac_stat stat
+
+#endif
+
+#ifdef _WIN32
+#define flac_stat_s __stat64 /* stat struct */
+#define flac_fstat _fstat64
+#else
+#define flac_stat_s stat /* stat struct */
+#define flac_fstat fstat
+#endif
+
+#ifdef ANDROID
+#include <limits.h>
+#endif
+
+#ifndef M_LN2
+#define M_LN2 0.69314718055994530942
+#endif
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+/* FLAC needs to compile and work correctly on systems with a normal ISO C99
+ * snprintf as well as Microsoft Visual Studio which has an non-standards
+ * conformant snprint_s function.
+ *
+ * This function wraps the MS version to behave more like the ISO version.
+ */
+#include <stdarg.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+int flac_snprintf(char *str, size_t size, const char *fmt, ...);
+int flac_vsnprintf(char *str, size_t size, const char *fmt, va_list va);
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* FLAC__SHARE__COMPAT_H */
--- /dev/null
+++ b/src/libflac/share/endswap.h
@@ -1,0 +1,84 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2012-2016  Xiph.org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+ */
+
+/* It is assumed that this header will be included after "config.h". */
+
+#if HAVE_BSWAP32			/* GCC and Clang */
+
+/* GCC prior to 4.8 didn't provide bswap16 on x86_64 */
+#if ! HAVE_BSWAP16
+static inline unsigned short __builtin_bswap16(unsigned short a)
+{
+	return (a<<8)|(a>>8);
+}
+#endif
+
+#define	ENDSWAP_16(x)		(__builtin_bswap16 (x))
+#define	ENDSWAP_32(x)		(__builtin_bswap32 (x))
+#define	ENDSWAP_64(x)		(__builtin_bswap64 (x))
+
+#elif defined _MSC_VER		/* Windows */
+
+#include <stdlib.h>
+
+#define	ENDSWAP_16(x)		(_byteswap_ushort (x))
+#define	ENDSWAP_32(x)		(_byteswap_ulong (x))
+#define	ENDSWAP_64(x)		(_byteswap_uint64 (x))
+
+#elif defined HAVE_BYTESWAP_H		/* Linux */
+
+#include <byteswap.h>
+
+#define	ENDSWAP_16(x)		(bswap_16 (x))
+#define	ENDSWAP_32(x)		(bswap_32 (x))
+#define	ENDSWAP_64(x)		(bswap_64 (x))
+
+#else
+
+#define	ENDSWAP_16(x)		((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8))
+#define	ENDSWAP_32(x)		((((x) >> 24) & 0xFF) | (((x) >> 8) & 0xFF00) | (((x) & 0xFF00) << 8) | (((x) & 0xFF) << 24))
+#define	ENDSWAP_64(x)		((ENDSWAP_32(((x) >> 32) & 0xFFFFFFFF)) | (ENDSWAP_32((x) & 0xFFFFFFFF) << 32))
+
+#endif
+
+
+/* Host to little-endian byte swapping (for MD5 calculation) */
+#if CPU_IS_BIG_ENDIAN
+
+#define H2LE_16(x)		ENDSWAP_16 (x)
+#define H2LE_32(x)		ENDSWAP_32 (x)
+
+#else
+
+#define H2LE_16(x)		(x)
+#define H2LE_32(x)		(x)
+
+#endif
--- /dev/null
+++ b/src/libflac/share/macros.h
@@ -1,0 +1,45 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2013-2016  Xiph.org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+/* FLAC_CHECK_RETURN : Check the return value of the provided function and
+ * print an error message if it fails (ie returns a value < 0).
+ *
+ * Ideally, a library should not print anything, but this macro is only used
+ * for things that extremely unlikely to fail, like `chown` to a previoulsy
+ * saved `uid`.
+ */
+
+#define FLAC_CHECK_RETURN(x) \
+			{	if ((x) < 0) \
+					fprintf (stderr, "%s : %s\n", #x, strerror (errno)) ; \
+			}
--- /dev/null
+++ b/src/libflac/share/safe_str.h
@@ -1,0 +1,69 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2013-2016  Xiph.org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+ */
+
+/* Safe string handling functions to replace things like strcpy, strncpy,
+ * strcat, strncat etc.
+ * All of these functions guarantee a correctly NUL terminated string but
+ * the string may be truncated if the destination buffer was too short.
+ */
+
+#ifndef FLAC__SHARE_SAFE_STR_H
+#define FLAC__SHARE_SAFE_STR_H
+
+static inline char *
+safe_strncat(char *dest, const char *src, size_t dest_size)
+{
+	char * ret;
+
+	if (dest_size < 1)
+		return dest;
+
+	ret = strncat(dest, src, dest_size - strlen (dest));
+	dest [dest_size - 1] = 0;
+
+	return ret;
+}
+
+static inline char *
+safe_strncpy(char *dest, const char *src, size_t dest_size)
+{
+	char * ret;
+
+	if (dest_size < 1)
+		return dest;
+
+	ret = strncpy(dest, src, dest_size);
+	dest [dest_size - 1] = 0;
+
+	return ret;
+}
+
+#endif /* FLAC__SHARE_SAFE_STR_H */
--- /dev/null
+++ b/src/libflac/share/utf8.h
@@ -1,0 +1,25 @@
+#ifndef SHARE__UTF8_H
+#define SHARE__UTF8_H
+
+/*
+ * Convert a string between UTF-8 and the locale's charset.
+ * Invalid bytes are replaced by '#', and characters that are
+ * not available in the target encoding are replaced by '?'.
+ *
+ * If the locale's charset is not set explicitly then it is
+ * obtained using nl_langinfo(CODESET), where available, the
+ * environment variable CHARSET, or assumed to be US-ASCII.
+ *
+ * Return value of conversion functions:
+ *
+ *  -1 : memory allocation failed
+ *   0 : data was converted exactly
+ *   1 : valid data was converted approximately (using '?')
+ *   2 : input was invalid (but still converted, using '#')
+ *   3 : unknown encoding (but still converted, using '?')
+ */
+
+int utf8_encode(const char *from, char **to);
+int utf8_decode(const char *from, char **to);
+
+#endif
--- /dev/null
+++ b/src/libflac/share/win_utf8_io.h
@@ -1,0 +1,62 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2013-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+ */
+
+#ifdef _WIN32
+
+#ifndef flac__win_utf8_io_h
+#define flac__win_utf8_io_h
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+size_t strlen_utf8(const char *str);
+int win_get_console_width(void);
+
+int get_utf8_argv(int *argc, char ***argv);
+
+int printf_utf8(const char *format, ...);
+int fprintf_utf8(FILE *stream, const char *format, ...);
+int vfprintf_utf8(FILE *stream, const char *format, va_list argptr);
+
+#define WIN32_MEAN_AND_LEAN
+#include <windows.h>
+HANDLE WINAPI CreateFile_utf8(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+#endif
--- /dev/null
+++ b/src/libflac/share/windows_unicode_filenames.h
@@ -1,0 +1,63 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2013-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+ */
+
+#ifdef _WIN32
+
+#ifndef flac__windows_unicode_filenames_h
+#define flac__windows_unicode_filenames_h
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "../FLAC/ordinals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void flac_internal_set_utf8_filenames(FLAC__bool flag);
+FLAC__bool flac_internal_get_utf8_filenames(void);
+#define flac_set_utf8_filenames flac_internal_set_utf8_filenames
+#define flac_get_utf8_filenames flac_internal_get_utf8_filenames
+
+FILE* flac_internal_fopen_utf8(const char *filename, const char *mode);
+int flac_internal_stat64_utf8(const char *path, struct __stat64 *buffer);
+int flac_internal_chmod_utf8(const char *filename, int pmode);
+int flac_internal_utime_utf8(const char *filename, struct utimbuf *times);
+int flac_internal_unlink_utf8(const char *filename);
+int flac_internal_rename_utf8(const char *oldname, const char *newname);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+#endif
--- /dev/null
+++ b/src/libflac/stream_decoder.c
@@ -1,0 +1,2951 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2000-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+ */
+
+// 8bb: hide POSIX warnings
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h> /* for malloc() */
+#include <string.h> /* for memset/memcpy() */
+#include <sys/stat.h> /* for stat() */
+#include <sys/types.h> /* for off_t */
+#include "share/compat.h"
+#include "share/alloc.h"
+#include "protected/stream_decoder.h"
+#include "private/bitreader.h"
+#include "private/bitmath.h"
+#include "private/crc.h"
+#include "private/fixed.h"
+#include "private/format.h"
+#include "private/lpc.h"
+#include "private/md5.h"
+#include "private/memory.h"
+#include "private/macros.h"
+
+/***********************************************************************
+ *
+ * Private static data
+ *
+ ***********************************************************************/
+
+static const FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' };
+
+/***********************************************************************
+ *
+ * Private class method prototypes
+ *
+ ***********************************************************************/
+
+static void set_defaults_(FLAC__StreamDecoder *decoder);
+static FILE *get_binary_stdin_(void);
+static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, uint32_t size, uint32_t channels);
+static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id);
+static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder);
+static FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder);
+static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length);
+static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length);
+static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, uint32_t length);
+static FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj);
+static FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj);
+static FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder);
+static FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder);
+static FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode);
+static FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder);
+static FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode);
+static FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode);
+static FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode);
+static FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode);
+static FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode);
+static FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_t predictor_order, uint32_t partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended);
+static FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder);
+static FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data);
+static FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
+static void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status);
+static FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample);
+static FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
+static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
+static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data);
+
+/***********************************************************************
+ *
+ * Private class data
+ *
+ ***********************************************************************/
+
+typedef struct FLAC__StreamDecoderPrivate {
+	FLAC__bool is_ogg;
+	FLAC__StreamDecoderReadCallback read_callback;
+	FLAC__StreamDecoderSeekCallback seek_callback;
+	FLAC__StreamDecoderTellCallback tell_callback;
+	FLAC__StreamDecoderLengthCallback length_callback;
+	FLAC__StreamDecoderEofCallback eof_callback;
+	FLAC__StreamDecoderWriteCallback write_callback;
+	FLAC__StreamDecoderMetadataCallback metadata_callback;
+	FLAC__StreamDecoderErrorCallback error_callback;
+	/* generic 32-bit datapath: */
+	void (*local_lpc_restore_signal)(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+	/* generic 64-bit datapath: */
+	void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+	/* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */
+	void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]);
+	void *client_data;
+	FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */
+	FLAC__BitReader *input;
+	FLAC__int32 *output[FLAC__MAX_CHANNELS];
+	FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */
+	FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents[FLAC__MAX_CHANNELS];
+	uint32_t output_capacity, output_channels;
+	FLAC__uint32 fixed_block_size, next_fixed_block_size;
+	FLAC__uint64 samples_decoded;
+	FLAC__bool has_stream_info, has_seek_table;
+	FLAC__StreamMetadata stream_info;
+	FLAC__StreamMetadata seek_table;
+	FLAC__bool metadata_filter[128]; /* MAGIC number 128 == total number of metadata block types == 1 << 7 */
+	FLAC__byte *metadata_filter_ids;
+	size_t metadata_filter_ids_count, metadata_filter_ids_capacity; /* units for both are IDs, not bytes */
+	FLAC__Frame frame;
+	FLAC__bool cached; /* true if there is a byte in lookahead */
+	FLAC__byte header_warmup[2]; /* contains the sync code and reserved bits */
+	FLAC__byte lookahead; /* temp storage when we need to look ahead one byte in the stream */
+	/* unaligned (original) pointers to allocated data */
+	FLAC__int32 *residual_unaligned[FLAC__MAX_CHANNELS];
+	FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek or if the metadata has a zero MD5 */
+	FLAC__bool internal_reset_hack; /* used only during init() so we can call reset to set up the decoder without rewinding the input */
+	FLAC__bool is_seeking;
+	FLAC__MD5Context md5context;
+	FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */
+	/* (the rest of these are only used for seeking) */
+	FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */
+	FLAC__uint64 first_frame_offset; /* hint to the seek routine of where in the stream the first audio frame starts */
+	FLAC__uint64 target_sample;
+	uint32_t unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */
+	FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */
+} FLAC__StreamDecoderPrivate;
+
+/***********************************************************************
+ *
+ * Public static class data
+ *
+ ***********************************************************************/
+
+FLAC_API const char * const FLAC__StreamDecoderStateString[] = {
+	"FLAC__STREAM_DECODER_SEARCH_FOR_METADATA",
+	"FLAC__STREAM_DECODER_READ_METADATA",
+	"FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC",
+	"FLAC__STREAM_DECODER_READ_FRAME",
+	"FLAC__STREAM_DECODER_END_OF_STREAM",
+	"FLAC__STREAM_DECODER_OGG_ERROR",
+	"FLAC__STREAM_DECODER_SEEK_ERROR",
+	"FLAC__STREAM_DECODER_ABORTED",
+	"FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR",
+	"FLAC__STREAM_DECODER_UNINITIALIZED"
+};
+
+FLAC_API const char * const FLAC__StreamDecoderInitStatusString[] = {
+	"FLAC__STREAM_DECODER_INIT_STATUS_OK",
+	"FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER",
+	"FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS",
+	"FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR",
+	"FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE",
+	"FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED"
+};
+
+FLAC_API const char * const FLAC__StreamDecoderReadStatusString[] = {
+	"FLAC__STREAM_DECODER_READ_STATUS_CONTINUE",
+	"FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM",
+	"FLAC__STREAM_DECODER_READ_STATUS_ABORT"
+};
+
+FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[] = {
+	"FLAC__STREAM_DECODER_SEEK_STATUS_OK",
+	"FLAC__STREAM_DECODER_SEEK_STATUS_ERROR",
+	"FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED"
+};
+
+FLAC_API const char * const FLAC__StreamDecoderTellStatusString[] = {
+	"FLAC__STREAM_DECODER_TELL_STATUS_OK",
+	"FLAC__STREAM_DECODER_TELL_STATUS_ERROR",
+	"FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED"
+};
+
+FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[] = {
+	"FLAC__STREAM_DECODER_LENGTH_STATUS_OK",
+	"FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR",
+	"FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED"
+};
+
+FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[] = {
+	"FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE",
+	"FLAC__STREAM_DECODER_WRITE_STATUS_ABORT"
+};
+
+FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = {
+	"FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC",
+	"FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER",
+	"FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH",
+	"FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM"
+};
+
+/***********************************************************************
+ *
+ * Class constructor/destructor
+ *
+ ***********************************************************************/
+FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void)
+{
+	FLAC__StreamDecoder *decoder;
+	uint32_t i;
+
+	decoder = calloc(1, sizeof(FLAC__StreamDecoder));
+	if(decoder == 0) {
+		return 0;
+	}
+
+	decoder->protected_ = calloc(1, sizeof(FLAC__StreamDecoderProtected));
+	if(decoder->protected_ == 0) {
+		free(decoder);
+		return 0;
+	}
+
+	decoder->private_ = calloc(1, sizeof(FLAC__StreamDecoderPrivate));
+	if(decoder->private_ == 0) {
+		free(decoder->protected_);
+		free(decoder);
+		return 0;
+	}
+
+	decoder->private_->input = FLAC__bitreader_new();
+	if(decoder->private_->input == 0) {
+		free(decoder->private_);
+		free(decoder->protected_);
+		free(decoder);
+		return 0;
+	}
+
+	decoder->private_->metadata_filter_ids_capacity = 16;
+	if(0 == (decoder->private_->metadata_filter_ids = malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) {
+		FLAC__bitreader_delete(decoder->private_->input);
+		free(decoder->private_);
+		free(decoder->protected_);
+		free(decoder);
+		return 0;
+	}
+
+	for(i = 0; i < FLAC__MAX_CHANNELS; i++) {
+		decoder->private_->output[i] = 0;
+		decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0;
+	}
+
+	decoder->private_->output_capacity = 0;
+	decoder->private_->output_channels = 0;
+	decoder->private_->has_seek_table = false;
+
+	for(i = 0; i < FLAC__MAX_CHANNELS; i++)
+		FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&decoder->private_->partitioned_rice_contents[i]);
+
+	decoder->private_->file = 0;
+
+	set_defaults_(decoder);
+
+	decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED;
+
+	return decoder;
+}
+
+FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder)
+{
+	uint32_t i;
+
+	if (decoder == NULL)
+		return ;
+
+	(void)FLAC__stream_decoder_finish(decoder);
+
+	if(0 != decoder->private_->metadata_filter_ids)
+		free(decoder->private_->metadata_filter_ids);
+
+	FLAC__bitreader_delete(decoder->private_->input);
+
+	for(i = 0; i < FLAC__MAX_CHANNELS; i++)
+		FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&decoder->private_->partitioned_rice_contents[i]);
+
+	free(decoder->private_);
+	free(decoder->protected_);
+	free(decoder);
+}
+
+/***********************************************************************
+ *
+ * Public class methods
+ *
+ ***********************************************************************/
+
+static FLAC__StreamDecoderInitStatus init_stream_internal_(
+	FLAC__StreamDecoder *decoder,
+	FLAC__StreamDecoderReadCallback read_callback,
+	FLAC__StreamDecoderSeekCallback seek_callback,
+	FLAC__StreamDecoderTellCallback tell_callback,
+	FLAC__StreamDecoderLengthCallback length_callback,
+	FLAC__StreamDecoderEofCallback eof_callback,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data,
+	FLAC__bool is_ogg
+)
+{
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED;
+
+	if(is_ogg)
+		return FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER;
+
+	if(
+		0 == read_callback ||
+		0 == write_callback ||
+		0 == error_callback ||
+		(seek_callback && (0 == tell_callback || 0 == length_callback || 0 == eof_callback))
+	)
+		return FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS;
+
+	decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal;
+	decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide;
+	decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal;
+
+	/* from here on, errors are fatal */
+
+	if(!FLAC__bitreader_init(decoder->private_->input, read_callback_, decoder)) {
+		decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+		return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR;
+	}
+
+	decoder->private_->read_callback = read_callback;
+	decoder->private_->seek_callback = seek_callback;
+	decoder->private_->tell_callback = tell_callback;
+	decoder->private_->length_callback = length_callback;
+	decoder->private_->eof_callback = eof_callback;
+	decoder->private_->write_callback = write_callback;
+	decoder->private_->metadata_callback = metadata_callback;
+	decoder->private_->error_callback = error_callback;
+	decoder->private_->client_data = client_data;
+	decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0;
+	decoder->private_->samples_decoded = 0;
+	decoder->private_->has_stream_info = false;
+	decoder->private_->cached = false;
+
+	decoder->private_->do_md5_checking = decoder->protected_->md5_checking;
+	decoder->private_->is_seeking = false;
+
+	decoder->private_->internal_reset_hack = true; /* so the following reset does not try to rewind the input */
+	if(!FLAC__stream_decoder_reset(decoder)) {
+		/* above call sets the state for us */
+		return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR;
+	}
+
+	return FLAC__STREAM_DECODER_INIT_STATUS_OK;
+}
+
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream(
+	FLAC__StreamDecoder *decoder,
+	FLAC__StreamDecoderReadCallback read_callback,
+	FLAC__StreamDecoderSeekCallback seek_callback,
+	FLAC__StreamDecoderTellCallback tell_callback,
+	FLAC__StreamDecoderLengthCallback length_callback,
+	FLAC__StreamDecoderEofCallback eof_callback,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+)
+{
+	return init_stream_internal_(
+		decoder,
+		read_callback,
+		seek_callback,
+		tell_callback,
+		length_callback,
+		eof_callback,
+		write_callback,
+		metadata_callback,
+		error_callback,
+		client_data,
+		/*is_ogg=*/false
+	);
+}
+
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream(
+	FLAC__StreamDecoder *decoder,
+	FLAC__StreamDecoderReadCallback read_callback,
+	FLAC__StreamDecoderSeekCallback seek_callback,
+	FLAC__StreamDecoderTellCallback tell_callback,
+	FLAC__StreamDecoderLengthCallback length_callback,
+	FLAC__StreamDecoderEofCallback eof_callback,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+)
+{
+	return init_stream_internal_(
+		decoder,
+		read_callback,
+		seek_callback,
+		tell_callback,
+		length_callback,
+		eof_callback,
+		write_callback,
+		metadata_callback,
+		error_callback,
+		client_data,
+		/*is_ogg=*/true
+	);
+}
+
+static FLAC__StreamDecoderInitStatus init_FILE_internal_(
+	FLAC__StreamDecoder *decoder,
+	FILE *file,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data,
+	FLAC__bool is_ogg
+)
+{
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED;
+
+	if(0 == write_callback || 0 == error_callback)
+		return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS;
+
+	/*
+	 * To make sure that our file does not go unclosed after an error, we
+	 * must assign the FILE pointer before any further error can occur in
+	 * this routine.
+	 */
+	if(file == stdin)
+		file = get_binary_stdin_(); /* just to be safe */
+
+	decoder->private_->file = file;
+
+	return init_stream_internal_(
+		decoder,
+		file_read_callback_,
+		decoder->private_->file == stdin? 0: file_seek_callback_,
+		decoder->private_->file == stdin? 0: file_tell_callback_,
+		decoder->private_->file == stdin? 0: file_length_callback_,
+		file_eof_callback_,
+		write_callback,
+		metadata_callback,
+		error_callback,
+		client_data,
+		is_ogg
+	);
+}
+
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE(
+	FLAC__StreamDecoder *decoder,
+	FILE *file,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+)
+{
+	return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false);
+}
+
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE(
+	FLAC__StreamDecoder *decoder,
+	FILE *file,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+)
+{
+	return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true);
+}
+
+static FLAC__StreamDecoderInitStatus init_file_internal_(
+	FLAC__StreamDecoder *decoder,
+	const char *filename,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data,
+	FLAC__bool is_ogg
+)
+{
+	FILE *file;
+
+	/*
+	 * To make sure that our file does not go unclosed after an error, we
+	 * have to do the same entrance checks here that are later performed
+	 * in FLAC__stream_decoder_init_FILE() before the FILE* is assigned.
+	 */
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED;
+
+	if(0 == write_callback || 0 == error_callback)
+		return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS;
+
+	file = filename? flac_fopen(filename, "rb") : stdin;
+
+	if(0 == file)
+		return FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE;
+
+	return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, is_ogg);
+}
+
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file(
+	FLAC__StreamDecoder *decoder,
+	const char *filename,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+)
+{
+	return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false);
+}
+
+FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file(
+	FLAC__StreamDecoder *decoder,
+	const char *filename,
+	FLAC__StreamDecoderWriteCallback write_callback,
+	FLAC__StreamDecoderMetadataCallback metadata_callback,
+	FLAC__StreamDecoderErrorCallback error_callback,
+	void *client_data
+)
+{
+	return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true);
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder)
+{
+	FLAC__bool md5_failed = false;
+	uint32_t i;
+
+	if(decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED)
+		return true;
+
+	/* see the comment in FLAC__stream_decoder_reset() as to why we
+	 * always call FLAC__MD5Final()
+	 */
+	FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context);
+
+	free(decoder->private_->seek_table.data.seek_table.points);
+	decoder->private_->seek_table.data.seek_table.points = 0;
+	decoder->private_->has_seek_table = false;
+
+	FLAC__bitreader_free(decoder->private_->input);
+	for(i = 0; i < FLAC__MAX_CHANNELS; i++) {
+		/* WATCHOUT:
+		 * FLAC__lpc_restore_signal_asm_ia32_mmx() and ..._intrin_sseN()
+		 * require that the output arrays have a buffer of up to 3 zeroes
+		 * in front (at negative indices) for alignment purposes;
+		 * we use 4 to keep the data well-aligned.
+		 */
+		if(0 != decoder->private_->output[i]) {
+			free(decoder->private_->output[i]-4);
+			decoder->private_->output[i] = 0;
+		}
+		if(0 != decoder->private_->residual_unaligned[i]) {
+			free(decoder->private_->residual_unaligned[i]);
+			decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0;
+		}
+	}
+	decoder->private_->output_capacity = 0;
+	decoder->private_->output_channels = 0;
+
+	if(0 != decoder->private_->file) {
+		if(decoder->private_->file != stdin)
+			fclose(decoder->private_->file);
+		decoder->private_->file = 0;
+	}
+
+	if(decoder->private_->do_md5_checking) {
+		if(memcmp(decoder->private_->stream_info.data.stream_info.md5sum, decoder->private_->computed_md5sum, 16))
+			md5_failed = true;
+	}
+	decoder->private_->is_seeking = false;
+
+	set_defaults_(decoder);
+
+	decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED;
+
+	return !md5_failed;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long value)
+{
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+
+	(void)value;
+	return false;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value)
+{
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+	decoder->protected_->md5_checking = value;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type)
+{
+	/* double protection */
+	if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE)
+		return false;
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+	decoder->private_->metadata_filter[type] = true;
+	if(type == FLAC__METADATA_TYPE_APPLICATION)
+		decoder->private_->metadata_filter_ids_count = 0;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4])
+{
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+
+	if(decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION])
+		return true;
+
+	if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) {
+		if(0 == (decoder->private_->metadata_filter_ids = safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+		decoder->private_->metadata_filter_ids_capacity *= 2;
+	}
+
+	memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8));
+	decoder->private_->metadata_filter_ids_count++;
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder)
+{
+	uint32_t i;
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+	for(i = 0; i < sizeof(decoder->private_->metadata_filter) / sizeof(decoder->private_->metadata_filter[0]); i++)
+		decoder->private_->metadata_filter[i] = true;
+	decoder->private_->metadata_filter_ids_count = 0;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type)
+{
+	/* double protection */
+	if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE)
+		return false;
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+	decoder->private_->metadata_filter[type] = false;
+	if(type == FLAC__METADATA_TYPE_APPLICATION)
+		decoder->private_->metadata_filter_ids_count = 0;
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4])
+{
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+
+	if(!decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION])
+		return true;
+
+	if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) {
+		if(0 == (decoder->private_->metadata_filter_ids = safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+		decoder->private_->metadata_filter_ids_capacity *= 2;
+	}
+
+	memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8));
+	decoder->private_->metadata_filter_ids_count++;
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder)
+{
+	if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+	memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter));
+	decoder->private_->metadata_filter_ids_count = 0;
+	return true;
+}
+
+FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->protected_->state;
+}
+
+FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder)
+{
+	return FLAC__StreamDecoderStateString[decoder->protected_->state];
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->protected_->md5_checking;
+}
+
+FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->private_->has_stream_info? decoder->private_->stream_info.data.stream_info.total_samples : 0;
+}
+
+FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->protected_->channels;
+}
+
+FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->protected_->channel_assignment;
+}
+
+FLAC_API uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->protected_->bits_per_sample;
+}
+
+FLAC_API uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->protected_->sample_rate;
+}
+
+FLAC_API uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder)
+{
+	return decoder->protected_->blocksize;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position)
+{
+	if(0 == decoder->private_->tell_callback)
+		return false;
+	if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK)
+		return false;
+	/* should never happen since all FLAC frames and metadata blocks are byte aligned, but check just in case */
+	if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input))
+		return false;
+	*position -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder);
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder)
+{
+	if(!decoder->private_->internal_reset_hack && decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED)
+		return false;
+
+	decoder->private_->samples_decoded = 0;
+	decoder->private_->do_md5_checking = false;
+
+	if(!FLAC__bitreader_clear(decoder->private_->input)) {
+		decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+	decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder)
+{
+	if(!FLAC__stream_decoder_flush(decoder)) {
+		/* above call sets the state for us */
+		return false;
+	}
+
+	/* Rewind if necessary.  If FLAC__stream_decoder_init() is calling us,
+	 * (internal_reset_hack) don't try to rewind since we are already at
+	 * the beginning of the stream and don't want to fail if the input is
+	 * not seekable.
+	 */
+	if(!decoder->private_->internal_reset_hack) {
+		if(decoder->private_->file == stdin)
+			return false; /* can't rewind stdin, reset fails */
+		if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR)
+			return false; /* seekable and seek fails, reset fails */
+	}
+	else
+		decoder->private_->internal_reset_hack = false;
+
+	decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA;
+
+	decoder->private_->has_stream_info = false;
+
+	free(decoder->private_->seek_table.data.seek_table.points);
+	decoder->private_->seek_table.data.seek_table.points = 0;
+	decoder->private_->has_seek_table = false;
+
+	decoder->private_->do_md5_checking = decoder->protected_->md5_checking;
+	/*
+	 * This goes in reset() and not flush() because according to the spec, a
+	 * fixed-blocksize stream must stay that way through the whole stream.
+	 */
+	decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0;
+
+	/* We initialize the FLAC__MD5Context even though we may never use it.  This
+	 * is because md5 checking may be turned on to start and then turned off if
+	 * a seek occurs.  So we init the context here and finalize it in
+	 * FLAC__stream_decoder_finish() to make sure things are always cleaned up
+	 * properly.
+	 */
+	FLAC__MD5Init(&decoder->private_->md5context);
+
+	decoder->private_->first_frame_offset = 0;
+	decoder->private_->unparseable_frame_count = 0;
+
+	return true;
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder)
+{
+	FLAC__bool got_a_frame;
+
+	while(1) {
+		switch(decoder->protected_->state) {
+			case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
+				if(!find_metadata_(decoder))
+					return false; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_READ_METADATA:
+				if(!read_metadata_(decoder))
+					return false; /* above function sets the status for us */
+				else
+					return true;
+			case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
+				if(!frame_sync_(decoder))
+					return true; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_READ_FRAME:
+				if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/true))
+					return false; /* above function sets the status for us */
+				if(got_a_frame)
+					return true; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_END_OF_STREAM:
+			case FLAC__STREAM_DECODER_ABORTED:
+				return true;
+			default:
+				return false;
+		}
+	}
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder)
+{
+	while(1) {
+		switch(decoder->protected_->state) {
+			case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
+				if(!find_metadata_(decoder))
+					return false; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_READ_METADATA:
+				if(!read_metadata_(decoder))
+					return false; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
+			case FLAC__STREAM_DECODER_READ_FRAME:
+			case FLAC__STREAM_DECODER_END_OF_STREAM:
+			case FLAC__STREAM_DECODER_ABORTED:
+				return true;
+			default:
+				return false;
+		}
+	}
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder)
+{
+	FLAC__bool dummy;
+
+	while(1) {
+		switch(decoder->protected_->state) {
+			case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
+				if(!find_metadata_(decoder))
+					return false; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_READ_METADATA:
+				if(!read_metadata_(decoder))
+					return false; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
+				if(!frame_sync_(decoder))
+					return true; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_READ_FRAME:
+				if(!read_frame_(decoder, &dummy, /*do_full_decode=*/true))
+					return false; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_END_OF_STREAM:
+			case FLAC__STREAM_DECODER_ABORTED:
+				return true;
+			default:
+				return false;
+		}
+	}
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder)
+{
+	FLAC__bool got_a_frame;
+
+	while(1) {
+		switch(decoder->protected_->state) {
+			case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
+			case FLAC__STREAM_DECODER_READ_METADATA:
+				return false; /* above function sets the status for us */
+			case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
+				if(!frame_sync_(decoder))
+					return true; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_READ_FRAME:
+				if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/false))
+					return false; /* above function sets the status for us */
+				if(got_a_frame)
+					return true; /* above function sets the status for us */
+				break;
+			case FLAC__STREAM_DECODER_END_OF_STREAM:
+			case FLAC__STREAM_DECODER_ABORTED:
+				return true;
+			default:
+				return false;
+		}
+	}
+}
+
+FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample)
+{
+	FLAC__uint64 length;
+
+	if(
+		decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA &&
+		decoder->protected_->state != FLAC__STREAM_DECODER_READ_METADATA &&
+		decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC &&
+		decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME &&
+		decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM
+	)
+		return false;
+
+	if(0 == decoder->private_->seek_callback)
+		return false;
+
+	if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder))
+		return false;
+
+	decoder->private_->is_seeking = true;
+
+	/* turn off md5 checking if a seek is attempted */
+	decoder->private_->do_md5_checking = false;
+
+	/* get the file length (currently our algorithm needs to know the length so it's also an error to get FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) */
+	if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__STREAM_DECODER_LENGTH_STATUS_OK) {
+		decoder->private_->is_seeking = false;
+		return false;
+	}
+
+	/* if we haven't finished processing the metadata yet, do that so we have the STREAMINFO, SEEK_TABLE, and first_frame_offset */
+	if(
+		decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA ||
+		decoder->protected_->state == FLAC__STREAM_DECODER_READ_METADATA
+	) {
+		if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) {
+			/* above call sets the state for us */
+			decoder->private_->is_seeking = false;
+			return false;
+		}
+		/* check this again in case we didn't know total_samples the first time */
+		if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) {
+			decoder->private_->is_seeking = false;
+			return false;
+		}
+	}
+
+	{
+		const FLAC__bool ok = seek_to_absolute_sample_(decoder, length, sample);
+		decoder->private_->is_seeking = false;
+		return ok;
+	}
+}
+
+/***********************************************************************
+ *
+ * Protected class methods
+ *
+ ***********************************************************************/
+
+uint32_t FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder)
+{
+	return FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) / 8;
+}
+
+/***********************************************************************
+ *
+ * Private class methods
+ *
+ ***********************************************************************/
+
+void set_defaults_(FLAC__StreamDecoder *decoder)
+{
+	decoder->private_->is_ogg = false;
+	decoder->private_->read_callback = 0;
+	decoder->private_->seek_callback = 0;
+	decoder->private_->tell_callback = 0;
+	decoder->private_->length_callback = 0;
+	decoder->private_->eof_callback = 0;
+	decoder->private_->write_callback = 0;
+	decoder->private_->metadata_callback = 0;
+	decoder->private_->error_callback = 0;
+	decoder->private_->client_data = 0;
+
+	memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter));
+	decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] = true;
+	decoder->private_->metadata_filter_ids_count = 0;
+
+	decoder->protected_->md5_checking = false;
+}
+
+/*
+ * This will forcibly set stdin to binary mode (for OSes that require it)
+ */
+FILE *get_binary_stdin_(void)
+{
+	/* if something breaks here it is probably due to the presence or
+	 * absence of an underscore before the identifiers 'setmode',
+	 * 'fileno', and/or 'O_BINARY'; check your system header files.
+	 */
+#if defined _MSC_VER || defined __MINGW32__
+	_setmode(_fileno(stdin), _O_BINARY);
+#elif defined __EMX__
+	setmode(fileno(stdin), O_BINARY);
+#endif
+
+	return stdin;
+}
+
+FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, uint32_t size, uint32_t channels)
+{
+	uint32_t i;
+	FLAC__int32 *tmp;
+
+	if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels)
+		return true;
+
+	/* simply using realloc() is not practical because the number of channels may change mid-stream */
+
+	for(i = 0; i < FLAC__MAX_CHANNELS; i++) {
+		if(0 != decoder->private_->output[i]) {
+			free(decoder->private_->output[i]-4);
+			decoder->private_->output[i] = 0;
+		}
+		if(0 != decoder->private_->residual_unaligned[i]) {
+			free(decoder->private_->residual_unaligned[i]);
+			decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0;
+		}
+	}
+
+	for(i = 0; i < channels; i++) {
+		/* WATCHOUT:
+		 * FLAC__lpc_restore_signal_asm_ia32_mmx() and ..._intrin_sseN()
+		 * require that the output arrays have a buffer of up to 3 zeroes
+		 * in front (at negative indices) for alignment purposes;
+		 * we use 4 to keep the data well-aligned.
+		 */
+		tmp = safe_malloc_muladd2_(sizeof(FLAC__int32), /*times (*/size, /*+*/4/*)*/);
+		if(tmp == 0) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+		memset(tmp, 0, sizeof(FLAC__int32)*4);
+		decoder->private_->output[i] = tmp + 4;
+
+		if(!FLAC__memory_alloc_aligned_int32_array(size, &decoder->private_->residual_unaligned[i], &decoder->private_->residual[i])) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+	}
+
+	decoder->private_->output_capacity = size;
+	decoder->private_->output_channels = channels;
+
+	return true;
+}
+
+FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id)
+{
+	size_t i;
+
+	for(i = 0; i < decoder->private_->metadata_filter_ids_count; i++)
+		if(0 == memcmp(decoder->private_->metadata_filter_ids + i * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)))
+			return true;
+
+	return false;
+}
+
+FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder)
+{
+	FLAC__uint32 x;
+	uint32_t i, id;
+	FLAC__bool first = true;
+
+	for(i = id = 0; i < 4; ) {
+		if(decoder->private_->cached) {
+			x = (FLAC__uint32)decoder->private_->lookahead;
+			decoder->private_->cached = false;
+		}
+		else {
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+				return false; /* read_callback_ sets the state for us */
+		}
+		if(x == FLAC__STREAM_SYNC_STRING[i]) {
+			first = true;
+			i++;
+			id = 0;
+			continue;
+		}
+
+		if(id >= 3)
+			return false;
+
+		if(x == ID3V2_TAG_[id]) {
+			id++;
+			i = 0;
+			if(id == 3) {
+				if(!skip_id3v2_tag_(decoder))
+					return false; /* skip_id3v2_tag_ sets the state for us */
+			}
+			continue;
+		}
+		id = 0;
+		if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
+			decoder->private_->header_warmup[0] = (FLAC__byte)x;
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+				return false; /* read_callback_ sets the state for us */
+
+			/* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */
+			/* else we have to check if the second byte is the end of a sync code */
+			if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
+				decoder->private_->lookahead = (FLAC__byte)x;
+				decoder->private_->cached = true;
+			}
+			else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */
+				decoder->private_->header_warmup[1] = (FLAC__byte)x;
+				decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME;
+				return true;
+			}
+		}
+		i = 0;
+		if(first) {
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+			first = false;
+		}
+	}
+
+	decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA;
+	return true;
+}
+
+FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder)
+{
+	FLAC__bool is_last;
+	FLAC__uint32 i, x, type, length;
+
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_IS_LAST_LEN))
+		return false; /* read_callback_ sets the state for us */
+	is_last = x? true : false;
+
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &type, FLAC__STREAM_METADATA_TYPE_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &length, FLAC__STREAM_METADATA_LENGTH_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	if(type == FLAC__METADATA_TYPE_STREAMINFO) {
+		if(!read_metadata_streaminfo_(decoder, is_last, length))
+			return false;
+
+		decoder->private_->has_stream_info = true;
+		if(0 == memcmp(decoder->private_->stream_info.data.stream_info.md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16))
+			decoder->private_->do_md5_checking = false;
+		if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] && decoder->private_->metadata_callback)
+			decoder->private_->metadata_callback(decoder, &decoder->private_->stream_info, decoder->private_->client_data);
+	}
+	else if(type == FLAC__METADATA_TYPE_SEEKTABLE) {
+		/* just in case we already have a seek table, and reading the next one fails: */
+		decoder->private_->has_seek_table = false;
+
+		if(!read_metadata_seektable_(decoder, is_last, length))
+			return false;
+
+		decoder->private_->has_seek_table = true;
+		if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback)
+			decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data);
+	}
+	else {
+		FLAC__bool skip_it = !decoder->private_->metadata_filter[type];
+		uint32_t real_length = length;
+		FLAC__StreamMetadata block;
+
+		memset(&block, 0, sizeof(block));
+		block.is_last = is_last;
+		block.type = (FLAC__MetadataType)type;
+		block.length = length;
+
+		if(type == FLAC__METADATA_TYPE_APPLICATION) {
+			if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))
+				return false; /* read_callback_ sets the state for us */
+
+			if(real_length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) { /* underflow check */
+				decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;/*@@@@@@ maybe wrong error? need to resync?*/
+				return false;
+			}
+
+			real_length -= FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8;
+
+			if(decoder->private_->metadata_filter_ids_count > 0 && has_id_filtered_(decoder, block.data.application.id))
+				skip_it = !skip_it;
+		}
+
+		if(skip_it) {
+			if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length))
+				return false; /* read_callback_ sets the state for us */
+		}
+		else {
+			FLAC__bool ok = true;
+			switch(type) {
+				case FLAC__METADATA_TYPE_PADDING:
+					/* skip the padding bytes */
+					if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length))
+						ok = false; /* read_callback_ sets the state for us */
+					break;
+				case FLAC__METADATA_TYPE_APPLICATION:
+					/* remember, we read the ID already */
+					if(real_length > 0) {
+						if(0 == (block.data.application.data = malloc(real_length))) {
+							decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+							ok = false;
+						}
+						else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.data, real_length))
+							ok = false; /* read_callback_ sets the state for us */
+					}
+					else
+						block.data.application.data = 0;
+					break;
+				case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+					if(!read_metadata_vorbiscomment_(decoder, &block.data.vorbis_comment, real_length))
+						ok = false;
+					break;
+				case FLAC__METADATA_TYPE_CUESHEET:
+					if(!read_metadata_cuesheet_(decoder, &block.data.cue_sheet))
+						ok = false;
+					break;
+				case FLAC__METADATA_TYPE_PICTURE:
+					if(!read_metadata_picture_(decoder, &block.data.picture))
+						ok = false;
+					break;
+				case FLAC__METADATA_TYPE_STREAMINFO:
+				case FLAC__METADATA_TYPE_SEEKTABLE:
+					break;
+				default:
+					if(real_length > 0) {
+						if(0 == (block.data.unknown.data = malloc(real_length))) {
+							decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+							ok = false;
+						}
+						else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.unknown.data, real_length))
+							ok = false; /* read_callback_ sets the state for us */
+					}
+					else
+						block.data.unknown.data = 0;
+					break;
+			}
+			if(ok && !decoder->private_->is_seeking && decoder->private_->metadata_callback)
+				decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data);
+
+			/* now we have to free any malloc()ed data in the block */
+			switch(type) {
+				case FLAC__METADATA_TYPE_PADDING:
+					break;
+				case FLAC__METADATA_TYPE_APPLICATION:
+					if(0 != block.data.application.data)
+						free(block.data.application.data);
+					break;
+				case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+					if(0 != block.data.vorbis_comment.vendor_string.entry)
+						free(block.data.vorbis_comment.vendor_string.entry);
+					if(block.data.vorbis_comment.num_comments > 0)
+						for(i = 0; i < block.data.vorbis_comment.num_comments; i++)
+							if(0 != block.data.vorbis_comment.comments[i].entry)
+								free(block.data.vorbis_comment.comments[i].entry);
+					if(0 != block.data.vorbis_comment.comments)
+						free(block.data.vorbis_comment.comments);
+					break;
+				case FLAC__METADATA_TYPE_CUESHEET:
+					if(block.data.cue_sheet.num_tracks > 0)
+						for(i = 0; i < block.data.cue_sheet.num_tracks; i++)
+							if(0 != block.data.cue_sheet.tracks[i].indices)
+								free(block.data.cue_sheet.tracks[i].indices);
+					if(0 != block.data.cue_sheet.tracks)
+						free(block.data.cue_sheet.tracks);
+					break;
+				case FLAC__METADATA_TYPE_PICTURE:
+					if(0 != block.data.picture.mime_type)
+						free(block.data.picture.mime_type);
+					if(0 != block.data.picture.description)
+						free(block.data.picture.description);
+					if(0 != block.data.picture.data)
+						free(block.data.picture.data);
+					break;
+				case FLAC__METADATA_TYPE_STREAMINFO:
+				case FLAC__METADATA_TYPE_SEEKTABLE:
+				default:
+					if(0 != block.data.unknown.data)
+						free(block.data.unknown.data);
+					break;
+			}
+
+			if(!ok) /* anything that unsets "ok" should also make sure decoder->protected_->state is updated */
+				return false;
+		}
+	}
+
+	if(is_last) {
+		/* if this fails, it's OK, it's just a hint for the seek routine */
+		if(!FLAC__stream_decoder_get_decode_position(decoder, &decoder->private_->first_frame_offset))
+			decoder->private_->first_frame_offset = 0;
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+	}
+
+	return true;
+}
+
+FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length)
+{
+	FLAC__uint32 x;
+	uint32_t bits, used_bits = 0;
+
+	decoder->private_->stream_info.type = FLAC__METADATA_TYPE_STREAMINFO;
+	decoder->private_->stream_info.is_last = is_last;
+	decoder->private_->stream_info.length = length;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN;
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, bits))
+		return false; /* read_callback_ sets the state for us */
+	decoder->private_->stream_info.data.stream_info.min_blocksize = x;
+	used_bits += bits;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN;
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	decoder->private_->stream_info.data.stream_info.max_blocksize = x;
+	used_bits += bits;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN;
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	decoder->private_->stream_info.data.stream_info.min_framesize = x;
+	used_bits += bits;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN;
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	decoder->private_->stream_info.data.stream_info.max_framesize = x;
+	used_bits += bits;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN;
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	decoder->private_->stream_info.data.stream_info.sample_rate = x;
+	used_bits += bits;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN;
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN))
+		return false; /* read_callback_ sets the state for us */
+	decoder->private_->stream_info.data.stream_info.channels = x+1;
+	used_bits += bits;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN;
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	decoder->private_->stream_info.data.stream_info.bits_per_sample = x+1;
+	used_bits += bits;
+
+	bits = FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN;
+	if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &decoder->private_->stream_info.data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN))
+		return false; /* read_callback_ sets the state for us */
+	used_bits += bits;
+
+	if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, decoder->private_->stream_info.data.stream_info.md5sum, 16))
+		return false; /* read_callback_ sets the state for us */
+	used_bits += 16*8;
+
+	/* skip the rest of the block */
+	length -= (used_bits / 8);
+	if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length))
+		return false; /* read_callback_ sets the state for us */
+
+	return true;
+}
+
+FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length)
+{
+	FLAC__uint32 i, x;
+	FLAC__uint64 xx;
+
+	decoder->private_->seek_table.type = FLAC__METADATA_TYPE_SEEKTABLE;
+	decoder->private_->seek_table.is_last = is_last;
+	decoder->private_->seek_table.length = length;
+
+	decoder->private_->seek_table.data.seek_table.num_points = length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
+
+	/* use realloc since we may pass through here several times (e.g. after seeking) */
+	if(0 == (decoder->private_->seek_table.data.seek_table.points = safe_realloc_mul_2op_(decoder->private_->seek_table.data.seek_table.points, decoder->private_->seek_table.data.seek_table.num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) {
+		decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+	for(i = 0; i < decoder->private_->seek_table.data.seek_table.num_points; i++) {
+		if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN))
+			return false; /* read_callback_ sets the state for us */
+		decoder->private_->seek_table.data.seek_table.points[i].sample_number = xx;
+
+		if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN))
+			return false; /* read_callback_ sets the state for us */
+		decoder->private_->seek_table.data.seek_table.points[i].stream_offset = xx;
+
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN))
+			return false; /* read_callback_ sets the state for us */
+		decoder->private_->seek_table.data.seek_table.points[i].frame_samples = x;
+	}
+	length -= (decoder->private_->seek_table.data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH);
+	/* if there is a partial point left, skip over it */
+	if(length > 0) {
+		/*@@@ do a send_error_to_client_() here?  there's an argument for either way */
+		if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length))
+			return false; /* read_callback_ sets the state for us */
+	}
+
+	return true;
+}
+
+FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, uint32_t length)
+{
+	FLAC__uint32 i;
+
+	/* read vendor string */
+	if (length >= 8) {
+		length -= 8; /* vendor string length + num comments entries alone take 8 bytes */
+		if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length))
+			return false; /* read_callback_ sets the state for us */
+		if (obj->vendor_string.length > 0) {
+			if (length < obj->vendor_string.length) {
+				obj->vendor_string.length = 0;
+				obj->vendor_string.entry = 0;
+				goto skip;
+			}
+			else
+				length -= obj->vendor_string.length;
+			if (0 == (obj->vendor_string.entry = safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) {
+				decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+				return false;
+			}
+			if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length))
+				return false; /* read_callback_ sets the state for us */
+			obj->vendor_string.entry[obj->vendor_string.length] = '\0';
+		}
+		else
+			obj->vendor_string.entry = 0;
+
+		/* read num comments */
+		if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->num_comments))
+			return false; /* read_callback_ sets the state for us */
+
+		/* read comments */
+		if (obj->num_comments > 100000) {
+			/* Possibly malicious file. */
+			obj->num_comments = 0;
+			return false;
+		}
+		if (obj->num_comments > 0) {
+			if (0 == (obj->comments = safe_malloc_mul_2op_p(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) {
+				obj->num_comments = 0;
+				decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+				return false;
+			}
+			for (i = 0; i < obj->num_comments; i++) {
+				/* Initialize here just to make sure. */
+				obj->comments[i].length = 0;
+				obj->comments[i].entry = 0;
+
+				if (length < 4) {
+					obj->num_comments = i;
+					goto skip;
+				}
+				else
+					length -= 4;
+				if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) {
+					obj->num_comments = i;
+					return false; /* read_callback_ sets the state for us */
+				}
+				if (obj->comments[i].length > 0) {
+					if (length < obj->comments[i].length) {
+						obj->num_comments = i;
+						goto skip;
+					}
+					else
+						length -= obj->comments[i].length;
+					if (0 == (obj->comments[i].entry = safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) {
+						decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+						obj->num_comments = i;
+						return false;
+					}
+					memset (obj->comments[i].entry, 0, obj->comments[i].length) ;
+					if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) {
+						/* Current i-th entry is bad, so we delete it. */
+						free (obj->comments[i].entry) ;
+						obj->comments[i].entry = NULL ;
+						obj->num_comments = i;
+						goto skip;
+					}
+					obj->comments[i].entry[obj->comments[i].length] = '\0';
+				}
+				else
+					obj->comments[i].entry = 0;
+			}
+		}
+	}
+
+  skip:
+	if (length > 0) {
+		/* length > 0 can only happen on files with invalid data in comments */
+		if(obj->num_comments < 1) {
+			free(obj->comments);
+			obj->comments = NULL;
+		}
+		if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length))
+			return false; /* read_callback_ sets the state for us */
+	}
+
+	return true;
+}
+
+FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj)
+{
+	FLAC__uint32 i, j, x;
+
+	memset(obj, 0, sizeof(FLAC__StreamMetadata_CueSheet));
+
+	if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8))
+		return false; /* read_callback_ sets the state for us */
+
+	if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &obj->lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN))
+		return false; /* read_callback_ sets the state for us */
+	obj->is_cd = x? true : false;
+
+	if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN))
+		return false; /* read_callback_ sets the state for us */
+	obj->num_tracks = x;
+
+	if(obj->num_tracks > 0) {
+		if(0 == (obj->tracks = safe_calloc_(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+			return false;
+		}
+		for(i = 0; i < obj->num_tracks; i++) {
+			FLAC__StreamMetadata_CueSheet_Track *track = &obj->tracks[i];
+			if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN))
+				return false; /* read_callback_ sets the state for us */
+
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN))
+				return false; /* read_callback_ sets the state for us */
+			track->number = (FLAC__byte)x;
+
+			if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8))
+				return false; /* read_callback_ sets the state for us */
+
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN))
+				return false; /* read_callback_ sets the state for us */
+			track->type = x;
+
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN))
+				return false; /* read_callback_ sets the state for us */
+			track->pre_emphasis = x;
+
+			if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN))
+				return false; /* read_callback_ sets the state for us */
+
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN))
+				return false; /* read_callback_ sets the state for us */
+			track->num_indices = (FLAC__byte)x;
+
+			if(track->num_indices > 0) {
+				if(0 == (track->indices = safe_calloc_(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) {
+					decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+					return false;
+				}
+				for(j = 0; j < track->num_indices; j++) {
+					FLAC__StreamMetadata_CueSheet_Index *indx = &track->indices[j];
+					if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &indx->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN))
+						return false; /* read_callback_ sets the state for us */
+
+					if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN))
+						return false; /* read_callback_ sets the state for us */
+					indx->number = (FLAC__byte)x;
+
+					if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN))
+						return false; /* read_callback_ sets the state for us */
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj)
+{
+	FLAC__uint32 x;
+
+	/* read type */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	obj->type = x;
+
+	/* read MIME type */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN))
+		return false; /* read_callback_ sets the state for us */
+	if(0 == (obj->mime_type = safe_malloc_add_2op_(x, /*+*/1))) {
+		decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+	if(x > 0) {
+		if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->mime_type, x))
+			return false; /* read_callback_ sets the state for us */
+	}
+	obj->mime_type[x] = '\0';
+
+	/* read description */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN))
+		return false; /* read_callback_ sets the state for us */
+	if(0 == (obj->description = safe_malloc_add_2op_(x, /*+*/1))) {
+		decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+	if(x > 0) {
+		if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->description, x))
+			return false; /* read_callback_ sets the state for us */
+	}
+	obj->description[x] = '\0';
+
+	/* read width */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	/* read height */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	/* read depth */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	/* read colors */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN))
+		return false; /* read_callback_ sets the state for us */
+
+	/* read data */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &(obj->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN))
+		return false; /* read_callback_ sets the state for us */
+	if(0 == (obj->data = safe_malloc_(obj->data_length))) {
+		decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+	if(obj->data_length > 0) {
+		if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->data, obj->data_length))
+			return false; /* read_callback_ sets the state for us */
+	}
+
+	return true;
+}
+
+FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder)
+{
+	FLAC__uint32 x;
+	uint32_t i, skip;
+
+	/* skip the version and flags bytes */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 24))
+		return false; /* read_callback_ sets the state for us */
+	/* get the size (in bytes) to skip */
+	skip = 0;
+	for(i = 0; i < 4; i++) {
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+			return false; /* read_callback_ sets the state for us */
+		skip <<= 7;
+		skip |= (x & 0x7f);
+	}
+	/* skip the rest of the tag */
+	if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, skip))
+		return false; /* read_callback_ sets the state for us */
+	return true;
+}
+
+FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder)
+{
+	FLAC__uint32 x;
+	FLAC__bool first = true;
+
+	/* If we know the total number of samples in the stream, stop if we've read that many. */
+	/* This will stop us, for example, from wasting time trying to sync on an ID3V1 tag. */
+	if(FLAC__stream_decoder_get_total_samples(decoder) > 0) {
+		if(decoder->private_->samples_decoded >= FLAC__stream_decoder_get_total_samples(decoder)) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM;
+			return true;
+		}
+	}
+
+	/* make sure we're byte aligned */
+	if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) {
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input)))
+			return false; /* read_callback_ sets the state for us */
+	}
+
+	while(1) {
+		if(decoder->private_->cached) {
+			x = (FLAC__uint32)decoder->private_->lookahead;
+			decoder->private_->cached = false;
+		}
+		else {
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+				return false; /* read_callback_ sets the state for us */
+		}
+		if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
+			decoder->private_->header_warmup[0] = (FLAC__byte)x;
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+				return false; /* read_callback_ sets the state for us */
+
+			/* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */
+			/* else we have to check if the second byte is the end of a sync code */
+			if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
+				decoder->private_->lookahead = (FLAC__byte)x;
+				decoder->private_->cached = true;
+			}
+			else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */
+				decoder->private_->header_warmup[1] = (FLAC__byte)x;
+				decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME;
+				return true;
+			}
+		}
+		if(first) {
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+			first = false;
+		}
+	}
+
+	return true;
+}
+
+FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode)
+{
+	uint32_t channel;
+	uint32_t i;
+	FLAC__int32 mid, side;
+	uint32_t frame_crc; /* the one we calculate from the input stream */
+	FLAC__uint32 x;
+
+	*got_a_frame = false;
+
+	/* init the CRC */
+	frame_crc = 0;
+	frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[0], frame_crc);
+	frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[1], frame_crc);
+	FLAC__bitreader_reset_read_crc16(decoder->private_->input, (FLAC__uint16)frame_crc);
+
+	if(!read_frame_header_(decoder))
+		return false;
+	if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means we didn't sync on a valid header */
+		return true;
+	if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels))
+		return false;
+	for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) {
+		/*
+		 * first figure the correct bits-per-sample of the subframe
+		 */
+		uint32_t bps = decoder->private_->frame.header.bits_per_sample;
+		switch(decoder->private_->frame.header.channel_assignment) {
+			case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT:
+				/* no adjustment needed */
+				break;
+			case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE:
+				if(channel == 1)
+					bps++;
+				break;
+			case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+				if(channel == 0)
+					bps++;
+				break;
+			case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE:
+				if(channel == 1)
+					bps++;
+				break;
+			default: break;
+		}
+		/*
+		 * now read it
+		 */
+		if(!read_subframe_(decoder, channel, bps, do_full_decode))
+			return false;
+		if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */
+			return true;
+	}
+	if(!read_zero_padding_(decoder))
+		return false;
+	if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption (i.e. "zero bits" were not all zeroes) */
+		return true;
+
+	/*
+	 * Read the frame CRC-16 from the footer and check
+	 */
+	frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input);
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN))
+		return false; /* read_callback_ sets the state for us */
+	if(frame_crc == x) {
+		if(do_full_decode) {
+			/* Undo any special channel coding */
+			switch(decoder->private_->frame.header.channel_assignment) {
+				case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT:
+					/* do nothing */
+					break;
+				case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE:
+					for(i = 0; i < decoder->private_->frame.header.blocksize; i++)
+						decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i];
+					break;
+				case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+					for(i = 0; i < decoder->private_->frame.header.blocksize; i++)
+						decoder->private_->output[0][i] += decoder->private_->output[1][i];
+					break;
+				case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE:
+					for(i = 0; i < decoder->private_->frame.header.blocksize; i++) {
+						mid = decoder->private_->output[0][i];
+						side = decoder->private_->output[1][i];
+						mid = ((uint32_t) mid) << 1;
+						mid |= (side & 1); /* i.e. if 'side' is odd... */
+						decoder->private_->output[0][i] = (mid + side) >> 1;
+						decoder->private_->output[1][i] = (mid - side) >> 1;
+					}
+					break;
+				default: break;
+			}
+		}
+	}
+	else {
+		/* Bad frame, emit error and zero the output signal */
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH);
+		if(do_full_decode) {
+			for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) {
+				memset(decoder->private_->output[channel], 0, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize);
+			}
+		}
+	}
+
+	*got_a_frame = true;
+
+	/* we wait to update fixed_block_size until here, when we're sure we've got a proper frame and hence a correct blocksize */
+	if(decoder->private_->next_fixed_block_size)
+		decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size;
+
+	/* put the latest values into the public section of the decoder instance */
+	decoder->protected_->channels = decoder->private_->frame.header.channels;
+	decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment;
+	decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample;
+	decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate;
+	decoder->protected_->blocksize = decoder->private_->frame.header.blocksize;
+	decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize;
+
+	/* write it */
+	if(do_full_decode) {
+		if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED;
+			return false;
+		}
+	}
+
+	decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+	return true;
+}
+
+FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder)
+{
+	FLAC__uint32 x;
+	FLAC__uint64 xx;
+	uint32_t i, blocksize_hint = 0, sample_rate_hint = 0;
+	FLAC__byte crc8, raw_header[16]; /* MAGIC NUMBER based on the maximum frame header size, including CRC */
+	uint32_t raw_header_len;
+	FLAC__bool is_unparseable = false;
+
+	/* init the raw header with the saved bits from synchronization */
+	raw_header[0] = decoder->private_->header_warmup[0];
+	raw_header[1] = decoder->private_->header_warmup[1];
+	raw_header_len = 2;
+
+	/* check to make sure that reserved bit is 0 */
+	if(raw_header[1] & 0x02) /* MAGIC NUMBER */
+		is_unparseable = true;
+
+	/*
+	 * Note that along the way as we read the header, we look for a sync
+	 * code inside.  If we find one it would indicate that our original
+	 * sync was bad since there cannot be a sync code in a valid header.
+	 *
+	 * Three kinds of things can go wrong when reading the frame header:
+	 *  1) We may have sync'ed incorrectly and not landed on a frame header.
+	 *     If we don't find a sync code, it can end up looking like we read
+	 *     a valid but unparseable header, until getting to the frame header
+	 *     CRC.  Even then we could get a false positive on the CRC.
+	 *  2) We may have sync'ed correctly but on an unparseable frame (from a
+	 *     future encoder).
+	 *  3) We may be on a damaged frame which appears valid but unparseable.
+	 *
+	 * For all these reasons, we try and read a complete frame header as
+	 * long as it seems valid, even if unparseable, up until the frame
+	 * header CRC.
+	 */
+
+	/*
+	 * read in the raw header as bytes so we can CRC it, and parse it on the way
+	 */
+	for(i = 0; i < 2; i++) {
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+			return false; /* read_callback_ sets the state for us */
+		if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
+			/* if we get here it means our original sync was erroneous since the sync code cannot appear in the header */
+			decoder->private_->lookahead = (FLAC__byte)x;
+			decoder->private_->cached = true;
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER);
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+			return true;
+		}
+		raw_header[raw_header_len++] = (FLAC__byte)x;
+	}
+
+	switch(x = raw_header[2] >> 4) {
+		case 0:
+			is_unparseable = true;
+			break;
+		case 1:
+			decoder->private_->frame.header.blocksize = 192;
+			break;
+		case 2:
+		case 3:
+		case 4:
+		case 5:
+			decoder->private_->frame.header.blocksize = 576 << (x-2);
+			break;
+		case 6:
+		case 7:
+			blocksize_hint = x;
+			break;
+		case 8:
+		case 9:
+		case 10:
+		case 11:
+		case 12:
+		case 13:
+		case 14:
+		case 15:
+			decoder->private_->frame.header.blocksize = 256 << (x-8);
+			break;
+		default:
+			break;
+	}
+
+	switch(x = raw_header[2] & 0x0f) {
+		case 0:
+			if(decoder->private_->has_stream_info)
+				decoder->private_->frame.header.sample_rate = decoder->private_->stream_info.data.stream_info.sample_rate;
+			else
+				is_unparseable = true;
+			break;
+		case 1:
+			decoder->private_->frame.header.sample_rate = 88200;
+			break;
+		case 2:
+			decoder->private_->frame.header.sample_rate = 176400;
+			break;
+		case 3:
+			decoder->private_->frame.header.sample_rate = 192000;
+			break;
+		case 4:
+			decoder->private_->frame.header.sample_rate = 8000;
+			break;
+		case 5:
+			decoder->private_->frame.header.sample_rate = 16000;
+			break;
+		case 6:
+			decoder->private_->frame.header.sample_rate = 22050;
+			break;
+		case 7:
+			decoder->private_->frame.header.sample_rate = 24000;
+			break;
+		case 8:
+			decoder->private_->frame.header.sample_rate = 32000;
+			break;
+		case 9:
+			decoder->private_->frame.header.sample_rate = 44100;
+			break;
+		case 10:
+			decoder->private_->frame.header.sample_rate = 48000;
+			break;
+		case 11:
+			decoder->private_->frame.header.sample_rate = 96000;
+			break;
+		case 12:
+		case 13:
+		case 14:
+			sample_rate_hint = x;
+			break;
+		case 15:
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER);
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+			return true;
+		default:
+			break;
+	}
+
+	x = (uint32_t)(raw_header[3] >> 4);
+	if(x & 8) {
+		decoder->private_->frame.header.channels = 2;
+		switch(x & 7) {
+			case 0:
+				decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE;
+				break;
+			case 1:
+				decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE;
+				break;
+			case 2:
+				decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE;
+				break;
+			default:
+				is_unparseable = true;
+				break;
+		}
+	}
+	else {
+		decoder->private_->frame.header.channels = (uint32_t)x + 1;
+		decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT;
+	}
+
+	switch(x = (uint32_t)(raw_header[3] & 0x0e) >> 1) {
+		case 0:
+			if(decoder->private_->has_stream_info)
+				decoder->private_->frame.header.bits_per_sample = decoder->private_->stream_info.data.stream_info.bits_per_sample;
+			else
+				is_unparseable = true;
+			break;
+		case 1:
+			decoder->private_->frame.header.bits_per_sample = 8;
+			break;
+		case 2:
+			decoder->private_->frame.header.bits_per_sample = 12;
+			break;
+		case 4:
+			decoder->private_->frame.header.bits_per_sample = 16;
+			break;
+		case 5:
+			decoder->private_->frame.header.bits_per_sample = 20;
+			break;
+		case 6:
+			decoder->private_->frame.header.bits_per_sample = 24;
+			break;
+		case 3:
+		case 7:
+			is_unparseable = true;
+			break;
+		default:
+			break;
+	}
+
+	/* check to make sure that reserved bit is 0 */
+	if(raw_header[3] & 0x01) /* MAGIC NUMBER */
+		is_unparseable = true;
+
+	/* read the frame's starting sample number (or frame number as the case may be) */
+	if(
+		raw_header[1] & 0x01 ||
+		/*@@@ this clause is a concession to the old way of doing variable blocksize; the only known implementation is flake and can probably be removed without inconveniencing anyone */
+		(decoder->private_->has_stream_info && decoder->private_->stream_info.data.stream_info.min_blocksize != decoder->private_->stream_info.data.stream_info.max_blocksize)
+	) { /* variable blocksize */
+		if(!FLAC__bitreader_read_utf8_uint64(decoder->private_->input, &xx, raw_header, &raw_header_len))
+			return false; /* read_callback_ sets the state for us */
+		if(xx == FLAC__U64L(0xffffffffffffffff)) { /* i.e. non-UTF8 code... */
+			decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */
+			decoder->private_->cached = true;
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER);
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+			return true;
+		}
+		decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER;
+		decoder->private_->frame.header.number.sample_number = xx;
+	}
+	else { /* fixed blocksize */
+		if(!FLAC__bitreader_read_utf8_uint32(decoder->private_->input, &x, raw_header, &raw_header_len))
+			return false; /* read_callback_ sets the state for us */
+		if(x == 0xffffffff) { /* i.e. non-UTF8 code... */
+			decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */
+			decoder->private_->cached = true;
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER);
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+			return true;
+		}
+		decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER;
+		decoder->private_->frame.header.number.frame_number = x;
+	}
+
+	if(blocksize_hint) {
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+			return false; /* read_callback_ sets the state for us */
+		raw_header[raw_header_len++] = (FLAC__byte)x;
+		if(blocksize_hint == 7) {
+			FLAC__uint32 _x;
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8))
+				return false; /* read_callback_ sets the state for us */
+			raw_header[raw_header_len++] = (FLAC__byte)_x;
+			x = (x << 8) | _x;
+		}
+		decoder->private_->frame.header.blocksize = x+1;
+	}
+
+	if(sample_rate_hint) {
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+			return false; /* read_callback_ sets the state for us */
+		raw_header[raw_header_len++] = (FLAC__byte)x;
+		if(sample_rate_hint != 12) {
+			FLAC__uint32 _x;
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8))
+				return false; /* read_callback_ sets the state for us */
+			raw_header[raw_header_len++] = (FLAC__byte)_x;
+			x = (x << 8) | _x;
+		}
+		if(sample_rate_hint == 12)
+			decoder->private_->frame.header.sample_rate = x*1000;
+		else if(sample_rate_hint == 13)
+			decoder->private_->frame.header.sample_rate = x;
+		else
+			decoder->private_->frame.header.sample_rate = x*10;
+	}
+
+	/* read the CRC-8 byte */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
+		return false; /* read_callback_ sets the state for us */
+	crc8 = (FLAC__byte)x;
+
+	if(FLAC__crc8(raw_header, raw_header_len) != crc8) {
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER);
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		return true;
+	}
+
+	/* calculate the sample number from the frame number if needed */
+	decoder->private_->next_fixed_block_size = 0;
+	if(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) {
+		x = decoder->private_->frame.header.number.frame_number;
+		decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER;
+		if(decoder->private_->fixed_block_size)
+			decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->fixed_block_size * (FLAC__uint64)x;
+		else if(decoder->private_->has_stream_info) {
+			if(decoder->private_->stream_info.data.stream_info.min_blocksize == decoder->private_->stream_info.data.stream_info.max_blocksize) {
+				decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->stream_info.data.stream_info.min_blocksize * (FLAC__uint64)x;
+				decoder->private_->next_fixed_block_size = decoder->private_->stream_info.data.stream_info.max_blocksize;
+			}
+			else
+				is_unparseable = true;
+		}
+		else if(x == 0) {
+			decoder->private_->frame.header.number.sample_number = 0;
+			decoder->private_->next_fixed_block_size = decoder->private_->frame.header.blocksize;
+		}
+		else {
+			/* can only get here if the stream has invalid frame numbering and no STREAMINFO, so assume it's not the last (possibly short) frame */
+			decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->frame.header.blocksize * (FLAC__uint64)x;
+		}
+	}
+
+	if(is_unparseable) {
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM);
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		return true;
+	}
+
+	return true;
+}
+
+FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode)
+{
+	FLAC__uint32 x;
+	FLAC__bool wasted_bits;
+	uint32_t i;
+
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) /* MAGIC NUMBER */
+		return false; /* read_callback_ sets the state for us */
+
+	wasted_bits = (x & 1);
+	x &= 0xfe;
+
+	if(wasted_bits) {
+		uint32_t u;
+		if(!FLAC__bitreader_read_unary_unsigned(decoder->private_->input, &u))
+			return false; /* read_callback_ sets the state for us */
+		decoder->private_->frame.subframes[channel].wasted_bits = u+1;
+		if (decoder->private_->frame.subframes[channel].wasted_bits >= bps)
+			return false;
+		bps -= decoder->private_->frame.subframes[channel].wasted_bits;
+	}
+	else
+		decoder->private_->frame.subframes[channel].wasted_bits = 0;
+
+	/*
+	 * Lots of magic numbers here
+	 */
+	if(x & 0x80) {
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		return true;
+	}
+	else if(x == 0) {
+		if(!read_subframe_constant_(decoder, channel, bps, do_full_decode))
+			return false;
+	}
+	else if(x == 2) {
+		if(!read_subframe_verbatim_(decoder, channel, bps, do_full_decode))
+			return false;
+	}
+	else if(x < 16) {
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM);
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		return true;
+	}
+	else if(x <= 24) {
+		if(!read_subframe_fixed_(decoder, channel, bps, (x>>1)&7, do_full_decode))
+			return false;
+		if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */
+			return true;
+	}
+	else if(x < 64) {
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM);
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		return true;
+	}
+	else {
+		if(!read_subframe_lpc_(decoder, channel, bps, ((x>>1)&31)+1, do_full_decode))
+			return false;
+		if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */
+			return true;
+	}
+
+	if(wasted_bits && do_full_decode) {
+		x = decoder->private_->frame.subframes[channel].wasted_bits;
+		for(i = 0; i < decoder->private_->frame.header.blocksize; i++) {
+			uint32_t val = decoder->private_->output[channel][i];
+			decoder->private_->output[channel][i] = (val << x);
+		}
+	}
+
+	return true;
+}
+
+FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode)
+{
+	FLAC__Subframe_Constant *subframe = &decoder->private_->frame.subframes[channel].data.constant;
+	FLAC__int32 x;
+	uint32_t i;
+	FLAC__int32 *output = decoder->private_->output[channel];
+
+	decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT;
+
+	if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps))
+		return false; /* read_callback_ sets the state for us */
+
+	subframe->value = x;
+
+	/* decode the subframe */
+	if(do_full_decode) {
+		for(i = 0; i < decoder->private_->frame.header.blocksize; i++)
+			output[i] = x;
+	}
+
+	return true;
+}
+
+FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode)
+{
+	FLAC__Subframe_Fixed *subframe = &decoder->private_->frame.subframes[channel].data.fixed;
+	FLAC__int32 i32;
+	FLAC__uint32 u32;
+	uint32_t u;
+
+	decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_FIXED;
+
+	subframe->residual = decoder->private_->residual[channel];
+	subframe->order = order;
+
+	/* read warm-up samples */
+	for(u = 0; u < order; u++) {
+		if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps))
+			return false; /* read_callback_ sets the state for us */
+		subframe->warmup[u] = i32;
+	}
+
+	/* read entropy coding method info */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32;
+	switch(subframe->entropy_coding_method.type) {
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE:
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2:
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN))
+				return false; /* read_callback_ sets the state for us */
+			if(decoder->private_->frame.header.blocksize >> u32 < order) {
+				send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+				decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+				return true;
+			}
+			subframe->entropy_coding_method.data.partitioned_rice.order = u32;
+			subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel];
+			break;
+		default:
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM);
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+			return true;
+	}
+
+	/* read residual */
+	switch(subframe->entropy_coding_method.type) {
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE:
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2:
+			if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2))
+				return false;
+			break;
+		default:
+			break;
+	}
+
+	/* decode the subframe */
+	if(do_full_decode) {
+		memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order);
+		FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order);
+	}
+
+	return true;
+}
+
+FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode)
+{
+	FLAC__Subframe_LPC *subframe = &decoder->private_->frame.subframes[channel].data.lpc;
+	FLAC__int32 i32;
+	FLAC__uint32 u32;
+	uint32_t u;
+
+	decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_LPC;
+
+	subframe->residual = decoder->private_->residual[channel];
+	subframe->order = order;
+
+	/* read warm-up samples */
+	for(u = 0; u < order; u++) {
+		if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps))
+			return false; /* read_callback_ sets the state for us */
+		subframe->warmup[u] = i32;
+	}
+
+	/* read qlp coeff precision */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN))
+		return false; /* read_callback_ sets the state for us */
+	if(u32 == (1u << FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN) - 1) {
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		return true;
+	}
+	subframe->qlp_coeff_precision = u32+1;
+
+	/* read qlp shift */
+	if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN))
+		return false; /* read_callback_ sets the state for us */
+	if(i32 < 0) {
+		send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+		decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		return true;
+	}
+	subframe->quantization_level = i32;
+
+	/* read quantized lp coefficiencts */
+	for(u = 0; u < order; u++) {
+		if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, subframe->qlp_coeff_precision))
+			return false; /* read_callback_ sets the state for us */
+		subframe->qlp_coeff[u] = i32;
+	}
+
+	/* read entropy coding method info */
+	if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN))
+		return false; /* read_callback_ sets the state for us */
+	subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32;
+	switch(subframe->entropy_coding_method.type) {
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE:
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2:
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN))
+				return false; /* read_callback_ sets the state for us */
+			if(decoder->private_->frame.header.blocksize >> u32 < order) {
+				send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+				decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+				return true;
+			}
+			subframe->entropy_coding_method.data.partitioned_rice.order = u32;
+			subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel];
+			break;
+		default:
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM);
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+			return true;
+	}
+
+	/* read residual */
+	switch(subframe->entropy_coding_method.type) {
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE:
+		case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2:
+			if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2))
+				return false;
+			break;
+		default:
+			break;
+	}
+
+	/* decode the subframe */
+	if(do_full_decode) {
+		memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order);
+		if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32)
+			if(bps <= 16 && subframe->qlp_coeff_precision <= 16)
+				decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order);
+			else
+				decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order);
+		else
+			decoder->private_->local_lpc_restore_signal_64bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order);
+	}
+
+	return true;
+}
+
+FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode)
+{
+	FLAC__Subframe_Verbatim *subframe = &decoder->private_->frame.subframes[channel].data.verbatim;
+	FLAC__int32 x, *residual = decoder->private_->residual[channel];
+	uint32_t i;
+
+	decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM;
+
+	subframe->data = residual;
+
+	for(i = 0; i < decoder->private_->frame.header.blocksize; i++) {
+		if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps))
+			return false; /* read_callback_ sets the state for us */
+		residual[i] = x;
+	}
+
+	/* decode the subframe */
+	if(do_full_decode)
+		memcpy(decoder->private_->output[channel], subframe->data, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize);
+
+	return true;
+}
+
+FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_t predictor_order, uint32_t partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended)
+{
+	FLAC__uint32 rice_parameter;
+	int i;
+	uint32_t partition, sample, u;
+	const uint32_t partitions = 1u << partition_order;
+	const uint32_t partition_samples = partition_order > 0? decoder->private_->frame.header.blocksize >> partition_order : decoder->private_->frame.header.blocksize - predictor_order;
+	const uint32_t plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN;
+	const uint32_t pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER;
+
+	if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, flac_max(6u, partition_order))) {
+		decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;
+		return false;
+	}
+
+	sample = 0;
+	for(partition = 0; partition < partitions; partition++) {
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, plen))
+			return false; /* read_callback_ sets the state for us */
+		partitioned_rice_contents->parameters[partition] = rice_parameter;
+		if(rice_parameter < pesc) {
+			partitioned_rice_contents->raw_bits[partition] = 0;
+			u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order;
+			if(!FLAC__bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter))
+				return false; /* read_callback_ sets the state for us */
+			sample += u;
+		}
+		else {
+			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN))
+				return false; /* read_callback_ sets the state for us */
+			partitioned_rice_contents->raw_bits[partition] = rice_parameter;
+			for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) {
+				if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter))
+					return false; /* read_callback_ sets the state for us */
+				residual[sample] = i;
+			}
+		}
+	}
+
+	return true;
+}
+
+FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder)
+{
+	if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) {
+		FLAC__uint32 zero = 0;
+		if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &zero, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input)))
+			return false; /* read_callback_ sets the state for us */
+		if(zero != 0) {
+			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
+		}
+	}
+	return true;
+}
+
+FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+	FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder *)client_data;
+
+	if(
+		decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data)
+	) {
+		*bytes = 0;
+		decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM;
+		return false;
+	}
+	else if(*bytes > 0) {
+		/* While seeking, it is possible for our seek to land in the
+		 * middle of audio data that looks exactly like a frame header
+		 * from a future version of an encoder.  When that happens, our
+		 * error callback will get an
+		 * FLAC__STREAM_DECODER_UNPARSEABLE_STREAM and increment its
+		 * unparseable_frame_count.  But there is a remote possibility
+		 * that it is properly synced at such a "future-codec frame",
+		 * so to make sure, we wait to see many "unparseable" errors in
+		 * a row before bailing out.
+		 */
+		if(decoder->private_->is_seeking && decoder->private_->unparseable_frame_count > 20) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED;
+			return false;
+		}
+		else {
+			const FLAC__StreamDecoderReadStatus status =
+				decoder->private_->read_callback(decoder, buffer, bytes, decoder->private_->client_data)
+			;
+			if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) {
+				decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED;
+				return false;
+			}
+			else if(*bytes == 0) {
+				if(
+					status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM ||
+					(
+						decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data)
+					)
+				) {
+					decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM;
+					return false;
+				}
+				else
+					return true;
+			}
+			else
+				return true;
+		}
+	}
+	else {
+		/* abort to avoid a deadlock */
+		decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED;
+		return false;
+	}
+	/* [1] @@@ HACK NOTE: The end-of-stream checking has to be hacked around
+	 * for Ogg FLAC.  This is because the ogg decoder aspect can lose sync
+	 * and at the same time hit the end of the stream (for example, seeking
+	 * to a point that is after the beginning of the last Ogg page).  There
+	 * is no way to report an Ogg sync loss through the callbacks (see note
+	 * in read_callback_ogg_aspect_()) so it returns CONTINUE with *bytes==0.
+	 * So to keep the decoder from stopping at this point we gate the call
+	 * to the eof_callback and let the Ogg decoder aspect set the
+	 * end-of-stream state when it is needed.
+	 */
+}
+
+FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+{
+	if(decoder->private_->is_seeking) {
+		FLAC__uint64 this_frame_sample = frame->header.number.sample_number;
+		FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize;
+		FLAC__uint64 target_sample = decoder->private_->target_sample;
+
+		decoder->private_->last_frame = *frame; /* save the frame */
+		if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */
+			uint32_t delta = (uint32_t)(target_sample - this_frame_sample);
+			/* kick out of seek mode */
+			decoder->private_->is_seeking = false;
+			/* shift out the samples before target_sample */
+			if(delta > 0) {
+				uint32_t channel;
+				const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS];
+				for(channel = 0; channel < frame->header.channels; channel++)
+					newbuffer[channel] = buffer[channel] + delta;
+				decoder->private_->last_frame.header.blocksize -= delta;
+				decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta;
+				/* write the relevant samples */
+				return decoder->private_->write_callback(decoder, &decoder->private_->last_frame, newbuffer, decoder->private_->client_data);
+			}
+			else {
+				/* write the relevant samples */
+				return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data);
+			}
+		}
+		else {
+			return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+		}
+	}
+	else {
+		/*
+		 * If we never got STREAMINFO, turn off MD5 checking to save
+		 * cycles since we don't have a sum to compare to anyway
+		 */
+		if(!decoder->private_->has_stream_info)
+			decoder->private_->do_md5_checking = false;
+		if(decoder->private_->do_md5_checking) {
+			if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8))
+				return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+		}
+		return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data);
+	}
+}
+
+void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status)
+{
+	if(!decoder->private_->is_seeking)
+		decoder->private_->error_callback(decoder, status, decoder->private_->client_data);
+	else if(status == FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM)
+		decoder->private_->unparseable_frame_count++;
+}
+
+FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample)
+{
+	FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample, this_frame_sample;
+	FLAC__int64 pos = -1;
+	int i;
+	uint32_t approx_bytes_per_frame;
+	FLAC__bool first_seek = true;
+	const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder);
+	const uint32_t min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize;
+	const uint32_t max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize;
+	const uint32_t max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize;
+	const uint32_t min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize;
+	/* take these from the current frame in case they've changed mid-stream */
+	uint32_t channels = FLAC__stream_decoder_get_channels(decoder);
+	uint32_t bps = FLAC__stream_decoder_get_bits_per_sample(decoder);
+	const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0;
+
+	/* use values from stream info if we didn't decode a frame */
+	if(channels == 0)
+		channels = decoder->private_->stream_info.data.stream_info.channels;
+	if(bps == 0)
+		bps = decoder->private_->stream_info.data.stream_info.bits_per_sample;
+
+	/* we are just guessing here */
+	if(max_framesize > 0)
+		approx_bytes_per_frame = (max_framesize + min_framesize) / 2 + 1;
+	/*
+	 * Check if it's a known fixed-blocksize stream.  Note that though
+	 * the spec doesn't allow zeroes in the STREAMINFO block, we may
+	 * never get a STREAMINFO block when decoding so the value of
+	 * min_blocksize might be zero.
+	 */
+	else if(min_blocksize == max_blocksize && min_blocksize > 0) {
+		/* note there are no () around 'bps/8' to keep precision up since it's an integer calculation */
+		approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64;
+	}
+	else
+		approx_bytes_per_frame = 4096 * channels * bps/8 + 64;
+
+	/*
+	 * First, we set an upper and lower bound on where in the
+	 * stream we will search.  For now we assume the worst case
+	 * scenario, which is our best guess at the beginning of
+	 * the first frame and end of the stream.
+	 */
+	lower_bound = first_frame_offset;
+	lower_bound_sample = 0;
+	upper_bound = stream_length;
+	upper_bound_sample = total_samples > 0 ? total_samples : target_sample /*estimate it*/;
+
+	/*
+	 * Now we refine the bounds if we have a seektable with
+	 * suitable points.  Note that according to the spec they
+	 * must be ordered by ascending sample number.
+	 *
+	 * Note: to protect against invalid seek tables we will ignore points
+	 * that have frame_samples==0 or sample_number>=total_samples
+	 */
+	if(seek_table) {
+		FLAC__uint64 new_lower_bound = lower_bound;
+		FLAC__uint64 new_upper_bound = upper_bound;
+		FLAC__uint64 new_lower_bound_sample = lower_bound_sample;
+		FLAC__uint64 new_upper_bound_sample = upper_bound_sample;
+
+		/* find the closest seek point <= target_sample, if it exists */
+		for(i = (int)seek_table->num_points - 1; i >= 0; i--) {
+			if(
+				seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER &&
+				seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */
+				(total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */
+				seek_table->points[i].sample_number <= target_sample
+			)
+				break;
+		}
+		if(i >= 0) { /* i.e. we found a suitable seek point... */
+			new_lower_bound = first_frame_offset + seek_table->points[i].stream_offset;
+			new_lower_bound_sample = seek_table->points[i].sample_number;
+		}
+
+		/* find the closest seek point > target_sample, if it exists */
+		for(i = 0; i < (int)seek_table->num_points; i++) {
+			if(
+				seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER &&
+				seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */
+				(total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */
+				seek_table->points[i].sample_number > target_sample
+			)
+				break;
+		}
+		if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */
+			new_upper_bound = first_frame_offset + seek_table->points[i].stream_offset;
+			new_upper_bound_sample = seek_table->points[i].sample_number;
+		}
+		/* final protection against unsorted seek tables; keep original values if bogus */
+		if(new_upper_bound >= new_lower_bound) {
+			lower_bound = new_lower_bound;
+			upper_bound = new_upper_bound;
+			lower_bound_sample = new_lower_bound_sample;
+			upper_bound_sample = new_upper_bound_sample;
+		}
+	}
+
+	/* there are 2 insidious ways that the following equality occurs, which
+	 * we need to fix:
+	 *  1) total_samples is 0 (unknown) and target_sample is 0
+	 *  2) total_samples is 0 (unknown) and target_sample happens to be
+	 *     exactly equal to the last seek point in the seek table; this
+	 *     means there is no seek point above it, and upper_bound_samples
+	 *     remains equal to the estimate (of target_samples) we made above
+	 * in either case it does not hurt to move upper_bound_sample up by 1
+	 */
+	if(upper_bound_sample == lower_bound_sample)
+		upper_bound_sample++;
+
+	decoder->private_->target_sample = target_sample;
+	while(1) {
+		/* check if the bounds are still ok */
+		if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+			return false;
+		}
+		pos = (FLAC__int64)lower_bound + (FLAC__int64)((double)(target_sample - lower_bound_sample) / (double)(upper_bound_sample - lower_bound_sample) * (double)(upper_bound - lower_bound)) - approx_bytes_per_frame;
+		if(pos >= (FLAC__int64)upper_bound)
+			pos = (FLAC__int64)upper_bound - 1;
+		if(pos < (FLAC__int64)lower_bound)
+			pos = (FLAC__int64)lower_bound;
+		if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+			return false;
+		}
+		if(!FLAC__stream_decoder_flush(decoder)) {
+			/* above call sets the state for us */
+			return false;
+		}
+		/* Now we need to get a frame.  First we need to reset our
+		 * unparseable_frame_count; if we get too many unparseable
+		 * frames in a row, the read callback will return
+		 * FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing
+		 * FLAC__stream_decoder_process_single() to return false.
+		 */
+		decoder->private_->unparseable_frame_count = 0;
+		if(!FLAC__stream_decoder_process_single(decoder) ||
+		   decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+			return false;
+		}
+		/* our write callback will change the state when it gets to the target frame */
+		/* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */
+		if(!decoder->private_->is_seeking)
+			break;
+
+		this_frame_sample = decoder->private_->last_frame.header.number.sample_number;
+
+		if (0 == decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) {
+			if (pos == (FLAC__int64)lower_bound) {
+				/* can't move back any more than the first frame, something is fatally wrong */
+				decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+				return false;
+			}
+			/* our last move backwards wasn't big enough, try again */
+			approx_bytes_per_frame = approx_bytes_per_frame? approx_bytes_per_frame * 2 : 16;
+			continue;
+		}
+		/* allow one seek over upper bound, so we can get a correct upper_bound_sample for streams with unknown total_samples */
+		first_seek = false;
+
+		/* make sure we are not seeking in corrupted stream */
+		if (this_frame_sample < lower_bound_sample) {
+			decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+			return false;
+		}
+
+		/* we need to narrow the search */
+		if(target_sample < this_frame_sample) {
+			upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize;
+/*@@@@@@ what will decode position be if at end of stream? */
+			if(!FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) {
+				decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+				return false;
+			}
+			approx_bytes_per_frame = (uint32_t)(2 * (upper_bound - pos) / 3 + 16);
+		}
+		else { /* target_sample >= this_frame_sample + this frame's blocksize */
+			lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize;
+			if(!FLAC__stream_decoder_get_decode_position(decoder, &lower_bound)) {
+				decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR;
+				return false;
+			}
+			approx_bytes_per_frame = (uint32_t)(2 * (lower_bound - pos) / 3 + 16);
+		}
+	}
+
+	return true;
+}
+
+FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+	(void)client_data;
+
+	if(*bytes > 0) {
+		*bytes = fread(buffer, sizeof(FLAC__byte), *bytes, decoder->private_->file);
+		if(ferror(decoder->private_->file))
+			return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+		else if(*bytes == 0)
+			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+		else
+			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+	}
+	else
+		return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
+}
+
+FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+	(void)client_data;
+
+	if(decoder->private_->file == stdin)
+		return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+	else if(fseeko(decoder->private_->file, (FLAC__off_t)absolute_byte_offset, SEEK_SET) < 0)
+		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+	else
+		return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+	FLAC__off_t pos;
+	(void)client_data;
+
+	if(decoder->private_->file == stdin)
+		return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+	else if((pos = ftello(decoder->private_->file)) < 0)
+		return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+	else {
+		*absolute_byte_offset = (FLAC__uint64)pos;
+		return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+	}
+}
+
+FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+{
+	struct flac_stat_s filestats;
+	(void)client_data;
+
+	if(decoder->private_->file == stdin)
+		return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+	else if(flac_fstat(fileno(decoder->private_->file), &filestats) != 0)
+		return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+	else {
+		*stream_length = (FLAC__uint64)filestats.st_size;
+		return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+	}
+}
+
+FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data)
+{
+	(void)client_data;
+
+	return feof(decoder->private_->file)? true : false;
+}
+
+void *get_client_data_from_decoder(FLAC__StreamDecoder *decoder)
+{
+	return decoder->private_->client_data;
+}
--- /dev/null
+++ b/src/libflac/window.c
@@ -1,0 +1,272 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2006-2009  Josh Coalson
+ * Copyright (C) 2011-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include "share/compat.h"
+#include "FLAC/format.h"
+#include "private/window.h"
+
+void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	if (L & 1) {
+		for (n = 0; n <= N/2; n++)
+			window[n] = 2.0f * n / (float)N;
+		for (; n <= N; n++)
+			window[n] = 2.0f - 2.0f * n / (float)N;
+	}
+	else {
+		for (n = 0; n <= L/2-1; n++)
+			window[n] = 2.0f * n / (float)N;
+		for (; n <= N; n++)
+			window[n] = 2.0f - 2.0f * n / (float)N;
+	}
+}
+
+void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N-0.5f) - 0.38f * cos(2.0f * M_PI * ((float)n/(float)N)));
+}
+
+void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N));
+}
+
+/* 4-term -92dB side-lobe */
+void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n <= N; n++)
+		window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N));
+}
+
+void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	const double N2 = (double)N / 2.;
+	FLAC__int32 n;
+
+	for (n = 0; n <= N; n++) {
+		double k = ((double)n - N2) / N2;
+		k = 1.0f - k * k;
+		window[n] = (FLAC__real)(k * k);
+	}
+}
+
+void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = (FLAC__real)(0.21557895f - 0.41663158f * cos(2.0f * M_PI * n / N) + 0.277263158f * cos(4.0f * M_PI * n / N) - 0.083578947f * cos(6.0f * M_PI * n / N) + 0.006947368f * cos(8.0f * M_PI * n / N));
+}
+
+void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev)
+{
+	const FLAC__int32 N = L - 1;
+	const double N2 = (double)N / 2.;
+	FLAC__int32 n;
+
+	for (n = 0; n <= N; n++) {
+		const double k = ((double)n - N2) / (stddev * N2);
+		window[n] = (FLAC__real)exp(-0.5f * k * k);
+	}
+}
+
+void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N));
+}
+
+void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N));
+}
+
+void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N));
+}
+
+void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N));
+}
+
+void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L)
+{
+	FLAC__int32 n;
+
+	for (n = 0; n < L; n++)
+		window[n] = 1.0f;
+}
+
+void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L)
+{
+	FLAC__int32 n;
+
+	if (L & 1) {
+		for (n = 1; n <= (L+1)/2; n++)
+			window[n-1] = 2.0f * n / ((float)L + 1.0f);
+		for (; n <= L; n++)
+			window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f);
+	}
+	else {
+		for (n = 1; n <= L/2; n++)
+			window[n-1] = 2.0f * n / ((float)L + 1.0f);
+		for (; n <= L; n++)
+			window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f);
+	}
+}
+
+void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p)
+{
+	if (p <= 0.0)
+		FLAC__window_rectangle(window, L);
+	else if (p >= 1.0)
+		FLAC__window_hann(window, L);
+	else {
+		const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1;
+		FLAC__int32 n;
+		/* start with rectangle... */
+		FLAC__window_rectangle(window, L);
+		/* ...replace ends with hann */
+		if (Np > 0) {
+			for (n = 0; n <= Np; n++) {
+				window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np));
+				window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np));
+			}
+		}
+	}
+}
+
+void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end)
+{
+	const FLAC__int32 start_n = (FLAC__int32)(start * L);
+	const FLAC__int32 end_n = (FLAC__int32)(end * L);
+	const FLAC__int32 N = end_n - start_n;
+	FLAC__int32 Np, n, i;
+
+	if (p <= 0.0f)
+		FLAC__window_partial_tukey(window, L, 0.05f, start, end);
+	else if (p >= 1.0f)
+		FLAC__window_partial_tukey(window, L, 0.95f, start, end);
+	else {
+
+		Np = (FLAC__int32)(p / 2.0f * N);
+
+		for (n = 0; n < start_n && n < L; n++)
+			window[n] = 0.0f;
+		for (i = 1; n < (start_n+Np) && n < L; n++, i++)
+			window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np));
+		for (; n < (end_n-Np) && n < L; n++)
+			window[n] = 1.0f;
+		for (i = Np; n < end_n && n < L; n++, i--)
+			window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np));
+		for (; n < L; n++)
+			window[n] = 0.0f;
+	}
+}
+
+void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end)
+{
+	const FLAC__int32 start_n = (FLAC__int32)(start * L);
+	const FLAC__int32 end_n = (FLAC__int32)(end * L);
+	FLAC__int32 Ns, Ne, n, i;
+
+	if (p <= 0.0f)
+		FLAC__window_punchout_tukey(window, L, 0.05f, start, end);
+	else if (p >= 1.0f)
+		FLAC__window_punchout_tukey(window, L, 0.95f, start, end);
+	else {
+
+		Ns = (FLAC__int32)(p / 2.0f * start_n);
+		Ne = (FLAC__int32)(p / 2.0f * (L - end_n));
+
+		for (n = 0, i = 1; n < Ns && n < L; n++, i++)
+			window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns));
+		for (; n < start_n-Ns && n < L; n++)
+			window[n] = 1.0f;
+		for (i = Ns; n < start_n && n < L; n++, i--)
+			window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns));
+		for (; n < end_n && n < L; n++)
+			window[n] = 0.0f;
+		for (i = 1; n < end_n+Ne && n < L; n++, i++)
+			window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne));
+		for (; n < L - (Ne) && n < L; n++)
+			window[n] = 1.0f;
+		for (i = Ne; n < L; n++, i--)
+			window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne));
+	}
+}
+
+void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L)
+{
+	const FLAC__int32 N = L - 1;
+	const double N2 = (double)N / 2.;
+	FLAC__int32 n;
+
+	for (n = 0; n <= N; n++) {
+		const double k = ((double)n - N2) / N2;
+		window[n] = (FLAC__real)(1.0f - k * k);
+	}
+}
--- /dev/null
+++ b/src/libflac/windows_unicode_filenames.c
@@ -1,0 +1,194 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2013-2016  Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Xiph.org Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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.
+ */
+
+#ifdef _WIN32
+
+#define WIN32_MEAN_AND_LEAN
+#include <io.h>
+#include <windows.h>
+#include "share/windows_unicode_filenames.h"
+
+/* convert UTF-8 back to WCHAR. Caller is responsible for freeing memory */
+static wchar_t *wchar_from_utf8(const char *str)
+{
+	wchar_t *widestr;
+	int len;
+
+	if (!str)
+		return NULL;
+	if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) == 0)
+		return NULL;
+	if ((widestr = (wchar_t *)malloc(len*sizeof(wchar_t))) == NULL)
+		return NULL;
+	if (MultiByteToWideChar(CP_UTF8, 0, str, -1, widestr, len) == 0) {
+		free(widestr);
+		widestr = NULL;
+	}
+
+	return widestr;
+}
+
+
+static FLAC__bool utf8_filenames = false;
+
+
+void flac_internal_set_utf8_filenames(FLAC__bool flag)
+{
+	utf8_filenames = flag ? true : false;
+}
+
+FLAC__bool flac_internal_get_utf8_filenames(void)
+{
+	return utf8_filenames;
+}
+
+/* file functions */
+
+FILE* flac_internal_fopen_utf8(const char *filename, const char *mode)
+{
+	if (!utf8_filenames) {
+		return fopen(filename, mode);
+	} else {
+		wchar_t *wname = NULL;
+		wchar_t *wmode = NULL;
+		FILE *f = NULL;
+
+		do {
+			wname = wchar_from_utf8(filename);
+			if (!wname) break;
+			wmode = wchar_from_utf8(mode);
+			if (!wmode) break;
+			f = _wfopen(wname, wmode);
+		} while(0);
+
+		free(wname);
+		free(wmode);
+
+		return f;
+	}
+}
+
+int flac_internal_stat64_utf8(const char *path, struct __stat64 *buffer)
+{
+	if (!utf8_filenames) {
+		return _stat64(path, buffer);
+	} else {
+		wchar_t *wpath;
+		int ret;
+
+		wpath = wchar_from_utf8(path);
+		if (!wpath) return -1;
+		ret = _wstat64(wpath, buffer);
+		free(wpath);
+
+		return ret;
+	}
+}
+
+int flac_internal_chmod_utf8(const char *filename, int pmode)
+{
+	if (!utf8_filenames) {
+		return _chmod(filename, pmode);
+	} else {
+		wchar_t *wname;
+		int ret;
+
+		wname = wchar_from_utf8(filename);
+		if (!wname) return -1;
+		ret = _wchmod(wname, pmode);
+		free(wname);
+
+		return ret;
+	}
+}
+
+int flac_internal_utime_utf8(const char *filename, struct utimbuf *times)
+{
+	if (!utf8_filenames) {
+		return utime(filename, times);
+	} else {
+		wchar_t *wname;
+		struct __utimbuf64 ut;
+		int ret;
+
+		wname = wchar_from_utf8(filename);
+		if (!wname) return -1;
+		ut.actime = times->actime;
+		ut.modtime = times->modtime;
+		ret = _wutime64(wname, &ut);
+		free(wname);
+
+		return ret;
+	}
+}
+
+int flac_internal_unlink_utf8(const char *filename)
+{
+	if (!utf8_filenames) {
+		return _unlink(filename);
+	} else {
+		wchar_t *wname;
+		int ret;
+
+		wname = wchar_from_utf8(filename);
+		if (!wname) return -1;
+		ret = _wunlink(wname);
+		free(wname);
+
+		return ret;
+	}
+}
+
+int flac_internal_rename_utf8(const char *oldname, const char *newname)
+{
+	if (!utf8_filenames) {
+		return rename(oldname, newname);
+	} else {
+		wchar_t *wold = NULL;
+		wchar_t *wnew = NULL;
+		int ret = -1;
+
+		do {
+			wold = wchar_from_utf8(oldname);
+			if (!wold) break;
+			wnew = wchar_from_utf8(newname);
+			if (!wnew) break;
+			ret = _wrename(wold, wnew);
+		} while(0);
+
+		free(wold);
+		free(wnew);
+
+		return ret;
+	}
+}
+
+#endif
--- a/src/mixer/ft2_center_mix.c
+++ b/src/mixer/ft2_center_mix.c
@@ -1,6 +1,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "ft2_mix_macros.h"
+#include "../ft2_cpu.h"
 
 /* Check out ft2_mix.c for comments on how this works.
 ** These are duplicates for center-mixing (slightly faster when it can be used).
@@ -13,10 +14,10 @@
 void centerMix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -55,10 +56,10 @@
 void centerMix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -97,10 +98,10 @@
 void centerMix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -141,10 +142,10 @@
 void centerMix8bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -184,10 +185,10 @@
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -251,10 +252,10 @@
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -320,10 +321,10 @@
 void centerMix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -362,10 +363,10 @@
 void centerMix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -404,10 +405,10 @@
 void centerMix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -448,11 +449,11 @@
 void centerMix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -498,11 +499,11 @@
 void centerMix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -548,11 +549,11 @@
 void centerMix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -600,11 +601,11 @@
 void centerMix8bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -651,11 +652,11 @@
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -731,11 +732,11 @@
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -812,11 +813,11 @@
 void centerMix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -862,11 +863,11 @@
 void centerMix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -912,11 +913,11 @@
 void centerMix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -968,10 +969,10 @@
 void centerMix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1010,10 +1011,10 @@
 void centerMix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1052,10 +1053,10 @@
 void centerMix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1096,10 +1097,10 @@
 void centerMix16bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1139,10 +1140,10 @@
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1206,10 +1207,10 @@
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1274,10 +1275,10 @@
 void centerMix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1316,10 +1317,10 @@
 void centerMix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1358,10 +1359,10 @@
 void centerMix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO
 	GET_MIXER_VARS
@@ -1403,11 +1404,11 @@
 void centerMix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1453,11 +1454,11 @@
 void centerMix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1503,11 +1504,11 @@
 void centerMix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1555,11 +1556,11 @@
 void centerMix16bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1606,11 +1607,11 @@
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1686,11 +1687,11 @@
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1767,11 +1768,11 @@
 void centerMix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1817,11 +1818,11 @@
 void centerMix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
@@ -1867,11 +1868,11 @@
 void centerMix16bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolL;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeL;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_MONO_RAMP
 	GET_MIXER_VARS_MONO_RAMP
--- a/src/mixer/ft2_mix.c
+++ b/src/mixer/ft2_mix.c
@@ -3,16 +3,17 @@
 #include "ft2_mix.h"
 #include "ft2_mix_macros.h"
 #include "ft2_center_mix.h"
+#include "../ft2_cpu.h"
 
 /*
-** ------------ double-precision floating-point audio channel mixer ------------
-**            (Note: Mixing macros can be found in ft2_mix_macros.h)
+** ------------ 32-bit floating-point audio channel mixer ------------
+**       (Note: Mixing macros can be found in ft2_mix_macros.h)
 **
 ** Specifications:
-** - Either no interpolation, 2-tap linear interpolation (FT2) or 8-tap windowed-sinc interpolation
-** - Linear volume ramping, matching FT2 (can be turned off)
-** - 32.32 fixed-point logic for resampling delta
-** - 64-bit doube-precision logic for mixing and interpolation
+** - No interpolation, 2-tap linear interpolation (FT2) or 8-tap windowed-sinc interpolation
+** - FT2-styled linear volume ramping (can be turned off)
+** - 32.32 (16.16 if 32-bit CPU) fixed-point precision for resampling delta/position
+** - 32-bit floating-point precision for mixing and interpolation
 **
 ** This file has separate routines for EVERY possible sampling variation:
 ** Interpolation none/sinc/linear, volumeramp on/off, 8-bit, 16-bit, no loop, loop, bidi.
@@ -34,10 +35,10 @@
 static void mix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -76,10 +77,10 @@
 static void mix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -118,10 +119,10 @@
 static void mix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -161,10 +162,10 @@
 static void mix8bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -204,10 +205,10 @@
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -271,10 +272,10 @@
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -339,10 +340,10 @@
 static void mix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -381,10 +382,10 @@
 static void mix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -423,10 +424,10 @@
 static void mix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -467,11 +468,11 @@
 static void mix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -517,11 +518,11 @@
 static void mix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -567,11 +568,11 @@
 static void mix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -619,11 +620,11 @@
 static void mix8bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -670,11 +671,11 @@
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -750,11 +751,11 @@
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -831,11 +832,11 @@
 static void mix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -881,11 +882,11 @@
 static void mix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -931,11 +932,11 @@
 static void mix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -987,10 +988,10 @@
 static void mix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1029,10 +1030,10 @@
 static void mix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1071,10 +1072,10 @@
 static void mix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1115,10 +1116,10 @@
 static void mix16bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1158,10 +1159,10 @@
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1225,10 +1226,10 @@
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1293,10 +1294,10 @@
 static void mix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1335,10 +1336,10 @@
 static void mix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1377,10 +1378,10 @@
 static void mix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL
 	GET_MIXER_VARS
@@ -1421,11 +1422,11 @@
 static void mix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1471,11 +1472,11 @@
 static void mix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1521,11 +1522,11 @@
 static void mix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1573,11 +1574,11 @@
 static void mix16bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1624,11 +1625,11 @@
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1704,11 +1705,11 @@
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1785,11 +1786,11 @@
 static void mix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1835,11 +1836,11 @@
 static void mix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac;
+	uintCPUWord_t positionFrac;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
@@ -1885,11 +1886,11 @@
 static void mix16bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
-	double dSample, *dMixBufferL, *dMixBufferR;
-	int32_t pos;
-	double dVolLDelta, dVolRDelta, dVolL, dVolR;
+	float fSample, *fMixBufferL, *fMixBufferR;
+	int32_t position;
+	float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR;
 	uint32_t i, samplesToMix, samplesLeft;
-	uint64_t posFrac, tmpDelta;
+	uintCPUWord_t positionFrac, tmpDelta;
 
 	GET_VOL_RAMP
 	GET_MIXER_VARS_RAMP
--- a/src/mixer/ft2_mix.h
+++ b/src/mixer/ft2_mix.h
@@ -1,7 +1,16 @@
 #pragma once
 
 #include <stdint.h>
-#include "../ft2_audio.h"
+#include "../ft2_cpu.h"
+
+#if CPU_64BIT
+#define MIXER_FRAC_BITS 32
+#else
+#define MIXER_FRAC_BITS 16
+#endif
+
+#define MIXER_FRAC_SCALE ((intCPUWord_t)1 << MIXER_FRAC_BITS)
+#define MIXER_FRAC_MASK (MIXER_FRAC_SCALE-1)
 
 typedef void (*mixFunc)(void *, uint32_t, uint32_t);
 
--- a/src/mixer/ft2_mix_macros.h
+++ b/src/mixer/ft2_mix_macros.h
@@ -1,6 +1,5 @@
 #pragma once
 
-#include <assert.h>
 #include "../ft2_audio.h"
 #include "ft2_windowed_sinc.h"
 
@@ -9,88 +8,89 @@
 /* ----------------------------------------------------------------------- */
 
 #define GET_VOL \
-	const double dVolL = v->dVolL; \
-	const double dVolR = v->dVolR; \
+	const float fVolumeL = v->fVolumeL; \
+	const float fVolumeR = v->fVolumeR;
 
 #define GET_VOL_MONO \
-	const double dVolL = v->dVolL; \
+	const float fVolumeL = v->fVolumeL;
 
 #define GET_VOL_RAMP \
-	dVolL = v->dVolL; \
-	dVolR = v->dVolR; \
+	fVolumeL = v->fVolumeL; \
+	fVolumeR = v->fVolumeR;
 
 #define GET_VOL_MONO_RAMP \
-	dVolL = v->dVolL; \
+	fVolumeL = v->fVolumeL;
 
 #define SET_VOL_BACK \
-	v->dVolL = dVolL; \
-	v->dVolR = dVolR; \
+	v->fVolumeL = fVolumeL; \
+	v->fVolumeR = fVolumeR;
 
 #define SET_VOL_BACK_MONO \
-	v->dVolL = v->dVolR = dVolL; \
+	v->fVolumeL = fVolumeL; \
+	v->fVolumeR = fVolumeL;
 
 #define GET_MIXER_VARS \
-	const uint64_t delta = v->delta; \
-	dMixBufferL = audio.dMixBufferL + bufferPos; \
-	dMixBufferR = audio.dMixBufferR + bufferPos; \
-	pos = v->pos; \
-	posFrac = v->posFrac; \
+	const uintCPUWord_t delta = v->delta; \
+	fMixBufferL = audio.fMixBufferL + bufferPos; \
+	fMixBufferR = audio.fMixBufferR + bufferPos; \
+	position = v->position; \
+	positionFrac = v->positionFrac;
 
 #define GET_MIXER_VARS_RAMP \
-	const uint64_t delta = v->delta; \
-	dMixBufferL = audio.dMixBufferL + bufferPos; \
-	dMixBufferR = audio.dMixBufferR + bufferPos; \
-	dVolLDelta = v->dVolDeltaL; \
-	dVolRDelta = v->dVolDeltaR; \
-	pos = v->pos; \
-	posFrac = v->posFrac; \
+	const uintCPUWord_t delta = v->delta; \
+	fMixBufferL = audio.fMixBufferL + bufferPos; \
+	fMixBufferR = audio.fMixBufferR + bufferPos; \
+	fVolumeLDelta = v->fVolumeLDelta; \
+	fVolumeRDelta = v->fVolumeRDelta; \
+	position = v->position; \
+	positionFrac = v->positionFrac;
 
 #define GET_MIXER_VARS_MONO_RAMP \
-	const uint64_t delta = v->delta; \
-	dMixBufferL = audio.dMixBufferL + bufferPos; \
-	dMixBufferR = audio.dMixBufferR + bufferPos; \
-	dVolLDelta = v->dVolDeltaL; \
-	pos = v->pos; \
-	posFrac = v->posFrac; \
+	const uintCPUWord_t delta = v->delta; \
+	fMixBufferL = audio.fMixBufferL + bufferPos; \
+	fMixBufferR = audio.fMixBufferR + bufferPos; \
+	fVolumeLDelta = v->fVolumeLDelta; \
+	position = v->position; \
+	positionFrac = v->positionFrac;
 
 #define PREPARE_TAP_FIX8 \
 	const int8_t *loopStartPtr = &v->base8[v->loopStart]; \
-	const int8_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS; \
+	const int8_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS;
 
 #define PREPARE_TAP_FIX16 \
 	const int16_t *loopStartPtr = &v->base16[v->loopStart]; \
-	const int16_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS; \
+	const int16_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS;
 
 #define SET_BASE8 \
 	base = v->base8; \
-	smpPtr = base + pos; \
+	smpPtr = base + position;
 
 #define SET_BASE16 \
 	base = v->base16; \
-	smpPtr = base + pos; \
+	smpPtr = base + position;
 
 #define SET_BASE8_BIDI \
 	base = v->base8; \
-	revBase = v->revBase8; \
+	revBase = v->revBase8;
 
 #define SET_BASE16_BIDI \
 	base = v->base16; \
-	revBase = v->revBase16; \
+	revBase = v->revBase16;
 
 #define INC_POS \
-	posFrac += delta; \
-	smpPtr += posFrac >> MIXER_FRAC_BITS; \
-	posFrac &= MIXER_FRAC_MASK; \
+	positionFrac += delta; \
+	smpPtr += positionFrac >> MIXER_FRAC_BITS; \
+	positionFrac &= MIXER_FRAC_MASK;
 
 #define INC_POS_BIDI \
-	posFrac += deltaLo; \
-	smpPtr += posFrac >> MIXER_FRAC_BITS; \
+	positionFrac += deltaLo; \
+	smpPtr += positionFrac >> MIXER_FRAC_BITS; \
 	smpPtr += deltaHi; \
-	posFrac &= MIXER_FRAC_MASK; \
+	positionFrac &= MIXER_FRAC_MASK;
 
 #define SET_BACK_MIXER_POS \
-	v->posFrac = posFrac; \
-	v->pos = pos; \
+	v->positionFrac = positionFrac; \
+	v->position = position;
 
 /* ----------------------------------------------------------------------- */
 /*                          SAMPLE RENDERING MACROS                        */
@@ -97,72 +97,66 @@
 /* ----------------------------------------------------------------------- */
 
 #define VOLUME_RAMPING \
-	dVolL += dVolLDelta; \
-	dVolR += dVolRDelta; \
+	fVolumeL += fVolumeLDelta; \
+	fVolumeR += fVolumeRDelta;
 
 #define VOLUME_RAMPING_MONO \
-	dVolL += dVolLDelta; \
+	fVolumeL += fVolumeLDelta;
 
 #define RENDER_8BIT_SMP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	dSample = *smpPtr * (1.0 / 128.0); \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	fSample = *smpPtr * (1.0f / 128.0f); \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_8BIT_SMP_MONO \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	dSample = (*smpPtr * (1.0 / 128.0)) * dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	fSample = (*smpPtr * (1.0f / 128.0f)) * fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 #define RENDER_16BIT_SMP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	dSample = *smpPtr * (1.0 / 32768.0); \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	fSample = *smpPtr * (1.0f / 32768.0f); \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_16BIT_SMP_MONO \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	dSample = (*smpPtr * (1.0 / 32768.0)) * dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	fSample = (*smpPtr * (1.0f / 32768.0f)) * fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 // 2-tap linear interpolation (like FT2)
 
+/* 8bitbubsy: It may look like we are potentially going out of bounds while looking up the sample points,
+** but the sample data has a fixed sample after the end (sampleEnd/loopEnd).
+*/
+
 #define LINEAR_INTERPOLATION(s, f, scale) \
 { \
-	/* uint32_t -> int32_t for less SIMD overhead when doing int->double conversion */ \
-	const int32_t frac = (uint32_t)(f) >> 1; /* (2^32)-1 -> (2^31)-1 */ \
-	\
-	const double dFrac = (double)(frac * (1.0 / (INT32_MAX+1.0))); /* 0.0 .. 0.999999999 */ \
-	dSample = ((s[0] + (s[1]-s[0]) * dFrac)) * (1.0 / scale); \
-} \
+	const int32_t frac = (uint32_t)(f) >> 1; /* uint32 -> int32 range, faster int->float conv. (x86/x86_64) */ \
+	const float fFrac = frac * (1.0f / (MIXER_FRAC_SCALE/2)); /* 0.0f .. 0.9999999f */ \
+	fSample = ((s[0] + (s[1]-s[0]) * fFrac)) * (1.0f / scale); \
+}
 
 #define RENDER_8BIT_SMP_LINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	LINEAR_INTERPOLATION(smpPtr, posFrac, 128) \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	LINEAR_INTERPOLATION(smpPtr, positionFrac, 128) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_8BIT_SMP_MONO_LINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	LINEAR_INTERPOLATION(smpPtr, posFrac, 128) \
-	dSample *= dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	LINEAR_INTERPOLATION(smpPtr, positionFrac, 128) \
+	fSample *= fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 #define RENDER_16BIT_SMP_LINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	LINEAR_INTERPOLATION(smpPtr, posFrac, 32768) \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	LINEAR_INTERPOLATION(smpPtr, positionFrac, 32768) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_16BIT_SMP_MONO_LINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	LINEAR_INTERPOLATION(smpPtr, posFrac, 32768) \
-	dSample *= dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	LINEAR_INTERPOLATION(smpPtr, positionFrac, 32768) \
+	fSample *= fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 // 8-tap windowed-sinc interpolation (better quality, through LUT: mixer/ft2_windowed_sinc.c)
 
@@ -176,8 +170,8 @@
 
 #define WINDOWED_SINC_INTERPOLATION(s, f, scale) \
 { \
-	const double *t = v->dSincLUT + (((uint32_t)f >> SINC_FSHIFT) & SINC_FMASK); \
-	dSample = ((s[-3] * t[0]) + \
+	const float *t = v->fSincLUT + (((uint32_t)(f) >> SINC_FSHIFT) & SINC_FMASK); \
+	fSample = ((s[-3] * t[0]) + \
 	           (s[-2] * t[1]) + \
 	           (s[-1] * t[2]) + \
 	           ( s[0] * t[3]) + \
@@ -184,34 +178,30 @@
 	           ( s[1] * t[4]) + \
 	           ( s[2] * t[5]) + \
 	           ( s[3] * t[6]) + \
-	           ( s[4] * t[7])) * (1.0 / scale); \
-} \
+	           ( s[4] * t[7])) * (1.0f / scale); \
+}
 
 #define RENDER_8BIT_SMP_SINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 128) \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 128) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_8BIT_SMP_MONO_SINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 128) \
-	dSample *= dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 128) \
+	fSample *= fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 #define RENDER_16BIT_SMP_SINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 32768) \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 32768) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_16BIT_SMP_MONO_SINTRP \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
-	WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 32768) \
-	dSample *= dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 32768) \
+	fSample *= fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 /* Special left-edge case mixers to get proper tap data after one loop cycle.
 ** These are only used with sinc interpolation on looped samples.
@@ -218,71 +208,76 @@
 */
 
 #define RENDER_8BIT_SMP_SINTRP_TAP_FIX  \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
 	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int8_t *)&v->leftEdgeTaps8[(int32_t)(smpPtr-loopStartPtr)] : (int8_t *)smpPtr; \
-	WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 128) \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 128) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_8BIT_SMP_MONO_SINTRP_TAP_FIX \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
 	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int8_t *)&v->leftEdgeTaps8[(int32_t)(smpPtr-loopStartPtr)] : (int8_t *)smpPtr; \
-	WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 128) \
-	dSample *= dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 128) \
+	fSample *= fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 #define RENDER_16BIT_SMP_SINTRP_TAP_FIX \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
 	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int16_t *)&v->leftEdgeTaps16[(int32_t)(smpPtr-loopStartPtr)] : (int16_t *)smpPtr; \
-	WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 32768) \
-	*dMixBufferL++ += dSample * dVolL; \
-	*dMixBufferR++ += dSample * dVolR; \
+	WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 32768) \
+	*fMixBufferL++ += fSample * fVolumeL; \
+	*fMixBufferR++ += fSample * fVolumeR;
 
 #define RENDER_16BIT_SMP_MONO_SINTRP_TAP_FIX \
-	assert(smpPtr >= base && smpPtr < base+v->end); \
 	smpTapPtr = (smpPtr <= leftEdgePtr) ? (int16_t *)&v->leftEdgeTaps16[(int32_t)(smpPtr-loopStartPtr)] : (int16_t *)smpPtr; \
-	WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 32768) \
-	dSample *= dVolL; \
-	*dMixBufferL++ += dSample; \
-	*dMixBufferR++ += dSample; \
+	WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 32768) \
+	fSample *= fVolumeL; \
+	*fMixBufferL++ += fSample; \
+	*fMixBufferR++ += fSample;
 
 /* ----------------------------------------------------------------------- */
 /*                      SAMPLES-TO-MIX LIMITING MACROS                     */
 /* ----------------------------------------------------------------------- */
 
+#if CPU_64BIT
+#define LIMIT_NUM
+#else
+#define LIMIT_NUM if (i > (1<<(32-MIXER_FRAC_BITS))-1) i = (1<<(32-MIXER_FRAC_BITS))-1;
+#endif
+
 #define LIMIT_MIX_NUM \
-	i = (v->end - 1) - pos; \
-	const uint64_t tmp64 = ((uint64_t)i << 32) | ((uint32_t)posFrac ^ 0xFFFFFFFF); \
+	samplesToMix = INT32_MAX; \
+	if (v->delta != 0) \
+	{ \
+		i = (v->sampleEnd - 1) - position; \
+		LIMIT_NUM \
+		const uintCPUWord_t dividend = ((uintCPUWord_t)i << MIXER_FRAC_BITS) | ((uint32_t)positionFrac ^ MIXER_FRAC_MASK); \
+		samplesToMix = (uint32_t)(dividend / (uintCPUWord_t)v->delta) + 1; \
+	} \
 	\
-	samplesToMix = (uint32_t)(tmp64 / (uint64_t)v->delta) + 1; /* this can be slow on 32-bit systems... */ \
 	if (samplesToMix > samplesLeft) \
-		samplesToMix = samplesLeft; \
+		samplesToMix = samplesLeft;
 
 #define START_BIDI \
-	if (v->backwards) \
+	if (v->samplingBackwards) \
 	{ \
 		tmpDelta = 0 - delta; \
-		assert(pos >= v->loopStart && pos < v->end); \
-		pos = ~pos; \
-		smpPtr = revBase + pos; \
-		posFrac ^= MIXER_FRAC_MASK; \
+		position = ~position; \
+		smpPtr = revBase + position; \
+		positionFrac ^= MIXER_FRAC_MASK; \
 	} \
 	else \
 	{ \
 		tmpDelta = delta; \
-		assert(pos >= 0 && pos < v->end); \
-		smpPtr = base + pos; \
+		smpPtr = base + position; \
 	} \
 	\
-	const int32_t deltaHi = (int64_t)tmpDelta >> MIXER_FRAC_BITS; \
-	const uint32_t deltaLo = tmpDelta & MIXER_FRAC_MASK; \
+	const int32_t deltaHi = (intCPUWord_t)tmpDelta >> MIXER_FRAC_BITS; \
+	const uint32_t deltaLo = tmpDelta & MIXER_FRAC_MASK;
 
 #define LIMIT_MIX_NUM_RAMP \
-	if (v->volRampSamples == 0) \
+	if (v->volumeRampLength == 0) \
 	{ \
-		dVolLDelta = 0.0; \
-		dVolRDelta = 0.0; \
+		fVolumeLDelta = 0.0; \
+		fVolumeRDelta = 0.0; \
 		\
 		if (v->isFadeOutVoice) \
 		{ \
@@ -292,16 +287,16 @@
 	} \
 	else \
 	{ \
-		if (samplesToMix > v->volRampSamples) \
-			samplesToMix = v->volRampSamples; \
+		if (samplesToMix > v->volumeRampLength) \
+			samplesToMix = v->volumeRampLength; \
 		\
-		v->volRampSamples -= samplesToMix; \
-	} \
+		v->volumeRampLength -= samplesToMix; \
+	}
 
 #define LIMIT_MIX_NUM_MONO_RAMP \
-	if (v->volRampSamples == 0) \
+	if (v->volumeRampLength == 0) \
 	{ \
-		dVolLDelta = 0.0; \
+		fVolumeLDelta = 0.0; \
 		if (v->isFadeOutVoice) \
 		{ \
 			v->active = false; /* volume ramp fadeout-voice is done, shut it down */ \
@@ -310,55 +305,54 @@
 	} \
 	else \
 	{ \
-		if (samplesToMix > v->volRampSamples) \
-			samplesToMix = v->volRampSamples; \
+		if (samplesToMix > v->volumeRampLength) \
+			samplesToMix = v->volumeRampLength; \
 		\
-		v->volRampSamples -= samplesToMix; \
-	} \
+		v->volumeRampLength -= samplesToMix; \
+	}
 
 #define HANDLE_SAMPLE_END \
-	pos = (int32_t)(smpPtr - base); \
-	if (pos >= v->end) \
+	position = (int32_t)(smpPtr - base); \
+	if (position >= v->sampleEnd) \
 	{ \
 		v->active = false; \
 		return; \
-	} \
+	}
 
 #define WRAP_LOOP \
-	pos = (int32_t)(smpPtr - base); \
-	if (pos >= v->end) \
+	position = (int32_t)(smpPtr - base); \
+	if (position >= v->sampleEnd) \
 	{ \
 		do \
 		{ \
-			pos -= v->loopLength; \
+			position -= v->loopLength; \
 		} \
-		while (pos >= v->end); \
+		while (position >= v->sampleEnd); \
 		\
-		smpPtr = base + pos; \
+		smpPtr = base + position; \
 		\
 		v->hasLooped = true; \
-	} \
+	}
 
 #define WRAP_BIDI_LOOP \
-	if (pos >= v->end) \
+	if (position >= v->sampleEnd) \
 	{ \
 		do \
 		{ \
-			pos -= v->loopLength; \
-			v->backwards ^= 1; \
+			position -= v->loopLength; \
+			v->samplingBackwards ^= 1; \
 		} \
-		while (pos >= v->end); \
+		while (position >= v->sampleEnd); \
 		v->hasLooped = true; \
-	} \
+	}
 
 #define END_BIDI \
-	if (v->backwards) \
+	if (v->samplingBackwards) \
 	{ \
-		posFrac ^= MIXER_FRAC_MASK; \
-		pos = ~(int32_t)(smpPtr - revBase); \
+		positionFrac ^= MIXER_FRAC_MASK; \
+		position = ~(int32_t)(smpPtr - revBase); \
 	} \
 	else \
 	{ \
-		pos = (int32_t)(smpPtr - base); \
-	} \
-
+		position = (int32_t)(smpPtr - base); \
+	}
--- a/src/mixer/ft2_silence_mix.c
+++ b/src/mixer/ft2_silence_mix.c
@@ -1,22 +1,23 @@
 #include <stdint.h>
 #include "../ft2_audio.h"
+#include "../ft2_cpu.h"
 
 // used for the audio channel mixer when voice volume is zero
 
 void silenceMixRoutine(voice_t *v, int32_t numSamples)
 {
-	const uint64_t newPos = v->delta * (uint64_t)numSamples;
-	const uint32_t addPos = (uint32_t)(newPos >> MIXER_FRAC_BITS);
-	uint64_t addFrac = newPos & MIXER_FRAC_MASK;
+	const uint64_t samplesToMix = (uint64_t)v->delta * (uint32_t)numSamples; // fixed-point
 
-	addFrac += v->posFrac;
-	int32_t pos = v->pos + addPos + (uint32_t)(addFrac >> MIXER_FRAC_BITS);
-	uint64_t  posFrac = addFrac & MIXER_FRAC_MASK;
+	const uint32_t samples = (uint32_t)(samplesToMix >> MIXER_FRAC_BITS);
+	const uintCPUWord_t samplesFrac = (samplesToMix & MIXER_FRAC_MASK) + v->positionFrac;
 
-	if (pos < v->end) // we haven't reached the sample's end yet
+	uint32_t position = v->position + samples + (uint32_t)(samplesFrac >> MIXER_FRAC_BITS);
+	uintCPUWord_t positionFrac = samplesFrac & MIXER_FRAC_MASK;
+
+	if (position < (unsigned)v->sampleEnd) // we haven't reached the sample's end yet
 	{
-		v->posFrac = posFrac;
-		v->pos = pos;
+		v->positionFrac = positionFrac;
+		v->position = position;
 		return;
 	}
 
@@ -31,28 +32,28 @@
 	if (v->loopType == LOOP_FORWARD)
 	{
 		if (v->loopLength >= 2)
-			pos = v->loopStart + ((pos - v->end) % v->loopLength);
+			position = v->loopStart + ((position - v->sampleEnd) % v->loopLength);
 		else
-			pos = v->loopStart;
+			position = v->loopStart;
 	}
 	else // pingpong loop
 	{
 		if (v->loopLength >= 2)
 		{
-			const int32_t overflow = pos - v->end;
-			const int32_t cycles = overflow / v->loopLength;
-			const int32_t phase = overflow % v->loopLength;
+			const uint32_t overflow = position - v->sampleEnd;
+			const uint32_t cycles = overflow / v->loopLength;
+			const uint32_t phase = overflow % v->loopLength;
 
-			pos = v->loopStart + phase;
-			v->backwards ^= !(cycles & 1);
+			position = v->loopStart + phase;
+			v->samplingBackwards ^= !(cycles & 1);
 		}
 		else
 		{
-			pos = v->loopStart;
+			position = v->loopStart;
 		}
 	}
 
 	v->hasLooped = true;
-	v->posFrac = posFrac;
-	v->pos = pos;
+	v->positionFrac = positionFrac;
+	v->position = position;
 }
--- a/src/mixer/ft2_windowed_sinc.c
+++ b/src/mixer/ft2_windowed_sinc.c
@@ -1,3 +1,9 @@
+/* Code taken from the OpenMPT project, which has a BSD
+** license which is compatible with this project.
+**
+** The code has been slightly modified.
+*/
+
 #include <stdint.h>
 #include <stdbool.h>
 #include <stdlib.h>
@@ -5,16 +11,10 @@
 #include "ft2_windowed_sinc.h"
 
 // globalized
-double *gKaiserSinc = NULL;
-double *gDownSample1 = NULL;
-double *gDownSample2 = NULL;
+float *fKaiserSinc = NULL;
+float *fDownSample1 = NULL;
+float *fDownSample2 = NULL;
 
-/* Code taken from the OpenMPT project, which has a BSD
-** license which is compatible with this this project.
-**
-** The code has been slightly modified.
-*/
-
 static double Izero(double y) // Compute Bessel function Izero(y) using a series approximation
 {
 	double s = 1.0, ds = 1.0, d = 0.0;
@@ -32,7 +32,7 @@
 	return s;
 }
 
-static void getSinc(double *dLUTPtr, const double beta, const double cutoff)
+static void getSinc(float *fLUTPtr, const double beta, const double cutoff)
 {
 	const double izeroBeta = Izero(beta);
 	const double kPi = 4.0 * atan(1.0) * cutoff; // M_PI can't be trusted
@@ -56,23 +56,22 @@
 			dSinc = sin(xPi) * Izero(beta * sqrt(1.0 - x * x * xMul)) / (izeroBeta * xPi); // Kaiser window
 		}
 
-		dLUTPtr[i] = dSinc * cutoff;
+		fLUTPtr[i] = (float)(dSinc * cutoff);
 	}
 }
 
 bool calcWindowedSincTables(void)
 {
-	gKaiserSinc  = (double *)malloc(SINC_LUT_LEN * sizeof (double));
-	gDownSample1 = (double *)malloc(SINC_LUT_LEN * sizeof (double));
-	gDownSample2 = (double *)malloc(SINC_LUT_LEN * sizeof (double));
+	fKaiserSinc  = (float *)malloc(SINC_LUT_LEN * sizeof (float));
+	fDownSample1 = (float *)malloc(SINC_LUT_LEN * sizeof (float));
+	fDownSample2 = (float *)malloc(SINC_LUT_LEN * sizeof (float));
 
-	if (gKaiserSinc == NULL || gDownSample1 == NULL || gDownSample2 == NULL)
+	if (fKaiserSinc == NULL || fDownSample1 == NULL || fDownSample2 == NULL)
 		return false;
 
-	// OpenMPT parameters
-	getSinc(gKaiserSinc, 9.6377, 0.97);
-	getSinc(gDownSample1, 8.5, 0.5);
-	getSinc(gDownSample2, 2.7625, 0.425);
+	getSinc(fKaiserSinc, 9.6377, 0.97);
+	getSinc(fDownSample1, 8.5, 0.5);
+	getSinc(fDownSample2, 2.7625, 0.425);
 
 	return true;
 }
@@ -79,21 +78,21 @@
 
 void freeWindowedSincTables(void)
 {
-	if (gKaiserSinc != NULL)
+	if (fKaiserSinc != NULL)
 	{
-		free(gKaiserSinc);
-		gKaiserSinc = NULL;
+		free(fKaiserSinc);
+		fKaiserSinc = NULL;
 	}
 
-	if (gDownSample1 != NULL)
+	if (fDownSample1 != NULL)
 	{
-		free(gDownSample1);
-		gDownSample1 = NULL;
+		free(fDownSample1);
+		fDownSample1 = NULL;
 	}
 
-	if (gDownSample2 != NULL)
+	if (fDownSample2 != NULL)
 	{
-		free(gDownSample2);
-		gDownSample2 = NULL;
+		free(fDownSample2);
+		fDownSample2 = NULL;
 	}
 }
--- a/src/mixer/ft2_windowed_sinc.h
+++ b/src/mixer/ft2_windowed_sinc.h
@@ -2,15 +2,12 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include "ft2_mix.h"
 
-#ifndef MIXER_FRAC_BITS
-#define MIXER_FRAC_BITS 32
-#endif
+// if you change this, also change SINC_PHASES_BITS (>8192 gives very little improvement)
+#define SINC_PHASES 8192
+#define SINC_PHASES_BITS 13 /* log2(SINC_PHASES) */
 
-// if you change this, also change SINC_PHASES_BITS (>4096 makes little sense)
-#define SINC_PHASES 4096
-#define SINC_PHASES_BITS 12 /* log2(SINC_PHASES) */
-
 // don't change these!
 
 #define SINC_TAPS 8
@@ -19,6 +16,7 @@
 #define SINC_FSHIFT (MIXER_FRAC_BITS-(SINC_PHASES_BITS+SINC_TAPS_BITS))
 #define SINC_FMASK ((SINC_TAPS*SINC_PHASES)-SINC_TAPS)
 
+#define SINC_CENTER_TAP (SINC_TAPS/2)
 #define SINC_LEFT_TAPS ((SINC_TAPS/2)-1)
 #define SINC_RIGHT_TAPS (SINC_TAPS/2)
 
@@ -25,9 +23,9 @@
 // for LUT calculation
 #define SINC_MID_TAP ((SINC_TAPS/2)*SINC_PHASES)
 
-extern double *gKaiserSinc;
-extern double *gDownSample1;
-extern double *gDownSample2;
+extern float *fKaiserSinc;
+extern float *fDownSample1;
+extern float *fDownSample2;
 
 bool calcWindowedSincTables(void);
 void freeWindowedSincTables(void);
--- a/src/modloaders/ft2_load_digi.c
+++ b/src/modloaders/ft2_load_digi.c
@@ -1,4 +1,8 @@
-// DIGI Booster (non-Pro) module loader
+/* DIGI Booster (non-Pro) module loader
+**
+** Note: Data sanitation is done in the last stage
+** of module loading, so you don't need to do that here.
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -13,15 +17,15 @@
 #pragma pack(push)
 #pragma pack(1)
 #endif
-typedef struct digiHeaderTyp_t
+typedef struct digiHdr_t
 {
-	char sig[20];
+	char ID[20];
 	char verStr[4];
-	uint8_t ver;
+	uint8_t version;
 	uint8_t numChannels;
 	uint8_t packedPatternsFlag;
 	char reserved[19];
-	uint8_t numPats;
+	uint8_t numPatterns;
 	uint8_t numOrders;
 	uint8_t orders[128];
 	uint32_t smpLength[31];
@@ -35,58 +39,58 @@
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-digiHeaderTyp;
+digiHdr_t;
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif
 
-static void readPatternNote(FILE *f, tonTyp *ton);
+static void readPatternNote(FILE *f, note_t *p);
 
 bool loadDIGI(FILE *f, uint32_t filesize)
 {
 	int16_t i, j, k;
-	tonTyp *ton;
-	sampleTyp *s;
-	digiHeaderTyp h_DIGI;
+	sample_t *s;
+	digiHdr_t hdr;
 
 	tmpLinearPeriodsFlag = false; // use Amiga periods
 
-	if (filesize < sizeof (h_DIGI))
+	if (filesize < sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	memset(&h_DIGI, 0, sizeof (digiHeaderTyp));
-	if (fread(&h_DIGI, 1, sizeof (h_DIGI), f) != sizeof (h_DIGI))
+	memset(&hdr, 0, sizeof (hdr));
+	if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	if (h_DIGI.numChannels < 1 || h_DIGI.numChannels > 8)
+	if (hdr.numChannels < 1 || hdr.numChannels > 8)
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	h_DIGI.numOrders++;
-	h_DIGI.numPats++;
+	hdr.numOrders++;
+	hdr.numPatterns++;
 
-	if (h_DIGI.numOrders < 1 || h_DIGI.numOrders > 128)
+	if (hdr.numOrders < 1 || hdr.numOrders > 128)
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	songTmp.antChn = h_DIGI.numChannels;
-	songTmp.len = h_DIGI.numOrders;
-	songTmp.initialTempo = songTmp.tempo = 6;
-	songTmp.speed = 125;
-	memcpy(songTmp.songTab, h_DIGI.orders, 128);
+	memcpy(songTmp.orders, hdr.orders, 128);
 
+	songTmp.numChannels = hdr.numChannels;
+	songTmp.songLength = hdr.numOrders;
+	songTmp.BPM = 125;
+	songTmp.speed = 6;
+
 	// load pattern data
-	for (i = 0; i < h_DIGI.numPats; i++)
+	for (i = 0; i < hdr.numPatterns; i++)
 	{
 		if (!allocateTmpPatt(i, 64))
 		{
@@ -94,7 +98,7 @@
 			return false;
 		}
 
-		if (h_DIGI.packedPatternsFlag)
+		if (hdr.packedPatternsFlag)
 		{
 			uint16_t pattSize;
 			uint8_t bitMasks[64];
@@ -105,61 +109,58 @@
 			for (j = 0; j < 64; j++)
 			{
 				uint8_t bit = 128;
-				for (k = 0; k < songTmp.antChn; k++, bit >>= 1)
+				for (k = 0; k < songTmp.numChannels; k++, bit >>= 1)
 				{
-					ton = &pattTmp[i][(j * MAX_VOICES) + k];
+					note_t *p = &patternTmp[i][(j * MAX_CHANNELS) + k];
 					if (bitMasks[j] & bit)
-						readPatternNote(f, ton);
+						readPatternNote(f, p);
 				}
 			}
 		}
 		else
 		{
-			for (j = 0; j < songTmp.antChn; j++)
+			for (j = 0; j < songTmp.numChannels; j++)
 			{
 				for (k = 0; k < 64; k++)
-				{
-					ton = &pattTmp[i][(k * MAX_VOICES) + j];
-					readPatternNote(f, ton);
-				}
+					readPatternNote(f, &patternTmp[i][(k * MAX_CHANNELS) + j]);
 			}
 		}
 
 		if (tmpPatternEmpty(i))
 		{
-			if (pattTmp[i] != NULL)
+			if (patternTmp[i] != NULL)
 			{
-				free(pattTmp[i]);
-				pattTmp[i] = NULL;
+				free(patternTmp[i]);
+				patternTmp[i] = NULL;
 			}
 		}
 	}
 
 	// pattern command handling
-	for (i = 0; i < h_DIGI.numPats; i++)
+	for (i = 0; i < hdr.numPatterns; i++)
 	{
-		if (pattTmp[i] == NULL)
+		if (patternTmp[i] == NULL)
 			continue;
 
 		for (j = 0; j < 64; j++)
 		{
-			for (k = 0; k < songTmp.antChn; k++)
+			for (k = 0; k < songTmp.numChannels; k++)
 			{
-				ton = &pattTmp[i][(j * MAX_VOICES) + k];
+				note_t *p = &patternTmp[i][(j * MAX_CHANNELS) + k];
 
-				if (ton->effTyp == 0x8) // Robot effect (not supported)
+				if (p->efx == 0x8) // Robot effect (not supported)
 				{
-					ton->effTyp = 0;
-					ton->eff = 0;
+					p->efx = 0;
+					p->efxData = 0;
 				}
-				else if (ton->effTyp == 0xE)
+				else if (p->efx == 0xE)
 				{
-					switch (ton->eff >> 4)
+					switch (p->efxData >> 4)
 					{
-						case 0x3: ton->effTyp = ton->eff = 0; break; // backwards play (not supported)
-						case 0x4: ton->eff = 0xC0; break; // stop sample (convert to EC0)
-						case 0x8: ton->effTyp = ton->eff = 0; break; // high sample offset (not supported)
-						case 0x9: ton->effTyp = ton->eff = 0; break; // retrace (not supported)
+						case 0x3: p->efx = p->efxData = 0;    break; // backwards play (not supported)
+						case 0x4:          p->efxData = 0xC0; break; // stop sample (convert to EC0)
+						case 0x8: p->efx = p->efxData = 0;    break; // high sample offset (not supported)
+						case 0x9: p->efx = p->efxData = 0;    break; // retrace (not supported)
 						default: break;
 					}
 				}
@@ -170,8 +171,8 @@
 	// load sample data
 	for (i = 0; i < 31; i++)
 	{
-		memcpy(songTmp.instrName[1+i], h_DIGI.smpName[i], 22);
-		if (h_DIGI.smpLength[i] == 0)
+		memcpy(songTmp.instrName[1+i], hdr.smpName[i], 22);
+		if (hdr.smpLength[i] == 0)
 			continue;
 
 		if (!allocateTmpInstr(1+i))
@@ -182,56 +183,53 @@
 
 		setNoEnvelope(instrTmp[1+i]);
 
-		s = &instrTmp[1+i]->samp[0];
-		memcpy(s->name, h_DIGI.smpName[i], 22);
+		s = &instrTmp[1+i]->smp[0];
+		memcpy(s->name, hdr.smpName[i], 22);
 
-		s->len = SWAP32(h_DIGI.smpLength[i]);
-		s->fine = 8 * ((2 * ((h_DIGI.smpFinetune[i] & 0xF) ^ 8)) - 16);
-		s->vol = h_DIGI.smpVolume[i];
-		s->repS = SWAP32(h_DIGI.smpLoopStart[i]);
-		s->repL = SWAP32(h_DIGI.smpLoopLength[i]);
+		s->length = SWAP32(hdr.smpLength[i]);
+		s->finetune = FINETUNE_MOD2XM(hdr.smpFinetune[i]);
+		s->volume = hdr.smpVolume[i];
+		s->loopStart = SWAP32(hdr.smpLoopStart[i]);
+		s->loopLength = SWAP32(hdr.smpLoopLength[i]);
 
-		if (s->vol > 64)
-			s->vol = 64;
+		if (s->loopLength < 2)
+			s->loopLength = 2;
 
-		if (s->repL < 2)
-			s->repL = 2;
-
 		// fix overflown loop
-		if (s->repS+s->repL > s->len)
+		if (s->loopStart+s->loopLength > s->length)
 		{
-			if (s->repS >= s->len)
+			if (s->loopStart >= s->length)
 			{
-				s->repS = 0;
-				s->repL = 0;
+				s->loopStart = 0;
+				s->loopLength = 0;
 			}
 			else
 			{
-				s->repL = s->len - s->repS;
+				s->loopLength = s->length - s->loopStart;
 			}
 		}
 
-		if (s->repS+s->repL > 2)
+		if (s->loopStart+s->loopLength > 2)
 		{
-			s->typ = 1; // enable loop
+			s->flags |= LOOP_FWD; // enable loop
 		}
 		else
 		{
-			s->repS = 0;
-			s->repL = 0;
+			s->loopStart = 0;
+			s->loopLength = 0;
 		}
 
-		if (!allocateTmpSmpData(s, s->len))
+		if (!allocateSmpData(s, s->length, false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f);
-		if (bytesRead < s->len)
+		int32_t bytesRead = (int32_t)fread(s->dataPtr, 1, s->length, f);
+		if (bytesRead < s->length)
 		{
-			int32_t bytesToClear = s->len - bytesRead;
-			memset(&s->pek[bytesRead], 0, bytesToClear);
+			int32_t bytesToClear = s->length - bytesRead;
+			memset(&s->dataPtr[bytesRead], 0, bytesToClear);
 		}
 	}
 
@@ -238,7 +236,7 @@
 	return true;
 }
 
-static void readPatternNote(FILE *f, tonTyp *ton)
+static void readPatternNote(FILE *f, note_t *p)
 {
 	uint8_t bytes[4];
 	fread(bytes, 1, 4, f);
@@ -249,12 +247,12 @@
 	{
 		if (period >= ptPeriods[i])
 		{
-			ton->ton = 1 + (3*12) + i;
+			p->note = 1 + (3*12) + i;
 			break;
 		}
 	}
 
-	ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
-	ton->effTyp = bytes[2] & 0x0F;
-	ton->eff = bytes[3];
+	p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
+	p->efx = bytes[2] & 0x0F;
+	p->efxData = bytes[3];
 }
--- a/src/modloaders/ft2_load_mod.c
+++ b/src/modloaders/ft2_load_mod.c
@@ -1,4 +1,8 @@
-// NoiseTracker/ProTracker (or compatible) MOD loader
+/* NoiseTracker/ProTracker (or compatible) MOD loader
+**
+** Note: Data sanitation is done in the last stage
+** of module loading, so you don't need to do that here.
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -27,27 +31,26 @@
 {
 	uint8_t bytes[4], modFormat, numChannels;
 	int16_t i, j, k;
-	uint16_t a, b, period;
-	tonTyp *ton;
-	sampleTyp *s;
-	songMOD31HeaderTyp h_MOD31;
+	uint16_t a, b;
+	sample_t *s;
+	modHdr_t hdr;
 
 	tmpLinearPeriodsFlag = false; // use Amiga periods
 
-	if (filesize < sizeof (h_MOD31))
+	if (filesize < sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	memset(&h_MOD31, 0, sizeof (songMOD31HeaderTyp));
-	if (fread(&h_MOD31, 1, sizeof (h_MOD31), f) != sizeof (h_MOD31))
+	memset(&hdr, 0, sizeof (hdr));
+	if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	modFormat = getModType(&numChannels, h_MOD31.sig);
+	modFormat = getModType(&numChannels, hdr.ID);
 	if (modFormat == FORMAT_UNKNOWN)
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
@@ -54,38 +57,35 @@
 		return false;
 	}
 
-	bool hasMoreThan32Chans = numChannels > 32;
+	if (modFormat == FORMAT_MK && hdr.numOrders == 129)
+		hdr.numOrders = 127; // fixes a specific copy of beatwave.mod (FIXME: Do we want to keep this hack?)
 
-	songTmp.antChn = hasMoreThan32Chans ? 32 : numChannels;
-	songTmp.len = h_MOD31.len;
-	songTmp.repS = h_MOD31.repS;
-	songTmp.initialTempo = songTmp.tempo = 6;
-	songTmp.speed = 125;
-
-	memcpy(songTmp.songTab, h_MOD31.songTab, 128);
-
-	if (modFormat == FORMAT_MK && songTmp.len == 129)
-		songTmp.len = 127; // fixes a specific copy of beatwave.mod (FIXME: Do we want to keep this hack?)
-
-	if (songTmp.repS >= songTmp.len)
-		songTmp.repS = 0;
-
-	if (songTmp.antChn == 0 || songTmp.len < 1 || songTmp.len > 128)
+	if (numChannels == 0 || hdr.numOrders < 1 || hdr.numOrders > 128)
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
+	bool tooManyChannels = numChannels > MAX_CHANNELS;
+
+	songTmp.numChannels = tooManyChannels ? MAX_CHANNELS : numChannels;
+	songTmp.songLength = hdr.numOrders;
+	songTmp.songLoopStart = hdr.songLoopStart;
+	songTmp.BPM = 125;
+	songTmp.speed = 6;
+
+	memcpy(songTmp.orders, hdr.orders, 128);
+
 	for (a = 0; a < 31; a++)
 	{
-		songMODInstrHeaderTyp *smp = &h_MOD31.instr[a];
+		modSmpHdr_t *modSmp = &hdr.smp[a];
 
 		// copy over sample name if format isn't "His Master's Noisetracker" (junk sample names)
 		if (modFormat != FORMAT_HMNT)
-			memcpy(songTmp.instrName[1+a], smp->name, 22);
+			memcpy(songTmp.instrName[1+a], modSmp->name, 22);
 	}
 
-	memcpy(songTmp.name, h_MOD31.name, 20);
+	memcpy(songTmp.name, hdr.name, 20);
 
 	// count number of patterns
 	b = 0;
@@ -92,10 +92,10 @@
 	for (a = 0; a < 128; a++)
 	{
 		if (modFormat == FORMAT_FLT8)
-			songTmp.songTab[a] >>= 1;
+			songTmp.orders[a] >>= 1;
 
-		if (songTmp.songTab[a] > b)
-			b = songTmp.songTab[a];
+		if (songTmp.orders[a] > b)
+			b = songTmp.orders[a];
 	}
 	b++;
 
@@ -114,30 +114,30 @@
 
 			for (j = 0; j < 64; j++)
 			{
-				for (k = 0; k < songTmp.antChn; k++)
+				for (k = 0; k < songTmp.numChannels; k++)
 				{
-					ton = &pattTmp[a][(j * MAX_VOICES) + k];
+					note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k];
 					fread(bytes, 1, 4, f);
 
 					// period to note
-					period = ((bytes[0] & 0x0F) << 8) | bytes[1];
+					uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1];
 					for (i = 0; i < 8*12; i++)
 					{
 						if (period >= amigaPeriod[i])
 						{
-							ton->ton = (uint8_t)i + 1;
+							p->note = (uint8_t)i + 1;
 							break;
 						}
 					}
 
-					ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
-					ton->effTyp = bytes[2] & 0x0F;
-					ton->eff = bytes[3];
+					p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
+					p->efx = bytes[2] & 0x0F;
+					p->efxData = bytes[3];
 				}
 
-				if (hasMoreThan32Chans)
+				if (tooManyChannels)
 				{
-					int32_t remainingChans = numChannels-songTmp.antChn;
+					int32_t remainingChans = numChannels-songTmp.numChannels;
 					fseek(f, remainingChans*4, SEEK_CUR);
 				}
 			}
@@ -144,10 +144,10 @@
 
 			if (tmpPatternEmpty(a))
 			{
-				if (pattTmp[a] != NULL)
+				if (patternTmp[a] != NULL)
 				{
-					free(pattTmp[a]);
-					pattTmp[a] = NULL;
+					free(patternTmp[a]);
+					patternTmp[a] = NULL;
 				}
 			}
 		}
@@ -167,7 +167,7 @@
 
 		for (a = 0; a < b*2; a++)
 		{
-			int32_t pattern = a >> 1;
+			int32_t pattNum = a >> 1;
 			int32_t chnOffset = (a & 1) * 4;
 
 			for (j = 0; j < 64; j++)
@@ -174,23 +174,23 @@
 			{
 				for (k = 0; k < 4; k++)
 				{
-					ton = &pattTmp[pattern][(j * MAX_VOICES) + (k+chnOffset)];
+					note_t *p = &patternTmp[pattNum][(j * MAX_CHANNELS) + (k+chnOffset)];
 					fread(bytes, 1, 4, f);
 
 					// period to note
-					period = ((bytes[0] & 0x0F) << 8) | bytes[1];
+					uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1];
 					for (i = 0; i < 8*12; i++)
 					{
 						if (period >= amigaPeriod[i])
 						{
-							ton->ton = (uint8_t)i + 1;
+							p->note = (uint8_t)i + 1;
 							break;
 						}
 					}
 
-					ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
-					ton->effTyp = bytes[2] & 0x0F;
-					ton->eff = bytes[3];
+					p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
+					p->efx = bytes[2] & 0x0F;
+					p->efxData = bytes[3];
 				}
 			}
 		}
@@ -199,10 +199,10 @@
 		{
 			if (tmpPatternEmpty(a))
 			{
-				if (pattTmp[a] != NULL)
+				if (patternTmp[a] != NULL)
 				{
-					free(pattTmp[a]);
-					pattTmp[a] = NULL;
+					free(patternTmp[a]);
+					patternTmp[a] = NULL;
 				}
 			}
 		}
@@ -211,52 +211,52 @@
 	// pattern command conversion
 	for (a = 0; a < b; a++)
 	{
-		if (pattTmp[a] == NULL)
+		if (patternTmp[a] == NULL)
 			continue;
 
 		for (j = 0; j < 64; j++)
 		{
-			for (k = 0; k < songTmp.antChn; k++)
+			for (k = 0; k < songTmp.numChannels; k++)
 			{
-				ton = &pattTmp[a][(j * MAX_VOICES) + k];
+				note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k];
 
-				if (ton->effTyp == 0xC)
+				if (p->efx == 0xC)
 				{
-					if (ton->eff > 64)
-						ton->eff = 64;
+					if (p->efxData > 64)
+						p->efxData = 64;
 				}
-				else if (ton->effTyp == 0x1)
+				else if (p->efx == 0x1)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0;
+					if (p->efxData == 0)
+						p->efx = 0;
 				}
-				else if (ton->effTyp == 0x2)
+				else if (p->efx == 0x2)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0;
+					if (p->efxData == 0)
+						p->efx = 0;
 				}
-				else if (ton->effTyp == 0x5)
+				else if (p->efx == 0x5)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0x3;
+					if (p->efxData == 0)
+						p->efx = 0x3;
 				}
-				else if (ton->effTyp == 0x6)
+				else if (p->efx == 0x6)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0x4;
+					if (p->efxData == 0)
+						p->efx = 0x4;
 				}
-				else if (ton->effTyp == 0xA)
+				else if (p->efx == 0xA)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0;
+					if (p->efxData == 0)
+						p->efx = 0;
 				}
-				else if (ton->effTyp == 0xE)
+				else if (p->efx == 0xE)
 				{
 					// check if certain E commands are empty
-					if (ton->eff == 0x10 || ton->eff == 0x20 || ton->eff == 0xA0 || ton->eff == 0xB0)
+					if (p->efxData == 0x10 || p->efxData == 0x20 || p->efxData == 0xA0 || p->efxData == 0xB0)
 					{
-						ton->effTyp = 0;
-						ton->eff = 0;
+						p->efx = 0;
+						p->efxData = 0;
 					}
 				}
 
@@ -263,24 +263,24 @@
 				if (modFormat == FORMAT_NT || modFormat == FORMAT_HMNT)
 				{
 					// any Dxx == D00 in NT/HMNT
-					if (ton->effTyp == 0xD)
-						ton->eff = 0;
+					if (p->efx == 0xD)
+						p->efxData = 0;
 
 					// effect F with param 0x00 does nothing in NT/HMNT
-					if (ton->effTyp == 0xF && ton->eff == 0)
-						ton->effTyp = 0;
+					if (p->efx == 0xF && p->efxData == 0)
+						p->efx = 0;
 				}
 				else if (modFormat == FORMAT_FLT4 || modFormat == FORMAT_FLT8) // Startrekker
 				{
-					if (ton->effTyp == 0xE) // remove unsupported "assembly macros" command
+					if (p->efx == 0xE) // remove unsupported "assembly macros" command
 					{
-						ton->effTyp = 0;
-						ton->eff = 0;
+						p->efx = 0;
+						p->efxData = 0;
 					}
 
 					// Startrekker is always in vblank mode, and limits speed to 0x1F
-					if (ton->effTyp == 0xF && ton->eff > 0x1F)
-						ton->eff = 0x1F;
+					if (p->efx == 0xF && p->efxData > 0x1F)
+						p->efxData = 0x1F;
 				}
 			}
 		}
@@ -288,7 +288,7 @@
 
 	for (a = 0; a < 31; a++)
 	{
-		if (h_MOD31.instr[a].len == 0)
+		if (hdr.smp[a].length == 0)
 			continue;
 
 		if (!allocateTmpInstr(1+a))
@@ -299,7 +299,7 @@
 
 		setNoEnvelope(instrTmp[1+a]);
 
-		s = &instrTmp[1+a]->samp[0];
+		s = &instrTmp[1+a]->smp[0];
 
 		// copy over sample name if format isn't "His Master's Noisetracker" (junk sample names)
 		if (modFormat != FORMAT_HMNT)
@@ -306,65 +306,62 @@
 			memcpy(s->name, songTmp.instrName[1+a], 22);
 
 		if (modFormat == FORMAT_HMNT) // finetune in "His Master's NoiseTracker" is different
-			h_MOD31.instr[a].fine = (uint8_t)((-h_MOD31.instr[a].fine & 0x1F) >> 1); // one more bit of precision, + inverted
+			hdr.smp[a].finetune = (uint8_t)((-hdr.smp[a].finetune & 0x1F) >> 1); // one more bit of precision, + inverted
 
-		s->len = 2 * SWAP16(h_MOD31.instr[a].len);
-		s->fine = 8 * ((2 * ((h_MOD31.instr[a].fine & 0xF) ^ 8)) - 16);
-		s->vol = h_MOD31.instr[a].vol;
-		s->repS = 2 * SWAP16(h_MOD31.instr[a].repS);
-		s->repL = 2 * SWAP16(h_MOD31.instr[a].repL);
+		s->length = 2 * SWAP16(hdr.smp[a].length);
+		s->finetune = FINETUNE_MOD2XM(hdr.smp[a].finetune);
+		s->volume = hdr.smp[a].volume;
+		s->loopStart = 2 * SWAP16(hdr.smp[a].loopStart);
+		s->loopLength = 2 * SWAP16(hdr.smp[a].loopLength);
 
-		if (s->vol > 64)
-			s->vol = 64;
+		if (s->loopLength < 2)
+			s->loopLength = 2;
 
-		if (s->repL < 2)
-			s->repL = 2;
-
 		// fix for poorly converted STK (< v2.5) -> PT/NT modules (FIXME: Worth keeping or not?)
-		if (s->repL > 2 && s->repS+s->repL > s->len)
+		if (s->loopLength > 2 && s->loopStart+s->loopLength > s->length)
 		{
-			if ((s->repS>>1)+s->repL <= s->len)
-				s->repS >>= 1;
+			if ((s->loopStart >> 1) + s->loopLength <= s->length)
+				s->loopStart >>= 1;
 		}
 
 		// fix overflown loop
-		if (s->repS+s->repL > s->len)
+		if (s->loopStart+s->loopLength > s->length)
 		{
-			if (s->repS >= s->len)
+			if (s->loopStart >= s->length)
 			{
-				s->repS = 0;
-				s->repL = 0;
+				s->loopStart = 0;
+				s->loopLength = 0;
 			}
 			else
 			{
-				s->repL = s->len - s->repS;
+				s->loopLength = s->length - s->loopStart;
 			}
 		}
 
-		if (s->repS+s->repL > 2)
-			s->typ = 1; // enable loop
+		if (s->loopStart+s->loopLength > 2)
+			s->flags |= LOOP_FWD; // enable loop
 
-		if (!allocateTmpSmpData(s, s->len))
+		if (!allocateSmpData(s, s->length, false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f);
-		if (bytesRead < s->len)
+		int32_t bytesRead = (int32_t)fread(s->dataPtr, 1, s->length, f);
+		if (bytesRead < s->length)
 		{
-			int32_t bytesToClear = s->len - bytesRead;
-			memset(&s->pek[bytesRead], 0, bytesToClear);
+			int32_t bytesToClear = s->length - bytesRead;
+			memset(&s->dataPtr[bytesRead], 0, bytesToClear);
 		}
 
-		if (s->typ == 0) // clear repL and repS on non-looping samples...
+		if (GET_LOOPTYPE(s->flags) == LOOP_OFF) // clear loopLength and loopStart on non-looping samples...
 		{
-			s->repL = 0;
-			s->repS = 0;
+			s->loopLength = 0;
+			s->loopStart = 0;
 		}
 	}
 
-	if (hasMoreThan32Chans)
+	if (tooManyChannels)
 		loaderMsgBox("Warning: Module contains >32 channels. The extra channels will be discarded!");
 
 	return true;
--- a/src/modloaders/ft2_load_s3m.c
+++ b/src/modloaders/ft2_load_s3m.c
@@ -1,4 +1,8 @@
-// Scream Tracker 3 (or compatible) S3M loader
+/* Scream Tracker 3 (or compatible) S3M loader
+**
+** Note: Data sanitation is done in the last stage
+** of module loading, so you don't need to do that here.
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -13,38 +17,36 @@
 #pragma pack(push)
 #pragma pack(1)
 #endif
-typedef struct songS3MinstrHeaderTyp_t
+typedef struct s3mSmpHdr_t
 {
-	uint8_t typ;
-	char dosName[12];
-	uint8_t memSegH;
-	uint16_t memSeg;
-	int32_t len, repS, repE;
-	uint8_t vol, dsk, pack, flags;
-	int32_t c2Spd, res1;
-	uint16_t gusPos;
-	uint8_t res2[6];
-	char name[28], id[4];
+	uint8_t type, junk1[12], offsetInFileH;
+	uint16_t offsetInFile;
+	int32_t length, loopStart, loopEnd;
+	uint8_t volume, junk2, packFlag, flags;
+	int32_t midCFreq, junk3;
+	uint16_t junk4;
+	uint8_t junk5[6];
+	char name[28], ID[4];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songS3MinstrHeaderTyp;
+s3mSmpHdr_t;
 
-typedef struct songS3MHeaderTyp_t
+typedef struct s3mHdr_t
 {
 	char name[28];
-	uint8_t id1a, typ;
-	uint16_t res1;
-	int16_t songTabLen, antInstr, antPatt;
-	uint16_t flags, trackerID, ver;
-	char id[4];
-	uint8_t globalVol, defSpeed, defTempo, masterVol, res2[12], chanType[32];
+	uint8_t junk1, type;
+	uint16_t junk2;
+	int16_t numOrders, numSamples, numPatterns;
+	uint16_t flags, junk3, version;
+	char ID[4];
+	uint8_t junk4, speed, BPM, junk5, junk6[12], chnSettings[32];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songS3MHeaderTyp;
+s3mHdr_t;
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif
@@ -55,117 +57,111 @@
 
 bool loadS3M(FILE *f, uint32_t filesize)
 {
-	uint8_t ha[2048];
 	uint8_t alastnfo[32], alastefx[32], alastvibnfo[32], s3mLastGInstr[32];
-	uint8_t typ;
-	int16_t ai, ap, ver, ii, kk, tmp;
-	uint16_t ptnOfs[256];
-	int32_t i, j, k, len;
-	tonTyp ton;
-	sampleTyp *s;
-	songS3MHeaderTyp h_S3M;
-	songS3MinstrHeaderTyp h_S3MInstr;
+	int16_t ii, kk, tmp;
+	int32_t patternOffsets[256], sampleOffsets[256], j, k;
+	note_t tmpNote;
+	sample_t *s;
+	s3mHdr_t hdr;
+	s3mSmpHdr_t smpHdr;
 
 	tmpLinearPeriodsFlag = false; // use Amiga periods
 
-	if (filesize < sizeof (h_S3M))
+	if (filesize < sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	memset(&h_S3M, 0, sizeof (h_S3M));
-	if (fread(&h_S3M, 1, sizeof (h_S3M), f) != sizeof (h_S3M))
+	memset(&hdr, 0, sizeof (hdr));
+	if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	if (h_S3M.antInstr > MAX_INST || h_S3M.songTabLen > 256 || h_S3M.antPatt > 256 ||
-		h_S3M.typ != 16 || h_S3M.ver < 1 || h_S3M.ver > 2)
+	if (hdr.numSamples > MAX_INST || hdr.numOrders > MAX_ORDERS || hdr.numPatterns > MAX_PATTERNS ||
+		hdr.type != 16 || hdr.version < 1 || hdr.version > 2)
 	{
 		loaderMsgBox("Error loading .s3m: Incompatible module!");
 		return false;
 	}
 
-	memset(songTmp.songTab, 255, sizeof (songTmp.songTab));
-	if (fread(songTmp.songTab, h_S3M.songTabLen, 1, f) != 1)
+	memset(songTmp.orders, 255, 256); // pad by 255
+	if (fread(songTmp.orders, hdr.numOrders, 1, f) != 1)
 	{
 		loaderMsgBox("General I/O error during loading! Is the file in use?");
 		return false;
 	}
 
-	songTmp.len = h_S3M.songTabLen;
+	songTmp.songLength = hdr.numOrders;
 
 	// remove pattern separators (254)
 	k = 0;
 	j = 0;
-	for (i = 0; i < songTmp.len; i++)
+	for (int32_t i = 0; i < songTmp.songLength; i++)
 	{
-		if (songTmp.songTab[i] != 254)
-			songTmp.songTab[j++] = songTmp.songTab[i];
+		if (songTmp.orders[i] != 254)
+			songTmp.orders[j++] = songTmp.orders[i];
 		else
 			k++;
 	}
 
-	if (k <= songTmp.len)
-		songTmp.len -= (uint16_t)k;
+	if (k <= songTmp.songLength)
+		songTmp.songLength -= (uint16_t)k;
 	else
-		songTmp.len = 1;
+		songTmp.songLength = 1;
 
-	for (i = 1; i < songTmp.len; i++)
+	for (int32_t i = 1; i < songTmp.songLength; i++)
 	{
-		if (songTmp.songTab[i] == 255)
+		if (songTmp.orders[i] == 255)
 		{
-			songTmp.len = (uint16_t)i;
+			songTmp.songLength = (uint16_t)i;
 			break;
 		}
 	}
 	
 	// clear unused song table entries
-	if (songTmp.len < 255)
-		memset(&songTmp.songTab[songTmp.len], 0, 255-songTmp.len);
+	if (songTmp.songLength < 255)
+		memset(&songTmp.orders[songTmp.songLength], 0, 255-songTmp.songLength);
 
-	songTmp.speed = h_S3M.defTempo;
-	if (songTmp.speed < 32)
-		songTmp.speed = 32;
+	memcpy(songTmp.name, hdr.name, 20);
 
-	songTmp.tempo = h_S3M.defSpeed;
-	if (songTmp.tempo == 0)
-		songTmp.tempo = 6;
+	songTmp.BPM = hdr.BPM;
+	songTmp.speed = hdr.speed;
 
-	if (songTmp.tempo > 31)
-		songTmp.tempo = 31;
-
-	songTmp.initialTempo = songTmp.tempo;
-	memcpy(songTmp.name, h_S3M.name, 20);
-
-	ap = h_S3M.antPatt;
-	ai = h_S3M.antInstr;
-	ver = h_S3M.ver;
-
-	k = 31;
-	while (k >= 0 && h_S3M.chanType[k] >= 16) k--;
-	songTmp.antChn = (k + 2) & 254;
-
-	if (fread(ha, ai*2, 1, f) != 1)
+	// load sample offsets
+	for (int32_t i = 0; i < hdr.numSamples; i++)
 	{
-		loaderMsgBox("General I/O error during loading! Is the file in use?");
-		return false;
+		uint16_t offset;
+		if (fread(&offset, 2, 1, f) != 1)
+		{
+			loaderMsgBox("General I/O error during loading! Is the file in use?");
+			return false;
+		}
+
+		sampleOffsets[i] = offset << 4;
 	}
 
-	if (fread(ptnOfs, ap*2, 1, f) != 1)
+	// load pattern offsets
+	for (int32_t i = 0; i < hdr.numPatterns; i++)
 	{
-		loaderMsgBox("General I/O error during loading! Is the file in use?");
-		return false;
+		uint16_t offset;
+		if (fread(&offset, 2, 1, f) != 1)
+		{
+			loaderMsgBox("General I/O error during loading! Is the file in use?");
+			return false;
+		}
+
+		patternOffsets[i] = offset << 4;
 	}
 
 	// *** PATTERNS ***
 
 	k = 0;
-	for (i = 0; i < ap; i++)
+	for (int32_t i = 0; i < hdr.numPatterns; i++)
 	{
-		if (ptnOfs[i]  == 0)
+		if (patternOffsets[i]  == 0)
 			continue; // empty pattern
 
 		memset(alastnfo, 0, sizeof (alastnfo));
@@ -173,7 +169,7 @@
 		memset(alastvibnfo, 0, sizeof (alastvibnfo));
 		memset(s3mLastGInstr, 0, sizeof (s3mLastGInstr));
 
-		fseek(f, ptnOfs[i] << 4, SEEK_SET);
+		fseek(f, patternOffsets[i], SEEK_SET);
 		if (feof(f))
 			continue;
 
@@ -198,69 +194,69 @@
 
 			while (k < j && kk < 64)
 			{
-				typ = pattBuff[k++];
+				uint8_t bits = pattBuff[k++];
 
-				if (typ == 0)
+				if (bits == 0)
 				{
 					kk++;
 				}
 				else
 				{
-					ii = typ & 31;
+					ii = bits & 31;
 
-					memset(&ton, 0, sizeof (ton));
+					memset(&tmpNote, 0, sizeof (tmpNote));
 
 					// note and sample
-					if (typ & 32)
+					if (bits & 32)
 					{
-						ton.ton = pattBuff[k++];
-						ton.instr = pattBuff[k++];
+						tmpNote.note = pattBuff[k++];
+						tmpNote.instr = pattBuff[k++];
 
-						if (ton.instr > MAX_INST)
-							ton.instr = 0;
+						if (tmpNote.instr > MAX_INST)
+							tmpNote.instr = 0;
 
-						     if (ton.ton == 254) ton.ton = 97;
-						else if (ton.ton == 255) ton.ton = 0;
+						     if (tmpNote.note == 254) tmpNote.note = NOTE_OFF;
+						else if (tmpNote.note == 255) tmpNote.note = 0;
 						else
 						{
-							ton.ton = 1 + (ton.ton & 0xF) + (ton.ton >> 4) * 12;
-							if (ton.ton > 96)
-								ton.ton = 0;
+							tmpNote.note = 1 + (tmpNote.note & 0xF) + (tmpNote.note >> 4) * 12;
+							if (tmpNote.note > 96)
+								tmpNote.note = 0;
 						}
 					}
 
 					// volume column
-					if (typ & 64)
+					if (bits & 64)
 					{
-						ton.vol = pattBuff[k++];
+						tmpNote.vol = pattBuff[k++];
 
-						if (ton.vol <= 64)
-							ton.vol += 0x10;
+						if (tmpNote.vol <= 64)
+							tmpNote.vol += 0x10;
 						else
-							ton.vol = 0;
+							tmpNote.vol = 0;
 					}
 
 					// effect
-					if (typ & 128)
+					if (bits & 128)
 					{
-						ton.effTyp = pattBuff[k++];
-						ton.eff = pattBuff[k++];
+						tmpNote.efx = pattBuff[k++];
+						tmpNote.efxData = pattBuff[k++];
 
-						if (ton.eff > 0)
+						if (tmpNote.efxData > 0)
 						{
-							alastnfo[ii] = ton.eff;
-							if (ton.effTyp == 8 || ton.effTyp == 21)
-								alastvibnfo[ii] = ton.eff; // H/U
+							alastnfo[ii] = tmpNote.efxData;
+							if (tmpNote.efx == 8 || tmpNote.efx == 21)
+								alastvibnfo[ii] = tmpNote.efxData; // H/U
 						}
 
 						// in ST3, a lot of effects directly share the same memory!
-						if (ton.eff == 0 && ton.effTyp != 7) // G
+						if (tmpNote.efxData == 0 && tmpNote.efx != 7) // G
 						{
-							uint8_t efx = ton.effTyp;
+							uint8_t efx = tmpNote.efx;
 							if (efx == 8 || efx == 21) // H/U
-								ton.eff = alastvibnfo[ii];
+								tmpNote.efxData = alastvibnfo[ii];
 							else if ((efx >= 4 && efx <= 12) || (efx >= 17 && efx <= 19)) // D/E/F/I/J/K/L/Q/R/S
-								ton.eff = alastnfo[ii];
+								tmpNote.efxData = alastnfo[ii];
 
 							/* If effect data is zero and effect type was the same as last one, clear out
 							** data if it's not J or S (those have no memory in the equivalent XM effects).
@@ -267,57 +263,57 @@
 							** Also goes for extra fine pitch slides and fine volume slides,
 							** since they get converted to other effects.
 							*/
-							if (efx == alastefx[ii] && ton.effTyp != 10 && ton.effTyp != 19) // J/S
+							if (efx == alastefx[ii] && tmpNote.efx != 10 && tmpNote.efx != 19) // J/S
 							{
-								uint8_t nfo = ton.eff;
+								uint8_t nfo = tmpNote.efxData;
 								bool extraFinePitchSlides = (efx == 5 || efx == 6) && ((nfo & 0xF0) == 0xE0);
 								bool fineVolSlides = (efx == 4 || efx == 11) &&
 								     ((nfo > 0xF0) || (((nfo & 0xF) == 0xF) && ((nfo & 0xF0) > 0)));
 
 								if (!extraFinePitchSlides && !fineVolSlides)
-									ton.eff = 0;
+									tmpNote.efxData = 0;
 							}
 						}
 
-						if (ton.effTyp > 0)
-							alastefx[ii] = ton.effTyp;
+						if (tmpNote.efx > 0)
+							alastefx[ii] = tmpNote.efx;
 
-						switch (ton.effTyp)
+						switch (tmpNote.efx)
 						{
 							case 1: // A
 							{
-								ton.effTyp = 0xF;
-								if (ton.eff == 0)
+								tmpNote.efx = 0xF;
+								if (tmpNote.efxData == 0)
 								{
-									ton.effTyp = 0;
-									ton.eff = 0;
+									tmpNote.efx = 0;
+									tmpNote.efxData = 0;
 								}
-								else if (ton.eff > 0x1F)
+								else if (tmpNote.efxData > 0x1F)
 								{
-									ton.eff = 0x1F;
+									tmpNote.efxData = 0x1F;
 								}
 							}
 							break;
 
-							case 2: ton.effTyp = 0xB; break; // B
-							case 3: ton.effTyp = 0xD; break; // C
+							case 2: tmpNote.efx = 0xB; break; // B
+							case 3: tmpNote.efx = 0xD; break; // C
 							case 4: // D
 							{
-								if (ton.eff > 0xF0) // fine slide up
+								if (tmpNote.efxData > 0xF0) // fine slide up
 								{
-									ton.effTyp = 0xE;
-									ton.eff = 0xB0 | (ton.eff & 0xF);
+									tmpNote.efx = 0xE;
+									tmpNote.efxData = 0xB0 | (tmpNote.efxData & 0xF);
 								}
-								else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down
+								else if ((tmpNote.efxData & 0x0F) == 0x0F && (tmpNote.efxData & 0xF0) > 0) // fine slide down
 								{
-									ton.effTyp = 0xE;
-									ton.eff = 0xA0 | (ton.eff >> 4);
+									tmpNote.efx = 0xE;
+									tmpNote.efxData = 0xA0 | (tmpNote.efxData >> 4);
 								}
 								else
 								{
-									ton.effTyp = 0xA;
-									if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3
-										ton.eff &= 0x0F;
+									tmpNote.efx = 0xA;
+									if (tmpNote.efxData & 0x0F) // on D/K, last nybble has first priority in ST3
+										tmpNote.efxData &= 0x0F;
 								}
 							}
 							break;
@@ -325,32 +321,32 @@
 							case 5: // E
 							case 6: // F
 							{
-								if ((ton.eff & 0xF0) >= 0xE0)
+								if ((tmpNote.efxData & 0xF0) >= 0xE0)
 								{
 									// convert to fine slide
-									if ((ton.eff & 0xF0) == 0xE0)
+									if ((tmpNote.efxData & 0xF0) == 0xE0)
 										tmp = 0x21;
 									else
 										tmp = 0xE;
 
-									ton.eff &= 0x0F;
+									tmpNote.efxData &= 0x0F;
 
-									if (ton.effTyp == 0x05)
-										ton.eff |= 0x20;
+									if (tmpNote.efx == 0x05)
+										tmpNote.efxData |= 0x20;
 									else
-										ton.eff |= 0x10;
+										tmpNote.efxData |= 0x10;
 
-									ton.effTyp = (uint8_t)tmp;
+									tmpNote.efx = (uint8_t)tmp;
 
-									if (ton.effTyp == 0x21 && ton.eff == 0)
+									if (tmpNote.efx == 0x21 && tmpNote.efxData == 0)
 									{
-										ton.effTyp = 0;
+										tmpNote.efx = 0;
 									}
 								}
 								else
 								{
 									// convert to normal 1xx/2xx slide
-									ton.effTyp = 7 - ton.effTyp;
+									tmpNote.efx = 7 - tmpNote.efx;
 								}
 							}
 							break;
@@ -357,98 +353,98 @@
 
 							case 7: // G
 							{
-								ton.effTyp = 0x03;
+								tmpNote.efx = 0x03;
 
 								// fix illegal slides (to new instruments)
-								if (ton.instr != 0 && ton.instr != s3mLastGInstr[ii])
-									ton.instr = s3mLastGInstr[ii];
+								if (tmpNote.instr != 0 && tmpNote.instr != s3mLastGInstr[ii])
+									tmpNote.instr = s3mLastGInstr[ii];
 							}
 							break;
 
 							case 11: // K
 							{
-								if (ton.eff > 0xF0) // fine slide up
+								if (tmpNote.efxData > 0xF0) // fine slide up
 								{
-									ton.effTyp = 0xE;
-									ton.eff = 0xB0 | (ton.eff & 0xF);
+									tmpNote.efx = 0xE;
+									tmpNote.efxData = 0xB0 | (tmpNote.efxData & 0xF);
 
 									// if volume column is unoccupied, set to vibrato
-									if (ton.vol == 0)
-										ton.vol = 0xB0;
+									if (tmpNote.vol == 0)
+										tmpNote.vol = 0xB0;
 								}
-								else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down
+								else if ((tmpNote.efxData & 0x0F) == 0x0F && (tmpNote.efxData & 0xF0) > 0) // fine slide down
 								{
-									ton.effTyp = 0xE;
-									ton.eff = 0xA0 | (ton.eff >> 4);
+									tmpNote.efx = 0xE;
+									tmpNote.efxData = 0xA0 | (tmpNote.efxData >> 4);
 
 									// if volume column is unoccupied, set to vibrato
-									if (ton.vol == 0)
-										ton.vol = 0xB0;
+									if (tmpNote.vol == 0)
+										tmpNote.vol = 0xB0;
 								}
 								else
 								{
-									ton.effTyp = 0x6;
-									if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3
-										ton.eff &= 0x0F;
+									tmpNote.efx = 0x6;
+									if (tmpNote.efxData & 0x0F) // on D/K, last nybble has first priority in ST3
+										tmpNote.efxData &= 0x0F;
 								}
 							}
 							break;
 
-							case 8: ton.effTyp = 0x04; break; // H
-							case 9: ton.effTyp = 0x1D; break; // I
-							case 10: ton.effTyp = 0x00; break; // J
-							case 12: ton.effTyp = 0x05; break; // L
-							case 15: ton.effTyp = 0x09; break; // O
-							case 17: ton.effTyp = 0x1B; break; // Q
-							case 18: ton.effTyp = 0x07; break; // R
+							case 8: tmpNote.efx = 0x04; break; // H
+							case 9: tmpNote.efx = 0x1D; break; // I
+							case 10: tmpNote.efx = 0x00; break; // J
+							case 12: tmpNote.efx = 0x05; break; // L
+							case 15: tmpNote.efx = 0x09; break; // O
+							case 17: tmpNote.efx = 0x1B; break; // Q
+							case 18: tmpNote.efx = 0x07; break; // R
 
 							case 19: // S
 							{
-								ton.effTyp = 0xE;
-								tmp = ton.eff >> 4;
-								ton.eff &= 0x0F;
+								tmpNote.efx = 0xE;
+								tmp = tmpNote.efxData >> 4;
+								tmpNote.efxData &= 0x0F;
 
-								     if (tmp == 0x1) ton.eff |= 0x30;
-								else if (tmp == 0x2) ton.eff |= 0x50;
-								else if (tmp == 0x3) ton.eff |= 0x40;
-								else if (tmp == 0x4) ton.eff |= 0x70;
+								     if (tmp == 0x1) tmpNote.efxData |= 0x30;
+								else if (tmp == 0x2) tmpNote.efxData |= 0x50;
+								else if (tmp == 0x3) tmpNote.efxData |= 0x40;
+								else if (tmp == 0x4) tmpNote.efxData |= 0x70;
 								// ignore S8x becuase it's not compatible with FT2 panning
-								else if (tmp == 0xB) ton.eff |= 0x60;
+								else if (tmp == 0xB) tmpNote.efxData |= 0x60;
 								else if (tmp == 0xC) // Note Cut
 								{
-									ton.eff |= 0xC0;
-									if (ton.eff == 0xC0)
+									tmpNote.efxData |= 0xC0;
+									if (tmpNote.efxData == 0xC0)
 									{
 										// EC0 does nothing in ST3 but cuts voice in FT2, remove effect
-										ton.effTyp = 0;
-										ton.eff = 0;
+										tmpNote.efx = 0;
+										tmpNote.efxData = 0;
 									}
 								}
 								else if (tmp == 0xD) // Note Delay
 								{
-									ton.eff |= 0xD0;
-									if (ton.ton == 0 || ton.ton == 97)
+									tmpNote.efxData |= 0xD0;
+									if (tmpNote.note == 0 || tmpNote.note == NOTE_OFF)
 									{
 										// EDx without a note does nothing in ST3 but retrigs in FT2, remove effect
-										ton.effTyp = 0;
-										ton.eff = 0;
+										tmpNote.efx = 0;
+										tmpNote.efxData = 0;
 									}
-									else if (ton.eff == 0xD0)
+									else if (tmpNote.efxData == 0xD0)
 									{
 										// ED0 prevents note/smp/vol from updating in ST3, remove everything
-										ton.ton = 0;
-										ton.instr = 0;
-										ton.vol = 0;
-										ton.effTyp = 0;
-										ton.eff = 0;
+										tmpNote.note = 0;
+										tmpNote.instr = 0;
+										tmpNote.vol = 0;
+										tmpNote.efx = 0;
+										tmpNote.efxData = 0;
 									}
 								}
-								else if (tmp == 0xE) ton.eff |= 0xE0;
-								else if (tmp == 0xF) ton.eff |= 0xF0;
+								else if (tmp == 0xE) tmpNote.efxData |= 0xE0;
+								else if (tmp == 0xF) tmpNote.efxData |= 0xF0;
 								else
 								{
-									ton.effTyp = 0;
-									ton.eff = 0;
+									tmpNote.efx = 0;
+									tmpNote.efxData = 0;
 								}
 							}
 							break;
@@ -455,11 +451,11 @@
 
 							case 20: // T
 							{
-								ton.effTyp = 0x0F;
-								if (ton.eff < 0x21) // Txx with a value lower than 33 (0x21) does nothing in ST3, remove effect
+								tmpNote.efx = 0x0F;
+								if (tmpNote.efxData < 0x21) // Txx with a value lower than 33 (0x21) does nothing in ST3, remove effect
 								{
-									ton.effTyp = 0;
-									ton.eff = 0;
+									tmpNote.efx = 0;
+									tmpNote.efxData = 0;
 								}
 							}
 							break;
@@ -466,12 +462,12 @@
 
 							case 22: // V
 							{
-								ton.effTyp = 0x10;
-								if (ton.eff > 0x40)
+								tmpNote.efx = 0x10;
+								if (tmpNote.efxData > 0x40)
 								{
 									// Vxx > 0x40 does nothing in ST3
-									ton.effTyp = 0;
-									ton.eff = 0;
+									tmpNote.efx = 0;
+									tmpNote.efxData = 0;
 								}
 							}
 							break;
@@ -478,26 +474,26 @@
 
 							default:
 							{
-								ton.effTyp = 0;
-								ton.eff = 0;
+								tmpNote.efx = 0;
+								tmpNote.efxData = 0;
 							}
 							break;
 						}
 					}
 
-					if (ton.instr != 0 && ton.effTyp != 0x3)
-						s3mLastGInstr[ii] = ton.instr;
+					if (tmpNote.instr != 0 && tmpNote.efx != 0x3)
+						s3mLastGInstr[ii] = tmpNote.instr;
 
-					pattTmp[i][(kk * MAX_VOICES) + ii] = ton;
+					patternTmp[i][(kk * MAX_CHANNELS) + ii] = tmpNote;
 				}
 			}
 
 			if (tmpPatternEmpty((uint16_t)i))
 			{
-				if (pattTmp[i] != NULL)
+				if (patternTmp[i] != NULL)
 				{
-					free(pattTmp[i]);
-					pattTmp[i] = NULL;
+					free(patternTmp[i]);
+					patternTmp[i] = NULL;
 				}
 			}
 		}
@@ -506,32 +502,34 @@
 	// *** SAMPLES ***
 
 	bool adlibInsWarn = false;
-
-	memcpy(ptnOfs, ha, 512);
-	for (i = 0; i < ai; i++)
+	for (int32_t i = 0; i < hdr.numSamples; i++)
 	{
-		fseek(f, ptnOfs[i] << 4, SEEK_SET);
+		if (sampleOffsets[i] == 0)
+			continue;
 
-		if (fread(&h_S3MInstr, 1, sizeof (h_S3MInstr), f) != sizeof (h_S3MInstr))
+		fseek(f, sampleOffsets[i], SEEK_SET);
+
+		if (fread(&smpHdr, 1, sizeof (smpHdr), f) != sizeof (smpHdr))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 		
-		memcpy(songTmp.instrName[1+i], h_S3MInstr.name, 22);
+		memcpy(songTmp.instrName[1+i], smpHdr.name, 22);
 
-		if (h_S3MInstr.typ == 2)
+		if (smpHdr.type == 2)
 		{
 			adlibInsWarn = true;
 		}
-		else if (h_S3MInstr.typ == 1)
+		else if (smpHdr.type == 1)
 		{
-			if ((h_S3MInstr.flags & (255-1-2-4)) != 0 || h_S3MInstr.pack != 0)
+			int32_t offsetInFile = ((smpHdr.offsetInFileH << 16) | smpHdr.offsetInFile) << 4;
+			if ((smpHdr.flags & (255-1-2-4)) != 0 || smpHdr.packFlag != 0)
 			{
 				loaderMsgBox("Error loading .s3m: Incompatible module!");
 				return false;
 			}
-			else if (h_S3MInstr.memSeg > 0 && h_S3MInstr.len > 0)
+			else if (offsetInFile > 0 && smpHdr.length > 0)
 			{
 				if (!allocateTmpInstr((int16_t)(1 + i)))
 				{
@@ -540,90 +538,86 @@
 				}
 
 				setNoEnvelope(instrTmp[1 + i]);
-				s = &instrTmp[1+i]->samp[0];
+				s = &instrTmp[1+i]->smp[0];
 
+				if (smpHdr.midCFreq > 65535) // ST3 (and OpenMPT) does this
+					smpHdr.midCFreq = 65535;
+
+				memcpy(s->name, smpHdr.name, 22);
+
 				// non-FT2: fixes "miracle man.s3m" and other broken S3Ms
-				if ((h_S3MInstr.memSeg<<4)+h_S3MInstr.len > (int32_t)filesize)
-					h_S3MInstr.len = filesize - (h_S3MInstr.memSeg << 4);
+				if (offsetInFile+smpHdr.length > (int32_t)filesize)
+					smpHdr.length = filesize - offsetInFile;
 
-				len = h_S3MInstr.len;
+				bool hasLoop = !!(smpHdr.flags & 1);
+				bool stereoSample = !!(smpHdr.flags & 2);
+				bool sample16Bit = !!(smpHdr.flags & 4);
 
-				bool hasLoop = h_S3MInstr.flags & 1;
-				bool stereoSample = (h_S3MInstr.flags >> 1) & 1;
-				bool is16Bit = (h_S3MInstr.flags >> 2) & 1;
-			
-				if (is16Bit) // 16-bit
-					len <<= 1;
+				if (stereoSample)
+					smpHdr.length <<= 1;
 
-				if (stereoSample) // stereo
-					len <<= 1;
+				int32_t lengthInFile = smpHdr.length;
 
-				if (!allocateTmpSmpData(s, len))
+				s->length = smpHdr.length;
+				s->volume = smpHdr.volume;
+				s->loopStart = smpHdr.loopStart;
+				s->loopLength = smpHdr.loopEnd - smpHdr.loopStart;
+
+				tuneSample(s, smpHdr.midCFreq, tmpLinearPeriodsFlag);
+
+				if (sample16Bit)
 				{
+					s->flags |= SAMPLE_16BIT;
+					lengthInFile <<= 1;
+				}
+
+				if (!allocateSmpData(s, s->length, sample16Bit))
+				{
 					loaderMsgBox("Not enough memory!");
 					return false;
 				}
 
-				memcpy(s->name, h_S3MInstr.name, 21);
-
-				if (h_S3MInstr.c2Spd > 65535) // ST3 (and OpenMPT) does this
-					h_S3MInstr.c2Spd = 65535;
-
-				s->len = h_S3MInstr.len;
-				s->vol = h_S3MInstr.vol;
-				s->repS = h_S3MInstr.repS;
-				s->repL = h_S3MInstr.repE - h_S3MInstr.repS;
-
-				tuneSample(s, h_S3MInstr.c2Spd, tmpLinearPeriodsFlag);
-
-				if (s->vol > 64)
-					s->vol = 64;
-
-				if (s->repL <= 2 || s->repS+s->repL > s->len || s->repL == 0)
+				if (s->loopLength <= 1 || s->loopStart+s->loopLength > s->length || s->loopLength == 0)
 				{
-					s->repS = 0;
-					s->repL = 0;
+					s->loopStart = 0;
+					s->loopLength = 0;
 					hasLoop = false;
 				}
 
-				s->typ = hasLoop + (is16Bit << 4);
+				if (hasLoop)
+					s->flags |= LOOP_FWD;
 
-				fseek(f, h_S3MInstr.memSeg << 4, SEEK_SET);
+				fseek(f, offsetInFile, SEEK_SET);
 
-				if (ver == 1)
+				if (hdr.version == 1)
 				{
-					fseek(f, len, SEEK_CUR); // sample not supported
+					fseek(f, lengthInFile, SEEK_CUR); // sample not supported
 				}
 				else
 				{
-					if (fread(s->pek, len, 1, f) != 1)
+					if (fread(s->dataPtr, SAMPLE_LENGTH_BYTES(s), 1, f) != 1)
 					{
 						loaderMsgBox("General I/O error during loading! Is the file in use?");
 						return false;
 					}
 
-					if (is16Bit)
-					{
-						conv16BitSample(s->pek, len, stereoSample);
-
-						s->len <<= 1;
-						s->repS <<= 1;
-						s->repL <<= 1;
-					}
+					if (sample16Bit)
+						conv16BitSample(s->dataPtr, s->length, stereoSample);
 					else
-					{
-						conv8BitSample(s->pek, len, stereoSample);
-					}
+						conv8BitSample(s->dataPtr, s->length, stereoSample);
 
 					// if stereo sample: reduce memory footprint after sample was downmixed to mono
 					if (stereoSample)
-						reallocateTmpSmpData(s, s->len);
+					{
+						s->length >>= 1;
+						reallocateSmpData(s, s->length, sample16Bit);
+					}
 				}
 			}
 		}
 	}
 
-	songTmp.antChn = countS3MChannels(ap);
+	songTmp.numChannels = countS3MChannels(hdr.numPatterns);
 
 	if (adlibInsWarn)
 		loaderMsgBox("Warning: The module contains unsupported AdLib instruments!");
@@ -639,15 +633,15 @@
 	int32_t channels = 0;
 	for (int32_t i = 0; i < antPtn; i++)
 	{
-		if (pattTmp[i] == NULL)
+		if (patternTmp[i] == NULL)
 			continue;
 
-		tonTyp *ton = pattTmp[i];
+		note_t *p = patternTmp[i];
 		for (int32_t j = 0; j < 64; j++)
 		{
-			for (int32_t k = 0; k < MAX_VOICES; k++, ton++)
+			for (int32_t k = 0; k < MAX_CHANNELS; k++, p++)
 			{
-				if (ton->eff == 0 && ton->effTyp == 0 && ton->instr == 0 && ton->ton == 0 && ton->vol == 0)
+				if (p->note == 0 && p->instr == 0 && p->vol == 0 && p->efx == 0 && p->efxData == 0)
 					continue;
 
 				if (k > channels)
--- a/src/modloaders/ft2_load_stk.c
+++ b/src/modloaders/ft2_load_stk.c
@@ -1,4 +1,8 @@
-// Ultimate SoundTracker (or compatible) STK loader
+/* Ultimate SoundTracker (or compatible) STK loader
+**
+** Note: Data sanitation is done in the last stage
+** of module loading, so you don't need to do that here.
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -13,16 +17,16 @@
 #pragma pack(push)
 #pragma pack(1)
 #endif
-typedef struct songMOD15HeaderTyp_t
+typedef struct stkHdr_t
 {
 	char name[20];
-	songMODInstrHeaderTyp instr[15];
-	uint8_t len, CIAVal, songTab[128];
+	modSmpHdr_t smp[15];
+	uint8_t numOrders, CIAVal, orders[128];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songMOD15HeaderTyp;
+stkHdr_t;
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif
@@ -31,10 +35,9 @@
 {
 	uint8_t bytes[4];
 	int16_t i, j, k;
-	uint16_t a, b, period;
-	tonTyp *ton;
-	sampleTyp *s;
-	songMOD15HeaderTyp h_MOD15;
+	uint16_t a, b;
+	sample_t *s;
+	stkHdr_t h;
 
 	tmpLinearPeriodsFlag = false; // use Amiga periods
 
@@ -41,69 +44,70 @@
 	bool veryLateSTKVerFlag = false; // "DFJ SoundTracker III" nad later
 	bool lateSTKVerFlag = false; // "TJC SoundTracker II" and later
 
-	if (filesize < sizeof (h_MOD15))
+	if (filesize < sizeof (h))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	memset(&h_MOD15, 0, sizeof (songMOD15HeaderTyp));
-	if (fread(&h_MOD15, 1, sizeof (h_MOD15), f) != sizeof (h_MOD15))
+	memset(&h, 0, sizeof (stkHdr_t));
+	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	if (h_MOD15.CIAVal == 0) // a CIA value of 0 results in 120
-		h_MOD15.CIAVal = 120;
+	if (h.CIAVal == 0) // a CIA value of 0 results in 120
+		h.CIAVal = 120;
 
-	songTmp.antChn = 4;
-	songTmp.len = h_MOD15.len;
-	songTmp.speed = 125;
-	songTmp.initialTempo = songTmp.tempo = 6;
-	memcpy(songTmp.songTab, h_MOD15.songTab, 128);
-
-	if (songTmp.len < 1 || songTmp.len > 128 || h_MOD15.CIAVal > 220)
+	if (h.numOrders < 1 || h.numOrders > 128 || h.CIAVal > 220)
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
+	memcpy(songTmp.orders, h.orders, 128);
+
+	songTmp.numChannels = 4;
+	songTmp.songLength = h.numOrders;
+	songTmp.BPM = 125;
+	songTmp.speed = 6;
+
 	for (a = 0; a < 15; a++)
 	{
-		songMODInstrHeaderTyp *smp = &h_MOD15.instr[a];
-		memcpy(songTmp.instrName[1+a], smp->name, 22);
+		modSmpHdr_t *modSmp = &h.smp[a];
+		memcpy(songTmp.instrName[1+a], modSmp->name, 22);
 
 		/* Only late versions of Ultimate SoundTracker supports samples larger than 9999 bytes.
 		** If found, we know for sure that this is a late STK module.
 		*/
-		const int32_t sampleLen = 2*SWAP16(smp->len);
+		const int32_t sampleLen = 2*SWAP16(modSmp->length);
 		if (sampleLen > 9999)
 			lateSTKVerFlag = true;
 	}
 
 	// jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored (hackish!)
-	if (!strcmp("jjk55", h_MOD15.name))
-		h_MOD15.CIAVal = 120;
+	if (!strcmp("jjk55", h.name))
+		h.CIAVal = 120;
 
-	if (h_MOD15.CIAVal != 120) // 120 is a special case and means 50Hz (125BPM)
+	if (h.CIAVal != 120) // 120 is a special case and means 50Hz (125BPM)
 	{
 		// convert UST tempo to BPM
-		uint16_t ciaPeriod = (240 - h_MOD15.CIAVal) * 122;
+		uint16_t ciaPeriod = (240 - h.CIAVal) * 122;
 		double dHz = 709379.0 / ciaPeriod;
 		int32_t BPM = (int32_t)((dHz * 2.5) + 0.5);
 
-		songTmp.speed = (uint16_t)BPM;
+		songTmp.BPM = (uint16_t)BPM;
 	}
 
-	memcpy(songTmp.name, h_MOD15.name, 20);
+	memcpy(songTmp.name, h.name, 20);
 
 	// count number of patterns
 	b = 0;
 	for (a = 0; a < 128; a++)
 	{
-		if (songTmp.songTab[a] > b)
-			b = songTmp.songTab[a];
+		if (songTmp.orders[a] > b)
+			b = songTmp.orders[a];
 	}
 	b++;
 
@@ -117,9 +121,9 @@
 
 		for (j = 0; j < 64; j++)
 		{
-			for (k = 0; k < songTmp.antChn; k++)
+			for (k = 0; k < songTmp.numChannels; k++)
 			{
-				ton = &pattTmp[a][(j * MAX_VOICES) + k];
+				note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k];
 
 				if (fread(bytes, 1, 4, f) != 4)
 				{
@@ -128,27 +132,27 @@
 				}
 
 				// period to note
-				period = ((bytes[0] & 0x0F) << 8) | bytes[1];
+				uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1];
 				for (i = 0; i < 3*12; i++)
 				{
 					if (period >= ptPeriods[i])
 					{
-						ton->ton = 1 + (3*12) + (uint8_t)i;
+						p->note = 1 + (3*12) + (uint8_t)i;
 						break;
 					}
 				}
 
-				ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
-				ton->effTyp = bytes[2] & 0x0F;
-				ton->eff = bytes[3];
+				p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
+				p->efx = bytes[2] & 0x0F;
+				p->efxData = bytes[3];
 
-				if (ton->effTyp == 0xC || ton->effTyp == 0xD || ton->effTyp == 0xE)
+				if (p->efx == 0xC || p->efx == 0xD || p->efx == 0xE)
 				{
 					// "TJC SoundTracker II" and later
 					lateSTKVerFlag = true;
 				}
 
-				if (ton->effTyp == 0xF)
+				if (p->efx == 0xF)
 				{
 					// "DFJ SoundTracker III" and later
 					lateSTKVerFlag = true;
@@ -155,43 +159,43 @@
 					veryLateSTKVerFlag = true;
 				}
 
-				if (ton->effTyp == 0xC)
+				if (p->efx == 0xC)
 				{
-					if (ton->eff > 64)
-						ton->eff = 64;
+					if (p->efxData > 64)
+						p->efxData = 64;
 				}
-				else if (ton->effTyp == 0x1)
+				else if (p->efx == 0x1)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0;
+					if (p->efxData == 0)
+						p->efxData = 0;
 				}
-				else if (ton->effTyp == 0x2)
+				else if (p->efx == 0x2)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0;
+					if (p->efxData == 0)
+						p->efxData = 0;
 				}
-				else if (ton->effTyp == 0x5)
+				else if (p->efx == 0x5)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0x3;
+					if (p->efxData == 0)
+						p->efxData = 0x3;
 				}
-				else if (ton->effTyp == 0x6)
+				else if (p->efx == 0x6)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0x4;
+					if (p->efxData == 0)
+						p->efxData = 0x4;
 				}
-				else if (ton->effTyp == 0xA)
+				else if (p->efx == 0xA)
 				{
-					if (ton->eff == 0)
-						ton->effTyp = 0;
+					if (p->efxData == 0)
+						p->efxData = 0;
 				}
-				else if (ton->effTyp == 0xE)
+				else if (p->efx == 0xE)
 				{
 					// check if certain E commands are empty
-					if (ton->eff == 0x10 || ton->eff == 0x20 || ton->eff == 0xA0 || ton->eff == 0xB0)
+					if (p->efxData == 0x10 || p->efxData == 0x20 || p->efxData == 0xA0 || p->efxData == 0xB0)
 					{
-						ton->effTyp = 0;
-						ton->eff = 0;
+						p->efx = 0;
+						p->efxData = 0;
 					}
 				}
 			}
@@ -199,10 +203,10 @@
 
 		if (tmpPatternEmpty(a))
 		{
-			if (pattTmp[a] != NULL)
+			if (patternTmp[a] != NULL)
 			{
-				free(pattTmp[a]);
-				pattTmp[a] = NULL;
+				free(patternTmp[a]);
+				patternTmp[a] = NULL;
 			}
 		}
 	}
@@ -210,14 +214,14 @@
 	// pattern command conversion for non-PT formats
 	for (a = 0; a < b; a++)
 	{
-		if (pattTmp[a] == NULL)
+		if (patternTmp[a] == NULL)
 			continue;
 
 		for (j = 0; j < 64; j++)
 		{
-			for (k = 0; k < songTmp.antChn; k++)
+			for (k = 0; k < songTmp.numChannels; k++)
 			{
-				ton = &pattTmp[a][(j * MAX_VOICES) + k];
+				note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k];
 
 				// convert STK effects to PT effects
 
@@ -225,24 +229,24 @@
 				{
 					// old SoundTracker 1.x commands
 
-					if (ton->effTyp == 1)
+					if (p->efx == 1)
 					{
 						// arpeggio
-						ton->effTyp = 0;
+						p->efx = 0;
 					}
-					else if (ton->effTyp == 2)
+					else if (p->efx == 2)
 					{
 						// pitch slide
-						if (ton->eff & 0xF0)
+						if (p->efxData & 0xF0)
 						{
 							// pitch slide down
-							ton->effTyp = 2;
-							ton->eff >>= 4;
+							p->efx = 2;
+							p->efxData >>= 4;
 						}
-						else if (ton->eff & 0x0F)
+						else if (p->efxData & 0x0F)
 						{
 							// pitch slide up
-							ton->effTyp = 1;
+							p->efx = 1;
 						}
 					}
 				}
@@ -250,24 +254,24 @@
 				{
 					// "DFJ SoundTracker II" or later
 
-					if (ton->effTyp == 0xD)
+					if (p->efx == 0xD)
 					{
 						if (veryLateSTKVerFlag) // "DFJ SoundTracker III" or later
 						{
 							// pattern break w/ no param (param must be cleared to fix some songs)
-							ton->eff = 0;
+							p->efxData = 0;
 						}
 						else
 						{
 							// volume slide
-							ton->effTyp = 0xA;
+							p->efx = 0xA;
 						}
 					}
 				}
 
 				// effect F with param 0x00 does nothing in UST/STK (I think)
-				if (ton->effTyp == 0xF && ton->eff == 0)
-					ton->effTyp = 0;
+				if (p->efx == 0xF && p->efxData == 0)
+					p->efx = 0;
 			}
 		}
 	}
@@ -274,7 +278,7 @@
 
 	for (a = 0; a < 15; a++)
 	{
-		if (h_MOD15.instr[a].len == 0)
+		if (h.smp[a].length == 0)
 			continue;
 
 		if (!allocateTmpInstr(1+a))
@@ -285,64 +289,60 @@
 
 		setNoEnvelope(instrTmp[1+a]);
 
-		s = &instrTmp[1+a]->samp[0];
-		s->vol = h_MOD15.instr[a].vol;
+		s = &instrTmp[1+a]->smp[0];
+		s->volume = h.smp[a].volume;
+		s->length = 2 * SWAP16(h.smp[a].length);
+		s->loopStart = SWAP16(h.smp[a].loopStart); // in STK, loopStart = bytes, not words
+		s->loopLength = 2 * SWAP16(h.smp[a].loopLength);
 
-		s->len  = 2 * SWAP16(h_MOD15.instr[a].len);
-		s->repS =     SWAP16(h_MOD15.instr[a].repS); // in STK, loopStart = bytes, not words
-		s->repL = 2 * SWAP16(h_MOD15.instr[a].repL);
+		if (s->loopLength < 2)
+			s->loopLength = 2;
 
-		if (s->vol > 64)
-			s->vol = 64;
-
-		if (s->repL < 2)
-			s->repL = 2;
-
 		// fix overflown loop
-		if (s->repS+s->repL > s->len)
+		if (s->loopStart+s->loopLength > s->length)
 		{
-			if (s->repS >= s->len)
+			if (s->loopStart >= s->length)
 			{
-				s->repS = 0;
-				s->repL = 0;
+				s->loopStart = 0;
+				s->loopLength = 0;
 			}
 			else
 			{
-				s->repL = s->len - s->repS;
+				s->loopLength = s->length - s->loopStart;
 			}
 		}
 
-		if (s->repS+s->repL > 2)
+		if (s->loopStart+s->loopLength > 2)
 		{
-			s->typ = 1; // enable loop
+			s->flags |= LOOP_FWD; // enable loop
 		}
 		else
 		{
-			s->repL = 0;
-			s->repS = 0;
+			s->loopLength = 0;
+			s->loopStart = 0;
 		}
 
 		/* In STK, only the loop area of a looped sample is played.
 		** Skip loading of eventual data present before loop start.
 		*/
-		if (s->repS > 0 && s->repL < s->len)
+		if (s->loopStart > 0 && s->loopLength < s->length)
 		{
-			s->len -= s->repS;
-			fseek(f, s->repS, SEEK_CUR);
-			s->repS = 0;
+			s->length -= s->loopStart;
+			fseek(f, s->loopStart, SEEK_CUR);
+			s->loopStart = 0;
 		}
 
-		if (!allocateTmpSmpData(s, s->len))
+		if (!allocateSmpData(s, s->length, false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f);
-		if (bytesRead < s->len)
+		int32_t bytesRead = (int32_t)fread(s->dataPtr, 1, s->length, f);
+		if (bytesRead < s->length)
 		{
-			int32_t bytesToClear = s->len - bytesRead;
-			memset(&s->pek[bytesRead], 0, bytesToClear);
+			int32_t bytesToClear = s->length - bytesRead;
+			memset(&s->dataPtr[bytesRead], 0, bytesToClear);
 		}
 	}
 
--- a/src/modloaders/ft2_load_stm.c
+++ b/src/modloaders/ft2_load_stm.c
@@ -1,4 +1,8 @@
-// Scream Tracker 2 STM loader
+/* Scream Tracker 2 STM loader
+**
+** Note: Data sanitation is done in the last stage
+** of module loading, so you don't need to do that here.
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -13,98 +17,92 @@
 #pragma pack(push)
 #pragma pack(1)
 #endif
-typedef struct songSTMinstrHeaderTyp_t
+typedef struct stmSmpHdr_t
 {
 	char name[12];
-	uint8_t nul, insDisk;
-	uint16_t reserved1, len, repS, repE;
-	uint8_t vol, reserved2;
-	uint16_t rate;
-	int32_t reserved3;
+	uint8_t nul, junk1;
+	uint16_t junk2, length, loopStart, loopEnd;
+	uint8_t volume, junk3;
+	uint16_t midCFreq;
+	int32_t junk4;
 	uint16_t paraLen;
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songSTMinstrHeaderTyp;
+stmSmpHdr_t;
 
-typedef struct songSTMHeaderTyp_t
+typedef struct stmHdr_t
 {
 	char name[20], sig[8];
-	uint8_t id1a, typ;
+	uint8_t x1A, type;
 	uint8_t verMajor, verMinor;
-	uint8_t tempo, ap, vol, reserved[13];
-	songSTMinstrHeaderTyp instr[31];
-	uint8_t songTab[128];
+	uint8_t tempo, numPatterns, volume, reserved[13];
+	stmSmpHdr_t smp[31];
+	uint8_t orders[128];
 }
 #ifdef __GNUC__
 __attribute__ ((packed))
 #endif
-songSTMHeaderTyp;
+stmHdr_t;
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif
 
-static const uint8_t stmEff[16] = { 0, 0, 11, 0, 10, 2, 1, 3, 4, 7, 0, 5, 6, 0, 0, 0 };
+static const uint8_t stmEfx[16] = { 0, 0, 11, 0, 10, 2, 1, 3, 4, 7, 0, 5, 6, 0, 0, 0 };
+static uint8_t pattBuff[64*4*4];
 
-static uint8_t stmTempoToBPM(uint8_t tempo);
+static uint16_t stmTempoToBPM(uint8_t tempo);
 
 bool loadSTM(FILE *f, uint32_t filesize)
 {
-	uint8_t typ, tempo, pattBuff[1024];
-	int16_t i, j, k, ap, tmp;
-	uint16_t a;
-	tonTyp *ton;
-	sampleTyp *s;
-	songSTMHeaderTyp h_STM;
+	int16_t i, j, k;
+	stmHdr_t hdr;
 
 	tmpLinearPeriodsFlag = false; // use Amiga periods
 
-	if (filesize < sizeof (h_STM))
+	if (filesize < sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	if (fread(&h_STM, 1, sizeof (h_STM), f) != sizeof (h_STM))
+	if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))
 	{
 		loaderMsgBox("Error: This file is either not a module, or is not supported.");
 		return false;
 	}
 
-	if (h_STM.verMinor == 0 || h_STM.typ != 2)
+	if (hdr.verMinor == 0 || hdr.type != 2)
 	{
 		loaderMsgBox("Error loading STM: Incompatible module!");
 		return false;
 	}
 
-	songTmp.antChn = 4;
-	memcpy(songTmp.songTab, h_STM.songTab, 128);
+	songTmp.numChannels = 4;
+	memcpy(songTmp.orders, hdr.orders, 128);
 
 	i = 0;
-	while (i < 128 && songTmp.songTab[i] < 99) i++;
-	songTmp.len = i + (i == 0);
+	while (i < 128 && songTmp.orders[i] < 99)
+		i++;
+	songTmp.songLength = i + (i == 0);
 
-	if (songTmp.len < 255)
-		memset(&songTmp.songTab[songTmp.len], 0, 256 - songTmp.len);
+	if (songTmp.songLength < 255)
+		memset(&songTmp.orders[songTmp.songLength], 0, 256 - songTmp.songLength);
 
-	memcpy(songTmp.name, h_STM.name, 20);
+	memcpy(songTmp.name, hdr.name, 20);
 
-	tempo = h_STM.tempo;
-	if (h_STM.verMinor < 21)
+	uint8_t tempo = hdr.tempo;
+	if (hdr.verMinor < 21)
 		tempo = ((tempo / 10) << 4) + (tempo % 10);
 
 	if (tempo == 0)
 		tempo = 96;
 
-	songTmp.initialTempo = songTmp.tempo = CLAMP(h_STM.tempo >> 4, 1, 31);
-	songTmp.speed = stmTempoToBPM(tempo);
+	songTmp.BPM = stmTempoToBPM(tempo);
+	songTmp.speed = hdr.tempo >> 4;
 
-	if (h_STM.verMinor > 10)
-		songTmp.globVol = MIN(h_STM.vol, 64);
-
-	ap = h_STM.ap;
-	for (i = 0; i < ap; i++)
+	for (i = 0; i < hdr.numPatterns; i++)
 	{
 		if (!allocateTmpPatt(i, 64))
 		{
@@ -118,76 +116,75 @@
 			return false;
 		}
 
-		a = 0;
+		uint8_t *pattPtr = pattBuff;
 		for (j = 0; j < 64; j++)
 		{
-			for (k = 0; k < 4; k++)
+			for (k = 0; k < 4; k++, pattPtr += 4)
 			{
-				ton = &pattTmp[i][(j * MAX_VOICES) + k];
+				note_t *p = &patternTmp[i][(j * MAX_CHANNELS) + k];
 				
-				if (pattBuff[a] == 254)
+				if (pattPtr[0] == 254)
 				{
-					ton->ton = 97;
+					p->note = NOTE_OFF;
 				}
-				else if (pattBuff[a] < 96)
+				else if (pattPtr[0] < 96)
 				{
-					ton->ton = (12 * (pattBuff[a] >> 4)) + (25 + (pattBuff[a] & 0x0F));
-					if (ton->ton > 96)
-						ton->ton = 0;
+					p->note = (12 * (pattPtr[0] >> 4)) + (25 + (pattPtr[0] & 0x0F));
+					if (p->note > 96)
+						p->note = 0;
 				}
 				else
 				{
-					ton->ton = 0;
+					p->note = 0;
 				}
 
-				ton->instr = pattBuff[a + 1] >> 3;
-				typ = (pattBuff[a + 1] & 7) + ((pattBuff[a + 2] & 0xF0) >> 1);
-				if (typ <= 64)
-					ton->vol = typ + 0x10;
+				p->instr = pattPtr[1] >> 3;
 
-				ton->eff = pattBuff[a + 3];
+				uint8_t vol = (pattPtr[1] & 7) + ((pattPtr[2] & 0xF0) >> 1);
+				if (vol <= 64)
+					p->vol = 0x10 + vol;
 
-				tmp = pattBuff[a + 2] & 0x0F;
-				if (tmp == 1)
+				p->efxData = pattPtr[3];
+
+				uint8_t efx = pattPtr[2] & 0x0F;
+				if (efx == 1)
 				{
-					ton->effTyp = 15;
+					p->efx = 15;
 
-					if (h_STM.verMinor < 21)
-						ton->eff = ((ton->eff / 10) << 4) + (ton->eff % 10);
+					if (hdr.verMinor < 21)
+						p->efxData = ((p->efxData / 10) << 4) + (p->efxData % 10);
 					
-					ton->eff >>= 4;
+					p->efxData >>= 4;
 				}
-				else if (tmp == 3)
+				else if (efx == 3)
 				{
-					ton->effTyp = 13;
-					ton->eff = 0;
+					p->efx = 13;
+					p->efxData = 0;
 				}
-				else if (tmp == 2 || (tmp >= 4 && tmp <= 12))
+				else if (efx == 2 || (efx >= 4 && efx <= 12))
 				{
-					ton->effTyp = stmEff[tmp];
-					if (ton->effTyp == 0xA)
+					p->efx = stmEfx[efx];
+					if (p->efx == 0xA)
 					{
-						if (ton->eff & 0x0F)
-							ton->eff &= 0x0F;
+						if (p->efxData & 0x0F)
+							p->efxData &= 0x0F;
 						else
-							ton->eff &= 0xF0;
+							p->efxData &= 0xF0;
 					}
 				}
 				else
 				{
-					ton->eff = 0;
+					p->efxData = 0;
 				}
-
-				a += 4;
 			}
 		}
 
 		if (tmpPatternEmpty(i))
 		{
-			if (pattTmp[i] != NULL)
+			if (patternTmp[i] != NULL)
 			{
-				free(pattTmp[i]);
-				pattTmp[i] = NULL;
+				free(patternTmp[i]);
+				patternTmp[i] = NULL;
 			}
 		}
 	}
@@ -194,47 +191,43 @@
 
 	for (i = 0; i < 31; i++)
 	{
-		memcpy(&songTmp.instrName[1+i], h_STM.instr[i].name, 12);
+		memcpy(&songTmp.instrName[1+i], hdr.smp[i].name, 12);
 
-		if (h_STM.instr[i].len != 0 && h_STM.instr[i].reserved1 != 0)
+		if (hdr.smp[i].length > 0)
 		{
 			allocateTmpInstr(1 + i);
 			setNoEnvelope(instrTmp[i]);
 
-			s = &instrTmp[1+i]->samp[0];
+			sample_t *s = &instrTmp[1+i]->smp[0];
 
-			if (!allocateTmpSmpData(s, h_STM.instr[i].len))
+			if (!allocateSmpData(s, hdr.smp[i].length, false))
 			{
 				loaderMsgBox("Not enough memory!");
 				return false;
 			}
 
-			s->len = h_STM.instr[i].len;
-			s->vol = h_STM.instr[i].vol;
-			s->repS = h_STM.instr[i].repS;
-			s->repL = h_STM.instr[i].repE - h_STM.instr[i].repS;
+			s->length = hdr.smp[i].length;
+			s->volume = hdr.smp[i].volume;
+			s->loopStart = hdr.smp[i].loopStart;
+			s->loopLength = hdr.smp[i].loopEnd - hdr.smp[i].loopStart;
 
-			memcpy(s->name, h_STM.instr[i].name, 12);
-			tuneSample(s, h_STM.instr[i].rate, tmpLinearPeriodsFlag);
+			memcpy(s->name, hdr.smp[i].name, 12);
+			tuneSample(s, hdr.smp[i].midCFreq, tmpLinearPeriodsFlag);
 
-			if (s->repS < s->len && h_STM.instr[i].repE > s->repS && h_STM.instr[i].repE != 0xFFFF)
+			if (s->loopStart < s->length && hdr.smp[i].loopEnd > s->loopStart && hdr.smp[i].loopEnd != 0xFFFF)
 			{
-				if (s->repS+s->repL > s->len)
-					s->repL = s->len - s->repS;
+				if (s->loopStart+s->loopLength > s->length)
+					s->loopLength = s->length - s->loopStart;
 
-				s->typ = 1; // enable loop
+				s->flags |= LOOP_FWD; // enable loop
 			}
 			else
 			{
-				s->repS = 0;
-				s->repL = 0;
-				s->typ = 0;
+				s->loopStart = 0;
+				s->loopLength = 0;
 			}
 
-			if (s->vol > 64)
-				s->vol = 64;
-
-			if (fread(s->pek, s->len, 1, f) != 1)
+			if (fread(s->dataPtr, s->length, 1, f) != 1)
 			{
 				loaderMsgBox("General I/O error during loading! Possibly corrupt module?");
 				return false;
@@ -245,7 +238,7 @@
 	return true;
 }
 
-static uint8_t stmTempoToBPM(uint8_t tempo) // ported from original ST2.3 replayer code
+static uint16_t stmTempoToBPM(uint8_t tempo) // ported from original ST2.3 replayer code
 {
 	const uint8_t slowdowns[16] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 };
 	uint16_t hz = 50;
@@ -252,6 +245,6 @@
 
 	hz -= ((slowdowns[tempo >> 4] * (tempo & 15)) >> 4); // can and will underflow
 
-	const uint32_t bpm = (hz << 1) + (hz >> 1); // BPM = hz * 2.5
-	return (uint8_t)CLAMP(bpm, 32, 255); // result can be slightly off, but close enough...
+	const uint16_t bpm = (hz << 1) + (hz >> 1); // BPM = hz * 2.5
+	return bpm; // result can be slightly off, but close enough...
 }
--- a/src/modloaders/ft2_load_xm.c
+++ b/src/modloaders/ft2_load_xm.c
@@ -1,4 +1,8 @@
-// Fasttracker II (or compatible) XM loader
+/* Fasttracker II (or compatible) XM loader
+**
+** Note: Data sanitation is done in the last stage
+** of module loading, so you don't need to do that here.
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -20,14 +24,13 @@
 static bool loadInstrHeader(FILE *f, uint16_t i);
 static bool loadInstrSample(FILE *f, uint16_t i);
 static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn);
-static bool loadPatterns(FILE *f, uint16_t antPtn);
+static bool loadPatterns(FILE *f, uint16_t antPtn, uint16_t xmVersion);
 static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn);
-static void sanitizeInstrument(instrTyp *ins);
+static void loadADPCMSample(FILE *f, sample_t *s); // ModPlug Tracker
 
 bool loadXM(FILE *f, uint32_t filesize)
 {
-	uint16_t i;
-	songHeaderTyp h;
+	xmHdr_t h;
 
 	if (filesize < sizeof (h))
 	{
@@ -41,31 +44,31 @@
 		return false;
 	}
 
-	if (h.ver < 0x0102 || h.ver > 0x0104)
+	if (h.version < 0x0102 || h.version > 0x0104)
 	{
-		loaderMsgBox("Error loading XM: Unsupported file version (v%01X.%02X).", (h.ver >> 8) & 15, h.ver & 0xFF);
+		loaderMsgBox("Error loading XM: Unsupported file version (v%01X.%02X).", (h.version >> 8) & 15, h.version & 0xFF);
 		return false;
 	}
 
-	if (h.len > MAX_ORDERS)
+	if (h.numOrders > MAX_ORDERS)
 	{
 		loaderMsgBox("Error loading XM: The song has more than 256 orders!");
 		return false;
 	}
 
-	if (h.antPtn > MAX_PATTERNS)
+	if (h.numPatterns > MAX_PATTERNS)
 	{
 		loaderMsgBox("Error loading XM: The song has more than 256 patterns!");
 		return false;
 	}
 
-	if (h.antChn == 0)
+	if (h.numChannels == 0)
 	{
 		loaderMsgBox("Error loading XM: This file is corrupt.");
 		return false;
 	}
 
-	if (h.antInstrs > 256) // if >128 instruments, we fake-load up to 128 extra instruments and discard them
+	if (h.numInstr > 256) // if >128 instruments, we fake-load up to 128 extra instruments and discard them
 	{
 		loaderMsgBox("Error loading XM: This file is corrupt.");
 		return false;
@@ -79,52 +82,48 @@
 	}
 
 	memcpy(songTmp.name, h.name, 20);
+	songTmp.name[20] = '\0';
 
-	songTmp.len = h.len;
-	songTmp.repS = h.repS;
-	songTmp.antChn = (uint8_t)h.antChn;
-	songTmp.speed = h.defSpeed;
-	songTmp.tempo = h.defTempo;
-	songTmp.ver = h.ver;
+	songTmp.songLength = h.numOrders;
+	songTmp.songLoopStart = h.songLoopStart;
+	songTmp.numChannels = (uint8_t)h.numChannels;
+	songTmp.BPM = h.BPM;
+	songTmp.speed = h.speed;
 	tmpLinearPeriodsFlag = h.flags & 1;
 
-	// non-FT2: clamp to max numbers that are okay for GUI
-	songTmp.speed = CLAMP(songTmp.speed, 1, 999);
-	songTmp.initialTempo = songTmp.tempo = CLAMP(songTmp.tempo, 1, 99);
-
-	if (songTmp.len == 0)
-		songTmp.len = 1; // songTmp.songTab is already empty
+	if (songTmp.songLength == 0)
+		songTmp.songLength = 1; // songTmp.songTab is already empty
 	else
-		memcpy(songTmp.songTab, h.songTab, songTmp.len);
+		memcpy(songTmp.orders, h.orders, songTmp.songLength);
 
 	// some strange XMs have the order list padded with 0xFF, remove them!
 	for (int16_t j = 255; j >= 0; j--)
 	{
-		if (songTmp.songTab[j] != 0xFF)
+		if (songTmp.orders[j] != 0xFF)
 			break;
 
-		if (songTmp.len > j)
-			songTmp.len = j;
+		if (songTmp.songLength > j)
+			songTmp.songLength = j;
 	}
 
 	// even though XM supports 256 orders, FT2 supports only 255...
-	if (songTmp.len > 0xFF)
-		songTmp.len = 0xFF;
+	if (songTmp.songLength > 255)
+		songTmp.songLength = 255;
 
-	if (songTmp.ver < 0x0104)
+	if (h.version < 0x0104)
 	{
 		// XM v1.02 and XM v1.03
 
-		for (i = 1; i <= h.antInstrs; i++)
+		for (uint16_t i = 1; i <= h.numInstr; i++)
 		{
 			if (!loadInstrHeader(f, i))
 				return false;
 		}
 
-		if (!loadPatterns(f, h.antPtn))
+		if (!loadPatterns(f, h.numPatterns, h.version))
 			return false;
 
-		for (i = 1; i <= h.antInstrs; i++)
+		for (uint16_t i = 1; i <= h.numInstr; i++)
 		{
 			if (!loadInstrSample(f, i))
 				return false;
@@ -134,10 +133,10 @@
 	{
 		// XM v1.04 (latest version)
 
-		if (!loadPatterns(f, h.antPtn))
+		if (!loadPatterns(f, h.numPatterns, h.version))
 			return false;
 
-		for (i = 1; i <= h.antInstrs; i++)
+		for (uint16_t i = 1; i <= h.numInstr; i++)
 		{
 			if (!loadInstrHeader(f, i))
 				return false;
@@ -148,9 +147,9 @@
 	}
 
 	// if we temporarily loaded more than 128 instruments, clear the extra allocated memory
-	if (h.antInstrs > MAX_INST)
+	if (h.numInstr > MAX_INST)
 	{
-		for (i = MAX_INST+1; i <= h.antInstrs; i++)
+		for (int32_t i = MAX_INST+1; i <= h.numInstr; i++)
 		{
 			if (instrTmp[i] != NULL)
 			{
@@ -165,22 +164,22 @@
 	** back to max 16 in the headers before loading is done.
 	*/
 	bool instrHasMoreThan16Samples = false;
-	for (i = 1; i <= MAX_INST; i++)
+	for (int32_t i = 1; i <= MAX_INST; i++)
 	{
-		if (instrTmp[i] != NULL && instrTmp[i]->antSamp > MAX_SMP_PER_INST)
+		if (instrTmp[i] != NULL && instrTmp[i]->numSamples > MAX_SMP_PER_INST)
 		{
 			instrHasMoreThan16Samples = true;
-			instrTmp[i]->antSamp = MAX_SMP_PER_INST;
+			instrTmp[i]->numSamples = MAX_SMP_PER_INST;
 		}
 	}
 
-	if (songTmp.antChn > MAX_VOICES)
+	if (songTmp.numChannels > MAX_CHANNELS)
 	{
-		songTmp.antChn = MAX_VOICES;
+		songTmp.numChannels = MAX_CHANNELS;
 		loaderMsgBox("Warning: Module contains >32 channels. The extra channels will be discarded!");
 	}
 
-	if (h.antInstrs > MAX_INST)
+	if (h.numInstr > MAX_INST)
 		loaderMsgBox("Warning: Module contains >128 instruments. The extra instruments will be discarded!");
 
 	if (instrHasMoreThan16Samples)
@@ -191,37 +190,35 @@
 
 static bool loadInstrHeader(FILE *f, uint16_t i)
 {
-	uint8_t j;
 	uint32_t readSize;
-	instrHeaderTyp ih;
-	instrTyp *ins;
-	sampleHeaderTyp *src;
-	sampleTyp *s;
+	xmInsHdr_t ih;
+	instr_t *ins;
+	xmSmpHdr_t *src;
+	sample_t *s;
 
 	memset(extraSampleLengths, 0, sizeof (extraSampleLengths));
 	memset(&ih, 0, sizeof (ih));
-	fread(&ih.instrSize, 4, 1, f);
 
-	readSize = ih.instrSize;
+	fread(&readSize, 4, 1, f);
+	fseek(f, -4, SEEK_CUR);
 
 	// yes, some XMs can have a header size of 0, and it usually means 263 bytes (INSTR_HEADER_SIZE)
 	if (readSize == 0 || readSize > INSTR_HEADER_SIZE)
 		readSize = INSTR_HEADER_SIZE;
 
-	if (readSize < 4)
+	if ((int32_t)readSize < 0)
 	{
-		loaderMsgBox("Error loading XM: This file is corrupt (or not supported)!");
+		loaderMsgBox("Error loading XM: This file is corrupt!");
 		return false;
 	}
 
-	// load instrument data into temp buffer
-	fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize
+	fread(&ih, readSize, 1, f); // read instrument header
 
 	// FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE
 	if (ih.instrSize > INSTR_HEADER_SIZE)
 		fseek(f, ih.instrSize-INSTR_HEADER_SIZE, SEEK_CUR);
 
-	if (ih.antSamp < 0 || ih.antSamp > 32)
+	if (ih.numSamples < 0 || ih.numSamples > 32)
 	{
 		loaderMsgBox("Error loading XM: This file is corrupt (or not supported)!");
 		return false;
@@ -230,7 +227,7 @@
 	if (i <= MAX_INST) // copy over instrument names
 		memcpy(songTmp.instrName[i], ih.name, 22);
 
-	if (ih.antSamp > 0)
+	if (ih.numSamples > 0 && ih.numSamples <= 32)
 	{
 		if (!allocateTmpInstr(i))
 		{
@@ -241,38 +238,36 @@
 		// copy instrument header elements to our instrument struct
 
 		ins = instrTmp[i];
-		memcpy(ins->ta, ih.ta, 96);
-		memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
-		memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
-		ins->envVPAnt = ih.envVPAnt;
-		ins->envPPAnt = ih.envPPAnt;
-		ins->envVSust = ih.envVSust;
-		ins->envVRepS = ih.envVRepS;
-		ins->envVRepE = ih.envVRepE;
-		ins->envPSust = ih.envPSust;
-		ins->envPRepS = ih.envPRepS;
-		ins->envPRepE = ih.envPRepE;
-		ins->envVTyp = ih.envVTyp;
-		ins->envPTyp = ih.envPTyp;
-		ins->vibTyp = ih.vibTyp;
+		memcpy(ins->note2SampleLUT, ih.note2SampleLUT, 96);
+		memcpy(ins->volEnvPoints, ih.volEnvPoints, 12*2*sizeof(int16_t));
+		memcpy(ins->panEnvPoints, ih.panEnvPoints, 12*2*sizeof(int16_t));
+		ins->volEnvLength = ih.volEnvLength;
+		ins->panEnvLength = ih.panEnvLength;
+		ins->volEnvSustain = ih.volEnvSustain;
+		ins->volEnvLoopStart = ih.volEnvLoopStart;
+		ins->volEnvLoopEnd = ih.volEnvLoopEnd;
+		ins->panEnvSustain = ih.panEnvSustain;
+		ins->panEnvLoopStart = ih.panEnvLoopStart;
+		ins->panEnvLoopEnd = ih.panEnvLoopEnd;
+		ins->volEnvFlags = ih.volEnvFlags;
+		ins->panEnvFlags = ih.panEnvFlags;
+		ins->vibType = ih.vibType;
 		ins->vibSweep = ih.vibSweep;
 		ins->vibDepth = ih.vibDepth;
 		ins->vibRate = ih.vibRate;
-		ins->fadeOut = ih.fadeOut;
+		ins->fadeout = ih.fadeout;
 		ins->midiOn = (ih.midiOn == 1) ? true : false;
 		ins->midiChannel = ih.midiChannel;
 		ins->midiProgram = ih.midiProgram;
 		ins->midiBend = ih.midiBend;
 		ins->mute = (ih.mute == 1) ? true : false; // correct logic, don't change this!
-		ins->antSamp = ih.antSamp; // used in loadInstrSample()
+		ins->numSamples = ih.numSamples; // used in loadInstrSample()
 
-		sanitizeInstrument(ins);
-
-		int32_t sampleHeadersToRead = ih.antSamp;
+		int32_t sampleHeadersToRead = ih.numSamples;
 		if (sampleHeadersToRead > MAX_SMP_PER_INST)
 			sampleHeadersToRead = MAX_SMP_PER_INST;
 
-		if (fread(ih.samp, sampleHeadersToRead * sizeof (sampleHeaderTyp), 1, f) != 1)
+		if (fread(ih.smp, sampleHeadersToRead * sizeof (xmSmpHdr_t), 1, f) != 1)
 		{
 			loaderMsgBox("General I/O error during loading!");
 			return false;
@@ -279,40 +274,41 @@
 		}
 
 		// if instrument contains more than 16 sample headers (unsupported), skip them
-		if (ih.antSamp > MAX_SMP_PER_INST) // can only be 0..32 at this point
+		if (ih.numSamples > MAX_SMP_PER_INST) // can only be 0..32 at this point
 		{
-			const int32_t samplesToSkip = ih.antSamp-MAX_SMP_PER_INST;
-			for (j = 0; j < samplesToSkip; j++)
+			const int32_t samplesToSkip = ih.numSamples-MAX_SMP_PER_INST;
+			for (int32_t j = 0; j < samplesToSkip; j++)
 			{
 				fread(&extraSampleLengths[j], 4, 1, f); // used for skipping data in loadInstrSample()
-				fseek(f, sizeof (sampleHeaderTyp)-4, SEEK_CUR);
+				fseek(f, sizeof (xmSmpHdr_t)-4, SEEK_CUR);
 			}
 		}
 
-		for (j = 0; j < sampleHeadersToRead; j++)
+		for (int32_t j = 0; j < sampleHeadersToRead; j++)
 		{
-			s = &instrTmp[i]->samp[j];
-			src = &ih.samp[j];
+			s = &instrTmp[i]->smp[j];
+			src = &ih.smp[j];
 
 			// copy sample header elements to our sample struct
 
-			s->len = src->len;
-			s->repS = src->repS;
-			s->repL = src->repL;
-			s->vol = src->vol;
-			s->fine = src->fine;
-			s->typ = src->typ;
-			s->pan = src->pan;
-			s->relTon = src->relTon;
-			memcpy(s->name, src->name, 22);
+			s->length = src->length;
+			s->loopStart = src->loopStart;
+			s->loopLength = src->loopLength;
+			s->volume = src->volume;
+			s->finetune = src->finetune;
+			s->flags = src->flags;
+			s->panning = src->panning;
+			s->relativeNote = src->relativeNote;
 
-			// dst->pek is set up later
+			/* If the sample is 8-bit mono and nameLength (reserved) is 0xAD,
+			** then this is a 4-bit ADPCM compressed sample (ModPlug Tracker).
+			*/
+			if (src->nameLength == 0xAD && !(src->flags & (SAMPLE_16BIT | SAMPLE_STEREO)))
+				s->flags |= SAMPLE_ADPCM;
 
-			// sanitize stuff broken/unsupported samples (FT2 doesn't do this, but we do!)
-			if (s->vol > 64)
-				s->vol = 64;
+			memcpy(s->name, src->name, 22);
 
-			s->relTon = CLAMP(s->relTon, -48, 71);
+			// dst->dataPtr is set up later
 		}
 	}
 
@@ -324,18 +320,18 @@
 	if (instrTmp[i] == NULL)
 		return true; // empty instrument, let's just pretend it got loaded successfully
 
-	uint16_t k = instrTmp[i]->antSamp;
+	uint16_t k = instrTmp[i]->numSamples;
 	if (k > MAX_SMP_PER_INST)
 		k = MAX_SMP_PER_INST;
 
-	sampleTyp *s = instrTmp[i]->samp;
+	sample_t *s = instrTmp[i]->smp;
 
 	if (i > MAX_INST) // insNum > 128, just skip sample data
 	{
 		for (uint16_t j = 0; j < k; j++, s++)
 		{
-			if (s->len > 0)
-				fseek(f, s->len, SEEK_CUR);
+			if (s->length > 0)
+				fseek(f, s->length, SEEK_CUR);
 		}
 	}
 	else
@@ -342,72 +338,72 @@
 	{
 		for (uint16_t j = 0; j < k; j++, s++)
 		{
-			// FT2: a sample with both forward loop and pingpong loop set results in pingpong
-			if ((s->typ & 3) == 3)
-				s->typ &= 0xFE; // remove forward loop flag
-
-			int32_t l = s->len;
-			if (l <= 0)
+			if (s->length <= 0)
 			{
-				s->len = 0;
-				s->repL = 0;
-				s->repS = 0;
-
-				if (s->typ & 32) // remove stereo flag if present
-					s->typ &= ~32;
+				s->length = 0;
+				s->loopStart = 0;
+				s->loopLength = 0;
+				s->flags = 0;
 			}
 			else
 			{
-				int32_t bytesToSkip = 0;
-				if (l > MAX_SAMPLE_LEN)
+				const int32_t lengthInFile = s->length;
+
+				bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+				bool stereoSample = !!(s->flags & SAMPLE_STEREO);
+				bool adpcmSample = !!(s->flags & SAMPLE_ADPCM); // ModPlug Tracker
+
+				if (sample16Bit) // we use units of samples (not bytes like in FT2)
 				{
-					bytesToSkip = l - MAX_SAMPLE_LEN;
-					l = MAX_SAMPLE_LEN;
+					s->length >>= 1;
+					s->loopStart >>= 1;
+					s->loopLength >>= 1;
 				}
 
-				if (!allocateTmpSmpData(s, l))
+				if (s->length > MAX_SAMPLE_LEN)
+					s->length = MAX_SAMPLE_LEN;
+
+				if (!allocateSmpData(s, s->length, sample16Bit))
 				{
 					loaderMsgBox("Not enough memory!");
 					return false;
 				}
 
-				const int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f);
-				if (bytesRead < l)
+				if (adpcmSample)
 				{
-					const int32_t bytesToClear = l - bytesRead;
-					memset(&s->pek[bytesRead], 0, bytesToClear);
+					loadADPCMSample(f, s);
 				}
+				else
+				{
+					const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s);
+					fread(s->dataPtr, 1, sampleLengthInBytes, f);
 
-				if (bytesToSkip > 0)
-					fseek(f, bytesToSkip, SEEK_CUR);
+					if (sampleLengthInBytes < lengthInFile)
+						fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR);
 
-				delta2Samp(s->pek, l, s->typ);
+					delta2Samp(s->dataPtr, s->length, s->flags);
 
-				if (s->typ & 32) // stereo sample - already downmixed to mono in delta2samp()
-				{
-					s->typ &= ~32; // remove stereo flag
+					if (stereoSample) // stereo sample - already downmixed to mono in delta2samp()
+					{
+						s->length >>= 1;
+						s->loopStart >>= 1;
+						s->loopLength >>= 1;
 
-					s->len >>= 1;
-					s->repL >>= 1;
-					s->repS >>= 1;
-
-					reallocateTmpSmpData(s, s->len); // dealloc unused memory
+						reallocateSmpData(s, s->length, sample16Bit); // dealloc unused memory
+					}
 				}
 			}
 
-			// NON-FT2 FIX: Align to 2-byte if 16-bit sample
-			if (s->typ & 16)
-			{
-				s->repL &= 0xFFFFFFFE;
-				s->repS &= 0xFFFFFFFE;
-				s->len &= 0xFFFFFFFE;
-			}
+			// remove stereo flag if present (already handled)
+			if (s->flags & SAMPLE_STEREO)
+				s->flags &= ~SAMPLE_STEREO;
 		}
 	}
 
-	if (instrTmp[i]->antSamp > MAX_SMP_PER_INST)
+	// skip sample headers if we have more than 16 samples in instrument
+	if (instrTmp[i]->numSamples > MAX_SMP_PER_INST)
 	{
-		const int32_t samplesToSkip = instrTmp[i]->antSamp-MAX_SMP_PER_INST;
+		const int32_t samplesToSkip = instrTmp[i]->numSamples-MAX_SMP_PER_INST;
 		for (i = 0; i < samplesToSkip; i++)
 		{
 			if (extraSampleLengths[i] > 0)
@@ -418,80 +414,80 @@
 	return true;
 }
 
-static bool loadPatterns(FILE *f, uint16_t antPtn)
+static bool loadPatterns(FILE *f, uint16_t antPtn, uint16_t xmVersion)
 {
 	uint8_t tmpLen;
-	patternHeaderTyp ph;
+	xmPatHdr_t ph;
 
 	bool pattLenWarn = false;
 	for (uint16_t i = 0; i < antPtn; i++)
 	{
-		if (fread(&ph.patternHeaderSize, 4, 1, f) != 1)
+		if (fread(&ph.headerSize, 4, 1, f) != 1)
 			goto pattCorrupt;
 
-		if (fread(&ph.typ, 1, 1, f) != 1)
+		if (fread(&ph.type, 1, 1, f) != 1)
 			goto pattCorrupt;
 
-		ph.pattLen = 0;
-		if (songTmp.ver == 0x0102)
+		ph.numRows = 0;
+		if (xmVersion == 0x0102)
 		{
 			if (fread(&tmpLen, 1, 1, f) != 1)
 				goto pattCorrupt;
 
-			if (fread(&ph.dataLen, 2, 1, f) != 1)
+			if (fread(&ph.dataSize, 2, 1, f) != 1)
 				goto pattCorrupt;
 
-			ph.pattLen = tmpLen + 1; // +1 in v1.02
+			ph.numRows = tmpLen + 1; // +1 in v1.02
 
-			if (ph.patternHeaderSize > 8)
-				fseek(f, ph.patternHeaderSize - 8, SEEK_CUR);
+			if (ph.headerSize > 8)
+				fseek(f, ph.headerSize - 8, SEEK_CUR);
 		}
 		else
 		{
-			if (fread(&ph.pattLen, 2, 1, f) != 1)
+			if (fread(&ph.numRows, 2, 1, f) != 1)
 				goto pattCorrupt;
 
-			if (fread(&ph.dataLen, 2, 1, f) != 1)
+			if (fread(&ph.dataSize, 2, 1, f) != 1)
 				goto pattCorrupt;
 
-			if (ph.patternHeaderSize > 9)
-				fseek(f, ph.patternHeaderSize - 9, SEEK_CUR);
+			if (ph.headerSize > 9)
+				fseek(f, ph.headerSize - 9, SEEK_CUR);
 		}
 
 		if (feof(f))
 			goto pattCorrupt;
 
-		pattLensTmp[i] = ph.pattLen;
-		if (pattLensTmp[i] > MAX_PATT_LEN)
+		patternNumRowsTmp[i] = ph.numRows;
+		if (patternNumRowsTmp[i] > MAX_PATT_LEN)
 		{
-			pattLensTmp[i] = MAX_PATT_LEN;
+			patternNumRowsTmp[i] = MAX_PATT_LEN;
 			pattLenWarn = true;
 		}
 
-		if (ph.dataLen > 0)
+		if (ph.dataSize > 0)
 		{
-			if (!allocateTmpPatt(i, pattLensTmp[i]))
+			if (!allocateTmpPatt(i, patternNumRowsTmp[i]))
 			{
 				loaderMsgBox("Not enough memory!");
 				return false;
 			}
 
-			if (fread(packedPattData, 1, ph.dataLen, f) != ph.dataLen)
+			if (fread(packedPattData, 1, ph.dataSize, f) != ph.dataSize)
 				goto pattCorrupt;
 
-			unpackPatt((uint8_t *)pattTmp[i], packedPattData, pattLensTmp[i], songTmp.antChn);
-			clearUnusedChannels(pattTmp[i], pattLensTmp[i], songTmp.antChn);
+			unpackPatt((uint8_t *)patternTmp[i], packedPattData, patternNumRowsTmp[i], songTmp.numChannels);
+			clearUnusedChannels(patternTmp[i], patternNumRowsTmp[i], songTmp.numChannels);
 		}
 
 		if (tmpPatternEmpty(i))
 		{
-			if (pattTmp[i] != NULL)
+			if (patternTmp[i] != NULL)
 			{
-				free(pattTmp[i]);
-				pattTmp[i] = NULL;
+				free(patternTmp[i]);
+				patternTmp[i] = NULL;
 			}
 
-			pattLensTmp[i] = 64;
+			patternNumRowsTmp[i] = 64;
 		}
 	}
 
@@ -512,14 +508,14 @@
 	if (dst == NULL)
 		return;
 
-	const int32_t srcEnd = len * (sizeof (tonTyp) * antChn);
+	const int32_t srcEnd = len * (sizeof (note_t) * antChn);
 	int32_t srcIdx = 0;
 
 	int32_t numChannels = antChn;
-	if (numChannels > MAX_VOICES)
-		numChannels = MAX_VOICES;
+	if (numChannels > MAX_CHANNELS)
+		numChannels = MAX_CHANNELS;
 
-	const int32_t pitch = sizeof (tonTyp) * (MAX_VOICES - antChn);
+	const int32_t pitch = sizeof (note_t) * (MAX_CHANNELS - antChn);
 	for (int32_t i = 0; i < len; i++)
 	{
 		for (j = 0; j < numChannels; j++)
@@ -545,7 +541,7 @@
 				*dst++ = *src++;
 			}
 
-			srcIdx += sizeof (tonTyp);
+			srcIdx += sizeof (note_t);
 		}
 
 		// if more than 32 channels, skip rest of the channels for this row
@@ -571,45 +567,32 @@
 				src++;
 			}
 
-			srcIdx += sizeof (tonTyp);
+			srcIdx += sizeof (note_t);
 		}
 
 		// if song has <32 channels, align pointer to next row (skip unused channels)
-		if (antChn < MAX_VOICES)
+		if (antChn < MAX_CHANNELS)
 			dst += pitch;
 	}
 }
 
-static void sanitizeInstrument(instrTyp *ins) // FT2 doesn't do this, but we do!
+static void loadADPCMSample(FILE *f, sample_t *s) // ModPlug Tracker
 {
-	ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
-	ins->midiBend = CLAMP(ins->midiBend, 0, 36);
+	int8_t deltaLUT[16];
+	fread(deltaLUT, 1, 16, f);
 
-	if (ins->midiChannel > 15) ins->midiChannel = 15;
-	if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
-	if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
-	if (ins->vibTyp > 3) ins->vibTyp = 0;
+	int8_t *dataPtr = s->dataPtr;
+	const int32_t dataLength = (s->length + 1) / 2;
 
-	for (int32_t i = 0; i < 96; i++)
+	int8_t currSample = 0;
+	for (int32_t i = 0; i < dataLength; i++)
 	{
-		if (ins->ta[i] >= MAX_SMP_PER_INST)
-			ins->ta[i] = MAX_SMP_PER_INST-1;
-	}
+		const uint8_t nibbles = (uint8_t)fgetc(f);
 
-	if (ins->envVPAnt > 12) ins->envVPAnt = 12;
-	if (ins->envVRepS > 11) ins->envVRepS = 11;
-	if (ins->envVRepE > 11) ins->envVRepE = 11;
-	if (ins->envVSust > 11) ins->envVSust = 11;
-	if (ins->envPPAnt > 12) ins->envPPAnt = 12;
-	if (ins->envPRepS > 11) ins->envPRepS = 11;
-	if (ins->envPRepE > 11) ins->envPRepE = 11;
-	if (ins->envPSust > 11) ins->envPSust = 11;
+		currSample += deltaLUT[nibbles & 0x0F];
+		*dataPtr++ = currSample;
 
-	for (int32_t  i= 0; i < 12; i++)
-	{
-		if ((uint16_t)ins->envVP[i][0] > 32767) ins->envVP[i][0] = 32767;
-		if ((uint16_t)ins->envPP[i][0] > 32767) ins->envPP[i][0] = 32767;
-		if ((uint16_t)ins->envVP[i][1] > 64) ins->envVP[i][1] = 64;
-		if ((uint16_t)ins->envPP[i][1] > 63) ins->envPP[i][1] = 63;
+		currSample += deltaLUT[nibbles >> 4];
+		*dataPtr++ = currSample;
 	}
 }
--- a/src/rtmidi/rtmidi_c.cpp
+++ b/src/rtmidi/rtmidi_c.cpp
@@ -3,6 +3,11 @@
 #include "rtmidi_c.h"
 #include "RtMidi.h"
 
+// 8bb: hide POSIX warnings
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+
 /* Compile-time assertions that will break if the enums are changed in
  * the future without synchronizing them properly.  If you get (g++)
  * "error: ‘StaticAssert<b>::StaticAssert() [with bool b = false]’ is
@@ -128,7 +133,7 @@
     } catch (const RtMidiError & err) {
         device->ok  = false;
         device->msg = err.what ();
-        return -1;
+        return (unsigned int)-1;
     }
 }
 
--- /dev/null
+++ b/src/scopes/ft2_scope_macros.h
@@ -1,0 +1,256 @@
+#pragma once
+
+#include <stdint.h>
+#include "ft2_scopes.h"
+
+/* ----------------------------------------------------------------------- */
+/*                          SCOPE DRAWING MACROS                           */
+/* ----------------------------------------------------------------------- */
+
+#define SCOPE_REGS_NO_LOOP \
+	const int32_t volume = s->volume; \
+	const int32_t sampleEnd = s->sampleEnd; \
+	const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \
+	const uint32_t color = video.palette[PAL_PATTEXT]; \
+	const uint32_t width = x + w; \
+	int32_t sample; \
+	int32_t position = s->position; \
+	uint32_t positionFrac = 0;
+
+#define SCOPE_REGS_LOOP \
+	const int32_t volume = s->volume; \
+	const int32_t sampleEnd = s->sampleEnd; \
+	const int32_t loopStart = s->loopStart; \
+	const int32_t loopLength = s->loopLength; \
+	const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \
+	const uint32_t color = video.palette[PAL_PATTEXT]; \
+	const uint32_t width = x + w; \
+	int32_t sample; \
+	int32_t position = s->position; \
+	uint32_t positionFrac = 0;
+
+#define SCOPE_REGS_PINGPONG \
+	const int32_t volume = s->volume; \
+	const int32_t sampleEnd = s->sampleEnd; \
+	const int32_t loopStart = s->loopStart; \
+	const int32_t loopLength = s->loopLength; \
+	const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \
+	const uint32_t color = video.palette[PAL_PATTEXT]; \
+	const uint32_t width = x + w; \
+	int32_t sample; \
+	int32_t position = s->position; \
+	uint32_t positionFrac = 0; \
+	int32_t direction = s->direction;
+
+#define LINED_SCOPE_REGS_NO_LOOP \
+	const int32_t volume = s->volume; \
+	const int32_t sampleEnd = s->sampleEnd; \
+	const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \
+	const uint32_t width = (x + w) - 1; \
+	int32_t sample, sample2; \
+	int32_t y1, y2; \
+	int32_t position = s->position; \
+	uint32_t positionFrac = 0;
+
+#define LINED_SCOPE_REGS_LOOP \
+	const int32_t volume = s->volume; \
+	const int32_t sampleEnd = s->sampleEnd; \
+	const int32_t loopStart = s->loopStart; \
+	const int32_t loopLength = s->loopLength; \
+	const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \
+	const uint32_t width = (x + w) - 1; \
+	int32_t sample, sample2; \
+	int32_t y1, y2; \
+	int32_t position = s->position; \
+	uint32_t positionFrac = 0;
+
+#define LINED_SCOPE_REGS_PINGPONG \
+	const int32_t volume = s->volume; \
+	const int32_t sampleEnd = s->sampleEnd; \
+	const int32_t loopStart = s->loopStart; \
+	const int32_t loopLength = s->loopLength; \
+	const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \
+	const uint32_t width = (x + w) - 1; \
+	int32_t sample, sample2; \
+	int32_t y1, y2; \
+	int32_t position = s->position; \
+	uint32_t positionFrac = 0; \
+	int32_t direction = s->direction;
+
+/* Note: Sample data already has a fixed tap samples at the end of the sample,
+** so that an out-of-bounds read is OK and reads the correct interpolation tap.
+*/
+
+#define COLLECT_LERP_SAMPLES8 \
+		sample = s->base8[position+0] << 8; \
+		sample2 = s->base8[position+1] << 8;
+
+#define COLLECT_LERP_SAMPLES16 \
+		sample = s->base16[position+0]; \
+		sample2 = s->base16[position+1];
+
+#define DO_LERP \
+		const int32_t frac = (uint32_t)positionFrac >> 1; /* 0..32767 */ \
+		sample2 -= sample; \
+		sample2 = (sample2 * frac) >> 15; \
+		sample += sample2; \
+
+#define DO_LERP_BIDI \
+		int32_t frac = (uint32_t)positionFrac >> 1; /* 0..32767 */ \
+		if (direction == -1) frac ^= 32767; /* negate frac */ \
+		sample2 -= sample; \
+		sample2 = (sample2 * frac) >> 15; \
+		sample += sample2;
+
+#define GET_LERP_SMP8 \
+		COLLECT_LERP_SAMPLES8 \
+		DO_LERP
+
+#define GET_LERP_SMP16 \
+		COLLECT_LERP_SAMPLES16 \
+		DO_LERP
+
+#define GET_LERP_SMP8_BIDI \
+		COLLECT_LERP_SAMPLES8 \
+		DO_LERP_BIDI
+
+#define GET_LERP_SMP16_BIDI  \
+		COLLECT_LERP_SAMPLES16 \
+		DO_LERP_BIDI
+
+#define SCOPE_GET_SMP8 \
+	if (s->active) \
+		sample = (s->base8[position] * volume) >> 8; \
+	else \
+		sample = 0;
+
+#define SCOPE_GET_SMP16 \
+	if (s->active) \
+		sample = (s->base16[position] * volume) >> 16; \
+	else \
+		sample = 0;
+
+#define SCOPE_GET_LERP_SMP8 \
+	if (s->active) \
+	{ \
+		GET_LERP_SMP8 \
+		sample = (sample * volume) >> 16; \
+	} \
+	else \
+	{ \
+		sample = 0; \
+	}
+
+#define SCOPE_GET_LERP_SMP16 \
+	if (s->active) \
+	{ \
+		GET_LERP_SMP16 \
+		sample = (sample * volume) >> 16; \
+	} \
+	else \
+	{ \
+		sample = 0; \
+	}
+
+#define SCOPE_GET_LERP_SMP8_BIDI \
+	if (s->active) \
+	{ \
+		GET_LERP_SMP8_BIDI \
+		sample = (sample * volume) >> 16; \
+	} \
+	else \
+	{ \
+		sample = 0; \
+	}
+
+#define SCOPE_GET_LERP_SMP16_BIDI \
+	if (s->active) \
+	{ \
+		GET_LERP_SMP16_BIDI \
+		sample = (sample * volume) >> 16; \
+	} \
+	else \
+	{ \
+		sample = 0; \
+	}
+
+#define SCOPE_UPDATE_DRAWPOS \
+	positionFrac += delta; \
+	position += positionFrac >> 16; \
+	positionFrac &= 0xFFFF;
+
+#define SCOPE_UPDATE_DRAWPOS_PINGPONG \
+	positionFrac += delta; \
+	position += (int32_t)(positionFrac >> 16) * direction; \
+	positionFrac &= 0xFFFF;
+
+#define SCOPE_DRAW_SMP \
+	video.frameBuffer[((lineY - sample) * SCREEN_W) + x] = color;
+
+#define LINED_SCOPE_PREPARE_SMP8 \
+	SCOPE_GET_SMP8 \
+	y1 = lineY - sample; \
+	SCOPE_UPDATE_DRAWPOS
+
+#define LINED_SCOPE_PREPARE_SMP16 \
+	SCOPE_GET_SMP16 \
+	y1 = lineY - sample; \
+	SCOPE_UPDATE_DRAWPOS
+
+#define LINED_SCOPE_PREPARE_LERP_SMP8 \
+	SCOPE_GET_LERP_SMP8 \
+	y1 = lineY - sample; \
+	SCOPE_UPDATE_DRAWPOS
+
+#define LINED_SCOPE_PREPARE_LERP_SMP16 \
+	SCOPE_GET_LERP_SMP16 \
+	y1 = lineY - sample; \
+	SCOPE_UPDATE_DRAWPOS
+
+#define LINED_SCOPE_PREPARE_LERP_SMP8_BIDI \
+	SCOPE_GET_LERP_SMP8_BIDI \
+	y1 = lineY - sample; \
+	SCOPE_UPDATE_DRAWPOS
+
+#define LINED_SCOPE_PREPARE_LERP_SMP16_BIDI \
+	SCOPE_GET_LERP_SMP16_BIDI \
+	y1 = lineY - sample; \
+	SCOPE_UPDATE_DRAWPOS
+
+#define LINED_SCOPE_DRAW_SMP \
+	y2 = lineY - sample; \
+	scopeLine(x, y1, y2); \
+	y1 = y2; \
+
+#define SCOPE_HANDLE_POS_NO_LOOP \
+	if (position >= sampleEnd) \
+		s->active = false;
+
+#define SCOPE_HANDLE_POS_LOOP \
+	if (position >= sampleEnd) \
+	{ \
+		if (loopLength >= 2) \
+			position = loopStart + ((position - sampleEnd) % loopLength); \
+		else \
+			position = loopStart; \
+	}
+
+#define SCOPE_HANDLE_POS_PINGPONG \
+	if (direction == -1 && position < loopStart) \
+	{ \
+		direction = 1; /* change direction to forwards */ \
+		\
+		if (loopLength >= 2) \
+			position = loopStart + ((loopStart - position - 1) % loopLength); \
+		else \
+			position = loopStart; \
+	} \
+	else if (position >= sampleEnd) \
+	{ \
+		direction = -1; /* change direction to backwards */ \
+		\
+		if (loopLength >= 2) \
+			position = (sampleEnd - 1) - ((position - sampleEnd) % loopLength); \
+		else \
+			position = sampleEnd - 1; \
+	}
--- /dev/null
+++ b/src/scopes/ft2_scopedraw.c
@@ -1,0 +1,244 @@
+#include "../ft2_video.h"
+#include "../ft2_palette.h"
+#include "ft2_scopes.h"
+#include "ft2_scopedraw.h"
+#include "ft2_scope_macros.h"
+#include "../ft2_cpu.h"
+
+static void scopeLine(int32_t x1, int32_t y1, int32_t y2);
+
+/* ----------------------------------------------------------------------- */
+/*                         SCOPE DRAWING ROUTINES                          */
+/* ----------------------------------------------------------------------- */
+
+static void scopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	SCOPE_REGS_NO_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_SMP8
+		SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_NO_LOOP
+	}
+}
+
+static void scopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	SCOPE_REGS_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_SMP8
+		SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_LOOP
+	}
+}
+
+static void scopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	SCOPE_REGS_PINGPONG
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_SMP8
+		SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS_PINGPONG
+		SCOPE_HANDLE_POS_PINGPONG
+	}
+}
+
+static void scopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	SCOPE_REGS_NO_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_SMP16
+		SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_NO_LOOP
+	}
+}
+
+static void scopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	SCOPE_REGS_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_SMP16
+		SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_LOOP
+	}
+}
+
+static void scopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	SCOPE_REGS_PINGPONG
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_SMP16
+		SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS_PINGPONG
+		SCOPE_HANDLE_POS_PINGPONG
+	}
+}
+
+/* ----------------------------------------------------------------------- */
+/*                   INTERPOLATED SCOPE DRAWING ROUTINES                   */
+/* ----------------------------------------------------------------------- */
+
+static void linedScopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	LINED_SCOPE_REGS_NO_LOOP
+	LINED_SCOPE_PREPARE_LERP_SMP8
+	SCOPE_HANDLE_POS_NO_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_LERP_SMP8
+		LINED_SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_NO_LOOP
+	}
+}
+
+static void linedScopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	LINED_SCOPE_REGS_LOOP
+	LINED_SCOPE_PREPARE_LERP_SMP8
+	SCOPE_HANDLE_POS_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_LERP_SMP8
+		LINED_SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_LOOP
+	}
+}
+
+static void linedScopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	LINED_SCOPE_REGS_PINGPONG
+	LINED_SCOPE_PREPARE_LERP_SMP8_BIDI
+	SCOPE_HANDLE_POS_PINGPONG
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_LERP_SMP8_BIDI
+		LINED_SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS_PINGPONG
+		SCOPE_HANDLE_POS_PINGPONG
+	}
+}
+
+static void linedScopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	LINED_SCOPE_REGS_NO_LOOP
+	LINED_SCOPE_PREPARE_LERP_SMP16
+	SCOPE_HANDLE_POS_NO_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_LERP_SMP16
+		LINED_SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_NO_LOOP
+	}
+}
+
+static void linedScopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	LINED_SCOPE_REGS_LOOP
+	LINED_SCOPE_PREPARE_LERP_SMP16
+	SCOPE_HANDLE_POS_LOOP
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_LERP_SMP16
+		LINED_SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS
+		SCOPE_HANDLE_POS_LOOP
+	}
+}
+
+static void linedScopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w)
+{
+	LINED_SCOPE_REGS_PINGPONG
+	LINED_SCOPE_PREPARE_LERP_SMP16_BIDI
+	SCOPE_HANDLE_POS_PINGPONG
+
+	for (; x < width; x++)
+	{
+		SCOPE_GET_LERP_SMP16_BIDI
+		LINED_SCOPE_DRAW_SMP
+		SCOPE_UPDATE_DRAWPOS_PINGPONG
+		SCOPE_HANDLE_POS_PINGPONG
+	}
+}
+
+// -----------------------------------------------------------------------
+
+static void scopeLine(int32_t x1, int32_t y1, int32_t y2)
+{
+	const int32_t dy = y2 - y1;
+	const int32_t sy = SGN(dy);
+	const uint32_t color = video.palette[PAL_PATTEXT];
+	const int32_t pitch = sy * SCREEN_W;
+
+	uint32_t *dst32 = &video.frameBuffer[(y1 * SCREEN_W) + x1];
+
+	*dst32 = color; // set first pixel
+
+	int32_t ay = ABS(dy);
+	if (ay <= 1)
+	{
+		if (ay != 0)
+			dst32 += pitch;
+
+		*++dst32 = color;
+		return;
+	}
+
+	int32_t d = 1 - ay;
+
+	ay <<= 1;
+	while (y1 != y2)
+	{
+		if (d >= 0)
+		{
+			d -= ay;
+			dst32++;
+		}
+
+		y1 += sy;
+		d += 2;
+
+		dst32 += pitch;
+		*dst32 = color;
+	}
+}
+
+// -----------------------------------------------------------------------
+
+const scopeDrawRoutine scopeDrawRoutineTable[12] =
+{
+	(scopeDrawRoutine)scopeDrawNoLoop_8bit,
+	(scopeDrawRoutine)scopeDrawLoop_8bit,
+	(scopeDrawRoutine)scopeDrawPingPong_8bit,
+	(scopeDrawRoutine)scopeDrawNoLoop_16bit,
+	(scopeDrawRoutine)scopeDrawLoop_16bit,
+	(scopeDrawRoutine)scopeDrawPingPong_16bit,
+	(scopeDrawRoutine)linedScopeDrawNoLoop_8bit,
+	(scopeDrawRoutine)linedScopeDrawLoop_8bit,
+	(scopeDrawRoutine)linedScopeDrawPingPong_8bit,
+	(scopeDrawRoutine)linedScopeDrawNoLoop_16bit,
+	(scopeDrawRoutine)linedScopeDrawLoop_16bit,
+	(scopeDrawRoutine)linedScopeDrawPingPong_16bit
+};
--- /dev/null
+++ b/src/scopes/ft2_scopedraw.h
@@ -1,0 +1,8 @@
+#pragma once
+
+#include <stdint.h>
+#include "ft2_scopes.h"
+
+typedef void (*scopeDrawRoutine)(const scope_t *, uint32_t, uint32_t, uint32_t);
+
+extern const scopeDrawRoutine scopeDrawRoutineTable[12]; // ft2_scopedraw.c
--- /dev/null
+++ b/src/scopes/ft2_scopes.c
@@ -1,0 +1,587 @@
+// for finding memory leaks in debug mode with Visual Studio
+#if defined _DEBUG && defined _MSC_VER
+#include <crtdbg.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <math.h> // modf()
+#ifndef _WIN32
+#include <unistd.h> // usleep()
+#endif
+#include "../ft2_header.h"
+#include "../ft2_events.h"
+#include "../ft2_config.h"
+#include "../ft2_audio.h"
+#include "../ft2_gui.h"
+#include "../ft2_midi.h"
+#include "../ft2_bmp.h"
+#include "../ft2_mouse.h"
+#include "../ft2_video.h"
+#include "../ft2_tables.h"
+#include "../ft2_structs.h"
+#include "ft2_scopes.h"
+#include "ft2_scopedraw.h"
+
+static volatile bool scopesUpdatingFlag, scopesDisplayingFlag;
+static uint32_t scopeTimeLen, scopeTimeLenFrac;
+static uint64_t timeNext64, timeNext64Frac;
+static volatile scope_t scope[MAX_CHANNELS];
+static SDL_Thread *scopeThread;
+
+lastChInstr_t lastChInstr[MAX_CHANNELS]; // global
+
+int32_t getSamplePosition(uint8_t ch)
+{
+	if (ch >= song.numChannels)
+		return -1;
+
+	volatile scope_t *sc = &scope[ch];
+
+	// cache some stuff
+	volatile bool active = sc->active;
+	volatile int32_t position = sc->position;
+	volatile int32_t sampleEnd = sc->sampleEnd;
+
+	if (!active || sampleEnd == 0)
+		return -1;
+
+	if (position >= 0 && position < sampleEnd)
+		return position;
+
+	return -1; // not active or overflown
+}
+
+void stopAllScopes(void)
+{
+	// wait for scopes to finish updating
+	while (scopesUpdatingFlag);
+	
+	volatile scope_t *sc = scope;
+	for (int32_t i = 0; i < MAX_CHANNELS; i++, sc++)
+		sc->active = false;
+
+	// wait for scope displaying to be done (safety)
+	while (scopesDisplayingFlag);
+}
+
+// toggle mute
+static void setChannel(int32_t chNr, bool on)
+{
+	channel_t *ch = &channel[chNr];
+
+	ch->channelOff = !on;
+	if (ch->channelOff)
+	{
+		ch->efx = 0;
+		ch->efxData = 0;
+		ch->realVol = 0;
+		ch->outVol = 0;
+		ch->oldVol = 0;
+		ch->fFinalVol = 0.0f;
+		ch->outPan = 128;
+		ch->oldPan = 128;
+		ch->finalPan = 128;
+		ch->status = IS_Vol;
+
+		ch->envSustainActive = false; // non-FT2 bug fix for stuck piano keys
+	}
+
+	scope[chNr].wasCleared = false;
+}
+
+static void drawScopeNumber(uint16_t scopeXOffs, uint16_t scopeYOffs, uint8_t chNr, bool outline)
+{
+	scopeXOffs++;
+	scopeYOffs++;
+	chNr++;
+
+	if (outline)
+	{
+		if (chNr < 10) // one digit?
+		{
+			charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + chNr);
+		}
+		else
+		{
+			charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[chNr]);
+			charOutOutlined(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[chNr]);
+		}
+	}
+	else
+	{
+		if (chNr < 10) // one digit?
+		{
+			charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + chNr);
+		}
+		else
+		{
+			charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[chNr]);
+			charOut(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[chNr]);
+		}
+	}
+}
+
+static void redrawScope(int32_t ch)
+{
+	int32_t i;
+
+	int32_t chansPerRow = (uint32_t)song.numChannels >> 1;
+	int32_t chanLookup = chansPerRow - 1;
+	const uint16_t *scopeLens = scopeLenTab[chanLookup];
+
+	// get x,y,len for scope according to channel (we must do it this way since 'len' can differ!)
+
+	uint16_t x = 2;
+	uint16_t y = 94;
+
+	uint16_t scopeLen = 0; // prevent compiler warning
+	for (i = 0; i < song.numChannels; i++)
+	{
+		scopeLen = scopeLens[i];
+
+		if (i == chansPerRow) // did we reach end of row?
+		{
+			// yes, go one row down
+			x  = 2;
+			y += 39;
+		}
+
+		if (i == ch)
+			break;
+
+		// adjust position to next channel
+		x += scopeLen + 3;
+	}
+
+	drawFramework(x, y, scopeLen + 2, 38, FRAMEWORK_TYPE2);
+
+	// draw mute graphics if channel is muted
+	if (!editor.chnMode[i])
+	{
+		const uint16_t muteGfxLen = scopeMuteBMP_Widths[chanLookup];
+		const uint16_t muteGfxX = x + ((scopeLen - muteGfxLen) >> 1);
+
+		blitFastClipX(muteGfxX, y + 6, bmp.scopeMute+scopeMuteBMP_Offs[chanLookup], 162, scopeMuteBMP_Heights[chanLookup], muteGfxLen);
+
+		if (config.ptnChnNumbers)
+			drawScopeNumber(x + 1, y + 1, (uint8_t)i, true);
+	}
+
+	scope[ch].wasCleared = false;
+}
+
+void refreshScopes(void)
+{
+	for (int32_t i = 0; i < MAX_CHANNELS; i++)
+		scope[i].wasCleared = false;
+}
+
+static void channelMode(int32_t chn)
+{
+	int32_t i;
+	
+	assert(chn < song.numChannels);
+
+	bool m = mouse.leftButtonPressed && !mouse.rightButtonPressed;
+	bool m2 = mouse.rightButtonPressed && mouse.leftButtonPressed;
+
+	if (m2)
+	{
+		bool test = false;
+		for (i = 0; i < song.numChannels; i++)
+		{
+			if (i != chn && !editor.chnMode[i])
+				test = true;
+		}
+
+		if (test)
+		{
+			for (i = 0; i < song.numChannels; i++)
+				editor.chnMode[i] = true;
+		}
+		else
+		{
+			for (i = 0; i < song.numChannels; i++)
+				editor.chnMode[i] = (i == chn);
+		}
+	}
+	else if (m)
+	{
+		editor.chnMode[chn] ^= 1;
+	}
+	else
+	{
+		if (editor.chnMode[chn])
+		{
+			config.multiRecChn[chn] ^= 1;
+		}
+		else
+		{
+			config.multiRecChn[chn] = true;
+			editor.chnMode[chn] = true;
+			m = true;
+		}
+	}
+
+	for (i = 0; i < song.numChannels; i++)
+		setChannel(i, editor.chnMode[i]);
+
+	if (m2)
+	{
+		for (i = 0; i < song.numChannels; i++)
+			redrawScope(i);
+	}
+	else
+	{
+		redrawScope(chn);
+	}
+}
+
+bool testScopesMouseDown(void)
+{
+	int32_t i;
+
+	if (!ui.scopesShown)
+		return false;
+
+	if (mouse.y >= 95 && mouse.y <= 169 && mouse.x >= 3 && mouse.x <= 288)
+	{
+		if (mouse.y > 130 && mouse.y < 134)
+			return true;
+
+		int32_t chansPerRow = (uint32_t)song.numChannels >> 1;
+		const uint16_t *scopeLens = scopeLenTab[chansPerRow-1];
+
+		// find out if we clicked inside a scope
+		uint16_t x = 3;
+		for (i = 0; i < chansPerRow; i++)
+		{
+			if (mouse.x >= x && mouse.x < x+scopeLens[i])
+				break;
+
+			x += scopeLens[i]+3;
+		}
+
+		if (i == chansPerRow)
+			return true; // scope framework was clicked instead
+
+		int32_t chanToToggle = i;
+		if (mouse.y >= 134) // second row of scopes?
+			chanToToggle += chansPerRow; // yes, increase lookup offset
+
+		channelMode(chanToToggle);
+		return true;
+	}
+
+	return false;
+}
+
+static void scopeTrigger(int32_t ch, const sample_t *s, int32_t playOffset)
+{
+	volatile scope_t tempState;
+	volatile scope_t *sc = &scope[ch];
+
+	int32_t length = s->length;
+	int32_t loopStart = s->loopStart;
+	int32_t loopLength = s->loopLength;
+	int32_t loopEnd = s->loopStart + s->loopLength;
+	uint8_t loopType = GET_LOOPTYPE(s->flags);
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+
+	if (s->dataPtr == NULL || length < 1)
+	{
+		sc->active = false; // shut down scope (illegal parameters)
+		return;
+	}
+
+	tempState = *sc; // get copy of current scope state
+
+	if (loopLength < 1) // disable loop if loopLength is below 1
+		loopType = 0;
+
+	if (sample16Bit)
+		tempState.base16 = (const int16_t *)s->dataPtr;
+	else
+		tempState.base8 = s->dataPtr;
+
+	tempState.sample16Bit = sample16Bit;
+	tempState.loopType = loopType;
+	tempState.direction = 1; // forwards
+	tempState.sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd;
+	tempState.loopStart = loopStart;
+	tempState.loopLength = loopLength;
+	tempState.position = playOffset;
+	tempState.positionFrac = 0;
+	
+	// if position overflows (f.ex. through 9xx command), shut down scopes
+	if (tempState.position >= tempState.sampleEnd)
+	{
+		sc->active = false;
+		return;
+	}
+
+	tempState.active = true;
+
+	/* Update live scope now.
+	** In theory it -can- be written to in the middle of a cached read,
+	** then the read thread writes its own non-updated cached copy back and
+	** the trigger never happens. So far I have never seen it happen,
+	** so it's probably very rare. Yes, this is not good coding...
+	*/
+
+	*sc = tempState; // set new scope state
+}
+
+static void updateScopes(void)
+{
+	scopesUpdatingFlag = true;
+
+	volatile scope_t *sc = scope;
+	for (int32_t i = 0; i < song.numChannels; i++, sc++)
+	{
+		volatile scope_t s = *sc; // get copy of current scope state
+		if (!s.active)
+			continue; // scope is not active
+
+		// scope position update
+
+		s.positionFrac += s.delta;
+		const uint32_t wholeSamples = (uint32_t)(s.positionFrac >> SCOPE_FRAC_BITS);
+		s.positionFrac &= SCOPE_FRAC_MASK;
+
+		if (s.direction == 1)
+			s.position += wholeSamples; // forwards
+		else
+			s.position -= wholeSamples; // backwards
+
+		// handle loop wrapping or sample end
+		if (s.direction == -1 && s.position < s.loopStart) // sampling backwards (definitely pingpong loop)
+		{
+			s.direction = 1; // change direction to forwards
+
+			if (s.loopLength >= 2)
+				s.position = s.loopStart + ((s.loopStart - s.position - 1) % s.loopLength);
+			else
+				s.position = s.loopStart;
+
+			assert(s.position >= s.loopStart && s.position < s.sampleEnd);
+		}
+		else if (s.position >= s.sampleEnd)
+		{
+			uint32_t loopOverflowVal;
+
+			if (s.loopLength >= 2)
+				loopOverflowVal = (s.position - s.sampleEnd) % s.loopLength;
+			else
+				loopOverflowVal = 0;
+
+			if (s.loopType == LOOP_DISABLED)
+			{
+				s.active = false;
+			}
+			else if (s.loopType == LOOP_FORWARD)
+			{
+				s.position = s.loopStart + loopOverflowVal;
+				assert(s.position >= s.loopStart && s.position < s.sampleEnd);
+			}
+			else // pingpong loop
+			{
+				s.direction = -1; // change direction to backwards
+				s.position = (s.sampleEnd - 1) - loopOverflowVal;
+				assert(s.position >= s.loopStart && s.position < s.sampleEnd);
+			}
+		}
+		assert(s.position >= 0);
+
+		*sc = s; // set new scope state
+	}
+	scopesUpdatingFlag = false;
+}
+
+void drawScopes(void)
+{
+	scopesDisplayingFlag = true;
+	int32_t chansPerRow = (uint32_t)song.numChannels >> 1;
+
+	const uint16_t *scopeLens = scopeLenTab[chansPerRow-1];
+	uint16_t scopeXOffs = 3;
+	uint16_t scopeYOffs = 95;
+	int16_t scopeLineY = 112;
+
+	for (int32_t i = 0; i < song.numChannels; i++)
+	{
+		// if we reached the last scope on the row, go to first scope on the next row
+		if (i == chansPerRow)
+		{
+			scopeXOffs = 3;
+			scopeYOffs = 134;
+			scopeLineY = 151;
+		}
+
+		const uint16_t scopeDrawLen = scopeLens[i];
+		if (!editor.chnMode[i]) // scope muted (mute graphics blit()'ed elsewhere)
+		{
+			scopeXOffs += scopeDrawLen+3; // align x to next scope
+			continue;
+		}
+
+		const scope_t s = scope[i]; // cache scope to lower thread race condition issues
+		if (s.active && s.volume > 0 && !audio.locked)
+		{
+			// scope is active
+			scope[i].wasCleared = false;
+
+			// clear scope background
+			clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT);
+
+			// draw scope
+			bool linedScopesFlag = !!(config.specialFlags & LINED_SCOPES);
+			scopeDrawRoutineTable[(linedScopesFlag * 6) + (s.sample16Bit * 3) + s.loopType](&s, scopeXOffs, scopeLineY, scopeDrawLen);
+		}
+		else
+		{
+			// scope is inactive
+			volatile scope_t *sc = &scope[i];
+			if (!sc->wasCleared)
+			{
+				// clear scope background
+				clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT);
+
+				// draw empty line
+				hLine(scopeXOffs, scopeLineY, scopeDrawLen, PAL_PATTEXT);
+
+				sc->wasCleared = true;
+			}
+		}
+
+		// draw channel numbering (if enabled)
+		if (config.ptnChnNumbers)
+			drawScopeNumber(scopeXOffs, scopeYOffs, (uint8_t)i, false);
+
+		// draw rec. symbol (if enabled)
+		if (config.multiRecChn[i])
+			blit(scopeXOffs + 1, scopeYOffs + 31, bmp.scopeRec, 13, 4);
+
+		scopeXOffs += scopeDrawLen+3; // align x to next scope
+	}
+
+	scopesDisplayingFlag = false;
+}
+
+void drawScopeFramework(void)
+{
+	drawFramework(0, 92, 291, 81, FRAMEWORK_TYPE1);
+	for (int32_t i = 0; i < song.numChannels; i++)
+		redrawScope(i);
+}
+
+void handleScopesFromChQueue(chSyncData_t *chSyncData, uint8_t *scopeUpdateStatus)
+{
+	volatile scope_t *sc = scope;
+	syncedChannel_t *ch = chSyncData->channels;
+	for (int32_t i = 0; i < song.numChannels; i++, sc++, ch++)
+	{
+		const uint8_t status = scopeUpdateStatus[i];
+
+		if (status & IS_Vol)
+			sc->volume = ch->scopeVolume;
+
+		if (status & IS_Period)
+			sc->delta = ch->scopeDelta;
+
+		if (status & IS_Trigger)
+		{
+			if (instr[ch->instrNum] != NULL)
+			{
+				scopeTrigger(i, &instr[ch->instrNum]->smp[ch->smpNum], ch->smpStartPos);
+
+				// set some stuff used by Smp. Ed. for sampling position line
+
+				if (ch->instrNum == 130 || (ch->instrNum == editor.curInstr && ch->smpNum == editor.curSmp))
+					editor.curSmpChannel = (uint8_t)i;
+
+				lastChInstr[i].instrNum = ch->instrNum;
+				lastChInstr[i].smpNum = ch->smpNum;
+			}
+			else
+			{
+				// empty instrument, shut down scope
+				scope[i].active = false;
+				lastChInstr[i].instrNum = 255;
+				lastChInstr[i].smpNum = 255;
+			}
+		}
+	}
+}
+
+static int32_t SDLCALL scopeThreadFunc(void *ptr)
+{
+	// this is needed for scope stability (confirmed)
+	SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
+
+	// set next frame time
+	timeNext64 = SDL_GetPerformanceCounter() + scopeTimeLen;
+	timeNext64Frac = scopeTimeLenFrac;
+
+	while (editor.programRunning)
+	{
+		editor.scopeThreadMutex = true;
+		updateScopes();
+		editor.scopeThreadMutex = false;
+
+		uint64_t time64 = SDL_GetPerformanceCounter();
+		if (time64 < timeNext64)
+		{
+			time64 = timeNext64 - time64;
+			if (time64 > INT32_MAX)
+				time64 = INT32_MAX;
+
+			const int32_t diff32 = (int32_t)time64;
+
+			// convert and round to microseconds
+			const int32_t time32 = (int32_t)((diff32 * editor.dPerfFreqMulMicro) + 0.5);
+
+			// delay until we have reached the next frame
+			if (time32 > 0)
+				usleep(time32);
+		}
+
+		// update next tick time
+		timeNext64 += scopeTimeLen;
+		timeNext64Frac += scopeTimeLenFrac;
+		if (timeNext64Frac > UINT32_MAX)
+		{
+			timeNext64Frac &= UINT32_MAX;
+			timeNext64++;
+		}
+	}
+
+	(void)ptr;
+	return true;
+}
+
+bool initScopes(void)
+{
+	double dInt;
+
+	// calculate scope time for performance counters and split into int/frac
+	double dFrac = modf(editor.dPerfFreq / SCOPE_HZ, &dInt);
+
+	// integer part
+	scopeTimeLen = (int32_t)dInt;
+
+	// fractional part (scaled to 0..2^32-1)
+	dFrac *= UINT32_MAX+1.0;
+	scopeTimeLenFrac = (uint32_t)dFrac;
+
+	scopeThread = SDL_CreateThread(scopeThreadFunc, NULL, NULL);
+	if (scopeThread == NULL)
+	{
+		showErrorMsgBox("Couldn't create channel scope thread!");
+		return false;
+	}
+
+	SDL_DetachThread(scopeThread);
+	return true;
+}
--- /dev/null
+++ b/src/scopes/ft2_scopes.h
@@ -1,0 +1,49 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "../ft2_header.h"
+#include "../ft2_audio.h"
+#include "../ft2_cpu.h"
+
+#define SCOPE_HEIGHT 36
+
+#if CPU_64BIT
+#define SCOPE_FRAC_BITS 32
+#else
+/* Max safe amount of fracbits for uint32_t on scopes @ 64Hz.
+** Since the scopes deltas are always high'ish, 13-bit
+** fractional precision is OK.
+*/
+#define SCOPE_FRAC_BITS 13
+#endif
+
+#define SCOPE_FRAC_SCALE ((intCPUWord_t)1 << SCOPE_FRAC_BITS)
+#define SCOPE_FRAC_MASK (SCOPE_FRAC_SCALE-1)
+
+int32_t getSamplePosition(uint8_t ch);
+void stopAllScopes(void);
+void refreshScopes(void);
+bool testScopesMouseDown(void);
+void drawScopes(void);
+void drawScopeFramework(void);
+bool initScopes(void);
+
+// actual scope data
+typedef struct scope_t
+{
+	volatile bool active;
+	const int8_t *base8;
+	const int16_t *base16;
+	bool wasCleared, sample16Bit;
+	uint8_t loopType;
+	int32_t volume, direction, loopStart, loopLength, sampleEnd, position;
+	uintCPUWord_t delta, positionFrac;
+} scope_t;
+
+typedef struct lastChInstr_t
+{
+	uint8_t smpNum, instrNum;
+} lastChInstr_t;
+
+extern lastChInstr_t lastChInstr[MAX_CHANNELS];
--- a/src/smploaders/ft2_load_aiff.c
+++ b/src/smploaders/ft2_load_aiff.c
@@ -1,4 +1,9 @@
-// Apple AIFF sample loader
+/* Apple AIFF sample loader
+**
+** Note: Vol/loop sanitation is done in the last stage
+** of sample loading, so you don't need to do that here.
+** Do NOT close the file handle!
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -9,7 +14,7 @@
 #include "../ft2_sysreqs.h"
 #include "../ft2_sample_loader.h"
 
-static int32_t getAIFFRate(uint8_t *in);
+static int32_t getAIFFSampleRate(uint8_t *in);
 static bool aiffIsStereo(FILE *f); // only ran on files that are confirmed to be AIFFs
 
 bool loadAIFF(FILE *f, uint32_t filesize)
@@ -17,20 +22,16 @@
 	char compType[4];
 	int8_t *audioDataS8;
 	uint8_t sampleRateBytes[10], *audioDataU8;
-	int16_t *audioDataS16, *audioDataS16_2, smp16;
+	int16_t *audioDataS16, smp16;
 	uint16_t numChannels, bitDepth;
-	int32_t smp32, *audioDataS32;
+	int32_t *audioDataS32;
 	uint32_t i, blockName, blockSize;
 	uint32_t offset, len32;
+	sample_t *s = &tmpSmp;
 
 	fseek(f, 8, SEEK_SET);
 	fread(compType, 1, 4, f);
 	rewind(f);
-	if (!memcmp(compType, "AIFC", 4))
-	{
-		loaderMsgBox("Error loading sample: This AIFF type (AIFC) is not supported!");
-		return false;
-	}
 
 	if (filesize < 12)
 	{
@@ -97,25 +98,37 @@
 		return false;
 	}
 
-	if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
-	{
-		loaderMsgBox("Error loading sample: Unsupported bitdepth!");
-		return false;
-	}
-
 	// read compression type (if present)
+	bool signedSample = true;
+	bool floatSample = false;
 	if (commLen > 18)
 	{
 		fread(&compType, 1, 4, f);
-		if (memcmp(compType, "NONE", 4) != 0)
+
+		if (!memcmp(compType, "raw ", 4))
 		{
-			loaderMsgBox("Error loading sample: The sample is not supported or is invalid!");
+			signedSample = false;
+		}
+		else if (!memcmp(compType, "FL32", 4) || !memcmp(compType, "fl32", 4) || !memcmp(compType, "FL64", 4) || !memcmp(compType, "fl64", 4))
+		{
+			floatSample = true;
+		}
+		else
+		{
+			loaderMsgBox("Error loading sample: Unsupported AIFF type!");
 			return false;
 		}
 	}
 
-	uint32_t sampleRate = getAIFFRate(sampleRateBytes);
+	if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32 &&
+		!(floatSample && bitDepth == 64) && !(floatSample && bitDepth == 32))
+	{
+		loaderMsgBox("Error loading sample: Unsupported AIFF type!");
+		return false;
+	}
 
+	uint32_t sampleRate = getAIFFSampleRate(sampleRateBytes);
+
 	// sample data chunk
 
 	fseek(f, ssndPtr, SEEK_SET);
@@ -132,8 +145,6 @@
 	ssndLen -= 8; // don't include offset and blockSize datas
 
 	uint32_t sampleLength = ssndLen;
-	if (sampleLength > MAX_SAMPLE_LEN)
-		sampleLength = MAX_SAMPLE_LEN;
 
 	int16_t stereoSampleLoadMode = -1;
 	if (aiffIsStereo(f))
@@ -141,23 +152,26 @@
 
 	// read sample data
 
-	if (bitDepth == 8)
+	if (!floatSample && bitDepth == 8) // 8-BIT INTEGER SAMPLE
 	{
-		// 8-BIT SIGNED PCM
-
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength))
+		if (!allocateSmpData(s, sampleLength, false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 1, f) != 1)
+		if (fread(s->dataPtr, sampleLength, 1, f) != 1)
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		audioDataS8 = (int8_t *)tmpSmp.pek;
+		audioDataS8 = (int8_t *)s->dataPtr;
+		if (!signedSample)
+		{
+			for (i = 0; i < sampleLength; i++)
+				audioDataS8[i] ^= 0x80;
+		}
 
 		// stereo conversion
 		if (numChannels == 2)
@@ -199,25 +213,23 @@
 			}
 		}
 	}
-	else if (bitDepth == 16)
+	else if (!floatSample && bitDepth == 16) // 16-BIT INTEGER SAMPLE
 	{
-		// 16-BIT SIGNED PCM
-
-		sampleLength /= 2;
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength*2))
+		sampleLength /= sizeof (int16_t);
+		if (!allocateSmpData(s, sampleLength * sizeof (int16_t), false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 2, f) != 2)
+		if (fread(s->dataPtr, sampleLength, sizeof (int16_t), f) != sizeof (int16_t))
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		// fix endianness
-		audioDataS16 = (int16_t *)tmpSmp.pek;
+		// change endianness
+		audioDataS16 = (int16_t *)s->dataPtr;
 		for (i = 0; i < sampleLength; i++)
 			audioDataS16[i] = SWAP16(audioDataS16[i]);
 
@@ -251,7 +263,7 @@
 					len32 = sampleLength - 1;
 					for (i = 0; i < len32; i++)
 					{
-						smp32 = (audioDataS16[(i * 2) + 0] + audioDataS16[(i * 2) + 1]) >> 1;
+						int32_t smp32 = (audioDataS16[(i * 2) + 0] + audioDataS16[(i * 2) + 1]) >> 1;
 						audioDataS16[i] = (int16_t)smp32;
 					}
 
@@ -261,30 +273,27 @@
 			}
 		}
 
-		sampleLength *= 2;
-		tmpSmp.typ |= 16;
+		s->flags |= SAMPLE_16BIT;
 	}
-	else if (bitDepth == 24)
+	else if (!floatSample && bitDepth == 24) // 24-BIT INTEGER SAMPLE
 	{
-		// 24-BIT SIGNED PCM
-
 		sampleLength /= 3;
-		if (!allocateTmpSmpData(&tmpSmp, (sampleLength * 4) * 2))
+		if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(&tmpSmp.pek[sampleLength], sampleLength, 3, f) != 3)
+		if (fread(&s->dataPtr[sampleLength], sampleLength, 3, f) != 3)
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		audioDataS32 = (int32_t *)tmpSmp.pek;
+		audioDataS32 = (int32_t *)s->dataPtr;
 
 		// convert to 32-bit
-		audioDataU8 = (uint8_t *)tmpSmp.pek + sampleLength;
+		audioDataU8 = (uint8_t *)s->dataPtr + sampleLength;
 		for (i = 0; i < sampleLength; i++)
 		{
 			audioDataS32[i] = (audioDataU8[0] << 24) | (audioDataU8[1] << 16) | (audioDataU8[2] << 8);
@@ -336,36 +345,29 @@
 
 		normalizeSigned32Bit(audioDataS32, sampleLength);
 
-		// downscale to 16-bit (ultra fast method!)
+		audioDataS16 = (int16_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+			audioDataS16[i] = audioDataS32[i] >> 16;
 
-		audioDataS16 = (int16_t *)tmpSmp.pek;
-		audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words
-
-		for (i = 0; i < sampleLength; i++, audioDataS16_2++)
-			audioDataS16[i] = audioDataS16_2[i];
-
-		sampleLength *= 2;
-		tmpSmp.typ |= 16;
+		s->flags |= SAMPLE_16BIT;
 	}
-	else if (bitDepth == 32)
+	else if (!floatSample && bitDepth == 32) // 32-BIT INTEGER SAMPLE
 	{
-		// 32-BIT SIGNED PCM
-
-		sampleLength /= 4;
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength * 4))
+		sampleLength /= sizeof (int32_t);
+		if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 4, f) != 4)
+		if (fread(s->dataPtr, sampleLength, sizeof (int32_t), f) != sizeof (int32_t))
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		// fix endianness
-		audioDataS32 = (int32_t *)tmpSmp.pek;
+		// change endianness
+		audioDataS32 = (int32_t *)s->dataPtr;
 		for (i = 0; i < sampleLength; i++)
 			audioDataS32[i] = SWAP32(audioDataS32[i]);
 
@@ -414,42 +416,188 @@
 
 		normalizeSigned32Bit(audioDataS32, sampleLength);
 
-		// downscale to 16-bit (ultra fast method!)
+		audioDataS16 = (int16_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+			audioDataS16[i] = audioDataS32[i] >> 16;
 
-		audioDataS16 = (int16_t *)tmpSmp.pek;
-		audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words
+		s->flags |= SAMPLE_16BIT;
+	}
+	else if (floatSample && bitDepth == 32) // 32-BIT FLOAT SAMPLE
+	{
+		sampleLength /= sizeof (float);
+		if (!allocateSmpData(s, sampleLength * sizeof (float), false))
+		{
+			loaderMsgBox("Not enough memory!");
+			return false;
+		}
 
-		for (i = 0; i < sampleLength; i++, audioDataS16_2++)
-			audioDataS16[i] = audioDataS16_2[i];
+		if (fread(s->dataPtr, sampleLength, sizeof (float), f) != sizeof (float))
+		{
+			loaderMsgBox("General I/O error during loading! Is the file in use?");
+			return false;
+		}
 
-		sampleLength *= 2;
-		tmpSmp.typ |= 16;
+		// change endianness
+		audioDataS32 = (int32_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+			audioDataS32[i] = SWAP32(audioDataS32[i]);
+
+		float *fAudioDataFloat = (float *)s->dataPtr;
+
+		// stereo conversion
+		if (numChannels == 2)
+		{
+			sampleLength /= 2;
+			switch (stereoSampleLoadMode)
+			{
+				case STEREO_SAMPLE_READ_LEFT:
+				{
+					// remove right channel data
+					for (i = 1; i < sampleLength; i++)
+						fAudioDataFloat[i] = fAudioDataFloat[(i * 2) + 0];
+				}
+				break;
+
+				case STEREO_SAMPLE_READ_RIGHT:
+				{
+					// remove left channel data
+					len32 = sampleLength - 1;
+					for (i = 0; i < len32; i++)
+						fAudioDataFloat[i] = fAudioDataFloat[(i * 2) + 1];
+
+					fAudioDataFloat[i] = 0.0f;
+				}
+				break;
+
+				default:
+				case STEREO_SAMPLE_CONVERT:
+				{
+					// mix stereo to mono
+					len32 = sampleLength - 1;
+					for (i = 0; i < len32; i++)
+						fAudioDataFloat[i] = (fAudioDataFloat[(i * 2) + 0] + fAudioDataFloat[(i * 2) + 1]) * 0.5f;
+
+					fAudioDataFloat[i] = 0.0f;
+				}
+				break;
+			}
+		}
+
+		normalize32BitFloatToSigned16Bit(fAudioDataFloat, sampleLength);
+
+		int16_t *ptr16 = (int16_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+		{
+			const int32_t smp32 = (const int32_t)fAudioDataFloat[i];
+			ptr16[i] = (int16_t)smp32;
+		}
+
+		s->flags |= SAMPLE_16BIT;
 	}
+	else if (floatSample && bitDepth == 64) // 64-BIT FLOAT SAMPLE
+	{
+		sampleLength /= sizeof (double);
+		if (!allocateSmpData(s, sampleLength * sizeof (double), false))
+		{
+			loaderMsgBox("Not enough memory!");
+			return false;
+		}
 
-	reallocateTmpSmpData(&tmpSmp, sampleLength); // readjust memory needed
+		if (fread(s->dataPtr, sampleLength, sizeof (double), f) != sizeof (double))
+		{
+			loaderMsgBox("General I/O error during loading! Is the file in use?");
+			return false;
+		}
 
-	tmpSmp.len = sampleLength;
-	tmpSmp.vol = 64;
-	tmpSmp.pan = 128;
+		// change endianness
+		int64_t *audioDataS64 = (int64_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+			audioDataS64[i] = SWAP64(audioDataS64[i]);
 
-	tuneSample(&tmpSmp, sampleRate, audio.linearPeriodsFlag);
+		double *dAudioDataDouble = (double *)s->dataPtr;
 
+		// stereo conversion
+		if (numChannels == 2)
+		{
+			sampleLength /= 2;
+			switch (stereoSampleLoadMode)
+			{
+				case STEREO_SAMPLE_READ_LEFT:
+				{
+					// remove right channel data
+					for (i = 1; i < sampleLength; i++)
+						dAudioDataDouble[i] = dAudioDataDouble[(i * 2) + 0];
+				}
+				break;
+
+				case STEREO_SAMPLE_READ_RIGHT:
+				{
+					// remove left channel data
+					len32 = sampleLength - 1;
+					for (i = 0; i < len32; i++)
+						dAudioDataDouble[i] = dAudioDataDouble[(i * 2) + 1];
+
+					dAudioDataDouble[i] = 0.0;
+				}
+				break;
+
+				default:
+				case STEREO_SAMPLE_CONVERT:
+				{
+					// mix stereo to mono
+					len32 = sampleLength - 1;
+					for (i = 0; i < len32; i++)
+						dAudioDataDouble[i] = (dAudioDataDouble[(i * 2) + 0] + dAudioDataDouble[(i * 2) + 1]) * 0.5;
+
+					dAudioDataDouble[i] = 0.0;
+				}
+				break;
+			}
+		}
+
+		normalize64BitFloatToSigned16Bit(dAudioDataDouble, sampleLength);
+
+		int16_t *ptr16 = (int16_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+		{
+			const int32_t smp32 = (const int32_t)dAudioDataDouble[i];
+			ptr16[i] = (int16_t)smp32;
+		}
+
+		s->flags |= SAMPLE_16BIT;
+	}
+
+	if (sampleLength > MAX_SAMPLE_LEN)
+		sampleLength = MAX_SAMPLE_LEN;
+
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+	reallocateSmpData(s, sampleLength, sample16Bit); // readjust memory needed
+
+	s->length = sampleLength;
+	s->volume = 64;
+	s->panning = 128;
+
+	tuneSample(s, sampleRate, audio.linearPeriodsFlag);
+
 	return true;
 }
 
-static int32_t getAIFFRate(uint8_t *in)
+static int32_t getAIFFSampleRate(uint8_t *in)
 {
-	int32_t exp = (int32_t)(((in[0] & 0x7F) << 8) | in[1]);
-	uint32_t lo = (in[2] << 24) | (in[3] << 16) | (in[4] << 8) | in[5];
-	uint32_t hi = (in[6] << 24) | (in[7] << 16) | (in[8] << 8) | in[9];
+	uint32_t mantissa = (in[2] << 24) | (in[3] << 16) | (in[4] << 8) | in[5];
+	uint8_t exp = 30 - in[1];
 
-	if (exp == 0 && lo == 0 && hi == 0)
-		return 0;
+	uint32_t lastMantissa = 0;
+	while (exp--)
+	{
+		lastMantissa = mantissa;
+		mantissa >>= 1;
+	}
 
-	exp -= 16383;
+	if (lastMantissa & 1)
+		mantissa++;
 
-	double dOut = ldexp(lo, -31 + exp) + ldexp(hi, -63 + exp);
-	return (int32_t)(dOut + 0.5); // rounded
+	return mantissa;
 }
 
 static bool aiffIsStereo(FILE *f) // only ran on files that are confirmed to be AIFFs
--- /dev/null
+++ b/src/smploaders/ft2_load_flac.c
@@ -1,0 +1,431 @@
+/* FLAC sample loader
+**
+** Note: Vol/loop sanitation is done in the last stage
+** of sample loading, so you don't need to do that here.
+** Do NOT close the file handle!
+*/
+
+#ifdef HAS_LIBFLAC
+
+// hide POSIX warning for fileno()
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+#include "../ft2_header.h"
+#include "../ft2_audio.h"
+#include "../ft2_sample_ed.h"
+#include "../ft2_sysreqs.h"
+#include "../ft2_sample_loader.h"
+#include "../libflac/FLAC/stream_decoder.h"
+
+static bool sample16Bit;
+static int16_t stereoSampleLoadMode = -1;
+static uint32_t numChannels, bitDepth, sampleLength, sampleRate, samplesRead;
+static sample_t *s;
+
+static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
+static FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+static FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+static FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
+static FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data);
+static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data);
+static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
+
+bool loadFLAC(FILE *f, uint32_t filesize)
+{
+	s = &tmpSmp;
+
+	s->volume = 64;
+	s->panning = 128;
+
+	numChannels = 0;
+	bitDepth = 0;
+	sampleLength = 0;
+	sampleRate = 0;
+	samplesRead = 0;
+
+	FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
+	if (decoder == NULL)
+	{
+		loaderMsgBox("Error loading sample: Unable to allocate FLAC decoder!");
+		goto error;
+	}
+
+	FLAC__stream_decoder_set_metadata_respond_all(decoder);
+
+	FLAC__StreamDecoderInitStatus initStatus =
+		FLAC__stream_decoder_init_stream
+		(
+			decoder,
+			read_callback, seek_callback,
+			tell_callback, length_callback,
+			eof_callback, write_callback,
+			metadata_callback, error_callback,
+			f
+		);
+
+	if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+	{
+		loaderMsgBox("Error loading sample: Unable to initialize FLAC decoder!");
+		goto error;
+	}
+
+	if (!FLAC__stream_decoder_process_until_end_of_stream(decoder))
+	{
+		loaderMsgBox("Error loading sample: Unable to decode FLAC!");
+		goto error;
+	}
+
+	FLAC__stream_decoder_finish(decoder);
+	FLAC__stream_decoder_delete(decoder);
+
+	tuneSample(s, sampleRate, audio.linearPeriodsFlag);
+
+	return true;
+
+error:
+	if (decoder != NULL) FLAC__stream_decoder_delete(decoder);
+
+	return false;
+
+	(void)filesize;
+}
+
+static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+	FILE *file = (FILE *)client_data;
+	if (*bytes > 0)
+	{
+		*bytes = fread(buffer, sizeof (FLAC__byte), *bytes, file);
+		if (ferror(file))
+			return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+		else if (*bytes == 0)
+			return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+		else
+			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+	}
+	else
+	{
+		return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+	}
+
+	(void)decoder;
+}
+
+static FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+	FILE *file = (FILE *)client_data;
+
+	if (absolute_byte_offset > INT32_MAX)
+		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+
+	if (fseek(file, (int32_t)absolute_byte_offset, SEEK_SET) < 0)
+		return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+	else
+		return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+
+	(void)decoder;
+}
+
+static FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+	FILE *file = (FILE *)client_data;
+	int32_t pos = ftell(file);
+
+	if (pos < 0)
+	{
+		return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+	}
+	else
+	{
+		*absolute_byte_offset = (FLAC__uint64)pos;
+		return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+	}
+
+	(void)decoder;
+}
+
+static FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
+{
+	FILE *file = (FILE *)client_data;
+	struct stat filestats;
+
+	if (fstat(fileno(file), &filestats) != 0)
+	{
+		return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+	}
+	else
+	{
+		*stream_length = (FLAC__uint64)filestats.st_size;
+		return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+	}
+
+	(void)decoder;
+}
+
+static FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data)
+{
+	FILE *file = (FILE *)client_data;
+	return feof(file) ? true : false;
+
+	(void)decoder;
+}
+
+static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+	if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO && metadata->data.stream_info.total_samples != 0)
+	{
+		bitDepth = metadata->data.stream_info.bits_per_sample;
+		numChannels = metadata->data.stream_info.channels;
+		sampleRate = metadata->data.stream_info.sample_rate;
+
+		sample16Bit = (bitDepth != 8);
+
+		int64_t tmp64 = metadata->data.stream_info.total_samples;
+		if (tmp64 > MAX_SAMPLE_LEN)
+			tmp64 = MAX_SAMPLE_LEN;
+
+		sampleLength = (uint32_t)tmp64;
+
+		s->length = sampleLength;
+		if (sample16Bit)
+			s->flags |= SAMPLE_16BIT;
+
+		stereoSampleLoadMode = -1;
+		if (numChannels == 2)
+			stereoSampleLoadMode = loaderSysReq(5, "System request", "This is a stereo sample...");
+	}
+
+	// check for RIFF chunks (loop/vol/pan information)
+	else if (metadata->type == FLAC__METADATA_TYPE_APPLICATION && !memcmp(metadata->data.application.id, "riff", 4))
+	{
+		const uint8_t *data = (const uint8_t *)metadata->data.application.data;
+
+		uint32_t chunkID  = *(uint32_t *)data; data += 4;
+		uint32_t chunkLen = *(uint32_t *)data; data += 4;
+
+		if (chunkID == 0x61727478 && chunkLen >= 8) // "xtra"
+		{
+			uint32_t xtraFlags = *(uint32_t *)data; data += 4;
+
+			// panning (0..256)
+			if (xtraFlags & 0x20) // set panning flag
+			{
+				uint16_t tmpPan = *(uint16_t *)data;
+				if (tmpPan > 255)
+					tmpPan = 255;
+
+				s->panning = (uint8_t)tmpPan;
+			}
+			data += 2;
+
+			// volume (0..256)
+			uint16_t tmpVol = *(uint16_t *)data;
+			if (tmpVol > 256)
+				tmpVol = 256;
+
+			s->volume = (uint8_t)((tmpVol + 2) / 4); // 0..256 -> 0..64 (rounded)
+		}
+
+		if (chunkID == 0x6C706D73 && chunkLen > 52) // "smpl"
+		{
+			data += 28; // seek to first wanted byte
+
+			uint32_t numLoops = *(uint32_t *)data; data += 4;
+			if (numLoops == 1)
+			{
+				data += 4+4; // skip "samplerData" and "identifier"
+
+				uint32_t loopType  = *(uint32_t *)data; data += 4;
+				uint32_t loopStart = *(uint32_t *)data; data += 4;
+				uint32_t loopEnd   = *(uint32_t *)data; data += 4;
+
+				s->loopStart = loopStart;
+				s->loopLength = (loopEnd+1) - loopStart;
+				s->flags |= (loopType == 0) ? LOOP_FWD : LOOP_BIDI;
+			}
+		}
+	}
+
+	else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+	{
+		uint32_t tmpSampleRate = 0, loopStart = 0, loopLength = 0;
+		for (uint32_t i = 0; i < metadata->data.vorbis_comment.num_comments; i++)
+		{
+			const char *tag = (const char *)metadata->data.vorbis_comment.comments[i].entry;
+			uint32_t length = metadata->data.vorbis_comment.comments[i].length;
+
+			if (length > 6 && !memcmp(tag, "TITLE=", 6))
+			{
+				length -= 6;
+				if (length > 22)
+					length = 22;
+
+				memcpy(s->name, &tag[6], length);
+				s->name[22] = '\0';
+
+				smpFilenameSet = true;
+			}
+
+			// the following tags haven't been tested!
+			else if (length > 11 && !memcmp(tag, "SAMPLERATE=", 11))
+			{
+				tmpSampleRate = atoi(&tag[11]);
+			}
+			else if (length > 10 && !memcmp(tag, "LOOPSTART=", 10))
+			{
+				loopLength = atoi(&tag[10]);
+			}
+			else if (length > 11 && !memcmp(tag, "LOOPLENGTH=", 11))
+			{
+				loopLength = atoi(&tag[11]);
+			}
+
+			if (loopLength > 0)
+			{
+				s->loopStart = loopLength;
+				s->loopLength = loopStart;
+
+				DISABLE_LOOP(s->flags);
+				s->flags |= LOOP_FWD;
+			}
+
+			if (tmpSampleRate > 0)
+				sampleRate = tmpSampleRate;
+		}
+	}
+
+	(void)client_data;
+	(void)decoder;
+}
+
+static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
+{
+	if (sampleLength == 0 || numChannels == 0)
+	{
+		loaderMsgBox("Error loading sample: The sample is empty or corrupt!");
+		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+	}
+
+	if (numChannels > 2)
+	{
+		loaderMsgBox("Error loading sample: Only mono/stereo FLACs are supported!");
+		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+	}
+
+	if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24)
+	{
+		loaderMsgBox("Error loading sample: Only FLACs with a bitdepth of 8/16/24 are supported!");
+		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+	}
+
+	if (frame->header.number.sample_number == 0)
+	{
+		if (!allocateSmpData(s, sampleLength, sample16Bit))
+		{
+			loaderMsgBox("Error loading sample: Out of memory!");
+			return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+		}
+
+		samplesRead = 0;
+	}
+
+	uint32_t blockSize = frame->header.blocksize;
+
+	const uint32_t samplesAllocated = sampleLength;
+	if (samplesRead+blockSize > samplesAllocated)
+		blockSize = samplesAllocated-samplesRead;
+
+	if (stereoSampleLoadMode == STEREO_SAMPLE_CONVERT) // mix to mono
+	{
+		const int32_t *src32_L = buffer[0];
+		const int32_t *src32_R = buffer[1];
+
+		switch (bitDepth)
+		{
+			case 8:
+			{
+				int8_t *dst8 = s->dataPtr + samplesRead;
+				for (uint32_t i = 0; i < blockSize; i++)
+					dst8[i] = (int8_t)((src32_L[i] + src32_R[i]) >> 1);
+			}
+			break;
+
+			case 16:
+			{
+				int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead;
+				for (uint32_t i = 0; i < blockSize; i++)
+					dst16[i] = (int16_t)((src32_L[i] + src32_R[i]) >> 1);
+			}
+			break;
+
+			case 24:
+			{
+				int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead;
+				for (uint32_t i = 0; i < blockSize; i++)
+					dst16[i] = (int16_t)((src32_L[i] + src32_R[i]) >> (16+1));
+			}
+			break;
+
+			default: break;
+		}
+	}
+	else // mono sample
+	{
+		const int32_t *src32 = (stereoSampleLoadMode == STEREO_SAMPLE_READ_RIGHT) ? buffer[1] : buffer[0];
+
+		switch (bitDepth)
+		{
+			case 8:
+			{
+				int8_t *dst8 = s->dataPtr + samplesRead;
+				for (uint32_t i = 0; i < blockSize; i++)
+					dst8[i] = (int8_t)src32[i];
+			}
+			break;
+
+			case 16:
+			{
+				int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead;
+				for (uint32_t i = 0; i < blockSize; i++)
+					dst16[i] = (int16_t)src32[i];
+			}
+			break;
+
+			case 24:
+			{
+				int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead;
+				for (uint32_t i = 0; i < blockSize; i++)
+					dst16[i] = (int16_t)(src32[i] >> 8);
+			}
+			break;
+
+			default: break;
+		}
+	}
+
+	samplesRead += blockSize;
+	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+
+	(void)client_data;
+	(void)decoder;
+}
+
+static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+	(void)status;
+	(void)decoder;
+	(void)client_data;
+}
+
+#endif
--- a/src/smploaders/ft2_load_iff.c
+++ b/src/smploaders/ft2_load_iff.c
@@ -1,4 +1,9 @@
-// IFF (Amiga/FT2) sample loader
+/* IFF (Amiga/FT2) sample loader
+**
+** Note: Vol/loop sanitation is done in the last stage
+** of sample loading, so you don't need to do that here.
+** Do NOT close the file handle!
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -12,7 +17,8 @@
 bool loadIFF(FILE *f, uint32_t filesize)
 {
 	char hdr[4+1];
-	uint32_t sampleLength, sampleVol, sampleLoopStart, sampleLoopLength, sampleRate;
+	uint32_t length, volume, loopStart, loopLength, sampleRate;
+	sample_t *s = &tmpSmp;
 
 	if (filesize < 12)
 	{
@@ -23,7 +29,7 @@
 	fseek(f, 8, SEEK_SET);
 	fread(hdr, 1, 4, f);
 	hdr[4] = '\0';
-	bool is16Bit = !strncmp(hdr, "16SV", 4);
+	bool sample16Bit = !strncmp(hdr, "16SV", 4);
 
 	uint32_t vhdrPtr = 0, vhdrLen = 0;
 	uint32_t bodyPtr = 0, bodyLen = 0;
@@ -82,8 +88,8 @@
 		bodyLen = filesize - bodyPtr;
 
 	fseek(f, vhdrPtr, SEEK_SET);
-	fread(&sampleLoopStart,  4, 1, f); sampleLoopStart = SWAP32(sampleLoopStart);
-	fread(&sampleLoopLength, 4, 1, f); sampleLoopLength = SWAP32(sampleLoopLength);
+	fread(&loopStart,  4, 1, f); loopStart = SWAP32(loopStart);
+	fread(&loopLength, 4, 1, f); loopLength = SWAP32(loopLength);
 	fseek(f, 4, SEEK_CUR);
 	fread(&sampleRate, 2, 1, f); sampleRate = SWAP16(sampleRate);
 	fseek(f, 1, SEEK_CUR);
@@ -94,83 +100,61 @@
 		return false;
 	}
 
-	fread(&sampleVol, 4, 1, f); sampleVol = SWAP32(sampleVol);
-	if (sampleVol > 65535)
-		sampleVol = 65535;
+	fread(&volume, 4, 1, f); volume = SWAP32(volume);
+	if (volume > 65535)
+		volume = 65535;
 
-	sampleVol = (sampleVol + 512) / 1024; // rounded
+	volume = (volume + 512) / 1024; // rounded
 
-	sampleLength = bodyLen;
-	if (sampleLength > MAX_SAMPLE_LEN)
-		sampleLength = MAX_SAMPLE_LEN;
-
-	if (sampleLoopStart >= MAX_SAMPLE_LEN || sampleLoopLength > MAX_SAMPLE_LEN)
+	length = bodyLen;
+	if (sample16Bit)
 	{
-		sampleLoopStart = 0;
-		sampleLoopLength = 0;
+		length >>= 1;
+		loopStart >>= 1;
+		loopLength >>= 1;
 	}
 
-	if (sampleLoopStart+sampleLoopLength > sampleLength)
-	{
-		sampleLoopStart = 0;
-		sampleLoopLength = 0;
-	}
+	s->length = length;
+	if (s->length > MAX_SAMPLE_LEN)
+		s->length = MAX_SAMPLE_LEN;
 
-	if (sampleLoopStart > sampleLength-2)
+	if (!allocateSmpData(s, length, sample16Bit))
 	{
-		sampleLoopStart = 0;
-		sampleLoopLength = 0;
-	}
-
-	if (!allocateTmpSmpData(&tmpSmp, sampleLength))
-	{
 		loaderMsgBox("Not enough memory!");
 		return false;
 	}
 
 	fseek(f, bodyPtr, SEEK_SET);
-	if (fread(tmpSmp.pek, sampleLength, 1, f) != 1)
+	if (fread(s->dataPtr, length << sample16Bit, 1, f) != 1)
 	{
 		loaderMsgBox("General I/O error during loading! Is the file in use?");
 		return false;
 	}
 
-	tmpSmp.len = sampleLength;
+	s->loopStart = loopStart;
+	s->loopLength = loopLength;
 
-	if (sampleLoopLength > 2)
-	{
-		tmpSmp.repS = sampleLoopStart;
-		tmpSmp.repL = sampleLoopLength;
-		tmpSmp.typ |= 1;
-	}
+	if (s->loopLength > 0)
+		s->flags |= LOOP_FWD;
 
-	if (is16Bit)
-	{
-		tmpSmp.len  &= 0xFFFFFFFE;
-		tmpSmp.repS &= 0xFFFFFFFE;
-		tmpSmp.repL &= 0xFFFFFFFE;
-		tmpSmp.typ |= 16;
-	}
+	if (sample16Bit)
+		s->flags |= SAMPLE_16BIT;
 
-	tmpSmp.vol = (uint8_t)sampleVol;
-	tmpSmp.pan = 128;
+	s->volume = (uint8_t)volume;
+	s->panning = 128;
 
-	tuneSample(&tmpSmp, sampleRate, audio.linearPeriodsFlag);
+	tuneSample(s, sampleRate, audio.linearPeriodsFlag);
 
 	// set name
 	if (namePtr != 0 && nameLen > 0)
 	{
 		fseek(f, namePtr, SEEK_SET);
-		if (nameLen > 21)
-		{
-			fread(tmpSmp.name, 1, 21, f);
-			tmpSmp.name[21] = '\0';
-		}
-		else
-		{
-			memset(tmpSmp.name, 0, 22);
-			fread(tmpSmp.name, 1, nameLen, f);
-		}
+
+		if (nameLen > 22)
+			nameLen = 22;
+
+		fread(s->name, 1, nameLen, f);
+		s->name[22] = '\0';
 
 		smpFilenameSet = true;
 	}
--- a/src/smploaders/ft2_load_raw.c
+++ b/src/smploaders/ft2_load_raw.c
@@ -1,4 +1,7 @@
-// RAW (header-less) sample loader
+/* RAW (header-less) sample loader
+**
+** Note: Do NOT close the file handle!
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -10,21 +13,23 @@
 
 bool loadRAW(FILE *f, uint32_t filesize)
 {
-	if (!allocateTmpSmpData(&tmpSmp, filesize))
+	sample_t *s = &tmpSmp;
+
+	if (!allocateSmpData(s, filesize, false))
 	{
 		loaderMsgBox("Not enough memory!");
 		return false;
 	}
 
-	if (fread(tmpSmp.pek, filesize, 1, f) != 1)
+	if (fread(s->dataPtr, filesize, 1, f) != 1)
 	{
 		okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?");
 		return false;
 	}
 
-	tmpSmp.len = filesize;
-	tmpSmp.vol = 64;
-	tmpSmp.pan = 128;
+	s->length = filesize;
+	s->volume = 64;
+	s->panning = 128;
 
 	return true;
 }
--- a/src/smploaders/ft2_load_wav.c
+++ b/src/smploaders/ft2_load_wav.c
@@ -1,4 +1,9 @@
-// WAV sample loader
+/* WAV sample loader
+**
+** Note: Vol/loop sanitation is done in the last stage
+** of sample loading, so you don't need to do that here.
+** Do NOT close the file handle!
+*/
 
 #include <stdio.h>
 #include <stdint.h>
@@ -20,7 +25,7 @@
 bool loadWAV(FILE *f, uint32_t filesize)
 {
 	uint8_t *audioDataU8;
-	int16_t *audioDataS16, *audioDataS16_2, *ptr16;
+	int16_t *audioDataS16, *ptr16;
 	uint16_t audioFormat, numChannels, bitsPerSample;
 	int32_t *audioDataS32;
 	uint32_t i, sampleRate, sampleLength;
@@ -27,6 +32,7 @@
 	uint32_t len32;
 	float *fAudioDataFloat;
 	double *dAudioDataDouble;
+	sample_t *s = &tmpSmp;
 
 	if (filesize < 12)
 	{
@@ -172,9 +178,6 @@
 	// ---- READ SAMPLE DATA ----
 	fseek(f, dataPtr, SEEK_SET);
 
-	if (sampleLength > MAX_SAMPLE_LEN)
-		sampleLength = MAX_SAMPLE_LEN;
-
 	int16_t stereoSampleLoadMode = -1;
 	if (wavIsStereo(f))
 		stereoSampleLoadMode = loaderSysReq(5, "System request", "This is a stereo sample...");
@@ -181,19 +184,19 @@
 
 	if (bitsPerSample == 8) // 8-BIT INTEGER SAMPLE
 	{
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength))
+		if (!allocateSmpData(s, sampleLength, false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 1, f) != 1)
+		if (fread(s->dataPtr, sampleLength, 1, f) != 1)
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		audioDataU8 = (uint8_t *)tmpSmp.pek;
+		audioDataU8 = (uint8_t *)s->dataPtr;
 
 		// stereo conversion
 		if (numChannels == 2)
@@ -236,24 +239,24 @@
 
 		// convert from unsigned to signed
 		for (i = 0; i < sampleLength; i++)
-			tmpSmp.pek[i] ^= 0x80;
+			s->dataPtr[i] ^= 0x80;
 	}
 	else if (bitsPerSample == 16) // 16-BIT INTEGER SAMPLE
 	{
 		sampleLength /= 2;
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength*2))
+		if (!allocateSmpData(s, sampleLength, true))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 2, f) != 2)
+		if (fread(s->dataPtr, sampleLength, 2, f) != 2)
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		audioDataS16 = (int16_t *)tmpSmp.pek;
+		audioDataS16 = (int16_t *)s->dataPtr;
 
 		// stereo conversion
 		if (numChannels == 2)
@@ -298,28 +301,27 @@
 			}
 		}
 
-		sampleLength *= 2;
-		tmpSmp.typ |= 16; // 16-bit
+		s->flags |= SAMPLE_16BIT;
 	}
 	else if (bitsPerSample == 24) // 24-BIT INTEGER SAMPLE
 	{
 		sampleLength /= 3;
-		if (!allocateTmpSmpData(&tmpSmp, (sampleLength * 4) * 2))
+		if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(&tmpSmp.pek[sampleLength], sampleLength, 3, f) != 3)
+		if (fread(&s->dataPtr[sampleLength], sampleLength, 3, f) != 3)
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		audioDataS32 = (int32_t *)tmpSmp.pek;
+		audioDataS32 = (int32_t *)s->dataPtr;
 
 		// convert to 32-bit
-		audioDataU8 = (uint8_t *)tmpSmp.pek + sampleLength;
+		audioDataU8 = (uint8_t *)s->dataPtr + sampleLength;
 		for (i = 0; i < sampleLength; i++)
 		{
 			audioDataS32[i] = (audioDataU8[2] << 24) | (audioDataU8[1] << 16) | (audioDataU8[0] << 8);
@@ -373,33 +375,28 @@
 
 		normalizeSigned32Bit(audioDataS32, sampleLength);
 
-		// downscale to 16-bit (ultra fast method!)
+		ptr16 = (int16_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+			ptr16[i] = audioDataS32[i] >> 16;
 
-		audioDataS16 = (int16_t *)tmpSmp.pek;
-		audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words
-
-		for (i = 0; i < sampleLength; i++, audioDataS16_2++)
-			audioDataS16[i] = audioDataS16_2[i];
-
-		sampleLength *= 2;
-		tmpSmp.typ |= 16; // 16-bit
+		s->flags |= SAMPLE_16BIT;
 	}
 	else if (audioFormat == WAV_FORMAT_PCM && bitsPerSample == 32) // 32-BIT INTEGER SAMPLE
 	{
-		sampleLength /= 4;
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength * 4))
+		sampleLength /= sizeof (int32_t);
+		if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 4, f) != 4)
+		if (fread(s->dataPtr, sampleLength, sizeof (int32_t), f) != sizeof (int32_t))
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		audioDataS32 = (int32_t *)tmpSmp.pek;
+		audioDataS32 = (int32_t *)s->dataPtr;
 
 		// stereo conversion
 		if (numChannels == 2)
@@ -448,33 +445,28 @@
 
 		normalizeSigned32Bit(audioDataS32, sampleLength);
 
-		// downscale to 16-bit (ultra fast method!)
+		ptr16 = (int16_t *)s->dataPtr;
+		for (i = 0; i < sampleLength; i++)
+			ptr16[i] = audioDataS32[i] >> 16;
 
-		audioDataS16 = (int16_t *)tmpSmp.pek;
-		audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words
-
-		for (i = 0; i < sampleLength; i++, audioDataS16_2++)
-			audioDataS16[i] = audioDataS16_2[i];
-
-		sampleLength *= 2;
-		tmpSmp.typ |= 16; // 16-bit
+		s->flags |= SAMPLE_16BIT;
 	}
 	else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 32) // 32-BIT FLOATING POINT SAMPLE
 	{
-		sampleLength /= 4;
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength * 4))
+		sampleLength /= sizeof (float);
+		if (!allocateSmpData(s, sampleLength * sizeof (float), false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 4, f) != 4)
+		if (fread(s->dataPtr, sampleLength, sizeof (float), f) != sizeof (float))
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		fAudioDataFloat = (float *)tmpSmp.pek;
+		fAudioDataFloat = (float *)s->dataPtr;
 
 		// stereo conversion
 		if (numChannels == 2)
@@ -517,29 +509,31 @@
 
 		normalize32BitFloatToSigned16Bit(fAudioDataFloat, sampleLength);
 
-		ptr16 = (int16_t *)tmpSmp.pek;
+		ptr16 = (int16_t *)s->dataPtr;
 		for (i = 0; i < sampleLength; i++)
-			ptr16[i] = (int16_t)fAudioDataFloat[i]; // should use SIMD if available
+		{
+			const int32_t smp32 = (const int32_t)fAudioDataFloat[i];
+			ptr16[i] = (int16_t)smp32;
+		}
 
-		sampleLength *= 2;
-		tmpSmp.typ |= 16; // 16-bit
+		s->flags |= SAMPLE_16BIT;
 	}
 	else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 64) // 64-BIT FLOATING POINT SAMPLE
 	{
-		sampleLength /= 8;
-		if (!allocateTmpSmpData(&tmpSmp, sampleLength * 8))
+		sampleLength /= sizeof (double);
+		if (!allocateSmpData(s, sampleLength * sizeof (double), false))
 		{
 			loaderMsgBox("Not enough memory!");
 			return false;
 		}
 
-		if (fread(tmpSmp.pek, sampleLength, 8, f) != 8)
+		if (fread(s->dataPtr, sampleLength, sizeof (double), f) != sizeof (double))
 		{
 			loaderMsgBox("General I/O error during loading! Is the file in use?");
 			return false;
 		}
 
-		dAudioDataDouble = (double *)tmpSmp.pek;
+		dAudioDataDouble = (double *)s->dataPtr;
 
 		// stereo conversion
 		if (numChannels == 2)
@@ -582,22 +576,28 @@
 
 		normalize64BitFloatToSigned16Bit(dAudioDataDouble, sampleLength);
 
-		ptr16 = (int16_t *)tmpSmp.pek;
+		ptr16 = (int16_t *)s->dataPtr;
 		for (i = 0; i < sampleLength; i++)
-			ptr16[i] = (int16_t)dAudioDataDouble[i]; // should use SIMD if available
+		{
+			const int32_t smp32 = (const int32_t)dAudioDataDouble[i];
+			ptr16[i] = (int16_t)smp32;
+		}
 
-		sampleLength *= 2;
-		tmpSmp.typ |= 16; // 16-bit
+		s->flags |= SAMPLE_16BIT;
 	}
 
-	reallocateTmpSmpData(&tmpSmp, sampleLength); // readjust memory needed
+	if (sampleLength > MAX_SAMPLE_LEN)
+		sampleLength = MAX_SAMPLE_LEN;
 
-	tuneSample(&tmpSmp, sampleRate, audio.linearPeriodsFlag);
+	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
+	reallocateSmpData(s, sampleLength, sample16Bit); // readjust memory needed
 
-	tmpSmp.vol = 64;
-	tmpSmp.pan = 128;
-	tmpSmp.len = sampleLength;
+	tuneSample(s, sampleRate, audio.linearPeriodsFlag);
 
+	s->volume = 64;
+	s->panning = 128;
+	s->length = sampleLength;
+
 	// ---- READ "smpl" chunk ----
 	if (smplPtr != 0 && smplLen > 52)
 	{
@@ -612,19 +612,14 @@
 
 			fread(&loopType, 4, 1, f);
 			fread(&loopStart, 4, 1, f);
-			fread(&loopEnd, 4, 1, f); loopEnd++;
+			fread(&loopEnd, 4, 1, f);
 
-			if (tmpSmp.typ & 16)
-			{
-				loopStart *= 2;
-				loopEnd *= 2;
-			}
-
+			loopEnd++;
 			if (loopEnd <= sampleLength)
 			{
-				tmpSmp.repS = loopStart;
-				tmpSmp.repL = loopEnd - loopStart;
-				tmpSmp.typ |= (loopType == 0) ? 1 : 2;
+				s->loopStart = loopStart;
+				s->loopLength = loopEnd - loopStart;
+				s->flags |= (loopType == 0) ? LOOP_FWD : LOOP_BIDI;
 			}
 		}
 	}
@@ -646,7 +641,7 @@
 			if (tmpPan > 255)
 				tmpPan = 255;
 
-			tmpSmp.pan = (uint8_t)tmpPan;
+			s->panning = (uint8_t)tmpPan;
 		}
 		else
 		{
@@ -659,7 +654,7 @@
 		if (tmpVol > 256)
 			tmpVol = 256;
 
-		tmpSmp.vol = (uint8_t)((tmpVol + 2) / 4); // 0..256 -> 0..64 (rounded)
+		s->volume = (uint8_t)((tmpVol + 2) / 4); // 0..256 -> 0..64 (rounded)
 	}
 	// ---------------------------
 
@@ -667,20 +662,11 @@
 	if (inamPtr != 0 && inamLen > 0)
 	{
 		fseek(f, inamPtr, SEEK_SET);
+		if (inamLen > 22)
+			inamLen = 22;
 
-		uint32_t len = 22;
-		if (len > inamLen)
-			len = inamLen;
-
-		memset(tmpSmp.name, 0, sizeof (tmpSmp.name));
-		for (i = 0; i < len; i++)
-		{
-			char chr = (char)fgetc(f);
-			if (chr == '\0')
-				break;
-
-			tmpSmp.name[i] = chr;
-		}
+		fread(s->name, 1, inamLen, f);
+		s->name[22] = '\0';
 
 		smpFilenameSet = true;
 	}
binary files /dev/null b/vs2019_project/ft2-clone/16.wav differ
--- a/vs2019_project/ft2-clone/ft2-clone.vcxproj
+++ b/vs2019_project/ft2-clone/ft2-clone.vcxproj
@@ -92,7 +92,7 @@
       <Optimization>MaxSpeed</Optimization>
       <AdditionalIncludeDirectories>
       </AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions>
+      <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <StringPooling>true</StringPooling>
@@ -100,7 +100,6 @@
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
-      <FloatingPointModel>Fast</FloatingPointModel>
       <OmitFramePointers>true</OmitFramePointers>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
@@ -108,6 +107,7 @@
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
       <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
+      <FloatingPointModel>Fast</FloatingPointModel>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>
@@ -142,7 +142,7 @@
       <Optimization>MaxSpeed</Optimization>
       <AdditionalIncludeDirectories>
       </AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions>
+      <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <StringPooling>true</StringPooling>
@@ -149,7 +149,6 @@
       <MinimalRebuild>false</MinimalRebuild>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
-      <FloatingPointModel>Fast</FloatingPointModel>
       <OmitFramePointers>true</OmitFramePointers>
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
       <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
@@ -157,6 +156,7 @@
       <DebugInformationFormat>None</DebugInformationFormat>
       <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
       <RuntimeTypeInfo>false</RuntimeTypeInfo>
+      <FloatingPointModel>Fast</FloatingPointModel>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>
@@ -199,11 +199,11 @@
       <WarningLevel>Level4</WarningLevel>
       <AdditionalIncludeDirectories>
       </AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions>
-      <FloatingPointModel>Fast</FloatingPointModel>
+      <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions>
       <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
       <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <FloatingPointModel>Fast</FloatingPointModel>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>
@@ -238,13 +238,13 @@
       <WarningLevel>Level4</WarningLevel>
       <AdditionalIncludeDirectories>
       </AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions>
-      <FloatingPointModel>Fast</FloatingPointModel>
+      <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions>
       <StringPooling>
       </StringPooling>
       <OmitFramePointers>false</OmitFramePointers>
       <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <FloatingPointModel>Fast</FloatingPointModel>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>
@@ -318,8 +318,6 @@
     <ClCompile Include="..\..\src\ft2_sample_ed.c" />
     <ClCompile Include="..\..\src\ft2_sample_loader.c" />
     <ClCompile Include="..\..\src\ft2_sample_saver.c" />
-    <ClCompile Include="..\..\src\ft2_scopedraw.c" />
-    <ClCompile Include="..\..\src\ft2_scopes.c" />
     <ClCompile Include="..\..\src\ft2_scrollbars.c" />
     <ClCompile Include="..\..\src\ft2_structs.c" />
     <ClCompile Include="..\..\src\ft2_sysreqs.c" />
@@ -338,6 +336,19 @@
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_instr.c" />
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_looppins.c" />
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_scopes.c" />
+    <ClCompile Include="..\..\src\libflac\bitmath.c" />
+    <ClCompile Include="..\..\src\libflac\bitreader.c" />
+    <ClCompile Include="..\..\src\libflac\crc.c" />
+    <ClCompile Include="..\..\src\libflac\fixed.c" />
+    <ClCompile Include="..\..\src\libflac\format.c" />
+    <ClCompile Include="..\..\src\libflac\lpc.c" />
+    <ClCompile Include="..\..\src\libflac\md5.c" />
+    <ClCompile Include="..\..\src\libflac\memory.c" />
+    <ClCompile Include="..\..\src\libflac\metadata_iterators.c" />
+    <ClCompile Include="..\..\src\libflac\metadata_object.c" />
+    <ClCompile Include="..\..\src\libflac\stream_decoder.c" />
+    <ClCompile Include="..\..\src\libflac\window.c" />
+    <ClCompile Include="..\..\src\libflac\windows_unicode_filenames.c" />
     <ClCompile Include="..\..\src\mixer\ft2_windowed_sinc.c" />
     <ClCompile Include="..\..\src\mixer\ft2_mix.c" />
     <ClCompile Include="..\..\src\mixer\ft2_center_mix.c" />
@@ -359,12 +370,11 @@
       <ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Sync</ExceptionHandling>
       <ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Sync</ExceptionHandling>
       <ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Sync</ExceptionHandling>
-      <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4245;4996;4267</DisableSpecificWarnings>
-      <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4245;4996;4267</DisableSpecificWarnings>
-      <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4245;4996;4267</DisableSpecificWarnings>
-      <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4245;4996;4267</DisableSpecificWarnings>
     </ClCompile>
+    <ClCompile Include="..\..\src\scopes\ft2_scopedraw.c" />
+    <ClCompile Include="..\..\src\scopes\ft2_scopes.c" />
     <ClCompile Include="..\..\src\smploaders\ft2_load_aiff.c" />
+    <ClCompile Include="..\..\src\smploaders\ft2_load_flac.c" />
     <ClCompile Include="..\..\src\smploaders\ft2_load_iff.c" />
     <ClCompile Include="..\..\src\smploaders\ft2_load_raw.c" />
     <ClCompile Include="..\..\src\smploaders\ft2_load_wav.c" />
@@ -376,6 +386,7 @@
     <ClInclude Include="..\..\src\ft2_bmp.h" />
     <ClInclude Include="..\..\src\ft2_checkboxes.h" />
     <ClInclude Include="..\..\src\ft2_config.h" />
+    <ClInclude Include="..\..\src\ft2_cpu.h" />
     <ClInclude Include="..\..\src\ft2_diskop.h" />
     <ClInclude Include="..\..\src\ft2_edit.h" />
     <ClInclude Include="..\..\src\ft2_events.h" />
@@ -402,7 +413,6 @@
     <ClInclude Include="..\..\src\ft2_sample_loader.h" />
     <ClInclude Include="..\..\src\ft2_sample_saver.h" />
     <ClInclude Include="..\..\src\ft2_scopedraw.h" />
-    <ClInclude Include="..\..\src\ft2_scopes.h" />
     <ClInclude Include="..\..\src\ft2_scrollbars.h" />
     <ClInclude Include="..\..\src\ft2_structs.h" />
     <ClInclude Include="..\..\src\ft2_sysreqs.h" />
@@ -419,6 +429,9 @@
     <ClInclude Include="..\..\src\mixer\ft2_silence_mix.h" />
     <ClInclude Include="..\..\src\rtmidi\RtMidi.h" />
     <ClInclude Include="..\..\src\rtmidi\rtmidi_c.h" />
+    <ClInclude Include="..\..\src\scopes\ft2_scopedraw.h" />
+    <ClInclude Include="..\..\src\scopes\ft2_scopes.h" />
+    <ClInclude Include="..\..\src\scopes\ft2_scope_macros.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\src\ft2-clone.rc" />
--- a/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters
+++ b/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters
@@ -11,7 +11,6 @@
     <ClCompile Include="..\..\src\ft2_edit.c" />
     <ClCompile Include="..\..\src\ft2_events.c" />
     <ClCompile Include="..\..\src\ft2_gui.c" />
-    <ClCompile Include="..\..\src\ft2_help.c" />
     <ClCompile Include="..\..\src\ft2_inst_ed.c" />
     <ClCompile Include="..\..\src\ft2_keyboard.c" />
     <ClCompile Include="..\..\src\ft2_main.c" />
@@ -31,8 +30,6 @@
     <ClCompile Include="..\..\src\ft2_sample_loader.c" />
     <ClCompile Include="..\..\src\ft2_sample_saver.c" />
     <ClCompile Include="..\..\src\ft2_sampling.c" />
-    <ClCompile Include="..\..\src\ft2_scopedraw.c" />
-    <ClCompile Include="..\..\src\ft2_scopes.c" />
     <ClCompile Include="..\..\src\ft2_scrollbars.c" />
     <ClCompile Include="..\..\src\ft2_structs.c" />
     <ClCompile Include="..\..\src\ft2_sysreqs.c" />
@@ -49,31 +46,31 @@
       <Filter>rtmidi</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_fonts.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_gui.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_instr.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_logo.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_looppins.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_midi.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_mouse.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_nibbles.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\gfxdata\ft2_bmp_scopes.c">
-      <Filter>graphics</Filter>
+      <Filter>libflac\graphics</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\mixer\ft2_mix.c">
       <Filter>mixer</Filter>
@@ -117,6 +114,55 @@
     <ClCompile Include="..\..\src\smploaders\ft2_load_wav.c">
       <Filter>smploaders</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\smploaders\ft2_load_flac.c">
+      <Filter>smploaders</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\bitmath.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\bitreader.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\crc.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\fixed.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\format.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\lpc.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\md5.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\memory.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\metadata_iterators.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\metadata_object.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\stream_decoder.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\libflac\window.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\ft2_help.c" />
+    <ClCompile Include="..\..\src\libflac\windows_unicode_filenames.c">
+      <Filter>libflac</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\scopes\ft2_scopedraw.c">
+      <Filter>scopes</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\scopes\ft2_scopes.c">
+      <Filter>scopes</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\src\rtmidi\RtMidi.h">
@@ -221,9 +267,6 @@
     <ClInclude Include="..\..\src\ft2_scopedraw.h">
       <Filter>headers</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\ft2_scopes.h">
-      <Filter>headers</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\src\ft2_scrollbars.h">
       <Filter>headers</Filter>
     </ClInclude>
@@ -266,14 +309,23 @@
     <ClInclude Include="..\..\src\mixer\ft2_windowed_sinc.h">
       <Filter>mixer</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\scopes\ft2_scope_macros.h">
+      <Filter>scopes</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\scopes\ft2_scopedraw.h">
+      <Filter>scopes</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\scopes\ft2_scopes.h">
+      <Filter>scopes</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\ft2_cpu.h">
+      <Filter>headers</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="headers">
       <UniqueIdentifier>{559f399e-331e-4688-8a7d-328ca6739456}</UniqueIdentifier>
     </Filter>
-    <Filter Include="graphics">
-      <UniqueIdentifier>{c6fad604-509b-4072-b181-d47835f08428}</UniqueIdentifier>
-    </Filter>
     <Filter Include="mixer">
       <UniqueIdentifier>{5c40c417-c4bb-4cf2-b71b-c557bf0a86cd}</UniqueIdentifier>
     </Filter>
@@ -285,6 +337,15 @@
     </Filter>
     <Filter Include="rtmidi">
       <UniqueIdentifier>{95e882a1-e589-4684-a3cb-48e0a1d073aa}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="libflac">
+      <UniqueIdentifier>{13e9047f-28ee-414f-9e23-4ad54deb281e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="scopes">
+      <UniqueIdentifier>{8ac888cf-e1a5-4633-bcca-240a8eae2e23}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="libflac\graphics">
+      <UniqueIdentifier>{c6fad604-509b-4072-b181-d47835f08428}</UniqueIdentifier>
     </Filter>
   </ItemGroup>
   <ItemGroup>