view music2/nwatowav.cc @ 47:5f548e5957a8

* get rid of the "deprecated conversion from string constant to ‘char*’" warnings
author thib
date Wed, 15 Apr 2009 20:26:32 +0000
parents 01aa5ddf7dc8
children 15a18fbe6f21
line wrap: on
line source

/* nwatowav : Visual Arts ·Ï¤Î¥²¡¼¥à¤Î¥Ç¥â¤Ç»È¤ï¤ì¤ë nwa ·Á¼°¤Î
**            ¥Õ¥¡¥¤¥ë¤ò wav ·Á¼°¤ËÊÑ´¹¤¹¤ë
**
**     compile : gcc -O2 -o nwatowav nwatowav.cc
**     usage : nwatowav [nwa-file [outfile]]
**	       nwatowav [nwk-file [outfile]]
**     example : nwatowav HM06.nwa HM06.wav	# BGM¥Õ¥¡¥¤¥ë¡£HM06.wav ¤ËŸ³«¤µ¤ì¤ë
**		 nwatowav z2813.nwk z2813	# ²»À¼¥Õ¥¡¥¤¥ë¡£ z2813-100.wav ¤Ê¤É¤Î¥Õ¥¡¥¤¥ë̾¤ÇŸ³«¤µ¤ì¤ë
**		 nwatowav z0513.ovk z0513	# ²»À¼¥Õ¥¡¥¤¥ë¡£ z0513-100.ogg ¤Ê¤É¤Î¥Õ¥¡¥¤¥ë̾¤ÇŸ³«¤µ¤ì¤ë
**
**
** 2004.5.19 ¾®¾¾¤µ¤ó<s1100089@u-aizu.ac.jp> ¤«¤é CLANNAD ¤Î̵°µ½Ìnwa·Á¼°¤ËÂбþ¤¹¤ë
**           ¥Ñ¥Ã¥Á¤ò¤¤¤¿¤À¤¤¤¿¤Î¤Ç¡¢Å¬ÍѤ·¤Þ¤·¤¿¡£¤¢¤ê¤¬¤È¤¦¤´¤¶¤¤¤Þ¤¹¡£
** 2006.9.10 ¡ÖÃÒÂ奢¥Õ¥¿¡¼¡×¤Î²»À¼¥Õ¥¡¥¤¥ë·Á¼° (complevel = 5) ¤ò¥µ¥Ý¡¼¥È
**	     .nwk ¤È¤¤¤¦³ÈÄ¥»Ò¤ò»ý¤Ä¥Õ¥¡¥¤¥ë¤ò¼õ¤±¼è¤ë¤È²»À¼¥Õ¥¡¥¤¥ë¤È¤·¤Æ
**	     ²ò¼á¡¢Ê¬³ä¤·¤ÆŸ³«¤¹¤ë¤è¤¦¤Ë¤¹¤ë
** 2007.7.28 ¡Ö¥ê¥È¥ë¥Ð¥¹¥¿¡¼¥º¡ª¡×¤Î²»À¼¥Õ¥¡¥¤¥ë·Á¼° (*.ovk; ogg Ï¢·ë·¿)
**		¤ò¥µ¥Ý¡¼¥È¡£.ovk ¤È¤¤¤¦³ÈÄ¥»Ò¤ò¤â¤Ä¥Õ¥¡¥¤¥ë¤ò¼õ¤±¼è¤ë¤È
**		²»À¼¥Õ¥¡¥¤¥ë¤È¤·¤Æ²ò¼á¡¢Ê¬³ä¤·¤ÆŸ³«¤¹¤ë¤è¤¦¤Ë¤¹¤ë
**	     ¡Ö¥ê¥È¥ë¥Ð¥¹¥¿¡¼¥º¡ª¡×¤ÎBGM¥Õ¥¡¥¤¥ë¤Ë¿Î̤ΥΥ¤¥º¤¬¾è¤ëÌäÂê¤â
**	     ¡¡²ò·è¡Ê¥é¥ó¥ì¥ó¥°¥¹°µ½Ì¤Î½èÍý¤¬ÉÔɬÍפÀ¤Ã¤¿¡Ë
*/

/*
 * Copyright 2001-2007  jagarl / Kazunori Ueno <jagarl@creator.club.ne.jp>
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted.
 *
 * ¤³¤Î¥×¥í¥°¥é¥à¤Îºî¼Ô¤Ï jagarl ¤Ç¤¹¡£
 *
 * ¤³¤Î¥×¥í¥°¥é¥à¡¢µÚ¤Ó¥³¥ó¥Ñ¥¤¥ë¤Ë¤è¤Ã¤ÆÀ¸À®¤·¤¿¥Ð¥¤¥Ê¥ê¤Ï
 * ¥×¥í¥°¥é¥à¤òÊѹ¹¤¹¤ë¡¢¤·¤Ê¤¤¤Ë¤«¤«¤ï¤é¤ººÆÇÛÉÛ²Äǽ¤Ç¤¹¡£
 * ¤½¤ÎºÝ¡¢¾åµ­ Copyright ɽ¼¨¤òÊÝ»ý¤¹¤ë¤Ê¤É¤Î¾ò·ï¤Ï²Ý¤·¤Þ
 * ¤»¤ó¡£Âбþ¤¬ÌÌÅݤʤΤǥХ°Êó¹ð¤ò½ü¤­¡¢¥á¡¼¥ë¤ÇÏ¢Íí¤ò¤¹¤ë
 * ¤Ê¤É¤ÎɬÍפ⤢¤ê¤Þ¤»¤ó¡£¥½¡¼¥¹¤Î°ìÉô¤òήÍѤ¹¤ë¤³¤È¤ò´Þ¤á¡¢
 * ¤´¼«Í³¤Ë¤ª»È¤¤¤¯¤À¤µ¤¤¡£
 *
 * THIS SOFTWARE IS PROVIDED BY KAZUNORI 'jagarl' UENO ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL KAZUNORI UENO BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 */

