diff -rNu rb/apps/SOURCES rb_hes/apps/SOURCES
--- rb/apps/SOURCES	2011-04-17 15:49:42.634405400 +0800
+++ rb_hes/apps/SOURCES	2011-05-15 17:10:24.375412900 +0800
@@ -224,6 +224,8 @@
 metadata/au.c
 metadata/vox.c
 metadata/tta.c
+metadata/hes.c
+metadata/gme_m3u.c
 #endif
 #ifdef HAVE_TAGCACHE
 tagcache.c
diff -rNu rb/apps/codecs/SOURCES rb_hes/apps/codecs/SOURCES
--- rb/apps/codecs/SOURCES	2011-04-17 15:49:33.825901600 +0800
+++ rb_hes/apps/codecs/SOURCES	2011-05-15 17:09:04.938869300 +0800
@@ -33,6 +33,7 @@
 wav64.c
 tta.c
 wmapro.c
+hes.c
 
 #ifdef HAVE_RECORDING
 
diff -rNu rb/apps/codecs/codecs.make rb_hes/apps/codecs/codecs.make
--- rb/apps/codecs/codecs.make	2011-04-17 15:49:33.815901000 +0800
+++ rb_hes/apps/codecs/codecs.make	2011-05-15 17:09:04.942869600 +0800
@@ -43,6 +43,7 @@
 include $(APPSDIR)/codecs/libatrac/libatrac.make
 include $(APPSDIR)/codecs/libpcm/libpcm.make
 include $(APPSDIR)/codecs/libtta/libtta.make
+include $(APPSDIR)/codecs/libhes/libhes.make
 
 # compile flags for codecs
 CODECFLAGS = $(CFLAGS) -fstrict-aliasing -I$(APPSDIR)/codecs \
@@ -93,6 +94,7 @@
 $(CODECDIR)/vox.codec : $(CODECDIR)/libpcm.a
 $(CODECDIR)/wav64.codec : $(CODECDIR)/libpcm.a
 $(CODECDIR)/tta.codec : $(CODECDIR)/libtta.a
+$(CODECDIR)/hes.codec : $(CODECDIR)/libhes.a
 
 $(CODECS): $(CODECLIB) # this must be last in codec dependency list
 
diff -rNu rb/apps/codecs/hes.c rb_hes/apps/codecs/hes.c
--- rb/apps/codecs/hes.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/hes.c	2011-05-15 17:09:04.945869700 +0800
@@ -0,0 +1,111 @@
+
+/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */
+
+#include <string.h>
+#include "codeclib.h"
+#include "libhes/hes_emu.h"
+ 
+CODEC_HEADER
+
+/* Maximum number of bytes to process in one iteration */
+#define CHUNK_SIZE (1024*2)
+
+static int16_t samples[CHUNK_SIZE] IBSS_ATTR;
+static struct Hes_Emu hes_emu IDATA_ATTR CACHEALIGN_ATTR;
+
+/****************** rockbox interface ******************/
+
+static void set_codec_track(int t) {
+    Hes_start_track(&hes_emu, t); 
+
+    /* for REPEAT_ONE we disable track limits */
+    if (ci->global_settings->repeat_mode != REPEAT_ONE) {
+        Track_set_fade(&hes_emu, Track_get_length( &hes_emu, t ), 4000);
+    }
+    ci->id3->elapsed = t*1000; /* t is track no to display */
+}
+
+/* this is the codec entry point */
+enum codec_status codec_main(enum codec_entry_call_reason reason)
+{
+    if (reason == CODEC_LOAD)
+    {
+        /* we only render 16 bits */
+        ci->configure(DSP_SET_SAMPLE_DEPTH, 16);
+
+        /* 44 Khz, Interleaved stereo */
+        ci->configure(DSP_SET_FREQUENCY, 44100);
+        ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
+
+        Hes_init(&hes_emu);
+        Hes_set_sample_rate(&hes_emu, 44100);
+    }
+    return CODEC_OK;
+}
+
+enum codec_status codec_run(void)
+{
+    blargg_err_t err;
+    uint8_t *buf;
+    size_t n;
+    int track=0;
+    intptr_t param;
+
+    DEBUGF("HES: next_track\n");
+    if (codec_init()) {
+        return CODEC_ERROR;
+    }
+
+    codec_set_replaygain(ci->id3);
+
+    /* Read the entire file */
+    DEBUGF("HES: request file\n");
+    ci->seek_buffer(0);
+    buf = ci->request_buffer(&n, ci->filesize);
+    if (!buf || n < (size_t)ci->filesize) {
+        DEBUGF("HES: file load failed\n");
+        return CODEC_ERROR;
+    }
+
+    if ((err = Hes_load(&hes_emu, buf, ci->filesize))) {
+        DEBUGF("HES: Hes_load failed (%s)\n", err);
+        return CODEC_ERROR;
+    }
+
+    char *p = strrchr(ci->id3->path, '.');
+    if (p) {
+        strcpy(p, ".m3u");
+        Hes_load_m3u(&hes_emu, ci->id3->path);
+
+        // Copy back hes extension
+        strcpy(p, ".hes");
+    }
+
+next_tune:
+    set_codec_track(track);
+
+    /* The main decoder loop */
+    while ( 1 ) {
+        enum codec_command_action action = ci->get_command(&param);
+        if (action == CODEC_ACTION_HALT)
+            break;
+
+        if (action == CODEC_ACTION_SEEK_TIME) {
+            track = param/1000;
+            ci->seek_complete();
+            if (track >= hes_emu.track_count) break;
+            goto next_tune;
+        }
+
+        /* Generate audio buffer */
+        err = Hes_play(&hes_emu, CHUNK_SIZE, samples);
+        if (err || hes_emu.track_ended) {
+            track++;
+            if (track >= hes_emu.track_count) break;
+            goto next_tune;
+        }
+
+        ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
+    }
+    return CODEC_OK;
+}
diff -rNu rb/apps/codecs/lib/codeclib.c rb_hes/apps/codecs/lib/codeclib.c
--- rb/apps/codecs/lib/codeclib.c	2011-05-13 23:36:52.897119800 +0800
+++ rb_hes/apps/codecs/lib/codeclib.c	2011-05-15 17:09:04.950870000 +0800
@@ -95,6 +95,11 @@
     return(ci->strlen(s));
 }
 
+char *strrchr(const char *s, int c)
+{
+    return(ci->strrchr(s, c));
+}
+
 char *strcpy(char *dest, const char *src)
 {
     return(ci->strcpy(dest,src));
@@ -110,6 +115,11 @@
     return(ci->strcmp(s1,s2));
 }
 
