
# wuvorbisfile.h などを出力するためのスクリプト


$file = "../vorbis/include/vorbis/vorbisfile.h";
$codec_h = "../vorbis/include/vorbis/codec.h";
$cpulib = "../../../../core/visual/IA32/tvpgl_ia32_intf.h";
$dll = "../../../../../bin/win32/plugin/wuvorbis.dll";
$def = "..\\wuvorbis.def";

open FH, $file or die;
undef $/;
$file = <FH>; # ファイルを一気に読み込み

open FH, $codec_h or die;
undef $/;
$codec_h = <FH>; # ファイルを一気に読み込み

open FH, $cpulib or die;
undef $/;
$cpulib = <FH>; # ファイルを一気に読み込み

if($file !~ /pragma pack/)
{
	# pragma pack はDLLとそれを利用する側で正常にバイナリレイアウトが
	# 一致するために必要。
	# vorbisfile.h の 構造体は #pragma pack(push, 4) で囲まれている必要がある。
	die "`pragma pack` not found in vorbisfile.h (foget patching ?)";
}
if($codec_h !~ /pragma pack/)
{
	# pragma pack はDLLとそれを利用する側で正常にバイナリレイアウトが
	# 一致するために必要。
	# vorbisfile.h の 構造体は #pragma pack(push, 4) で囲まれている必要がある。
	die "`pragma pack` not found in codec.h (foget patching ?)";
}

if($file !~ /_cdecl/)
{
	# 呼び出し規約の衝突(wuvorbis.dll 内では__fastcall なので) を
	# 防ぐために _cdecl 呼び出し規約が ov_callbacks 構造体内の
	# 各関数に必要。
	die "`_cdecl` not found in vorbisfile.h (foget patching ?)";
}

# 型を変換

$file =~ s/ogg_int64_t/wu_ogg_int64_t/gs;
$file =~ s/OggVorbis_File/wu_OggVorbis_File/gs;
$file =~ s/ov_callbacks/wu_ov_callbacks/gs;
$codec_h =~ s/vorbis_info/wu_vorbis_info/gs;
$codec_h =~ s/vorbis_comment/wu_vorbis_comment/gs;
$file =~ s/vorbis_info/wu_vorbis_info/gs;
$file =~ s/vorbis_comment/wu_vorbis_comment/gs;

# ファイルを開く

open FH, ">include\\wuvorbisfile.h";
open FC, ">..\\wuvorbisfile_stub.c";
open FD, ">$def";

# wuvorbis.def の共通部分を出力
print FD <<EOF;
EXPORTS
	GetModuleInstance
	V2Link
	V2Unlink
	GetOptionDesc
	Query_sizeof_OggVorbis_File
EOF


# ヘッダを出力

print FH <<EOF;

/*
	OggVorbis decoder interface file

	This file is always generated by gen_wuvorbisfile_h.pl
*/

#ifndef _WUVORBISFILE_H_
#define _WUVORBISFILE_H_

#include <stddef.h>