/********************************************
**
**	nwa ¥Õ¥©¡¼¥Þ¥Ã¥È¤Ë¤Ä¤¤¤Æ
**
**		Á´ÂΤȤ·¤Æ¤Ï°Ê²¼¤Î¹½Â¤¤ò»ý¤Ä
**		NWA Header
**		data offset index
**		data block<0>
**		data block<1>
**		...
**		data block<N>
**
**	NWA Header: ¥Õ¥¡¥¤¥ëÀèƬ¤«¤é 44 bytes
**		magic number ¤Ê¤É¤Ï¤Ê¤¤¤Î¤Çnwa ¥Õ¥¡¥¤¥ë¤«¤Ï
**		¥Ç¡¼¥¿¤ÎÀ°¹çÀ­¤«¤éȽÃǤ¹¤ëɬÍפ¬¤¢¤ë
**		¥Ç¡¼¥¿¤ÏÁ´¤Æ little endian ¤Ç¡¢
**		short(signed 2byte)¤Þ¤¿¤Ï int(signed 4byte) ¤Ç¤¢¤ë¡£
**
**		+00 short   channel ¿ô(1/2)
**		+02 short   sample °ì¤Ä¤¢¤¿¤ê¤Î bit ¿ô(16)
**		+04 int     ¼þÇÈ¿ô(°ìÉ䢤¿¤ê¤Î¥Ç¡¼¥¿¿ô)
**		+08 int     °µ½Ì¥ì¥Ù¥ë¡§-1~5.2¤ÇºÇ¾®¤Î¥Ç¡¼¥¿¡¢0¤ÇºÇÂç¤ÎÉü¸µÅÙ(-1¤Ï̵°µ½Ìraw¥Ç¡¼¥¿¤È¤ß¤Ê¤µ¤ì¤ë)
**		+12 int     ?
**		+16 int     ¥Ö¥í¥Ã¥¯¿ô
**		+20 int     Ÿ³«¸å¤Î¥Ç¡¼¥¿¤ÎÂ礭¤µ(¥Ð¥¤¥Èñ°Ì)
**		+24 int     °µ½Ì»þ¤Î¥Ç¡¼¥¿¤ÎÂ礭¤µ(nwa ¥Õ¥¡¥¤¥ë¤ÎÂ礭¤µ¡£¥Ð¥¤¥Èñ°Ì)
**		+28 int     ¥µ¥ó¥×¥ë¿ô¡§Å¸³«¸å¤Î¥Ç¡¼¥¿¿ô(16bit data¤Ê¤é short ñ°Ì==¥µ¥ó¥×¥ëñ°Ì¤Î¥Ç¡¼¥¿¤ÎÂ礭¤µ)
**		+32 int     ¥Ç¡¼¥¿£±¥Ö¥í¥Ã¥¯¤òŸ³«¤·¤¿»þ¤Î¥µ¥ó¥×¥ëñ°Ì¤Î¥Ç¡¼¥¿¿ô
**		+36 int     ºÇ½ª¥Ö¥í¥Ã¥¯¤òŸ³«¤·¤¿»þ¤Î¥µ¥ó¥×¥ëñ°Ì¤Î¥Ç¡¼¥¿¿ô
**		+40 int     ?
**		
**	data offset index
**		Á´¥Ö¥í¥Ã¥¯¿ô x 4 byte ¤Î¥Ç¡¼¥¿
**		¤½¤ì¤¾¤ì int ¤Î¥Ç¡¼¥¿¤¬Á´¥Ö¥í¥Ã¥¯¿ô³¤¤¤Æ¤¤¤ë
**
**		¥Ç¡¼¥¿¥Ö¥í¥Ã¥¯¤ÎÀèƬ¤ò»Ø¤¹¥Õ¥¡¥¤¥ëÀèƬ¤«¤é¤Î°ÌÃÖ(¥ª¥Õ¥»¥Ã¥È)
**		¤¬³ÊǼ¤µ¤ì¤Æ¤¤¤ë
**
**	data block
**		Ťµ¤Ï²ÄÊÑ¡£Å¸³«¤¹¤ë¤³¤È¤Ç°ìÄê¤ÎÂ礭¤µ¤ò¤â¤Ä¥Ç¡¼¥¿¤ËŸ³«¤µ¤ì¤ë¡£
**		¥Ç¡¼¥¿¤ÏDPCM·Á¼°¡£¸µ PCM ¥Ç¡¼¥¿¤¬ a,b,c ¤Ê¤é¤Ð (a),b-a, c-b ¤È
**		¤¤¤Ã¤¿º¹Ê¬¥Ç¡¼¥¿¤¬¡¢²¾¿ô3-5bit,»Ø¿ô3bit¤Î·Á¼°¤ÇÊݸ¤µ¤ì¤Æ¤¤¤ë¡£
**		·ë²ÌŪ¤Ë¡¢16bit ¤Î¥Ç¡¼¥¿¤¬Â¿¤¯¤Î¾ì¹ç 6-8bit ¤Ç³ÊǼ¤µ¤ì¤ë¡£
**		²¾¿ô¤Î¥Ó¥Ã¥È¿ô¤Ï°µ½Ì¥ì¥Ù¥ë0¤Ç5bit¡¢°µ½Ì¥ì¥Ù¥ë2¤Ç3bit¤È¤Ê¤ë¡£
**		°Ê²¼¡¢°µ½Ì¥ì¥Ù¥ë2¤Î¾ì¹ç¤Ë¤Ä¤¤¤ÆÏäò¿Ê¤á¤ë¡£
**		¥â¥Î¥é¥ë¤Î¾ì¹ç¡§
**			+00 short  ¥Ö¥í¥Ã¥¯Æâ¤ÎºÇ½é¤Î¥Ç¡¼¥¿
**			+02- bit stream
**		¥¹¥Æ¥ì¥ª¤Î¾ì¹ç¡§
**			+00 short  º¸(?)¥Á¥ã¥ó¥Í¥ë¤ÎºÇ½é¤Î¥Ç¡¼¥¿
**			+02 short  ±¦(?)¥Á¥ã¥ó¥Í¥ë¤ÎºÇ½é¤Î¥Ç¡¼¥¿
**			+04- bit stream
**
**		º¹Ê¬¥Ç¡¼¥¿¤ÎÀºÅÙ¤¬¹â¤¯¤Ê¤¤¤Î¤Ç³Æ¥Ö¥í¥Ã¥¯¤ÎÀèƬ¤Ç
**		Àµ³Î¤Ê¥Ç¡¼¥¿¤Ë¤è¤êÊäÀµ¤µ¤ì¤ë(¡©)
**
**	bit stream
**		little endian
**		+0 - +2 : »Ø¿ô
**		+3 - +5 : ²¾¿ô
**		¤Î·Á¼°¡£Î㤨¤Ð a,b,c ¤È¤¤¤¦8bit¥Ç¡¼¥¿¤¬¤¢¤ì¤Ð¡¢
**		a&0x07 : ¥Ç¡¼¥¿£±¤Î»Ø¿ô
**		(a>>3)&0x07 : ¥Ç¡¼¥¿£±¤Î²¾¿ô(signed ; 
**		((b<<2)|(a>>6))&0x07 : ¥Ç¡¼¥¿£²¤Î»Ø¿ô
**		(b>>1)&0x07 : ¥Ç¡¼¥¿£²¤Î²¾¿ô
**		¤È¤Ê¤ë¡£
**		¤¿¤À¤·¡¢»Ø¿ô¤ÎÃͤˤè¤ê²¾¿ô¤Îbit¿ô¤¬ÊѲ½¤¹¤ë¤³¤È¤¬¤¢¤ë¡£
**		»Ø¿ô = 1 - 6 ¤Î¾ì¹ç¡§
**			a=»Ø¿ô¡¢b=²¾¿ô¡¢p=Á°¤Î¥Ç¡¼¥¿¤È¤·¤Æ¡¢º£²ó¤Î¥Ç¡¼¥¿d ¤Ï
**			b¤Î2bitÌܤ¬Î©¤Ã¤Æ¤¤¤ë¾ì¹ç¡§
**				d = p - (b&3)<<(4+a)
**			Ω¤Ã¤Æ¤Ê¤¤¾ì¹ç¡§
**				d = p + (b&3)<<(4+a)
**		»Ø¿ô = 0 ¤Î¾ì¹ç¡§²¾¿ô¤Ï¸ºß¤·¤Ê¤¤(¥Ç¡¼¥¿¤Ï3bit¤È¤Ê¤ë)
**			d = p
**			¡ÖÃÒÂ奢¥Õ¥¿¡¼¡×¤Î²»À¼¥Õ¥¡¥¤¥ë (complevel == 5) ¤Ç¤Ï¥é¥ó¥ì¥ó¥°¥¹°µ½ÌÍѤ˻Ȥï¤ì¤Æ¤¤¤ë¡£
**		»Ø¿ô = 7
**			¼¡¤Î bit ¤¬Î©¤Ã¤Æ¤¤¤ë¾ì¹ç¡§
**				d = 0 (¸½ºß̤»ÈÍÑ)
**				(¥Ç¡¼¥¿¤Ï4bit¤È¤Ê¤ë)
**			¼¡¤Î bit ¤¬Î©¤Ã¤Æ¤Ê¤¤¾ì¹ç¡§
**				complevel = 0,1,2:
**				   ²¾¿ô b = 6bit
**				   b ¤Î 5bit Ìܤ¬Î©¤Ã¤Æ¤¤¤ë¾ì¹ç¡§
**					d = p - (b&0x1f)<<(4+7)
**				   Ω¤Ã¤Æ¤Ê¤¤¾ì¹ç¡§
**					d = p + (b&0x1f)<<(4+7)
**				   (¥Ç¡¼¥¿¤Ï10bit¤È¤Ê¤ë)
**				complevel = 3,4,5:
**				   ²¾¿ô b = 8bit
**				   b ¤Î 7bit Ìܤ¬Î©¤Ã¤Æ¤¤¤ë¾ì¹ç¡§
**					d = p - (b&0x7f)<<9
**				   Ω¤Ã¤Æ¤Ê¤¤¾ì¹ç¡§
**					d = p + (b&0x1f)<<9
**				   (¥Ç¡¼¥¿¤Ï10bit¤È¤Ê¤ë)
**
**		°µ½Ì¥ì¥Ù¥ë¤¬°Û¤Ê¤ë¾ì¹ç¡¢¤¿¤È¤¨¤Ð°µ½Ì¥ì¥Ù¥ë==0¤Ç
**			»Ø¿ô==1~6¤Çd¤ÎºÇ¾å°Ìbit¤¬Î©¤Ã¤Æ¤¤¤ë¾ì¹ç
**				d = p - (b&0x0f)<<(2+a)
**			»Ø¿ô==7¤Çd¤ÎºÇ¾å°Ìbit¤¬Î©¤Ã¤Æ¤¤¤ë¾ì¹ç
**				d = p - (b&0x7f)<<(2+7)
**				(b : 8bit¤Ê¤Î¤Ç¥Ç¡¼¥¿¤Ï12bit¤È¤Ê¤ë)
**		¤Î¤è¤¦¤Ë¡¢ÀºÅÙ¤À¤±¤¬ÊѲ½¤¹¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£
**
**	¥Ø¥Ã¥ÀÆɤ߹þ¤ß¤Ë¤Ä¤¤¤Æ¤ÏNWAData::ReadHeader()»²¾È
**	bit stream ¤«¤é¤Î¥Ç¡¼¥¿Å¸³«¤Ë¤Ä¤¤¤Æ¤Ï NWADecode()»²¾È
**************************************************************
*/