+int strcasecmp(const char *s1, const char *s2)
+{
+    return(ci->strcasecmp(s1, s2));
+}
+
 void *memcpy(void *dest, const void *src, size_t n)
 {
     return(ci->memcpy(dest,src,n));
diff -rNu rb/apps/codecs/lib/codeclib.h rb_hes/apps/codecs/lib/codeclib.h
--- rb/apps/codecs/lib/codeclib.h	2011-05-08 21:58:23.549935900 +0800
+++ rb_hes/apps/codecs/lib/codeclib.h	2011-05-15 17:09:04.955870300 +0800
@@ -54,6 +54,7 @@
 void *memmove(void *s1, const void *s2, size_t n);
 
 size_t strlen(const char *s);
+char *strrchr(const char *s, int c);
 char *strcpy(char *dest, const char *src);
 char *strcat(char *dest, const char *src);
 
@@ -61,6 +62,7 @@
  * breaks if we write down strcmp's prototype */
 #undef strcmp
 int strcmp(const char *s1, const char *s2);
+int strcasecmp(const char *s1, const char *s2);
 
 void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
 
diff -rNu rb/apps/codecs/libhes/SOURCES rb_hes/apps/codecs/libhes/SOURCES
--- rb/apps/codecs/libhes/SOURCES	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/SOURCES	2011-05-15 17:09:04.957870400 +0800
@@ -0,0 +1,8 @@
+blip_buffer.c
+hes_apu.c
+hes_apu_adpcm.c
+hes_cpu.c
+hes_emu.c
+multi_buffer.c
+rom_data.c
+m3u_playlist.c
diff -rNu rb/apps/codecs/libhes/blargg_common.h rb_hes/apps/codecs/libhes/blargg_common.h
--- rb/apps/codecs/libhes/blargg_common.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/blargg_common.h	2011-05-15 17:09:04.961870700 +0800
@@ -0,0 +1,150 @@
+// Sets up common environment for Shay Green's libraries.
+// To change configuration options, modify blargg_config.h, not this file.
+
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+#undef BLARGG_COMMON_H
+// allow blargg_config.h to #include blargg_common.h
+#include "blargg_config.h"
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+#if defined(CPU_ARM) || !defined(ROCKBOX)
+#if (CONFIG_CPU != PP5002) || defined(WIN32)
+    #undef  ICODE_ATTR
+    #define ICODE_ATTR
+
+    #undef  IDATA_ATTR
+    #define IDATA_ATTR
+
+    #undef  ICONST_ATTR
+    #define ICONST_ATTR
+
+    #undef  IBSS_ATTR
+    #define IBSS_ATTR
+#endif
+#endif
+
+// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
+#ifndef STATIC_CAST
+	#define STATIC_CAST(T,expr) ((T) (expr))
+#endif
+
+// blargg_err_t (0 on success, otherwise error string)
+#ifndef blargg_err_t
+	typedef const char* blargg_err_t;
+#endif
+
+#define BLARGG_4CHAR( a, b, c, d ) \
+	((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF))
+
+// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
+#ifndef BOOST_STATIC_ASSERT
+	#ifdef _MSC_VER
+		// MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified
+		#define BOOST_STATIC_ASSERT( expr ) \
+			void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
+	#else
+		// Some other compilers fail when declaring same function multiple times in class,
+		// so differentiate them by line
+		#define BOOST_STATIC_ASSERT( expr ) \
+			void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
+	#endif
+#endif
+
+// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1,
+// compiler is assumed to support bool. If undefined, availability is determined.
+#ifndef BLARGG_COMPILER_HAS_BOOL
+	#if defined (__MWERKS__)
+		#if !__option(bool)
+			#define BLARGG_COMPILER_HAS_BOOL 0
+		#endif
+	#elif defined (_MSC_VER)
+		#if _MSC_VER < 1100
+			#define BLARGG_COMPILER_HAS_BOOL 0
+		#endif
+	#elif defined (__GNUC__)
+		// supports bool
+	#elif __cplusplus < 199711
+		#define BLARGG_COMPILER_HAS_BOOL 0
+	#endif
+#endif
+#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL
+	// If you get errors here, modify your blargg_config.h file
+	typedef int bool;
+	static bool true  = 1;
+	static bool false = 0;
+#endif
+
+// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
+#include <limits.h>
+
+#if INT_MAX >= 0x7FFFFFFF
+	typedef int blargg_long;
+#else
+	typedef long blargg_long;
+#endif
+
+#if UINT_MAX >= 0xFFFFFFFF
+	typedef unsigned blargg_ulong;
+#else
+	typedef unsigned long blargg_ulong;
+#endif
+
+// int8_t etc.
+
+
+// ROCKBOX: If defined, use <codeclib.h> for int_8_t etc
+#if defined (ROCKBOX)
+	#include <codecs/lib/codeclib.h>
+// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
+#elif defined (HAVE_STDINT_H)
+	#include <stdint.h>
+	#define BOOST
+
+// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
+#elif defined (HAVE_INTTYPES_H)
+	#include <inttypes.h>
+	#define BOOST
+
+#else
+	#if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F
+		typedef signed char     int8_t;
+		typedef unsigned char   uint8_t;
+	#else
+		// No suitable 8-bit type available
+		typedef struct see_blargg_common_h int8_t;
+		typedef struct see_blargg_common_h uint8_t;
+	#endif
+		
+	#if USHRT_MAX == 0xFFFF
+		typedef short           int16_t;
+		typedef unsigned short  uint16_t;
+	#else
+		// No suitable 16-bit type available
+		typedef struct see_blargg_common_h int16_t;
+		typedef struct see_blargg_common_h uint16_t;
+	#endif
+		
+	#if ULONG_MAX == 0xFFFFFFFF
+		typedef long            int32_t;
+		typedef unsigned long   uint32_t;
+	#elif UINT_MAX == 0xFFFFFFFF
+		typedef int             int32_t;
+		typedef unsigned int    uint32_t;
+	#else
+		// No suitable 32-bit type available
+		typedef struct see_blargg_common_h int32_t;
+		typedef struct see_blargg_common_h uint32_t;
+	#endif
+#endif
+
+#endif
+#endif
diff -rNu rb/apps/codecs/libhes/blargg_config.h rb_hes/apps/codecs/libhes/blargg_config.h
--- rb/apps/codecs/libhes/blargg_config.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/blargg_config.h	2011-05-15 17:09:04.964870800 +0800
@@ -0,0 +1,30 @@
+// Library configuration. Modify this file as necessary.
+
+#ifndef BLARGG_CONFIG_H
+#define BLARGG_CONFIG_H
+
+// Uncomment to enable platform-specific optimizations
+//#define BLARGG_NONPORTABLE 1
+
+// Uncomment if automatic byte-order determination doesn't work
+#ifdef ROCKBOX_BIG_ENDIAN
+	#define BLARGG_BIG_ENDIAN 1
+#endif
+
+// Uncomment if you get errors in the bool section of blargg_common.h
+#define BLARGG_COMPILER_HAS_BOOL 1
+
+// Uncomment to disable m3u playlist support
+// #define GME_DISABLE_M3U_PLAYLIST 1
+
+// To handle undefined reference to assert
+#define NDEBUG 1
+
+// Use standard config.h if present
+#define HAVE_CONFIG_H 1
+
+#ifdef HAVE_CONFIG_H
+	#include "config.h"
+#endif
+
+#endif
diff -rNu rb/apps/codecs/libhes/blargg_endian.h rb_hes/apps/codecs/libhes/blargg_endian.h
--- rb/apps/codecs/libhes/blargg_endian.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/blargg_endian.h	2011-05-15 17:09:04.968871100 +0800
@@ -0,0 +1,147 @@
+// CPU Byte Order Utilities
+
+// Game_Music_Emu 0.5.2
+#ifndef BLARGG_ENDIAN
+#define BLARGG_ENDIAN
+
+#include "blargg_common.h"
+
+// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+		defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+	#define BLARGG_CPU_X86 1
+	#define BLARGG_CPU_CISC 1
+#endif
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc)
+	#define BLARGG_CPU_POWERPC 1
+#endif
+
+// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only
+// one may be #defined to 1. Only needed if something actually depends on byte order.
+#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN)
+#ifdef __GLIBC__
+	// GCC handles this for us
+	#include <endian.h>
+	#if __BYTE_ORDER == __LITTLE_ENDIAN
+		#define BLARGG_LITTLE_ENDIAN 1
+	#elif __BYTE_ORDER == __BIG_ENDIAN
+		#define BLARGG_BIG_ENDIAN 1
+	#endif
+#else
+
+#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || defined (BLARGG_CPU_X86) || \
+		(defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234)
+	#define BLARGG_LITTLE_ENDIAN 1
+#endif
+
+#if defined (MSB_FIRST)     || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \
+	defined (__mips__)      || defined (__sparc__)      ||  defined (BLARGG_CPU_POWERPC) || \
+	(defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321)
+	#define BLARGG_BIG_ENDIAN 1
+#else
+	// No endian specified; assume little-endian, since it's most common
+	#define BLARGG_LITTLE_ENDIAN 1
+#endif
+#endif
+#endif
+
+#if defined (BLARGG_LITTLE_ENDIAN) && defined(BLARGG_BIG_ENDIAN)
+	#undef BLARGG_LITTLE_ENDIAN
+	#undef BLARGG_BIG_ENDIAN
+#endif
+
+static inline void blargg_verify_byte_order( void )
+{
+	#ifndef NDEBUG
+		#if BLARGG_BIG_ENDIAN
+			volatile int i = 1;
+			assert( *(volatile char*) &i == 0 );
+		#elif BLARGG_LITTLE_ENDIAN
+			volatile int i = 1;
+			assert( *(volatile char*) &i != 0 );
+		#endif
+	#endif
+}
+
+static inline unsigned get_le16( void const* p ) {
+	return  ((unsigned char const*) p) [1] * 0x100u +
+			((unsigned char const*) p) [0];
+}
+static inline unsigned get_be16( void const* p ) {
+	return  ((unsigned char const*) p) [0] * 0x100u +
+			((unsigned char const*) p) [1];
+}
+static inline blargg_ulong get_le32( void const* p ) {
+	return  ((unsigned char const*) p) [3] * 0x01000000u +
+			((unsigned char const*) p) [2] * 0x00010000u +
+			((unsigned char const*) p) [1] * 0x00000100u +
+			((unsigned char const*) p) [0];
+}
+static inline blargg_ulong get_be32( void const* p ) {
+	return  ((unsigned char const*) p) [0] * 0x01000000u +
+			((unsigned char const*) p) [1] * 0x00010000u +
+			((unsigned char const*) p) [2] * 0x00000100u +
+			((unsigned char const*) p) [3];
+}
+static inline void set_le16( void* p, unsigned n ) {
+	((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [0] = (unsigned char) n;
+}
+static inline void set_be16( void* p, unsigned n ) {
+	((unsigned char*) p) [0] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [1] = (unsigned char) n;
+}
+static inline void set_le32( void* p, blargg_ulong n ) {
+	((unsigned char*) p) [3] = (unsigned char) (n >> 24);
+	((unsigned char*) p) [2] = (unsigned char) (n >> 16);
+	((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [0] = (unsigned char) n;
+}
+static inline void set_be32( void* p, blargg_ulong n ) {
+	((unsigned char*) p) [0] = (unsigned char) (n >> 24);
+	((unsigned char*) p) [1] = (unsigned char) (n >> 16);
+	((unsigned char*) p) [2] = (unsigned char) (n >> 8);
+	((unsigned char*) p) [3] = (unsigned char) n;
+}
+
+#if defined(BLARGG_NONPORTABLE)
+	// Optimized implementation if byte order is known
+	#if defined(BLARGG_LITTLE_ENDIAN)
+		#define GET_LE16( addr )        (*(BOOST::uint16_t*) (addr))
+		#define GET_LE32( addr )        (*(BOOST::uint32_t*) (addr))
+		#define SET_LE16( addr, data )  (void) (*(BOOST::uint16_t*) (addr) = (data))
+		#define SET_LE32( addr, data )  (void) (*(BOOST::uint32_t*) (addr) = (data))
+	#elif defined(BLARGG_BIG_ENDIAN)
+		#define GET_BE16( addr )        (*(BOOST::uint16_t*) (addr))
+		#define GET_BE32( addr )        (*(BOOST::uint32_t*) (addr))
+		#define SET_BE16( addr, data )  (void) (*(BOOST::uint16_t*) (addr) = (data))
+		#define SET_BE32( addr, data )  (void) (*(BOOST::uint32_t*) (addr) = (data))
+	#endif
+	
+	#if defined(BLARGG_CPU_POWERPC) && defined (__MWERKS__)
+		// PowerPC has special byte-reversed instructions
+		// to do: assumes that PowerPC is running in big-endian mode
+		// to do: implement for other compilers which don't support these macros
+		#define GET_LE16( addr )        (__lhbrx( (addr), 0 ))
+		#define GET_LE32( addr )        (__lwbrx( (addr), 0 ))
+		#define SET_LE16( addr, data )  (__sthbrx( (data), (addr), 0 ))
+		#define SET_LE32( addr, data )  (__stwbrx( (data), (addr), 0 ))
+	#endif
+#endif
+
+#ifndef GET_LE16
+	#define GET_LE16( addr )        get_le16( addr )
+	#define GET_LE32( addr )        get_le32( addr )
+	#define SET_LE16( addr, data )  set_le16( addr, data )
+	#define SET_LE32( addr, data )  set_le32( addr, data )
+#endif
+
+#ifndef GET_BE16
+	#define GET_BE16( addr )        get_be16( addr )
+	#define GET_BE32( addr )        get_be32( addr )
+	#define SET_BE16( addr, data )  set_be16( addr, data )
+	#define SET_BE32( addr, data )  set_be32( addr, data )
+#endif
+
+#endif
diff -rNu rb/apps/codecs/libhes/blargg_source.h rb_hes/apps/codecs/libhes/blargg_source.h
--- rb/apps/codecs/libhes/blargg_source.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/blargg_source.h	2011-05-15 17:09:04.971871200 +0800
@@ -0,0 +1,66 @@
+// Included at the beginning of library source files, after all other #include lines
+#ifndef BLARGG_SOURCE_H
+#define BLARGG_SOURCE_H
+
+// If debugging is enabled, abort program if expr is false. Meant for checking
+// internal state and consistency. A failed assertion indicates a bug in the module.
+// void assert( bool expr );
+#include <assert.h>
+
+// If debugging is enabled and expr is false, abort program. Meant for checking
+// caller-supplied parameters and operations that are outside the control of the
+// module. A failed requirement indicates a bug outside the module.
+// void require( bool expr );
+#undef require
+#define require( expr ) assert( expr )
+
+// Like printf() except output goes to debug log file. Might be defined to do
+// nothing (not even evaluate its arguments).
+// void dprintf( const char* format, ... );
+#if defined(ROCKBOX)
+#define dprintf DEBUGF
+#else
+static inline void blargg_dprintf_( const char* fmt, ... ) { }
+#undef dprintf
+#define dprintf (1) ? (void) 0 : blargg_dprintf_
+#endif
+
+// If enabled, evaluate expr and if false, make debug log entry with source file
+// and line. Meant for finding situations that should be examined further, but that
+// don't indicate a problem. In all cases, execution continues normally.
+#undef check
+#define check( expr ) ((void) 0)
+
+// If expr yields error string, return it from current function, otherwise continue.
+#undef RETURN_ERR
+#define RETURN_ERR( expr ) do {                         \
+		blargg_err_t blargg_return_err_ = (expr);               \
+		if ( blargg_return_err_ ) return blargg_return_err_;    \
+	} while ( 0 )
+
+// If ptr is 0, return out of memory error string.
+#undef CHECK_ALLOC
+#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
+
+#ifndef max
+	#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#ifndef min
+	#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+// TODO: good idea? bad idea?
+#undef byte
+#define byte byte_
+typedef unsigned char byte;
+
+// deprecated
+#define BLARGG_CHECK_ALLOC CHECK_ALLOC
+#define BLARGG_RETURN_ERR RETURN_ERR
+
+// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
+#ifdef BLARGG_SOURCE_BEGIN
+	#include BLARGG_SOURCE_BEGIN
+#endif
+
+#endif
diff -rNu rb/apps/codecs/libhes/blip_buffer.c rb_hes/apps/codecs/libhes/blip_buffer.c
--- rb/apps/codecs/libhes/blip_buffer.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/blip_buffer.c	2011-05-15 17:09:04.975871500 +0800
@@ -0,0 +1,282 @@
+// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+
+#include "blip_buffer.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+	#include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
+
+void Blip_init( struct Blip_Buffer* this )
+{
+	this->factor_       = LONG_MAX;
+	this->offset_       = 0;
+	this->buffer_size_  = 0;
+	this->sample_rate_  = 0;
+	this->reader_accum_ = 0;
+	this->bass_shift_   = 0;
+	this->clock_rate_   = 0;
+	this->bass_freq_    = 16;
+	this->length_       = 0;
+	
+	// assumptions code makes about implementation-defined features
+	#ifndef NDEBUG
+		// right shift of negative value preserves sign
+		buf_t_ i = -0x7FFFFFFE;
+		assert( (i >> 1) == -0x3FFFFFFF );
+		
+		// casting to short truncates to 16 bits and sign-extends
+		i = 0x18000;
+		assert( (short) i == -0x8000 );
+	#endif
+}
+
+void Blip_stop( struct Blip_Buffer* this )
+{
+	if ( this->buffer_size_ != silent_buf_size )
+		free( this->buffer_ );
+}
+
+void Blip_clear( struct Blip_Buffer* this, int entire_buffer )
+{
+	this->offset_      = 0;
+	this->reader_accum_ = 0;
+	this->modified_    = 0;
+	if ( this->buffer_ )
+	{
+		long count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
+		memset( this->buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) );
+	}
+}
+
+blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, long new_rate, int msec )
+{
+	if ( this->buffer_size_ == silent_buf_size )
+	{
+		assert( 0 );
+		return "Internal (tried to resize Silent_Blip_Buffer)";
+	}
+	
+	// start with maximum length that resampled time can represent
+	long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
+	if ( msec != blip_max_length )
+	{
+		long s = (new_rate * (msec + 1) + 999) / 1000;
+		if ( s < new_size )
+			new_size = s;
+		else
+			assert( 0 ); // fails if requested buffer length exceeds limit
+	}
+	
+	if ( new_size > blip_buffer_max )
+		return "Out of memory";
+	
+	this->buffer_size_ = new_size;
+	assert( this->buffer_size_ != silent_buf_size );
+	
+	// update things based on the sample rate
+	this->sample_rate_ = new_rate;
+	this->length_ = new_size * 1000 / new_rate - 1;
+	if ( msec )
+		assert( this->length_ == msec ); // ensure length is same as that passed in
+	if ( this->clock_rate_ )
+		Blip_set_clock_rate( this, this->clock_rate_ );
+	Blip_bass_freq( this, this->bass_freq_ );
+	
+	Blip_clear( this, 1 );
+	
+	return 0; // success
+}
+
+/* Not sure if this affects sound quality */
+#if defined(ROCKBOX)
+  #define floor
+#endif
+
+blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long rate )
+{
+	double ratio = (double) this->sample_rate_ / rate;
+	blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 );
+	assert( factor > 0 || !this->sample_rate_ ); // fails if clock/output ratio is too large
+	return (blip_resampled_time_t) factor;
+}
+
+void Blip_bass_freq( struct Blip_Buffer* this, int freq )
+{
+	this->bass_freq_ = freq;
+	int shift = 31;
+	if ( freq > 0 )
+	{
+		shift = 13;
+		long f = (freq << 16) / this->sample_rate_;
+		while ( (f >>= 1) && --shift ) { }
+	}
+	this->bass_shift_ = shift;
+}
+
+void Blip_end_frame( struct Blip_Buffer* this, blip_time_t t )
+{
+	this->offset_ += t * this->factor_;
+	assert( Blip_samples_avail( this ) <= (long) this->buffer_size_ ); // time outside buffer length
+}
+
+void Blip_remove_silence( struct Blip_Buffer* this, long count )
+{
+	assert( count <= Blip_samples_avail( this ) ); // tried to remove more samples than available
+	this->offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+}
+
+long Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
+{
+	unsigned long last_sample  = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
+	unsigned long first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
+	return (long) (last_sample - first_sample);
+}
+
+blip_time_t Blip_count_clocks( struct Blip_Buffer* this, long count )
+{
+	if ( !this->factor_ )
+	{
+		assert( 0 ); // sample rate and clock rates must be set first
+		return 0;
+	}
+	
+	if ( count > this->buffer_size_ )
+		count = this->buffer_size_;
+	blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+	return (blip_time_t) ((time - this->offset_ + this->factor_ - 1) / this->factor_);
+}
+
+void Blip_remove_samples( struct Blip_Buffer* this, long count )
+{
+	if ( count )
+	{
+		Blip_remove_silence( this, count );
+		
+		// copy remaining samples to beginning and clear old samples
+		long remain = Blip_samples_avail( this ) + blip_buffer_extra_;
+		memmove( this->buffer_, this->buffer_ + count, remain * sizeof *this->buffer_ );
+		memset( this->buffer_ + remain, 0, count * sizeof *this->buffer_ );
+	}
+}
+
+long Blip_read_samples( struct Blip_Buffer* this, blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo )
+{
+	long count = Blip_samples_avail( this );
+	if ( count > max_samples )
+		count = max_samples;
+	
+	if ( count )
+	{
+		int const bass = BLIP_READER_BASS( *this );
+		BLIP_READER_BEGIN( reader, *this );
+		
+		if ( !stereo )
+		{
+			blip_long n;
+			for ( n = count; n; --n )
+			{
+				blip_long s = BLIP_READER_READ( reader );
+				if ( (blip_sample_t) s != s )
+					s = 0x7FFF - (s >> 24);
+				*out++ = (blip_sample_t) s;
+				BLIP_READER_NEXT( reader, bass );
+			}
+		}
+		else
+		{
+			blip_long n;
+			for ( n = count; n; --n )
+			{
+				blip_long s = BLIP_READER_READ( reader );
+				if ( (blip_sample_t) s != s )
+					s = 0x7FFF - (s >> 24);
+				*out = (blip_sample_t) s;
+				out += 2;
+				BLIP_READER_NEXT( reader, bass );
+			}
+		}
+		BLIP_READER_END( reader, *this );
+		
+		Blip_remove_samples( this, count );
+	}
+	return count;
+}
+
+void Blip_mix_samples( struct Blip_Buffer* this,  blip_sample_t const* in, long count )
+{
+	if ( this->buffer_size_ == silent_buf_size )
+	{
+		assert( 0 );
+		return;
+	}
+	
+	buf_t_* out = this->buffer_ + (this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
+	
+	int const sample_shift = blip_sample_bits - 16;
+	int prev = 0;
+	while ( count-- )
+	{
+		blip_long s = (blip_long) *in++ << sample_shift;
+		*out += s - prev;
+		prev = s;
+		++out;
+	}
+	*out -= prev;
+}
+
+void Blip_set_modified( struct Blip_Buffer* this ) 
+{ 
+	this->modified_ = 1; 
+}
+
+int Blip_clear_modified( struct Blip_Buffer* this )
+{ 
+	int b = this->modified_;
+	this->modified_ = 0;
+	return b; 
+}
+
+blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t )
+{
+	return t * this->factor_;
+}
+
+blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
+{
+	return t * this->factor_ + this->offset_;
+}
+
+
+// Blip_Synth
+
+void Synth_init( struct Blip_Synth_* this )
+{
+	this->buf = 0;
+	this->last_amp = 0;
+	this->delta_factor = 0;
+}
+
+// Set overall volume of waveform
+void Synth_volume( struct Blip_Synth_* this, double v )
+{
+	this->delta_factor = (int) (v * (1L << blip_sample_bits) + 0.5);
+}
diff -rNu rb/apps/codecs/libhes/blip_buffer.h rb_hes/apps/codecs/libhes/blip_buffer.h
--- rb/apps/codecs/libhes/blip_buffer.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/blip_buffer.h	2011-05-15 17:09:04.980871800 +0800
@@ -0,0 +1,270 @@
+// Band-limited sound synthesis buffer
+
+// Blip_Buffer 0.4.1
+#ifndef BLIP_BUFFER_H
+#define BLIP_BUFFER_H
+
+#include <assert.h>
+
+	// internal
+	#include "blargg_common.h"
+	#if INT_MAX >= 0x7FFFFFFF
+		typedef int blip_long;
+		typedef unsigned blip_ulong;
+	#else
+		typedef long blip_long;
+		typedef unsigned long blip_ulong;
+	#endif
+
+// Time unit at source clock rate
+typedef blip_long blip_time_t;
+
+// Number of bits in resample ratio fraction. Higher values give a more accurate ratio
+// but reduce maximum buffer size.
+#ifndef BLIP_BUFFER_ACCURACY
+	#define BLIP_BUFFER_ACCURACY 16
+#endif
+
+// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
+// noticeable broadband noise when synthesizing high frequency square waves.
+// Affects size of Blip_Synth objects since they store the waveform directly.
+#ifndef BLIP_PHASE_BITS
+	#define BLIP_PHASE_BITS 8
+#endif
+
+// Output samples are 16-bit signed, with a range of -32768 to 32767
+typedef short blip_sample_t;
+enum { blip_sample_max = 32767 };
+enum { blip_widest_impulse_ = 16 };
+enum { blip_buffer_extra_ = blip_widest_impulse_ + 2 };
+enum { blip_res = 1 << BLIP_PHASE_BITS };
+enum { blip_max_length = 0 };
+enum { blip_default_length = 250 };
+
+// Maximun buffer size (48Khz, 50 ms)
+enum { blip_buffer_max = 2466 };
+enum { blip_sample_bits = 30 };
+
+typedef blip_time_t buf_t_;
+/* typedef const char* blargg_err_t; */
+typedef blip_ulong blip_resampled_time_t;
+
+struct Blip_Buffer {
+	blip_ulong factor_;
+	blip_resampled_time_t offset_;
+	buf_t_ buffer_ [blip_buffer_max];
+	blip_long buffer_size_;
+	blip_long reader_accum_;
+	int bass_shift_;
+
+	long sample_rate_;
+	long clock_rate_;
+	int bass_freq_;
+	int length_;
+	int modified_;
+};
+
+// not documented yet
+void Blip_set_modified( struct Blip_Buffer* this ); ICODE_ATTR
+int Blip_clear_modified( struct Blip_Buffer* this__ ); ICODE_ATTR
+void Blip_remove_silence( struct Blip_Buffer* this__, long count ); ICODE_ATTR
+blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this__, int t ); ICODE_ATTR
+blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this__, blip_time_t t ); ICODE_ATTR
+blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this__, long clock_rate ); ICODE_ATTR
+
+// Initializes Blip_Buffer structure
+void Blip_init( struct Blip_Buffer* this__ );
+
+// Stops (clear) Blip_Buffer structure
+void Blip_stop( struct Blip_Buffer* this__ );
+
+// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
+// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
+// isn't enough memory, returns error without affecting current buffer setup.
+blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this__, long samples_per_sec, int msec_length );
+
+// Set number of source time units per second
+static inline void Blip_set_clock_rate( struct Blip_Buffer* this__, long cps )
+{
+	this__->factor_ = Blip_clock_rate_factor( this__, this__->clock_rate_ = cps );
+}
+
+// End current time frame of specified duration and make its samples available
+// (along with any still-unread samples) for reading with read_samples(). Begins
+// a new time frame at the end of the current frame.
+void Blip_end_frame( struct Blip_Buffer* this__, blip_time_t time ); ICODE_ATTR
+
+// Read at most 'max_samples' out of buffer into 'dest', removing them from from
+// the buffer. Returns number of samples actually read and removed. If stereo is
+// true, increments 'dest' one extra time after writing each sample, to allow
+// easy interleving of two channels into a stereo output buffer.
+long Blip_read_samples( struct Blip_Buffer* this__, blip_sample_t* dest, long max_samples, int stereo ); ICODE_ATTR
+
+// Additional optional features
+
+// Current output sample rate
+static inline long Blip_sample_rate( struct Blip_Buffer* this__ )
+{
+	return this__->sample_rate_;
+}
+
+// Length of buffer, in milliseconds
+static inline int  Blip_length( struct Blip_Buffer* this__ )
+{
+	return this__->length_;
+}
+
+// Number of source time units per second
+static inline long Blip_clock_rate( struct Blip_Buffer* this__ )
+{
+	return this__->clock_rate_;
+}
+
+
+// Set frequency high-pass filter frequency, where higher values reduce bass more
+void Blip_bass_freq( struct Blip_Buffer* this__, int frequency );
+
+// Number of samples delay from synthesis to samples read out
+static inline int  Blip_output_latency( void )
+{ 
+	return blip_widest_impulse_ / 2;
+}
+
+// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
+// false, just clears out any samples waiting rather than the entire buffer.
+void Blip_clear( struct Blip_Buffer* this__, int entire_buffer );
+
+// Number of samples available for reading with read_samples()
+static inline long Blip_samples_avail( struct Blip_Buffer* this__ )
+{ 
+	return (long) (this__->offset_ >> BLIP_BUFFER_ACCURACY);
+}
+
+// Remove 'count' samples from those waiting to be read
+void Blip_remove_samples( struct Blip_Buffer* this__, long count ); ICODE_ATTR
+
+// Experimental features
+
+// Count number of clocks needed until 'count' samples will be available.
+// If buffer can't even hold 'count' samples, returns number of clocks until
+// buffer becomes full.
+blip_time_t Blip_count_clocks( struct Blip_Buffer* this__, long count ); ICODE_ATTR
+
+// Number of raw samples that can be mixed within frame of specified duration.
+long Blip_count_samples( struct Blip_Buffer* this__, blip_time_t duration ); ICODE_ATTR
+
+// Mix 'count' samples from 'buf' into buffer.
+void Blip_mix_samples( struct Blip_Buffer* this__, blip_sample_t const* buf, long count ); ICODE_ATTR
+
+// Range specifies the greatest expected change in amplitude. Calculate it
+// by finding the difference between the maximum and minimum expected
+// amplitudes (max - min).
+
+struct Blip_Synth_ {
+	struct Blip_Buffer* buf;
+	int last_amp;
+	int delta_factor;
+};
+
+// Initializes Blip_Synth structure
+void Synth_init( struct Blip_Synth_* this__ );
+
+// Set overall volume of waveform
+void Synth_volume( struct Blip_Synth_* this__, double v ); ICODE_ATTR
+/* In case we want to port the Nes emu */
+/* void Synth_volume( struct Blip_Synth_ *this__, double v, int range ); */
+
+// Get/set Blip_Buffer used for output
+const struct Blip_Buffer* Synth_output( struct Blip_Synth_* this__ ); ICODE_ATTR
+
+// Low-level interface
+
+	#if defined (__GNUC__) || _MSC_VER >= 1100
+		#define BLIP_RESTRICT __restrict
+	#else
+		#define BLIP_RESTRICT
+	#endif
+
+// Works directly in terms of fractional output samples. Contact author for more info.
+static inline void Synth_offset_resampled( struct Blip_Synth_* this__, blip_resampled_time_t time,
+	int delta, struct Blip_Buffer* blip_buf )
+{
+	// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
+	// need for a longer buffer as set by set_sample_rate().
+	assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
+	delta *= this__->delta_factor;
+	blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
+	int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
+
+	blip_long left = buf [0] + delta;
+
+	// Kind of crappy, but doing shift after multiply results in overflow.
+	// Alternate way of delaying multiply by delta_factor results in worse
+	// sub-sample resolution.
+	blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
+	left  -= right;
+	right += buf [1];
+
+	buf [0] = left;
+	buf [1] = right;
+}
+
+// Update amplitude of waveform at given time. Using this__ requires a separate
+// Blip_Synth for each waveform.
+static inline void Synth_update( struct Blip_Synth_* this__, blip_time_t t, int amp )
+{
+	int delta = amp - this__->last_amp;
+	this__->last_amp = amp;
+	Synth_offset_resampled( this__, t * this__->buf->factor_ + this__->buf->offset_, delta, this__->buf );
+}
+
+// Add an amplitude transition of specified delta, optionally into specified buffer
+// rather than the one set with output(). Delta can be positive or negative.
+// The actual change in amplitude is delta * (volume / range)
+static inline void Synth_offset( struct Blip_Synth_* this__, blip_time_t t, int delta, struct Blip_Buffer* buf )
+{
+	Synth_offset_resampled( this__, t * buf->factor_ + buf->offset_, delta, buf );
+}
+/* void Synth_offset( struct Blip_Synth_* this__, blip_time_t t, int delta ) {
+	Synth_offset( this__, t, delta, this__->buf ); } */
+
+// Same as offset(), except code is inlined for higher performance
+static inline void Synth_offset_inline( struct Blip_Synth_* this__, blip_time_t t, int delta, struct Blip_Buffer* buf )
+{
+	Synth_offset_resampled( this__, t * buf->factor_ + buf->offset_, delta, buf );
+}
+/* void offset_inline( struct Blip_Synth_* this__, blip_time_t t, int delta ) const {
+	offset_resampled( t * this__->buf->factor_ + this__->buf->offset_, delta, this__->buf );
+} */
+
+
+// Optimized reading from Blip_Buffer, for use in custom sample output
+
+// Begin reading from buffer. Name should be unique to the current block.
+#define BLIP_READER_BEGIN( name, blip_buffer ) \
+	buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
+	blip_long name##_reader_accum = (blip_buffer).reader_accum_
+
+// Get value to pass to BLIP_READER_NEXT()
+#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
+
+// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
+// code at the cost of having no bass control
+/* static int blip_reader_default_bass = 9; */
+
+// Current sample
+#define BLIP_READER_READ( name )        (name##_reader_accum >> (blip_sample_bits - 16))
+
+// Current raw sample in full internal resolution
+#define BLIP_READER_READ_RAW( name )    (name##_reader_accum)
+
+// Advance to next sample
+#define BLIP_READER_NEXT( name, bass ) \
+	(void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
+
+// End reading samples from buffer. The number of samples read must now be removed
+// using Blip_remove_samples().
+#define BLIP_READER_END( name, blip_buffer ) \
+	(void) ((blip_buffer).reader_accum_ = name##_reader_accum)
+
+#endif
diff -rNu rb/apps/codecs/libhes/gme.h rb_hes/apps/codecs/libhes/gme.h
--- rb/apps/codecs/libhes/gme.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/gme.h	2011-05-15 17:09:04.982871900 +0800
@@ -0,0 +1,18 @@
+/* Game music emulator library C interface (also usable from C++) */
+
+/* Game_Music_Emu 0.5.2 */
+#ifndef GME_H
+#define GME_H
+
+#ifdef __cplusplus
+	extern "C" {
+#endif
+
+/* Error string returned by library functions, or NULL if no error (success) */
+typedef const char* gme_err_t;
+
+#ifdef __cplusplus
+	}
+#endif
+
+#endif
diff -rNu rb/apps/codecs/libhes/hes_apu.c rb_hes/apps/codecs/libhes/hes_apu.c
--- rb/apps/codecs/libhes/hes_apu.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_apu.c	2011-05-15 17:09:04.987872200 +0800
@@ -0,0 +1,315 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "hes_apu.h"
+#include <string.h>
+
+/* Copyright (C) 2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes
+
+static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ); ICODE_ATTR
+static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
+{
+	static short const log_table [32] ICONST_ATTR = { // ~1.5 db per step
+		#define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5)
+		ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ),
+		ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ),
+		ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ),
+		ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ),
+		ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ),
+		ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ),
+		ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ),
+		ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ),
+		#undef ENTRY
+	};
+	
+	int vol = (osc->control & 0x1F) - 0x1E * 2;
+	
+	int left  = vol + (osc->balance >> 3 & 0x1E) + (this->balance >> 3 & 0x1E);
+	if ( left  < 0 ) left  = 0;
+	
+	int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E);
+	if ( right < 0 ) right = 0;
+	
+	left  = log_table [left ];
+	right = log_table [right];
+	
+	// optimizing for the common case of being centered also allows easy
+	// panning using Effects_Buffer
+	osc->outputs [0] = osc->chans [0]; // center
+	osc->outputs [1] = 0;
+	if ( left != right )
+	{
+		osc->outputs [0] = osc->chans [1]; // left
+		osc->outputs [1] = osc->chans [2]; // right
+	}
+	
+	if ( center_waves )
+	{
+		osc->last_amp [0] += (left  - osc->volume [0]) * 16;
+		osc->last_amp [1] += (right - osc->volume [1]) * 16;
+	}
+	
+	osc->volume [0] = left;
+	osc->volume [1] = right;
+}
+
+void Apu_init( struct Hes_Apu* this )
+{
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		osc->outputs [0] = 0;
+		osc->outputs [1] = 0;
+		osc->chans [0] = 0;
+		osc->chans [1] = 0;
+		osc->chans [2] = 0;
+	}
+	while ( osc != this->oscs );
+	
+	Apu_reset( this );
+}
+
+void Apu_reset( struct Hes_Apu* this )
+{
+	this->latch   = 0;
+	this->balance = 0xFF;
+	
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		memset( osc, 0, offsetof (struct Hes_Osc,outputs) );
+		osc->noise_lfsr = 1;
+		osc->control    = 0x40;
+		osc->balance    = 0xFF;
+	}
+	while ( osc != this->oscs );
+}
+
+void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
+{
+	require( (unsigned) index < osc_count );
+	this->oscs [index].chans [0] = center;
+	this->oscs [index].chans [1] = left;
+	this->oscs [index].chans [2] = right;
+	
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		Apu_balance_changed( this, osc );
+	}
+	while ( osc != this->oscs );
+}
+
+void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth_* synth_, blip_time_t end_time )
+{
+	struct Blip_Buffer* const osc_outputs_0 = this->outputs [0]; // cache often-used values
+	if ( osc_outputs_0 && this->control & 0x80 )
+	{
+		int dac = this->dac;
+		
+		int const volume_0 = this->volume [0];
+		{
+			int delta = dac * volume_0 - this->last_amp [0];
+			if ( delta )
+				Synth_offset( synth_, this->last_time, delta, osc_outputs_0 );
+			Blip_set_modified( osc_outputs_0 );
+		}
+		
+		struct Blip_Buffer* const osc_outputs_1 = this->outputs [1];
+		int const volume_1 = this->volume [1];
+		if ( osc_outputs_1 )
+		{
+			int delta = dac * volume_1 - this->last_amp [1];
+			if ( delta )
+				Synth_offset( synth_, this->last_time, delta, osc_outputs_1 );
+			Blip_set_modified( osc_outputs_1 );
+		}
+		
+		blip_time_t time = this->last_time + this->delay;
+		if ( time < end_time )
+		{
+			if ( this->noise & 0x80 )
+			{
+				if ( volume_0 | volume_1 )
+				{
+					// noise
+					int const period = (32 - (this->noise & 0x1F)) * 64; // TODO: correct?
+					unsigned noise_lfsr = this->noise_lfsr;
+					do
+					{
+						int new_dac = 0x1F & -(noise_lfsr >> 1 & 1);
+						// Implemented using "Galios configuration"
+						// TODO: find correct LFSR algorithm
+						noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1));
+						//noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1));
+						int delta = new_dac - dac;
+						if ( delta )
+						{
+							dac = new_dac;
+							Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 );
+							if ( osc_outputs_1 )
+								Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 );
+						}
+						time += period;
+					}
+					while ( time < end_time );
+					
+					this->noise_lfsr = noise_lfsr;
+					assert( noise_lfsr );
+				}
+			}
+			else if ( !(this->control & 0x40) )
+			{
+				// wave
+				int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop
+				int period = this->period * 2;
+				if ( period >= 14 && (volume_0 | volume_1) )
+				{
+					do
+					{
+						int new_dac = this->wave [phase];
+						phase = (phase + 1) & 0x1F;
+						int delta = new_dac - dac;
+						if ( delta )
+						{
+							dac = new_dac;
+							Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 );
+							if ( osc_outputs_1 )
+								Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 );
+						}
+						time += period;
+					}
+					while ( time < end_time );
+				}
+				else
+				{
+					if ( !period )
+					{
+						// TODO: Gekisha Boy assumes that period = 0 silences wave
+						//period = 0x1000 * 2;
+						period = 1;
+						//if ( !(volume_0 | volume_1) )
+						//  dprintf( "Used period 0\n" );
+					}
+					
+					// maintain phase when silent
+					blargg_long count = (end_time - time + period - 1) / period;
+					phase += count; // phase will be masked below
+					time += count * period;
+				}
+				this->phase = (phase - 1) & 0x1F; // undo pre-advance
+			}
+		}
+		time -= end_time;
+		if ( time < 0 )
+			time = 0;
+		this->delay = time;
+		
+		this->dac = dac;
+		this->last_amp [0] = dac * volume_0;
+		this->last_amp [1] = dac * volume_1;
+	}
+	this->last_time = end_time;
+}
+
+void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data )
+{
+	if ( addr == 0x800 )
+	{
+		this->latch = data & 7;
+	}
+	else if ( addr == 0x801 )
+	{
+		if ( this->balance != data )
+		{
+			this->balance = data;
+			
+			struct Hes_Osc* osc = &this->oscs [osc_count];
+			do
+			{
+				osc--;
+				Osc_run_until( osc, &this->synth, time );
+				Apu_balance_changed( this, this->oscs );
+			}
+			while ( osc != this->oscs );
+		}
+	}
+	else if ( this->latch < osc_count )
+	{
+		struct Hes_Osc* osc = &this->oscs [this->latch];
+		Osc_run_until( osc, &this->synth, time );
+		switch ( addr )
+		{
+		case 0x802:
+			osc->period = (osc->period & 0xF00) | data;
+			break;
+		
+		case 0x803:
+			osc->period = (osc->period & 0x0FF) | ((data & 0x0F) << 8);
+			break;
+		
+		case 0x804:
+			if ( osc->control & 0x40 & ~data )
+				osc->phase = 0;
+			osc->control = data;
+			Apu_balance_changed( this, osc );
+			break;
+		
+		case 0x805:
+			osc->balance = data;
+			Apu_balance_changed( this, osc );
+			break;
+		
+		case 0x806:
+			data &= 0x1F;
+			if ( !(osc->control & 0x40) )
+			{
+				osc->wave [osc->phase] = data;
+				osc->phase = (osc->phase + 1) & 0x1F;
+			}
+			else if ( osc->control & 0x80 )
+			{
+				osc->dac = data;
+			}
+			break;
+		
+		 case 0x807:
+		 	if ( osc >= &this->oscs [4] )
+		 		osc->noise = data;
+		 	break;
+		 case 0x809:
+		 	if ( !(data & 0x80) && (data & 0x03) != 0 ) {
+		 		dprintf( "HES LFO not supported\n" );
+			}
+		}
+	}
+}
+
+void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time )
+{
+	struct Hes_Osc* osc = &this->oscs [osc_count];
+	do
+	{
+		osc--;
+		if ( end_time > osc->last_time )
+			Osc_run_until( osc, &this->synth, end_time );
+		assert( osc->last_time >= end_time );
+		osc->last_time -= end_time;
+	}
+	while ( osc != this->oscs );
+}
diff -rNu rb/apps/codecs/libhes/hes_apu.h rb_hes/apps/codecs/libhes/hes_apu.h
--- rb/apps/codecs/libhes/hes_apu.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_apu.h	2011-05-15 17:09:04.990872300 +0800
@@ -0,0 +1,55 @@
+// Turbo Grafx 16 (PC Engine) PSG sound chip emulator
+
+// Game_Music_Emu 0.5.2
+#ifndef HES_APU_H
+#define HES_APU_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { amp_range = 0x8000 };
+enum { osc_count = 6 };
+enum { start_addr = 0x0800 };
+enum { end_addr   = 0x0809 };
+
+struct Hes_Osc
+{
+	unsigned char wave [32];
+	short volume [2];
+	int last_amp [2];
+	int delay;
+	int period;
+	unsigned char noise;
+	unsigned char phase;
+	unsigned char balance;
+	unsigned char dac;
+	blip_time_t last_time;
+	
+	struct Blip_Buffer* outputs [2];
+	struct Blip_Buffer* chans [3];
+	unsigned noise_lfsr;
+	unsigned char control;
+};
+
+void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth_* synth, blip_time_t ); ICODE_ATTR
+
+struct Hes_Apu {
+	struct Hes_Osc oscs [osc_count];
+	
+	int latch;
+	int balance;
+	struct Blip_Synth_ synth;
+};
+
+// Init HES apu sound chip
+void Apu_init( struct Hes_Apu* this );
+
+// Reset HES apu couns chip
+void Apu_reset( struct Hes_Apu* this );
+
+void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ); ICODE_ATTR
+void Apu_write_data( struct Hes_Apu* this, blip_time_t, int addr, int data ); ICODE_ATTR
+void Apu_end_frame( struct Hes_Apu* this, blip_time_t ); ICODE_ATTR
+
+static inline void Apu_volume( struct Hes_Apu* this, double v ) { Synth_volume( &this->synth, 1.8 / osc_count / amp_range * v ); }
+#endif
diff -rNu rb/apps/codecs/libhes/hes_apu_adpcm.c rb_hes/apps/codecs/libhes/hes_apu_adpcm.c
--- rb/apps/codecs/libhes/hes_apu_adpcm.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_apu_adpcm.c	2011-05-15 17:09:04.994872600 +0800
@@ -0,0 +1,297 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "hes_apu_adpcm.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+void Adpcm_init( struct Hes_Apu_Adpcm* this )
+{
+	this->output = NULL;
+	memset( &this->state, 0, sizeof( this->state ) );
+	Adpcm_reset( this );
+}
+
+void Adpcm_reset( struct Hes_Apu_Adpcm* this )
+{
+	this->last_time = 0;
+	this->next_timer = 0;
+	this->last_amp = 0;
+
+	memset( &this->state.pcmbuf, 0, sizeof(this->state.pcmbuf) );
+	memset( &this->state.port, 0, sizeof(this->state.port) );
+
+	this->state.ad_sample = 0;
+	this->state.ad_ref_index = 0;
+
+	this->state.addr = 0;
+	this->state.freq = 0;
+	this->state.writeptr = 0;
+	this->state.readptr = 0;
+	this->state.playflag = 0;
+	this->state.repeatflag = 0;
+	this->state.length = 0;
+	this->state.volume = 0xFF;
+	this->state.fadetimer = 0;
+	this->state.fadecount = 0;
+}
+
+static short stepsize[49] = {
+  16,  17,  19,  21,  23,  25,  28,
+  31,  34,  37,  41,  45,  50,  55,
+  60,  66,  73,  80,  88,  97, 107,
+ 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796,
+ 876, 963,1060,1166,1282,1411,1552
+};
+
+static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code ); ICODE_ATTR
+static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code )
+{
+	struct State* state = &this->state;
+	int step = stepsize[state->ad_ref_index];
+	int delta;
+	int c = code & 7;
+#if 1
+	delta = 0;
+	if ( c & 4 ) delta += step;
+	step >>= 1;
+	if ( c & 2 ) delta += step;
+	step >>= 1;
+	if ( c & 1 ) delta += step;
+	step >>= 1;
+	delta += step;
+#else
+	delta = ( ( c + c + 1 ) * step ) / 8; // maybe faster, but introduces rounding
+#endif
+	if ( c != code )
+	{
+		state->ad_sample -= delta;
+		if ( state->ad_sample < -2048 )
+			state->ad_sample = -2048;
+	}
+	else
+	{
+		state->ad_sample += delta;
+		if ( state->ad_sample > 2047 )
+			state->ad_sample = 2047;
+	}
+
+	static int const steps [8] ICONST_ATTR = {
+		-1, -1, -1, -1, 2, 4, 6, 8
+	};
+	state->ad_ref_index += steps [c];
+	if ( state->ad_ref_index < 0 )
+		state->ad_ref_index = 0;
+	else if ( state->ad_ref_index > 48 )
+		state->ad_ref_index = 48;
+
+	return state->ad_sample;
+}
+
+static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time ); ICODE_ATTR
+static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time )
+{
+	struct State* state = &this->state;
+	int volume = state->volume;
+	int fadetimer = state->fadetimer;
+	int fadecount = state->fadecount;
+	int last_time = this->last_time;
+	double next_timer = this->next_timer;
+	int last_amp = this->last_amp;
+	
+	struct Blip_Buffer* output = this->output; // cache often-used values
+
+	while ( state->playflag && last_time < end_time )
+	{
+		while ( last_time >= next_timer )
+		{
+			if ( fadetimer )
+			{
+				if ( fadecount > 0 )
+				{
+					fadecount--;
+					volume = 0xFF * fadecount / fadetimer;
+				}
+				else if ( fadecount < 0 )
+				{
+					fadecount++;
+					volume = 0xFF - ( 0xFF * fadecount / fadetimer );
+				}
+			}
+			next_timer += 7159.091;
+		}
+		int amp;
+		if ( state->ad_low_nibble )
+		{
+			amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] & 0x0F );
+			state->ad_low_nibble = false;
+			state->playptr++;
+			state->playedsamplecount++;
+			if ( state->playedsamplecount == state->playlength )
+			{
+				state->playflag = 0;
+			}
+		}
+		else
+		{
+			amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] >> 4 );
+			state->ad_low_nibble = true;
+		}
+		amp = amp * volume / 0xFF;
+		int delta = amp - last_amp;
+		if ( output && delta )
+		{
+			last_amp = amp;
+			Synth_offset_inline( &this->synth, last_time, delta, output );
+		}
+		last_time += state->freq;
+	}
+
+	if ( !state->playflag )
+	{
+		while ( next_timer <= end_time ) next_timer += 7159.091;
+		last_time = end_time;
+	}
+	
+	this->last_time  = last_time;
+	this->next_timer = next_timer;
+	this->last_amp   = last_amp;
+	state->volume = volume;
+	state->fadetimer = fadetimer;
+	state->fadecount = fadecount;
+}
+
+void Adpcm_write_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr, int data )
+{
+	if ( time > this->last_time ) Adpcm_run_until( this, time );
+	struct State* state = &this->state;
+
+	data &= 0xFF;
+	state->port[ addr & 15 ] = data;
+	switch ( addr & 15 )
+	{
+	case 8:
+		state->addr &= 0xFF00;
+		state->addr |= data;
+		break;
+	case 9:
+		state->addr &= 0xFF;
+		state->addr |= data << 8;
+		break;
+	case 10:
+		state->pcmbuf[ state->writeptr++ ] = data;
+		state->playlength ++;
+		break;
+	case 11:
+		dprintf("ADPCM DMA 0x%02X", data);
+		break;
+	case 13:
+		if ( data & 0x80 )
+		{
+			state->addr = 0;
+			state->freq = 0;
+			state->writeptr = 0;
+			state->readptr = 0;
+			state->playflag = 0;
+			state->repeatflag = 0;
+			state->length = 0;
+			state->volume = 0xFF;
+		}
+		if ( ( data & 3 ) == 3 )
+		{
+			state->writeptr = state->addr;
+		}
+		if ( data & 8 )
+		{
+			state->readptr = state->addr ? state->addr - 1 : state->addr;
+		}
+		if ( data & 0x10 )
+		{
+			state->length = state->addr;
+		}
+		state->repeatflag = data & 0x20;
+		state->playflag = data & 0x40;
+		if ( state->playflag )
+		{
+			state->playptr = state->readptr;
+			state->playlength = state->length + 1;
+			state->playedsamplecount = 0;
+			state->ad_sample = 0;
+			state->ad_low_nibble = false;
+		}
+		break;
+	case 14:
+		state->freq = 7159091 / ( 32000 / ( 16 - ( data & 15 ) ) );
+		break;
+	case 15:
+		switch ( data & 15 )
+		{
+		case 0:
+		case 8:
+		case 12:
+			state->fadetimer = -100;
+			state->fadecount = state->fadetimer;
+			break;
+		case 10:
+			state->fadetimer = 5000;
+			state->fadecount = state->fadetimer;
+			break;
+		case 14:
+			state->fadetimer = 1500;
+			state->fadecount = state->fadetimer;
+			break;
+		}
+		break;
+	}
+}
+
+int Adpcm_read_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr )
+{
+	if ( time > this->last_time ) Adpcm_run_until( this, time );
+
+	struct State* state = &this->state;
+	switch ( addr & 15 )
+	{
+	case 10:
+		return state->pcmbuf [state->readptr++];
+	case 11:
+		return state->port [11] & ~1;
+	case 12:
+		if (!state->playflag)
+		{
+			state->port [12] |= 1;
+			state->port [12] &= ~8;
+		}
+		else
+		{
+			state->port [12] &= ~1;
+			state->port [12] |= 8;
+		}
+		return state->port [12];
+	case 13:
+		return state->port [13];
+	}
+
+	return 0xFF;
+}
+
+void Adpcm_end_frame( struct Hes_Apu_Adpcm* this, blip_time_t end_time )
+{
+	Adpcm_run_until( this, end_time );
+	this->last_time -= end_time;
+	this->next_timer -= (double)end_time;
+	check( last_time >= 0 );
+	if ( this->output )
+		Blip_set_modified( this->output );
+}
diff -rNu rb/apps/codecs/libhes/hes_apu_adpcm.h rb_hes/apps/codecs/libhes/hes_apu_adpcm.h
--- rb/apps/codecs/libhes/hes_apu_adpcm.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_apu_adpcm.h	2011-05-15 17:09:04.998872800 +0800
@@ -0,0 +1,89 @@
+// Turbo Grafx 16 (PC Engine) ADPCM sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef HES_APU_ADPCM_H
+#define HES_APU_ADPCM_H
+
+#include "blargg_source.h"
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+enum { adpcm_amp_range = 2048 };
+enum { adpcm_osc_count = 1 }; // 0 <= chan < osc_count
+
+// Registers are at io_addr to io_addr+io_size-1
+enum { io_addr = 0x1800 };
+enum { io_size = 0x400 };
+
+struct State
+{
+	byte           pcmbuf [0x10000];
+	byte           port [0x10];
+	int            ad_sample;
+	int            ad_ref_index;
+	bool           ad_low_nibble;
+	int            freq;
+	unsigned short addr;
+	unsigned short writeptr;
+	unsigned short readptr;
+	unsigned short playptr;
+	byte           playflag;
+	byte           repeatflag;
+	int            length;
+	int            playlength;
+	int            playedsamplecount;
+	int            volume;
+	int            fadetimer;
+	int            fadecount;
+};
+
+struct Hes_Apu_Adpcm {
+	struct State state;
+	struct Blip_Synth_ synth;
+
+	struct Blip_Buffer* output;
+	blip_time_t  last_time;
+	double       next_timer;
+	int          last_amp;
+};
+
+// Init HES adpcm sound chip
+void Adpcm_init( struct Hes_Apu_Adpcm* this );
+
+// Rest HES adpcm sound chip
+void Adpcm_reset( struct Hes_Apu_Adpcm* this );
+
+// Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0,
+// output is mono.
+static inline void Adpcm_set_output( struct Hes_Apu_Adpcm* this, int chan, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
+{
+	// Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+	require( !center || (center && !left && !right) || (center && left && right) );
+	require( (unsigned) chan < adpcm_osc_count ); // fails if you pass invalid osc index
+
+#if defined(ROCKBOX)
+    (void) chan;
+#endif
+
+	if ( !center || !left || !right )
+	{
+		left  = center;
+		right = center;
+	}
+	
+	this->output = center;
+}
+
+// Emulates to time t, then writes data to addr
+void Adpcm_write_data( struct Hes_Apu_Adpcm* this, blip_time_t t, int addr, int data ); ICODE_ATTR
+	
+// Emulates to time t, then reads from addr
+int Adpcm_read_data( struct Hes_Apu_Adpcm* this, blip_time_t t, int addr ); ICODE_ATTR
+
+// Emulates to time t, then subtracts t from the current time.
+// OK if previous write call had time slightly after t.
+void Adpcm_end_frame( struct Hes_Apu_Adpcm* this,blip_time_t t ); ICODE_ATTR
+
+// Sets overall volume, where 1.0 is normal
+static inline void Adpcm_volume( struct Hes_Apu_Adpcm* this, double v )	{ Synth_volume( &this->synth, 0.6 / adpcm_osc_count / adpcm_amp_range * v ); }
+#endif
diff -rNu rb/apps/codecs/libhes/hes_cpu.c rb_hes/apps/codecs/libhes/hes_cpu.c
--- rb/apps/codecs/libhes/hes_cpu.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_cpu.c	2011-05-15 17:09:05.008873400 +0800
@@ -0,0 +1,1321 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "hes_cpu.h"
+
+#include "blargg_endian.h"
+
+//#include "hes_cpu_log.h"
+
+/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// TODO: support T flag, including clearing it at appropriate times?
+
+// all zero-page should really use whatever is at page 1, but that would
+// reduce efficiency quite a bit
+int const ram_addr = 0x2000;
+
+#define FLUSH_TIME()    (void) (s.time = s_time)
+#define CACHE_TIME()    (void) (s_time = s.time)
+
+#include "hes_cpu_io.h"
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_NONPORTABLE
+	#define PAGE_OFFSET( addr ) (addr)
+#else
+	#define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
+
+// status flags
+int const st_n = 0x80;
+int const st_v = 0x40;
+int const st_t = 0x20;
+int const st_b = 0x10;
+int const st_d = 0x08;
+int const st_i = 0x04;
+int const st_z = 0x02;
+int const st_c = 0x01;
+
+void Cpu_init( struct Hes_Cpu* this )
+{
+	this->state = &this->state_;
+}
+
+void Cpu_reset( struct Hes_Cpu* this )
+{
+	check( this->state == &state_ );
+	this->state = &this->state_;
+	
+	this->state_.time = 0;
+	this->state_.base = 0;
+	this->irq_time   = future_hes_time;
+	this->end_time   = future_hes_time;
+	
+	this->r.status = st_i;
+	this->r.sp     = 0;
+	this->r.pc     = 0;
+	this->r.a      = 0;
+	this->r.x      = 0;
+	this->r.y      = 0;
+	
+	blargg_verify_byte_order();
+}
+
+void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank )
+{
+	assert( (unsigned) reg <= page_count ); // allow page past end to be set
+	assert( (unsigned) bank < 0x100 );
+	this->cpu.mmr [reg] = bank;
+	uint8_t const* code = CPU_SET_MMR( this, reg, bank );
+	this->cpu.state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift );
+}
+
+#define TIME    (s_time + s.base)
+
+#define READ( addr )            CPU_READ( this, (addr), TIME )
+#define WRITE( addr, data )     {CPU_WRITE( this, (addr), (data), TIME );}
+#define READ_LOW( addr )        (cpu->ram [(int) (addr)])
+#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
+#define READ_PROG( addr )       (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )])
+
+#define SET_SP( v )     (sp = ((v) + 1) | 0x100)
+#define GET_SP()        ((sp - 1) & 0xFF)
+#define PUSH( v )       ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
+
+// even on x86, using short and unsigned char was slower
+typedef int         fint16;
+typedef unsigned    fuint16;
+typedef unsigned    fuint8;
+typedef blargg_long fint32;
+
+bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time )
+{
+	bool illegal_encountered = false;
+		
+	// Set cpu end time
+	struct Hes_Cpu* cpu = &this->cpu;
+	cpu->state->time += Cpu_update_end_time( cpu, cpu->r.status, (cpu->end_time = end_time), cpu->irq_time );
+	
+	struct state_t s = cpu->state_;
+	cpu->state = &s;
+	
+	// even on x86, using s.time in place of s_time was slower
+	fint16 s_time = s.time;
+	
+	struct registers_t* r = &cpu->r;
+	
+	// registers
+	fuint16 pc = r->pc;
+	fuint8 a = r->a;
+	fuint8 x = r->x;
+	fuint8 y = r->y;
+	fuint16 sp;
+	SET_SP( r->sp );
+	
+	#define IS_NEG (nz & 0x8080)
+	
+	#define CALC_STATUS( out ) do {\
+		out = status & (st_v | st_d | st_i);\
+		out |= ((nz >> 8) | nz) & st_n;\
+		out |= c >> 8 & st_c;\
+		if ( !(nz & 0xFF) ) out |= st_z;\
+	} while ( 0 )
+
+	#define SET_STATUS( in ) do {\
+		status = in & (st_v | st_d | st_i);\
+		nz = in << 8;\
+		c = nz;\
+		nz |= ~in & st_z;\
+	} while ( 0 )
+	
+	fuint8 status;
+	fuint16 c;  // carry set if (c & 0x100) != 0
+	fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
+	{
+		fuint8 temp = r->status;
+		SET_STATUS( temp );
+	}
+	
+	goto loop;
+branch_not_taken:
+	s_time -= 2;
+loop:
+	
+	#ifndef NDEBUG
+	{
+		hes_time_t correct = end_time_;
+		if ( !(status & st_i) && correct > irq_time_ )
+			correct = irq_time_;
+		check( s.base == correct );
+		/*
+		static long count;
+		if ( count == 1844 ) Debugger();
+		if ( s.base != correct ) dprintf( "%ld\n", count );
+		count++;
+		*/
+	}
+	#endif
+
+	check( (unsigned) GET_SP() < 0x100 );
+	check( (unsigned) a < 0x100 );
+	check( (unsigned) x < 0x100 );
+	
+	uint8_t const* instr = s.code_map [pc >> page_shift];
+	fuint8 opcode;
+	
+	// TODO: eliminate this special case
+	#ifdef BLARGG_NONPORTABLE
+		opcode = instr [pc];
+		pc++;
+		instr += pc;
+	#else
+		instr += PAGE_OFFSET( pc );
+		opcode = *instr++;
+		pc++;
+	#endif
+	
+	// TODO: each reference lists slightly different timing values, ugh
+	static uint8_t const clock_table [256] ICONST_ATTR =
+	{// 0 1 2  3 4 5 6 7 8 9 A B C D E F
+		1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0
+		4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1
+		7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2
+		4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3
+		7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4
+		4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5
+		7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6
+		4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7
+		4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8
+		4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9
+		2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A
+		4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B
+		2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C
+		4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D
+		2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E
+		4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F
+	}; // 0x00 was 8
+	
+	fuint16 data;
+	data = clock_table [opcode];
+	if ( (s_time += data) >= 0 )
+		goto possibly_out_of_time;
+almost_out_of_time:
+	
+	data = *instr;
+	
+	#ifdef HES_CPU_LOG_H
+		log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
+				instr [3], instr [4], instr [5] );
+		//log_opcode( opcode );
+	#endif
+	
+	switch ( opcode )
+	{
+possibly_out_of_time:
+		if ( s_time < (int) data )
+			goto almost_out_of_time;
+		s_time -= data;
+		goto out_of_time;
+
+// Macros
+
+#define GET_MSB()           (instr [1])
+#define ADD_PAGE( out )     (pc++, out = data + 0x100 * GET_MSB());
+#define GET_ADDR()          GET_LE16( instr )
+
+// TODO: is the penalty really always added? the original 6502 was much better
+//#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
+#define PAGE_CROSS_PENALTY( lsb )
+
+// Branch
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH( cond )\
+{\
+	fint16 offset = (int8_t) data;\
+	pc++;\
+	if ( !(cond) ) goto branch_not_taken;\
+	pc = (uint16_t) (pc + offset);\
+	goto loop;\
+}
+
+	case 0xF0: // BEQ
+		BRANCH( !((uint8_t) nz) );
+	
+	case 0xD0: // BNE
+		BRANCH( (uint8_t) nz );
+	
+	case 0x10: // BPL
+		BRANCH( !IS_NEG );
+	
+	case 0x90: // BCC
+		BRANCH( !(c & 0x100) )
+	
+	case 0x30: // BMI
+		BRANCH( IS_NEG )
+	
+	case 0x50: // BVC
+		BRANCH( !(status & st_v) )
+	
+	case 0x70: // BVS
+		BRANCH( status & st_v )
+	
+	case 0xB0: // BCS
+		BRANCH( c & 0x100 )
+	
+	case 0x80: // BRA
+	branch_taken:
+		BRANCH( true );
+	
+	case 0xFF:
+		if ( pc == idle_addr + 1 )
+			goto idle_done;
+	case 0x0F: // BBRn
+	case 0x1F:
+	case 0x2F:
+	case 0x3F:
+	case 0x4F:
+	case 0x5F:
+	case 0x6F:
+	case 0x7F:
+	case 0x8F: // BBSn
+	case 0x9F:
+	case 0xAF:
+	case 0xBF:
+	case 0xCF:
+	case 0xDF:
+	case 0xEF: {
+		fuint16 t = 0x101 * READ_LOW( data );
+		t ^= 0xFF;
+		pc++;
+		data = GET_MSB();
+		BRANCH( t & (1 << (opcode >> 4)) )
+	}
+	
+	case 0x4C: // JMP abs
+		pc = GET_ADDR();
+		goto loop;
+	
+	case 0x7C: // JMP (ind+X)
+		data += x;
+	case 0x6C:{// JMP (ind)
+		data += 0x100 * GET_MSB();
+		pc = GET_LE16( &READ_PROG( data ) );
+		goto loop;
+	}
+	
+// Subroutine
+
+	case 0x44: // BSR
+		WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
+		sp = (sp - 2) | 0x100;
+		WRITE_LOW( sp, pc );
+		goto branch_taken;
+	
+	case 0x20: { // JSR
+		fuint16 temp = pc + 1;
+		pc = GET_ADDR();
+		WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
+		sp = (sp - 2) | 0x100;
+		WRITE_LOW( sp, temp );
+		goto loop;
+	}
+	
+	case 0x60: // RTS
+		pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
+		pc += 1 + READ_LOW( sp );
+		sp = (sp - 0xFE) | 0x100;
+		goto loop;
+	
+	case 0x00: // BRK
+		goto handle_brk;
+	
+// Common
+
+	case 0xBD:{// LDA abs,X
+		PAGE_CROSS_PENALTY( data + x );
+		fuint16 addr = GET_ADDR() + x;
+		pc += 2;
+		CPU_READ_FAST( this, addr, TIME, nz );
+		a = nz;
+		goto loop;
+	}
+	
+	case 0x9D:{// STA abs,X
+		fuint16 addr = GET_ADDR() + x;
+		pc += 2;
+		CPU_WRITE_FAST( this, addr, a, TIME );
+		goto loop;
+	}
+	
+	case 0x95: // STA zp,x
+		data = (uint8_t) (data + x);
+	case 0x85: // STA zp
+		pc++;
+		WRITE_LOW( data, a );
+		goto loop;
+	
+	case 0xAE:{// LDX abs
+		fuint16 addr = GET_ADDR();
+		pc += 2;
+		CPU_READ_FAST( this, addr, TIME, nz );
+		x = nz;
+		goto loop;
+	}
+	
+	case 0xA5: // LDA zp
+		a = nz = READ_LOW( data );
+		pc++;
+		goto loop;
+	
+// Load/store
+	
+	{
+		fuint16 addr;
+	case 0x91: // STA (ind),Y
+		addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		addr += READ_LOW( data ) + y;
+		pc++;
+		goto sta_ptr;
+	
+	case 0x81: // STA (ind,X)
+		data = (uint8_t) (data + x);
+	case 0x92: // STA (ind)
+		addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		addr += READ_LOW( data );
+		pc++;
+		goto sta_ptr;
+	
+	case 0x99: // STA abs,Y
+		data += y;
+	case 0x8D: // STA abs
+		addr = data + 0x100 * GET_MSB();
+		pc += 2;
+	sta_ptr:
+		CPU_WRITE_FAST( this, addr, a, TIME );
+		goto loop;
+	}
+	
+	{
+		fuint16 addr;
+	case 0xA1: // LDA (ind,X)
+		data = (uint8_t) (data + x);
+	case 0xB2: // LDA (ind)
+		addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		addr += READ_LOW( data );
+		pc++;
+		goto a_nz_read_addr;
+	
+	case 0xB1:// LDA (ind),Y
+		addr = READ_LOW( data ) + y;
+		PAGE_CROSS_PENALTY( addr );
+		addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
+		pc++;
+		goto a_nz_read_addr;
+	
+	case 0xB9: // LDA abs,Y
+		data += y;
+		PAGE_CROSS_PENALTY( data );
+	case 0xAD: // LDA abs
+		addr = data + 0x100 * GET_MSB();
+		pc += 2;
+	a_nz_read_addr:
+		CPU_READ_FAST( this, addr, TIME, nz );
+		a = nz;
+		goto loop;
+	}
+
+	case 0xBE:{// LDX abs,y
+		PAGE_CROSS_PENALTY( data + y );
+		fuint16 addr = GET_ADDR() + y;
+		pc += 2;
+		FLUSH_TIME();
+		x = nz = READ( addr );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	case 0xB5: // LDA zp,x
+		a = nz = READ_LOW( (uint8_t) (data + x) );
+		pc++;
+		goto loop;
+	
+	case 0xA9: // LDA #imm
+		pc++;
+		a  = data;
+		nz = data;
+		goto loop;
+
+// Bit operations
+
+	case 0x3C: // BIT abs,x
+		data += x;
+	case 0x2C:{// BIT abs
+		fuint16 addr;
+		ADD_PAGE( addr );
+		FLUSH_TIME();
+		nz = READ( addr );
+		CACHE_TIME();
+		goto bit_common;
+	}
+	case 0x34: // BIT zp,x
+		data = (uint8_t) (data + x);
+	case 0x24: // BIT zp
+		data = READ_LOW( data );
+	case 0x89: // BIT imm
+		nz = data;
+	bit_common:
+		pc++;
+		status &= ~st_v;
+		status |= nz & st_v;
+		if ( nz & a )
+			goto loop; // Z should be clear, and nz must be non-zero if nz & a is
+		nz <<= 8; // set Z flag without affecting N flag
+		goto loop;
+		
+	{
+		fuint16 addr;
+		
+	case 0xB3: // TST abs,x
+		addr = GET_MSB() + x;
+		goto tst_abs;
+	
+	case 0x93: // TST abs
+		addr = GET_MSB();
+	tst_abs:
+		addr += 0x100 * instr [2];
+		pc++;
+		FLUSH_TIME();
+		nz = READ( addr );
+		CACHE_TIME();
+		goto tst_common;
+	}
+	
+	case 0xA3: // TST zp,x
+		nz = READ_LOW( (uint8_t) (GET_MSB() + x) );
+		goto tst_common;
+	
+	case 0x83: // TST zp
+		nz = READ_LOW( GET_MSB() );
+	tst_common:
+		pc += 2;
+		status &= ~st_v;
+		status |= nz & st_v;
+		if ( nz & data )
+			goto loop; // Z should be clear, and nz must be non-zero if nz & data is
+		nz <<= 8; // set Z flag without affecting N flag
+		goto loop;
+	
+	{
+		fuint16 addr;
+	case 0x0C: // TSB abs
+	case 0x1C: // TRB abs
+		addr = GET_ADDR();
+		pc++;
+		goto txb_addr;
+	
+	// TODO: everyone lists different behaviors for the status flags, ugh
+	case 0x04: // TSB zp
+	case 0x14: // TRB zp
+		addr = data + ram_addr;
+	txb_addr:
+		FLUSH_TIME();
+		nz = a | READ( addr );
+		if ( opcode & 0x10 )
+			nz ^= a; // bits from a will already be set, so this clears them
+		status &= ~st_v;
+		status |= nz & st_v;
+		pc++;
+		WRITE( addr, nz );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	case 0x07: // RMBn
+	case 0x17:
+	case 0x27:
+	case 0x37:
+	case 0x47:
+	case 0x57:
+	case 0x67:
+	case 0x77:
+		pc++;
+		READ_LOW( data ) &= ~(1 << (opcode >> 4));
+		goto loop;
+	
+	case 0x87: // SMBn
+	case 0x97:
+	case 0xA7:
+	case 0xB7:
+	case 0xC7:
+	case 0xD7:
+	case 0xE7:
+	case 0xF7:
+		pc++;
+		READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
+		goto loop;
+	
+// Load/store
+	
+	case 0x9E: // STZ abs,x
+		data += x;
+	case 0x9C: // STZ abs
+		ADD_PAGE( data );
+		pc++;
+		FLUSH_TIME();
+		WRITE( data, 0 );
+		CACHE_TIME();
+		goto loop;
+	
+	case 0x74: // STZ zp,x
+		data = (uint8_t) (data + x);
+	case 0x64: // STZ zp
+		pc++;
+		WRITE_LOW( data, 0 );
+		goto loop;
+	
+	case 0x94: // STY zp,x
+		data = (uint8_t) (data + x);
+	case 0x84: // STY zp
+		pc++;
+		WRITE_LOW( data, y );
+		goto loop;
+	
+	case 0x96: // STX zp,y
+		data = (uint8_t) (data + y);
+	case 0x86: // STX zp
+		pc++;
+		WRITE_LOW( data, x );
+		goto loop;
+	
+	case 0xB6: // LDX zp,y
+		data = (uint8_t) (data + y);
+	case 0xA6: // LDX zp
+		data = READ_LOW( data );
+	case 0xA2: // LDX #imm
+		pc++;
+		x = data;
+		nz = data;
+		goto loop;
+	
+	case 0xB4: // LDY zp,x
+		data = (uint8_t) (data + x);
+	case 0xA4: // LDY zp
+		data = READ_LOW( data );
+	case 0xA0: // LDY #imm
+		pc++;
+		y = data;
+		nz = data;
+		goto loop;
+	
+	case 0xBC: // LDY abs,X
+		data += x;
+		PAGE_CROSS_PENALTY( data );
+	case 0xAC:{// LDY abs
+		fuint16 addr = data + 0x100 * GET_MSB();
+		pc += 2;
+		FLUSH_TIME();
+		y = nz = READ( addr );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	{
+		fuint8 temp;
+	case 0x8C: // STY abs
+		temp = y;
+		goto store_abs;
+	
+	case 0x8E: // STX abs
+		temp = x;
+	store_abs:
+		{
+			fuint16 addr = GET_ADDR();
+			pc += 2;
+			FLUSH_TIME();
+			WRITE( addr, temp );
+			CACHE_TIME();
+			goto loop;
+		}	
+	}
+
+// Compare
+
+	case 0xEC:{// CPX abs
+		fuint16 addr = GET_ADDR();
+		pc++;
+		FLUSH_TIME();
+		data = READ( addr );
+		CACHE_TIME();
+		goto cpx_data;
+	}
+	
+	case 0xE4: // CPX zp
+		data = READ_LOW( data );
+	case 0xE0: // CPX #imm
+	cpx_data:
+		nz = x - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xFF;
+		goto loop;
+	
+	case 0xCC:{// CPY abs
+		fuint16 addr = GET_ADDR();
+		pc++;
+		FLUSH_TIME();
+		data = READ( addr );
+		CACHE_TIME();
+		goto cpy_data;
+	}
+	
+	case 0xC4: // CPY zp
+		data = READ_LOW( data );
+	case 0xC0: // CPY #imm
+	cpy_data:
+		nz = y - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xFF;
+		goto loop;
+	
+// Logical
+
+#define ARITH_ADDR_MODES( op )\
+	case op - 0x04: /* (ind,x) */\
+		data = (uint8_t) (data + x);\
+	case op + 0x0D: /* (ind) */\
+		data = 0x100 * READ_LOW( (uint8_t) (data + 1) ) + READ_LOW( data );\
+		goto ptr##op;\
+	case op + 0x0C:{/* (ind),y */\
+		fuint16 temp = READ_LOW( data ) + y;\
+		PAGE_CROSS_PENALTY( temp );\
+		data = temp + 0x100 * READ_LOW( (uint8_t) (data + 1) );\
+		goto ptr##op;\
+	}\
+	case op + 0x10: /* zp,X */\
+		data = (uint8_t) (data + x);\
+	case op + 0x00: /* zp */\
+		data = READ_LOW( data );\
+		goto imm##op;\
+	case op + 0x14: /* abs,Y */\
+		data += y;\
+		goto ind##op;\
+	case op + 0x18: /* abs,X */\
+		data += x;\
+	ind##op:\
+		PAGE_CROSS_PENALTY( data );\
+	case op + 0x08: /* abs */\
+		ADD_PAGE( data );\
+	ptr##op:\
+		FLUSH_TIME();\
+		data = READ( data );\
+		CACHE_TIME();\
+	case op + 0x04: /* imm */\
+	imm##op:
+
+	ARITH_ADDR_MODES( 0xC5 ) // CMP
+		nz = a - data;
+		pc++;
+		c = ~nz;
+		nz &= 0xFF;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x25 ) // AND
+		nz = (a &= data);
+		pc++;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x45 ) // EOR
+		nz = (a ^= data);
+		pc++;
+		goto loop;
+	
+	ARITH_ADDR_MODES( 0x05 ) // ORA
+		nz = (a |= data);
+		pc++;
+		goto loop;
+	
+// Add/subtract
+
+	ARITH_ADDR_MODES( 0xE5 ) // SBC
+		data ^= 0xFF;
+		goto adc_imm;
+	
+	ARITH_ADDR_MODES( 0x65 ) // ADC
+	adc_imm: {
+		if ( status & st_d ) {
+			dprintf( "Decimal mode not supported\n" );
+		}
+		fint16 carry = c >> 8 & 1;
+		fint16 ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
+		status &= ~st_v;
+		status |= ov >> 2 & 0x40;
+		c = nz = a + data + carry;
+		pc++;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+// Shift/rotate
+
+	case 0x4A: // LSR A
+		c = 0;
+	case 0x6A: // ROR A
+		nz = c >> 1 & 0x80;
+		c = a << 8;
+		nz |= a >> 1;
+		a = nz;
+		goto loop;
+
+	case 0x0A: // ASL A
+		nz = a << 1;
+		c = nz;
+		a = (uint8_t) nz;
+		goto loop;
+
+	case 0x2A: { // ROL A
+		nz = a << 1;
+		fint16 temp = c >> 8 & 1;
+		c = nz;
+		nz |= temp;
+		a = (uint8_t) nz;
+		goto loop;
+	}
+	
+	case 0x5E: // LSR abs,X
+		data += x;
+	case 0x4E: // LSR abs
+		c = 0;
+	case 0x6E: // ROR abs
+	ror_abs: {
+		ADD_PAGE( data );
+		FLUSH_TIME();
+		int temp = READ( data );
+		nz = (c >> 1 & 0x80) | (temp >> 1);
+		c = temp << 8;
+		goto rotate_common;
+	}
+	
+	case 0x3E: // ROL abs,X
+		data += x;
+		goto rol_abs;
+	
+	case 0x1E: // ASL abs,X
+		data += x;
+	case 0x0E: // ASL abs
+		c = 0;
+	case 0x2E: // ROL abs
+	rol_abs:
+		ADD_PAGE( data );
+		nz = c >> 8 & 1;
+		FLUSH_TIME();
+		nz |= (c = READ( data ) << 1);
+	rotate_common:
+		pc++;
+		WRITE( data, (uint8_t) nz );
+		CACHE_TIME();
+		goto loop;
+	
+	case 0x7E: // ROR abs,X
+		data += x;
+		goto ror_abs;
+	
+	case 0x76: // ROR zp,x
+		data = (uint8_t) (data + x);
+		goto ror_zp;
+	
+	case 0x56: // LSR zp,x
+		data = (uint8_t) (data + x);
+	case 0x46: // LSR zp
+		c = 0;
+	case 0x66: // ROR zp
+	ror_zp: {
+		int temp = READ_LOW( data );
+		nz = (c >> 1 & 0x80) | (temp >> 1);
+		c = temp << 8;
+		goto write_nz_zp;
+	}
+	
+	case 0x36: // ROL zp,x
+		data = (uint8_t) (data + x);
+		goto rol_zp;
+	
+	case 0x16: // ASL zp,x
+		data = (uint8_t) (data + x);
+	case 0x06: // ASL zp
+		c = 0;
+	case 0x26: // ROL zp
+	rol_zp:
+		nz = c >> 8 & 1;
+		nz |= (c = READ_LOW( data ) << 1);
+		goto write_nz_zp;
+	
+// Increment/decrement
+
+#define INC_DEC_AXY( reg, n ) reg = (uint8_t) (nz = reg + n); goto loop;
+
+	case 0x1A: // INA
+		INC_DEC_AXY( a, +1 )
+	
+	case 0xE8: // INX
+		INC_DEC_AXY( x, +1 )
+	
+	case 0xC8: // INY
+		INC_DEC_AXY( y, +1 )
+
+	case 0x3A: // DEA
+		INC_DEC_AXY( a, -1 )
+	
+	case 0xCA: // DEX
+		INC_DEC_AXY( x, -1 )
+	
+	case 0x88: // DEY
+		INC_DEC_AXY( y, -1 )
+	
+	case 0xF6: // INC zp,x
+		data = (uint8_t) (data + x);
+	case 0xE6: // INC zp
+		nz = 1;
+		goto add_nz_zp;
+	
+	case 0xD6: // DEC zp,x
+		data = (uint8_t) (data + x);
+	case 0xC6: // DEC zp
+		nz = (unsigned) -1;
+	add_nz_zp:
+		nz += READ_LOW( data );
+	write_nz_zp:
+		pc++;
+		WRITE_LOW( data, nz );
+		goto loop;
+	
+	case 0xFE: // INC abs,x
+		data = x + GET_ADDR();
+		goto inc_ptr;
+	
+	case 0xEE: // INC abs
+		data = GET_ADDR();
+	inc_ptr:
+		nz = 1;
+		goto inc_common;
+	
+	case 0xDE: // DEC abs,x
+		data = x + GET_ADDR();
+		goto dec_ptr;
+	
+	case 0xCE: // DEC abs
+		data = GET_ADDR();
+	dec_ptr:
+		nz = (unsigned) -1;
+	inc_common:
+		FLUSH_TIME();
+		nz += READ( data );
+		pc += 2;
+		WRITE( data, (uint8_t) nz );
+		CACHE_TIME();
+		goto loop;
+		
+// Transfer
+
+	case 0xA8: // TAY
+		y  = a;
+		nz = a;
+		goto loop;
+	
+	case 0x98: // TYA
+		a  = y;
+		nz = y;
+		goto loop;
+	
+	case 0xAA: // TAX
+		x  = a;
+		nz = a;
+		goto loop;
+		
+	case 0x8A: // TXA
+		a  = x;
+		nz = x;
+		goto loop;
+
+	case 0x9A: // TXS
+		SET_SP( x ); // verified (no flag change)
+		goto loop;
+	
+	case 0xBA: // TSX
+		x = nz = GET_SP();
+		goto loop;
+	
+	#define SWAP_REGS( r1, r2 ) {\
+		fuint8 t = r1;\
+		r1 = r2;\
+		r2 = t;\
+		goto loop;\
+	}
+	
+	case 0x02: // SXY
+		SWAP_REGS( x, y );
+	
+	case 0x22: // SAX
+		SWAP_REGS( a, x );
+	
+	case 0x42: // SAY
+		SWAP_REGS( a, y );
+	
+	case 0x62: // CLA
+		a = 0;
+		goto loop;
+	
+	case 0x82: // CLX
+		x = 0;
+		goto loop;
+	
+	case 0xC2: // CLY
+		y = 0;
+		goto loop;
+	
+// Stack
+	
+	case 0x48: // PHA
+		PUSH( a );
+		goto loop;
+		
+	case 0xDA: // PHX
+		PUSH( x );
+		goto loop;
+		
+	case 0x5A: // PHY
+		PUSH( y );
+		goto loop;
+		
+	case 0x40:{// RTI
+		fuint8 temp = READ_LOW( sp );
+		pc  = READ_LOW( 0x100 | (sp - 0xFF) );
+		pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
+		sp = (sp - 0xFD) | 0x100;
+		data = status;
+		SET_STATUS( temp );
+		r->status = status; // update externally-visible I flag
+		if ( (data ^ status) & st_i )
+		{
+			hes_time_t new_time = cpu->end_time;
+			if ( !(status & st_i) && new_time > cpu->irq_time )
+				new_time = cpu->irq_time;
+			blargg_long delta = s.base - new_time;
+			s.base = new_time;
+			s_time += delta;
+		}
+		goto loop;
+	}
+	
+	#define POP()  READ_LOW( sp ); sp = (sp - 0xFF) | 0x100
+	
+	case 0x68: // PLA
+		a = nz = POP();
+		goto loop;
+	
+	case 0xFA: // PLX
+		x = nz = POP();
+		goto loop;
+	
+	case 0x7A: // PLY
+		y = nz = POP();
+		goto loop;
+	
+	case 0x28:{// PLP
+		fuint8 temp = POP();
+		fuint8 changed = status ^ temp;
+		SET_STATUS( temp );
+		if ( !(changed & st_i) )
+			goto loop; // I flag didn't change
+		if ( status & st_i )
+			goto handle_sei;
+		goto handle_cli;
+	}
+	#undef POP
+	
+	case 0x08: { // PHP
+		fuint8 temp;
+		CALC_STATUS( temp );
+		PUSH( temp | st_b );
+		goto loop;
+	}
+	
+// Flags
+
+	case 0x38: // SEC
+		c = (unsigned) ~0;
+		goto loop;
+	
+	case 0x18: // CLC
+		c = 0;
+		goto loop;
+		
+	case 0xB8: // CLV
+		status &= ~st_v;
+		goto loop;
+	
+	case 0xD8: // CLD
+		status &= ~st_d;
+		goto loop;
+	
+	case 0xF8: // SED
+		status |= st_d;
+		goto loop;
+	
+	case 0x58: // CLI
+		if ( !(status & st_i) )
+			goto loop;
+		status &= ~st_i;
+	handle_cli: {
+		r->status = status; // update externally-visible I flag
+		blargg_long delta = s.base - cpu->irq_time;
+		if ( delta <= 0 )
+		{
+			if ( TIME < cpu->irq_time )
+				goto loop;
+			goto delayed_cli;
+		}
+		s.base = cpu->irq_time;
+		s_time += delta;
+		if ( s_time < 0 )
+			goto loop;
+		
+		if ( delta >= s_time + 1 )
+		{
+			// delayed irq until after next instruction
+			s.base += s_time + 1;
+			s_time = -1;
+			cpu->irq_time = s.base; // TODO: remove, as only to satisfy debug check in loop
+			goto loop;
+		}
+	delayed_cli:
+		dprintf( "Delayed CLI not supported\n" ); // TODO: implement
+		goto loop;
+	}
+	
+	case 0x78: // SEI
+		if ( status & st_i )
+			goto loop;
+		status |= st_i;
+	handle_sei: {
+		r->status = status; // update externally-visible I flag
+		blargg_long delta = s.base - cpu->end_time;
+		s.base = cpu->end_time;
+		s_time += delta;
+		if ( s_time < 0 )
+			goto loop;
+		dprintf( "Delayed SEI not supported\n" ); // TODO: implement
+		goto loop;
+	}
+	
+// Special
+	
+	case 0x53:{// TAM
+		fuint8 const bits = data; // avoid using data across function call
+		pc++;
+		int i;
+		for ( i = 0; i < 8; i++ )
+			if ( bits & (1 << i) )
+				/* this->cpu.set_mmr( i, a ); */
+				Cpu_set_mmr( this, i, a );
+		goto loop;
+	}
+	
+	case 0x43:{// TMA
+		pc++;
+		byte const* in = cpu->mmr;
+		do
+		{
+			if ( data & 1 )
+				a = *in;
+			in++;
+		}
+		while ( (data >>= 1) != 0 );
+		goto loop;
+	}
+	
+	case 0x03: // ST0
+	case 0x13: // ST1
+	case 0x23:{// ST2
+		fuint16 addr = opcode >> 4;
+		if ( addr )
+			addr++;
+		pc++;
+		FLUSH_TIME();
+		CPU_WRITE_VDP( this, addr, data, TIME );
+		CACHE_TIME();
+		goto loop;
+	}
+	
+	case 0xEA: // NOP
+		goto loop;
+
+	case 0x54: // CSL
+		dprintf( "CSL not supported\n" );
+		illegal_encountered = true;
+		goto loop;
+	
+	case 0xD4: // CSH
+		goto loop;
+	
+	case 0xF4: { // SET
+		//fuint16 operand = GET_MSB();
+		dprintf( "SET not handled\n" );
+		//switch ( data )
+		//{
+		//}
+		illegal_encountered = true;
+		goto loop;
+	}
+	
+// Block transfer
+
+	{
+		fuint16 in_alt;
+		fint16 in_inc;
+		fuint16 out_alt;
+		fint16 out_inc;
+		
+	case 0xE3: // TIA
+		in_alt  = 0;
+		goto bxfer_alt;
+	
+	case 0xF3: // TAI
+		in_alt  = 1;
+	bxfer_alt:
+		in_inc  = in_alt ^ 1;
+		out_alt = in_inc;
+		out_inc = in_alt;
+		goto bxfer;
+	
+	case 0xD3: // TIN
+		in_inc  = 1;
+		out_inc = 0;
+		goto bxfer_no_alt;
+	
+	case 0xC3: // TDD
+		in_inc  = -1;
+		out_inc = -1;
+		goto bxfer_no_alt;
+	
+	case 0x73: // TII
+		in_inc  = 1;
+		out_inc = 1;
+	bxfer_no_alt:
+		in_alt  = 0;
+		out_alt = 0;
+	bxfer: {
+			fuint16 in    = GET_LE16( instr + 0 );
+			fuint16 out   = GET_LE16( instr + 2 );
+			int     count = GET_LE16( instr + 4 );
+			if ( !count )
+				count = 0x10000;
+			pc += 6;
+			WRITE_LOW( 0x100 | (sp - 1), y );
+			WRITE_LOW( 0x100 | (sp - 2), a );
+			WRITE_LOW( 0x100 | (sp - 3), x );
+			FLUSH_TIME();
+			do
+			{
+				// TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O
+				fuint8 t = READ( in );
+				in += in_inc;
+				in &= 0xFFFF;
+				s.time += 6;
+				if ( in_alt )
+					in_inc = -in_inc;
+				WRITE( out, t );
+				out += out_inc;
+				out &= 0xFFFF;
+				if ( out_alt )
+					out_inc = -out_inc;
+			}
+			while ( --count );
+			CACHE_TIME();
+			goto loop;
+		}
+	}
+
+// Illegal
+
+	default:
+		assert( (unsigned) opcode <= 0xFF );
+		dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
+		illegal_encountered = true;
+		goto loop;
+	}
+	assert( false );
+	
+	int result_;
+handle_brk:
+	pc++;
+	result_ = 6;
+	
+interrupt:
+	{
+		s_time += 7;
+		
+		WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
+		WRITE_LOW( 0x100 | (sp - 2), pc );
+		pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ );
+		
+		sp = (sp - 3) | 0x100;
+		fuint8 temp;
+		CALC_STATUS( temp );
+		if ( result_ == 6 )
+			temp |= st_b;
+		WRITE_LOW( sp, temp );
+		
+		status &= ~st_d;
+		status |= st_i;
+		r->status = status; // update externally-visible I flag
+		
+		blargg_long delta = s.base - cpu->end_time;
+		s.base = cpu->end_time;
+		s_time += delta;
+		goto loop;
+	}
+	
+idle_done:
+	s_time = 0;
+out_of_time:
+	pc--;
+	FLUSH_TIME();
+	CPU_DONE( this, TIME, result_ );
+	CACHE_TIME();
+	if ( result_ > 0 )
+		goto interrupt;
+	if ( s_time < 0 )
+		goto loop;
+	
+	s.time = s_time;
+	
+	r->pc = pc;
+	r->sp = GET_SP();
+	r->a = a;
+	r->x = x;
+	r->y = y;
+	
+	{
+		fuint8 temp;
+		CALC_STATUS( temp );
+		r->status = temp;
+	}
+	
+	cpu->state_ = s;
+	cpu->state = &cpu->state_;
+	
+	return illegal_encountered;
+}
+
diff -rNu rb/apps/codecs/libhes/hes_cpu.h rb_hes/apps/codecs/libhes/hes_cpu.h
--- rb/apps/codecs/libhes/hes_cpu.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_cpu.h	2011-05-15 17:09:05.011873500 +0800
@@ -0,0 +1,94 @@
+// PC Engine CPU emulator for use with HES music files
+
+// Game_Music_Emu 0.5.2
+#ifndef HES_CPU_H
+#define HES_CPU_H
+
+#include "blargg_common.h"
+
+typedef blargg_long hes_time_t; // clock cycle count
+typedef unsigned hes_addr_t; // 16-bit address
+
+struct Hes_Emu;
+
+enum { future_hes_time = LONG_MAX / 2 + 1 };
+enum { page_size = 0x2000 };
+enum { page_shift = 13 };
+enum { page_count = 8 };
+
+// Attempt to execute instruction here results in CPU advancing time to
+// lesser of irq_time() and end_time() (or end_time() if IRQs are
+// disabled)
+enum { idle_addr = 0x1FFF };
+	
+// Can read this many bytes past end of a page
+enum { cpu_padding = 8 };
+enum { irq_inhibit = 0x04 };
+
+
+// Cpu state
+struct state_t {
+	uint8_t const* code_map [page_count + 1];
+	hes_time_t base;
+	blargg_long time;
+};
+
+// Cpu registers
+struct registers_t {
+	uint16_t pc;
+	uint8_t a;
+	uint8_t x;
+	uint8_t y;
+	uint8_t status;
+	uint8_t sp;
+};
+
+struct Hes_Cpu {
+	struct registers_t r;
+	
+	// page mapping registers
+	uint8_t mmr [page_count + 1];
+	struct state_t* state; // points to state_ or a local copy within run()
+	struct state_t state_;
+	hes_time_t irq_time;
+	hes_time_t end_time;
+	
+	uint8_t ram [page_size];
+};
+
+// Init cpu state
+void Cpu_init( struct Hes_Cpu* this );
+
+// Reset hes cpu
+void Cpu_reset( struct Hes_Cpu* this );
+
+// Set end_time and run CPU from current time. Returns true if any illegal
+// instructions were encountered.
+bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time ); ICODE_ATTR
+
+void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank ); ICODE_ATTR
+
+// Time of ning of next instruction to be executed
+static inline hes_time_t Cpu_time( struct Hes_Cpu* this )
+{
+	return this->state->time + this->state->base;
+}
+
+static inline uint8_t const* Cpu_get_code( struct Hes_Cpu* this, hes_addr_t addr )
+{
+	return this->state->code_map [addr >> page_shift] + addr
+	#if !defined (BLARGG_NONPORTABLE)
+		% (unsigned) page_size
+	#endif
+	;
+}
+
+static inline int Cpu_update_end_time( struct Hes_Cpu* this, uint8_t reg_status, hes_time_t t, hes_time_t irq )
+{
+	if ( irq < t && !(reg_status & irq_inhibit) ) t = irq;
+	int delta = this->state->base - t;
+	this->state->base = t;
+	return delta;
+}
+
+#endif
diff -rNu rb/apps/codecs/libhes/hes_cpu_io.h rb_hes/apps/codecs/libhes/hes_cpu_io.h
--- rb/apps/codecs/libhes/hes_cpu_io.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_cpu_io.h	2011-05-15 17:09:05.014873700 +0800
@@ -0,0 +1,72 @@
+
+#include "hes_emu.h"
+
+#include "blargg_source.h"
+
+int Cpu_read( struct Hes_Emu* this, hes_addr_t addr )
+{
+	check( addr <= 0xFFFF );
+	int result = *Cpu_get_code( &this->cpu, addr );
+	if ( this->cpu.mmr [addr >> page_shift] == 0xFF )
+		result = Emu_cpu_read( this, addr );
+	return result;
+}
+
+void Cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
+{
+	check( addr <= 0xFFFF );
+	byte* out = this->write_pages [addr >> page_shift];
+	addr &= page_size - 1;
+	if ( out )
+		out [addr] = data;
+	else if ( this->cpu.mmr [addr >> page_shift] == 0xFF )
+		Emu_cpu_write( this, addr, data );
+}
+
+#define CPU_READ_FAST( emu, addr, time, out ) \
+	CPU_READ_FAST_( emu, addr, time, out )
+
+#define CPU_READ_FAST_( emu, addr, time, out ) \
+{\
+	out = READ_PROG( addr );\
+	if ( emu->cpu.mmr [addr >> page_shift] == 0xFF )\
+	{\
+		FLUSH_TIME();\
+		out = Emu_cpu_read( emu, addr );\
+		CACHE_TIME();\
+	}\
+}
+
+#define CPU_WRITE_FAST( emu, addr, data, time ) \
+	CPU_WRITE_FAST_( emu, addr, data, time )
+
+#define CPU_WRITE_FAST_( emu, addr, data, time ) \
+{\
+	byte* out = emu->write_pages [addr >> page_shift];\
+	addr &= page_size - 1;\
+	if ( out )\
+	{\
+		out [addr] = data;\
+	}\
+	else if ( emu->cpu.mmr [addr >> page_shift] == 0xFF )\
+	{\
+		FLUSH_TIME();\
+		Emu_cpu_write( emu, addr, data );\
+		CACHE_TIME();\
+	}\
+}
+
+#define CPU_READ( emu, addr, time ) \
+	Cpu_read( emu, addr )
+
+#define CPU_WRITE( emu, addr, data, time ) \
+	Cpu_write( emu, addr, data )
+
+#define CPU_WRITE_VDP( emu, addr, data, time ) \
+	Cpu_write_vdp( emu, addr, data )
+
+#define CPU_SET_MMR( emu, page, bank ) \
+	Emu_cpu_set_mmr( emu, page, bank )
+
+#define CPU_DONE( emu, time, result_out ) \
+	result_out = Cpu_done( emu )
diff -rNu rb/apps/codecs/libhes/hes_emu.c rb_hes/apps/codecs/libhes/hes_emu.c
--- rb/apps/codecs/libhes/hes_emu.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_emu.c	2011-05-15 17:09:05.023874200 +0800
@@ -0,0 +1,890 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "hes_emu.h"
+
+#include "blargg_endian.h"
+#include "blargg_source.h"
+
+/* Copyright (C) 2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+int const timer_mask  = 0x04;
+int const vdp_mask    = 0x02;
+int const i_flag_mask = 0x04;
+int const unmapped    = 0xFF;
+
+long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
+
+int const stereo = 2; // number of channels for stereo
+int const silence_max = 6; // seconds
+int const silence_threshold = 0x10;
+long const fade_block_size = 512;
+int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
+
+const char gme_wrong_file_type [] ICONST_ATTR = "Wrong file type for this emulator";
+
+void clear_track_vars( struct Hes_Emu* this )
+{
+	this->current_track_   = -1;
+	this->out_time         = 0;
+	this->emu_time         = 0;
+	this->emu_track_ended_ = true;
+	this->track_ended     = true;
+	this->fade_start       = LONG_MAX / 2 + 1;
+	this->fade_step        = 1;
+	this->silence_time     = 0;
+	this->silence_count    = 0;
+	this->buf_remain       = 0;
+}
+
+
+void Hes_init( struct Hes_Emu* this )
+{
+	this->sample_rate_ = 0;
+	this->mute_mask_   = 0;
+	this->tempo_       = 1.0;
+
+	// defaults
+	this->max_initial_silence = 2;
+	this->ignore_silence     = false;
+
+	// Unload
+	this->voice_count_ = 0;
+	clear_track_vars( this );
+                      
+	this->timer.raw_load = 0;
+	this->silence_lookahead = 6;
+	Sound_set_gain( this, 1.11 );
+
+	Apu_init( &this->apu );
+	Adpcm_init( &this->adpcm );
+	Cpu_init( &this->cpu );
+
+	/* Set default track count */
+	this->track_count = 255;
+
+	/* Clear m3u playlist */
+	M3u_clear( &this->m3u );
+}
+
+static blargg_err_t check_hes_header( void const* header )
+{
+	if ( memcmp( header, "HESM", 4 ) )
+		return gme_wrong_file_type;
+	return 0;
+}
+
+// Setup
+
+blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
+{
+	// Unload
+	this->voice_count_ = 0;
+	clear_track_vars( this );
+             
+	assert( offsetof (struct header_t,unused [4]) == header_size );
+	RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, unmapped ) );
+	
+	RETURN_ERR( check_hes_header( this->header.tag ) );
+	
+	/* if ( header_.vers != 0 )
+		warning( "Unknown file version" );
+	
+	if ( memcmp( header_.data_tag, "DATA", 4 ) )
+		warning( "Data header missing" );
+	
+	if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
+		warning( "Unknown header data" ); */
+	
+	// File spec supports multiple blocks, but I haven't found any, and
+	// many files have bad sizes in the only block, so it's simpler to
+	// just try to load the damn data as best as possible.
+	
+	long addr = get_le32( this->header.addr );
+	/* long rom_size = get_le32( this->header.size ); */
+	long const rom_max = 0x100000;
+	if ( addr & ~(rom_max - 1) )
+	{
+		/* warning( "Invalid address" ); */
+		addr &= rom_max - 1;
+	}
+	/* if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
+		warning( "Invalid size" );
+	
+	if ( rom_size != rom.file_size() )
+	{
+		if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) )
+			warning( "Multiple DATA not supported" );
+		else if ( size < rom.file_size() )
+			warning( "Extra file data" );
+		else
+			warning( "Missing file data" );
+	} */
+	
+	Rom_set_addr( &this->rom, addr );
+	
+	this->voice_count_ = osc_count + adpcm_osc_count;
+	
+	Apu_volume( &this->apu, this->gain_ );
+	Adpcm_volume( &this->adpcm, this->gain_ );
+
+    // Setup buffer	
+	this->clock_rate_ = 7159091;
+	Buffer_clock_rate( &this->buf, 7159091 );
+	this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
+	
+	Sound_set_tempo( this, this->tempo_ );
+	Sound_mute_voices( this, this->mute_mask_ );
+	
+	// Reset track count
+	this->track_count = 255;
+	
+	// Clear m3u playlist
+	M3u_clear( &this->m3u );
+	return 0;
+}
+
+
+// Emulation
+
+void recalc_timer_load( struct Hes_Emu* this ); ICODE_ATTR
+void recalc_timer_load( struct Hes_Emu* this )
+{
+	this->timer.load = this->timer.raw_load * this->timer_base + 1;
+}
+
+// Hardware
+
+void irq_changed( struct Hes_Emu* this ); ICODE_ATTR
+void run_until( struct Hes_Emu* this, hes_time_t present ); ICODE_ATTR
+void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
+{
+	switch ( addr )
+	{
+	case 0:
+		this->vdp.latch = data & 0x1F;
+		break;
+	
+	case 2:
+		if ( this->vdp.latch == 5 )
+		{
+			/* if ( data & 0x04 )
+				warning( "Scanline interrupt unsupported" ); */
+			run_until( this, Cpu_time( &this->cpu ) );
+			this->vdp.control = data;
+			irq_changed( this );
+		}
+		else
+		{
+			dprintf( "VDP not supported: $%02X <- $%02X\n", this->vdp.latch, data );
+		}
+		break;
+	
+	case 3:
+		dprintf( "VDP MSB not supported: $%02X <- $%02X\n", this->vdp.latch, data );
+		break;
+	}
+}
+
+int Cpu_done( struct Hes_Emu* this )
+{
+	check( time() >= end_time() ||
+			(!(r.status & i_flag_mask) && time() >= irq_time()) );
+	
+	if ( !(this->cpu.r.status & i_flag_mask) )
+	{
+		hes_time_t present = Cpu_time( &this->cpu );
+		
+		if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
+		{
+			this->timer.fired = true;
+			this->irq.timer = future_hes_time;
+			irq_changed( this ); // overkill, but not worth writing custom code
+			#if defined (GME_FRAME_HOOK_DEFINED)
+			{
+				unsigned const threshold = period_60hz / 30;
+				unsigned long elapsed = present - last_frame_hook;
+				if ( elapsed - period_60hz + threshold / 2 < threshold )
+				{
+					last_frame_hook = present;
+					GME_FRAME_HOOK( this );
+				}
+			}
+			#endif
+			return 0x0A;
+		}
+		
+		if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
+		{
+			// work around for bugs with music not acknowledging VDP
+			//run_until( present );
+			//irq.vdp = future_hes_time;
+			//irq_changed();
+			#if defined(GME_FRAME_HOOK_DEFINED)
+				last_frame_hook = present;
+				GME_FRAME_HOOK( this );
+			#endif
+			return 0x08;
+		}
+	}
+	return 0;
+}
+
+void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
+{
+	hes_time_t time = Cpu_time( &this->cpu );
+	if ( (unsigned) (addr - start_addr) <= end_addr - start_addr )
+	{
+		GME_APU_HOOK( this, addr - apu.start_addr, data );
+		// avoid going way past end when a long block xfer is writing to I/O space
+		hes_time_t t = min( time, this->cpu.end_time + 8 );
+		Apu_write_data( &this->apu, t, addr, data );
+		return;
+	}
+	
+	if ( (unsigned) (addr - io_addr) < io_size )
+	{
+		hes_time_t t = min( time, this->cpu.end_time + 6 );
+		Adpcm_write_data( &this->adpcm, t, addr, data );
+		return;
+	}
+	
+	switch ( addr )
+	{
+	case 0x0000:
+	case 0x0002:
+	case 0x0003:
+		Cpu_write_vdp( this, addr, data );
+		return;
+	
+	case 0x0C00: {
+		run_until( this, time );
+		this->timer.raw_load = (data & 0x7F) + 1;
+		recalc_timer_load( this );
+		this->timer.count = this->timer.load;
+		break;
+	}
+	
+	case 0x0C01:
+		data &= 1;
+		if ( this->timer.enabled == data )
+			return;
+		run_until( this, time );
+		this->timer.enabled = data;
+		if ( data )
+			this->timer.count = this->timer.load;
+		break;
+	
+	case 0x1402:
+		run_until( this, time );
+		this->irq.disables = data;
+		
+		// flag questionable values
+		if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) {
+			dprintf( "Int mask: $%02X\n", data );
+		}
+		break;
+	
+	case 0x1403:
+		run_until( this, time );
+		if ( this->timer.enabled )
+			this->timer.count = this->timer.load;
+		this->timer.fired = false;
+		break;
+	
+#ifndef NDEBUG
+	case 0x1000: // I/O port
+	case 0x0402: // palette
+	case 0x0403:
+	case 0x0404:
+	case 0x0405:
+		return;
+		
+	default:
+		dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
+		return;
+#endif
+	}
+	
+	irq_changed( this );
+}
+
+int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
+{
+	hes_time_t time = Cpu_time( &this->cpu );
+	addr &= page_size - 1;
+	switch ( addr )
+	{
+	case 0x0000:
+		if ( this->irq.vdp > time )
+			return 0;
+		this->irq.vdp = future_hes_time;
+		run_until( this, time );
+		irq_changed( this );
+		return 0x20;
+		
+	case 0x0002:
+	case 0x0003:
+		dprintf( "VDP read not supported: %d\n", addr );
+		return 0;
+	
+	case 0x0C01:
+		//return timer.enabled; // TODO: remove?
+	case 0x0C00:
+		run_until( this, time );
+		dprintf( "Timer count read\n" );
+		return (unsigned) (this->timer.count - 1) / this->timer_base;
+	
+	case 0x1402:
+		return this->irq.disables;
+	
+	case 0x1403:
+		{
+			int status = 0;
+			if ( this->irq.timer <= time ) status |= timer_mask;
+			if ( this->irq.vdp   <= time ) status |= vdp_mask;
+			return status;
+		}
+		
+	case 0x180A:
+	case 0x180B:
+	case 0x180C:
+	case 0x180D:
+		return Adpcm_read_data( &this->adpcm, time, addr );
+		
+	#ifndef NDEBUG
+		case 0x1000: // I/O port
+		// case 0x180C: // CD-ROM
+		// case 0x180D:
+			break;
+		
+		default:
+			dprintf( "unmapped read  $%04X\n", addr );
+	#endif
+	}
+	
+	return unmapped;
+}
+
+// see hes_cpu_io.h for core read/write functions
+
+// Emulation
+
+void run_until( struct Hes_Emu* this, hes_time_t present )
+{
+	while ( this->vdp.next_vbl < present )
+		this->vdp.next_vbl += this->play_period;
+	
+	hes_time_t elapsed = present - this->timer.last_time;
+	if ( elapsed > 0 )
+	{
+		if ( this->timer.enabled )
+		{
+			this->timer.count -= elapsed;
+			if ( this->timer.count <= 0 )
+				this->timer.count += this->timer.load;
+		}
+		this->timer.last_time = present;
+	}
+}
+
+void irq_changed( struct Hes_Emu* this )
+{
+	hes_time_t present = Cpu_time( &this->cpu );
+	
+	if ( this->irq.timer > present )
+	{
+		this->irq.timer = future_hes_time;
+		if ( this->timer.enabled && !this->timer.fired )
+			this->irq.timer = present + this->timer.count;
+	}
+	
+	if ( this->irq.vdp > present )
+	{
+		this->irq.vdp = future_hes_time;
+		if ( this->vdp.control & 0x08 )
+			this->irq.vdp = this->vdp.next_vbl;
+	}
+	
+	hes_time_t time = future_hes_time;
+	if ( !(this->irq.disables & timer_mask) ) time = this->irq.timer;
+	if ( !(this->irq.disables &   vdp_mask) ) time = min( time, this->irq.vdp );
+	
+	// Set cpu irq time
+	this->cpu.state->time += Cpu_update_end_time( &this->cpu, this->cpu.r.status, 
+					this->cpu.end_time, (this->cpu.irq_time = time) );
+}
+
+static void adjust_time( blargg_long* time, hes_time_t delta ); ICODE_ATTR
+static void adjust_time( blargg_long* time, hes_time_t delta )
+{
+	if ( *time < future_hes_time )
+	{
+		*time -= delta;
+		if ( *time < 0 )
+			*time = 0;
+	}
+}
+
+blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ ); ICODE_ATTR
+blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
+{
+	blip_time_t duration = *duration_; // cache
+	
+	Cpu_run( this, duration );
+	/* warning( "Emulation error (illegal instruction)" ); */
+	
+	check( time() >= duration );
+	//check( time() - duration < 20 ); // Txx instruction could cause going way over
+	
+	run_until( this, duration );
+	
+	// end time frame
+	this->timer.last_time -= duration;
+	this->vdp.next_vbl    -= duration;
+	#if defined (GME_FRAME_HOOK_DEFINED)
+		last_frame_hook -= *duration;
+	#endif
+
+	// End cpu frame
+	this->cpu.state_.base -= duration;
+	if ( this->cpu.irq_time < future_hes_time ) this->cpu.irq_time -= duration;
+	if ( this->cpu.end_time < future_hes_time ) this->cpu.end_time -= duration;
+	
+	adjust_time( &this->irq.timer, duration );
+	adjust_time( &this->irq.vdp,   duration );
+	Apu_end_frame( &this->apu, duration );
+	Adpcm_end_frame( &this->adpcm, duration );
+	
+	return 0;
+}
+
+blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out ); ICODE_ATTR
+blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
+{
+	long remain = count;
+	while ( remain )
+	{
+		remain -= Buffer_read_samples( &this->buf, &out [count - remain], remain );
+		if ( remain )
+		{
+			if ( this->buf_changed_count != Buffer_channels_changed_count( &this->buf ) )
+			{
+				this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
+				// Remute voices
+				Sound_mute_voices( this, this->mute_mask_ );
+			}
+
+			int msec = Buffer_length( &this->buf );
+			blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000;
+			RETURN_ERR( run_clocks( this, &clocks_emulated ) );
+			assert( clocks_emulated );
+			Buffer_end_frame( &this->buf, clocks_emulated );
+		}
+	}
+	return 0;
+}
+
+
+// Music emu
+
+blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long rate )
+{
+	require( !this->sample_rate_ ); // sample rate can't be changed once set
+	Buffer_init( &this->buf );
+	RETURN_ERR( Buffer_set_sample_rate( &this->buf, rate, 1000 / 20 ) );
+	
+	// Set bass frequency
+	Buffer_bass_freq( &this->buf, 60 );
+	
+	this->sample_rate_ = rate;
+	return 0;
+}
+
+void Sound_mute_voice( struct Hes_Emu* this, int index, bool mute )
+{
+	require( (unsigned) index < (unsigned) this->voice_count_ );
+	int bit = 1 << index;
+	int mask = this->mute_mask_ | bit;
+	if ( !mute )
+		mask ^= bit;
+	Sound_mute_voices( this, mask );
+}
+
+void Sound_mute_voices( struct Hes_Emu* this, int mask )
+{
+	require( this->sample_rate_ ); // sample rate must be set first
+	this->mute_mask_ = mask;
+
+	// Set adpcm voice
+	struct channel_t ch = Buffer_channel( &this->buf );
+	if ( mask & (1 << this->voice_count_ ) )
+		Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 );
+	else
+		Adpcm_set_output( &this->adpcm, 0, ch.center, ch.left, ch.right );
+	
+	// Set apu voices
+	int i = this->voice_count_ - 1;
+	for ( ; i--; )
+	{
+		if ( mask & (1 << i) )
+		{
+			Apu_osc_output( &this->apu, i, 0, 0, 0 );
+		}
+		else
+		{	
+			assert( (ch.center && ch.left && ch.right) ||
+					(!ch.center && !ch.left && !ch.right) ); // all or nothing
+			Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right );
+		}
+	}
+}
+
+void Sound_set_tempo( struct Hes_Emu* this, double t )
+{
+	require( this->sample_rate_ ); // sample rate must be set first
+	double const min = 0.02;
+	double const max = 4.00;
+	if ( t < min ) t = min;
+	if ( t > max ) t = max;
+	this->play_period = (hes_time_t) (period_60hz / t);
+	this->timer_base = (int) (1024 / t);
+	recalc_timer_load( this );
+	this->tempo_ = t;
+}
+
+void fill_buf( struct Hes_Emu* this ); ICODE_ATTR
+blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
+{
+	clear_track_vars( this );
+	
+	// Remap track if playlist available
+	if ( this->m3u.size > 0 ) {
+		struct entry_t* e = &this->m3u.entries[track];
+		if ( e->track >= 0 )
+		{
+			track = e->track;
+			/* TODO: Is this necessary?*/
+			/* if ( !(this_->m3u.type_->flags_ & 0x02) )
+				track -= e.decimal_track; */
+		}
+		
+		if ( track >= 255 )
+			track = 0;
+	}
+	
+	this->current_track_ = track;
+	
+	Buffer_clear( &this->buf );
+	
+	memset( this->cpu.ram, 0, sizeof this->cpu.ram ); // some HES music relies on zero fill
+	memset( this->sgx, 0, sizeof this->sgx );
+	
+	Apu_reset( &this->apu );
+	Adpcm_reset( &this->adpcm );
+	Cpu_reset( &this->cpu );
+	
+	unsigned i;
+	for ( i = 0; i < sizeof this->header.banks; i++ )
+		Cpu_set_mmr( this, i, this->header.banks [i] );
+	Cpu_set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
+	
+	this->irq.disables  = timer_mask | vdp_mask;
+	this->irq.timer     = future_hes_time;
+	this->irq.vdp       = future_hes_time;
+	
+	this->timer.enabled = false;
+	this->timer.raw_load= 0x80;
+	this->timer.count   = this->timer.load;
+	this->timer.fired   = false;
+	this->timer.last_time = 0;
+	
+	this->vdp.latch     = 0;
+	this->vdp.control   = 0;
+	this->vdp.next_vbl  = 0;
+	
+	this->cpu.ram [0x1FF] = (idle_addr - 1) >> 8;
+	this->cpu.ram [0x1FE] = (idle_addr - 1) & 0xFF;
+	this->cpu.r.sp = 0xFD;
+	this->cpu.r.pc = get_le16( this->header.init_addr );
+	this->cpu.r.a  = track;
+	
+	recalc_timer_load( this );
+	this->last_frame_hook = 0;
+	
+	this->emu_track_ended_ = false;
+	this->track_ended     = false;
+	
+	if ( !this->ignore_silence )
+	{
+		// play until non-silence or end of track
+		long end;
+		for ( end =this-> max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; )
+		{
+			fill_buf( this );
+			if ( this->buf_remain | (int) this->emu_track_ended_ )
+				break;
+		}
+		
+		this->emu_time      = this->buf_remain;
+		this->out_time      = 0;
+		this->silence_time  = 0;
+		this->silence_count = 0;
+	}
+	/* return track_ended() ? warning() : 0; */
+	return 0;
+}
+
+// Tell/Seek
+
+blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+{
+	blargg_long sec = msec / 1000;
+	msec -= sec * 1000;
+	return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
+}
+
+long Track_tell( struct Hes_Emu* this )
+{
+	blargg_long rate = this->sample_rate_ * stereo;
+	blargg_long sec = this->out_time / rate;
+	return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+}
+
+blargg_err_t Track_seek( struct Hes_Emu* this, long msec )
+{
+	blargg_long time = msec_to_samples( msec, this->sample_rate_ );
+	if ( time < this->out_time )
+		RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
+	return Track_skip( this, time - this->out_time );
+}
+
+blargg_err_t skip_( struct Hes_Emu* this, long count ); ICODE_ATTR
+blargg_err_t skip_( struct Hes_Emu* this, long count )
+{
+	// for long skip, mute sound
+	const long threshold = 30000;
+	if ( count > threshold )
+	{
+		int saved_mute = this->mute_mask_;
+		Sound_mute_voices( this, ~0 );
+		
+		while ( count > threshold / 2 && !this->emu_track_ended_ )
+		{
+			RETURN_ERR( play_( this, buf_size, this->buf_ ) );
+			count -= buf_size;
+		}
+		
+		Sound_mute_voices( this, saved_mute );
+	}
+	
+	while ( count && !this->emu_track_ended_ )
+	{
+		long n = buf_size;
+		if ( n > count )
+			n = count;
+		count -= n;
+		RETURN_ERR( play_( this, n, this->buf_ ) );
+	}
+	return 0;
+}
+
+blargg_err_t Track_skip( struct Hes_Emu* this, long count )
+{
+	require( this->current_track_ >= 0 ); // start_track() must have been called already
+	this->out_time += count;
+	
+	// remove from silence and buf first
+	{
+		long n = min( count, this->silence_count );
+		this->silence_count -= n;
+		count -= n;
+		
+		n = min( count, this->buf_remain );
+		this->buf_remain -= n;
+		count -= n;
+	}
+		
+	if ( count && !this->emu_track_ended_ )
+	{
+		this->emu_time += count;
+		
+		// End track if error
+		if ( skip_( this, count ) )
+			this->emu_track_ended_ = true;
+	}
+	
+	if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
+		this->track_ended |= this->emu_track_ended_;
+	
+	return 0;
+}
+
+
+
+// Fading
+
+void Track_set_fade( struct Hes_Emu* this, long start_msec, long length_msec )
+{
+	this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
+	this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
+}
+
+// unit / pow( 2.0, (double) x / step )
+static int int_log( blargg_long x, int step, int unit ); ICODE_ATTR
+static int int_log( blargg_long x, int step, int unit )
+{
+	int shift = x / step;
+	int fraction = (x - shift * step) * unit / step;
+	return ((unit - fraction) + (fraction >> 1)) >> shift;
+}
+
+void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out ); ICODE_ATTR
+void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out )
+{
+	int i;
+	for ( i = 0; i < out_count; i += fade_block_size )
+	{
+		int const shift = 14;
+		int const unit = 1 << shift;
+		int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
+				this->fade_step, unit );
+		if ( gain < (unit >> fade_shift) )
+			this->track_ended = this->emu_track_ended_ = true;
+		
+		sample_t* io = &out [i];
+		int count;
+		for ( count = min( fade_block_size, out_count - i ); count; --count )
+		{
+			*io = (sample_t) ((*io * gain) >> shift);
+			++io;
+		}
+	}
+}
+
+// Silence detection
+
+void emu_play( struct Hes_Emu* this, long count, sample_t* out ); ICODE_ATTR
+void emu_play( struct Hes_Emu* this, long count, sample_t* out )
+{
+	check( current_track_ >= 0 );
+	this->emu_time += count;
+	if ( this->current_track_ >= 0 && !this->emu_track_ended_ ) {
+		
+		// End track if error
+		if ( play_( this, count, out ) )
+			this->emu_track_ended_ = true;
+	}
+	else
+		memset( out, 0, count * sizeof *out );
+}
+
+// number of consecutive silent samples at end
+static long count_silence( sample_t* begin, long size ); ICODE_ATTR
+static long count_silence( sample_t* begin, long size )
+{
+	sample_t first = *begin;
+	*begin = silence_threshold; // sentinel
+	sample_t* p = begin + size;
+	while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
+	*begin = first;
+	return size - (p - begin);
+}
+
+// fill internal buffer and check it for silence
+void fill_buf( struct Hes_Emu* this )
+{
+	assert( !this->buf_remain );
+	if ( !this->emu_track_ended_ )
+	{
+		emu_play( this, buf_size, this->buf_ );
+		long silence = count_silence( this->buf_, buf_size );
+		if ( silence < buf_size )
+		{
+			this->silence_time = this->emu_time - silence;
+			this->buf_remain   = buf_size;
+			return;
+		}
+	}
+	this->silence_count += buf_size;
+}
+
+blargg_err_t Hes_play( struct Hes_Emu* this, long out_count, sample_t* out )
+{
+	if ( this->track_ended )
+	{
+		memset( out, 0, out_count * sizeof *out );
+	}
+	else
+	{
+		require( this->current_track_ >= 0 );
+		require( out_count % stereo == 0 );
+		
+		assert( this->emu_time >= this->out_time );
+		
+		// prints nifty graph of how far ahead we are when searching for silence
+		//dprintf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
+		
+		long pos = 0;
+		if ( this->silence_count )
+		{
+			// during a run of silence, run emulator at >=2x speed so it gets ahead
+			long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
+			while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
+				fill_buf( this );
+			
+			// fill with silence
+			pos = min( this->silence_count, out_count );
+			memset( out, 0, pos * sizeof *out );
+			this->silence_count -= pos;
+			
+			if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate_ )
+			{
+				this->track_ended  = this->emu_track_ended_ = true;
+				this->silence_count = 0;
+				this->buf_remain    = 0;
+			}
+		}
+		
+		if ( this->buf_remain )
+		{
+			// empty silence buf
+			long n = min( this->buf_remain, out_count - pos );
+			memcpy( &out [pos], this->buf_ + (buf_size - this->buf_remain), n * sizeof *out );
+			this->buf_remain -= n;
+			pos += n;
+		}
+		
+		// generate remaining samples normally
+		long remain = out_count - pos;
+		if ( remain )
+		{
+			emu_play( this, remain, out + pos );
+			this->track_ended |= this->emu_track_ended_;
+			
+			if ( !this->ignore_silence || this->out_time > this->fade_start )
+			{
+				// check end for a new run of silence
+				long silence = count_silence( out + pos, remain );
+				if ( silence < remain )
+					this->silence_time = this->emu_time - silence;
+				
+				if ( this->emu_time - this->silence_time >= buf_size )
+					fill_buf( this ); // cause silence detection on next play()
+			}
+		}
+		
+		if ( this->out_time > this->fade_start )
+			handle_fade( this, out_count, out );
+	}
+	this->out_time += out_count;
+	return 0;
+}
diff -rNu rb/apps/codecs/libhes/hes_emu.h rb_hes/apps/codecs/libhes/hes_emu.h
--- rb/apps/codecs/libhes/hes_emu.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/hes_emu.h	2011-05-15 17:09:05.028874500 +0800
@@ -0,0 +1,237 @@
+// TurboGrafx-16/PC Engine HES music file emulator
+
+// Game_Music_Emu 0.5.2
+#ifndef HES_EMU_H
+#define HES_EMU_H
+
+#include "blargg_source.h"
+
+#include "multi_buffer.h"
+#include "rom_data.h"
+#include "hes_apu.h"
+#include "hes_apu_adpcm.h"
+#include "hes_cpu.h"
+#include "m3u_playlist.h"
+
+typedef short sample_t;
+
+enum { buf_size = 2048 };
+
+// HES file header
+enum { header_size = 0x20 };
+struct header_t
+{
+	byte tag [4];
+	byte vers;
+	byte first_track;
+	byte init_addr [2];
+	byte banks [8];
+	byte data_tag [4];
+	byte size [4];
+	byte addr [4];
+	byte unused [4];
+};
+
+
+struct timer_t {
+	hes_time_t last_time;
+	blargg_long count;
+	blargg_long load;
+	int raw_load;
+	byte enabled;
+	byte fired;
+};
+	
+struct vdp_t {
+	hes_time_t next_vbl;
+	byte latch;
+	byte control;
+};
+	
+struct irq_t {
+	hes_time_t timer;
+	hes_time_t vdp;
+	byte disables;
+};
+
+
+struct Hes_Emu {
+	// Header for currently loaded file
+	struct header_t header;
+	
+	// M3u Playlist
+	struct M3u_Playlist m3u;
+	
+	// Hes Cpu
+	struct Hes_Cpu cpu;
+	byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space	
+
+	hes_time_t play_period;
+	hes_time_t last_frame_hook;
+	int timer_base;
+	
+	struct timer_t timer;
+	struct vdp_t vdp;
+	struct irq_t irq;
+	
+	// Sound
+	struct Hes_Apu apu;
+	struct Hes_Apu_Adpcm adpcm;
+
+	struct Stereo_Buffer buf;
+	long clock_rate_;
+	long sample_rate_;
+	unsigned buf_changed_count;
+	int voice_count_;
+	double tempo_;
+	double gain_;
+	
+	// track-specific
+	byte track_count;
+	volatile bool track_ended;
+	int current_track_;
+	blargg_long out_time;  // number of samples played since start of track
+	blargg_long emu_time;  // number of samples emulator has generated since start of track
+	bool emu_track_ended_; // emulator has reached end of track
+	
+	// fading
+	blargg_long fade_start;
+	int fade_step;
+	
+	// silence detection
+	// Disable automatic end-of-track detection and skipping of silence at beginning
+	bool ignore_silence;
+	
+	int max_initial_silence;
+	int mute_mask_;
+	int silence_lookahead; // speed to run emulator when looking ahead for silence
+	long silence_time;     // number of samples where most recent silence began
+	long silence_count;    // number of samples of silence to play before using buf
+	long buf_remain;       // number of samples left in silence buffer
+	
+	sample_t buf_ [buf_size];
+	
+	// rom & ram
+	struct Rom_Data rom;
+	byte sgx [3 * page_size + cpu_padding];
+};
+
+
+// Basic functionality
+// Initializes Hes_Emu structure
+void Hes_init( struct Hes_Emu* this );
+
+// Stops (clear) Hes_Emu structure
+void Hes_stop( struct Hes_Emu* this );
+
+// Loads a file from memory
+blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size );
+
+// Set output sample rate. Must be called only once before loading file.
+blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long sample_rate );
+
+// Start a track, where 0 is the first track. Also clears warning string.
+blargg_err_t Hes_start_track( struct Hes_Emu* this, int );
+
+// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
+// errors set warning string, and major errors also end track.
+blargg_err_t Hes_play( struct Hes_Emu* this, long count, sample_t* buf ); ICODE_ATTR
+
+// Loads an m3u playlist
+blargg_err_t Hes_load_m3u( struct Hes_Emu* this, const char* path );
+
+
+// Track status/control
+// Number of milliseconds (1000 msec = 1 second) played since ning of track
+long Track_tell( struct Hes_Emu* this );
+
+// Seek to new time in track. Seeking backwards or far forward can take a while.
+blargg_err_t Track_seek( struct Hes_Emu* this, long msec );
+
+// Skip n samples
+blargg_err_t Track_skip( struct Hes_Emu* this, long n );
+
+// Set start time and length of track fade out. Once fade ends track_ended() returns
+// true. Fade time can be changed while track is playing.
+void Track_set_fade( struct Hes_Emu* this, long start_msec, long length_msec );
+
+// Get track length in milliseconds
+static inline long Track_get_length( struct Hes_Emu* this, int n )
+{
+    long length = 120 * 1000;  /* 2 minutes */ 
+	if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
+		struct entry_t* entry = &this->m3u.entries [n];
+		length = entry->length;
+		if ( length <= 0 ) {
+			long intro = entry->intro > 0 ? entry->intro : 120 * 1000;
+			long loop = entry->loop > 0 ? entry->loop : 0;
+			length = intro + 2 * loop; /* intro + 2 loops */
+		}
+	} 
+	
+	return length;
+}
+
+
+// Sound customization
+// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed.
+// Track length as returned by track_info() assumes a tempo of 1.0.
+void Sound_set_tempo( struct Hes_Emu* this, double );
+
+// Mute/unmute voice i, where voice 0 is first voice
+void Sound_mute_voice( struct Hes_Emu* this, int index, bool mute );
+
+// Set muting state of all voices at once using a bit mask, where -1 mutes them all,
+// 0 unmutes them all, 0x01 mutes just the first voice, etc.
+void Sound_mute_voices( struct Hes_Emu* this, int mask );
+
+// Change overall output amplitude, where 1.0 results in minimal clamping.
+// Must be called before set_sample_rate().
+static inline void Sound_set_gain( struct Hes_Emu* this, double g )
+{
+	assert( !this->sample_rate_ ); // you must set gain before setting sample rate
+	this->gain_ = g;
+}
+
+
+// Emulation (You shouldn't touch these)
+
+int Cpu_read( struct Hes_Emu* this, hes_addr_t ); ICODE_ATTR
+void Cpu_write( struct Hes_Emu* this, hes_addr_t, int ); ICODE_ATTR
+void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data ); ICODE_ATTR
+int Cpu_done( struct Hes_Emu* this ); ICODE_ATTR
+
+int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t ); ICODE_ATTR
+void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t, int data ); ICODE_ATTR
+
+static inline byte const* Emu_cpu_set_mmr( struct Hes_Emu* this, int page, int bank )
+{
+	this->write_pages [page] = 0;
+	if ( bank < 0x80 )
+		return Rom_at_addr( &this->rom, bank * (blargg_long) page_size );
+	
+	byte* data = 0;
+	switch ( bank )
+	{
+		case 0xF8:
+			data = this->cpu.ram;
+			break;
+		
+		case 0xF9:
+		case 0xFA:
+		case 0xFB:
+			data = &this->sgx [(bank - 0xF9) * page_size];
+			break;
+		
+		default:
+			if ( bank != 0xFF ) {
+				dprintf( "Unmapped bank $%02X\n", bank );
+			}
+			return this->rom.unmapped;
+	}
+	
+	this->write_pages [page] = data;
+	return data;
+}
+
+#endif
diff -rNu rb/apps/codecs/libhes/libhes.make rb_hes/apps/codecs/libhes/libhes.make
--- rb/apps/codecs/libhes/libhes.make	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/libhes.make	2011-05-15 17:09:05.031874700 +0800
@@ -0,0 +1,17 @@
+
+# libhes
+HESLIB := $(CODECDIR)/libhes.a
+HESLIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libhes/SOURCES)
+HESLIB_OBJ := $(call c2obj, $(HESLIB_SRC))
+OTHER_SRC += $(HESLIB_SRC)
+
+$(HESLIB): $(HESLIB_OBJ)
+	$(SILENT)$(shell rm -f $@)
+	$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
+
+HESFLAGS = $(filter-out -O%,$(CODECFLAGS)) -fno-strict-aliasing
+HESFLAGS += -O1
+
+$(CODECDIR)/libhes/%.o: $(ROOTDIR)/apps/codecs/libhes/%.c
+	$(SILENT)mkdir -p $(dir $@)
+	$(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) $(HESFLAGS) -c $< -o $@
diff -rNu rb/apps/codecs/libhes/m3u_playlist.c rb_hes/apps/codecs/libhes/m3u_playlist.c
--- rb/apps/codecs/libhes/m3u_playlist.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/m3u_playlist.c	2011-05-15 17:09:05.035874900 +0800
@@ -0,0 +1,413 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "m3u_playlist.h"
+#include "hes_emu.h"
+
+#include <string.h>
+#if defined (ROCKBOX)
+#include <codecs/lib/codeclib.h>
+#else
+#include <fcntl.h>
+#endif
+
+/* Copyright (C) 2006 Shay Green. this module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. this
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+static char* skip_white( char* in )
+{
+	while ( *in == ' ' || *in == '\t' )
+		in++;
+	return in;
+}
+
+inline unsigned from_dec( unsigned n ) { return n - '0'; }
+
+static char* parse_filename( char* in, struct entry_t* entry )
+{
+	entry->file = in;
+	entry->type = "";
+	char* out = in;
+	while ( 1 )
+	{
+		int c = *in;
+		if ( !c ) break;
+		in++;
+		
+		if ( c == ',' ) // commas in filename
+		{
+			char* p = skip_white( in );
+			if ( *p == '$' || from_dec( *p ) <= 9 )
+			{
+				in = p;
+				break;
+			}
+		}
+		
+		if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix
+		{
+			entry->type = ++in;
+			while ( (c = *in) != 0 && c != ',' )
+				in++;
+			if ( c == ',' )
+			{
+				*in++ = 0; // terminate type
+				in = skip_white( in );
+			}
+			break;
+		}
+		
+		if ( c == '\\' ) // \ prefix for special characters
+		{
+			c = *in;
+			if ( !c ) break;
+			in++;
+		}
+		*out++ = (char) c;
+	}
+	*out = 0; // terminate string
+	return in;
+}
+
+static char* next_field( char* in, int* result )
+{
+	while ( 1 )
+	{
+		in = skip_white( in );
+		
+		if ( !*in )
+			break;
+		
+		if ( *in == ',' )
+		{
+			in++;
+			break;
+		}
+		
+		*result = 1;
+		in++;
+	}
+	return skip_white( in );
+}
+
+static char* parse_int_( char* in, int* out )
+{
+	int n = 0;
+	while ( 1 )
+	{
+		unsigned d = from_dec( *in );
+		if ( d > 9 )
+			break;
+		in++;
+		n = n * 10 + d;
+		*out = n;
+	}
+	return in;
+}
+
+static char* parse_int( char* in, int* out, int* result )
+{
+	return next_field( parse_int_( in, out ), result );
+}
+
+// Returns 16 or greater if not hex
+inline int from_hex_char( int h )
+{
+	h -= 0x30;
+	if ( (unsigned) h > 9 )
+		h = ((h - 0x11) & 0xDF) + 10;
+	return h;
+}
+
+static char* parse_track( char* in, struct entry_t* entry, int* result )
+{
+	if ( *in == '$' )
+	{
+		in++;
+		int n = 0;
+		while ( 1 )
+		{
+			int h = from_hex_char( *in );
+			if ( h > 15 )
+				break;
+			in++;
+			n = n * 16 + h;
+			entry->track = n;
+		}
+	}
+	else
+	{
+		in = parse_int_( in, &entry->track );
+		if ( entry->track >= 0 )
+			entry->decimal_track = 1;
+	}
+	return next_field( in, result );
+}
+
+static char* parse_time_( char* in, int* out )
+{
+	*out = -1;
+	int n = -1;
+	in = parse_int_( in, &n );
+	if ( n >= 0 )
+	{
+		*out = n;
+		while ( *in == ':' )
+		{
+			n = -1;
+			in = parse_int_( in + 1, &n );
+			if ( n >= 0 )
+				*out = *out * 60 + n;
+		}
+		*out *= 1000;
+		if ( *in == '.' )
+		{
+			n = -1;
+			in = parse_int_( in + 1, &n );
+			if ( n >= 0 )
+				*out = *out + n; 
+		}
+	}
+	return in;
+}
+
+static char* parse_time( char* in, int* out, int* result )
+{
+	return next_field( parse_time_( in, out ), result );
+}
+
+static char* parse_name( char* in )
+{
+	char* out = in;
+	while ( 1 )
+	{
+		int c = *in;
+		if ( !c ) break;
+		in++;
+		
+		if ( c == ',' ) // commas in string
+		{
+			char* p = skip_white( in );
+			if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 )
+			{
+				in = p;
+				break;
+			}
+		}
+		
+		if ( c == '\\' ) // \ prefix for special characters
+		{
+			c = *in;
+			if ( !c ) break;
+			in++;
+		}
+		*out++ = (char) c;
+	}
+	*out = 0; // terminate string
+	return in;
+}
+
+static int parse_line( char* in, struct entry_t* entry )
+{
+	int result = 0;
+	
+	// file
+	entry->file = in;
+	entry->type = "";
+	in = parse_filename( in, entry );
+	
+	// track
+	entry->track = -1;
+	entry->decimal_track = 0;
+	in = parse_track( in, entry, &result );
+	
+	// name
+	entry->name = in;
+	in = parse_name( in );
+	
+	// time
+	entry->length = -1;
+	in = parse_time( in, &entry->length, &result );
+	
+	// loop
+	entry->intro = -1;
+	entry->loop  = -1;
+	if ( *in == '-' )
+	{
+		entry->loop = entry->length;
+		in++;
+	}
+	else
+	{
+		in = parse_time_( in, &entry->loop );
+		if ( entry->loop >= 0 )
+		{
+			entry->intro = 0;
+			if ( *in == '-' ) // trailing '-' means that intro length was specified 
+			{
+				in++;
+				entry->intro = entry->loop;
+				entry->loop  = entry->length - entry->intro;
+			}
+		}
+	}
+	in = next_field( in, &result );
+	
+	// fade
+	entry->fade = -1;
+	in = parse_time( in, &entry->fade, &result );
+	
+	// repeat
+	entry->repeat = -1;
+	in = parse_int( in, &entry->repeat, &result );
+	
+	return result;
+}
+
+static void parse_comment( char* in, struct info_t* info, bool first )
+{
+	in = skip_white( in + 1 );
+	char* field = in;
+	while ( *in && *in != ':' && *in != '@' )
+		in++;
+		
+	/* Support Kaminari playlist format */
+    if ( *in == '@' ) {
+        field = in + 1;
+		
+        while ( *in && *in != ' ' && *in != '\t' )
+            in++;
+		
+        char* text = skip_white( in + 1 );
+		if ( *text )
+		{
+			*in = 0;
+			     if ( !stricmp( "Title"   , field ) ) info->title = text;
+			else if ( !stricmp( "Composer", field ) ) info->composer = text;
+			else if ( !stricmp( "Artist", field ) 
+			       || !stricmp( "Sequencer", field) 
+				   || !stricmp( "Date",      field) 
+				   || !stricmp( "Copyright", field) ) info->engineer = text;
+			else if ( !stricmp( "Ripping" , field ) ) info->ripping  = text;
+			else if ( !stricmp( "Tagging" , field ) ) info->tagging  = text;
+			else
+				text = 0;
+			if ( text )
+				return;
+			*in = ':';
+		}
+		
+		return;
+    }
+	
+	if ( *in == ':' )
+	{
+		char* text = skip_white( in + 1 );
+		if ( *text )
+		{
+			*in = 0;
+			     if ( !strcmp( "Composer", field ) ) info->composer = text;
+			else if ( !strcmp( "Engineer", field ) ) info->engineer = text;
+			else if ( !strcmp( "Ripping" , field ) ) info->ripping  = text;
+			else if ( !strcmp( "Tagging" , field ) ) info->tagging  = text;
+			else
+				text = 0;
+			if ( text )
+				return;
+			*in = ':';
+		}
+	}
+	
+	if ( first )
+		info->title = field;
+}
+
+blargg_err_t parse( struct M3u_Playlist* this )
+{
+	int const CR = 13;
+	int const LF = 10;
+	
+    *((this->data + this->raw_size - 1)) = LF; // terminate input
+	
+	this->first_error = 0;
+	bool first_comment = true;
+	int line  = 0;
+	int count = 0;
+	char* in = this->data;
+	while ( in < (this->data + this->raw_size) )
+	{
+		// find end of line and terminate it
+		line++;
+		char* begin = in;
+		while ( *in != CR && *in != LF )
+		{
+			if ( !*in )
+				return "Not an m3u playlist";
+			in++;
+		}
+		if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line
+			*in++ = 0;
+		*in++ = 0;
+		
+		// parse line
+		if ( *begin == '#' )
+		{
+			parse_comment( begin, &this->info, first_comment );
+			first_comment = false;
+		}
+		else if ( *begin )
+		{
+			if ( (int) this->size <= count )
+				this->size = count * 2 + 64;
+			
+			if ( !parse_line( begin, &this->entries [count] ) )
+				count++;
+			else if ( !this->first_error )
+				this->first_error = line;
+			first_comment = false;
+		}
+	}
+	if ( count <= 0 )
+		return "Not an m3u playlist";
+
+	this->size = count;
+	return 0;
+}
+
+blargg_err_t Hes_load_m3u( struct Hes_Emu* this, const char* path )
+{
+#ifndef GME_DISABLE_M3U_PLAYLIST
+	#if defined (ROCKBOX)
+		int fd = ci->open( path, O_RDONLY );
+	#else
+		int fd = open( path, O_RDONLY );
+	#endif
+		if ( fd < 0 ) return "Error when loading file";
+
+	#if defined (ROCKBOX)
+		this->m3u.raw_size = ci->read( fd, this->m3u.data, max_size );
+		ci->close( fd );
+	#else
+		this->m3u.raw_size = read( fd, this->m3u.data, max_size );
+		close( fd );
+	#endif
+
+		blargg_err_t err = parse( &this->m3u );
+		if ( err ) 
+			return err;
+
+		if ( this->m3u.size > 0 ) 
+			this->track_count = (byte) this->m3u.size; 
+#endif
+
+	return 0;
+}
diff -rNu rb/apps/codecs/libhes/m3u_playlist.h rb_hes/apps/codecs/libhes/m3u_playlist.h
--- rb/apps/codecs/libhes/m3u_playlist.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/m3u_playlist.h	2011-05-15 17:09:05.038875100 +0800
@@ -0,0 +1,57 @@
+// M3U playlist file parser, with support for subtrack information
+
+// Game_Music_Emu 0.5.2
+#ifndef M3U_PLAYLIST_H
+#define M3U_PLAYLIST_H
+
+#include "blargg_common.h"
+
+enum { max_entries = 255 };
+enum { max_size = 0x1000 };
+
+struct info_t
+{
+	char* title;
+	char* composer;
+	char* engineer;
+	char* ripping;
+	char* tagging;
+};
+
+struct entry_t
+{
+	const char* file; // filename without stupid ::TYPE suffix
+	const char* type; // if filename has ::TYPE suffix, this will be "TYPE". "" if none.
+	const char* name;
+	bool decimal_track; // true if track was specified in hex
+	                    // integers are -1 if not present
+	int track;  // 1-based
+	int length; // milliseconds
+	int intro;
+	int loop;
+	int fade;
+	int repeat; // count
+};
+
+struct M3u_Playlist {
+	int size;	
+	long raw_size;
+	int first_error;
+	
+	struct info_t info;
+	struct entry_t entries [max_entries];
+	char data [max_size];
+};
+
+// Initialize M3u_Playlist structure
+static inline void M3u_clear( struct M3u_Playlist* this )
+{
+	this->first_error = 0;
+	memset( this->entries, 0, sizeof this->entries );
+	memset( this->data, 0, sizeof this->data );
+	memset( &this->info, 0, sizeof this->info );
+	this->raw_size = 0;
+	this->size = 0;
+}
+
+#endif
diff -rNu rb/apps/codecs/libhes/multi_buffer.c rb_hes/apps/codecs/libhes/multi_buffer.c
--- rb/apps/codecs/libhes/multi_buffer.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/multi_buffer.c	2011-05-15 17:09:05.042875300 +0800
@@ -0,0 +1,226 @@
+// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+
+#include "multi_buffer.h"
+
+/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+	#include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+// Stereo_Buffer
+ 
+void Buffer_init( struct Stereo_Buffer* this )
+{
+	Blip_init( &this->bufs [0] );
+	Blip_init( &this->bufs [1] );
+	Blip_init( &this->bufs [2] );
+			
+	this->chan.center = &this->bufs [0];
+	this->chan.left = &this->bufs [1];
+	this->chan.right = &this->bufs [2];
+	
+	this->length_ = 0;
+	this->sample_rate_ = 0;
+	this->channels_changed_count_ = 1;
+	this->samples_per_frame_ = 2;
+}
+
+blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long rate, int msec )
+{
+	int i;
+	for ( i = 0; i < buf_count; i++ )
+		RETURN_ERR( Blip_set_sample_rate( &this->bufs[i], rate, msec ) );
+		
+	this->sample_rate_ = Blip_sample_rate( &this->bufs [0] );
+	this->length_ = Blip_length( &this->bufs [0] );
+	return 0;
+}
+
+void Buffer_clock_rate( struct Stereo_Buffer* this, long rate )
+{
+	int i;
+	for ( i = 0; i < buf_count; i++ )
+		Blip_set_clock_rate( &this->bufs [i], rate );
+}
+
+void Buffer_bass_freq( struct Stereo_Buffer* this, int bass )
+{
+	unsigned i;
+	for ( i = 0; i < buf_count; i++ )
+		Blip_bass_freq( &this->bufs [i], bass );
+}
+
+struct channel_t Buffer_channel( struct Stereo_Buffer* this )
+{
+	return this->chan;
+}
+
+void Buffer_clear( struct Stereo_Buffer* this )
+{
+	this->stereo_added = 0;
+	this->was_stereo   = false;
+	int i;
+	for ( i = 0; i < buf_count; i++ )
+		Blip_clear( &this->bufs [i], 1 );
+}
+
+void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t clock_count )
+{
+	this->stereo_added = 0;
+	unsigned i;
+	for ( i = 0; i < buf_count; i++ )
+	{
+		this->stereo_added |= Blip_clear_modified( &this->bufs [i] ) << i;
+		Blip_end_frame( &this->bufs [i], clock_count );
+	}
+}
+
+long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t* out, long count )
+{
+	require( !(count & 1) ); // count must be even
+	count = (unsigned) count / 2;
+	
+	long avail = Blip_samples_avail( &this->bufs [0] );
+	if ( count > avail )
+		count = avail;
+	if ( count )
+	{
+		int bufs_used = this->stereo_added | this->was_stereo;
+		//dprintf( "%X\n", bufs_used );
+		if ( bufs_used <= 1 )
+		{
+			Buffer_mix_mono( this, out, count );
+			Blip_remove_samples( &this->bufs [0], count );
+			Blip_remove_silence( &this->bufs [1], count );
+			Blip_remove_silence( &this->bufs [2], count );
+		}
+		else if ( bufs_used & 1 )
+		{
+			Buffer_mix_stereo( this, out, count );
+			Blip_remove_samples( &this->bufs [0], count );
+			Blip_remove_samples( &this->bufs [1], count );
+			Blip_remove_samples( &this->bufs [2], count );
+		}
+		else
+		{
+			Buffer_mix_stereo_no_center( this, out, count );
+			Blip_remove_silence( &this->bufs [0], count );
+			Blip_remove_samples( &this->bufs [1], count );
+			Blip_remove_samples( &this->bufs [2], count );
+		}
+		
+		// to do: this might miss opportunities for optimization
+		if ( !Blip_samples_avail( &this->bufs [0] ) )
+		{
+			this->was_stereo   = this->stereo_added;
+			this->stereo_added = 0;
+		}
+	}
+	
+	return count * 2;
+}
+
+unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this )
+{
+	return this->channels_changed_count_;
+}
+
+void Buffer_channels_changed( struct Stereo_Buffer* this )
+{
+	this->channels_changed_count_++;
+}
+
+void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+{
+	blip_sample_t* BLIP_RESTRICT out = out_;
+	int const bass = BLIP_READER_BASS( this->bufs [1] );
+	BLIP_READER_BEGIN( left, this->bufs [1] );
+	BLIP_READER_BEGIN( right, this->bufs [2] );
+	BLIP_READER_BEGIN( center, this->bufs [0] );
+	
+	for ( ; count; --count )
+	{
+		int c = BLIP_READER_READ( center );
+		blargg_long l = c + BLIP_READER_READ( left );
+		blargg_long r = c + BLIP_READER_READ( right );
+		if ( (int16_t) l != l )
+			l = 0x7FFF - (l >> 24);
+		
+		BLIP_READER_NEXT( center, bass );
+		if ( (int16_t) r != r )
+			r = 0x7FFF - (r >> 24);
+		
+		BLIP_READER_NEXT( left, bass );
+		BLIP_READER_NEXT( right, bass );
+		
+		out [0] = l;
+		out [1] = r;
+		out += 2;
+	}
+	
+	BLIP_READER_END( center, this->bufs [0] );
+	BLIP_READER_END( right, this->bufs [2] );
+	BLIP_READER_END( left, this->bufs [1] );
+}
+
+void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+{
+	blip_sample_t* BLIP_RESTRICT out = out_;
+	int const bass = BLIP_READER_BASS( this->bufs [1] );
+	BLIP_READER_BEGIN( left, this->bufs [1] );
+	BLIP_READER_BEGIN( right, this->bufs [2] );
+	
+	for ( ; count; --count )
+	{
+		blargg_long l = BLIP_READER_READ( left );
+		if ( (int16_t) l != l )
+			l = 0x7FFF - (l >> 24);
+		
+		blargg_long r = BLIP_READER_READ( right );
+		if ( (int16_t) r != r )
+			r = 0x7FFF - (r >> 24);
+		
+		BLIP_READER_NEXT( left, bass );
+		BLIP_READER_NEXT( right, bass );
+		
+		out [0] = l;
+		out [1] = r;
+		out += 2;
+	}
+	
+	BLIP_READER_END( right, this->bufs [2] );
+	BLIP_READER_END( left, this->bufs [1] );
+}
+
+void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+{
+	blip_sample_t* BLIP_RESTRICT out = out_;
+	int const bass = BLIP_READER_BASS( this->bufs [0] );
+	BLIP_READER_BEGIN( center, this->bufs [0] );
+	
+	for ( ; count; --count )
+	{
+		blargg_long s = BLIP_READER_READ( center );
+		if ( (int16_t) s != s )
+			s = 0x7FFF - (s >> 24);
+		
+		BLIP_READER_NEXT( center, bass );
+		out [0] = s;
+		out [1] = s;
+		out += 2;
+	}
+	
+	BLIP_READER_END( center, this->bufs [0] );
+}
diff -rNu rb/apps/codecs/libhes/multi_buffer.h rb_hes/apps/codecs/libhes/multi_buffer.h
--- rb/apps/codecs/libhes/multi_buffer.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/multi_buffer.h	2011-05-15 17:09:05.045875500 +0800
@@ -0,0 +1,72 @@
+// Multi-channel sound buffer interface, and basic mono and stereo buffers
+
+// Blip_Buffer 0.4.1
+#ifndef MULTI_BUFFER_H
+#define MULTI_BUFFER_H
+
+#include "blargg_common.h"
+#include "blip_buffer.h"
+
+// Get indexed channel, from 0 to channel count - 1
+struct channel_t {
+	struct Blip_Buffer* center;
+	struct Blip_Buffer* left;
+	struct Blip_Buffer* right;
+};
+
+enum { type_index_mask = 0xFF };
+enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
+enum { buf_count = 3 };
+	
+struct Stereo_Buffer {
+	struct Blip_Buffer bufs [buf_count];
+	struct channel_t chan;
+	int stereo_added;
+	int was_stereo;
+	
+	unsigned channels_changed_count_;
+	long sample_rate_;
+	int length_;
+	int samples_per_frame_;
+};
+
+// Initializes Stereo_Buffer structure
+void Buffer_init( struct Stereo_Buffer* this );
+
+blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long, int msec );
+void Buffer_clock_rate( struct Stereo_Buffer* this, long );
+void Buffer_bass_freq( struct Stereo_Buffer* this, int );
+void Buffer_clear( struct Stereo_Buffer* this );
+struct channel_t Buffer_channel( struct Stereo_Buffer* this );
+void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t ); ICODE_ATTR
+	
+long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t*, long ); ICODE_ATTR
+	
+// Count of changes to channel configuration. Incremented whenever
+// a change is made to any of the Blip_Buffers for any channel.
+unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this ); ICODE_ATTR
+void Buffer_channels_changed( struct Stereo_Buffer* this ); ICODE_ATTR
+	
+void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t*, blargg_long ); ICODE_ATTR
+void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t*, blargg_long ); ICODE_ATTR
+void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t*, blargg_long ); ICODE_ATTR
+
+// Number of samples per output frame (1 = mono, 2 = stereo)
+static inline int Buffer_samples_per_frame( struct Stereo_Buffer* this )
+{
+	return this->samples_per_frame_;
+}
+
+// See Blip_Buffer.h
+static inline long Buffer_sample_rate( struct Stereo_Buffer* this )
+{
+	return this->sample_rate_;
+}
+
+// Length of buffer, in milliseconds
+static inline int Buffer_length( struct Stereo_Buffer* this )
+{
+	return this->length_;
+}
+
+#endif
diff -rNu rb/apps/codecs/libhes/rom_data.c rb_hes/apps/codecs/libhes/rom_data.c
--- rb/apps/codecs/libhes/rom_data.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/rom_data.c	2011-05-15 17:09:05.048875600 +0800
@@ -0,0 +1,76 @@
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "rom_data.h"
+
+/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <string.h>
+#include "blargg_source.h"
+
+// Rom_Data
+
+blargg_err_t Rom_load( struct Rom_Data* this, void* data, long size,
+		int header_size, void* header_out, int fill )
+{
+	long file_offset = pad_size;
+	
+	this->rom_addr = 0;
+	this->mask     = 0;
+	this->size    = 0;
+	
+	if ( size <= header_size ) // <= because there must be data after header
+		return gme_wrong_file_type;
+	
+	// Read header
+	memcpy( header_out, data, header_size );
+	
+	this->file_size = size - header_size;
+	this->file_data = (byte*) data + header_size;
+	
+	memset( this->unmapped, fill, rom_size );
+	memcpy( &this->unmapped [file_offset], this->file_data, 
+		this->file_size < pad_size ? this->file_size : pad_size );
+	
+	return 0;
+}
+
+void Rom_set_addr( struct Rom_Data* this, long addr )
+{
+	this->rom_addr = addr - bank_size - pad_extra;
+	
+	long rounded = (addr + this->file_size + bank_size - 1) / bank_size * bank_size;
+	if ( rounded <= 0 )
+	{
+		rounded = 0;
+	}
+	else
+	{
+		int shift = 0;
+		unsigned long max_addr = (unsigned long) (rounded - 1);
+		while ( max_addr >> shift )
+			shift++;
+		this->mask = (1L << shift) - 1;
+	}
+	
+	if ( addr < 0 )
+		addr = 0;
+	this->size = rounded;
+	this->rsize_ = rounded - this->rom_addr + pad_extra;
+
+	if ( 0 )
+	{
+		dprintf( "addr: %X\n", addr );
+		dprintf( "file_size: %d\n", this->file_size );
+		dprintf( "rounded: %d\n", rounded );
+		dprintf( "mask: $%X\n", this->mask );
+	}
+}
diff -rNu rb/apps/codecs/libhes/rom_data.h rb_hes/apps/codecs/libhes/rom_data.h
--- rb/apps/codecs/libhes/rom_data.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/codecs/libhes/rom_data.h	2011-05-15 17:09:05.051875800 +0800
@@ -0,0 +1,72 @@
+// Common aspects of emulators which use rom data
+
+// Game_Music_Emu 0.5.2
+#ifndef ROM_DATA_H
+#define ROM_DATA_H
+
+#include "blargg_common.h"
+#include "blargg_source.h"
+
+// ROM data handler, used by several Classic_Emu derivitives. Loads file data
+// with padding on both sides, allowing direct use in bank mapping. The main purpose
+// is to allow all file data to be loaded with only one read() call (for efficiency).
+
+extern const char gme_wrong_file_type []; // declared in gme.h
+
+enum { bank_size = 0x2000 };
+enum { pad_extra = 8 };
+enum { pad_size = bank_size + pad_extra };
+enum { rom_size = 2 * pad_size };
+
+struct  Rom_Data {	
+	byte* file_data;
+	long file_size;
+	
+	blargg_long rom_addr;
+	blargg_long mask;
+	blargg_long size; // TODO: eliminate
+	blargg_long rsize_;
+	
+	// Unmapped space
+	byte unmapped [rom_size];
+};
+
+// Load file data, using already-loaded header 'h' if not NULL. Copy header
+// from loaded file data into *out and fill unmapped bytes with 'fill'.
+blargg_err_t Rom_load( struct Rom_Data* this, void* data, long size, int header_size, void* header_out, int fill );
+
+// Set address that file data should start at
+void Rom_set_addr( struct Rom_Data* this, long addr );
+
+// Mask address to nearest power of two greater than size()
+static inline blargg_long mask_addr( blargg_long addr, blargg_long mask )
+{
+	#ifdef check
+		check( addr <= mask );
+	#endif
+	return addr & mask;
+}
+
+// Pointer to page starting at addr. Returns unmapped() if outside data.
+static inline byte* Rom_at_addr( struct Rom_Data* this, blargg_long addr )
+{
+	blargg_ulong offset = mask_addr( addr, this->mask ) - this->rom_addr;
+	if ( offset > (blargg_ulong) (this->rsize_ - pad_size) )
+		offset = 0; // unmapped
+			
+	if ( offset < pad_size	) return &this->unmapped [offset];
+	else return &this->file_data [offset - pad_size];
+}
+
+
+#ifndef GME_APU_HOOK
+	#define GME_APU_HOOK( emu, addr, data ) ((void) 0)
+#endif
+
+#ifndef GME_FRAME_HOOK
+	#define GME_FRAME_HOOK( emu ) ((void) 0)
+#else
+	#define GME_FRAME_HOOK_DEFINED 1
+#endif
+
+#endif
diff -rNu rb/apps/codecs.c rb_hes/apps/codecs.c
--- rb/apps/codecs.c	2011-05-13 23:36:53.393148200 +0800
+++ rb_hes/apps/codecs.c	2011-05-15 17:09:05.057876200 +0800
@@ -72,7 +72,7 @@
 
 extern void* plugin_get_audio_buffer(size_t *buffer_size);
 