#ifdef __cplusplus
extern "C"
{
#endif

typedef __int64 wu_ogg_int64_t;

#ifndef IN_WUVORBISMAINUNIT
extern int WuVorbisInit(const char *dll_file_name_can_be_null);
extern int WuVorbisUninit(void);
extern int  (_stdcall* Query_sizeof_OggVorbis_File)(void);
#endif

EOF


# wuvorbismainunit.cpp 内蔵の関数を追加
$file .= <<EOF;

extern void SetCPUType(unsigned __int32 type);
extern unsigned __int32 DetectCPU(void);
extern void ScaleOutput(float scale);

EOF


# OggVorbis_File の構造体サイズをチェック
# 構造体のサイズは、実際に DLL 中の Query_sizeof_OggVorbis_File
# という関数を呼び出して得る。Query_sizeof_OggVorbis_File は
# 以下のような単純な関数で、
# extern "C" int _stdcall _export Query_sizeof_OggVorbis_File()
# {
#	// returns sizeof(OggVorbis_File)
#	return sizeof(OggVorbis_File);
# }
# OggVorbis_File 構造体のサイズを得るのに使う。
$size = - - `query_sizeof_oggvorbis_file.exe $dll`;
if($size == 0)
{
	# wuvorbis.dll と gen_wuvorbisfile_h.pl は相互に依存しています。
	# ここで失敗した場合は、いったんそのまま wuvorbis.dll を構築してから
	# もう一度 gen_wuvorbisfile_h.pl を実行し、それからもう一度
	# wuvorbis.dll を構築する必要があります。
	print "Warning: query_sizeof_oggvorbis_file.exe failed.\n";
	print "         You must once build wuvorbis.dll and re-run this script,\n";
	print "         then build wuvorbis.dll again.\n";
}

# OggVorbis_File 構造体を出力
# 構造体サイズに +256 をしているのは、将来の
# wuvorbis.dll の仕様変更に柔軟に対応出来るようにするため
# OggVorbis_File のサイズがすこしぐらい増えても問題にならないように

print FH <<EOF;

#pragma pack(push, 4)

typedef struct
{
	char data[$size + 256];
} wu_OggVorbis_File;

EOF


# ov_callback 構造体を出力

$file =~ /(typedef struct \{.*?wu_ov_callbacks)/s;

$struct = $1;

print FH "$struct;\n\n";

# vorbis_info 構造体を出力

$codec_h =~ /(typedef struct wu_vorbis_info.*?wu_vorbis_info;)/s;

$struct = $1;

$struct =~ s/\/\*.*?\*\///gs; # コメントは削除させて頂く
$struct =~ s/\n\s*\n+/\n/gs; # 空行も

print FH "$struct\n\n";

# vorbis_comment 構造体を出力

$codec_h =~ /(typedef struct wu_vorbis_comment.*wu_vorbis_comment;)/s;

$struct = $1;

$struct =~ s/\/\*.*?\*\///gs; # コメントは削除させて頂く
$struct =~ s/\n\s*\n+/\n/gs; # 空行も

print FH "$struct\n\n";

# define 定義を出力

while($file =~ /(#define\s+)(\w+)(\s+\d+)/g)
{
	print FH "${1}WU_$2$3\n";
}
while($codec_h =~ /(#define\s+)(\w+)(\s+\-?\d+)/g)
{
	print FH "${1}WU_$2$3\n";
}
while($cpulib =~ /(#define\s+)(\w+)(\s+0x[0-9A-Fa-f]+)/g)
{
	$def = $1;
	$name = $2;
	$val = $3;
	$name =~ s/TVP_/WU_/g;
	print FH "$def$name$val\n";
}

print FH <<EOF;

#pragma pack(pop)

EOF


# 各関数定義を出力
print FH <<EOF;
#ifndef IN_WUVORBISMAINUNIT
EOF

while($file =~ /(extern\s+[^\"].*?;)/gs) # "
{
	$func = $1;
	$func =~ s/\n//gs;
	$func =~ /extern (.*?)(\w+)\((.*?)\);/;

	$rettype = $1;
	$funcname = $2;
	$args = $3;

	if($args =~ /FILE/) { next; } # FILE 構造体は扱えません

	push (@impfuncs, "wu_$funcname");

	print FH "extern $rettype (_cdecl *wu_$funcname)($args);\n";

	$impstub .= "$rettype (_cdecl *wu_$funcname)($args) = NULL;\n";

	# fc に呼び出し規約変換用のスタブを出力する
	print FC "extern $rettype _cdecl wu_$funcname ($args){\n";

	$rettype =~ /^\s*(.*?)\s*$/;
	$rettype = $1;
	if($rettype ne "void")
	{
		print FC "\treturn ($rettype) $funcname(";
	}
	else
	{
		print FC "\t$funcname(";
	}

	$args =~ /^\s*(.*?)\s*$/;
	$args = $1;
	if($args ne "void")
	{

		@args = split(/,/, $args);
		$first = 1;
		foreach $each (@args)
		{
			$each =~ /^\s*(.*?)\s*$/;
			$each = $1;
			$each =~ /(.*?)(\w+)$/;
			$type = $1;
			$value = $2;

			$type =~ /^\s*(.*?)\s*$/;
			$type = $1;
			$type =~ s/wu_(\w+)/$1/;

			if($type eq "ov_callbacks")
			{
				# 構造体渡しなのでちょいと複雑
				$cast = "*(ov_callbacks*)&"
			}
			else
			{
				$cast = "($type)";
			}

			print FC "," if !$first;
			$first = 0;
			print FC "$cast$value";
		}
	}
	print FC ");\n";
	print FC "}\n";

	# FD に関数名を出力
	print FD "\twu_$funcname\n";
}

print FH <<EOF;
#endif
EOF


# フッタを出力

print FH <<EOF;

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif


EOF

# インポートスタブを作成

open FI, ">lib\\wuvorbisfile.c";

print FI <<EOF;

/*
	OggVorbis decoder interface file

	This file is always generated by gen_wuvorbisfile_h.pl
*/

#include <windows.h>
#include "wuvorbisfile.h"

$impstub;

static HMODULE WuVorbisDLL = NULL;
int  (_stdcall* Query_sizeof_OggVorbis_File)(void) = NULL;
int WuVorbisUninit(void);

int WuVorbisInit(const char *dll_file_name_can_be_null)
{
	/*
	 initialize wuvorbis.dll and import functions
	 input:
		 dll_file_name_can_be_null:
		 	Path to the wuvorbis.dll. This can be NULL, then default
		 	name "wuvorbis.dll" is used.
	 return value:
		 0   success
		 1   failed (wuvorbis.dll not found or cannot be loaded)
		 2   failed (at least one of import functions is not found)
		 3   failed (incompatible version of wuvorbis.dll)
	*/
	typedef int  (_stdcall*t_Query_sizeof_OggVorbis_File)(void);

	if(WuVorbisDLL) return 0;

	WuVorbisDLL = LoadLibrary(dll_file_name_can_be_null ? dll_file_name_can_be_null : "wuvorbis.dll");
	if(!WuVorbisDLL) return 1;

	Query_sizeof_OggVorbis_File = (int  (_stdcall*)(void))
		GetProcAddress(WuVorbisDLL, "Query_sizeof_OggVorbis_File");
	if(!Query_sizeof_OggVorbis_File) goto fail;

	if(Query_sizeof_OggVorbis_File() > sizeof(wu_OggVorbis_File))
		goto fail_with_status_3;

EOF

	foreach $each (@impfuncs)
	{
		print FI "\t*(void**)&$each = GetProcAddress(WuVorbisDLL, \"$each\");\n";
		print FI "\tif(!$each) goto fail;\n";
	}


print FI <<EOF;
	return 0;

fail:
	WuVorbisUninit();
	return 2;

fail_with_status_3:
	WuVorbisUninit();
	return 3;
}

int WuVorbisUninit(void)
{
	/*
	 uninitialize wuvorbis.dll
	 return value:
	 0   success
	 1   unloading failed
	*/

	if(!WuVorbisDLL) return 0;

	Query_sizeof_OggVorbis_File = NULL;

EOF
	foreach $each (@impfuncs)
	{
		print FI "\t$each = NULL;\n";
	}

print FI <<EOF;

	FreeLibrary(WuVorbisDLL);
	WuVorbisDLL = NULL;

	return 0;
}


EOF