// #define NDEBUG /* ¤Ê¤¼¤« assert¤¬Æþ¤Ã¤¿Êý¤¬Â®¤¤¡¢¡¢¡¢ */

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>	// for isatty() function
#include<sys/stat.h>
#include<string.h>


#ifdef WORDS_BIGENDIAN
#error Sorry, This program does not support BIG-ENDIAN system yet.
/* ¤â¤· big endian ¤Î¥·¥¹¥Æ¥à¤ËÂбþ¤µ¤»¤ë¾ì¹ç
** °Ê²¼¤Î *_little_endian_* µÚ¤Ó
** getbits() ´Ø¿ô¤òÊѹ¹¤¹¤ëɬÍפ¬¤¢¤ë
*/
#endif

inline int read_little_endian_int(const char* buf) {
	return *(int*)buf;
}

inline int read_little_endian_short(const char* buf) {
	return *(short*)buf;
}

inline int write_little_endian_int(char* buf, int number) {
	int c = *(int*)buf; *(int*)buf = number; return c;
}

inline int write_little_endian_short(char* buf, int number) {
	int c = *(short*)buf; *(short*)buf = number; return c;
}
inline int getbits(const char*& data, int& shift, int bits) {
	if (shift > 8) { data++; shift-=8;}
	int ret = read_little_endian_short(data)>>shift;
	shift += bits;
	return ret & ((1<<bits)-1); /* mask */
}