-#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(HAVE_RECORDING)
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
 #undef open
 static int open(const char* pathname, int flags, ...)
 {
@@ -152,6 +152,7 @@
     enc_finish_chunk,
     enc_get_pcm_data,
     enc_unget_pcm_data,
+#endif /* HAVE_RECORDING */
 
     /* file */
     (open_func)PREFIX(open),
@@ -161,10 +162,10 @@
     (write_func)PREFIX(write),
     round_value_to_list32,
 
-#endif /* HAVE_RECORDING */
-
     /* new stuff at the end, sort into place next time
        the API gets incompatible */
+    strrchr,
+    strcasecmp,
 };
 
 void codec_get_full_path(char *path, const char *codec_root_fn)
diff -rNu rb/apps/codecs.h rb_hes/apps/codecs.h
--- rb/apps/codecs.h	2011-05-13 23:36:53.399148500 +0800
+++ rb_hes/apps/codecs.h	2011-05-15 17:09:05.061876400 +0800
@@ -210,6 +210,7 @@
     void            (*enc_finish_chunk)(void);
     unsigned char * (*enc_get_pcm_data)(size_t size);
     size_t          (*enc_unget_pcm_data)(size_t size);
+#endif
 
     /* file */
     int (*open)(const char* pathname, int flags, ...);
