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 }