/* »ØÄꤵ¤ì¤¿·Á¼°¤Î¥Ø¥Ã¥À¤ò¤Ä¤¯¤ë */
const char* make_wavheader(int size, int channels, int bps, int freq) {
	static char wavheader[0x2c] = {
		'R','I','F','F',
		0,0,0,0, /* +0x04: riff size*/
		'W','A','V','E',
		'f','m','t',' ',
		16,0,0,0, /* +0x10 : fmt size=0x10 */
		1, 0,    /* +0x14 : tag : pcm = 1 */
		2, 0,    /* +0x16 : channels */
		0,0,0,0, /* +0x18 : samples per second */
		0,0,0,0, /* +0x1c : average bytes per second */
		0,0,     /* +0x20 : block alignment */
		0,0,     /* +0x22 : bits per sample */
		'd','a','t','a',
		0,0,0,0};/* +0x28 : data size */
	write_little_endian_int(wavheader+0x04, size+0x24);
	write_little_endian_int(wavheader+0x28, size);
	write_little_endian_short(wavheader+0x16, channels);
	write_little_endian_short(wavheader+0x22, bps);
	write_little_endian_int(wavheader+0x18, freq);
	int byps = (bps+7)>>3;
	write_little_endian_int(wavheader+0x1c, freq*byps*channels);
	write_little_endian_short(wavheader+0x20, byps*channels);
	return wavheader;
}

/* NWA ¤Î bitstreamŸ³«¤ËɬÍפȤʤë¾ðÊó */
class NWAInfo {
	int channels;
	int bps;
	int complevel;
	bool use_runlength;
public:
	NWAInfo(int c,int b,int cl) {
		channels=c;
		bps=b;
		complevel=cl;
		use_runlength = false;
		if (cl == 5) {
			use_runlength = true; // Tomoyo After (.nwk koe file)
			if (channels == 2) use_runlength = false; // BGM*.nwa in Little Busters!
		}
	}
	int Channels(void) const{return channels;}
	int Bps(void) const { return bps;}
	int CompLevel(void) const { return complevel;}
	int UseRunLength(void) const { return use_runlength; }
};

