Artifact Content

Not logged in

Artifact f1f9b8362f0839ce37237bda755c6d12ea964d0d


     1  private import std.string;
     2  private import std.file;
     3  private import win32.ansi.windows;
     4  private import util;
     5  private import windll;
     6  private import bga_melter;
     7  private import qbga_gui;
     8  
     9  //----------------------------------------------------------------
    10  // おきまりのDLL初期化ルーチン
    11  //----------------------------------------------------------------
    12  
    13  HINSTANCE g_hinst;
    14  
    15  extern(C)
    16  {
    17  	void gc_init();
    18  	void gc_term();
    19  	void _minit();
    20  	void _moduleCtor();
    21  	void _moduleDtor();
    22  	void _moduleUnitTests();
    23  }
    24  
    25  extern (Windows)
    26  BOOL DllMain( HINSTANCE inst, ULONG reason, void* reserved )
    27  {
    28  	switch( reason )
    29  	{
    30  	case DLL_PROCESS_ATTACH:
    31  		g_hinst = inst;
    32  		gc_init();          // GC初期化
    33  		_minit();           // モジュールリスト初期化
    34  		_moduleCtor();      // モジュールコンストラクタ実行
    35  		_moduleUnitTests(); // 単体テスト実行
    36  		if( g_orig_dll is null )
    37  			return false;
    38  		break;
    39  
    40  	case DLL_PROCESS_DETACH:
    41  		_moduleDtor();
    42  		gc_term();     // GC終了
    43  		break;
    44  
    45  	default:
    46  	case DLL_THREAD_ATTACH:
    47  	case DLL_THREAD_DETACH:
    48  		break;
    49  	}
    50  	return true;
    51  }
    52  
    53  //----------------------------------------------------------------
    54  // API転送処理
    55  //----------------------------------------------------------------
    56  
    57  WinDLL g_orig_dll = null;
    58  UINT   WM_ARCEXTRACT;
    59  
    60  static this()
    61  {
    62  	g_orig_dll = WinDLL.load( "_Bga32.DLL" );
    63  	WM_ARCEXTRACT = RegisterWindowMessage("wm_arcextract");
    64  }
    65  
    66  static ~this()
    67  {
    68  	g_orig_dll.close();
    69  }
    70  
    71  template api(FnT)
    72  {
    73  	FnT api( char[] name )
    74  	{
    75  		return g_orig_dll.get_api!(FnT)( name );
    76  	}
    77  }
    78  
    79  //----------------------------------------------------------------
    80  // 統合アーカイバAPI:転送
    81  //----------------------------------------------------------------
    82  
    83  extern(Windows)
    84  {
    85  	int Bga( HWND a, char* b, char* c, DWORD d )
    86  	{
    87  		int r = Bga_impl( a, toString(b) );
    88  		if( r < 0 ) // このダミーDLLでは処理できないコマンドだった時
    89  			return api!(typeof(&Bga))("Bga")(a,b,c,d);
    90  		return r;
    91  	}
    92  
    93  	WORD QBgaGetVersion()
    94  	{
    95  		return 4;
    96  	}
    97  
    98  	WORD BgaGetVersion()
    99  	{
   100  		return api!(typeof(&BgaGetVersion))("BgaGetVersion")();
   101  	}
   102  
   103  	BOOL BgaGetRunning()
   104  	{
   105  		return api!(typeof(&BgaGetRunning))("BgaGetRunning")();
   106  	}
   107  
   108  	BOOL BgaCheckArchive( char* a, int b )
   109  	{
   110  		return api!(typeof(&BgaCheckArchive))("BgaCheckArchive")(a,b);
   111  	}
   112  
   113  	BOOL BgaConfigDialog( HWND a, char* b, int c )
   114  	{
   115  		return api!(typeof(&BgaConfigDialog))("BgaConfigDialog")(a,b,c);
   116  	}
   117  
   118  	int BgaGetFileCount( char* a )
   119  	{
   120  		return api!(typeof(&BgaGetFileCount))("BgaGetFileCount")(a);
   121  	}
   122  
   123  	BOOL BgaQueryFunctionList( int a )
   124  	{
   125  		return api!(typeof(&BgaQueryFunctionList))("BgaQueryFunctionList")(a);
   126  	}
   127  
   128  	alias void* HARC;
   129  	HARC BgaOpenArchive( HWND a, char* b, DWORD c )
   130  	{
   131  		return api!(typeof(&BgaOpenArchive))("BgaOpenArchive")(a,b,c);
   132  	}
   133  
   134  	int BgaCloseArchive( HARC a )
   135  	{
   136  		return api!(typeof(&BgaCloseArchive))("BgaCloseArchive")(a);
   137  	}
   138  
   139  	alias void* LPINDIVIDUALINFO;
   140  	int BgaFindFirst( HARC a, char* b, LPINDIVIDUALINFO c )
   141  	{
   142  		return api!(typeof(&BgaFindFirst))("BgaFindFirst")(a,b,c);
   143  	}
   144  
   145  	int BgaFindNext( HARC a, LPINDIVIDUALINFO b )
   146  	{
   147  		return api!(typeof(&BgaFindNext))("BgaFindNext")(a,b);
   148  	}
   149  
   150  	DWORD BgaGetArcOriginalSize( HARC a )
   151  	{
   152  		return api!(typeof(&BgaGetArcOriginalSize))("BgaGetArcOriginalSize")(a);
   153  	}
   154  
   155  	DWORD BgaGetArcCompressedSize( HARC a )
   156  	{
   157  		return api!(typeof(&BgaGetArcCompressedSize))("BgaGetArcCompressedSize")(a);
   158  	}
   159  
   160  	WORD BgaGetArcRatio( HARC a )
   161  	{
   162  		return api!(typeof(&BgaGetArcRatio))("BgaGetArcRatio")(a);
   163  	}
   164  
   165  	BOOL BgaSetOwnerWindow( HWND a )
   166  	{
   167  		BOOL r = api!(typeof(&BgaSetOwnerWindow))("BgaSetOwnerWindow")(a);
   168  		if( r ) BgaSetOwnerWindow_impl(a);
   169  		return r;
   170  	}
   171  
   172  	BOOL BgaClearOwnerWindow()
   173  	{
   174  		BOOL r = api!(typeof(&BgaClearOwnerWindow))("BgaClearOwnerWindow")();
   175  		BgaClearOwnerWindow_impl();
   176  		return r;
   177  	}
   178  
   179  	alias BOOL function(HWND,UINT,UINT,EXTRACTINGINFOEX*) ARCHIVERPROC;
   180  	BOOL BgaSetOwnerWindowEx( HWND a, ARCHIVERPROC* b )
   181  	{
   182  		BOOL r = api!(typeof(&BgaSetOwnerWindowEx))("BgaSetOwnerWindowEx")(a,b);
   183  		if( r ) BgaSetOwnerWindowEx_impl(a,b);
   184  		return r;
   185  	}
   186  
   187  	BOOL BgaKillOwnerWindowEx( HWND a )
   188  	{
   189  		BOOL r = api!(typeof(&BgaKillOwnerWindowEx))("BgaKillOwnerWindowEx")(a);
   190  		BgaClearOwnerWindow_impl();
   191  		return r;
   192  	}
   193  
   194  	alias void* UNLHA_WND_ENUMMEMBPROC;
   195  	BOOL BgaSetEnumMembersProc( UNLHA_WND_ENUMMEMBPROC a )
   196  	{
   197  		return api!(typeof(&BgaSetEnumMembersProc))("BgaSetEnumMembersProc")(a);
   198  	}
   199  
   200  	BOOL BgaClearEnumMembersProc()
   201  	{
   202  		return api!(typeof(&BgaClearEnumMembersProc))("BgaClearEnumMembersProc")();
   203  	}
   204  }
   205  
   206  //----------------------------------------------------------------
   207  // 統合アーカイバAPI:実装( Bga )
   208  //----------------------------------------------------------------
   209  
   210  int Bga_impl( HWND wnd, char[] cmd_str )
   211  {
   212  	enum { UNSUPPORTED = -1 }
   213  
   214  	//
   215  	// コマンドライン解析
   216  	//
   217  	char[][] cmd = cmd_parse(cmd_str);
   218  
   219  	// x以外のコマンドは扱わないで本物DLLに回します。注意点として:
   220  	//  > command はコマンドラインの最初の引数としてください。なお、command を省略
   221  	//  > した場合は 'x' command が指定されたものとみなします。
   222  	if( cmd.length == 0 )
   223  		return UNSUPPORTED;
   224  	if( cmd[0].length == 1 )
   225  	{
   226  		if( 0 <= find("adjlmnstvADJLMNSTV", cmd[0][0]) )
   227  			return UNSUPPORTED;
   228  		if( cmd[0][0]=='x' || cmd[0][0]=='X' )
   229  			cmd = cmd[1 .. length];
   230  	}
   231  
   232  		// ※ この時点で、cmdにはcommandを除いた残りの引数が入っているはず
   233  
   234  	//
   235  	// スイッチ解析、引数解析
   236  	//
   237  	bool all_attrs       = false; // -a
   238  	bool silent          = false; // -i
   239  	bool ignore_dir      = false; // -j
   240  	bool newfile_only    = false; // -n
   241  	bool force_overwrite = false; // -o
   242  	bool recursive       = false; // -r
   243  	bool sanitize_path   = true;
   244  
   245  	char[] arc_name = null;
   246  	char[] base_dir = null;
   247  	char[][] paths;
   248  
   249  	foreach( char[] param ; cmd )
   250  		if( param[0] == '-' )
   251  			switch( param[1] )
   252  			{
   253  			case 'a','A': all_attrs       = true; break;
   254  			case 'i','I': silent          = true; break;
   255  			case 'j','J': ignore_dir      = true; break;
   256  			case 'n','N': newfile_only    = true; break;
   257  			case 'o','O': force_overwrite = true; break;
   258  			case 'r','R': recursive       = true; break;
   259  			default:  break;
   260  			}
   261  		else if( arc_name is null )
   262  		{
   263  			arc_name = param;
   264  		}
   265  		else if( base_dir is null )
   266  		{
   267  			if( lastChar(param) == '\\' )
   268  				base_dir = param;
   269  			else {
   270  				base_dir.length = GetCurrentDirectory(0,null)+1;
   271  				GetCurrentDirectory(base_dir.length, base_dir);
   272  				base_dir.length = strlen(base_dir);
   273  				if( lastChar(base_dir) != '\\' )
   274  					base_dir ~= '\\';
   275  			}
   276  		}
   277  		else
   278  			paths ~= param;
   279  
   280  	//
   281  	// 展開処理にGo!
   282  	//
   283  	ProgressDlg dlg = null;
   284  	if( !do_ownerwnd_proc( OP_ARC_BEGIN, null, 0, arc_name ) )
   285  		return 0x8020;
   286  
   287  	try
   288  	{
   289  		if( !silent && g_handler is null ) // -i / OwnerWndProc
   290  		{
   291  			dlg = new ProgressDlg(
   292  				cast(DLGTEMPLATE*) g_orig_dll.load_dialog("#2025"), wnd );
   293  			dlg.set_arcname(arc_name);
   294  		}
   295  
   296  		char[]    src_fname; // OwnerWndProc関係
   297  		BgaHeader cur_hdr;   // OwnerWndProc関係
   298  
   299  		BgaAnswer handler( inout BgaHeader hdr )
   300  		{
   301  			src_fname = hdr.fname;
   302  			process_messages();
   303  
   304  			// paths
   305  			if( paths.length > 0 )
   306  			{
   307  				char[] fname = // -r
   308  					(recursive ? hdr.fname[hdr.dir_name_len..length]
   309  					           : hdr.fname);
   310  				foreach( char[] w ; paths )
   311  					if( wild_match( w, fname ) )
   312  						goto ok;
   313  				return BgaAnswer.SkipIt;
   314  			ok:;
   315  			}
   316  			// -a
   317  			if( !all_attrs && (hdr.attrib&6) )
   318  				return BgaAnswer.SkipIt;
   319  			// dialog
   320  			if( dlg )
   321  				if( dlg.closed )
   322  					return BgaAnswer.Abort;
   323  				else
   324  					dlg.set_filename( hdr.fname[hdr.dir_name_len .. length] );
   325  			// -j
   326  			if( ignore_dir )
   327  				hdr.fname = hdr.fname[hdr.dir_name_len .. length];
   328  			// sanitize
   329  			if( sanitize_path )
   330  				hdr.fname = check_path(hdr.fname);
   331  			// base_dir
   332  			hdr.fname = base_dir ~ hdr.fname;
   333  			// -o
   334  			if( !force_overwrite )
   335  				try {
   336  				if( std.file.exists(hdr.fname) && std.file.isfile(hdr.fname) )
   337  					// -n
   338  					if( newfile_only )
   339  					{
   340  						if( newer_than(hdr.date,hdr.time,hdr.fname) )
   341  							return BgaAnswer.SkipIt;
   342  					}
   343  					else
   344  					{
   345  						int r = MessageBox( dlg?dlg.hwnd:wnd,
   346  							toStringz("Overwrite "~hdr.fname~" ?"),
   347  							"QBga32.dll", MB_YESNOCANCEL );
   348  						if( r == IDNO )     return BgaAnswer.SkipIt;
   349  						if( r == IDCANCEL ) return BgaAnswer.Abort;
   350  					}
   351  				} catch {}
   352  
   353  			cur_hdr = hdr;
   354  			if( !do_ownerwnd_proc( OP_FILE_BEGIN, &cur_hdr, 0, src_fname ) )
   355  				return BgaAnswer.Abort;
   356  			return BgaAnswer.MeltIt;
   357  		}
   358  
   359  		BgaAnswer progress_handler( int cur, int max )
   360  		{
   361  			process_messages();
   362  			if( dlg )
   363  				if( dlg.closed )
   364  					return BgaAnswer.Abort;
   365  				else
   366  					dlg.set_pos( cast(real)(cur)/max );
   367  			if( !do_ownerwnd_proc( OP_FILE_MIDDLE, &cur_hdr, cur, src_fname ) )
   368  				return BgaAnswer.Abort;
   369  			return BgaAnswer.MeltIt;
   370  		}
   371  
   372  		(new BgaMelter(arc_name)).start(&handler,&progress_handler);
   373  	}
   374  	catch( BgaMelterError e )
   375  	{
   376  		return e.errcode;
   377  	}
   378  	finally
   379  	{
   380  		do_ownerwnd_proc( OP_ARC_END, null, 0, arc_name );
   381  		if( dlg )
   382  			dlg.close();
   383  	}
   384  	return 0;
   385  }
   386  
   387  //----------------------------------------------------------------
   388  // 統合アーカイバAPI:実装( SetOwnerWindow )
   389  //----------------------------------------------------------------
   390  
   391  align(1) struct EXTRACTINGINFO
   392  {
   393  	DWORD   dwFileSize;
   394  	DWORD   dwWriteSize;
   395  	char    szSourceFileName[512 + 1];
   396  	char    dummy1[3];
   397  	char    szDestFileName[512 + 1];
   398  	char    dummy[3];
   399  }
   400  align(1) struct EXTRACTINGINFOEX
   401  {
   402  	EXTRACTINGINFO exinfo;
   403  	DWORD dwCompressedSize;
   404  	DWORD dwCRC;
   405  	UINT  uOSType;
   406  	WORD  wRatio;
   407  	WORD  wDate;
   408  	WORD  wTime;
   409  	char  szAttribute[8];
   410  	char  szMode[8];
   411  }
   412  
   413  HWND g_owner_window;
   414  extern(Windows) BOOL function(HWND,UINT,UINT,EXTRACTINGINFOEX*) g_handler;
   415  extern(Windows) BOOL noex_handler( HWND w,UINT m,UINT s, EXTRACTINGINFOEX* e )
   416  {
   417  	return !SendMessage( w, m, s, cast(LPARAM) &e.exinfo );
   418  }
   419  
   420  void BgaSetOwnerWindow_impl( HWND wnd )
   421  {
   422  	g_owner_window = wnd;
   423  	g_handler      = &noex_handler;
   424  }
   425  
   426  void BgaClearOwnerWindow_impl()
   427  {
   428  	g_owner_window = null;
   429  	g_handler      = null;
   430  }
   431  
   432  void BgaSetOwnerWindowEx_impl( HWND wnd, ARCHIVERPROC* proc )
   433  {
   434  	g_owner_window = wnd;
   435  	g_handler      = *proc;
   436  }
   437  
   438  enum { OP_FILE_BEGIN, OP_FILE_MIDDLE, OP_ARC_END, OP_ARC_BEGIN }
   439  bool do_ownerwnd_proc( UINT uState, BgaHeader* hdr, int cur, char[] src_fname )
   440  {
   441  	if( g_handler is null )
   442  		return true;
   443  	EXTRACTINGINFOEX ex;
   444  	if( uState == OP_ARC_BEGIN || uState == OP_ARC_END )
   445  	{
   446  		lstrcpyn( ex.exinfo.szSourceFileName, toStringz(src_fname), 512 );
   447  	}
   448  	else
   449  	{
   450  		ex.exinfo.dwFileSize  = hdr.original_size;
   451  		ex.exinfo.dwWriteSize = cur;
   452  		lstrcpyn( ex.exinfo.szSourceFileName, toStringz(src_fname), 512 );
   453  		lstrcpyn( ex.exinfo.szDestFileName, toStringz(hdr.fname), 512 );
   454  		ex.dwCompressedSize = hdr.compressed_size;
   455  		ex.wRatio = cast(int)( (cast(real)hdr.compressed_size)/hdr.original_size*1000 );
   456  		ex.wDate  = hdr.date;
   457  		ex.wTime  = hdr.time;
   458  		ex.szAttribute[0] = (hdr.attrib&32 ? 'A': '-');
   459  		ex.szAttribute[1] = (hdr.attrib&1  ? 'R': '-');
   460  		ex.szAttribute[2] = (hdr.attrib&2  ? 'H': '-');
   461  		ex.szAttribute[3] = (hdr.attrib&4  ? 'S': '-');
   462  		ex.szAttribute[4] = (hdr.attrib&16 ? 'D': '-');
   463  		ex.szAttribute[5] = '\0';
   464  		if( hdr.method[0]=='G' )
   465  			lstrcpy(ex.szMode,"-gzip-");
   466  		else
   467  			lstrcpy(ex.szMode,"-bzip2-");
   468  	}
   469  
   470  	return false != g_handler( g_owner_window, WM_ARCEXTRACT, uState, &ex );
   471  }
   472  
   473  //----------------------------------------------------------------
   474  // パス検査系
   475  //----------------------------------------------------------------
   476  
   477  alias std.c.windows.windows.IsDBCSLeadByte isDL;
   478  char[] replace_yen( char[] s )
   479  {
   480  	char[] ans;
   481  	int j=0;
   482  	for(int i=0; i!=s.length; i=i+(isDL(s[i])?2:1))
   483  		if( s[i] == '\\' )
   484  			ans~=s[j .. i], ans~='/', j=i+1;
   485  	ans ~= s[j .. length];
   486  	return ans;
   487  }
   488  
   489  bool wild_match( char[] wild, char[] name )
   490  {
   491  	bool wild_match_nopath( char[] w, char[] s )
   492  	{
   493  		char[] advance( char[] s )
   494  		{
   495  			return s[(IsDBCSLeadByte(s[0])?2:1) .. length];
   496  		}
   497  
   498  		while( w.length>0 )
   499  			switch( w[0] )
   500  			{
   501  			case '?':
   502  				if( s.length==0 )
   503  					return false;
   504  				w = advance(w);
   505  				s = advance(s);
   506  				break;
   507  			case '*':
   508  				if( s.length==0 )
   509  					return false;
   510  				w = advance(w);
   511  				if( w.length == 0 )
   512  					return true;
   513  				for( ; s.length!=0; s=advance(s) )
   514  					if( wild_match_nopath(w,s) )
   515  						return true;
   516  				return false;
   517  			default:
   518  				if( s.length==0 )
   519  					return false;
   520  				if( isDL(w[0]) )
   521  				  { if( w[0..2] != s[0..2] ) return false; }
   522  				else
   523  				  { if( w[0]    != s[0]    ) return false; }
   524  				w = advance(w);
   525  				s = advance(s);
   526  				break;
   527  			}
   528  		return s.length==0;
   529  	}
   530  
   531  	if( wild=="" || wild=="*.*" || wild=="*" || wild=="**" )
   532  		return true;
   533  
   534  	char[][] wilds = split( replace_yen( tolower(wild) ), "/" );
   535  	char[][] names = split( replace_yen( tolower(name) ), "/" );
   536  
   537  	if( wilds.length != names.length )
   538  		return false;
   539  	for(int i=0; i!=wilds.length; ++i)
   540  		if( wilds[i]!="*.*" && wilds[i]!="*" && wilds[i]!="**" )
   541  			if( !wild_match_nopath( wilds[i], names[i] ) )
   542  				return false;
   543  	return true;
   544  }
   545  
   546  char[] check_path( char[] path )
   547  {
   548  	// C:\ ==> C_\
   549  	if( path.length>=2 && path[1]==':' )
   550  		path = path.dup, path[1] = '_';
   551  
   552  	// \\hoge ==> hoge
   553  	// /hoge  ==> hoge
   554  	while( path.length>0 && (path[0]=='\\'||path[0]=='/') )
   555  		path = path[1..length];
   556  
   557  	// .. ==> __
   558  	char[][] paths = split( replace_yen(path), "/" );
   559  	L1:
   560  	foreach( inout char[] pc ; paths )
   561  		if( pc.length >= 2 )
   562  		{
   563  			foreach( char c ; pc )
   564  				if( c != '.' )
   565  					continue L1;
   566  			pc = replace( pc, ".", "_" );
   567  		}
   568  
   569  	return join( paths, "\\" );
   570  }
   571  
   572  
   573  
   574  //----------------------------------------------------------------
   575  // 簡易テスト@もっとマジメに書かなきゃ…
   576  //----------------------------------------------------------------
   577  
   578  unittest
   579  {
   580  	assert( check_path(`\\\\hoge\fuga`)==`hoge\fuga` );
   581  	assert( check_path(`/usr/local/`)==`usr\local\` );
   582  	assert( check_path(`..\abc def\...\.\g`)==`__\abc def\___\.\g` );
   583  	assert( wild_match(`a/b/c`,`A\b\C`) );
   584  	assert( wild_match(`a/*.*/a?x`,`A\hoge\Afx`) );
   585  	assert( Bga_impl(null,"a hoge") < 0 );
   586  }