@@ -221,10 +222,11 @@
                                  const unsigned long list[],
                                  int count,
                                  bool signd);
-#endif
 
     /* new stuff at the end, sort into place next time
        the API gets incompatible */
+    char * (*strrchr)(const char *s, int c);
+    int (*strcasecmp)(const char *, const char *);
 };
 
 /* codec header */
diff -rNu rb/apps/filetypes.c rb_hes/apps/filetypes.c
--- rb/apps/filetypes.c	2011-04-17 15:49:42.649406300 +0800
+++ rb_hes/apps/filetypes.c	2011-05-15 17:09:05.066876700 +0800
@@ -112,6 +112,7 @@
     { "vox", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
     { "w64", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
     { "tta", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+    { "hes", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
 #endif
     { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
     { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
diff -rNu rb/apps/metadata/gme_m3u.c rb_hes/apps/metadata/gme_m3u.c
--- rb/apps/metadata/gme_m3u.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/metadata/gme_m3u.c	2011-05-15 15:49:20.240200200 +0800
@@ -0,0 +1,376 @@
+#include <string.h>
+#include <ctype.h>
+
+#include "metadata.h"
+#include "gme_m3u.h"
+
+char* skip_white( char* in )
+{
+	while ( *in == ' ' || *in == '\t' )
+		in++;
+	return in;
+}
+
+unsigned from_dec( unsigned n ) { return n - '0'; }
+
+char* parse_filename( char* in, struct entry_t* entry )
+{
+	entry->file = in;
+	entry->type = "";
+	char* out = in;
+	while ( 1 )
+	{
+		int c = *in;
+		if ( !c ) break;
+		in++;
+		
+		if ( c == ',' ) // commas in filename
+		{
+			char* p = skip_white( in );
+			if ( *p == '$' || from_dec( *p ) <= 9 )
+			{
+				in = p;
+				break;
+			}
+		}
+
+		if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix
+		{
+			entry->type = ++in;
+			while ( (c = *in) != 0 && c != ',' )
+				in++;
+			if ( c == ',' )
+			{
+				*in++ = 0; // terminate type
+				in = skip_white( in );
+			}
+			break;
+		}
+		
+		if ( c == '\\' ) // \ prefix for special characters
+		{
+			c = *in;
+			if ( !c ) break;
+			in++;
+		}
+		*out++ = (char) c;
+	}
+	*out = 0; // terminate string
+	return in;
+}
+
+char* next_field( char* in, int* result )
+{
+	while ( 1 )
+	{
+		in = skip_white( in );
+		
+		if ( !*in )
+			break;
+		
+		if ( *in == ',' )
+		{
+			in++;
+			break;
+		}
+		
+		*result = 1;
+		in++;
+	}
+	return skip_white( in );
+}
+
+char* parse_int_( char* in, int* out )
+{
+	int n = 0;
+	while ( 1 )
+	{
+		unsigned d = from_dec( *in );
+		if ( d > 9 )
+			break;
+		in++;
+		n = n * 10 + d;
+		*out = n;
+	}
+	return in;
+}
+
+char* parse_int( char* in, int* out, int* result )
+{
+	return next_field( parse_int_( in, out ), result );
+}
+
+// Returns 16 or greater if not hex
+int from_hex_char( int h )
+{
+	h -= 0x30;
+	if ( (unsigned) h > 9 )
+		h = ((h - 0x11) & 0xDF) + 10;
+	return h;
+}
+
+char* parse_track( char* in, struct entry_t* entry, int* result )
+{
+	if ( *in == '$' )
+	{
+		in++;
+		int n = 0;
+		while ( 1 )
+		{
+			int h = from_hex_char( *in );
+			if ( h > 15 )
+				break;
+			in++;
+			n = n * 16 + h;
+			entry->track = n;
+		}
+	}
+	else
+	{
+		in = parse_int_( in, &entry->track );
+		if ( entry->track >= 0 )
+			entry->decimal_track = 1;
+	}
+	return next_field( in, result );
+}
+
+char* parse_time_( char* in, int* out )
+{
+	*out = -1;
+	int n = -1;
+	in = parse_int_( in, &n );
+	if ( n >= 0 )
+	{
+		*out = n;
+		while ( *in == ':' )
+		{
+			n = -1;
+			in = parse_int_( in + 1, &n );
+			if ( n >= 0 )
+				*out = *out * 60 + n;
+		}
+		*out *= 1000;
+		if ( *in == '.' )
+		{
+			n = -1;
+			in = parse_int_( in + 1, &n );
+			if ( n >= 0 )
+				*out = *out + n; 
+		}
+	}
+	return in;
+}
+
+char* parse_time( char* in, int* out, int* result )
+{
+	return next_field( parse_time_( in, out ), result );
+}
+
+char* parse_name( char* in )
+{
+	char* out = in;
+	while ( 1 )
+	{
+		int c = *in;
+		if ( !c ) break;
+		in++;
+		
+		if ( c == ',' ) // commas in string
+		{
+			char* p = skip_white( in );
+			if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 )
+			{
+				in = p;
+				break;
+			}
+		}
+		
+		if ( c == '\\' ) // \ prefix for special characters
+		{
+			c = *in;
+			if ( !c ) break;
+			in++;
+		}
+		*out++ = (char) c;
+	}
+	*out = 0; // terminate string
+	return in;
+}
+
+int parse_line( char* in, struct entry_t* entry )
+{
+	int result = 0;
+	// file
+	entry->file = in;
+	entry->type = "";
+	in = parse_filename( in, entry );
+	// track
+	entry->track = -1;
+	entry->decimal_track = 0;
+	in = parse_track( in, entry, &result );
+	// name
+	entry->name = in;
+	in = parse_name( in );
+	// time
+	entry->length = -1;
+	in = parse_time( in, &entry->length, &result );
+	// loop
+	entry->intro = -1;
+	entry->loop  = -1;
+	if ( *in == '-' )
+	{
+		entry->loop = entry->length;
+		in++;
+	}
+	else
+	{
+		in = parse_time_( in, &entry->loop );
+		if ( entry->loop >= 0 )
+		{
+			entry->intro = 0;
+			if ( *in == '-' ) // trailing '-' means that intro length was specified 
+			{
+				in++;
+				entry->intro = entry->loop;
+				entry->loop  = entry->length - entry->intro;
+			}
+		}
+	}
+	in = next_field( in, &result );
+	
+	// fade
+	entry->fade = -1;
+	in = parse_time( in, &entry->fade, &result );
+
+	// repeat
+	entry->repeat = -1;
+	in = parse_int( in, &entry->repeat, &result );
+	
+	return result;
+}
+
+void parse_comment( char* in, struct info_t* info, bool first )
+{
+	in = skip_white( in + 1 );
+	char* field = in;
+	while ( *in && *in != ':' && *in != '@' )
+		in++;
+
+	/* Support Kaminari playlist format */
+    if ( *in == '@' ) {
+        field = in + 1;
+		
+        while ( *in && *in != ' ' && *in != '\t' )
+            in++;
+
+        char* text = skip_white( in + 1 );
+		if ( *text )
+		{
+			*in = 0;
+			     if ( !stricmp( "Title"   , field ) ) info->title = text;
+			else if ( !stricmp( "Composer", field ) ) info->composer = text;
+			else if ( !stricmp( "Artist", field ) 
+			       || !stricmp( "Sequencer", field) 
+				   || !stricmp( "Date",      field) 
+				   || !stricmp( "Copyright", field) ) info->engineer = text;
+			else if ( !stricmp( "Ripping" , field ) ) info->ripping  = text;
+			else if ( !stricmp( "Tagging" , field ) ) info->tagging  = text;
+			else
+				text = 0;
+			if ( text )
+				return;
+			*in = ':';
+		}
+		return;
+    }
+
+	if ( *in == ':' )
+	{
+		char* text = skip_white( in + 1 );
+		if ( *text )
+		{
+			*in = 0;
+			     if ( !strcmp( "Composer", field ) ) info->composer = text;
+			else if ( !strcmp( "Engineer", field ) ) info->engineer = text;
+			else if ( !strcmp( "Ripping" , field ) ) info->ripping  = text;
+			else if ( !strcmp( "Tagging" , field ) ) info->tagging  = text;
+			else
+				text = 0;
+			if ( text )
+				return;
+			*in = ':';
+		}
+	}
+
+	if ( first )
+		info->title = field;
+}
+
+blargg_err_t parse_gme( struct M3u_Playlist* this )
+{
+	int const CR = 13;
+	int const LF = 10;
+
+    *((this->data + this->raw_size - 1)) = LF; // terminate input
+
+	this->first_error = 0;
+	bool first_comment = true;
+	int line  = 0;
+	int count = 0;
+	char* in = this->data;
+	while ( in < (this->data + this->raw_size) )
+	{
+		// find end of line and terminate it
+		line++;
+		char* begin = in;
+		while ( *in != CR && *in != LF )
+		{
+			if ( !*in )
+				return "Not an m3u playlist";
+			in++;
+		}
+		if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line
+			*in++ = 0;
+		*in++ = 0;
+
+		// parse line
+		if ( *begin == '#' )
+		{
+			parse_comment( begin, &this->info, first_comment );
+			first_comment = false;
+		}
+		else if ( *begin )
+		{
+			if ( (int) this->size <= count )
+				this->size = count * 2 + 64;
+			
+			if ( !parse_line( begin, &this->entries [count] ) )
+				count++;
+			else if ( !this->first_error )
+				this->first_error = line;
+			first_comment = false;
+		}
+	}
+	if ( count <= 0 )
+		return "Not an m3u playlist";
+
+	this->size = count;
+	return 0;
+}
+
+blargg_err_t Gme_load_m3u( struct Gme_Emu* this, const char* path )
+{
+		int fd = open( path, O_RDONLY );
+		if ( fd < 0 ) return "Error when loading file";
+
+		this->m3u.raw_size = read( fd, this->m3u.data, max_size );
+		close( fd );
+
+		blargg_err_t err = parse_gme( &this->m3u );
+		if ( err ) 
+			return err;
+
+		if ( this->m3u.size > 0 ) 
+			this->track_count = (byte) this->m3u.size; 
+	return 0;
+}
diff -rNu rb/apps/metadata/gme_m3u.h rb_hes/apps/metadata/gme_m3u.h
--- rb/apps/metadata/gme_m3u.h	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/metadata/gme_m3u.h	2011-05-15 15:49:20.243200400 +0800
@@ -0,0 +1,46 @@
+typedef const char* blargg_err_t;
+typedef unsigned char byte;
+
+enum { max_entries = 255 };
+enum { max_size = 0x1000 };
+
+struct info_t
+{
+	char* title;
+	char* composer;
+	char* engineer;
+	char* ripping;
+	char* tagging;
+};
+
+struct entry_t
+{
+	const char* file;
+	const char* type;
+	const char* name;
+	bool decimal_track;
+
+	int track;
+	int length;
+	int intro;
+	int loop;
+	int fade;
+	int repeat;
+};
+
+struct M3u_Playlist {
+	int size;	
+	long raw_size;
+	int first_error;
+	
+	struct info_t info;
+	struct entry_t entries [max_entries];
+	char data [max_size];
+};
+
+struct Gme_Emu {
+	struct M3u_Playlist m3u;
+	byte track_count;
+};
+
+blargg_err_t Gme_load_m3u( struct Gme_Emu* this, const char* path );
diff -rNu rb/apps/metadata/hes.c rb_hes/apps/metadata/hes.c
--- rb/apps/metadata/hes.c	1970-01-01 08:00:00.000000000 +0800
+++ rb_hes/apps/metadata/hes.c	2011-05-15 15:49:20.245200500 +0800
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "system.h"
+#include "metadata.h"
+#include "metadata_common.h"
+#include "metadata_parsers.h"
+#include "rbunicode.h"
+#include "string-extra.h"
+#include "gme_m3u.h"
+
+struct Gme_Emu hes_emu_meta;
+
+bool get_hes_metadata(int fd, struct mp3entry* id3)
+{
+    /* Use the trackname part of the id3 structure as a temporary buffer */
+    unsigned char* buf = (unsigned char *)id3->path;
+    int read_bytes;
+    char *m3u_item = id3->id3v2buf;
+
+    /* Load extended m3u playlist */
+    char *p = strrchr(id3->path, '.');
+    if (p) {
+        strcpy(p, ".m3u");
+        Gme_load_m3u(&hes_emu_meta, id3->path);
+
+        // Copy back hes extension
+        strcpy(p, ".hes");
+    }
+    strcpy(id3->path, "");
+
+    struct info_t* info = &hes_emu_meta.m3u.info;
+
+    if (info->title)
+        id3->title = m3u_item,
+        m3u_item += strlcpy(m3u_item, info->title, 32) + 1;
+
+    if (info->composer)
+        id3->artist = m3u_item,
+        m3u_item += strlcpy(m3u_item, info->composer, 32) + 1;
+
+    if (info->engineer)
+        id3->album = m3u_item,
+        strlcpy(m3u_item, info->engineer, 32);
+
+    if (hes_emu_meta.track_count) id3->length = hes_emu_meta.track_count*1000;
+    else id3->length = 120*1000;
+
+    if ((lseek(fd, 0, SEEK_SET) < 0) 
+         || ((read_bytes = read(fd, buf, 4)) < 4))
+    {
+        return false;
+    }
+
+    id3->vbr = false;
+    id3->filesize = filesize(fd);
+
+    if (memcmp(buf,"HESM",4) != 0)
+    {
+        return false;
+    }
+
+    return true;
+}
diff -rNu rb/apps/metadata/metadata_parsers.h rb_hes/apps/metadata/metadata_parsers.h
--- rb/apps/metadata/metadata_parsers.h	2011-04-17 15:40:57.524370900 +0800
+++ rb_hes/apps/metadata/metadata_parsers.h	2011-05-15 17:09:05.075877200 +0800
@@ -47,3 +47,4 @@
 bool get_vox_metadata(int fd, struct mp3entry* id3);
 bool get_wave64_metadata(int fd, struct mp3entry* id3);
 bool get_tta_metadata(int fd, struct mp3entry* id3);
+bool get_hes_metadata(int fd, struct mp3entry* id3);
diff -rNu rb/apps/metadata.c rb_hes/apps/metadata.c
--- rb/apps/metadata.c	2011-04-27 13:39:44.892262900 +0800
+++ rb_hes/apps/metadata.c	2011-05-15 17:09:05.080877500 +0800
@@ -210,6 +210,9 @@
     /* Advanced Audio Coding High Efficiency in M4A container */
     [AFMT_MP4_AAC_HE] =
         AFMT_ENTRY("AAC-HE","aac",  NULL,       get_mp4_metadata,   "mp4\0"),
+    /* HES (Hudson Entertainment System Sound Format) */
+    [AFMT_HES] =
+        AFMT_ENTRY("HES",   "hes",  NULL,       get_hes_metadata,   "hes\0"),
 #endif
 };
 
@@ -293,6 +296,7 @@
     case AFMT_SID:
     case AFMT_MOD:
     case AFMT_SAP:
+    case AFMT_HES:
         /* Type must be allocated and loaded in its entirety onto
            the buffer */
         return TYPE_ATOMIC_AUDIO;
@@ -405,7 +409,8 @@
 
     /* Take our best guess at the codec type based on file extension */
     id3->codectype = probe_file_format(trackname);
-
+    if (id3->codectype == AFMT_HES)
+    strcpy(id3->path,trackname)/*,DEBUGF("I AM GME\n")*/;
     entry = &audio_formats[id3->codectype];
 
     /* Load codec specific track tag information and confirm the codec type. */
diff -rNu rb/apps/metadata.h rb_hes/apps/metadata.h
--- rb/apps/metadata.h	2011-05-08 00:22:07.411169000 +0800
+++ rb_hes/apps/metadata.h	2011-05-15 17:09:05.083877600 +0800
@@ -87,6 +87,7 @@
     AFMT_WMAVOICE,     /* WMA Voice in ASF */
     AFMT_MPC_SV8,      /* Musepack SV8 */
     AFMT_MP4_AAC_HE,   /* Advanced Audio Coding (AAC-HE) in M4A container */
+    AFMT_HES,          /* HES (Hudson Entertainment System Sound Format) */
 #endif
 
     /* add new formats at any index above this line to have a sensible order -