template<class NWAI> void NWADecode(const NWAI& info,const char* data, char* outdata, int datasize, int outdatasize) {
	int d[2];
	int i;
	int shift = 0;
	const char* dataend = data+datasize;
	/* ºÇ½é¤Î¥Ç¡¼¥¿¤òÆɤ߹þ¤à */
	if (info.Bps() == 8) {d[0] = *data++; datasize--;}
	else /* info.Bps() == 16 */ {d[0] = read_little_endian_short(data); data+=2; datasize-=2;}
	if (info.Channels() == 2) {
		if (info.Bps() == 8) {d[1] = *data++; datasize--;}
		else /* info.Bps() == 16 */ {d[1] = read_little_endian_short(data); data+=2; datasize-=2;}
	}
	int dsize = outdatasize / (info.Bps()/8);
	int flip_flag = 0; /* stereo ÍÑ */
	int runlength = 0;
	for (i=0; i<dsize; i++) {
		if (data >= dataend) break;
		if (runlength == 0) { // ¥³¥Ô¡¼¥ë¡¼¥×Ãæ¤Ç¤Ê¤¤¤Ê¤é¥Ç¡¼¥¿Æɤ߹þ¤ß
			int type = getbits(data, shift, 3);
			/* type ¤Ë¤è¤êʬ´ô¡§0, 1-6, 7 */
			if (type == 7) {
				/* 7 : Â礭¤Êº¹Ê¬ */
				/* RunLength() Í­¸ú»þ¡ÊCompLevel==5, ²»À¼¥Õ¥¡¥¤¥ë) ¤Ç¤Ï̵¸ú */
				if (getbits(data, shift, 1) == 1) {
					d[flip_flag] = 0; /* ̤»ÈÍÑ */
				} else {
					int BITS, SHIFT;
					if (info.CompLevel() >= 3) {
						BITS = 8;
						SHIFT = 9;
					} else {
						BITS = 8-info.CompLevel();
						SHIFT = 2+7+info.CompLevel();
					}
					const int MASK1 = (1<<(BITS-1));
					const int MASK2 = (1<<(BITS-1))-1;
					int b = getbits(data, shift, BITS);
					if (b&MASK1)
						d[flip_flag] -= (b&MASK2)<<SHIFT;
					else
						d[flip_flag] += (b&MASK2)<<SHIFT;
				}
			} else if (type != 0) {
				/* 1-6 : Ä̾ï¤Îº¹Ê¬ */
				int BITS, SHIFT;
				if (info.CompLevel() >= 3) {
					BITS = info.CompLevel()+3;
					SHIFT = 1+type;
				} else {
					BITS = 5-info.CompLevel();
					SHIFT = 2+type+info.CompLevel();
				}
				const int MASK1 = (1<<(BITS-1));
				const int MASK2 = (1<<(BITS-1))-1;
				int b = getbits(data, shift, BITS);
				if (b&MASK1)
					d[flip_flag] -= (b&MASK2)<<SHIFT;
				else
					d[flip_flag] += (b&MASK2)<<SHIFT;
			} else { /* type == 0 */
				/* ¥é¥ó¥ì¥ó¥°¥¹°µ½Ì¤Ê¤·¤Î¾ì¹ç¤Ï¤Ê¤Ë¤â¤·¤Ê¤¤ */
				if (info.UseRunLength() == true) {
					/* ¥é¥ó¥ì¥ó¥°¥¹°µ½Ì¤¢¤ê¤Î¾ì¹ç */
					runlength = getbits(data,shift,1);
					if (runlength==1) {
						runlength = getbits(data,shift,2);
						if (runlength == 3) {
							runlength = getbits(data, shift, 8);
						}
					}
				}
			}
		} else {
			runlength--;
		}
		if (info.Bps() == 8) {
			*outdata++ = d[flip_flag];
		} else {
			write_little_endian_short(outdata, d[flip_flag]);
			outdata += 2;
		}
		if (info.Channels() == 2) flip_flag ^= 1; /* channel ÀÚ¤êÂؤ¨ */
	}
	return;
};

class NWAData {
public:
	int channels;
	int bps; /* bits per sample */
	int freq; /* samples per second */
private:
	int complevel; /* compression level */
	int dummy; /* ? : 0x00 */
public:
	int blocks; /* block count */
	int datasize; /* all data size */
private:
	int compdatasize; /* compressed data size */
	int samplecount; /* all samples */
	int blocksize; /* samples per block */
	int restsize; /* samples of the last block */
	int dummy2; /* ? : 0x89 */
	int curblock;
	int* offsets;
	int offset_start;
	int filesize;
	char* tmpdata;
public:
	void ReadHeader(FILE* in, int file_size=-1);
	int CheckHeader(void); /* false: invalid true: valid */
	NWAData(void) {
		offsets = 0;
		tmpdata = 0;
	}
	~NWAData(void) {
		if (offsets) delete[] offsets;
		if (tmpdata) delete[] tmpdata;
	}
	int BlockLength(void) {
		if (complevel != -1) {
			if (offsets == 0) return false;
			if (tmpdata == 0) return false;
		}
		return blocksize * (bps/8);
	}
	/* data ¤Ï BlockLength °Ê¾å¤ÎŤµ¤ò»ý¤Ä¤³¤È
	** ÊÖ¤êÃͤϺîÀ®¤·¤¿¥Ç¡¼¥¿¤ÎŤµ¡£½ªÎ»»þ¤Ï 0¡£
	** ¥¨¥é¡¼»þ¤Ï -1
	*/
	int Decode(FILE* in, char* data, int& skip_count);
	void Rewind(FILE* in);
};

