private import std.string;
private import std.file;
private import win32.ansi.windows;
private import util;
private import windll;
private import bga_melter;
private import qbga_gui;
//----------------------------------------------------------------
// おきまりのDLL初期化ルーチン
//----------------------------------------------------------------
HINSTANCE g_hinst;
extern(C)
{
void gc_init();
void gc_term();
void _minit();
void _moduleCtor();
void _moduleDtor();
void _moduleUnitTests();
}
extern (Windows)
BOOL DllMain( HINSTANCE inst, ULONG reason, void* reserved )
{
switch( reason )
{
case DLL_PROCESS_ATTACH:
g_hinst = inst;
gc_init(); // GC初期化
_minit(); // モジュールリスト初期化
_moduleCtor(); // モジュールコンストラクタ実行
_moduleUnitTests(); // 単体テスト実行
if( g_orig_dll is null )
return false;
break;
case DLL_PROCESS_DETACH:
_moduleDtor();
gc_term(); // GC終了
break;
default:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return true;
}
//----------------------------------------------------------------
// API転送処理
//----------------------------------------------------------------
WinDLL g_orig_dll = null;
UINT WM_ARCEXTRACT;
static this()
{
g_orig_dll = WinDLL.load( "_Bga32.DLL" );
WM_ARCEXTRACT = RegisterWindowMessage("wm_arcextract");
}
static ~this()
{
g_orig_dll.close();
}
template api(FnT)
{
FnT api( char[] name )
{
return g_orig_dll.get_api!(FnT)( name );
}
}
//----------------------------------------------------------------
// 統合アーカイバAPI:転送
//----------------------------------------------------------------
extern(Windows)
{
int Bga( HWND a, char* b, char* c, DWORD d )
{
int r = Bga_impl( a, toString(b) );
if( r < 0 ) // このダミーDLLでは処理できないコマンドだった時
return api!(typeof(&Bga))("Bga")(a,b,c,d);
return r;
}
WORD QBgaGetVersion()
{
return 4;
}
WORD BgaGetVersion()
{
return api!(typeof(&BgaGetVersion))("BgaGetVersion")();
}
BOOL BgaGetRunning()
{
return api!(typeof(&BgaGetRunning))("BgaGetRunning")();
}
BOOL BgaCheckArchive( char* a, int b )
{
return api!(typeof(&BgaCheckArchive))("BgaCheckArchive")(a,b);
}
BOOL BgaConfigDialog( HWND a, char* b, int c )
{
return api!(typeof(&BgaConfigDialog))("BgaConfigDialog")(a,b,c);
}
int BgaGetFileCount( char* a )
{
return api!(typeof(&BgaGetFileCount))("BgaGetFileCount")(a);
}
BOOL BgaQueryFunctionList( int a )
{
return api!(typeof(&BgaQueryFunctionList))("BgaQueryFunctionList")(a);
}
alias void* HARC;
HARC BgaOpenArchive( HWND a, char* b, DWORD c )
{
return api!(typeof(&BgaOpenArchive))("BgaOpenArchive")(a,b,c);
}
int BgaCloseArchive( HARC a )
{
return api!(typeof(&BgaCloseArchive))("BgaCloseArchive")(a);
}
alias void* LPINDIVIDUALINFO;
int BgaFindFirst( HARC a, char* b, LPINDIVIDUALINFO c )
{
return api!(typeof(&BgaFindFirst))("BgaFindFirst")(a,b,c);
}
int BgaFindNext( HARC a, LPINDIVIDUALINFO b )
{
return api!(typeof(&BgaFindNext))("BgaFindNext")(a,b);
}
DWORD BgaGetArcOriginalSize( HARC a )
{
return api!(typeof(&BgaGetArcOriginalSize))("BgaGetArcOriginalSize")(a);
}
DWORD BgaGetArcCompressedSize( HARC a )
{
return api!(typeof(&BgaGetArcCompressedSize))("BgaGetArcCompressedSize")(a);
}
WORD BgaGetArcRatio( HARC a )
{
return api!(typeof(&BgaGetArcRatio))("BgaGetArcRatio")(a);
}
BOOL BgaSetOwnerWindow( HWND a )
{
BOOL r = api!(typeof(&BgaSetOwnerWindow))("BgaSetOwnerWindow")(a);
if( r ) BgaSetOwnerWindow_impl(a);
return r;
}
BOOL BgaClearOwnerWindow()
{
BOOL r = api!(typeof(&BgaClearOwnerWindow))("BgaClearOwnerWindow")();
BgaClearOwnerWindow_impl();
return r;
}
alias BOOL function(HWND,UINT,UINT,EXTRACTINGINFOEX*) ARCHIVERPROC;
BOOL BgaSetOwnerWindowEx( HWND a, ARCHIVERPROC* b )
{
BOOL r = api!(typeof(&BgaSetOwnerWindowEx))("BgaSetOwnerWindowEx")(a,b);
if( r ) BgaSetOwnerWindowEx_impl(a,b);
return r;
}
BOOL BgaKillOwnerWindowEx( HWND a )
{
BOOL r = api!(typeof(&BgaKillOwnerWindowEx))("BgaKillOwnerWindowEx")(a);
BgaClearOwnerWindow_impl();
return r;
}
alias void* UNLHA_WND_ENUMMEMBPROC;
BOOL BgaSetEnumMembersProc( UNLHA_WND_ENUMMEMBPROC a )
{
return api!(typeof(&BgaSetEnumMembersProc))("BgaSetEnumMembersProc")(a);
}
BOOL BgaClearEnumMembersProc()
{
return api!(typeof(&BgaClearEnumMembersProc))("BgaClearEnumMembersProc")();
}
}
//----------------------------------------------------------------
// 統合アーカイバAPI:実装( Bga )
//----------------------------------------------------------------
int Bga_impl( HWND wnd, char[] cmd_str )
{
enum { UNSUPPORTED = -1 }
//
// コマンドライン解析
//
char[][] cmd = cmd_parse(cmd_str);
// x以外のコマンドは扱わないで本物DLLに回します。注意点として:
// > command はコマンドラインの最初の引数としてください。なお、command を省略
// > した場合は 'x' command が指定されたものとみなします。
if( cmd.length == 0 )
return UNSUPPORTED;
if( cmd[0].length == 1 )
{
if( 0 <= find("adjlmnstvADJLMNSTV", cmd[0][0]) )
return UNSUPPORTED;
if( cmd[0][0]=='x' || cmd[0][0]=='X' )
cmd = cmd[1 .. length];
}
// ※ この時点で、cmdにはcommandを除いた残りの引数が入っているはず
//
// スイッチ解析、引数解析
//
bool all_attrs = false; // -a
bool silent = false; // -i
bool ignore_dir = false; // -j
bool newfile_only = false; // -n
bool force_overwrite = false; // -o
bool recursive = false; // -r
bool sanitize_path = true;
char[] arc_name = null;
char[] base_dir = null;
char[][] paths;
foreach( char[] param ; cmd )
if( param[0] == '-' )
switch( param[1] )
{
case 'a','A': all_attrs = true; break;
case 'i','I': silent = true; break;
case 'j','J': ignore_dir = true; break;
case 'n','N': newfile_only = true; break;
case 'o','O': force_overwrite = true; break;
case 'r','R': recursive = true; break;
default: break;
}
else if( arc_name is null )
{
arc_name = param;
}
else if( base_dir is null )
{
if( lastChar(param) == '\\' )
base_dir = param;
else {
base_dir.length = GetCurrentDirectory(0,null)+1;
GetCurrentDirectory(base_dir.length, base_dir);
base_dir.length = strlen(base_dir);
if( lastChar(base_dir) != '\\' )
base_dir ~= '\\';
}
}
else
paths ~= param;
//
// 展開処理にGo!
//
ProgressDlg dlg = null;
if( !do_ownerwnd_proc( OP_ARC_BEGIN, null, 0, arc_name ) )
return 0x8020;
try
{
if( !silent && g_handler is null ) // -i / OwnerWndProc
{
dlg = new ProgressDlg(
cast(DLGTEMPLATE*) g_orig_dll.load_dialog("#2025"), wnd );
dlg.set_arcname(arc_name);
}
char[] src_fname; // OwnerWndProc関係
BgaHeader cur_hdr; // OwnerWndProc関係
BgaAnswer handler( inout BgaHeader hdr )
{
src_fname = hdr.fname;
process_messages();
// paths
if( paths.length > 0 )
{
char[] fname = // -r
(recursive ? hdr.fname[hdr.dir_name_len..length]
: hdr.fname);
foreach( char[] w ; paths )
if( wild_match( w, fname ) )
goto ok;
return BgaAnswer.SkipIt;
ok:;
}
// -a
if( !all_attrs && (hdr.attrib&6) )
return BgaAnswer.SkipIt;
// dialog
if( dlg )
if( dlg.closed )
return BgaAnswer.Abort;
else
dlg.set_filename( hdr.fname[hdr.dir_name_len .. length] );
// -j
if( ignore_dir )
hdr.fname = hdr.fname[hdr.dir_name_len .. length];
// sanitize
if( sanitize_path )
hdr.fname = check_path(hdr.fname);
// base_dir
hdr.fname = base_dir ~ hdr.fname;
// -o
if( !force_overwrite )
try {
if( std.file.exists(hdr.fname) && std.file.isfile(hdr.fname) )
// -n
if( newfile_only )
{
if( newer_than(hdr.date,hdr.time,hdr.fname) )
return BgaAnswer.SkipIt;
}
else
{
int r = MessageBox( dlg?dlg.hwnd:wnd,
toStringz("Overwrite "~hdr.fname~" ?"),
"QBga32.dll", MB_YESNOCANCEL );
if( r == IDNO ) return BgaAnswer.SkipIt;
if( r == IDCANCEL ) return BgaAnswer.Abort;
}
} catch {}
cur_hdr = hdr;
if( !do_ownerwnd_proc( OP_FILE_BEGIN, &cur_hdr, 0, src_fname ) )
return BgaAnswer.Abort;
return BgaAnswer.MeltIt;
}
BgaAnswer progress_handler( int cur, int max )
{
process_messages();
if( dlg )
if( dlg.closed )
return BgaAnswer.Abort;
else
dlg.set_pos( cast(real)(cur)/max );
if( !do_ownerwnd_proc( OP_FILE_MIDDLE, &cur_hdr, cur, src_fname ) )
return BgaAnswer.Abort;
return BgaAnswer.MeltIt;
}
(new BgaMelter(arc_name)).start(&handler,&progress_handler);
}
catch( BgaMelterError e )
{
return e.errcode;
}
finally
{
do_ownerwnd_proc( OP_ARC_END, null, 0, arc_name );
if( dlg )
dlg.close();
}
return 0;
}
//----------------------------------------------------------------
// 統合アーカイバAPI:実装( SetOwnerWindow )
//----------------------------------------------------------------
align(1) struct EXTRACTINGINFO
{
DWORD dwFileSize;
DWORD dwWriteSize;
char szSourceFileName[512 + 1];
char dummy1[3];
char szDestFileName[512 + 1];
char dummy[3];
}
align(1) struct EXTRACTINGINFOEX
{
EXTRACTINGINFO exinfo;
DWORD dwCompressedSize;
DWORD dwCRC;
UINT uOSType;
WORD wRatio;
WORD wDate;
WORD wTime;
char szAttribute[8];
char szMode[8];
}
HWND g_owner_window;
extern(Windows) BOOL function(HWND,UINT,UINT,EXTRACTINGINFOEX*) g_handler;
extern(Windows) BOOL noex_handler( HWND w,UINT m,UINT s, EXTRACTINGINFOEX* e )
{
return !SendMessage( w, m, s, cast(LPARAM) &e.exinfo );
}
void BgaSetOwnerWindow_impl( HWND wnd )
{
g_owner_window = wnd;
g_handler = &noex_handler;
}
void BgaClearOwnerWindow_impl()
{
g_owner_window = null;
g_handler = null;
}
void BgaSetOwnerWindowEx_impl( HWND wnd, ARCHIVERPROC* proc )
{
g_owner_window = wnd;
g_handler = *proc;
}
enum { OP_FILE_BEGIN, OP_FILE_MIDDLE, OP_ARC_END, OP_ARC_BEGIN }
bool do_ownerwnd_proc( UINT uState, BgaHeader* hdr, int cur, char[] src_fname )
{
if( g_handler is null )
return true;
EXTRACTINGINFOEX ex;
if( uState == OP_ARC_BEGIN || uState == OP_ARC_END )
{
lstrcpyn( ex.exinfo.szSourceFileName, toStringz(src_fname), 512 );
}
else
{
ex.exinfo.dwFileSize = hdr.original_size;
ex.exinfo.dwWriteSize = cur;
lstrcpyn( ex.exinfo.szSourceFileName, toStringz(src_fname), 512 );
lstrcpyn( ex.exinfo.szDestFileName, toStringz(hdr.fname), 512 );
ex.dwCompressedSize = hdr.compressed_size;
ex.wRatio = cast(int)( (cast(real)hdr.compressed_size)/hdr.original_size*1000 );
ex.wDate = hdr.date;
ex.wTime = hdr.time;
ex.szAttribute[0] = (hdr.attrib&32 ? 'A': '-');
ex.szAttribute[1] = (hdr.attrib&1 ? 'R': '-');
ex.szAttribute[2] = (hdr.attrib&2 ? 'H': '-');
ex.szAttribute[3] = (hdr.attrib&4 ? 'S': '-');
ex.szAttribute[4] = (hdr.attrib&16 ? 'D': '-');
ex.szAttribute[5] = '\0';
if( hdr.method[0]=='G' )
lstrcpy(ex.szMode,"-gzip-");
else
lstrcpy(ex.szMode,"-bzip2-");
}
return false != g_handler( g_owner_window, WM_ARCEXTRACT, uState, &ex );
}
//----------------------------------------------------------------
// パス検査系
//----------------------------------------------------------------
alias std.c.windows.windows.IsDBCSLeadByte isDL;
char[] replace_yen( char[] s )
{
char[] ans;
int j=0;
for(int i=0; i!=s.length; i=i+(isDL(s[i])?2:1))
if( s[i] == '\\' )
ans~=s[j .. i], ans~='/', j=i+1;
ans ~= s[j .. length];
return ans;
}
bool wild_match( char[] wild, char[] name )
{
bool wild_match_nopath( char[] w, char[] s )
{
char[] advance( char[] s )
{
return s[(IsDBCSLeadByte(s[0])?2:1) .. length];
}
while( w.length>0 )
switch( w[0] )
{
case '?':
if( s.length==0 )
return false;
w = advance(w);
s = advance(s);
break;
case '*':
if( s.length==0 )
return false;
w = advance(w);
if( w.length == 0 )
return true;
for( ; s.length!=0; s=advance(s) )
if( wild_match_nopath(w,s) )
return true;
return false;
default:
if( s.length==0 )
return false;
if( isDL(w[0]) )
{ if( w[0..2] != s[0..2] ) return false; }
else
{ if( w[0] != s[0] ) return false; }
w = advance(w);
s = advance(s);
break;
}
return s.length==0;
}
if( wild=="" || wild=="*.*" || wild=="*" || wild=="**" )
return true;
char[][] wilds = split( replace_yen( tolower(wild) ), "/" );
char[][] names = split( replace_yen( tolower(name) ), "/" );
if( wilds.length != names.length )
return false;
for(int i=0; i!=wilds.length; ++i)
if( wilds[i]!="*.*" && wilds[i]!="*" && wilds[i]!="**" )
if( !wild_match_nopath( wilds[i], names[i] ) )
return false;
return true;
}
char[] check_path( char[] path )
{
// C:\ ==> C_\
if( path.length>=2 && path[1]==':' )
path = path.dup, path[1] = '_';
// \\hoge ==> hoge
// /hoge ==> hoge
while( path.length>0 && (path[0]=='\\'||path[0]=='/') )
path = path[1..length];
// .. ==> __
char[][] paths = split( replace_yen(path), "/" );
L1:
foreach( inout char[] pc ; paths )
if( pc.length >= 2 )
{
foreach( char c ; pc )
if( c != '.' )
continue L1;
pc = replace( pc, ".", "_" );
}
return join( paths, "\\" );
}
//----------------------------------------------------------------
// 簡易テスト@もっとマジメに書かなきゃ…
//----------------------------------------------------------------
unittest
{
assert( check_path(`\\\\hoge\fuga`)==`hoge\fuga` );
assert( check_path(`/usr/local/`)==`usr\local\` );
assert( check_path(`..\abc def\...\.\g`)==`__\abc def\___\.\g` );
assert( wild_match(`a/b/c`,`A\b\C`) );
assert( wild_match(`a/*.*/a?x`,`A\hoge\Afx`) );
assert( Bga_impl(null,"a hoge") < 0 );
}