void NWAData::ReadHeader(FILE* in, int _file_size) {
	char header[0x2c];
	struct stat sb;
	int i;
	if (offsets) delete[] offsets;
	if (tmpdata) delete[] tmpdata;
	offsets = 0;
	tmpdata = 0;
	filesize = 0;
	offset_start = ftell(in);
	if (offset_start == -1) offset_start = 0;
	if (_file_size != -1) filesize = _file_size;
	curblock = -1;
	/* header Æɤ߹þ¤ß */
	if (in == 0 || feof(in) || ferror(in)) {
		fprintf(stderr,"invalid stream\n");
		return;
	}
	fread(header, 0x2c, 1, in);
	if (feof(in) || ferror(in)) {
		fprintf(stderr,"invalid stream\n");
		return;
	}
	channels = read_little_endian_short(header+0x00);
	bps = read_little_endian_short(header+0x02);
	freq = read_little_endian_int(header+0x04);
	complevel = read_little_endian_int(header+0x08);
	dummy = read_little_endian_int(header+0x0c);
	blocks = read_little_endian_int(header+0x10);
	datasize = read_little_endian_int(header+0x14);
	compdatasize = read_little_endian_int(header+0x18);
	samplecount = read_little_endian_int(header+0x1c);
	blocksize = read_little_endian_int(header+0x20);
	restsize = read_little_endian_int(header+0x24);
	dummy2 = read_little_endian_int(header+0x28);
	if (complevel == -1) {	/* ̵°µ½Ìraw¥Ç¡¼¥¿ */
		/* ŬÅö¤Ë·è¤áÂǤÁ¤¹¤ë */
		blocksize = 65536;
		restsize = (datasize % (blocksize * (bps/8))) / (bps/8);
		blocks = datasize / (blocksize * (bps/8)) + (restsize > 0 ? 1 : 0);
	}
	if (blocks <= 0 || blocks > 1000000) {
		/* £±»þ´Ö¤òĶ¤¨¤ë¶Ê¤Ã¤Æ¤Î¤Ï¤Ê¤¤¤Ç¤·¤ç*/
		fprintf(stderr,"too large blocks : %d\n",blocks);
		return;
	}
	/* regular file ¤Ê¤é filesize Æɤ߹þ¤ß */
	if (filesize == 0 && fstat(fileno(in), &sb)==0 && (sb.st_mode&S_IFMT) == S_IFREG) {
		int pos = ftell(in);
		fseek(in, 0, 2);
		filesize = ftell(in);
		fseek(in, pos, 0);
		if (pos+blocks*4 >= filesize) {
			fprintf(stderr,"offset block is not exist\n");
			return;
		}
	}
	if (complevel == -1) return;
	/* offset index Æɤ߹þ¤ß */
	offsets = new int[blocks];
	fread(offsets, blocks, 4, in);
	for (i=0; i<blocks; i++) {
		offsets[i] = read_little_endian_int((char*)(offsets+i));
	}
	if (feof(in) || ferror(in)) {
		fprintf(stderr,"invalid stream\n");
		delete[] offsets;
		offsets = 0;
		return;
	}
	return;
}
void NWAData::Rewind(FILE* in) {
	curblock = -1;
	fseek(in, 0x2c, 0);
	if (offsets) fseek(in, blocks*4, 1);
}
int NWAData::CheckHeader(void) {
	if (complevel != -1 && offsets == 0) return false;
	/* ¥Ç¡¼¥¿¤½¤Î¤â¤Î¤Î¥Á¥§¥Ã¥¯ */
	if (channels != 1 && channels != 2) {
		fprintf(stderr,"This program only supports mono / stereo data : data have %d channels.\n",channels);
		return false;
	}
	if (bps != 8 && bps != 16) {
		fprintf(stderr,"This program only supports 8 / 16bit data : data is %d bits\n",bps);
		return false;
	}
	if (complevel == -1) {
		int byps = bps/8; /* bytes per sample */
		if (datasize != samplecount*byps) {
			fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps);
			return false;
		}
		if (samplecount != (blocks-1)*blocksize+restsize ) {
			fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize);
			return false;
		}
		else
			return true;
	}
	//if (complevel < 0 || complevel > 2) {
	if (complevel < 0 || complevel > 5) {
		fprintf(stderr,"This program only supports -1,0,1,2 compression level : the level of data is %d\n",complevel);
		return false;
	}
	/* À°¹çÀ­¥Á¥§¥Ã¥¯ */
	if (filesize != 0 && filesize != compdatasize) {
		fprintf(stderr,"file size is invalid : %d != %d\n",filesize,compdatasize);
		return false;
	}
	if (offsets[blocks-1] >= compdatasize) {
		fprintf(stderr,"the last offset overruns the file.\n");
		return false;
	}
	int byps = bps/8; /* bytes per sample */
	if (datasize != samplecount*byps) {
		fprintf(stderr,"invalid datasize : datasize %d != samplecount %d * samplesize %d\n",datasize,samplecount,byps);
		return false;
	}
	if (samplecount != (blocks-1)*blocksize+restsize ) {
		fprintf(stderr,"total sample count is invalid : samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n",samplecount,blocks-1,blocksize,restsize);
		return false;
	}
	tmpdata = new char[blocksize*byps*2]; /* ¤³¤ì°Ê¾å¤ÎÂ礭¤µ¤Ï¤Ê¤¤¤À¤í¤¦¡¢¡¢¡¢ */
	return true;
}

class NWAInfo_sw2 {
public:
	int Channels(void) const{return 2;}
	int Bps(void) const { return 16;}
	int CompLevel(void) const { return 2;}
	int UseRunLength(void) const { return false; }
};
int NWAData::Decode(FILE* in, char* data, int& skip_count) {
	if (complevel == -1) {		/* ̵°µ½Ì»þ¤Î½èÍý */
		if (feof(in) || ferror(in)) return -1;
		if (curblock == -1) {
			/* ºÇ½é¤Î¥Ö¥í¥Ã¥¯¤Ê¤é¡¢wave header ½ÐÎÏ */
			memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c);
			curblock++;
			fseek(in, offset_start + 0x2c, SEEK_SET);
			return 0x2c;
		}
		if (skip_count > blocksize/channels) {
			skip_count -= blocksize/channels;
			fseek(in, blocksize*(bps/8), SEEK_CUR);
			curblock++;
			return -2;
		}
		if (curblock < blocks) {
			int readsize = blocksize;
			if (skip_count) {
				fseek(in, skip_count*channels*(bps/8), SEEK_CUR);
				readsize -= skip_count * channels;
				skip_count = 0;
			}
			int err = fread(data, 1, readsize * (bps/8), in);
			curblock++;
			return err;
		}
		return -1;
	}
	if (offsets == 0 || tmpdata == 0) return -1;
	if (blocks == curblock) return 0;
	if (feof(in) || ferror(in)) return -1;
	if (curblock == -1) {
		/* ºÇ½é¤Î¥Ö¥í¥Ã¥¯¤Ê¤é¡¢wave header ½ÐÎÏ */
		memcpy(data, make_wavheader(datasize, channels, bps, freq), 0x2c);
		curblock++;
		return 0x2c;
	}
	/* º£²óÆɤ߹þ¤à¡¿¥Ç¥³¡¼¥É¤¹¤ë¥Ç¡¼¥¿¤ÎÂ礭¤µ¤òÆÀ¤ë */
	int curblocksize, curcompsize;
	if (curblock != blocks-1) {
		curblocksize = blocksize * (bps/8);
		curcompsize = offsets[curblock+1] - offsets[curblock];
		if (curblocksize >= blocksize*(bps/8)*2) return -1; // Fatal error
	} else {
		curblocksize = restsize * (bps/8);
		curcompsize = blocksize*(bps/8)*2;
	}
	if (skip_count > blocksize/channels) {
		skip_count -= blocksize/channels;
		fseek(in, curcompsize, SEEK_CUR);
		curblock++;
		return -2;
	}
	/* ¥Ç¡¼¥¿Æɤ߹þ¤ß */
	fread(tmpdata, 1, curcompsize, in);
	/* Ÿ³« */
	if (channels == 2 && bps == 16 && complevel == 2) {
		NWAInfo_sw2 info;
		NWADecode(info, tmpdata, data, curcompsize, curblocksize);
	} else {
		NWAInfo info(channels, bps, complevel);
		NWADecode(info, tmpdata, data, curcompsize, curblocksize);
	}
	int retsize = curblocksize;
	if (skip_count) {
		int skip_c = skip_count * channels * (bps/8);
		retsize -= skip_c;
		memmove(data, data+skip_c, skip_c);
		skip_count = 0;
	}
	curblock++;
	return retsize;
}

#ifdef USE_MAIN

void conv(FILE* in, FILE* out, int skip_count, int in_size = -1) {
	NWAData h;
	h.ReadHeader(in, in_size);
	h.CheckHeader();
	int bs = h.BlockLength();
	char* d = new char[bs];
	int err;
	while( (err=h.Decode(in, d, skip_count)) != 0) {
		if (err == -1) break;
		if (err == -2) continue;
		fwrite(d, err, 1, out);
	}
	return;
}
int main(int argc, char** argv) {
	int skip_count = 0;

	if (argc > 2 && strcmp(argv[1], "--skip") == 0) {
		skip_count = atoi(argv[2]);
		argc -= 2;
		argv[1] = argv[3];
		argv[2] = argv[4];
	}
	if (argc != 2 && argc != 3) {
		fprintf(stderr,"usage : nwatowav [inputfile [outputfile]]\n");
		return -1;
	}
	if (strstr(argv[1], ".nwk") != 0 || strstr(argv[1], ".ovk") != 0) {
		bool is_ovk;
		int headblk_sz;
		char* out_ext;

		char* outpath = new char[strlen(argv[1])+10];
		char buf[1024];
		memset(buf, 0, 1024);
		FILE* in = fopen(argv[1], "rb");
		if (in == 0) {
			fprintf(stderr,"Cannot open file : %s\n",argv[1]);
			return -1;
		}
		if (strstr(argv[1], ".ovk") != 0) {
			is_ovk = true;
			headblk_sz = 16; 
			out_ext = "ogg";
		} else {
			is_ovk = false;
			headblk_sz = 12;
			out_ext = "wav";
		}
		fread(buf, 1, 4, in);
		int index = read_little_endian_int(buf);
		if (index <= 0) { 
			if (is_ovk)
				fprintf(stderr,"Invalid Ogg-ovk file : %s : index = %d\n",argv[1],index);
			else
				fprintf(stderr,"Invalid Koe-nwk file : %s : index = %d\n",argv[1],index);
			return -1;
		}
		int* tbl_off = new int[index];
		int* tbl_siz = new int[index];
		int* tbl_cnt = new int[index];
		int* tbl_origsiz = new int[index];
		int i;
		for (i=0; i<index; i++) {
			fread(buf, 1, headblk_sz, in);
			tbl_siz[i] = read_little_endian_int(buf);
			tbl_off[i] = read_little_endian_int(buf+4);
			tbl_cnt[i] = read_little_endian_int(buf+8);
			tbl_origsiz[i] = read_little_endian_int(buf+12);
		}
		fseek(in, 0, 2);
		int fsize = ftell(in);
		for (i=0; i<index; i++) {
			if (tbl_off[i] <= 0 || tbl_siz[i] <= 0 || tbl_off[i]+tbl_siz[i] > fsize) {
				fprintf(stderr,"Invalid table[%d] : cnt %d off %d size %d / %d\n",i,tbl_cnt[i],tbl_off[i],tbl_siz[i],fsize);
				continue;
			}
			if (argc == 2)
				sprintf(outpath, "%s-%d.%s", argv[1], tbl_cnt[i],out_ext);
			else
				sprintf(outpath, "%s-%d.%s", argv[2], tbl_cnt[i],out_ext);
			FILE* out = fopen(outpath, "wb");
			if (out == 0) {
				fprintf(stderr,"Cannot open output file %s\n",outpath);
				continue;
			}
			fprintf(stderr,"Writing file %s...\n",outpath);
			fseek(in, tbl_off[i], 0);
			if (is_ovk) { // copy file
				int sz = tbl_siz[i];
				char buf[32*1024];
				while(sz > 32*1024) {
					fread(buf, 32*1024, 1, in);
					fwrite(buf, 32*1024, 1, out);
					sz -= 1024*32;
				}
				if (sz > 0) {
					fread(buf, sz, 1, in);
					fwrite(buf, sz, 1, out);
				}
			} else { // .nwk
				conv(in, out, 0, tbl_siz[i]);
			}
			fclose(out);
		}
		fclose(in);
		return 0;
	}
	FILE* in = fopen(argv[1],"rb");
	if (in == 0) {
		fprintf(stderr,"Cannot open file : %s\n",argv[1]);
		return -1;
	}
	FILE* out;
	if (argc != 3 && (!isatty(fileno(stdout)))) {	// wave file is written to stdout if stdout is redirected to a file
		out = stdout;
	} else {					// make a new file or use argv[2] for output file name
		char* outpath = new char[strlen(argv[1])+10];
		sprintf(outpath, "%s.wav",argv[1]);
		if (argc == 3) outpath = argv[2];
		out = fopen(outpath, "wb");
		if (out == 0) {
			fprintf(stderr,"Cannot open file : %s\n",outpath);
			return -1;
		}
	}
	conv(in, out, skip_count);
	fclose(in);
	if (out != stdout) fclose(out);
	return 0;
}
#else

#include"wavfile.h"

void NWAFILE::Seek(int count) {
	if (data == 0) data = new char[block_size];
	nwa->Rewind(stream);
	int dmy = 0;
	nwa->Decode(stream, data, dmy); // skip wav header
	data_len = 0;
	skip_count = count;
}
NWAFILE::NWAFILE(FILE* _stream) {
	skip_count = 0;
	data = 0;
	stream = _stream;
	nwa = new NWAData;
	nwa->ReadHeader(stream);
	if (!nwa->CheckHeader()) {
		return;
	}
	block_size = nwa->BlockLength();
	data = new char[block_size];
	data_len = 0;

	wavinfo.SamplingRate = nwa->freq;
	wavinfo.Channels = nwa->channels;
	wavinfo.DataBits = nwa->bps;

	int dmy = 0;
	data_len = nwa->Decode(stream, data, dmy); // skip wav header

	return;
}
NWAFILE::~NWAFILE() {
	if (stream) fclose(stream);
	if (data) delete[] data;
	if (nwa) delete nwa;
}
int NWAFILE::Read(char* buf, int blksize, int blklen) {
	if (data == 0) return -1; // end of file

	if (data_len > blksize * blklen) {
		int len = blksize * blklen;
		memcpy(buf, data, len);
		memmove(data, data+len, data_len-len);
		data_len -= len;
		return blklen;
	}
	memcpy(buf, data, data_len);
	int copied_length = data_len;
	data_len = 0;

	if (stream == 0) {
		delete[] data;
		data = 0;
		return copied_length / blksize;
	}

	// read
	do {
		int err;
retry:
		err = nwa->Decode(stream, data, skip_count);
		if (err == 0 || err == -1) { // eof or error
			delete[] data;
			data = 0;
			return copied_length / blksize;
		}
		if (err == -2) goto retry; // EAGAIN
		data_len = err;
		if (copied_length + data_len < blklen*blksize) {
			memcpy(buf+copied_length, data, data_len);
			copied_length += data_len;
			goto retry;
		}
	} while(0);

	// determine return length
	int datablks = (data_len+copied_length)/blksize;
	if (datablks <= 0) return 0;
	if (datablks > blklen) datablks = blklen;
	int rest_len = datablks * blksize - copied_length;
	if (rest_len) {
		memcpy(buf+copied_length, data, rest_len);
		memmove(data, data+rest_len, data_len-rest_len);
		data_len -= rest_len;
	}
	return datablks;
}

char* NWAFILE::ReadAll(FILE* in, int& total_size) {
	NWAData h;
	if (in == 0) return 0;
	h.ReadHeader(in);
	h.CheckHeader();
	int bs = h.BlockLength();
	total_size = h.datasize+0x2c;
	char* d = new char[total_size + bs*2];
	int dcur = 0;
	int err;
	int skip = 0;
	while(dcur < total_size+bs && (err=h.Decode(in, d+dcur, skip)) != 0) {
		if (err == -1) break;
		if (err == -2) continue;
		dcur += err;
	}
	return d;
}

#include"music.h"

char* decode_koe_nwa(AvgKoeInfo info, int* data_len) {
	NWAData h;
	if (info.stream == 0) return 0;
	fseek(info.stream, info.offset, 0);
	h.ReadHeader(info.stream, info.length);
	if (h.CheckHeader() == false) return 0;
	int bs = h.BlockLength();
	int total = h.datasize + 0x2c;
	char* d = new char[total + bs*2];
	int dcur = 0;
	int err;
	int skip = 0;
	while(dcur < total+bs && (err=h.Decode(info.stream, d+dcur, skip)) != 0) {
		if (err == -1) break;
		if (err == -2) continue;
		dcur += err;
	}
	if (data_len) {
		*data_len = dcur;
		if (*data_len > total) *data_len = total;
	}
	return d;
}

#endif