From 619cdeee734e3f88ae71e72acd7c870b882a546b Mon Sep 17 00:00:00 2001 From: Robert McGovern Date: Tue, 14 Jul 2020 23:40:26 +0100 Subject: [PATCH] WIP: Updated project to build for Apple Silicon, and updated included version of libunrar. haven't looked into how to compile the lib to include both platforms. (Commented out Growl for the moment as Framework doesn\t have aarm symbols yet) --- English.lproj/MainMenu.xib | 4116 ----------------- .../Versions/A/Headers/Growl.h | 6 +- PasswordView.xib | 759 +-- QuietUnrar-Info.plist | 2 +- QuietUnrar.xcodeproj/project.pbxproj | 117 +- QuietUnrarAppDelegate.m | 20 +- {English.lproj => en.lproj}/InfoPlist.strings | 0 en.lproj/MainMenu.xib | 669 +++ libunrar/UnRAR.vcxproj | 279 ++ libunrar/UnRARDll.vcxproj | 420 ++ libunrar/acknow.txt | 92 + libunrar/arccmt.cpp | 246 +- libunrar/archive.cpp | 341 +- libunrar/archive.hpp | 142 +- libunrar/arcread.cpp | 1687 +++++-- libunrar/array.hpp | 99 +- libunrar/beosea.cpp | 113 - libunrar/blake2s.cpp | 183 + libunrar/blake2s.hpp | 102 + libunrar/blake2s_sse.cpp | 129 + libunrar/blake2sp.cpp | 153 + libunrar/cmddata.cpp | 1578 +++---- libunrar/cmddata.hpp | 69 +- libunrar/cmdfilter.cpp | 352 ++ libunrar/cmdmix.cpp | 118 + libunrar/coder.hpp | 1 - libunrar/compress.hpp | 67 +- libunrar/consio.cpp | 455 +- libunrar/consio.hpp | 49 +- libunrar/crc.cpp | 111 +- libunrar/crc.hpp | 13 +- libunrar/crypt.cpp | 481 +- libunrar/crypt.hpp | 127 +- libunrar/crypt1.cpp | 79 + libunrar/crypt2.cpp | 133 + libunrar/crypt3.cpp | 68 + libunrar/crypt5.cpp | 233 + libunrar/dll.cpp | 494 +- libunrar/dll.def | 1 + libunrar/dll.hpp | 73 +- libunrar/dll.rc | 28 + libunrar/dll_nocrypt.def | 13 + libunrar/encname.cpp | 24 +- libunrar/encname.hpp | 2 +- libunrar/errhnd.cpp | 384 +- libunrar/errhnd.hpp | 97 +- libunrar/extinfo.cpp | 182 +- libunrar/extinfo.hpp | 19 +- libunrar/extract.cpp | 1466 +++--- libunrar/extract.hpp | 49 +- libunrar/filcreat.cpp | 308 +- libunrar/filcreat.hpp | 13 +- libunrar/file.cpp | 615 +-- libunrar/file.hpp | 128 +- libunrar/filefn.cpp | 681 ++- libunrar/filefn.hpp | 56 +- libunrar/filestr.cpp | 289 +- libunrar/filestr.hpp | 16 +- libunrar/find.cpp | 283 +- libunrar/find.hpp | 28 +- libunrar/getbits.cpp | 38 +- libunrar/getbits.hpp | 25 +- libunrar/global.cpp | 3 + libunrar/hardlinks.cpp | 39 + libunrar/hash.cpp | 135 + libunrar/hash.hpp | 52 + libunrar/headers.cpp | 61 + libunrar/headers.hpp | 322 +- libunrar/headers5.hpp | 100 + libunrar/isnt.cpp | 15 +- libunrar/isnt.hpp | 9 +- libunrar/libunrar.xcodeproj/project.pbxproj | 551 +++ libunrar/license.txt | 18 +- libunrar/list.cpp | 632 +-- libunrar/loclang.hpp | 736 +-- libunrar/log.cpp | 33 +- libunrar/log.hpp | 12 +- libunrar/{makefile.unix => makefile} | 62 +- libunrar/makefile.bcc | 501 -- libunrar/makefile.cygmin | 54 - libunrar/makefile.dj | 50 - libunrar/makefile.dmc | 54 - libunrar/makefile.msc | 564 --- libunrar/match.cpp | 229 +- libunrar/match.hpp | 33 +- libunrar/model.cpp | 107 +- libunrar/model.hpp | 76 +- libunrar/msc.dep | 2390 ---------- libunrar/options.cpp | 15 +- libunrar/options.hpp | 140 +- libunrar/os.hpp | 220 +- libunrar/os2ea.cpp | 94 - libunrar/patch-makefile.unix | 44 - libunrar/pathfn.cpp | 1033 +++-- libunrar/pathfn.hpp | 97 +- libunrar/qopen.cpp | 300 ++ libunrar/qopen.hpp | 61 + libunrar/rar.cpp | 131 +- libunrar/rar.hpp | 41 +- libunrar/rardefs.hpp | 27 +- libunrar/raros.hpp | 19 +- libunrar/rarpch.cpp | 2 + libunrar/rartypes.hpp | 47 +- libunrar/rarvm.cpp | 967 +--- libunrar/rarvm.hpp | 85 +- libunrar/rarvmtbl.cpp | 53 - libunrar/rawint.hpp | 122 + libunrar/rawread.cpp | 167 +- libunrar/rawread.hpp | 33 +- libunrar/rdwrfn.cpp | 170 +- libunrar/rdwrfn.hpp | 32 +- libunrar/readme.txt | 35 +- libunrar/recvol.cpp | 445 +- libunrar/recvol.hpp | 82 +- libunrar/recvol3.cpp | 544 +++ libunrar/recvol5.cpp | 523 +++ libunrar/resource.cpp | 18 +- libunrar/resource.hpp | 11 +- libunrar/rijndael.cpp | 367 +- libunrar/rijndael.hpp | 27 +- libunrar/rs.cpp | 93 +- libunrar/rs.hpp | 18 +- libunrar/rs16.cpp | 419 ++ libunrar/rs16.hpp | 44 + libunrar/savepos.cpp | 15 - libunrar/savepos.hpp | 31 +- libunrar/scantree.cpp | 439 +- libunrar/scantree.hpp | 42 +- libunrar/secpassword.cpp | 216 + libunrar/secpassword.hpp | 35 + libunrar/sha1.cpp | 335 +- libunrar/sha1.hpp | 14 +- libunrar/sha256.cpp | 148 + libunrar/sha256.hpp | 17 + libunrar/smallfn.cpp | 34 +- libunrar/strfn.cpp | 424 +- libunrar/strfn.hpp | 48 +- libunrar/strlist.cpp | 130 +- libunrar/strlist.hpp | 34 +- libunrar/suballoc.cpp | 37 +- libunrar/suballoc.hpp | 39 +- libunrar/system.cpp | 146 +- libunrar/system.hpp | 20 +- libunrar/threadmisc.cpp | 151 + libunrar/threadpool.cpp | 212 + libunrar/threadpool.hpp | 107 + libunrar/timefn.cpp | 427 +- libunrar/timefn.hpp | 59 +- libunrar/ui.cpp | 14 + libunrar/ui.hpp | 171 + libunrar/uicommon.cpp | 65 + libunrar/uiconsole.cpp | 425 ++ libunrar/uisilent.cpp | 69 + libunrar/ulinks.cpp | 119 +- libunrar/ulinks.hpp | 9 - libunrar/unicode.cpp | 671 +-- libunrar/unicode.hpp | 54 +- libunrar/unios2.cpp | 128 - libunrar/unpack.cpp | 1242 ++--- libunrar/unpack.hpp | 385 +- libunrar/unpack15.cpp | 139 +- libunrar/unpack20.cpp | 197 +- libunrar/unpack30.cpp | 765 +++ libunrar/unpack50.cpp | 687 +++ libunrar/unpack50frag.cpp | 103 + libunrar/unpack50mt.cpp | 655 +++ libunrar/unpackinline.cpp | 147 + libunrar/uowners.cpp | 111 +- libunrar/version.hpp | 10 +- libunrar/volume.cpp | 371 +- libunrar/volume.hpp | 3 +- libunrar/win32acl.cpp | 114 +- libunrar/win32lnk.cpp | 174 + libunrar/win32stm.cpp | 158 +- 174 files changed, 22452 insertions(+), 19979 deletions(-) delete mode 100644 English.lproj/MainMenu.xib rename {English.lproj => en.lproj}/InfoPlist.strings (100%) create mode 100644 en.lproj/MainMenu.xib create mode 100644 libunrar/UnRAR.vcxproj create mode 100644 libunrar/UnRARDll.vcxproj create mode 100644 libunrar/acknow.txt delete mode 100644 libunrar/beosea.cpp create mode 100644 libunrar/blake2s.cpp create mode 100644 libunrar/blake2s.hpp create mode 100644 libunrar/blake2s_sse.cpp create mode 100644 libunrar/blake2sp.cpp create mode 100644 libunrar/cmdfilter.cpp create mode 100644 libunrar/cmdmix.cpp create mode 100644 libunrar/crypt1.cpp create mode 100644 libunrar/crypt2.cpp create mode 100644 libunrar/crypt3.cpp create mode 100644 libunrar/crypt5.cpp create mode 100644 libunrar/dll.rc create mode 100644 libunrar/dll_nocrypt.def create mode 100644 libunrar/hardlinks.cpp create mode 100644 libunrar/hash.cpp create mode 100644 libunrar/hash.hpp create mode 100644 libunrar/headers.cpp create mode 100644 libunrar/headers5.hpp create mode 100644 libunrar/libunrar.xcodeproj/project.pbxproj rename libunrar/{makefile.unix => makefile} (69%) delete mode 100644 libunrar/makefile.bcc delete mode 100644 libunrar/makefile.cygmin delete mode 100644 libunrar/makefile.dj delete mode 100644 libunrar/makefile.dmc delete mode 100644 libunrar/makefile.msc delete mode 100644 libunrar/msc.dep delete mode 100644 libunrar/os2ea.cpp delete mode 100644 libunrar/patch-makefile.unix create mode 100644 libunrar/qopen.cpp create mode 100644 libunrar/qopen.hpp create mode 100644 libunrar/rarpch.cpp delete mode 100644 libunrar/rarvmtbl.cpp create mode 100644 libunrar/rawint.hpp create mode 100644 libunrar/recvol3.cpp create mode 100644 libunrar/recvol5.cpp create mode 100644 libunrar/rs16.cpp create mode 100644 libunrar/rs16.hpp delete mode 100644 libunrar/savepos.cpp create mode 100644 libunrar/secpassword.cpp create mode 100644 libunrar/secpassword.hpp create mode 100644 libunrar/sha256.cpp create mode 100644 libunrar/sha256.hpp create mode 100644 libunrar/threadmisc.cpp create mode 100644 libunrar/threadpool.cpp create mode 100644 libunrar/threadpool.hpp create mode 100644 libunrar/ui.cpp create mode 100644 libunrar/ui.hpp create mode 100644 libunrar/uicommon.cpp create mode 100644 libunrar/uiconsole.cpp create mode 100644 libunrar/uisilent.cpp delete mode 100644 libunrar/ulinks.hpp delete mode 100644 libunrar/unios2.cpp create mode 100644 libunrar/unpack30.cpp create mode 100644 libunrar/unpack50.cpp create mode 100644 libunrar/unpack50frag.cpp create mode 100644 libunrar/unpack50mt.cpp create mode 100644 libunrar/unpackinline.cpp create mode 100644 libunrar/win32lnk.cpp diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib deleted file mode 100644 index 9d75ca9..0000000 --- a/English.lproj/MainMenu.xib +++ /dev/null @@ -1,4116 +0,0 @@ - - - - 1060 - 10C540 - 740 - 1038.25 - 458.00 - - com.apple.InterfaceBuilder.CocoaPlugin - 740 - - - YES - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - NSApplication - - - FirstResponder - - - NSApplication - - - AMainMenu - - YES - - - QuietUnrar - - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - QuietUnrar - - YES - - - About QuietUnrar - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Preferences… - , - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Services - - 1048576 - 2147483647 - - - submenuAction: - - Services - - YES - - _NSServicesMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Hide QuietUnrar - h - 1048576 - 2147483647 - - - - - - Hide Others - h - 1572864 - 2147483647 - - - - - - Show All - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Quit QuietUnrar - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - - File - - 1048576 - 2147483647 - - - submenuAction: - - File - - YES - - - New - n - 1048576 - 2147483647 - - - - - - Open… - o - 1048576 - 2147483647 - - - - - - Open Recent - - 1048576 - 2147483647 - - - submenuAction: - - Open Recent - - YES - - - Clear Menu - - 1048576 - 2147483647 - - - - - _NSRecentDocumentsMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Close - w - 1048576 - 2147483647 - - - - - - Save - s - 1048576 - 2147483647 - - - - - - Save As… - S - 1179648 - 2147483647 - - - - - - Revert to Saved - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Page Setup... - P - 1179648 - 2147483647 - - - - - - - Print… - p - 1048576 - 2147483647 - - - - - - - - - Edit - - 1048576 - 2147483647 - - - submenuAction: - - Edit - - YES - - - Undo - z - 1048576 - 2147483647 - - - - - - Redo - Z - 1179648 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Cut - x - 1048576 - 2147483647 - - - - - - Copy - c - 1048576 - 2147483647 - - - - - - Paste - v - 1048576 - 2147483647 - - - - - - Paste and Match Style - V - 1572864 - 2147483647 - - - - - - Delete - - 1048576 - 2147483647 - - - - - - Select All - a - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Find - - 1048576 - 2147483647 - - - submenuAction: - - Find - - YES - - - Find… - f - 1048576 - 2147483647 - - - 1 - - - - Find Next - g - 1048576 - 2147483647 - - - 2 - - - - Find Previous - G - 1179648 - 2147483647 - - - 3 - - - - Use Selection for Find - e - 1048576 - 2147483647 - - - 7 - - - - Jump to Selection - j - 1048576 - 2147483647 - - - - - - - - - Spelling and Grammar - - 1048576 - 2147483647 - - - submenuAction: - - Spelling and Grammar - - YES - - - Show Spelling and Grammar - : - 1048576 - 2147483647 - - - - - - Check Document Now - ; - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Check Spelling While Typing - - 1048576 - 2147483647 - - - - - - Check Grammar With Spelling - - 1048576 - 2147483647 - - - - - - Correct Spelling Automatically - - 2147483647 - - - - - - - - - Substitutions - - 1048576 - 2147483647 - - - submenuAction: - - Substitutions - - YES - - - Show Substitutions - - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Smart Copy/Paste - f - 1048576 - 2147483647 - - - 1 - - - - Smart Quotes - g - 1048576 - 2147483647 - - - 2 - - - - Smart Dashes - - 2147483647 - - - - - - Smart Links - G - 1179648 - 2147483647 - - - 3 - - - - Text Replacement - - 2147483647 - - - - - - - - - Transformations - - 2147483647 - - - submenuAction: - - Transformations - - YES - - - Make Upper Case - - 2147483647 - - - - - - Make Lower Case - - 2147483647 - - - - - - Capitalize - - 2147483647 - - - - - - - - - Speech - - 1048576 - 2147483647 - - - submenuAction: - - Speech - - YES - - - Start Speaking - - 1048576 - 2147483647 - - - - - - Stop Speaking - - 1048576 - 2147483647 - - - - - - - - - - - - Format - - 2147483647 - - - submenuAction: - - Format - - YES - - - Font - - 2147483647 - - - submenuAction: - - Font - - YES - - - Show Fonts - t - 1048576 - 2147483647 - - - - - - Bold - b - 1048576 - 2147483647 - - - 2 - - - - Italic - i - 1048576 - 2147483647 - - - 1 - - - - Underline - u - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Bigger - + - 1048576 - 2147483647 - - - 3 - - - - Smaller - - - 1048576 - 2147483647 - - - 4 - - - - YES - YES - - - 2147483647 - - - - - - Kern - - 2147483647 - - - submenuAction: - - Kern - - YES - - - Use Default - - 2147483647 - - - - - - Use None - - 2147483647 - - - - - - Tighten - - 2147483647 - - - - - - Loosen - - 2147483647 - - - - - - - - - Ligature - - 2147483647 - - - submenuAction: - - Ligature - - YES - - - Use Default - - 2147483647 - - - - - - Use None - - 2147483647 - - - - - - Use All - - 2147483647 - - - - - - - - - Baseline - - 2147483647 - - - submenuAction: - - Baseline - - YES - - - Use Default - - 2147483647 - - - - - - Superscript - - 2147483647 - - - - - - Subscript - - 2147483647 - - - - - - Raise - - 2147483647 - - - - - - Lower - - 2147483647 - - - - - - - - - YES - YES - - - 2147483647 - - - - - - Show Colors - C - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Copy Style - c - 1572864 - 2147483647 - - - - - - Paste Style - v - 1572864 - 2147483647 - - - - - _NSFontMenu - - - - - Text - - 2147483647 - - - submenuAction: - - Text - - YES - - - Align Left - { - 1048576 - 2147483647 - - - - - - Center - | - 1048576 - 2147483647 - - - - - - Justify - - 2147483647 - - - - - - Align Right - } - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Writing Direction - - 2147483647 - - - submenuAction: - - Writing Direction - - YES - - - YES - Paragraph - - 2147483647 - - - - - - CURlZmF1bHQ - - 2147483647 - - - - - - CUxlZnQgdG8gUmlnaHQ - - 2147483647 - - - - - - CVJpZ2h0IHRvIExlZnQ - - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - YES - Selection - - 2147483647 - - - - - - CURlZmF1bHQ - - 2147483647 - - - - - - CUxlZnQgdG8gUmlnaHQ - - 2147483647 - - - - - - CVJpZ2h0IHRvIExlZnQ - - 2147483647 - - - - - - - - - YES - YES - - - 2147483647 - - - - - - Show Ruler - - 2147483647 - - - - - - Copy Ruler - c - 1310720 - 2147483647 - - - - - - Paste Ruler - v - 1310720 - 2147483647 - - - - - - - - - - - - View - - 1048576 - 2147483647 - - - submenuAction: - - View - - YES - - - Show Toolbar - t - 1572864 - 2147483647 - - - - - - Customize Toolbar… - - 1048576 - 2147483647 - - - - - - - - - Window - - 1048576 - 2147483647 - - - submenuAction: - - Window - - YES - - - Minimize - m - 1048576 - 2147483647 - - - - - - Zoom - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Bring All to Front - - 1048576 - 2147483647 - - - - - _NSWindowsMenu - - - - - Help - - 2147483647 - - - submenuAction: - - Help - - YES - - - QuietUnrar Help - ? - 1048576 - 2147483647 - - - - - _NSHelpMenu - - - - _NSMainMenu - - - 15 - 2 - {{335, 390}, {480, 360}} - 1954021376 - QuietUnrar - NSWindow - - {1.79769e+308, 1.79769e+308} - - - 256 - {480, 360} - - {{0, 0}, {1920, 1178}} - {1.79769e+308, 1.79769e+308} - - - QuietUnrarAppDelegate - - - NSFontManager - - - - - YES - - - performMiniaturize: - - - - 37 - - - - arrangeInFront: - - - - 39 - - - - print: - - - - 86 - - - - runPageLayout: - - - - 87 - - - - clearRecentDocuments: - - - - 127 - - - - orderFrontStandardAboutPanel: - - - - 142 - - - - performClose: - - - - 193 - - - - toggleContinuousSpellChecking: - - - - 222 - - - - undo: - - - - 223 - - - - copy: - - - - 224 - - - - checkSpelling: - - - - 225 - - - - paste: - - - - 226 - - - - stopSpeaking: - - - - 227 - - - - cut: - - - - 228 - - - - showGuessPanel: - - - - 230 - - - - redo: - - - - 231 - - - - selectAll: - - - - 232 - - - - startSpeaking: - - - - 233 - - - - delete: - - - - 235 - - - - performZoom: - - - - 240 - - - - performFindPanelAction: - - - - 241 - - - - centerSelectionInVisibleArea: - - - - 245 - - - - toggleGrammarChecking: - - - - 347 - - - - toggleSmartInsertDelete: - - - - 355 - - - - toggleAutomaticQuoteSubstitution: - - - - 356 - - - - toggleAutomaticLinkDetection: - - - - 357 - - - - saveDocument: - - - - 362 - - - - saveDocumentAs: - - - - 363 - - - - revertDocumentToSaved: - - - - 364 - - - - runToolbarCustomizationPalette: - - - - 365 - - - - toggleToolbarShown: - - - - 366 - - - - hide: - - - - 367 - - - - hideOtherApplications: - - - - 368 - - - - unhideAllApplications: - - - - 370 - - - - newDocument: - - - - 373 - - - - openDocument: - - - - 374 - - - - addFontTrait: - - - - 421 - - - - addFontTrait: - - - - 422 - - - - modifyFont: - - - - 423 - - - - orderFrontFontPanel: - - - - 424 - - - - modifyFont: - - - - 425 - - - - raiseBaseline: - - - - 426 - - - - lowerBaseline: - - - - 427 - - - - copyFont: - - - - 428 - - - - subscript: - - - - 429 - - - - superscript: - - - - 430 - - - - tightenKerning: - - - - 431 - - - - underline: - - - - 432 - - - - orderFrontColorPanel: - - - - 433 - - - - useAllLigatures: - - - - 434 - - - - loosenKerning: - - - - 435 - - - - pasteFont: - - - - 436 - - - - unscript: - - - - 437 - - - - useStandardKerning: - - - - 438 - - - - useStandardLigatures: - - - - 439 - - - - turnOffLigatures: - - - - 440 - - - - turnOffKerning: - - - - 441 - - - - terminate: - - - - 449 - - - - toggleAutomaticSpellingCorrection: - - - - 456 - - - - orderFrontSubstitutionsPanel: - - - - 458 - - - - toggleAutomaticDashSubstitution: - - - - 461 - - - - toggleAutomaticTextReplacement: - - - - 463 - - - - uppercaseWord: - - - - 464 - - - - capitalizeWord: - - - - 467 - - - - lowercaseWord: - - - - 468 - - - - pasteAsPlainText: - - - - 486 - - - - performFindPanelAction: - - - - 487 - - - - performFindPanelAction: - - - - 488 - - - - performFindPanelAction: - - - - 489 - - - - showHelp: - - - - 493 - - - - delegate - - - - 495 - - - - alignCenter: - - - - 518 - - - - pasteRuler: - - - - 519 - - - - toggleRuler: - - - - 520 - - - - alignRight: - - - - 521 - - - - copyRuler: - - - - 522 - - - - alignJustified: - - - - 523 - - - - alignLeft: - - - - 524 - - - - makeBaseWritingDirectionNatural: - - - - 525 - - - - makeBaseWritingDirectionLeftToRight: - - - - 526 - - - - makeBaseWritingDirectionRightToLeft: - - - - 527 - - - - makeTextWritingDirectionNatural: - - - - 528 - - - - makeTextWritingDirectionLeftToRight: - - - - 529 - - - - makeTextWritingDirectionRightToLeft: - - - - 530 - - - - window - - - - 532 - - - - - YES - - 0 - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 29 - - - YES - - - - - - - - - - - - 19 - - - YES - - - - - - 56 - - - YES - - - - - - 217 - - - YES - - - - - - 83 - - - YES - - - - - - 81 - - - YES - - - - - - - - - - - - - - - - 75 - - - - - 80 - - - - - 78 - - - - - 72 - - - - - 82 - - - - - 124 - - - YES - - - - - - 77 - - - - - 73 - - - - - 79 - - - - - 112 - - - - - 74 - - - - - 125 - - - YES - - - - - - 126 - - - - - 205 - - - YES - - - - - - - - - - - - - - - - - - - - 202 - - - - - 198 - - - - - 207 - - - - - 214 - - - - - 199 - - - - - 203 - - - - - 197 - - - - - 206 - - - - - 215 - - - - - 218 - - - YES - - - - - - 216 - - - YES - - - - - - 200 - - - YES - - - - - - - - - - - 219 - - - - - 201 - - - - - 204 - - - - - 220 - - - YES - - - - - - - - - - 213 - - - - - 210 - - - - - 221 - - - - - 208 - - - - - 209 - - - - - 57 - - - YES - - - - - - - - - - - - - - - - 58 - - - - - 134 - - - - - 150 - - - - - 136 - - - - - 144 - - - - - 129 - - - - - 143 - - - - - 236 - - - - - 131 - - - YES - - - - - - 149 - - - - - 145 - - - - - 130 - - - - - 24 - - - YES - - - - - - - - - 92 - - - - - 5 - - - - - 239 - - - - - 23 - - - - - 295 - - - YES - - - - - - 296 - - - YES - - - - - - - 297 - - - - - 298 - - - - - 211 - - - YES - - - - - - 212 - - - YES - - - - - - - 195 - - - - - 196 - - - - - 346 - - - - - 348 - - - YES - - - - - - 349 - - - YES - - - - - - - - - - - - 350 - - - - - 351 - - - - - 354 - - - - - 371 - - - YES - - - - - - 372 - - - - - 375 - - - YES - - - - - - 376 - - - YES - - - - - - - 377 - - - YES - - - - - - 388 - - - YES - - - - - - - - - - - - - - - - - - - - - 389 - - - - - 390 - - - - - 391 - - - - - 392 - - - - - 393 - - - - - 394 - - - - - 395 - - - - - 396 - - - - - 397 - - - YES - - - - - - 398 - - - YES - - - - - - 399 - - - YES - - - - - - 400 - - - - - 401 - - - - - 402 - - - - - 403 - - - - - 404 - - - - - 405 - - - YES - - - - - - - - - - 406 - - - - - 407 - - - - - 408 - - - - - 409 - - - - - 410 - - - - - 411 - - - YES - - - - - - - - 412 - - - - - 413 - - - - - 414 - - - - - 415 - - - YES - - - - - - - - - 416 - - - - - 417 - - - - - 418 - - - - - 419 - - - - - 420 - - - - - 450 - - - YES - - - - - - 451 - - - YES - - - - - - - - 452 - - - - - 453 - - - - - 454 - - - - - 457 - - - - - 459 - - - - - 460 - - - - - 462 - - - - - 465 - - - - - 466 - - - - - 485 - - - - - 490 - - - YES - - - - - - 491 - - - YES - - - - - - 492 - - - - - 494 - - - - - 496 - - - YES - - - - - - 497 - - - YES - - - - - - - - - - - - - - - 498 - - - - - 499 - - - - - 500 - - - - - 501 - - - - - 502 - - - - - 503 - - - YES - - - - - - 504 - - - - - 505 - - - - - 506 - - - - - 507 - - - - - 508 - - - YES - - - - - - - - - - - - - - 509 - - - - - 510 - - - - - 511 - - - - - 512 - - - - - 513 - - - - - 514 - - - - - 515 - - - - - 516 - - - - - 517 - - - - - - - YES - - YES - -3.IBPluginDependency - 112.IBPluginDependency - 112.ImportedFromIB2 - 124.IBPluginDependency - 124.ImportedFromIB2 - 125.IBPluginDependency - 125.ImportedFromIB2 - 125.editorWindowContentRectSynchronizationRect - 126.IBPluginDependency - 126.ImportedFromIB2 - 129.IBPluginDependency - 129.ImportedFromIB2 - 130.IBPluginDependency - 130.ImportedFromIB2 - 130.editorWindowContentRectSynchronizationRect - 131.IBPluginDependency - 131.ImportedFromIB2 - 134.IBPluginDependency - 134.ImportedFromIB2 - 136.IBPluginDependency - 136.ImportedFromIB2 - 143.IBPluginDependency - 143.ImportedFromIB2 - 144.IBPluginDependency - 144.ImportedFromIB2 - 145.IBPluginDependency - 145.ImportedFromIB2 - 149.IBPluginDependency - 149.ImportedFromIB2 - 150.IBPluginDependency - 150.ImportedFromIB2 - 19.IBPluginDependency - 19.ImportedFromIB2 - 195.IBPluginDependency - 195.ImportedFromIB2 - 196.IBPluginDependency - 196.ImportedFromIB2 - 197.IBPluginDependency - 197.ImportedFromIB2 - 198.IBPluginDependency - 198.ImportedFromIB2 - 199.IBPluginDependency - 199.ImportedFromIB2 - 200.IBEditorWindowLastContentRect - 200.IBPluginDependency - 200.ImportedFromIB2 - 200.editorWindowContentRectSynchronizationRect - 201.IBPluginDependency - 201.ImportedFromIB2 - 202.IBPluginDependency - 202.ImportedFromIB2 - 203.IBPluginDependency - 203.ImportedFromIB2 - 204.IBPluginDependency - 204.ImportedFromIB2 - 205.IBEditorWindowLastContentRect - 205.IBPluginDependency - 205.ImportedFromIB2 - 205.editorWindowContentRectSynchronizationRect - 206.IBPluginDependency - 206.ImportedFromIB2 - 207.IBPluginDependency - 207.ImportedFromIB2 - 208.IBPluginDependency - 208.ImportedFromIB2 - 209.IBPluginDependency - 209.ImportedFromIB2 - 210.IBPluginDependency - 210.ImportedFromIB2 - 211.IBPluginDependency - 211.ImportedFromIB2 - 212.IBPluginDependency - 212.ImportedFromIB2 - 212.editorWindowContentRectSynchronizationRect - 213.IBPluginDependency - 213.ImportedFromIB2 - 214.IBPluginDependency - 214.ImportedFromIB2 - 215.IBPluginDependency - 215.ImportedFromIB2 - 216.IBPluginDependency - 216.ImportedFromIB2 - 217.IBPluginDependency - 217.ImportedFromIB2 - 218.IBPluginDependency - 218.ImportedFromIB2 - 219.IBPluginDependency - 219.ImportedFromIB2 - 220.IBEditorWindowLastContentRect - 220.IBPluginDependency - 220.ImportedFromIB2 - 220.editorWindowContentRectSynchronizationRect - 221.IBPluginDependency - 221.ImportedFromIB2 - 23.IBPluginDependency - 23.ImportedFromIB2 - 236.IBPluginDependency - 236.ImportedFromIB2 - 239.IBPluginDependency - 239.ImportedFromIB2 - 24.IBEditorWindowLastContentRect - 24.IBPluginDependency - 24.ImportedFromIB2 - 24.editorWindowContentRectSynchronizationRect - 29.IBEditorWindowLastContentRect - 29.IBPluginDependency - 29.ImportedFromIB2 - 29.WindowOrigin - 29.editorWindowContentRectSynchronizationRect - 295.IBPluginDependency - 296.IBEditorWindowLastContentRect - 296.IBPluginDependency - 296.editorWindowContentRectSynchronizationRect - 297.IBPluginDependency - 298.IBPluginDependency - 346.IBPluginDependency - 346.ImportedFromIB2 - 348.IBPluginDependency - 348.ImportedFromIB2 - 349.IBEditorWindowLastContentRect - 349.IBPluginDependency - 349.ImportedFromIB2 - 349.editorWindowContentRectSynchronizationRect - 350.IBPluginDependency - 350.ImportedFromIB2 - 351.IBPluginDependency - 351.ImportedFromIB2 - 354.IBPluginDependency - 354.ImportedFromIB2 - 371.IBEditorWindowLastContentRect - 371.IBPluginDependency - 371.IBWindowTemplateEditedContentRect - 371.NSWindowTemplate.visibleAtLaunch - 371.editorWindowContentRectSynchronizationRect - 371.windowTemplate.maxSize - 372.IBPluginDependency - 375.IBPluginDependency - 376.IBEditorWindowLastContentRect - 376.IBPluginDependency - 377.IBPluginDependency - 388.IBEditorWindowLastContentRect - 388.IBPluginDependency - 389.IBPluginDependency - 390.IBPluginDependency - 391.IBPluginDependency - 392.IBPluginDependency - 393.IBPluginDependency - 394.IBPluginDependency - 395.IBPluginDependency - 396.IBPluginDependency - 397.IBPluginDependency - 398.IBPluginDependency - 399.IBPluginDependency - 400.IBPluginDependency - 401.IBPluginDependency - 402.IBPluginDependency - 403.IBPluginDependency - 404.IBPluginDependency - 405.IBPluginDependency - 406.IBPluginDependency - 407.IBPluginDependency - 408.IBPluginDependency - 409.IBPluginDependency - 410.IBPluginDependency - 411.IBPluginDependency - 412.IBPluginDependency - 413.IBPluginDependency - 414.IBPluginDependency - 415.IBPluginDependency - 416.IBPluginDependency - 417.IBPluginDependency - 418.IBPluginDependency - 419.IBPluginDependency - 450.IBPluginDependency - 451.IBEditorWindowLastContentRect - 451.IBPluginDependency - 452.IBPluginDependency - 453.IBPluginDependency - 454.IBPluginDependency - 457.IBPluginDependency - 459.IBPluginDependency - 460.IBPluginDependency - 462.IBPluginDependency - 465.IBPluginDependency - 466.IBPluginDependency - 485.IBPluginDependency - 490.IBPluginDependency - 491.IBEditorWindowLastContentRect - 491.IBPluginDependency - 492.IBPluginDependency - 496.IBPluginDependency - 497.IBEditorWindowLastContentRect - 497.IBPluginDependency - 498.IBPluginDependency - 499.IBPluginDependency - 5.IBPluginDependency - 5.ImportedFromIB2 - 500.IBPluginDependency - 501.IBPluginDependency - 502.IBPluginDependency - 503.IBPluginDependency - 504.IBPluginDependency - 505.IBPluginDependency - 506.IBPluginDependency - 507.IBPluginDependency - 508.IBEditorWindowLastContentRect - 508.IBPluginDependency - 509.IBPluginDependency - 510.IBPluginDependency - 511.IBPluginDependency - 512.IBPluginDependency - 513.IBPluginDependency - 514.IBPluginDependency - 515.IBPluginDependency - 516.IBPluginDependency - 517.IBPluginDependency - 56.IBPluginDependency - 56.ImportedFromIB2 - 57.IBEditorWindowLastContentRect - 57.IBPluginDependency - 57.ImportedFromIB2 - 57.editorWindowContentRectSynchronizationRect - 58.IBPluginDependency - 58.ImportedFromIB2 - 72.IBPluginDependency - 72.ImportedFromIB2 - 73.IBPluginDependency - 73.ImportedFromIB2 - 74.IBPluginDependency - 74.ImportedFromIB2 - 75.IBPluginDependency - 75.ImportedFromIB2 - 77.IBPluginDependency - 77.ImportedFromIB2 - 78.IBPluginDependency - 78.ImportedFromIB2 - 79.IBPluginDependency - 79.ImportedFromIB2 - 80.IBPluginDependency - 80.ImportedFromIB2 - 81.IBEditorWindowLastContentRect - 81.IBPluginDependency - 81.ImportedFromIB2 - 81.editorWindowContentRectSynchronizationRect - 82.IBPluginDependency - 82.ImportedFromIB2 - 83.IBPluginDependency - 83.ImportedFromIB2 - 92.IBPluginDependency - 92.ImportedFromIB2 - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{522, 812}, {146, 23}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{436, 809}, {64, 6}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{753, 187}, {275, 113}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {275, 83}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{547, 180}, {254, 283}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{187, 434}, {243, 243}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {167, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{753, 217}, {238, 103}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {241, 103}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{654, 239}, {194, 73}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{525, 802}, {197, 73}} - {{380, 736}, {444, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - - {74, 862} - {{6, 978}, {478, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - {{604, 269}, {231, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - {{475, 832}, {234, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{746, 287}, {220, 133}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{608, 612}, {215, 63}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{380, 396}, {480, 360}} - com.apple.InterfaceBuilder.CocoaPlugin - {{380, 396}, {480, 360}} - - {{33, 99}, {480, 360}} - {3.40282e+38, 3.40282e+38} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{591, 420}, {83, 43}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{523, 2}, {178, 283}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{753, 197}, {170, 63}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{725, 289}, {246, 23}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{674, 260}, {204, 183}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{878, 180}, {164, 173}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - {{286, 129}, {275, 183}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{23, 794}, {245, 183}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{452, 109}, {196, 203}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{145, 474}, {199, 203}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - - - - YES - - - YES - - - - - YES - - - YES - - - - 532 - - - - YES - - QuietUnrarAppDelegate - NSObject - - window - NSWindow - - - IBProjectSource - QuietUnrarAppDelegate.h - - - - - YES - - NSApplication - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSApplication.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSApplicationScripting.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSColorPanel.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSHelpManager.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSPageLayout.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSUserInterfaceItemSearching.h - - - - NSBrowser - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSBrowser.h - - - - NSControl - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSControl.h - - - - NSDocument - NSObject - - YES - - YES - printDocument: - revertDocumentToSaved: - runPageLayout: - saveDocument: - saveDocumentAs: - saveDocumentTo: - - - YES - id - id - id - id - id - id - - - - IBFrameworkSource - AppKit.framework/Headers/NSDocument.h - - - - NSDocument - - IBFrameworkSource - AppKit.framework/Headers/NSDocumentScripting.h - - - - NSDocumentController - NSObject - - YES - - YES - clearRecentDocuments: - newDocument: - openDocument: - saveAllDocuments: - - - YES - id - id - id - id - - - - IBFrameworkSource - AppKit.framework/Headers/NSDocumentController.h - - - - NSFontManager - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSFontManager.h - - - - NSFormatter - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFormatter.h - - - - NSMatrix - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSMatrix.h - - - - NSMenu - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSMenu.h - - - - NSMenuItem - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSMenuItem.h - - - - NSMovieView - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSMovieView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSAccessibility.h - - - - NSObject - - - - NSObject - - - - NSObject - - - - NSObject - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSDictionaryController.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSDragging.h - - - - NSObject - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSFontPanel.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSKeyValueBinding.h - - - - NSObject - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSNibLoading.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSOutlineView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSPasteboard.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSSavePanel.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSTableView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSToolbarItem.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSView.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSClassDescription.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSError.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFileManager.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueObserving.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyedArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObject.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObjectScripting.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSPortCoder.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSRunLoop.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptClassDescription.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptObjectSpecifiers.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptWhoseTests.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSThread.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURL.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLConnection.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLDownload.h - - - - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSInterfaceStyle.h - - - - NSResponder - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSResponder.h - - - - NSTableView - NSControl - - - - NSText - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSText.h - - - - NSTextView - NSText - - IBFrameworkSource - AppKit.framework/Headers/NSTextView.h - - - - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSClipView.h - - - - NSView - - - - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSRulerView.h - - - - NSView - NSResponder - - - - NSWindow - - IBFrameworkSource - AppKit.framework/Headers/NSDrawer.h - - - - NSWindow - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSWindow.h - - - - NSWindow - - IBFrameworkSource - AppKit.framework/Headers/NSWindowScripting.h - - - - - 0 - - com.apple.InterfaceBuilder.CocoaPlugin.macosx - - - - com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 - - - YES - ../QuietUnrar.xcodeproj - 3 - - diff --git a/Frameworks/Growl.framework/Versions/A/Headers/Growl.h b/Frameworks/Growl.framework/Versions/A/Headers/Growl.h index e2a4425..72dfcdf 100644 --- a/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +++ b/Frameworks/Growl.framework/Versions/A/Headers/Growl.h @@ -1,6 +1,6 @@ -#include "GrowlDefines.h" +#include #ifdef __OBJC__ -# include "GrowlApplicationBridge.h" +# include #endif -#include "GrowlApplicationBridge-Carbon.h" +#include diff --git a/PasswordView.xib b/PasswordView.xib index e79c3c3..c8ff849 100644 --- a/PasswordView.xib +++ b/PasswordView.xib @@ -1,715 +1,46 @@ - - - 1060 - 10C540 - 740 - 1038.25 - 458.00 - - com.apple.InterfaceBuilder.CocoaPlugin - 740 - - - YES - - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - QuietUnrarAppDelegate - - - FirstResponder - - - NSApplication - - - - 268 - - YES - - - 268 - {{66, 0}, {207, 22}} - - YES - - 343014976 - 272630848 - - - LucidaGrande - 13 - 1044 - - - YES - - 6 - System - textBackgroundColor - - 3 - MQA - - - - 6 - System - textColor - - 3 - MAA - - - - YES - NSAllRomanInputSourcesLocaleIdentifier - - - - - - 268 - {{-3, 2}, {64, 17}} - - YES - - 68288064 - 272630784 - Password - - - - 6 - System - controlColor - - 3 - MC42NjY2NjY2NjY3AA - - - - 6 - System - controlTextColor - - - - - - {273, 22} - - NSView - - - - - YES - - - passwordView - - - - 14 - - - - passwordField - - - - 15 - - - - - YES - - 0 - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 1 - - - YES - - - - - - - 4 - - - YES - - - - - - 5 - - - YES - - - - - - 8 - - - - - 9 - - - - - - - YES - - YES - 1.IBEditorWindowLastContentRect - 1.IBPluginDependency - 1.WindowOrigin - 1.editorWindowContentRectSynchronizationRect - 4.IBPluginDependency - 5.IBPluginDependency - 8.IBPluginDependency - 9.IBPluginDependency - - - YES - {{414, 718}, {273, 22}} - com.apple.InterfaceBuilder.CocoaPlugin - {628, 654} - {{217, 442}, {480, 272}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - YES - - - YES - - - - - YES - - - YES - - - - 15 - - - - YES - - QuietUnrarAppDelegate - NSObject - - YES - - YES - passwordField - passwordView - window - - - YES - NSSecureTextField - NSView - NSWindow - - - - IBProjectSource - QuietUnrarAppDelegate.h - - - - - YES - - NSActionCell - NSCell - - IBFrameworkSource - AppKit.framework/Headers/NSActionCell.h - - - - NSApplication - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSApplication.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSApplicationScripting.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSColorPanel.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSHelpManager.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSPageLayout.h - - - - NSApplication - - IBFrameworkSource - AppKit.framework/Headers/NSUserInterfaceItemSearching.h - - - - NSCell - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSCell.h - - - - NSControl - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSControl.h - - - - NSFormatter - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFormatter.h - - - - NSMenu - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSMenu.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSAccessibility.h - - - - NSObject - - - - NSObject - - - - NSObject - - - - NSObject - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSDictionaryController.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSDragging.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSFontManager.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSFontPanel.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSKeyValueBinding.h - - - - NSObject - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSNibLoading.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSOutlineView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSPasteboard.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSSavePanel.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSTableView.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSToolbarItem.h - - - - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSView.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSClassDescription.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSError.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFileManager.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueObserving.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyedArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObject.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObjectScripting.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSPortCoder.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSRunLoop.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptClassDescription.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptObjectSpecifiers.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSScriptWhoseTests.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSThread.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURL.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLConnection.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLDownload.h - - - - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSInterfaceStyle.h - - - - NSResponder - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSResponder.h - - - - NSSecureTextField - NSTextField - - IBFrameworkSource - AppKit.framework/Headers/NSSecureTextField.h - - - - NSSecureTextFieldCell - NSTextFieldCell - - - - NSTextField - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSTextField.h - - - - NSTextFieldCell - NSActionCell - - IBFrameworkSource - AppKit.framework/Headers/NSTextFieldCell.h - - - - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSClipView.h - - - - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSMenuItem.h - - - - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSRulerView.h - - - - NSView - NSResponder - - - - NSWindow - - IBFrameworkSource - AppKit.framework/Headers/NSDrawer.h - - - - NSWindow - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSWindow.h - - - - NSWindow - - IBFrameworkSource - AppKit.framework/Headers/NSWindowScripting.h - - - - - 0 - - com.apple.InterfaceBuilder.CocoaPlugin.macosx - - - - com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 - - - YES - QuietUnrar.xcodeproj - 3 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSAllRomanInputSourcesLocaleIdentifier + + + + + + + + diff --git a/QuietUnrar-Info.plist b/QuietUnrar-Info.plist index 6453810..b02e193 100644 --- a/QuietUnrar-Info.plist +++ b/QuietUnrar-Info.plist @@ -130,7 +130,7 @@ CFBundleIconFile CFBundleIdentifier - com.yourcompany.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/QuietUnrar.xcodeproj/project.pbxproj b/QuietUnrar.xcodeproj/project.pbxproj index 2be9f53..3e2dda8 100644 --- a/QuietUnrar.xcodeproj/project.pbxproj +++ b/QuietUnrar.xcodeproj/project.pbxproj @@ -11,10 +11,8 @@ 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; D488BC6810AF437B00B3451C /* libunrar.so in Frameworks */ = {isa = PBXBuildFile; fileRef = D488BC6710AF437B00B3451C /* libunrar.so */; }; - D488BCC110AF49C700B3451C /* libunrar.so in CopyFiles */ = {isa = PBXBuildFile; fileRef = D488BC6710AF437B00B3451C /* libunrar.so */; }; + D488BCC110AF49C700B3451C /* libunrar.so in CopyFiles */ = {isa = PBXBuildFile; fileRef = D488BC6710AF437B00B3451C /* libunrar.so */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; D488BE5510B05F3800B3451C /* PasswordView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D488BE5410B05F3800B3451C /* PasswordView.xib */; }; - D488BFB010B1F97F00B3451C /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D488BFAF10B1F97F00B3451C /* Growl.framework */; }; - D488BFBF10B1FBC700B3451C /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D488BFAF10B1F97F00B3451C /* Growl.framework */; }; D488BFCC10B1FD4500B3451C /* Growl Registration Ticket.growlRegDict in Resources */ = {isa = PBXBuildFile; fileRef = D488BFCB10B1FD4500B3451C /* Growl Registration Ticket.growlRegDict */; }; D4A49691105435BE00BE38AE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; D4A49692105435C100BE38AE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; @@ -38,7 +36,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - D488BFBF10B1FBC700B3451C /* Growl.framework in CopyFiles */, D488BCC110AF49C700B3451C /* libunrar.so in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; @@ -46,10 +43,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; - 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; 256AC3D80F4B6AC300CF3369 /* QuietUnrarAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuietUnrarAppDelegate.h; sourceTree = ""; }; 256AC3D90F4B6AC300CF3369 /* QuietUnrarAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QuietUnrarAppDelegate.m; sourceTree = ""; }; 256AC3F00F4B6AF500CF3369 /* QuietUnrar_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuietUnrar_Prefix.pch; sourceTree = ""; }; @@ -68,7 +63,6 @@ D4A495761054177300BE38AE /* archive.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = archive.hpp; path = libunrar/archive.hpp; sourceTree = ""; }; D4A495771054177300BE38AE /* arcread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = arcread.cpp; path = libunrar/arcread.cpp; sourceTree = ""; }; D4A495781054177300BE38AE /* array.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = array.hpp; path = libunrar/array.hpp; sourceTree = ""; }; - D4A495791054177300BE38AE /* beosea.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = beosea.cpp; path = libunrar/beosea.cpp; sourceTree = ""; }; D4A4957A1054177300BE38AE /* cmddata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cmddata.cpp; path = libunrar/cmddata.cpp; sourceTree = ""; }; D4A4957B1054177300BE38AE /* cmddata.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = cmddata.hpp; path = libunrar/cmddata.hpp; sourceTree = ""; }; D4A4957C1054177300BE38AE /* coder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = coder.cpp; path = libunrar/coder.cpp; sourceTree = ""; }; @@ -112,7 +106,6 @@ D4A495A21054177300BE38AE /* loclang.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = loclang.hpp; path = libunrar/loclang.hpp; sourceTree = ""; }; D4A495A31054177300BE38AE /* log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = log.cpp; path = libunrar/log.cpp; sourceTree = ""; }; D4A495A41054177300BE38AE /* log.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = log.hpp; path = libunrar/log.hpp; sourceTree = ""; }; - D4A495A51054177300BE38AE /* makefile.unix */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = makefile.unix; path = libunrar/makefile.unix; sourceTree = ""; }; D4A495A61054177300BE38AE /* match.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = match.cpp; path = libunrar/match.cpp; sourceTree = ""; }; D4A495A71054177300BE38AE /* match.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = match.hpp; path = libunrar/match.hpp; sourceTree = ""; }; D4A495A81054177300BE38AE /* model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = model.cpp; path = libunrar/model.cpp; sourceTree = ""; }; @@ -120,7 +113,6 @@ D4A495AA1054177300BE38AE /* options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options.cpp; path = libunrar/options.cpp; sourceTree = ""; }; D4A495AB1054177300BE38AE /* options.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options.hpp; path = libunrar/options.hpp; sourceTree = ""; }; D4A495AC1054177300BE38AE /* os.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = os.hpp; path = libunrar/os.hpp; sourceTree = ""; }; - D4A495AD1054177300BE38AE /* os2ea.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = os2ea.cpp; path = libunrar/os2ea.cpp; sourceTree = ""; }; D4A495AE1054177300BE38AE /* pathfn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pathfn.cpp; path = libunrar/pathfn.cpp; sourceTree = ""; }; D4A495AF1054177300BE38AE /* pathfn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = pathfn.hpp; path = libunrar/pathfn.hpp; sourceTree = ""; }; D4A495B01054177300BE38AE /* rar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rar.cpp; path = libunrar/rar.cpp; sourceTree = ""; }; @@ -131,7 +123,6 @@ D4A495B51054177300BE38AE /* rartypes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rartypes.hpp; path = libunrar/rartypes.hpp; sourceTree = ""; }; D4A495B61054177300BE38AE /* rarvm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rarvm.cpp; path = libunrar/rarvm.cpp; sourceTree = ""; }; D4A495B71054177300BE38AE /* rarvm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rarvm.hpp; path = libunrar/rarvm.hpp; sourceTree = ""; }; - D4A495B81054177300BE38AE /* rarvmtbl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rarvmtbl.cpp; path = libunrar/rarvmtbl.cpp; sourceTree = ""; }; D4A495B91054177300BE38AE /* rawread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rawread.cpp; path = libunrar/rawread.cpp; sourceTree = ""; }; D4A495BA1054177300BE38AE /* rawread.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rawread.hpp; path = libunrar/rawread.hpp; sourceTree = ""; }; D4A495BB1054177300BE38AE /* rdwrfn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rdwrfn.cpp; path = libunrar/rdwrfn.cpp; sourceTree = ""; }; @@ -144,7 +135,6 @@ D4A495C21054177300BE38AE /* rijndael.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rijndael.hpp; path = libunrar/rijndael.hpp; sourceTree = ""; }; D4A495C31054177300BE38AE /* rs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rs.cpp; path = libunrar/rs.cpp; sourceTree = ""; }; D4A495C41054177300BE38AE /* rs.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rs.hpp; path = libunrar/rs.hpp; sourceTree = ""; }; - D4A495C51054177300BE38AE /* savepos.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = savepos.cpp; path = libunrar/savepos.cpp; sourceTree = ""; }; D4A495C61054177300BE38AE /* savepos.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = savepos.hpp; path = libunrar/savepos.hpp; sourceTree = ""; }; D4A495C71054177300BE38AE /* scantree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = scantree.cpp; path = libunrar/scantree.cpp; sourceTree = ""; }; D4A495C81054177300BE38AE /* scantree.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = scantree.hpp; path = libunrar/scantree.hpp; sourceTree = ""; }; @@ -163,10 +153,8 @@ D4A495D51054177300BE38AE /* timefn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = timefn.cpp; path = libunrar/timefn.cpp; sourceTree = ""; }; D4A495D61054177300BE38AE /* timefn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = timefn.hpp; path = libunrar/timefn.hpp; sourceTree = ""; }; D4A495D71054177300BE38AE /* ulinks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ulinks.cpp; path = libunrar/ulinks.cpp; sourceTree = ""; }; - D4A495D81054177300BE38AE /* ulinks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ulinks.hpp; path = libunrar/ulinks.hpp; sourceTree = ""; }; D4A495D91054177300BE38AE /* unicode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unicode.cpp; path = libunrar/unicode.cpp; sourceTree = ""; }; D4A495DA1054177300BE38AE /* unicode.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = unicode.hpp; path = libunrar/unicode.hpp; sourceTree = ""; }; - D4A495DB1054177300BE38AE /* unios2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unios2.cpp; path = libunrar/unios2.cpp; sourceTree = ""; }; D4A495DC1054177300BE38AE /* unpack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unpack.cpp; path = libunrar/unpack.cpp; sourceTree = ""; }; D4A495DD1054177300BE38AE /* unpack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = unpack.hpp; path = libunrar/unpack.hpp; sourceTree = ""; }; D4A495DE1054177300BE38AE /* unpack15.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unpack15.cpp; path = libunrar/unpack15.cpp; sourceTree = ""; }; @@ -178,6 +166,8 @@ D4A495E41054177300BE38AE /* win32acl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = win32acl.cpp; path = libunrar/win32acl.cpp; sourceTree = ""; }; D4A495E51054177300BE38AE /* win32stm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = win32stm.cpp; path = libunrar/win32stm.cpp; sourceTree = ""; }; D4A96E2010545E9A0091ECB4 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + E296811D24BE4BCD00974229 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + E296811E24BE4BCD00974229 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -188,7 +178,6 @@ D488BC6810AF437B00B3451C /* libunrar.so in Frameworks */, 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, D4A96E2110545E9A0091ECB4 /* Carbon.framework in Frameworks */, - D488BFB010B1F97F00B3451C /* Growl.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -285,7 +274,6 @@ D4A495761054177300BE38AE /* archive.hpp */, D4A495771054177300BE38AE /* arcread.cpp */, D4A495781054177300BE38AE /* array.hpp */, - D4A495791054177300BE38AE /* beosea.cpp */, D4A4957A1054177300BE38AE /* cmddata.cpp */, D4A4957B1054177300BE38AE /* cmddata.hpp */, D4A4957C1054177300BE38AE /* coder.cpp */, @@ -329,7 +317,6 @@ D4A495A21054177300BE38AE /* loclang.hpp */, D4A495A31054177300BE38AE /* log.cpp */, D4A495A41054177300BE38AE /* log.hpp */, - D4A495A51054177300BE38AE /* makefile.unix */, D4A495A61054177300BE38AE /* match.cpp */, D4A495A71054177300BE38AE /* match.hpp */, D4A495A81054177300BE38AE /* model.cpp */, @@ -337,7 +324,6 @@ D4A495AA1054177300BE38AE /* options.cpp */, D4A495AB1054177300BE38AE /* options.hpp */, D4A495AC1054177300BE38AE /* os.hpp */, - D4A495AD1054177300BE38AE /* os2ea.cpp */, D4A495AE1054177300BE38AE /* pathfn.cpp */, D4A495AF1054177300BE38AE /* pathfn.hpp */, D4A495B01054177300BE38AE /* rar.cpp */, @@ -348,7 +334,6 @@ D4A495B51054177300BE38AE /* rartypes.hpp */, D4A495B61054177300BE38AE /* rarvm.cpp */, D4A495B71054177300BE38AE /* rarvm.hpp */, - D4A495B81054177300BE38AE /* rarvmtbl.cpp */, D4A495B91054177300BE38AE /* rawread.cpp */, D4A495BA1054177300BE38AE /* rawread.hpp */, D4A495BB1054177300BE38AE /* rdwrfn.cpp */, @@ -361,7 +346,6 @@ D4A495C21054177300BE38AE /* rijndael.hpp */, D4A495C31054177300BE38AE /* rs.cpp */, D4A495C41054177300BE38AE /* rs.hpp */, - D4A495C51054177300BE38AE /* savepos.cpp */, D4A495C61054177300BE38AE /* savepos.hpp */, D4A495C71054177300BE38AE /* scantree.cpp */, D4A495C81054177300BE38AE /* scantree.hpp */, @@ -380,10 +364,8 @@ D4A495D51054177300BE38AE /* timefn.cpp */, D4A495D61054177300BE38AE /* timefn.hpp */, D4A495D71054177300BE38AE /* ulinks.cpp */, - D4A495D81054177300BE38AE /* ulinks.hpp */, D4A495D91054177300BE38AE /* unicode.cpp */, D4A495DA1054177300BE38AE /* unicode.hpp */, - D4A495DB1054177300BE38AE /* unios2.cpp */, D4A495DC1054177300BE38AE /* unpack.cpp */, D4A495DD1054177300BE38AE /* unpack.hpp */, D4A495DE1054177300BE38AE /* unpack15.cpp */, @@ -403,7 +385,7 @@ /* Begin PBXLegacyTarget section */ D4A4962A105419AA00BE38AE /* libunrar */ = { isa = PBXLegacyTarget; - buildArgumentsString = "-f makefile.unix lib $(ACTION)"; + buildArgumentsString = "-f makefile lib"; buildConfigurationList = D4A4962D105419C800BE38AE /* Build configuration list for PBXLegacyTarget "libunrar" */; buildPhases = ( ); @@ -444,9 +426,17 @@ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; + attributes = { + LastUpgradeCheck = 1200; + }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "QuietUnrar" */; compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; hasScannedForEncodings = 1; + knownRegions = ( + en, + Base, + ); mainGroup = 29B97314FDCFA39411CA2CEA /* QuietUnrar */; projectDirPath = ""; projectRoot = ""; @@ -511,7 +501,7 @@ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( - 089C165DFE840E0CC02AAC07 /* English */, + E296811D24BE4BCD00974229 /* en */, ); name = InfoPlist.strings; sourceTree = ""; @@ -519,7 +509,7 @@ 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( - 1DDD58150DA1D0A300B32029 /* English */, + E296811E24BE4BCD00974229 /* en */, ); name = MainMenu.xib; sourceTree = ""; @@ -531,13 +521,14 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/Frameworks\"", ); GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -551,6 +542,7 @@ "$(inherited)", "\"$(SRCROOT)/libunrar\"", ); + PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = QuietUnrar; }; name = Debug; @@ -559,6 +551,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -576,6 +570,7 @@ "$(inherited)", "\"$(SRCROOT)/libunrar\"", ); + PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = QuietUnrar; }; name = Release; @@ -583,52 +578,102 @@ C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_ENABLE_OBJC_GC = required; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; ONLY_ACTIVE_ARCH = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - VALID_ARCHS = "i386 x86_64"; + SDKROOT = macosx; + VALID_ARCHS = "arm64 arm64e i386 x86_64"; }; name = Debug; }; C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_ENABLE_OBJC_GC = required; + GCC_NO_COMMON_BLOCKS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = macosx10.6; - VALID_ARCHS = "i386 x86_64"; + MACOSX_DEPLOYMENT_TARGET = 10.15; + SDKROOT = macosx; + VALID_ARCHS = "arm64 arm64e i386 x86_64"; }; name = Release; }; D4A4962B105419AA00BE38AE /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; PRODUCT_NAME = libunrar; + SDKROOT = macosx; }; name = Debug; }; D4A4962C105419AA00BE38AE /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; PRODUCT_NAME = libunrar; + SDKROOT = macosx; ZERO_LINK = NO; }; name = Release; diff --git a/QuietUnrarAppDelegate.m b/QuietUnrarAppDelegate.m index eb27302..bb0393d 100644 --- a/QuietUnrarAppDelegate.m +++ b/QuietUnrarAppDelegate.m @@ -7,7 +7,7 @@ // #import -#import +//#import #import "QuietUnrarAppDelegate.h" #import "libunrar/dll.hpp" @@ -91,16 +91,16 @@ int callbackFunction(UINT message, LPARAM userData, LPARAM parameterOne, LPARAM for (NSString * filename in arrayOfFilenames) { BOOL extracted = [self extractRarWith:filename]; if (extracted) { - [GrowlApplicationBridge setGrowlDelegate:@""]; + //[GrowlApplicationBridge setGrowlDelegate:@""]; - [GrowlApplicationBridge - notifyWithTitle:@"QuietUnrar: Extraction Complete" - description:[NSString stringWithFormat:@"The archive %@ was successfully extracted", filename] - notificationName:@"QuietUnrarExtractionComplete" - iconData:nil - priority:0 - isSticky:NO - clickContext:nil]; +// [GrowlApplicationBridge +// notifyWithTitle:@"QuietUnrar: Extraction Complete" +// description:[NSString stringWithFormat:@"The archive %@ was successfully extracted", filename] +// notificationName:@"QuietUnrarExtractionComplete" +// iconData:nil +// priority:0 +// isSticky:NO +// clickContext:nil]; } } } diff --git a/English.lproj/InfoPlist.strings b/en.lproj/InfoPlist.strings similarity index 100% rename from English.lproj/InfoPlist.strings rename to en.lproj/InfoPlist.strings diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib new file mode 100644 index 0000000..11bada9 --- /dev/null +++ b/en.lproj/MainMenu.xib @@ -0,0 +1,669 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libunrar/UnRAR.vcxproj b/libunrar/UnRAR.vcxproj new file mode 100644 index 0000000..512bcf1 --- /dev/null +++ b/libunrar/UnRAR.vcxproj @@ -0,0 +1,279 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {95CC809B-03FC-4EDB-BB20-FD07A698C05F} + UnRAR + Win32Proj + 8.1 + + + + Application + v140_xp + MultiByte + true + + + Application + v140_xp + MultiByte + + + Application + v140_xp + MultiByte + false + + + Application + v140_xp + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + build\unrar32\$(Configuration)\ + build\unrar32\$(Configuration)\obj\ + true + false + + + build\unrar64\$(Configuration)\ + build\unrar64\$(Configuration)\obj\ + true + false + + + build\unrar32\$(Configuration)\ + build\unrar32\$(Configuration)\obj\ + false + false + + + build\unrar64\$(Configuration)\ + build\unrar64\$(Configuration)\obj\ + false + false + + + + /MP %(AdditionalOptions) + Disabled + UNRAR;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NoExtensions + + + true + Console + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + Disabled + UNRAR;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + true + Console + MachineX64 + + + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + UNRAR;%(PreprocessorDefinitions) + false + MultiThreaded + Default + true + true + NoExtensions + Precise + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + + + true + Console + true + true + + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + MinSpace + true + Neither + true + false + UNRAR;%(PreprocessorDefinitions) + false + false + MultiThreaded + true + true + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + true + Console + true + true + + MachineX64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libunrar/UnRARDll.vcxproj b/libunrar/UnRARDll.vcxproj new file mode 100644 index 0000000..ec5c17b --- /dev/null +++ b/libunrar/UnRARDll.vcxproj @@ -0,0 +1,420 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + release_nocrypt + Win32 + + + release_nocrypt + x64 + + + Release + Win32 + + + Release + x64 + + + + UnRAR + {E815C46C-36C4-499F-BBC2-E772C6B17971} + UnRAR + Win32Proj + 8.1 + + + + DynamicLibrary + v140_xp + MultiByte + true + + + DynamicLibrary + v140_xp + MultiByte + true + + + DynamicLibrary + v140_xp + MultiByte + + + DynamicLibrary + v140_xp + MultiByte + false + + + DynamicLibrary + v140_xp + MultiByte + false + + + DynamicLibrary + v140_xp + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + build\unrardll32\$(Configuration)\ + build\unrardll32\$(Configuration)\obj\ + true + true + + + build\unrardll64\$(Configuration)\ + build\unrardll64\$(Configuration)\obj\ + true + true + + + build\unrardll32\$(Configuration)\ + build\unrardll32\$(Configuration)\obj\ + false + true + + + build\unrardll64\$(Configuration)\ + build\unrardll64\$(Configuration)\obj\ + false + true + + + build\unrardll32\$(Configuration)\ + build\unrardll32\$(Configuration)\obj\ + false + true + + + build\unrardll64\$(Configuration)\ + build\unrardll64\$(Configuration)\obj\ + false + true + + + + /MP %(AdditionalOptions) + Disabled + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + Sync + EnableFastChecks + MultiThreadedDebug + 4Bytes + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + NoExtensions + + + $(OutDir)unrar.dll + dll.def + true + Console + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + Disabled + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + Sync + EnableFastChecks + MultiThreadedDebug + 4Bytes + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + $(OutDir)unrar.dll + dll.def + true + Console + MachineX64 + + + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + Sync + MultiThreaded + 4Bytes + true + true + NoExtensions + Precise + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + + + /SAFESEH %(AdditionalOptions) + $(OutDir)unrar.dll + dll.def + true + Console + true + true + + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + false + Sync + MultiThreaded + 4Bytes + true + true + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + $(OutDir)unrar.dll + dll.def + true + Console + true + true + + MachineX64 + + + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions) + false + Sync + MultiThreaded + 4Bytes + true + true + NoExtensions + Precise + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + + + /SAFESEH %(AdditionalOptions) + $(OutDir)unrar.dll + dll_nocrypt.def + true + Console + true + true + + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions) + false + false + Sync + MultiThreaded + 4Bytes + true + true + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + $(OutDir)unrar.dll + dll_nocrypt.def + true + Console + true + true + + MachineX64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libunrar/acknow.txt b/libunrar/acknow.txt new file mode 100644 index 0000000..a68b672 --- /dev/null +++ b/libunrar/acknow.txt @@ -0,0 +1,92 @@ + ACKNOWLEDGMENTS + +* We used "Screaming Fast Galois Field Arithmetic Using Intel + SIMD Instructions" paper by James S. Plank, Kevin M. Greenan + and Ethan L. Miller to improve Reed-Solomon coding performance. + Also we are grateful to Artem Drobanov and Bulat Ziganshin + for samples and ideas allowed to make Reed-Solomon coding + more efficient. + +* RAR text compression algorithm is based on Dmitry Shkarin PPMII + and Dmitry Subbotin carryless rangecoder public domain source code. + You may find it in ftp.elf.stuba.sk/pub/pc/pack. + +* RAR encryption includes parts of code from Szymon Stefanek + and Brian Gladman AES implementations also as Steve Reid SHA-1 source. + + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + + Source code of this package also as other cryptographic technology + and computing project related links are available on Brian Gladman's + web site: http://www.gladman.me.uk + +* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm. + Original Intel Slicing-by-8 code is available here: + + http://sourceforge.net/projects/slicing-by-8/ + + Original Intel Slicing-by-8 code is licensed under BSD License + available at http://www.opensource.org/licenses/bsd-license.html + + Copyright (c) 2004-2006 Intel Corporation. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with + the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "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 THE COPYRIGHT + HOLDER OR CONTRIBUTORS 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. + +* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ), + designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn + and Christian Winnerlein. + +* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed + to significantly improve RAR compression and speed. diff --git a/libunrar/arccmt.cpp b/libunrar/arccmt.cpp index 0960425..8b7e498 100644 --- a/libunrar/arccmt.cpp +++ b/libunrar/arccmt.cpp @@ -1,59 +1,74 @@ -bool Archive::GetComment(Array *CmtData,Array *CmtDataW) +static bool IsAnsiEscComment(const wchar *Data,size_t Size); + +bool Archive::GetComment(Array *CmtData) { if (!MainComment) - return(false); - SaveFilePos SavePos(*this); + return false; + int64 SavePos=Tell(); + bool Success=DoGetComment(CmtData); + Seek(SavePos,SEEK_SET); + return Success; +} + +bool Archive::DoGetComment(Array *CmtData) +{ #ifndef SFX_MODULE - ushort CmtLength; - if (OldFormat) + uint CmtLength; + if (Format==RARFMT14) { - Seek(SFXSize+SIZEOF_OLDMHD,SEEK_SET); + Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET); CmtLength=GetByte(); CmtLength+=(GetByte()<<8); } else #endif { - if (NewMhd.Flags & MHD_COMMENT) + if (MainHead.CommentInHeader) { - Seek(SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD,SEEK_SET); - ReadHeader(); + // Old style (RAR 2.9) archive comment embedded into the main + // archive header. + Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET); + if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT) + return false; } else { - Seek(SFXSize+SIZEOF_MARKHEAD+NewMhd.HeadSize,SEEK_SET); - return(SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData,CmtDataW)!=0); + // Current (RAR 3.0+) version of archive comment. + Seek(GetStartPos(),SEEK_SET); + return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData); } #ifndef SFX_MODULE - if (CommHead.HeadCRC!=HeaderCRC) + // Old style (RAR 2.9) comment header embedded into the main + // archive header. + if (BrokenHeader || CommHead.HeadSize UNP_VER || CommHead.Method > 0x35)) - return(false); + if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35)) + return false; ComprDataIO DataIO; - Unpack Unpack(&DataIO); - Unpack.Init(); DataIO.SetTestMode(true); uint UnpCmtLength; - if (OldFormat) + if (Format==RARFMT14) { -#ifdef NOCRYPT - return(false); +#ifdef RAR_NOCRYPT + return false; #else UnpCmtLength=GetByte(); UnpCmtLength+=(GetByte()<<8); + if (CmtLength<2) + return false; CmtLength-=2; DataIO.SetCmt13Encryption(); + CommHead.UnpVer=15; #endif } else @@ -61,157 +76,110 @@ bool Archive::GetComment(Array *CmtData,Array *CmtDataW) DataIO.SetFiles(this,NULL); DataIO.EnableShowProgress(false); DataIO.SetPackedSizeToRead(CmtLength); - Unpack.SetDestSize(UnpCmtLength); - Unpack.DoUnpack(CommHead.UnpVer,false); + DataIO.UnpHash.Init(HASH_CRC32,1); + DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet. - if (!OldFormat && ((~DataIO.UnpFileCRC)&0xffff)!=CommHead.CommCRC) + Unpack CmtUnpack(&DataIO); + CmtUnpack.Init(0x10000,false); + CmtUnpack.SetDestSize(UnpCmtLength); + CmtUnpack.DoUnpack(CommHead.UnpVer,false); + + if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC) { - Log(FileName,St(MLogCommBrk)); - Alarm(); - return(false); + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; } else { byte *UnpData; size_t UnpDataSize; DataIO.GetUnpackedData(&UnpData,&UnpDataSize); - CmtData->Alloc(UnpDataSize); - memcpy(&((*CmtData)[0]),UnpData,UnpDataSize); + if (UnpDataSize>0) + { +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); +#endif + CmtData->Alloc(UnpDataSize+1); + memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar)); + CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size()); + CmtData->Alloc(wcslen(CmtData->Addr(0))); + } } } else { - CmtData->Alloc(CmtLength); - - Read(&((*CmtData)[0]),CmtLength); - if (!OldFormat && CommHead.CommCRC!=(~CRC(0xffffffff,&((*CmtData)[0]),CmtLength)&0xffff)) + if (CmtLength==0) + return false; + Array CmtRaw(CmtLength); + int ReadSize=Read(&CmtRaw[0],CmtLength); + if (ReadSize>=0 && (uint)ReadSizeReset(); - return(false); + CmtLength=ReadSize; + CmtRaw.Alloc(CmtLength); } - } -#endif -#if defined(_WIN_32) && !defined(_WIN_CE) - if (CmtData->Size()>0) - { - size_t CmtSize=CmtData->Size(); - OemToCharBuff((char *)CmtData->Addr(),(char *)CmtData->Addr(),(DWORD)CmtSize); - if (CmtDataW!=NULL) + if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff)) { - CmtDataW->Alloc(CmtSize+1); - CmtData->Push(0); - CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1); - CmtData->Alloc(CmtSize); - CmtDataW->Alloc(strlenw(CmtDataW->Addr())); + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; } + CmtData->Alloc(CmtLength+1); + CmtRaw.Push(0); +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]); +#endif + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + CmtData->Alloc(wcslen(CmtData->Addr(0))); } #endif - return(CmtData->Size()>0); + return CmtData->Size() > 0; } -size_t Archive::ReadCommentData(Array *CmtData,Array *CmtDataW) +bool Archive::ReadCommentData(Array *CmtData) { - bool Unicode=SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE; - if (!ReadSubData(CmtData,NULL)) - return(0); - size_t CmtSize=CmtData->Size(); - if (Unicode) - { - CmtSize/=2; - Array DataW(CmtSize+1); - RawToWide(CmtData->Addr(),DataW.Addr(),CmtSize); - DataW[CmtSize]=0; - size_t DestSize=CmtSize*4; - CmtData->Alloc(DestSize+1); - WideToChar(DataW.Addr(),(char *)CmtData->Addr(),DestSize); - (*CmtData)[DestSize]=0; - CmtSize=strlen((char *)CmtData->Addr()); - CmtData->Alloc(CmtSize); - if (CmtDataW!=NULL) - { - *CmtDataW=DataW; - CmtDataW->Alloc(CmtSize); - } - } + Array CmtRaw; + if (!ReadSubData(&CmtRaw,NULL,false)) + return false; + size_t CmtSize=CmtRaw.Size(); + CmtRaw.Push(0); + CmtData->Alloc(CmtSize+1); + if (Format==RARFMT50) + UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); else - if (CmtDataW!=NULL) + if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0) { - CmtData->Push(0); - CmtDataW->Alloc(CmtSize+1); - CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1); - CmtData->Alloc(CmtSize); - CmtDataW->Alloc(strlenw(CmtDataW->Addr())); + RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2); + (*CmtData)[CmtSize/2]=0; + } - return(CmtSize); + else + { + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + } + CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length. + return true; } void Archive::ViewComment() { -#ifndef GUI if (Cmd->DisableComment) return; - Array CmtBuf; - if (GetComment(&CmtBuf,NULL)) + Array CmtBuf; + if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments. { size_t CmtSize=CmtBuf.Size(); - char *ChPtr=(char *)memchr(&CmtBuf[0],0x1A,CmtSize); + wchar *ChPtr=wcschr(&CmtBuf[0],0x1A); if (ChPtr!=NULL) - CmtSize=ChPtr-(char *)&CmtBuf[0]; - mprintf("\n"); - OutComment((char *)&CmtBuf[0],CmtSize); - } -#endif -} - - -#ifndef SFX_MODULE -void Archive::ViewFileComment() -{ - if (!(NewLhd.Flags & LHD_COMMENT) || Cmd->DisableComment || OldFormat) - return; -#ifndef GUI - mprintf(St(MFileComment)); -#endif - const int MaxSize=0x8000; - Array CmtBuf(MaxSize); - SaveFilePos SavePos(*this); - Seek(CurBlockPos+SIZEOF_NEWLHD+NewLhd.NameSize,SEEK_SET); - int64 SaveCurBlockPos=CurBlockPos; - int64 SaveNextBlockPos=NextBlockPos; - - size_t Size=ReadHeader(); - - CurBlockPos=SaveCurBlockPos; - NextBlockPos=SaveNextBlockPos; - - if (Size<7 || CommHead.HeadType!=COMM_HEAD) - return; - if (CommHead.HeadCRC!=HeaderCRC) - { -#ifndef GUI - Log(FileName,St(MLogCommHead)); -#endif - return; - } - if (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER || - CommHead.Method > 0x30 || CommHead.UnpSize > MaxSize) - return; - Read(&CmtBuf[0],CommHead.UnpSize); - if (CommHead.CommCRC!=((~CRC(0xffffffff,&CmtBuf[0],CommHead.UnpSize)&0xffff))) - { - Log(FileName,St(MLogBrokFCmt)); - } - else - { - OutComment(&CmtBuf[0],CommHead.UnpSize); -#ifndef GUI - mprintf("\n"); -#endif + CmtSize=ChPtr-&CmtBuf[0]; + mprintf(L"\n"); + OutComment(&CmtBuf[0],CmtSize); } } -#endif + + diff --git a/libunrar/archive.cpp b/libunrar/archive.cpp index 5f807b5..8c5a1da 100644 --- a/libunrar/archive.cpp +++ b/libunrar/archive.cpp @@ -1,133 +1,150 @@ #include "rar.hpp" -#ifndef SHELL_EXT #include "arccmt.cpp" -#endif Archive::Archive(RAROptions *InitCmd) { - Cmd=InitCmd==NULL ? &DummyCmd:InitCmd; + Cmd=NULL; // Just in case we'll have an exception in 'new' below. + + DummyCmd=(InitCmd==NULL); + Cmd=DummyCmd ? (new RAROptions):InitCmd; + OpenShared=Cmd->OpenShared; - OldFormat=false; + Format=RARFMT15; Solid=false; Volume=false; MainComment=false; Locked=false; Signed=false; - NotFirstVolume=false; + FirstVolume=false; + NewNumbering=false; SFXSize=0; LatestTime.Reset(); Protected=false; Encrypted=false; - BrokenFileHeader=false; + FailedHeaderDecryption=false; + BrokenHeader=false; LastReadBlock=0; CurBlockPos=0; NextBlockPos=0; - RecoveryPos=SIZEOF_MARKHEAD; - RecoverySectors=-1; - memset(&NewMhd,0,sizeof(NewMhd)); - NewMhd.HeadType=MAIN_HEAD; - NewMhd.HeadSize=SIZEOF_NEWMHD; - HeaderCRC=0; + memset(&MainHead,0,sizeof(MainHead)); + memset(&CryptHead,0,sizeof(CryptHead)); + memset(&EndArcHead,0,sizeof(EndArcHead)); + + VolNumber=0; VolWrite=0; AddingFilesSize=0; AddingHeadersSize=0; -#if !defined(SHELL_EXT) && !defined(NOCRYPT) - *HeadersSalt=0; - *SubDataSalt=0; -#endif *FirstVolumeName=0; - *FirstVolumeNameW=0; Splitting=false; NewArchive=false; SilentOpen=false; +#ifdef USE_QOPEN + ProhibitQOpen=false; +#endif + +} + + +Archive::~Archive() +{ + if (DummyCmd) + delete Cmd; } -#ifndef SHELL_EXT void Archive::CheckArc(bool EnableBroken) { if (!IsArchive(EnableBroken)) { - Log(FileName,St(MBadArc),FileName); - ErrHandler.Exit(FATAL_ERROR); + // If FailedHeaderDecryption is set, we already reported that archive + // password is incorrect. + if (!FailedHeaderDecryption) + uiMsg(UIERROR_BADARCHIVE,FileName); + ErrHandler.Exit(RARX_FATAL); } } -#endif -#if !defined(SHELL_EXT) && !defined(SFX_MODULE) -void Archive::CheckOpen(char *Name,wchar *NameW) +#if !defined(SFX_MODULE) +void Archive::CheckOpen(const wchar *Name) { - TOpen(Name,NameW); + TOpen(Name); CheckArc(false); } #endif -bool Archive::WCheckOpen(char *Name,wchar *NameW) +bool Archive::WCheckOpen(const wchar *Name) { - if (!WOpen(Name,NameW)) - return(false); + if (!WOpen(Name)) + return false; if (!IsArchive(false)) { -#ifndef SHELL_EXT - Log(FileName,St(MNotRAR),FileName); -#endif + uiMsg(UIERROR_BADARCHIVE,FileName); Close(); - return(false); + return false; } - return(true); + return true; } -bool Archive::IsSignature(byte *D) +RARFORMAT Archive::IsSignature(const byte *D,size_t Size) { - bool Valid=false; - if (D[0]==0x52) + RARFORMAT Type=RARFMT_NONE; + if (Size>=1 && D[0]==0x52) #ifndef SFX_MODULE - if (D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) - { - OldFormat=true; - Valid=true; - } + if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) + Type=RARFMT14; else #endif - if (D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07 && D[6]==0x00) + if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07) { - OldFormat=false; - Valid=true; + // We check the last signature byte, so we can return a sensible + // warning in case we'll want to change the archive format + // sometimes in the future. + if (D[6]==0) + Type=RARFMT15; + else + if (D[6]==1) + Type=RARFMT50; + else + if (D[6]>1 && D[6]<5) + Type=RARFMT_FUTURE; } - return(Valid); + return Type; } bool Archive::IsArchive(bool EnableBroken) { Encrypted=false; + BrokenHeader=false; // Might be left from previous volume. + #ifndef SFX_MODULE if (IsDevice()) { -#ifndef SHELL_EXT - Log(FileName,St(MInvalidName),FileName); -#endif - return(false); + uiMsg(UIERROR_INVALIDNAME,FileName,FileName); + return false; } #endif - if (Read(MarkHead.Mark,SIZEOF_MARKHEAD)!=SIZEOF_MARKHEAD) - return(false); + if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3) + return false; SFXSize=0; - if (IsSignature(MarkHead.Mark)) + + RARFORMAT Type; + if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE) { - if (OldFormat) - Seek(0,SEEK_SET); + Format=Type; + if (Format==RARFMT14) + Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET); } else { @@ -135,9 +152,10 @@ bool Archive::IsArchive(bool EnableBroken) long CurPos=(long)Tell(); int ReadSize=Read(&Buffer[0],Buffer.Size()-16); for (int I=0;I0 && CurPos<28 && ReadSize>31) + Format=Type; + if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31) { char *D=&Buffer[28-CurPos]; if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58) @@ -145,98 +163,111 @@ bool Archive::IsArchive(bool EnableBroken) } SFXSize=CurPos+I; Seek(SFXSize,SEEK_SET); - if (!OldFormat) - Read(MarkHead.Mark,SIZEOF_MARKHEAD); + if (Format==RARFMT15 || Format==RARFMT50) + Read(MarkHead.Mark,SIZEOF_MARKHEAD3); break; } if (SFXSize==0) - return(false); + return false; } - ReadHeader(); - SeekToNext(); -#ifndef SFX_MODULE - if (OldFormat) + if (Format==RARFMT_FUTURE) { - NewMhd.Flags=OldMhd.Flags & 0x3f; - NewMhd.HeadSize=OldMhd.HeadSize; + uiMsg(UIERROR_NEWRARFORMAT,FileName); + return false; + } + if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer. + { + if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0) + return false; + MarkHead.HeadSize=SIZEOF_MARKHEAD5; } else -#endif - { - if (HeaderCRC!=NewMhd.HeadCRC) - { -#ifndef SHELL_EXT - Log(FileName,St(MLogMainHead)); -#endif - Alarm(); - if (!EnableBroken) - return(false); - } - } - Volume=(NewMhd.Flags & MHD_VOLUME); - Solid=(NewMhd.Flags & MHD_SOLID)!=0; - MainComment=(NewMhd.Flags & MHD_COMMENT)!=0; - Locked=(NewMhd.Flags & MHD_LOCK)!=0; - Signed=(NewMhd.PosAV!=0); - Protected=(NewMhd.Flags & MHD_PROTECT)!=0; - Encrypted=(NewMhd.Flags & MHD_PASSWORD)!=0; + MarkHead.HeadSize=SIZEOF_MARKHEAD3; - if (NewMhd.EncryptVer>UNP_VER) - { #ifdef RARDLL - Cmd->DllError=ERAR_UNKNOWN_FORMAT; -#else - ErrHandler.SetErrorCode(WARNING); - #if !defined(SILENT) && !defined(SFX_MODULE) - Log(FileName,St(MUnknownMeth),FileName); - Log(FileName,St(MVerRequired),NewMhd.EncryptVer/10,NewMhd.EncryptVer%10); - #endif -#endif - return(false); - } -#ifdef RARDLL - SilentOpen=true; + // If callback function is not set, we cannot get the password, + // so we skip the initial header processing for encrypted header archive. + // It leads to skipped archive comment, but the rest of archive data + // is processed correctly. + if (Cmd->Callback==NULL) + SilentOpen=true; #endif - //if not encrypted, we'll check it below - NotFirstVolume=Encrypted && (NewMhd.Flags & MHD_FIRSTVOLUME)==0; - - if (!SilentOpen || !Encrypted) + bool HeadersLeft; // Any headers left to read. + bool StartFound=false; // Main or encryption headers found. + // Skip the archive encryption header if any and read the main header. + while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang. { - SaveFilePos SavePos(*this); + SeekToNext(); + + HEADER_TYPE Type=GetHeaderType(); + // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to + // avoid the password prompt. + StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT; + if (StartFound) + break; + } + + + // We should not do it for EnableBroken or we'll get 'not RAR archive' + // messages when extracting encrypted archives with wrong password. + if (FailedHeaderDecryption && !EnableBroken) + return false; + + if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing. + { + if (!FailedHeaderDecryption) // If not reported a wrong password already. + uiMsg(UIERROR_MHEADERBROKEN,FileName); + if (!EnableBroken) + return false; + } + + MainComment=MainHead.CommentInHeader; + + // If we process non-encrypted archive or can request a password, + // we set 'first volume' flag based on file attributes below. + // It is necessary for RAR 2.x archives, which did not have 'first volume' + // flag in main header. Also for all RAR formats we need to scan until + // first file header to set "comment" flag when reading service header. + // Unless we are in silent mode, we need to know about presence of comment + // immediately after IsArchive call. + if (HeadersLeft && (!SilentOpen || !Encrypted)) + { + int64 SavePos=Tell(); int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; - NotFirstVolume=false; while (ReadHeader()!=0) { - int HeaderType=GetHeaderType(); - if (HeaderType==NEWSUB_HEAD) + HEADER_TYPE HeaderType=GetHeaderType(); + if (HeaderType==HEAD_SERVICE) { - if (SubHead.CmpName(SUBHEAD_TYPE_CMT)) - MainComment=true; - if ((SubHead.Flags & LHD_SPLIT_BEFORE) || - Volume && (NewMhd.Flags & MHD_FIRSTVOLUME)==0) - NotFirstVolume=true; + // If we have a split service headers, it surely indicates non-first + // volume. But not split service header does not guarantee the first + // volume, because we can have split file after non-split archive + // comment. So we do not quit from loop here. + FirstVolume=Volume && !SubHead.SplitBefore; } else - { - if (HeaderType==FILE_HEAD && ((NewLhd.Flags & LHD_SPLIT_BEFORE)!=0 || - Volume && NewLhd.UnpVer>=29 && (NewMhd.Flags & MHD_FIRSTVOLUME)==0)) - NotFirstVolume=true; - break; - } + if (HeaderType==HEAD_FILE) + { + FirstVolume=Volume && !FileHead.SplitBefore; + break; + } + else + if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header. + break; SeekToNext(); } CurBlockPos=SaveCurBlockPos; NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + Seek(SavePos,SEEK_SET); } - if (!Volume || !NotFirstVolume) - { - strcpy(FirstVolumeName,FileName); - strcpyw(FirstVolumeNameW,FileNameW); - } + if (!Volume || FirstVolume) + wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName)); - return(true); + return true; } @@ -248,20 +279,60 @@ void Archive::SeekToNext() } -#ifndef SFX_MODULE -int Archive::GetRecoverySize(bool Required) + + + + +// Calculate the block size including encryption fields and padding if any. +uint Archive::FullHeaderSize(size_t Size) { - if (!Protected) - return(0); - if (RecoverySectors!=-1 || !Required) - return(RecoverySectors); - SaveFilePos SavePos(*this); - Seek(SFXSize,SEEK_SET); - SearchSubBlock(SUBHEAD_TYPE_RR); - return(RecoverySectors); + if (Encrypted) + { + Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size. + if (Format == RARFMT50) + Size += SIZE_INITV; + else + Size += SIZE_SALT30; + } + return uint(Size); +} + + + + +#ifdef USE_QOPEN +bool Archive::Open(const wchar *Name,uint Mode) +{ + // Important if we reuse Archive object and it has virtual QOpen + // file position not matching real. For example, for 'l -v volname'. + QOpen.Unload(); + + return File::Open(Name,Mode); +} + + +int Archive::Read(void *Data,size_t Size) +{ + size_t Result; + if (QOpen.Read(Data,Size,Result)) + return (int)Result; + return File::Read(Data,Size); +} + + +void Archive::Seek(int64 Offset,int Method) +{ + if (!QOpen.Seek(Offset,Method)) + File::Seek(Offset,Method); +} + + +int64 Archive::Tell() +{ + int64 QPos; + if (QOpen.Tell(&QPos)) + return QPos; + return File::Tell(); } #endif - - - diff --git a/libunrar/archive.hpp b/libunrar/archive.hpp index fa1bf35..d9518f1 100644 --- a/libunrar/archive.hpp +++ b/libunrar/archive.hpp @@ -1,126 +1,148 @@ #ifndef _RAR_ARCHIVE_ #define _RAR_ARCHIVE_ -class Pack; +class PPack; +class RawRead; +class RawWrite; -enum {EN_LOCK=1,EN_VOL=2,EN_FIRSTVOL=4}; +enum NOMODIFY_FLAGS +{ + NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4 +}; + +enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE}; + +enum ADDSUBDATA_FLAGS +{ + ASDF_SPLIT = 1, // Allow to split archive just before header if necessary. + ASDF_COMPRESS = 2, // Allow to compress data following subheader. + ASDF_CRYPT = 4, // Encrypt data after subheader if password is set. + ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode. +}; + +// RAR5 headers must not exceed 2 MB. +#define MAX_HEADER_SIZE_RAR5 0x200000 class Archive:public File { private: - bool IsSignature(byte *D); void UpdateLatestTime(FileHeader *CurBlock); - void ConvertNameCase(char *Name); void ConvertNameCase(wchar *Name); - void ConvertUnknownHeader(); - size_t ReadOldHeader(); + void ConvertFileHeader(FileHeader *hd); + size_t ReadHeader14(); + size_t ReadHeader15(); + size_t ReadHeader50(); + void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb); + void RequestArcPassword(); void UnexpEndArcMsg(); + void BrokenHeaderMsg(); + void UnkEncVerMsg(const wchar *Name,const wchar *Info); + bool DoGetComment(Array *CmtData); + bool ReadCommentData(Array *CmtData); -#if !defined(SHELL_EXT) && !defined(NOCRYPT) +#if !defined(RAR_NOCRYPT) CryptData HeadersCrypt; - byte HeadersSalt[SALT_SIZE]; #endif -#ifndef SHELL_EXT ComprDataIO SubDataIO; - byte SubDataSalt[SALT_SIZE]; -#endif - RAROptions *Cmd,DummyCmd; + bool DummyCmd; + RAROptions *Cmd; - MarkHeader MarkHead; - OldMainHeader OldMhd; - - int RecoverySectors; - int64 RecoveryPos; RarTime LatestTime; int LastReadBlock; - int CurHeaderType; + HEADER_TYPE CurHeaderType; bool SilentOpen; +#ifdef USE_QOPEN + QuickOpen QOpen; + bool ProhibitQOpen; +#endif public: Archive(RAROptions *InitCmd=NULL); + ~Archive(); + static RARFORMAT IsSignature(const byte *D,size_t Size); bool IsArchive(bool EnableBroken); - size_t SearchBlock(int BlockType); - size_t SearchSubBlock(const char *Type); - int ReadBlock(int BlockType); - void WriteBlock(int BlockType,BaseBlock *wb=NULL); - int PrepareNamesToWrite(char *Name,wchar *NameW,char *DestName,byte *DestNameW); - void SetLhdSize(); + size_t SearchBlock(HEADER_TYPE HeaderType); + size_t SearchSubBlock(const wchar *Type); + size_t SearchRR(); size_t ReadHeader(); void CheckArc(bool EnableBroken); - void CheckOpen(char *Name,wchar *NameW=NULL); - bool WCheckOpen(char *Name,wchar *NameW=NULL); - bool TestLock(int Mode); - void MakeTemp(); - void CopyMainHeader(Archive &Src,bool CopySFX=true,char *NameToDisplay=NULL); - bool ProcessToFileHead(Archive &Src,bool LastBlockAdded, - Pack *Pack=NULL,const char *SkipName=NULL); - void TmpToArc(Archive &Src); - void CloseNew(int AdjustRecovery,bool CloseVolume); - void WriteEndBlock(bool CloseVolume); - void CopyFileRecord(Archive &Src); - void CopyArchiveData(Archive &Src); - bool GetComment(Array *CmtData,Array *CmtDataW); + void CheckOpen(const wchar *Name); + bool WCheckOpen(const wchar *Name); + bool GetComment(Array *CmtData); void ViewComment(); - void ViewFileComment(); void SetLatestTime(RarTime *NewTime); void SeekToNext(); bool CheckAccess(); bool IsArcDir(); - bool IsArcLabel(); void ConvertAttributes(); - int GetRecoverySize(bool Required); void VolSubtractHeaderSize(size_t SubSize); - void AddSubData(byte *SrcData,size_t DataSize,File *SrcFile,const char *Name,bool AllowSplit); - bool ReadSubData(Array *UnpData,File *DestFile); - int GetHeaderType() {return(CurHeaderType);}; - size_t ReadCommentData(Array *CmtData,Array *CmtDataW); - void WriteCommentData(byte *Data,size_t DataSize,bool FileComment); - RAROptions* GetRAROptions() {return(Cmd);} + uint FullHeaderSize(size_t Size); + int64 GetStartPos(); + void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile, + const wchar *Name,uint Flags); + bool ReadSubData(Array *UnpData,File *DestFile,bool TestMode); + HEADER_TYPE GetHeaderType() {return CurHeaderType;} + RAROptions* GetRAROptions() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} +#if 0 + void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); +#endif +#ifdef USE_QOPEN + bool Open(const wchar *Name,uint Mode=FMF_READ); + int Read(void *Data,size_t Size); + void Seek(int64 Offset,int Method); + int64 Tell(); + void QOpenUnload() {QOpen.Unload();} + void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;} +#endif BaseBlock ShortBlock; - MainHeader NewMhd; - FileHeader NewLhd; + MarkHeader MarkHead; + MainHeader MainHead; + CryptHeader CryptHead; + FileHeader FileHead; EndArcHeader EndArcHead; SubBlockHeader SubBlockHead; FileHeader SubHead; CommentHeader CommHead; ProtectHeader ProtectHead; - AVHeader AVHead; - SignHeader SignHead; UnixOwnersHeader UOHead; - MacFInfoHeader MACHead; EAHeader EAHead; StreamHeader StreamHead; int64 CurBlockPos; int64 NextBlockPos; - bool OldFormat; + RARFORMAT Format; bool Solid; bool Volume; bool MainComment; bool Locked; bool Signed; - bool NotFirstVolume; + bool FirstVolume; + bool NewNumbering; bool Protected; bool Encrypted; size_t SFXSize; - bool BrokenFileHeader; + bool BrokenHeader; + bool FailedHeaderDecryption; + +#if !defined(RAR_NOCRYPT) + byte ArcSalt[SIZE_SALT50]; +#endif bool Splitting; - ushort HeaderCRC; - + uint VolNumber; int64 VolWrite; - int64 AddingFilesSize; - size_t AddingHeadersSize; + uint64 AddingFilesSize; + uint64 AddingHeadersSize; bool NewArchive; - char FirstVolumeName[NM]; - wchar FirstVolumeNameW[NM]; + wchar FirstVolumeName[NM]; }; + #endif diff --git a/libunrar/arcread.cpp b/libunrar/arcread.cpp index 256287d..b0cf39f 100644 --- a/libunrar/arcread.cpp +++ b/libunrar/arcread.cpp @@ -1,87 +1,159 @@ #include "rar.hpp" -size_t Archive::SearchBlock(int BlockType) +size_t Archive::ReadHeader() { - size_t Size,Count=0; - while ((Size=ReadHeader())!=0 && - (BlockType==ENDARC_HEAD || GetHeaderType()!=ENDARC_HEAD)) + // Once we failed to decrypt an encrypted block, there is no reason to + // attempt to do it further. We'll never be successful and only generate + // endless errors. + if (FailedHeaderDecryption) + return 0; + + CurBlockPos=Tell(); + + // Other developers asked us to initialize it to suppress "may be used + // uninitialized" warning in code below in some compilers. + size_t ReadSize=0; + + switch(Format) { - if ((++Count & 127)==0) - Wait(); - if (GetHeaderType()==BlockType) - return(Size); - SeekToNext(); +#ifndef SFX_MODULE + case RARFMT14: + ReadSize=ReadHeader14(); + break; +#endif + case RARFMT15: + ReadSize=ReadHeader15(); + break; + case RARFMT50: + ReadSize=ReadHeader50(); + break; } - return(0); + + // It is important to check ReadSize>0 here, because it is normal + // for RAR2 and RAR3 archives without end of archive block to have + // NextBlockPos==CurBlockPos after the end of archive has reached. + if (ReadSize>0 && NextBlockPos<=CurBlockPos) + { + BrokenHeaderMsg(); + ReadSize=0; + } + + if (ReadSize==0) + CurHeaderType=HEAD_UNKNOWN; + + return ReadSize; } -size_t Archive::SearchSubBlock(const char *Type) +size_t Archive::SearchBlock(HEADER_TYPE HeaderType) { - size_t Size; - while ((Size=ReadHeader())!=0 && GetHeaderType()!=ENDARC_HEAD) + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && + (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC)) { - if (GetHeaderType()==NEWSUB_HEAD && SubHead.CmpName(Type)) - return(Size); + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==HeaderType) + return Size; SeekToNext(); } - return(0); + return 0; +} + + +size_t Archive::SearchSubBlock(const wchar *Type) +{ + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC) + { + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type)) + return Size; + SeekToNext(); + } + return 0; +} + + +size_t Archive::SearchRR() +{ + // If locator extra field is available for recovery record, let's utilize it. + if (MainHead.Locator && MainHead.RROffset!=0) + { + uint64 CurPos=Tell(); + Seek(MainHead.RROffset,SEEK_SET); + size_t Size=ReadHeader(); + if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR)) + return Size; + Seek(CurPos,SEEK_SET); + } + // Otherwise scan the entire archive to find the recovery record. + return SearchSubBlock(SUBHEAD_TYPE_RR); } void Archive::UnexpEndArcMsg() { int64 ArcSize=FileLength(); - if (CurBlockPos>ArcSize || NextBlockPos>ArcSize) + + // If block positions are equal to file size, this is not an error. + // It can happen when we reached the end of older RAR 1.5 archive, + // which did not have the end of archive block. + if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) { -#ifndef SHELL_EXT - Log(FileName,St(MLogUnexpEOF)); -#endif - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_UNEXPEOF,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); } } -size_t Archive::ReadHeader() +void Archive::BrokenHeaderMsg() { - CurBlockPos=Tell(); + uiMsg(UIERROR_HEADERBROKEN,FileName); + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); +} -#ifndef SFX_MODULE - if (OldFormat) - return(ReadOldHeader()); -#endif +void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info) +{ + uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info); + ErrHandler.SetErrorCode(RARX_WARNING); +} + + +// Return f in case of signed integer overflow or negative parameters +// or v1+v2 otherwise. We use it for file offsets, which are signed +// for compatibility with off_t in POSIX file functions and third party code. +// Signed integer overflow is the undefined behavior according to +// C++ standard and it causes fuzzers to complain. +inline int64 SafeAdd(int64 v1,int64 v2,int64 f) +{ + return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f; +} + + +size_t Archive::ReadHeader15() +{ RawRead Raw(this); - bool Decrypt=Encrypted && CurBlockPos>=(int64)SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD; + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3; if (Decrypt) { -#if defined(SHELL_EXT) || defined(NOCRYPT) - return(0); +#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. + return 0; #else - if (Read(HeadersSalt,SALT_SIZE)!=SALT_SIZE) + RequestArcPassword(); + + byte Salt[SIZE_SALT30]; + if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) { UnexpEndArcMsg(); - return(0); + return 0; } - if (*Cmd->Password==0) -#ifdef RARDLL - if (Cmd->Callback==NULL || - Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1) - { - Close(); - ErrHandler.Exit(USER_BREAK); - } - -#else - if (!GetPassword(PASSWORD_ARCHIVE,FileName,Cmd->Password,sizeof(Cmd->Password))) - { - Close(); - ErrHandler.Exit(USER_BREAK); - } -#endif - HeadersCrypt.SetCryptKeys(Cmd->Password,HeadersSalt,false,false,NewMhd.EncryptVer>=36); + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL); Raw.SetCrypt(&HeadersCrypt); #endif } @@ -90,164 +162,239 @@ size_t Archive::ReadHeader() if (Raw.Size()==0) { UnexpEndArcMsg(); - return(0); + return 0; } - Raw.Get(ShortBlock.HeadCRC); - byte HeadType; - Raw.Get(HeadType); - ShortBlock.HeadType=(HEADER_TYPE)HeadType; - Raw.Get(ShortBlock.Flags); - Raw.Get(ShortBlock.HeadSize); + ShortBlock.HeadCRC=Raw.Get2(); + + ShortBlock.Reset(); + + uint HeaderType=Raw.Get1(); + ShortBlock.Flags=Raw.Get2(); + ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0; + ShortBlock.HeadSize=Raw.Get2(); + + ShortBlock.HeaderType=(HEADER_TYPE)HeaderType; if (ShortBlock.HeadSizeReset(); + *(BaseBlock *)hd=ShortBlock; - Raw.Get(hd->PackSize); - Raw.Get(hd->UnpSize); - Raw.Get(hd->HostOS); - Raw.Get(hd->FileCRC); - Raw.Get(hd->FileTime); - Raw.Get(hd->UnpVer); - Raw.Get(hd->Method); - Raw.Get(hd->NameSize); - Raw.Get(hd->FileAttr); - if (hd->Flags & LHD_LARGE) - { - Raw.Get(hd->HighPackSize); - Raw.Get(hd->HighUnpSize); - } - else - { - hd->HighPackSize=hd->HighUnpSize=0; - if (hd->UnpSize==0xffffffff) + + hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0; + hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0; + hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0; + hd->SaltSet=(hd->Flags & LHD_SALT)!=0; + hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; + hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); + hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0; + hd->Version=(hd->Flags & LHD_VERSION)!=0; + + hd->DataSize=Raw.Get4(); + uint LowUnpSize=Raw.Get4(); + hd->HostOS=Raw.Get1(); + + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + + uint FileTime=Raw.Get4(); + hd->UnpVer=Raw.Get1(); + + hd->Method=Raw.Get1()-0x30; + size_t NameSize=Raw.Get2(); + hd->FileAttr=Raw.Get4(); + + // RAR15 did not use the special dictionary size to mark dirs. + if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) + hd->Dir=true; + + hd->CryptMethod=CRYPT_NONE; + if (hd->Encrypted) + switch(hd->UnpVer) { - // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates - // that we do not know the unpacked file size and must unpack it - // until we find the end of file marker in compressed data. - hd->UnpSize=(uint)(INT64NDF); - hd->HighUnpSize=(uint)(INT64NDF>>32); + case 13: hd->CryptMethod=CRYPT_RAR13; break; + case 15: hd->CryptMethod=CRYPT_RAR15; break; + case 20: + case 26: hd->CryptMethod=CRYPT_RAR20; break; + default: hd->CryptMethod=CRYPT_RAR30; break; } - } - hd->FullPackSize=INT32TO64(hd->HighPackSize,hd->PackSize); - hd->FullUnpSize=INT32TO64(hd->HighUnpSize,hd->UnpSize); - char FileName[NM*4]; - int NameSize=Min(hd->NameSize,sizeof(FileName)-1); - Raw.Get((byte *)FileName,NameSize); - FileName[NameSize]=0; + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOSHSType=HSYS_WINDOWS; - strncpyz(hd->FileName,FileName,ASIZE(hd->FileName)); + hd->RedirType=FSREDIR_NONE; - if (hd->HeadType==NEWSUB_HEAD) + // RAR 4.x Unix symlink. + if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000) { - int DataSize=hd->HeadSize-hd->NameSize-SIZEOF_NEWLHD; - if (hd->Flags & LHD_SALT) - DataSize-=SALT_SIZE; - if (DataSize>0) - { - hd->SubData.Alloc(DataSize); - Raw.Get(&hd->SubData[0],DataSize); - if (hd->CmpName(SUBHEAD_TYPE_RR)) - { - byte *D=&hd->SubData[8]; - RecoverySectors=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24); - } - } + hd->RedirType=FSREDIR_UNIXSYMLINK; + *hd->RedirName=0; + } + + hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0; + + hd->LargeFile=(hd->Flags & LHD_LARGE)!=0; + + uint HighPackSize,HighUnpSize; + if (hd->LargeFile) + { + HighPackSize=Raw.Get4(); + HighUnpSize=Raw.Get4(); + hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff); } else - if (hd->HeadType==FILE_HEAD) - { - if (hd->Flags & LHD_UNICODE) - { - EncodeFileName NameCoder; - size_t Length=strlen(FileName); - if (Length==hd->NameSize) - { - UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1); - WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1); - ExtToInt(hd->FileName,hd->FileName); - } - else - { - Length++; - NameCoder.Decode(FileName,(byte *)FileName+Length, - hd->NameSize-Length,hd->FileNameW, - sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])); - } - if (*hd->FileNameW==0) - hd->Flags &= ~LHD_UNICODE; - } - else - *hd->FileNameW=0; -#ifndef SFX_MODULE - ConvertNameCase(hd->FileName); - ConvertNameCase(hd->FileNameW); -#endif - ConvertUnknownHeader(); - } - if (hd->Flags & LHD_SALT) - Raw.Get(hd->Salt,SALT_SIZE); - hd->mtime.SetDos(hd->FileTime); - hd->ctime.Reset(); - hd->atime.Reset(); - hd->arctime.Reset(); - if (hd->Flags & LHD_EXTTIME) { - ushort Flags; - Raw.Get(Flags); + HighPackSize=HighUnpSize=0; + // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates + // that we do not know the unpacked file size and must unpack it + // until we find the end of file marker in compressed data. + hd->UnknownUnpSize=(LowUnpSize==0xffffffff); + } + hd->PackSize=INT32TO64(HighPackSize,hd->DataSize); + hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize); + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; + + char FileName[NM*4]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + + if (FileBlock) + { + *hd->FileName=0; + if ((hd->Flags & LHD_UNICODE)!=0) + { + EncodeFileName NameCoder; + size_t Length=strlen(FileName); + Length++; + if (ReadNameSize>Length) + NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length, + ReadNameSize-Length,hd->FileName, + ASIZE(hd->FileName)); + } + + if (*hd->FileName==0) + ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM); + +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); +#endif + ConvertFileHeader(hd); + } + else + { + CharToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Calculate the size of optional data. + int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3); + if ((hd->Flags & LHD_SALT)!=0) + DataSize-=SIZE_SALT30; + + if (DataSize>0) + { + // Here we read optional additional fields for subheaders. + // They are stored after the file name and before salt. + hd->SubData.Alloc(DataSize); + Raw.GetB(&hd->SubData[0],DataSize); + + } + + if (hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + } + if ((hd->Flags & LHD_SALT)!=0) + Raw.GetB(hd->Salt,SIZE_SALT30); + hd->mtime.SetDos(FileTime); + if ((hd->Flags & LHD_EXTTIME)!=0) + { + ushort Flags=Raw.Get2(); RarTime *tbl[4]; - tbl[0]=&NewLhd.mtime; - tbl[1]=&NewLhd.ctime; - tbl[2]=&NewLhd.atime; - tbl[3]=&NewLhd.arctime; + tbl[0]=&FileHead.mtime; + tbl[1]=&FileHead.ctime; + tbl[2]=&FileHead.atime; + tbl[3]=NULL; // Archive time is not used now. for (int I=0;I<4;I++) { RarTime *CurTime=tbl[I]; uint rmode=Flags>>(3-I)*4; - if ((rmode & 8)==0) + if ((rmode & 8)==0 || CurTime==NULL) continue; if (I!=0) { - uint DosTime; - Raw.Get(DosTime); + uint DosTime=Raw.Get4(); CurTime->SetDos(DosTime); } RarLocalTime rlt; @@ -255,108 +402,103 @@ size_t Archive::ReadHeader() if (rmode & 4) rlt.Second++; rlt.Reminder=0; - int count=rmode&3; - for (int J=0;JSetLocal(&rlt); } } - NextBlockPos+=hd->FullPackSize; - bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0; - HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff; + // Set to 0 in case of overflow, so end of ReadHeader cares about it. + NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0); + + bool CRCProcessedOnly=hd->CommentInHeader; + ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); if (hd->HeadCRC!=HeaderCRC) { - if (hd->HeadType==NEWSUB_HEAD) - strcat(hd->FileName,"- ???"); - BrokenFileHeader=true; - ErrHandler.SetErrorCode(WARNING); -#ifndef SHELL_EXT - Log(Archive::FileName,St(MLogFileHead),IntNameToExt(hd->FileName)); - Alarm(); -#endif + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_WARNING); + + // If we have a broken encrypted header, we do not need to display + // the error message here, because it will be displayed for such + // headers later in this function. Also such headers are unlikely + // to have anything sensible in file name field, so it is useless + // to display the file name. + if (!Decrypt) + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); } } break; + case HEAD_ENDARC: + *(BaseBlock *)&EndArcHead=ShortBlock; + EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0; + EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0; + EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0; + EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0; + if (EndArcHead.DataCRC) + EndArcHead.ArcDataCRC=Raw.Get4(); + if (EndArcHead.StoreVolNumber) + VolNumber=EndArcHead.VolNumber=Raw.Get2(); + break; #ifndef SFX_MODULE - case COMM_HEAD: + case HEAD3_CMT: *(BaseBlock *)&CommHead=ShortBlock; - Raw.Get(CommHead.UnpSize); - Raw.Get(CommHead.UnpVer); - Raw.Get(CommHead.Method); - Raw.Get(CommHead.CommCRC); + CommHead.UnpSize=Raw.Get2(); + CommHead.UnpVer=Raw.Get1(); + CommHead.Method=Raw.Get1(); + CommHead.CommCRC=Raw.Get2(); break; - case SIGN_HEAD: - *(BaseBlock *)&SignHead=ShortBlock; - Raw.Get(SignHead.CreationTime); - Raw.Get(SignHead.ArcNameSize); - Raw.Get(SignHead.UserNameSize); - break; - case AV_HEAD: - *(BaseBlock *)&AVHead=ShortBlock; - Raw.Get(AVHead.UnpVer); - Raw.Get(AVHead.Method); - Raw.Get(AVHead.AVVer); - Raw.Get(AVHead.AVInfoCRC); - break; - case PROTECT_HEAD: + case HEAD3_PROTECT: *(BaseBlock *)&ProtectHead=ShortBlock; - Raw.Get(ProtectHead.DataSize); - Raw.Get(ProtectHead.Version); - Raw.Get(ProtectHead.RecSectors); - Raw.Get(ProtectHead.TotalBlocks); - Raw.Get(ProtectHead.Mark,8); + ProtectHead.DataSize=Raw.Get4(); + ProtectHead.Version=Raw.Get1(); + ProtectHead.RecSectors=Raw.Get2(); + ProtectHead.TotalBlocks=Raw.Get4(); + Raw.GetB(ProtectHead.Mark,8); NextBlockPos+=ProtectHead.DataSize; - RecoverySectors=ProtectHead.RecSectors; break; - case SUB_HEAD: + case HEAD3_OLDSERVICE: // RAR 2.9 and earlier. *(BaseBlock *)&SubBlockHead=ShortBlock; - Raw.Get(SubBlockHead.DataSize); + SubBlockHead.DataSize=Raw.Get4(); NextBlockPos+=SubBlockHead.DataSize; - Raw.Get(SubBlockHead.SubType); - Raw.Get(SubBlockHead.Level); + SubBlockHead.SubType=Raw.Get2(); + SubBlockHead.Level=Raw.Get1(); switch(SubBlockHead.SubType) { case UO_HEAD: *(SubBlockHeader *)&UOHead=SubBlockHead; - Raw.Get(UOHead.OwnerNameSize); - Raw.Get(UOHead.GroupNameSize); - if (UOHead.OwnerNameSize>NM-1) - UOHead.OwnerNameSize=NM-1; - if (UOHead.GroupNameSize>NM-1) - UOHead.GroupNameSize=NM-1; - Raw.Get((byte *)UOHead.OwnerName,UOHead.OwnerNameSize); - Raw.Get((byte *)UOHead.GroupName,UOHead.GroupNameSize); + UOHead.OwnerNameSize=Raw.Get2(); + UOHead.GroupNameSize=Raw.Get2(); + if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName)) + UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1; + if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName)) + UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1; + Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize); + Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize); UOHead.OwnerName[UOHead.OwnerNameSize]=0; UOHead.GroupName[UOHead.GroupNameSize]=0; break; - case MAC_HEAD: - *(SubBlockHeader *)&MACHead=SubBlockHead; - Raw.Get(MACHead.fileType); - Raw.Get(MACHead.fileCreator); - break; - case EA_HEAD: - case BEEA_HEAD: case NTACL_HEAD: *(SubBlockHeader *)&EAHead=SubBlockHead; - Raw.Get(EAHead.UnpSize); - Raw.Get(EAHead.UnpVer); - Raw.Get(EAHead.Method); - Raw.Get(EAHead.EACRC); + EAHead.UnpSize=Raw.Get4(); + EAHead.UnpVer=Raw.Get1(); + EAHead.Method=Raw.Get1(); + EAHead.EACRC=Raw.Get4(); break; case STREAM_HEAD: *(SubBlockHeader *)&StreamHead=SubBlockHead; - Raw.Get(StreamHead.UnpSize); - Raw.Get(StreamHead.UnpVer); - Raw.Get(StreamHead.Method); - Raw.Get(StreamHead.StreamCRC); - Raw.Get(StreamHead.StreamNameSize); - if (StreamHead.StreamNameSize>NM-1) - StreamHead.StreamNameSize=NM-1; - Raw.Get((byte *)StreamHead.StreamName,StreamHead.StreamNameSize); + StreamHead.UnpSize=Raw.Get4(); + StreamHead.UnpVer=Raw.Get1(); + StreamHead.Method=Raw.Get1(); + StreamHead.StreamCRC=Raw.Get4(); + StreamHead.StreamNameSize=Raw.Get2(); + if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName)) + StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1; + Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize); StreamHead.StreamName[StreamHead.StreamNameSize]=0; break; } @@ -364,349 +506,980 @@ size_t Archive::ReadHeader() #endif default: if (ShortBlock.Flags & LONG_BLOCK) - { - uint DataSize; - Raw.Get(DataSize); - NextBlockPos+=DataSize; - } + NextBlockPos+=Raw.Get4(); break; } - HeaderCRC=~Raw.GetCRC(false)&0xffff; - CurHeaderType=ShortBlock.HeadType; - if (Decrypt) + + ushort HeaderCRC=Raw.GetCRC15(false); + + // Old AV header does not have header CRC properly set. + if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN && + ShortBlock.HeaderType!=HEAD3_AV) { - NextBlockPos+=Raw.PaddedSize()+SALT_SIZE; - - if (ShortBlock.HeadCRC!=HeaderCRC) + bool Recovered=false; + if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) { - bool Recovered=false; - if (ShortBlock.HeadType==ENDARC_HEAD && (EndArcHead.Flags & EARC_REVSPACE)!=0) - { - // Last 7 bytes of recovered volume can contain zeroes, because - // REV files store its own information (volume number, etc.) here. - SaveFilePos SavePos(*this); - int64 Length=Tell(); - Seek(Length-7,SEEK_SET); - Recovered=true; - for (int J=0;J<7;J++) - if (GetByte()!=0) - Recovered=false; - } - if (!Recovered) - { -#ifndef SILENT - Log(FileName,St(MEncrBadCRC),FileName); -#endif - Close(); + // Last 7 bytes of recovered volume can contain zeroes, because + // REV files store its own information (volume number, etc.) here. + int64 Length=Tell(); + Seek(Length-7,SEEK_SET); + Recovered=true; + for (int J=0;J<7;J++) + if (GetByte()!=0) + Recovered=false; + } + if (!Recovered) + { + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); - BrokenFileHeader=true; - ErrHandler.SetErrorCode(CRC_ERROR); - return(0); -// ErrHandler.Exit(CRC_ERROR); + if (Decrypt) + { + uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); + FailedHeaderDecryption=true; + return 0; } } } - if (NextBlockPos<=CurBlockPos) + return Raw.Size(); +} + + +size_t Archive::ReadHeader50() +{ + RawRead Raw(this); + + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5; + + if (Decrypt) { -#ifndef SHELL_EXT - Log(FileName,St(MLogFileHead),"???"); +#if defined(RAR_NOCRYPT) + return 0; +#else + + byte HeadersInitV[SIZE_INITV]; + if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) + { + UnexpEndArcMsg(); + return 0; + } + + // We repeat the password request only for manually entered passwords + // and not for -p. Wrong password can be intentionally provided + // in -p to not stop batch processing for encrypted archives. + bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); + + while (true) // Repeat the password prompt for wrong passwords. + { + RequestArcPassword(); + + byte PswCheck[SIZE_PSWCHECK]; + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); + // Verify password validity. + if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) + { + if (GlobalPassword) // For -p or Ctrl+P. + { + // This message is used by Android GUI to reset cached passwords. + // Update appropriate code if changed. + uiMsg(UIERROR_BADPSW,FileName,FileName); + FailedHeaderDecryption=true; + ErrHandler.SetErrorCode(RARX_BADPWD); + return 0; + } + else // For passwords entered manually. + { + // This message is used by Android GUI and Windows GUI and SFX to + // reset cached passwords. Update appropriate code if changed. + uiMsg(UIWAIT_BADPSW,FileName,FileName); + Cmd->Password.Clean(); + } + +#ifdef RARDLL + // Avoid new requests for unrar.dll to prevent the infinite loop + // if app always returns the same password. + ErrHandler.SetErrorCode(RARX_BADPWD); + Cmd->DllError=ERAR_BAD_PASSWORD; + ErrHandler.Exit(RARX_BADPWD); +#else + continue; // Request a password again. +#endif + } + break; + } + + Raw.SetCrypt(&HeadersCrypt); #endif - BrokenFileHeader=true; - ErrHandler.SetErrorCode(CRC_ERROR); - return(0); } - return(Raw.Size()); + + // Header size must not occupy more than 3 variable length integer bytes + // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5), + // so here we read 4 byte CRC32 followed by 3 bytes or less of header size. + const size_t FirstReadSize=7; // Smallest possible block size. + if (Raw.Read(FirstReadSize)=ShortBlock.HeadSize) + { + BrokenHeaderMsg(); + return 0; + } + } + + uint64 DataSize=0; + if ((ShortBlock.Flags & HFL_DATA)!=0) + DataSize=Raw.GetV(); + + NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); + // Set to 0 in case of overflow, so end of ReadHeader cares about it. + NextBlockPos=SafeAdd(NextBlockPos,DataSize,0); + + switch(ShortBlock.HeaderType) + { + case HEAD_CRYPT: + { + *(BaseBlock *)&CryptHead=ShortBlock; + uint CryptVersion=(uint)Raw.GetV(); + if (CryptVersion>CRYPT_VERSION) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"h%u",CryptVersion); + UnkEncVerMsg(FileName,Info); + return 0; + } + uint EncFlags=(uint)Raw.GetV(); + CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0; + CryptHead.Lg2Count=Raw.Get1(); + if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count); + UnkEncVerMsg(FileName,Info); + return 0; + } + + Raw.GetB(CryptHead.Salt,SIZE_SALT50); + if (CryptHead.UsePswCheck) + { + Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK); + + byte csum[SIZE_PSWCHECK_CSUM]; + Raw.GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + } + Encrypted=true; + } + break; + case HEAD_MAIN: + { + MainHead.Reset(); + *(BaseBlock *)&MainHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + + Volume=(ArcFlags & MHFL_VOLUME)!=0; + Solid=(ArcFlags & MHFL_SOLID)!=0; + Locked=(ArcFlags & MHFL_LOCK)!=0; + Protected=(ArcFlags & MHFL_PROTECT)!=0; + Signed=false; + NewNumbering=true; + + if ((ArcFlags & MHFL_VOLNUMBER)!=0) + VolNumber=(uint)Raw.GetV(); + else + VolNumber=0; + FirstVolume=Volume && VolNumber==0; + + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead); + +#ifdef USE_QOPEN + if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) + { + // We seek to QO block in the end of archive when processing + // QOpen.Load, so we need to preserve current block positions + // to not break normal archive processing by calling function. + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; + + QOpen.Init(this,false); + QOpen.Load(MainHead.QOpenOffset); + + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + } +#endif + } + break; + case HEAD_FILE: + case HEAD_SERVICE: + { + FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead; + hd->Reset(); + *(BaseBlock *)hd=ShortBlock; + + bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; + + hd->LargeFile=true; + + hd->PackSize=DataSize; + hd->FileFlags=(uint)Raw.GetV(); + hd->UnpSize=Raw.GetV(); + + hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0; + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; + + hd->MaxSize=Max(hd->PackSize,hd->UnpSize); + hd->FileAttr=(uint)Raw.GetV(); + if ((hd->FileFlags & FHFL_UTIME)!=0) + hd->mtime.SetUnix((time_t)Raw.Get4()); + + hd->FileHash.Type=HASH_NONE; + if ((hd->FileFlags & FHFL_CRC32)!=0) + { + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + } + + hd->RedirType=FSREDIR_NONE; + + uint CompInfo=(uint)Raw.GetV(); + hd->Method=(CompInfo>>7) & 7; + + // "+ 50" to not mix with old RAR format algorithms. For example, + // we may need to use the compression algorithm 15 in the future, + // but it was already used in RAR 1.5 and Unpack needs to distinguish + // them. + hd->UnpVer=(CompInfo & 0x3f) + 50; + if (hd->UnpVer!=50) // Only 5.0 compression is known now. + hd->UnpVer=VER_UNKNOWN; + + hd->HostOS=(byte)Raw.GetV(); + size_t NameSize=(size_t)Raw.GetV(); + hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0; + + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST5_UNIX) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOS==HOST5_WINDOWS) + hd->HSType=HSYS_WINDOWS; + + hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0; + hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0; + hd->SubBlock=(hd->Flags & HFL_CHILD)!=0; + hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0; + hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; + hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); + + hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE; + + char FileName[NM*4]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + + UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Should do it before converting names, because extra fields can + // affect name processing, like in case of NTFS streams. + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,hd); + + if (FileBlock) + { +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); +#endif + ConvertFileHeader(hd); + } + + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + +#if 0 + // For RAR5 format we read the user specified recovery percent here. + // It would be useful to do it for shell extension too, so we display + // the correct recovery record size in archive properties. But then + // we would need to include the entire recovery record processing + // code to shell extension, which is not done now. + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0) + { + RecoveryPercent=hd->SubData[0]; + RSBlockHeader Header; + GetRRInfo(this,&Header); + RecoverySize=Header.RecSectionSize*Header.RecCount; + } +#endif + + if (BadCRC) // Add the file name to broken header message displayed above. + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); + } + break; + case HEAD_ENDARC: + { + *(BaseBlock *)&EndArcHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0; + EndArcHead.StoreVolNumber=false; + EndArcHead.DataCRC=false; + EndArcHead.RevSpace=false; + } + break; + } + + return Raw.Size(); +} + + +#if !defined(RAR_NOCRYPT) +void Archive::RequestArcPassword() +{ + if (!Cmd->Password.IsSet()) + { +#ifdef RARDLL + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + } + if (!Cmd->Password.IsSet()) + { + Close(); + Cmd->DllError=ERAR_MISSING_PASSWORD; + ErrHandler.Exit(RARX_USERBREAK); + } +#else + if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password)) + { + Close(); + uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. + ErrHandler.Exit(RARX_USERBREAK); + } +#endif + Cmd->ManualPassword=true; + } +} +#endif + + +void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb) +{ + // Read extra data from the end of block skipping any fields before it. + size_t ExtraStart=Raw->Size()-ExtraSize; + if (ExtraStartGetPos()) + return; + Raw->SetPos(ExtraStart); + while (Raw->DataLeft()>=2) + { + int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative. + if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft()) + break; + size_t NextPos=size_t(Raw->GetPos()+FieldSize); + uint64 FieldType=Raw->GetV(); + + FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields. + + if (FieldSize<0) // FieldType is longer than expected extra field size. + break; + + if (bb->HeaderType==HEAD_MAIN) + { + MainHeader *hd=(MainHeader *)bb; + if (FieldType==MHEXTRA_LOCATOR) + { + hd->Locator=true; + uint Flags=(uint)Raw->GetV(); + if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->QOpenOffset=Offset+CurBlockPos; + } + if ((Flags & MHEXTRA_LOCATOR_RR)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->RROffset=Offset+CurBlockPos; + } + } + } + + if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE) + { + FileHeader *hd=(FileHeader *)bb; + switch(FieldType) + { + case FHEXTRA_CRYPT: + { + FileHeader *hd=(FileHeader *)bb; + uint EncVersion=(uint)Raw->GetV(); + if (EncVersion>CRYPT_VERSION) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"x%u",EncVersion); + UnkEncVerMsg(hd->FileName,Info); + } + else + { + uint Flags=(uint)Raw->GetV(); + hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0; + hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; + hd->Lg2Count=Raw->Get1(); + if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count); + UnkEncVerMsg(hd->FileName,Info); + } + Raw->GetB(hd->Salt,SIZE_SALT50); + Raw->GetB(hd->InitV,SIZE_INITV); + if (hd->UsePswCheck) + { + Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); + + // It is important to know if password check data is valid. + // If it is damaged and header CRC32 fails to detect it, + // archiver would refuse to decompress a possibly valid file. + // Since we want to be sure distinguishing a wrong password + // or corrupt file data, we use 64-bit password check data + // and to control its validity we use 32 bits of password + // check data SHA-256 additionally to 32-bit header CRC32. + byte csum[SIZE_PSWCHECK_CSUM]; + Raw->GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + + // RAR 5.21 and earlier set PswCheck field in service records to 0 + // even if UsePswCheck was present. + if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) + hd->UsePswCheck=0; + } + hd->SaltSet=true; + hd->CryptMethod=CRYPT_RAR50; + hd->Encrypted=true; + } + } + break; + case FHEXTRA_HASH: + { + FileHeader *hd=(FileHeader *)bb; + uint Type=(uint)Raw->GetV(); + if (Type==FHEXTRA_HASH_BLAKE2) + { + hd->FileHash.Type=HASH_BLAKE2; + Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + } + } + break; + case FHEXTRA_HTIME: + if (FieldSize>=5) + { + byte Flags=(byte)Raw->GetV(); + bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0) + if (UnixTime) + hd->mtime.SetUnix(Raw->Get4()); + else + hd->mtime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0) + if (UnixTime) + hd->ctime.SetUnix(Raw->Get4()); + else + hd->ctime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0) + if (UnixTime) + hd->atime.SetUnix((time_t)Raw->Get4()); + else + hd->atime.SetWin(Raw->Get8()); + if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds. + { + uint ns; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->mtime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->ctime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->atime.Adjust(ns); + } + } + break; + case FHEXTRA_VERSION: + if (FieldSize>=1) + { + Raw->GetV(); // Skip flags field. + uint Version=(uint)Raw->GetV(); + if (Version!=0) + { + hd->Version=true; + + wchar VerText[20]; + swprintf(VerText,ASIZE(VerText),L";%u",Version); + wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName)); + } + } + break; + case FHEXTRA_REDIR: + { + hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); + uint Flags=(uint)Raw->GetV(); + hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; + size_t NameSize=(size_t)Raw->GetV(); + + char UtfName[NM*4]; + *UtfName=0; + if (NameSizeGetB(UtfName,NameSize); + UtfName[NameSize]=0; + } +#ifdef _WIN_ALL + UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName)); +#endif + UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName)); + } + break; + case FHEXTRA_UOWNER: + { + uint Flags=(uint)Raw->GetV(); + hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0; + hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0; + *hd->UnixOwnerName=*hd->UnixGroupName=0; + if ((Flags & FHEXTRA_UOWNER_UNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixOwnerName)-1); + Raw->GetB(hd->UnixOwnerName,Length); + hd->UnixOwnerName[Length]=0; + } + if ((Flags & FHEXTRA_UOWNER_GNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixGroupName)-1); + Raw->GetB(hd->UnixGroupName,Length); + hd->UnixGroupName[Length]=0; + } +#ifdef _UNIX + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uid_t)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(gid_t)Raw->GetV(); +#else + // Need these fields in Windows too for 'list' command, + // but uid_t and gid_t are not defined. + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uint)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(uint)Raw->GetV(); +#endif + hd->UnixOwnerSet=true; + } + break; + case FHEXTRA_SUBDATA: + { + // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than + // required. It did not hurt extraction, because UnRAR 5.21 + // and earlier ignored this field and set FieldSize as data left + // in entire extra area. But now we set the correct field size + // and set FieldSize based on the actual extra record size, + // so we need to adjust it for those older archives here. + // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE + // and always is last in extra area. So since its size is by 1 + // less than needed, we always have 1 byte left in extra area, + // which fact we use here to detect such archives. + if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1) + FieldSize++; + + // We cannot allocate too much memory here, because above + // we check FieldSize againt Raw size and we control that Raw size + // is sensible when reading headers. + hd->SubData.Alloc((size_t)FieldSize); + Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize); + } + break; + } + } + + Raw->SetPos(NextPos); + } } #ifndef SFX_MODULE -size_t Archive::ReadOldHeader() +size_t Archive::ReadHeader14() { RawRead Raw(this); if (CurBlockPos<=(int64)SFXSize) { - Raw.Read(SIZEOF_OLDMHD); - Raw.Get(OldMhd.Mark,4); - Raw.Get(OldMhd.HeadSize); - Raw.Get(OldMhd.Flags); - NextBlockPos=CurBlockPos+OldMhd.HeadSize; - CurHeaderType=MAIN_HEAD; + Raw.Read(SIZEOF_MAINHEAD14); + MainHead.Reset(); + byte Mark[4]; + Raw.GetB(Mark,4); + uint HeadSize=Raw.Get2(); + if (HeadSize<7) + return false; + byte Flags=Raw.Get1(); + NextBlockPos=CurBlockPos+HeadSize; + CurHeaderType=HEAD_MAIN; + + Volume=(Flags & MHD_VOLUME)!=0; + Solid=(Flags & MHD_SOLID)!=0; + Locked=(Flags & MHD_LOCK)!=0; + MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0; + MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0; } else { - OldFileHeader OldLhd; - Raw.Read(SIZEOF_OLDLHD); - NewLhd.HeadType=FILE_HEAD; - Raw.Get(NewLhd.PackSize); - Raw.Get(NewLhd.UnpSize); - Raw.Get(OldLhd.FileCRC); - Raw.Get(NewLhd.HeadSize); - Raw.Get(NewLhd.FileTime); - Raw.Get(OldLhd.FileAttr); - Raw.Get(OldLhd.Flags); - Raw.Get(OldLhd.UnpVer); - Raw.Get(OldLhd.NameSize); - Raw.Get(OldLhd.Method); + Raw.Read(SIZEOF_FILEHEAD14); + FileHead.Reset(); - NewLhd.Flags=OldLhd.Flags|LONG_BLOCK; - NewLhd.UnpVer=(OldLhd.UnpVer==2) ? 13 : 10; - NewLhd.Method=OldLhd.Method+0x30; - NewLhd.NameSize=OldLhd.NameSize; - NewLhd.FileAttr=OldLhd.FileAttr; - NewLhd.FileCRC=OldLhd.FileCRC; - NewLhd.FullPackSize=NewLhd.PackSize; - NewLhd.FullUnpSize=NewLhd.UnpSize; + FileHead.HeaderType=HEAD_FILE; + FileHead.DataSize=Raw.Get4(); + FileHead.UnpSize=Raw.Get4(); + FileHead.FileHash.Type=HASH_RAR14; + FileHead.FileHash.CRC32=Raw.Get2(); + FileHead.HeadSize=Raw.Get2(); + if (FileHead.HeadSize<21) + return false; + uint FileTime=Raw.Get4(); + FileHead.FileAttr=Raw.Get1(); + FileHead.Flags=Raw.Get1()|LONG_BLOCK; + FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10; + size_t NameSize=Raw.Get1(); + FileHead.Method=Raw.Get1(); - NewLhd.mtime.SetDos(NewLhd.FileTime); - NewLhd.ctime.Reset(); - NewLhd.atime.Reset(); - NewLhd.arctime.Reset(); + FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0; + FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0; + FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0; + FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE; - Raw.Read(OldLhd.NameSize); - Raw.Get((byte *)NewLhd.FileName,OldLhd.NameSize); - NewLhd.FileName[OldLhd.NameSize]=0; - ConvertNameCase(NewLhd.FileName); - *NewLhd.FileNameW=0; + FileHead.PackSize=FileHead.DataSize; + FileHead.WinSize=0x10000; + FileHead.Dir=(FileHead.FileAttr & 0x10)!=0; + + FileHead.HostOS=HOST_MSDOS; + FileHead.HSType=HSYS_WINDOWS; + + FileHead.mtime.SetDos(FileTime); + + Raw.Read(NameSize); + + char FileName[NM]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + IntToExt(FileName,FileName,ASIZE(FileName)); + CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName)); + ConvertNameCase(FileHead.FileName); + ConvertFileHeader(&FileHead); if (Raw.Size()!=0) - NextBlockPos=CurBlockPos+NewLhd.HeadSize+NewLhd.PackSize; - CurHeaderType=FILE_HEAD; + NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize; + CurHeaderType=HEAD_FILE; } - return(NextBlockPos>CurBlockPos ? Raw.Size():0); + return NextBlockPos>CurBlockPos ? Raw.Size() : 0; } #endif -void Archive::ConvertNameCase(char *Name) -{ - if (Cmd->ConvertNames==NAMES_UPPERCASE) - { - IntToExt(Name,Name); - strupper(Name); - ExtToInt(Name,Name); - } - if (Cmd->ConvertNames==NAMES_LOWERCASE) - { - IntToExt(Name,Name); - strlower(Name); - ExtToInt(Name,Name); - } -} - - #ifndef SFX_MODULE void Archive::ConvertNameCase(wchar *Name) { if (Cmd->ConvertNames==NAMES_UPPERCASE) - strupperw(Name); + wcsupper(Name); if (Cmd->ConvertNames==NAMES_LOWERCASE) - strlowerw(Name); + wcslower(Name); } #endif bool Archive::IsArcDir() { - return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY); -} - - -bool Archive::IsArcLabel() -{ - return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8)); + return FileHead.Dir; } void Archive::ConvertAttributes() { -#if defined(_WIN_32) || defined(_EMX) - switch(NewLhd.HostOS) - { - case HOST_MSDOS: - case HOST_OS2: - case HOST_WIN32: - break; - case HOST_UNIX: - case HOST_BEOS: - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x10; - else - NewLhd.FileAttr=0x20; - break; - default: - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x10; - else - NewLhd.FileAttr=0x20; - break; - } +#if defined(_WIN_ALL) || defined(_EMX) + if (FileHead.HSType!=HSYS_WINDOWS) + FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20; #endif #ifdef _UNIX // umask defines which permission bits must not be set by default - // when creating a file or directory. + // when creating a file or directory. The typical default value + // for the process umask is S_IWGRP | S_IWOTH (octal 022), + // resulting in 0644 mode for new files. + // Normally umask is applied automatically when creating a file, + // but we set attributes with chmod later, so we need to calculate + // resulting attributes here. We do it only for non-Unix archives. + // We restore native Unix attributes as is, because it can be backup. static mode_t mask = (mode_t) -1; if (mask == (mode_t) -1) { - // umask call returns the current umask value. Argument (022) is not - // important here. + // umask call returns the current umask value. Argument (022) is not + // really important here. mask = umask(022); // Restore the original umask value, which was changed to 022 above. umask(mask); } - switch(NewLhd.HostOS) + switch(FileHead.HSType) { - case HOST_MSDOS: - case HOST_OS2: - case HOST_WIN32: + case HSYS_WINDOWS: { // Mapping MSDOS, OS/2 and Windows file attributes to Unix. - if (NewLhd.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY + if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY { // For directories we use 0777 mask. - NewLhd.FileAttr=0777 & ~mask; + FileHead.FileAttr=0777 & ~mask; } else - if (NewLhd.FileAttr & 1) // FILE_ATTRIBUTE_READONLY + if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY { // For read only files we use 0444 mask with 'w' bits turned off. - NewLhd.FileAttr=0444 & ~mask; + FileHead.FileAttr=0444 & ~mask; } else { // umask does not set +x for regular files, so we use 0666 // instead of 0777 as for directories. - NewLhd.FileAttr=0666 & ~mask; + FileHead.FileAttr=0666 & ~mask; } } break; - case HOST_UNIX: - case HOST_BEOS: + case HSYS_UNIX: break; default: - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x41ff & ~mask; + if (FileHead.Dir) + FileHead.FileAttr=0x41ff & ~mask; else - NewLhd.FileAttr=0x81b6 & ~mask; + FileHead.FileAttr=0x81b6 & ~mask; break; } #endif } -void Archive::ConvertUnknownHeader() +void Archive::ConvertFileHeader(FileHeader *hd) { - if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10)) - NewLhd.Flags|=LHD_DIRECTORY; - if (NewLhd.HostOS>=HOST_MAX) - { - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x10; + if (hd->HSType==HSYS_UNKNOWN) + if (hd->Dir) + hd->FileAttr=0x10; else - NewLhd.FileAttr=0x20; - } - for (char *s=NewLhd.FileName;*s!=0;s=charnext(s)) + hd->FileAttr=0x20; + +#ifdef _WIN_ALL + if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. + ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName)); +#endif + + for (wchar *s=hd->FileName;*s!=0;s++) { - if (*s=='/' || *s=='\\') - *s=CPATHDIVIDER; -#if defined(_APPLE) && !defined(UNICODE_SUPPORTED) - if ((byte)*s<32 || (byte)*s>127) +#ifdef _UNIX + // Backslash is the invalid character for Windows file headers, + // but it can present in Unix file names extracted in Unix. + if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS) *s='_'; #endif -#if defined(_WIN_32) || defined(_EMX) +#if defined(_WIN_ALL) || defined(_EMX) + // RAR 5.0 archives do not use '\' as path separator, so if we see it, + // it means that it is a part of Unix file name, which we cannot + // extract in Windows. + if (*s=='\\' && Format==RARFMT50) + *s='_'; + // ':' in file names is allowed in Unix, but not in Windows. // Even worse, file data will be written to NTFS stream on NTFS, - // so automatic name correction on file create error in extraction - // routine does not work. In Windows and DOS versions we better + // so automatic name correction on file create error in extraction + // routine does not work. In Windows and DOS versions we better // replace ':' now. if (*s==':') *s='_'; #endif - } - - for (wchar *s=NewLhd.FileNameW;*s!=0;s++) - { - if (*s=='/' || *s=='\\') + // This code must be performed only after other path separator checks, + // because it produces backslashes illegal for some of checks above. + // Backslash is allowed in file names in Unix, but not in Windows. + // Still, RAR 4.x uses backslashes as path separator even in Unix. + // Forward slash is not allowed in both systems. In RAR 5.0 we use + // the forward slash as universal path separator. + if (*s=='/' || *s=='\\' && Format!=RARFMT50) *s=CPATHDIVIDER; - -#if defined(_WIN_32) || defined(_EMX) - // ':' in file names is allowed in Unix, but not in Windows. - // Even worse, file data will be written to NTFS stream on NTFS, - // so automatic name correction on file create error in extraction - // routine does not work. In Windows and DOS versions we better - // replace ':' now. - if (*s==':') - *s='_'; -#endif } } -#ifndef SHELL_EXT -bool Archive::ReadSubData(Array *UnpData,File *DestFile) +int64 Archive::GetStartPos() { - if (HeaderCRC!=SubHead.HeadCRC) + int64 StartPos=SFXSize+MarkHead.HeadSize; + if (Format==RARFMT15) + StartPos+=MainHead.HeadSize; + else // RAR 5.0. + StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize); + return StartPos; +} + + +bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode) +{ + if (BrokenHeader) { -#ifndef SHELL_EXT - Log(FileName,St(MSubHeadCorrupt)); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); - return(false); + uiMsg(UIERROR_SUBHEADERBROKEN,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return false; } - if (SubHead.Method<0x30 || SubHead.Method>0x35 || SubHead.UnpVer>/*PACK_VER*/36) + if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK)) { -#ifndef SHELL_EXT - Log(FileName,St(MSubHeadUnknown)); -#endif - return(false); + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; } - if (SubHead.PackSize==0 && (SubHead.Flags & LHD_SPLIT_AFTER)==0) - return(true); + if (SubHead.PackSize==0 && !SubHead.SplitAfter) + return true; SubDataIO.Init(); Unpack Unpack(&SubDataIO); - Unpack.Init(); + Unpack.Init(SubHead.WinSize,false); if (DestFile==NULL) { - UnpData->Alloc(SubHead.UnpSize); - SubDataIO.SetUnpackToMemory(&(*UnpData)[0],SubHead.UnpSize); - } - if (SubHead.Flags & LHD_PASSWORD) - if (*Cmd->Password) - SubDataIO.SetEncryption(SubHead.UnpVer,Cmd->Password, - (SubHead.Flags & LHD_SALT) ? SubHead.Salt:NULL,false, - SubHead.UnpVer>=36); + if (SubHead.UnpSize>0x1000000) + { + // So huge allocation must never happen in valid archives. + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; + } + if (UnpData==NULL) + SubDataIO.SetTestMode(true); else - return(false); + { + UnpData->Alloc((size_t)SubHead.UnpSize); + SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize); + } + } + if (SubHead.Encrypted) + if (Cmd->Password.IsSet()) + SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password, + SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV, + SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck); + else + return false; + SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1); SubDataIO.SetPackedSizeToRead(SubHead.PackSize); SubDataIO.EnableShowProgress(false); SubDataIO.SetFiles(this,DestFile); - SubDataIO.UnpVolume=(SubHead.Flags & LHD_SPLIT_AFTER)!=0; + SubDataIO.SetTestMode(TestMode); + SubDataIO.UnpVolume=SubHead.SplitAfter; SubDataIO.SetSubHeader(&SubHead,NULL); Unpack.SetDestSize(SubHead.UnpSize); - if (SubHead.Method==0x30) + if (SubHead.Method==0) CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize); else Unpack.DoUnpack(SubHead.UnpVer,false); - if (SubHead.FileCRC!=~SubDataIO.UnpFileCRC) + if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL)) { -#ifndef SHELL_EXT - Log(FileName,St(MSubHeadDataCRC),SubHead.FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName); + ErrHandler.SetErrorCode(RARX_CRC); if (UnpData!=NULL) UnpData->Reset(); - return(false); + return false; } - return(true); + return true; } -#endif diff --git a/libunrar/array.hpp b/libunrar/array.hpp index c67817a..20d258d 100644 --- a/libunrar/array.hpp +++ b/libunrar/array.hpp @@ -9,26 +9,39 @@ template class Array T *Buffer; size_t BufSize; size_t AllocSize; + size_t MaxSize; + bool Secure; // Clean memory if true. public: Array(); Array(size_t Size); + Array(const Array &Src); // Copy constructor. ~Array(); inline void CleanData(); - inline T& operator [](size_t Item); - inline size_t Size(); + inline T& operator [](size_t Item) const; + inline T* operator + (size_t Pos); + inline size_t Size(); // Returns the size in items, not in bytes. void Add(size_t Items); void Alloc(size_t Items); void Reset(); + void SoftReset(); void operator = (Array &Src); void Push(T Item); - T* Addr() {return(Buffer);} + void Append(T *Item,size_t Count); + T* Addr(size_t Item) {return Buffer+Item;} + void SetMaxSize(size_t Size) {MaxSize=Size;} + T* Begin() {return Buffer;} + T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} + void SetSecure() {Secure=true;} }; + template void Array::CleanData() { Buffer=NULL; BufSize=0; AllocSize=0; + MaxSize=0; + Secure=false; } @@ -40,30 +53,47 @@ template Array::Array() template Array::Array(size_t Size) { - Buffer=(T *)rarmalloc(sizeof(T)*Size); - if (Buffer==NULL && Size!=0) - ErrHandler.MemoryError(); + CleanData(); + Add(Size); +} - AllocSize=BufSize=Size; + +// Copy constructor in case we need to pass an object as value. +template Array::Array(const Array &Src) +{ + CleanData(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); } template Array::~Array() { if (Buffer!=NULL) - rarfree(Buffer); + { + if (Secure) + cleandata(Buffer,AllocSize*sizeof(T)); + free(Buffer); + } } -template inline T& Array::operator [](size_t Item) +template inline T& Array::operator [](size_t Item) const { - return(Buffer[Item]); + return Buffer[Item]; +} + + +template inline T* Array::operator +(size_t Pos) +{ + return Buffer+Pos; } template inline size_t Array::Size() { - return(BufSize); + return BufSize; } @@ -72,12 +102,35 @@ template void Array::Add(size_t Items) BufSize+=Items; if (BufSize>AllocSize) { + if (MaxSize!=0 && BufSize>MaxSize) + { + ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize); + ErrHandler.MemoryError(); + } + size_t Suggested=AllocSize+AllocSize/4+32; size_t NewSize=Max(BufSize,Suggested); - Buffer=(T *)rarrealloc(Buffer,NewSize*sizeof(T)); - if (Buffer==NULL) - ErrHandler.MemoryError(); + T *NewBuffer; + if (Secure) + { + NewBuffer=(T *)malloc(NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); + if (Buffer!=NULL) + { + memcpy(NewBuffer,Buffer,AllocSize*sizeof(T)); + cleandata(Buffer,AllocSize*sizeof(T)); + free(Buffer); + } + } + else + { + NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); + } + Buffer=NewBuffer; AllocSize=NewSize; } } @@ -96,7 +149,7 @@ template void Array::Reset() { if (Buffer!=NULL) { - rarfree(Buffer); + free(Buffer); Buffer=NULL; } BufSize=0; @@ -104,6 +157,14 @@ template void Array::Reset() } +// Reset buffer size, but preserve already allocated memory if any, +// so we can reuse it without wasting time to allocation. +template void Array::SoftReset() +{ + BufSize=0; +} + + template void Array::operator =(Array &Src) { Reset(); @@ -119,4 +180,12 @@ template void Array::Push(T Item) (*this)[Size()-1]=Item; } + +template void Array::Append(T *Items,size_t Count) +{ + size_t CurSize=Size(); + Add(Count); + memcpy(Buffer+CurSize,Items,Count*sizeof(T)); +} + #endif diff --git a/libunrar/beosea.cpp b/libunrar/beosea.cpp deleted file mode 100644 index 86eb7d4..0000000 --- a/libunrar/beosea.cpp +++ /dev/null @@ -1,113 +0,0 @@ - - -void ExtractBeEA(Archive &Arc,char *FileName) -{ - if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER) - { - Log(Arc.FileName,St(MEAUnknHeader),FileName); - return; - } - - ComprDataIO DataIO; - Unpack Unpack(&DataIO); - Unpack.Init(); - - Array UnpData(Arc.EAHead.UnpSize); - DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); - DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); - DataIO.EnableShowProgress(false); - DataIO.SetFiles(&Arc,NULL); - Unpack.SetDestSize(Arc.EAHead.UnpSize); - Unpack.DoUnpack(Arc.EAHead.UnpVer,false); - - if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - int fd = open(FileName,O_WRONLY); - if (fd==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - return; - } - - int AttrPos=0; - while (AttrPos=sizeof(Name)) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - memcpy(Name,CurItem+10,NameSize); - Name[NameSize]=0; - if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - AttrPos+=10+NameSize+Size; - } - close(fd); - mprintf(St(MShowEA)); -} - - -void ExtractBeEANew(Archive &Arc,char *FileName) -{ - Array SubData; - if (!Arc.ReadSubData(&SubData,NULL)) - return; - - int fd = open(FileName,O_WRONLY); - if (fd==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - return; - } - - int AttrPos=0; - while (AttrPos=sizeof(Name)) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - memcpy(Name,CurItem+10,NameSize); - Name[NameSize]=0; - if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - AttrPos+=10+NameSize+Size; - } - close(fd); - mprintf(St(MShowEA)); -} - diff --git a/libunrar/blake2s.cpp b/libunrar/blake2s.cpp new file mode 100644 index 0000000..317603d --- /dev/null +++ b/libunrar/blake2s.cpp @@ -0,0 +1,183 @@ +// Based on public domain code written in 2012 by Samuel Neves + +#include "rar.hpp" + +#ifdef USE_SSE +#include "blake2s_sse.cpp" +#endif + +static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth); +static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ); +static void blake2s_final( blake2s_state *S, byte *digest ); + +#include "blake2sp.cpp" + +static const uint32 blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +static const byte blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static inline void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = ~0U; +} + + +/* Some helper functions, not necessarily useful */ +static inline void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = ~0U; +} + + +static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + + +/* init2 xors IV with input parameter block */ +void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth) +{ +#ifdef USE_SSE + if (_SSE_Version>=SSE_SSE2) + blake2s_init_sse(); +#endif + + S->init(); // Clean data. + for( int i = 0; i < 8; ++i ) + S->h[i] = blake2s_IV[i]; + + S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block. + S->h[2] ^= node_offset; + S->h[3] ^= (node_depth<<16)|0x20000000; +} + + +#define G(r,i,m,a,b,c,d) \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); + + +static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + uint32 m[16]; + uint32 v[16]; + + for( size_t i = 0; i < 16; ++i ) + m[i] = RawGet4( block + i * 4 ); + + for( size_t i = 0; i < 8; ++i ) + v[i] = S->h[i]; + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows. + { + G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]); + G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]); + G(r,2,m,v[ 2],v[ 6],v[10],v[14]); + G(r,3,m,v[ 3],v[ 7],v[11],v[15]); + G(r,4,m,v[ 0],v[ 5],v[10],v[15]); + G(r,5,m,v[ 1],v[ 6],v[11],v[12]); + G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]); + G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]); + } + + for( size_t i = 0; i < 8; ++i ) + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; +} + + +void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ) +{ + while( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = 2 * BLAKE2S_BLOCKBYTES - left; + + if( inlen > fill ) + { + memcpy( S->buf + left, in, fill ); // Fill buffer + S->buflen += fill; + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + +#ifdef USE_SSE +#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode. + if (_SSE_Version>=SSE_SSE2) +#else + if (_SSE_Version>=SSE_SSSE3) +#endif + blake2s_compress_sse( S, S->buf ); + else + blake2s_compress( S, S->buf ); // Compress +#else + blake2s_compress( S, S->buf ); // Compress +#endif + + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left + S->buflen -= BLAKE2S_BLOCKBYTES; + in += fill; + inlen -= fill; + } + else // inlen <= fill + { + memcpy( S->buf + left, in, (size_t)inlen ); + S->buflen += (size_t)inlen; // Be lazy, do not compress + in += inlen; + inlen = 0; + } + } +} + + +void blake2s_final( blake2s_state *S, byte *digest ) +{ + if( S->buflen > BLAKE2S_BLOCKBYTES ) + { + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); + S->buflen -= BLAKE2S_BLOCKBYTES; + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen ); + } + + blake2s_increment_counter( S, ( uint32 )S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( int i = 0; i < 8; ++i ) /* Output full hash */ + RawPut4( S->h[i], digest + 4 * i ); +} + diff --git a/libunrar/blake2s.hpp b/libunrar/blake2s.hpp new file mode 100644 index 0000000..f88ef37 --- /dev/null +++ b/libunrar/blake2s.hpp @@ -0,0 +1,102 @@ +// Based on public domain code written in 2012 by Samuel Neves +#ifndef _RAR_BLAKE2_ +#define _RAR_BLAKE2_ + +#define BLAKE2_DIGEST_SIZE 32 +#define BLAKE2_THREADS_NUMBER 8 + +enum blake2s_constant +{ + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32 +}; + + +// Alignment to 64 improves performance of both SSE and non-SSE versions. +// Alignment to n*16 is required for SSE version, so we selected 64. +// We use the custom alignment scheme instead of __declspec(align(x)), +// because it is less compiler dependent. Also the compiler directive +// does not help if structure is a member of class allocated through +// 'new' operator. +struct blake2s_state +{ + enum { BLAKE_ALIGNMENT = 64 }; + + // buffer and uint32 h[8], t[2], f[2]; + enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES }; + + byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT]; + + byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES]. + uint32 *h, *t, *f; // uint32 h[8], t[2], f[2]. + + size_t buflen; + byte last_node; + + blake2s_state() + { + set_pointers(); + } + + // Required when we declare and assign in the same command. + blake2s_state(blake2s_state &st) + { + set_pointers(); + *this=st; + } + + void set_pointers() + { + // Set aligned pointers. Must be done in constructor, not in Init(), + // so assignments like 'blake2sp_state res=blake2ctx' work correctly + // even if blake2sp_init is not called for 'res'. + buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT); + h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES); + t = h + 8; + f = t + 2; + } + + void init() + { + memset( ubuf, 0, sizeof( ubuf ) ); + buflen = 0; + last_node = 0; + } + + // Since we use pointers, the default = would work incorrectly. + blake2s_state& operator = (blake2s_state &st) + { + if (this != &st) + { + memcpy(buf, st.buf, BLAKE_DATA_SIZE); + buflen = st.buflen; + last_node = st.last_node; + } + return *this; + } +}; + + +#ifdef RAR_SMP +class ThreadPool; +#endif + +struct blake2sp_state +{ + blake2s_state S[8]; + blake2s_state R; + byte buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + +#ifdef RAR_SMP + ThreadPool *ThPool; + uint MaxThreads; +#endif +}; + +void blake2sp_init( blake2sp_state *S ); +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ); +void blake2sp_final( blake2sp_state *S, byte *digest ); + +#endif + diff --git a/libunrar/blake2s_sse.cpp b/libunrar/blake2s_sse.cpp new file mode 100644 index 0000000..1a02f21 --- /dev/null +++ b/libunrar/blake2s_sse.cpp @@ -0,0 +1,129 @@ +// Based on public domain code written in 2012 by Samuel Neves + +extern const byte blake2s_sigma[10][16]; + +// Initialization vector. +static __m128i blake2s_IV_0_3, blake2s_IV_4_7; + +#ifdef _WIN_64 +// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro. +static __m128i crotr8, crotr16; +#endif + +static void blake2s_init_sse() +{ + // We cannot initialize these 128 bit variables in place when declaring + // them globally, because global scope initialization is performed before + // our SSE check and it would make code incompatible with older non-SSE2 + // CPUs. Also we cannot initialize them as static inside of function + // using these variables, because SSE static initialization is not thread + // safe: first thread starts initialization and sets "init done" flag even + // if it is not done yet, second thread can attempt to access half-init + // SSE data. So we moved init code here. + + blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A ); + blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ); + +#ifdef _WIN_64 + crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); + crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); +#endif +} + + +#define LOAD(p) _mm_load_si128( (__m128i *)(p) ) +#define STORE(p,r) _mm_store_si128((__m128i *)(p), r) + +#ifdef _WIN_32 +// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient +// to not use _mm_shuffle_epi8 here. +#define mm_rotr_epi32(r, c) ( \ + _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#else +#define mm_rotr_epi32(r, c) ( \ + c==8 ? _mm_shuffle_epi8(r,crotr8) \ + : c==16 ? _mm_shuffle_epi8(r,crotr16) \ + : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#endif + + +#define G1(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 16); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 12); + +#define G2(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 8); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 7); + +#define DIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) ); + +#define UNDIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) ); + +#ifdef _WIN_64 + // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load + // from stack operations, which are slower than this code. + #define _mm_set_epi32(i3,i2,i1,i0) \ + _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \ + _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3))) +#endif + +// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode +// and about the same in x64 mode in our test. Perhaps depends on compiler. +// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather +// instructions here, but they did not show any speed gain on i7-6700K. +#define SSE_ROUND(m,row,r) \ +{ \ + __m128i buf; \ + buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + DIAGONALIZE(row[0],row[1],row[2],row[3]); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \ +} + + +static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + __m128i row[4]; + __m128i ff0, ff1; + + const uint32 *m = ( uint32 * )block; + + row[0] = ff0 = LOAD( &S->h[0] ); + row[1] = ff1 = LOAD( &S->h[4] ); + + row[2] = blake2s_IV_0_3; + row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) ); + SSE_ROUND( m, row, 0 ); + SSE_ROUND( m, row, 1 ); + SSE_ROUND( m, row, 2 ); + SSE_ROUND( m, row, 3 ); + SSE_ROUND( m, row, 4 ); + SSE_ROUND( m, row, 5 ); + SSE_ROUND( m, row, 6 ); + SSE_ROUND( m, row, 7 ); + SSE_ROUND( m, row, 8 ); + SSE_ROUND( m, row, 9 ); + STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) ); + STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) ); + return 0; +} diff --git a/libunrar/blake2sp.cpp b/libunrar/blake2sp.cpp new file mode 100644 index 0000000..da64588 --- /dev/null +++ b/libunrar/blake2sp.cpp @@ -0,0 +1,153 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Written in 2012 by Samuel Neves + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along with + this software. If not, see . +*/ + +#define PARALLELISM_DEGREE 8 + +void blake2sp_init( blake2sp_state *S ) +{ + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + + blake2s_init_param( &S->R, 0, 1 ); // Init root. + + for( uint i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_init_param( &S->S[i], i, 0 ); // Init leaf. + + S->R.last_node = 1; + S->S[PARALLELISM_DEGREE - 1].last_node = 1; +} + + +struct Blake2ThreadData +{ + void Update(); + blake2s_state *S; + const byte *in; + size_t inlen; +}; + + +void Blake2ThreadData::Update() +{ + size_t inlen__ = inlen; + const byte *in__ = ( const byte * )in; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { +#ifdef USE_SSE + // We gain 5% in i7 SSE mode by prefetching next data block. + if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES) + _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0); +#endif + blake2s_update( S, in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } +} + +#ifdef RAR_SMP +THREAD_PROC(Blake2Thread) +{ + Blake2ThreadData *td=(Blake2ThreadData *)Data; + td->Update(); +} +#endif + + +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ) +{ + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + + Blake2ThreadData btd_array[PARALLELISM_DEGREE]; + +#ifdef RAR_SMP + uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads; + + if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here. + ThreadNumber=4; +#else + uint ThreadNumber=1; +#endif + + for (size_t id__=0;id__inlen = inlen; + btd->in = in + id__ * BLAKE2S_BLOCKBYTES; + btd->S = &S->S[id__]; + +#ifdef RAR_SMP + if (ThreadNumber>1) + S->ThPool->AddTask(Blake2Thread,(void*)btd); + else + btd->Update(); +#else + btd->Update(); +#endif + id__++; + } +#ifdef RAR_SMP + if (S->ThPool!=NULL) // Can be NULL in -mt1 mode. + S->ThPool->WaitDone(); +#endif // RAR_SMP + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, (size_t)inlen ); + + S->buflen = left + (size_t)inlen; +} + + +void blake2sp_final( blake2sp_state *S, byte *digest ) +{ + byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2S_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; + + if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; + + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); + } + + blake2s_final( &S->S[i], hash[i] ); + } + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES ); + + blake2s_final( &S->R, digest ); +} diff --git a/libunrar/cmddata.cpp b/libunrar/cmddata.cpp index 4f53f44..74e565a 100644 --- a/libunrar/cmddata.cpp +++ b/libunrar/cmddata.cpp @@ -1,127 +1,154 @@ #include "rar.hpp" +#include "cmdfilter.cpp" +#include "cmdmix.cpp" + CommandData::CommandData() { - FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL; Init(); } -CommandData::~CommandData() -{ - Close(); -} - - void CommandData::Init() { - Close(); + RAROptions::Init(); *Command=0; *ArcName=0; - *ArcNameW=0; FileLists=false; NoMoreSwitches=false; - FileArgs=new StringList; - ExclArgs=new StringList; - InclArgs=new StringList; - StoreArgs=new StringList; - ArcNames=new StringList; -} + ListMode=RCLM_AUTO; + + BareOutput=false; -void CommandData::Close() -{ - delete FileArgs; - delete ExclArgs; - delete InclArgs; - delete StoreArgs; - delete ArcNames; - FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL; + FileArgs.Reset(); + ExclArgs.Reset(); + InclArgs.Reset(); + StoreArgs.Reset(); + ArcNames.Reset(); NextVolSizes.Reset(); } +// Return the pointer to next position in the string and store dynamically +// allocated command line parameter in Par. +static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par) +{ + const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0); + if (NextCmd==NULL) + return NULL; + size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero. + *Par=(wchar *)malloc(ParSize*sizeof(wchar)); + if (*Par==NULL) + return NULL; + return GetCmdParam(CmdLine,*Par,ParSize); +} + + #if !defined(SFX_MODULE) -void CommandData::ParseArg(char *Arg,wchar *ArgW) +void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) +{ + *Command=0; + NoMoreSwitches=false; +#ifdef CUSTOM_CMDLINE_PARSER + // In Windows we may prefer to implement our own command line parser + // to avoid replacing \" by " in standard parser. Such replacing corrupts + // destination paths like "dest path\" in extraction commands. + // Also our own parser is Unicode compatible. + const wchar *CmdLine=GetCommandLine(); + + wchar *Par; + for (bool FirstParam=true;;FirstParam=false) + { + if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL) + break; + if (!FirstParam) // First parameter is the executable name. + if (Preprocess) + PreprocessArg(Par); + else + ParseArg(Par); + free(Par); + } +#else + Array Arg; + for (int I=1;IAddString(Arg); + if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS)) + FileArgs.AddString(Arg); else { - struct FindData FileData; - bool Found=FindFile::FastFind(Arg,NULL,&FileData); - if (!Found && *Arg=='@' && !IsWildcard(Arg)) + FindData FileData; + bool Found=FindFile::FastFind(Arg,&FileData); + if ((!Found || ListMode==RCLM_ACCEPT_LISTS) && + ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1)) { FileLists=true; - RAR_CHARSET Charset=FilelistCharset; + ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true); -#if defined(_WIN_32) && !defined(GUI) - // for compatibility reasons we use OEM encoding - // in Win32 console version by default - - if (Charset==RCH_DEFAULT) - Charset=RCH_OEM; -#endif - - ReadTextFile(Arg+1,FileArgs,false,true,Charset,true,true,true); } - else - if (Found && FileData.IsDir && Extract && *ExtrPath==0) + else // We use 'destpath\' when extracting and reparing. + if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0) { - strcpy(ExtrPath,Arg); - AddEndSlash(ExtrPath); + wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); + AddEndSlash(ExtrPath,ASIZE(ExtrPath)); } else - FileArgs->AddString(Arg); + FileArgs.AddString(Arg); } } } @@ -130,257 +157,163 @@ void CommandData::ParseArg(char *Arg,wchar *ArgW) void CommandData::ParseDone() { - if (FileArgs->ItemsCount()==0 && !FileLists) - FileArgs->AddString(MASKALL); - char CmdChar=etoupper(*Command); + if (FileArgs.ItemsCount()==0 && !FileLists) + FileArgs.AddString(MASKALL); + wchar CmdChar=toupperw(Command[0]); bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P'; if (Test && Extract) Test=false; // Switch '-t' is senseless for 'X', 'E', 'P' commands. - BareOutput=(CmdChar=='L' || CmdChar=='V') && Command[1]=='B'; + + // Suppress the copyright message and final end of line for 'lb' and 'vb'. + if ((CmdChar=='L' || CmdChar=='V') && Command[1]=='B') + BareOutput=true; } -#if !defined(SFX_MODULE) && !defined(_WIN_CE) +#if !defined(SFX_MODULE) void CommandData::ParseEnvVar() { char *EnvStr=getenv("RAR"); if (EnvStr!=NULL) - ProcessSwitchesString(EnvStr); -} -#endif - - - -// return 'false' if -cfg- is present and preprocess switches -// which must be processed before the rest of command line - -#ifndef SFX_MODULE -bool CommandData::IsConfigEnabled(int argc,char *argv[]) -{ - bool ConfigEnabled=true; - for (int I=1;I='0' && Switch[2]<='4'; - if (CommonMode) - Mode=(EXTTIME_MODE)(Switch[2]-'0'); - if (Switch[2]=='-') - Mode=EXTTIME_NONE; - if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0) - xmtime=xctime=xatime=Mode; - else - { - if (Switch[3]>='0' && Switch[3]<='4') - Mode=(EXTTIME_MODE)(Switch[3]-'0'); - if (Switch[3]=='-') - Mode=EXTTIME_NONE; - switch(etoupper(Switch[2])) - { - case 'M': - xmtime=Mode; - break; - case 'C': - xctime=Mode; - break; - case 'A': - xatime=Mode; - break; - case 'R': - xarctime=Mode; - break; - } - } - } - break; - case '-': - Test=false; - break; - case 0: - Test=true; - break; - default: - BadSwitch(Switch); - break; - } + case '@': + ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS; break; case 'A': - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'C': ClearArc=true; break; case 'D': - AppendArcNameToPath=true; + if (Switch[2]==0) + AppendArcNameToPath=APPENDARCNAME_DESTPATH; + else + if (Switch[2]=='1') + AppendArcNameToPath=APPENDARCNAME_OWNDIR; break; +#ifndef SFX_MODULE case 'G': if (Switch[2]=='-' && Switch[3]==0) GenerateArcName=0; else - { - GenerateArcName=true; - strncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); - } + if (toupperw(Switch[2])=='F') + wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask)); + else + { + GenerateArcName=true; + wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); + } break; +#endif case 'I': IgnoreGeneralAttr=true; break; - case 'N': //reserved for archive name + case 'N': // Reserved for archive name. break; case 'O': AddArcOnly=true; break; case 'P': - strcpy(ArcPath,Switch+2); - if (SwitchW!=NULL && *SwitchW!=0) - strcpyw(ArcPathW,SwitchW+2); + wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath)); break; case 'S': SyncFiles=true; @@ -390,9 +323,24 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; } break; + case 'C': + if (Switch[2]==0) + switch(toupperw(Switch[1])) + { + case '-': + DisableComment=true; + break; + case 'U': + ConvertNames=NAMES_UPPERCASE; + break; + case 'L': + ConvertNames=NAMES_LOWERCASE; + break; + } + break; case 'D': if (Switch[2]==0) - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'S': DisableSortSolid=true; @@ -405,98 +353,8 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; } break; - case 'O': - switch(etoupper(Switch[1])) - { - case '+': - Overwrite=OVERWRITE_ALL; - break; - case '-': - Overwrite=OVERWRITE_NONE; - break; - case 0: - Overwrite=OVERWRITE_FORCE_ASK; - break; - case 'R': - Overwrite=OVERWRITE_AUTORENAME; - break; - case 'W': - ProcessOwners=true; - break; -#ifdef SAVE_LINKS - case 'L': - SaveLinks=true; - break; -#endif -#ifdef _WIN_32 - case 'S': - SaveStreams=true; - break; - case 'C': - SetCompressedAttr=true; - break; -#endif - default : - BadSwitch(Switch); - break; - } - break; - case 'R': - switch(etoupper(Switch[1])) - { - case 0: - Recurse=RECURSE_ALWAYS; - break; - case '-': - Recurse=RECURSE_DISABLE; - break; - case '0': - Recurse=RECURSE_WILDCARDS; - break; -#ifndef _WIN_CE - case 'I': - { - Priority=atoi(Switch+2); - char *ChPtr=strchr(Switch+2,':'); - if (ChPtr!=NULL) - { - SleepTime=atoi(ChPtr+1); - InitSystemOptions(SleepTime); - } - SetPriority(Priority); - } - break; -#endif - } - break; - case 'Y': - AllYes=true; - break; - case 'N': - case 'X': - if (Switch[1]!=0) - { - StringList *Args=etoupper(Switch[0])=='N' ? InclArgs:ExclArgs; - if (Switch[1]=='@' && !IsWildcard(Switch)) - { - RAR_CHARSET Charset=FilelistCharset; - -#if defined(_WIN_32) && !defined(GUI) - // for compatibility reasons we use OEM encoding - // in Win32 console version by default - - if (Charset==RCH_DEFAULT) - Charset=RCH_OEM; -#endif - - ReadTextFile(Switch+2,Args,false,true,Charset,true,true,true); - } - else - Args->AddString(Switch+1); - } - break; case 'E': - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'P': switch(Switch[2]) @@ -515,77 +373,166 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; } break; - case 'E': - ProcessEA=false; - break; - case 'N': - NoEndBlock=true; - break; default: if (Switch[1]=='+') { - InclFileAttr=GetExclAttr(&Switch[2]); + InclFileAttr|=GetExclAttr(Switch+2,InclDir); InclAttrSet=true; } else - ExclFileAttr=GetExclAttr(&Switch[1]); + ExclFileAttr|=GetExclAttr(Switch+1,ExclDir); break; } break; - case 'P': + case 'F': if (Switch[1]==0) - { - GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password)); - eprintf("\n"); - } + FreshFiles=true; else - strncpyz(Password,Switch+1,ASIZE(Password)); + BadSwitch(Switch); break; case 'H': - if (etoupper(Switch[1])=='P') + switch (toupperw(Switch[1])) { - EncryptHeaders=true; - if (Switch[2]!=0) - strncpyz(Password,Switch+2,ASIZE(Password)); - else - if (*Password==0) + case 'P': + EncryptHeaders=true; + if (Switch[2]!=0) { - GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password)); - eprintf("\n"); + Password.Set(Switch+2); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } + else + if (!Password.IsSet()) + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + eprintf(L"\n"); + } + break; + default : + BadSwitch(Switch); + break; } break; - case 'Z': - strncpyz(CommentFile,Switch[1]!=0 ? Switch+1:"stdin",ASIZE(CommentFile)); + case 'I': + if (wcsnicomp(Switch+1,L"LOG",3)==0) + { + wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName)); + break; + } + if (wcsnicomp(Switch+1,L"SND",3)==0) + { + Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON; + break; + } + if (wcsicomp(Switch+1,L"ERR")==0) + { + MsgStream=MSG_STDERR; + // Set it immediately when parsing the command line, so it also + // affects messages issued while parsing the command line. + SetConsoleMsgStream(MSG_STDERR); + break; + } + if (wcsnicomp(Switch+1,L"EML",3)==0) + { + wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); + break; + } + if (wcsicomp(Switch+1,L"M")==0) + { + MoreInfo=true; + break; + } + if (wcsicomp(Switch+1,L"NUL")==0) + { + MsgStream=MSG_NULL; + SetConsoleMsgStream(MSG_NULL); + break; + } + if (toupperw(Switch[1])=='D') + { + for (uint I=2;Switch[I]!=0;I++) + switch(toupperw(Switch[I])) + { + case 'Q': + MsgStream=MSG_ERRONLY; + SetConsoleMsgStream(MSG_ERRONLY); + break; + case 'C': + DisableCopyright=true; + break; + case 'D': + DisableDone=true; + break; + case 'P': + DisablePercentage=true; + break; + } + break; + } + if (wcsnicomp(Switch+1,L"OFF",3)==0) + { + switch(Switch[4]) + { + case 0: + case '1': + Shutdown=POWERMODE_OFF; + break; + case '2': + Shutdown=POWERMODE_HIBERNATE; + break; + case '3': + Shutdown=POWERMODE_SLEEP; + break; + case '4': + Shutdown=POWERMODE_RESTART; + break; + } + break; + } + if (wcsicomp(Switch+1,L"VER")==0) + { + PrintVersion=true; + break; + } + break; + case 'K': + switch(toupperw(Switch[1])) + { + case 'B': + KeepBroken=true; + break; + case 0: + Lock=true; + break; + } break; case 'M': - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'C': { - char *Str=Switch+2; + const wchar *Str=Switch+2; if (*Str=='-') - for (int I=0;IAddString(Mask); + wcsncpyz(Mask,Names,ASIZE(Mask)); + StoreArgs.AddString(Mask); if (End==NULL) break; Names=End+1; } } break; -#ifdef PACK_SMP +#ifdef RAR_SMP case 'T': - Threads=atoi(Switch+2); - if (Threads>16) + Threads=atoiw(Switch+2); + if (Threads>MaxPoolThreads || Threads<1) BadSwitch(Switch); else { @@ -659,121 +595,140 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; } break; - case 'V': - switch(etoupper(Switch[1])) + case 'N': + case 'X': + if (Switch[1]!=0) { -#ifdef _WIN_32 - case 'D': - EraseDisk=true; - break; -#endif - case 'N': - OldNumbering=true; - break; - case 'P': - VolumePause=true; - break; - case 'E': - if (etoupper(Switch[2])=='R') - VersionControl=atoi(Switch+3)+1; + StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs; + if (Switch[1]=='@' && !IsWildcard(Switch)) + ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true); + else + Args->AddString(Switch+1); + } + break; + case 'O': + switch(toupperw(Switch[1])) + { + case '+': + Overwrite=OVERWRITE_ALL; break; case '-': - VolSize=0; + Overwrite=OVERWRITE_NONE; + break; + case 0: + Overwrite=OVERWRITE_FORCE_ASK; + break; +#ifdef _WIN_ALL + case 'C': + SetCompressedAttr=true; + break; +#endif + case 'H': + SaveHardLinks=true; break; - default: - { - int64 NewVolSize=atoil(&Switch[1]); - if (NewVolSize==0) - NewVolSize=INT64NDF; // Autodetecting volume size. - else - switch (Switch[strlen(Switch)-1]) - { - case 'f': - case 'F': - switch(NewVolSize) - { - case 360: - NewVolSize=362496; - break; - case 720: - NewVolSize=730112; - break; - case 1200: - NewVolSize=1213952; - break; - case 1440: - NewVolSize=1457664; - break; - case 2880: - NewVolSize=2915328; - break; - } - break; - case 'k': - NewVolSize*=1024; - break; - case 'm': - NewVolSize*=1024*1024; - break; - case 'M': - NewVolSize*=1000*1000; - break; - case 'g': - NewVolSize*=1024*1024; - NewVolSize*=1024; - break; - case 'G': - NewVolSize*=1000*1000; - NewVolSize*=1000; - break; - case 'b': - case 'B': - break; - default: - NewVolSize*=1000; - break; - } - if (VolSize==0) - VolSize=NewVolSize; - else - NextVolSizes.Push(NewVolSize); + +#ifdef SAVE_LINKS + case 'L': + SaveSymLinks=true; + if (toupperw(Switch[2])=='A') + AbsoluteLinks=true; + break; +#endif +#ifdef _WIN_ALL + case 'N': + if (toupperw(Switch[2])=='I') + AllowIncompatNames=true; + break; +#endif + case 'R': + Overwrite=OVERWRITE_AUTORENAME; + break; +#ifdef _WIN_ALL + case 'S': + SaveStreams=true; + break; +#endif + case 'W': + ProcessOwners=true; + break; + default : + BadSwitch(Switch); + break; + } + break; + case 'P': + if (Switch[1]==0) + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + eprintf(L"\n"); + } + else + { + Password.Set(Switch+1); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); + } + break; +#ifndef SFX_MODULE + case 'Q': + if (toupperw(Switch[1])=='O') + switch(toupperw(Switch[2])) + { + case 0: + QOpenMode=QOPEN_AUTO; + break; + case '-': + QOpenMode=QOPEN_NONE; + break; + case '+': + QOpenMode=QOPEN_ALWAYS; + break; + default: + BadSwitch(Switch); + break; + } + else + BadSwitch(Switch); + break; +#endif + case 'R': + switch(toupperw(Switch[1])) + { + case 0: + Recurse=RECURSE_ALWAYS; + break; + case '-': + Recurse=RECURSE_DISABLE; + break; + case '0': + Recurse=RECURSE_WILDCARDS; + break; + case 'I': + { + Priority=atoiw(Switch+2); + if (Priority<0 || Priority>15) + BadSwitch(Switch); + const wchar *ChPtr=wcschr(Switch+2,':'); + if (ChPtr!=NULL) + { + SleepTime=atoiw(ChPtr+1); + if (SleepTime>1000) + BadSwitch(Switch); + InitSystemOptions(SleepTime); + } + SetPriority(Priority); } break; } break; - case 'F': - if (Switch[1]==0) - FreshFiles=true; - else - BadSwitch(Switch); - break; - case 'U': - if (Switch[1]==0) - UpdateFiles=true; - else - BadSwitch(Switch); - break; - case 'W': - strncpyz(TempPath,&Switch[1],ASIZE(TempPath)); - AddEndSlash(TempPath); - break; case 'S': - if (strnicomp(Switch,"SFX",3)==0) - { - const char *SFXName=Switch[3] ? Switch+3:DefSFXName; - if (PointToName(SFXName)!=SFXName || FileExist(SFXName)) - strcpy(SFXModule,SFXName); - else - GetConfigName(SFXName,SFXModule,true); - } if (IsDigit(Switch[1])) { Solid|=SOLID_COUNT; - SolidCount=atoi(&Switch[1]); + SolidCount=atoiw(&Switch[1]); } else - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 0: Solid|=SOLID_NORMAL; @@ -792,19 +747,18 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; case 'L': if (IsDigit(Switch[2])) - FileSizeLess=atoil(Switch+2); + FileSizeLess=atoilw(Switch+2); break; case 'M': if (IsDigit(Switch[2])) - FileSizeMore=atoil(Switch+2); + FileSizeMore=atoilw(Switch+2); break; case 'C': { - // Switch is already found bad, avoid reporting it several times. - bool AlreadyBad=false; + bool AlreadyBad=false; // Avoid reporting "bad switch" several times. RAR_CHARSET rch=RCH_DEFAULT; - switch(etoupper(Switch[2])) + switch(toupperw(Switch[2])) { case 'A': rch=RCH_ANSI; @@ -815,6 +769,9 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) case 'U': rch=RCH_UNICODE; break; + case 'F': + rch=RCH_UTF8; + break; default : BadSwitch(Switch); AlreadyBad=true; @@ -822,10 +779,10 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) }; if (!AlreadyBad) if (Switch[3]==0) - CommentCharset=FilelistCharset=rch; + CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch; else - for (int I=3;Switch[I]!=0 && !AlreadyBad;I++) - switch(etoupper(Switch[I])) + for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++) + switch(toupperw(Switch[I])) { case 'C': CommentCharset=rch; @@ -833,47 +790,100 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) case 'L': FilelistCharset=rch; break; + case 'R': + RedirectCharset=rch; + break; default: BadSwitch(Switch); AlreadyBad=true; break; } + // Set it immediately when parsing the command line, so it also + // affects messages issued while parsing the command line. + SetConsoleRedirectCharset(RedirectCharset); } break; } break; - case 'C': - if (Switch[2]==0) - switch(etoupper(Switch[1])) - { - case '-': - DisableComment=true; - break; - case 'U': - ConvertNames=NAMES_UPPERCASE; - break; - case 'L': - ConvertNames=NAMES_LOWERCASE; - break; - } - break; - case 'K': - switch(etoupper(Switch[1])) + case 'T': + switch(toupperw(Switch[1])) { + case 'K': + ArcTime=ARCTIME_KEEP; + break; + case 'L': + ArcTime=ARCTIME_LATEST; + break; + case 'O': + SetTimeFilters(Switch+2,true,true); + break; + case 'N': + SetTimeFilters(Switch+2,false,true); + break; case 'B': - KeepBroken=true; + SetTimeFilters(Switch+2,true,false); + break; + case 'A': + SetTimeFilters(Switch+2,false,false); + break; + case 'S': + SetStoreTimeMode(Switch+2); + break; + case '-': + Test=false; break; case 0: - Lock=true; + Test=true; + break; + default: + BadSwitch(Switch); break; } break; -#ifndef GUI - case '?' : - OutHelp(); + case 'U': + if (Switch[1]==0) + UpdateFiles=true; + else + BadSwitch(Switch); + break; + case 'V': + switch(toupperw(Switch[1])) + { + case 'P': + VolumePause=true; + break; + case 'E': + if (toupperw(Switch[2])=='R') + VersionControl=atoiw(Switch+3)+1; + break; + case '-': + VolSize=0; + break; + default: + VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command. + break; + } + break; + case 'W': + wcsncpyz(TempPath,Switch+1,ASIZE(TempPath)); + AddEndSlash(TempPath,ASIZE(TempPath)); + break; + case 'Y': + AllYes=true; + break; + case 'Z': + if (Switch[1]==0) + { + // If comment file is not specified, we read data from stdin. + wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile)); + } + else + wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile)); + break; + case '?' : + OutHelp(RARX_SUCCESS); break; -#endif default : BadSwitch(Switch); break; @@ -882,310 +892,59 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) #endif -#ifndef SFX_MODULE -void CommandData::BadSwitch(char *Switch) +#if !defined(SFX_MODULE) +void CommandData::BadSwitch(const wchar *Switch) { mprintf(St(MUnknownOption),Switch); - ErrHandler.Exit(USER_ERROR); + ErrHandler.Exit(RARX_USERERROR); } #endif -#ifndef GUI -void CommandData::OutTitle() -{ - if (BareOutput || DisableCopyright) - return; -#if defined(__GNUC__) && defined(SFX_MODULE) - mprintf(St(MCopyrightS)); -#else -#ifndef SILENT - static bool TitleShown=false; - if (TitleShown) - return; - TitleShown=true; - char Version[50]; - int Beta=RARVER_BETA; - if (Beta!=0) - sprintf(Version,"%d.%02d %s %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); - else - sprintf(Version,"%d.%02d",RARVER_MAJOR,RARVER_MINOR); -#ifdef UNRAR - mprintf(St(MUCopyright),Version,RARVER_YEAR); -#else -#endif -#endif -#endif -} -#endif - - -inline bool CmpMSGID(MSGID i1,MSGID i2) -{ -#ifdef MSGID_INT - return(i1==i2); -#else - // If MSGID is const char*, we cannot compare pointers only. - // Pointers to different instances of same strings can differ, - // so we need to compare complete strings. - return(strcmp(i1,i2)==0); -#endif -} - -void CommandData::OutHelp() -{ -#if !defined(GUI) && !defined(SILENT) - OutTitle(); - static MSGID Help[]={ -#ifdef SFX_MODULE - // Console SFX switches definition. - MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV -#elif defined(UNRAR) - // UnRAR switches definition. - MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, - MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw, - MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAI,MCHelpSwAP, - MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU, - MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, - MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, - MCHelpSwO,MCHelpSwOC,MCHelpSwOR,MCHelpSwOW,MCHelpSwP, - MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSL,MCHelpSwSM,MCHelpSwTA, - MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr, - MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY -#else - // RAR switches definition. - MRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdA,MCHelpCmdC,MCHelpCmdCF, - MCHelpCmdCH,MCHelpCmdCW,MCHelpCmdD,MCHelpCmdE,MCHelpCmdF,MCHelpCmdI, - MCHelpCmdK,MCHelpCmdL,MCHelpCmdM,MCHelpCmdP,MCHelpCmdR,MCHelpCmdRC, - MCHelpCmdRN,MCHelpCmdRR,MCHelpCmdRV,MCHelpCmdS,MCHelpCmdT,MCHelpCmdU, - MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG, - MCHelpSwAI,MCHelpSwAO,MCHelpSwAP,MCHelpSwAS,MCHelpSwAV,MCHelpSwAVm, - MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDF,MCHelpSwDH, - MCHelpSwDR,MCHelpSwDS,MCHelpSwDW,MCHelpSwEa,MCHelpSwED,MCHelpSwEE, - MCHelpSwEN,MCHelpSwEP,MCHelpSwEP1,MCHelpSwEP2,MCHelpSwEP3,MCHelpSwF, - MCHelpSwHP,MCHelpSwIDP,MCHelpSwIEML,MCHelpSwIERR,MCHelpSwILOG,MCHelpSwINUL, - MCHelpSwIOFF,MCHelpSwISND,MCHelpSwK,MCHelpSwKB,MCHelpSwMn,MCHelpSwMC, - MCHelpSwMD,MCHelpSwMS,MCHelpSwMT,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, - MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOS,MCHelpSwOW, - MCHelpSwP,MCHelpSwPm,MCHelpSwR,MCHelpSwRm,MCHelpSwR0,MCHelpSwRI, - MCHelpSwRR,MCHelpSwRV,MCHelpSwS,MCHelpSwSm,MCHelpSwSC,MCHelpSwSFX, - MCHelpSwSI,MCHelpSwSL,MCHelpSwSM,MCHelpSwT,MCHelpSwTA,MCHelpSwTB, - MCHelpSwTK,MCHelpSwTL,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU, - MCHelpSwV,MCHelpSwVn,MCHelpSwVD,MCHelpSwVER,MCHelpSwVN,MCHelpSwVP, - MCHelpSwW,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY,MCHelpSwZ -#endif - }; - - for (int I=0;IRewind(); - while ((CurName=Args->GetString())!=NULL) -#ifndef SFX_MODULE - if (CheckFullPath && IsFullPath(CurName)) - { - if (*FullName==0) - ConvertNameToFull(CheckName,FullName); - if (CmpName(CurName,FullName,MatchMode)) - return(true); - } - else -#endif - if (CmpName(ConvertPath(CurName,NULL),Name,MatchMode)) - return(true); - return(false); -} - - -// Return 'true' if we need to exclude the file from processing as result -// of -x switch. If CheckInclList is true, we also check the file against -// the include list created with -n switch. -bool CommandData::ExclCheck(char *CheckName,bool CheckFullPath,bool CheckInclList) -{ - if (ExclCheckArgs(ExclArgs,CheckName,CheckFullPath,MATCH_WILDSUBPATH)) - return(true); - if (!CheckInclList || InclArgs->ItemsCount()==0) - return(false); - if (ExclCheckArgs(InclArgs,CheckName,false,MATCH_WILDSUBPATH)) - return(false); - return(true); -} - - - - -#ifndef SFX_MODULE -bool CommandData::TimeCheck(RarTime &ft) -{ - if (FileTimeBefore.IsSet() && ft>=FileTimeBefore) - return(true); - if (FileTimeAfter.IsSet() && ft<=FileTimeAfter) - return(true); - return(false); -} -#endif - - -#ifndef SFX_MODULE -bool CommandData::SizeCheck(int64 Size) -{ - if (FileSizeLess!=INT64NDF && Size>=FileSizeLess) - return(true); - if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) - return(true); - return(false); -} -#endif - - - - -int CommandData::IsProcessFile(FileHeader &NewLhd,bool *ExactMatch,int MatchType) -{ - if (strlen(NewLhd.FileName)>=NM || strlenw(NewLhd.FileNameW)>=NM) - return(0); - if (ExclCheck(NewLhd.FileName,false,true)) - return(0); -#ifndef SFX_MODULE - if (TimeCheck(NewLhd.mtime)) - return(0); - if ((NewLhd.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (NewLhd.FileAttr & InclFileAttr)==0) - return(0); - if ((NewLhd.Flags & LHD_WINDOWMASK)!=LHD_DIRECTORY && SizeCheck(NewLhd.FullUnpSize)) - return(0); -#endif - char *ArgName; - wchar *ArgNameW; - FileArgs->Rewind(); - for (int StringCount=1;FileArgs->GetString(&ArgName,&ArgNameW);StringCount++) - { -#ifndef SFX_MODULE - bool Unicode=(NewLhd.Flags & LHD_UNICODE) || ArgNameW!=NULL; - if (Unicode) - { - wchar NameW[NM],ArgW[NM],*NamePtr=NewLhd.FileNameW; - bool CorrectUnicode=true; - if (ArgNameW==NULL) - { - if (!CharToWide(ArgName,ArgW) || *ArgW==0) - CorrectUnicode=false; - ArgNameW=ArgW; - } - if ((NewLhd.Flags & LHD_UNICODE)==0) - { - if (!CharToWide(NewLhd.FileName,NameW) || *NameW==0) - CorrectUnicode=false; - NamePtr=NameW; - } - if (CmpName(ArgNameW,NamePtr,MatchType)) - { - if (ExactMatch!=NULL) - *ExactMatch=stricompcw(ArgNameW,NamePtr)==0; - return(StringCount); - } - if (CorrectUnicode) - continue; - } -#endif - if (CmpName(ArgName,NewLhd.FileName,MatchType)) - { - if (ExactMatch!=NULL) - *ExactMatch=stricompc(ArgName,NewLhd.FileName)==0; - return(StringCount); - } - } - return(0); -} - - -#ifndef GUI void CommandData::ProcessCommand() { #ifndef SFX_MODULE - const char *SingleCharCommands="FUADPXETK"; - if (Command[1] && strchr(SingleCharCommands,*Command)!=NULL || *ArcName==0) - OutHelp(); + const wchar *SingleCharCommands=L"FUADPXETK"; + if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0) + OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. + const wchar *ArcExt=GetExt(ArcName); #ifdef _UNIX - if (GetExt(ArcName)==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) - strcat(ArcName,".rar"); + if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #else - if (GetExt(ArcName)==NULL) - strcat(ArcName,".rar"); + if (ArcExt==NULL) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #endif - - if (strchr("AFUMD",*Command)==NULL) + // Treat arcname.part1 as arcname.part1.rar. + if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) && + !FileExist(ArcName)) { + wchar Name[NM]; + wcsncpyz(Name,ArcName,ASIZE(Name)); + wcsncatz(Name,L".rar",ASIZE(Name)); + if (FileExist(Name)) + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + } + + if (wcschr(L"AFUMD",*Command)==NULL) + { + if (GenerateArcName) + { + const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask; + GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false); + } + StringList ArcMasks; ArcMasks.AddString(ArcName); - ScanTree Scan(&ArcMasks,Recurse,SaveLinks,SCAN_SKIPDIRS); + ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS); FindData FindData; while (Scan.GetNext(&FindData)==SCAN_SUCCESS) - AddArcName(FindData.Name,FindData.NameW); + AddArcName(FindData.Name); } else - AddArcName(ArcName,NULL); + AddArcName(ArcName); #endif switch(Command[0]) @@ -1194,10 +953,9 @@ void CommandData::ProcessCommand() case 'X': case 'E': case 'T': - case 'I': { - CmdExtract Extract; - Extract.DoExtract(this); + CmdExtract Extract(this); + Extract.DoExtract(); } break; #ifndef SILENT @@ -1206,77 +964,72 @@ void CommandData::ProcessCommand() ListArchive(this); break; default: - OutHelp(); + OutHelp(RARX_USERERROR); #endif } if (!BareOutput) - mprintf("\n"); -} -#endif - - -void CommandData::AddArcName(char *Name,wchar *NameW) -{ - ArcNames->AddString(Name,NameW); + mprintf(L"\n"); } -bool CommandData::GetArcName(char *Name,wchar *NameW,int MaxSize) +void CommandData::AddArcName(const wchar *Name) { - if (!ArcNames->GetString(Name,NameW,NM)) - return(false); - return(true); + ArcNames.AddString(Name); +} + + +bool CommandData::GetArcName(wchar *Name,int MaxSize) +{ + return ArcNames.GetString(Name,MaxSize); } bool CommandData::IsSwitch(int Ch) { -#if defined(_WIN_32) || defined(_EMX) - return(Ch=='-' || Ch=='/'); +#if defined(_WIN_ALL) || defined(_EMX) + return Ch=='-' || Ch=='/'; #else - return(Ch=='-'); + return Ch=='-'; #endif } #ifndef SFX_MODULE -uint CommandData::GetExclAttr(char *Str) +uint CommandData::GetExclAttr(const wchar *Str,bool &Dir) { if (IsDigit(*Str)) - return(strtol(Str,NULL,0)); - else + return wcstol(Str,NULL,0); + + uint Attr=0; + while (*Str!=0) { - uint Attr; - for (Attr=0;*Str;Str++) - switch(etoupper(*Str)) - { + switch(toupperw(*Str)) + { + case 'D': + Dir=true; + break; #ifdef _UNIX - case 'D': - Attr|=S_IFDIR; - break; - case 'V': - Attr|=S_IFCHR; - break; -#elif defined(_WIN_32) || defined(_EMX) - case 'R': - Attr|=0x1; - break; - case 'H': - Attr|=0x2; - break; - case 'S': - Attr|=0x4; - break; - case 'D': - Attr|=0x10; - break; - case 'A': - Attr|=0x20; - break; + case 'V': + Attr|=S_IFCHR; + break; +#elif defined(_WIN_ALL) || defined(_EMX) + case 'R': + Attr|=0x1; + break; + case 'H': + Attr|=0x2; + break; + case 'S': + Attr|=0x4; + break; + case 'A': + Attr|=0x20; + break; #endif - } - return(Attr); + } + Str++; } + return Attr; } #endif @@ -1286,13 +1039,44 @@ uint CommandData::GetExclAttr(char *Str) #ifndef SFX_MODULE bool CommandData::CheckWinSize() { - static int ValidSize[]={ - 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000 - }; - for (int I=0;IRewind(); + while (Args->GetString(CurMask,ASIZE(CurMask))) + { + wchar *LastMaskChar=PointToLastChar(CurMask); + bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. + + if (Dir) + { + // CheckName is a directory. + if (DirMask) + { + // We process the directory and have the directory exclusion mask. + // So let's convert "mask\" to "mask" and process it normally. + + *LastMaskChar=0; + } + else + { + // REMOVED, we want -npath\* to match empty folders too. + // If mask has wildcards in name part and does not have the trailing + // '\' character, we cannot use it for directories. + + // if (IsWildcard(PointToName(CurMask))) + // continue; + } + } + else + { + // If we process a file inside of directory excluded by "dirmask\". + // we want to exclude such file too. So we convert "dirmask\" to + // "dirmask\*". It is important for operations other than archiving + // with -x. When archiving with -x, directory matched by "dirmask\" + // is excluded from further scanning. + + if (DirMask) + wcsncatz(CurMask,L"*",ASIZE(CurMask)); + } + +#ifndef SFX_MODULE + if (CheckFullPath && IsFullPath(CurMask)) + { + // We do not need to do the special "*\" processing here, because + // unlike the "else" part of this "if", now we convert names to full + // format, so they all include the path, which is matched by "*\" + // correctly. Moreover, removing "*\" from mask would break + // the comparison, because now all names have the path. + + if (*FullName==0) + ConvertNameToFull(CheckName,FullName,ASIZE(FullName)); + if (CmpName(CurMask,FullName,MatchMode)) + return true; + } + else +#endif + { + wchar NewName[NM+2],*CurName=Name; + + // Important to convert before "*\" check below, so masks like + // d:*\something are processed properly. + wchar *CmpMask=ConvertPath(CurMask,NULL,0); + + if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1])) + { + // We want "*\name" to match 'name' not only in subdirectories, + // but also in the current directory. We convert the name + // from 'name' to '.\name' to be matched by "*\" part even if it is + // in current directory. + NewName[0]='.'; + NewName[1]=CPATHDIVIDER; + wcsncpyz(NewName+2,Name,ASIZE(NewName)-2); + CurName=NewName; + } + + if (CmpName(CmpMask,CurName,MatchMode)) + return true; + } + } + return false; +} + + + + +#ifndef SFX_MODULE +// Now this function performs only one task and only in Windows version: +// it skips symlinks to directories if -e1024 switch is specified. +// Symlinks are skipped in ScanTree class, so their entire contents +// is skipped too. Without this function we would check the attribute +// only directly before archiving, so we would skip the symlink record, +// but not the contents of symlinked directory. +bool CommandData::ExclDirByAttr(uint FileAttr) +{ +#ifdef _WIN_ALL + if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 && + (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) + return true; +#endif + return false; +} +#endif + + + + +#if !defined(SFX_MODULE) +void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age) +{ + bool ModeOR=false,TimeMods=false; + const wchar *S=Mod; + // Check if any 'mca' modifiers are present, set OR mode if 'o' is present, + // skip modifiers and set S to beginning of time string. Be sure to check + // *S!=0, because termination 0 is a part of string for wcschr. + for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++) + if (*S=='o' || *S=='O') + ModeOR=true; + else + TimeMods=true; + + if (!TimeMods) // Assume 'm' if no modifiers are specified. + Mod=L"m"; + + // Set the specified time for every modifier. Be sure to check *Mod!=0, + // because termination 0 is a part of string for wcschr. This check is + // important when we set Mod to "m" above. + for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++) + switch(toupperw(*Mod)) + { + case 'M': + if (Before) + { + Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S); + FileMtimeBeforeOR=ModeOR; + } + else + { + Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S); + FileMtimeAfterOR=ModeOR; + } + break; + case 'C': + if (Before) + { + Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S); + FileCtimeBeforeOR=ModeOR; + } + else + { + Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S); + FileCtimeAfterOR=ModeOR; + } + break; + case 'A': + if (Before) + { + Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S); + FileAtimeBeforeOR=ModeOR; + } + else + { + Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S); + FileAtimeAfterOR=ModeOR; + } + break; + } +} +#endif + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta) +{ + bool FilterOR=false; + + if (FileMtimeBefore.IsSet()) // Filter present. + if (ftm>=FileMtimeBefore) // Condition not matched. + if (FileMtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileMtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileMtimeAfter.IsSet()) // Filter present. + if (ftm=FileCtimeBefore) // Condition not matched. + if (FileCtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileCtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileCtimeAfter.IsSet()) // Filter present. + if (ftc=FileAtimeBefore) // Condition not matched. + if (FileAtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileAtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileAtimeAfter.IsSet()) // Filter present. + if (fta=FileSizeLess) + return true; + if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) + return true; + return false; +} +#endif + + + + +// Return 0 if file must not be processed or a number of matched parameter otherwise. +int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, + bool Flags,wchar *MatchedArg,uint MatchedArgSize) +{ + if (MatchedArg!=NULL && MatchedArgSize>0) + *MatchedArg=0; + bool Dir=FileHead.Dir; + if (ExclCheck(FileHead.FileName,Dir,false,true)) + return 0; +#ifndef SFX_MODULE + if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime)) + return 0; + if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) + return 0; + if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 || + FileHead.Dir && !InclDir)) + return 0; + if (!Dir && SizeCheck(FileHead.UnpSize)) + return 0; +#endif + wchar *ArgName; + FileArgs.Rewind(); + for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++) + if (CmpName(ArgName,FileHead.FileName,MatchType)) + { + if (ExactMatch!=NULL) + *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0; + if (MatchedArg!=NULL) + wcsncpyz(MatchedArg,ArgName,MatchedArgSize); + return StringCount; + } + return 0; +} + + +#if !defined(SFX_MODULE) +void CommandData::SetStoreTimeMode(const wchar *S) +{ + if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+') + { + // Apply -ts, -ts1, -ts-, -ts+ to all 3 times. + // Handle obsolete -ts[2,3,4] as ts+. + EXTTIME_MODE Mode=EXTTIME_MAX; + if (*S=='-') + Mode=EXTTIME_NONE; + if (*S=='1') + Mode=EXTTIME_1S; + xmtime=xctime=xatime=Mode; + S++; + } + + while (*S!=0) + { + EXTTIME_MODE Mode=EXTTIME_MAX; + if (S[1]=='-') + Mode=EXTTIME_NONE; + if (S[1]=='1') + Mode=EXTTIME_1S; + switch(toupperw(*S)) + { + case 'M': + xmtime=Mode; + break; + case 'C': + xctime=Mode; + break; + case 'A': + xatime=Mode; + break; + case 'P': + PreserveAtime=true; + break; + } + S++; + } +} +#endif diff --git a/libunrar/cmdmix.cpp b/libunrar/cmdmix.cpp new file mode 100644 index 0000000..3990cc1 --- /dev/null +++ b/libunrar/cmdmix.cpp @@ -0,0 +1,118 @@ +void CommandData::OutTitle() +{ + if (BareOutput || DisableCopyright) + return; +#if defined(__GNUC__) && defined(SFX_MODULE) + mprintf(St(MCopyrightS)); +#else +#ifndef SILENT + static bool TitleShown=false; + if (TitleShown) + return; + TitleShown=true; + + wchar Version[80]; + if (RARVER_BETA!=0) + swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); + else + swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR); +#if defined(_WIN_32) || defined(_WIN_64) + wcsncatz(Version,L" ",ASIZE(Version)); +#endif +#ifdef _WIN_32 + wcsncatz(Version,St(Mx86),ASIZE(Version)); +#endif +#ifdef _WIN_64 + wcsncatz(Version,St(Mx64),ASIZE(Version)); +#endif + if (PrintVersion) + { + mprintf(L"%s",Version); + exit(0); + } + mprintf(St(MUCopyright),Version,RARVER_YEAR); +#endif +#endif +} + + +inline bool CmpMSGID(MSGID i1,MSGID i2) +{ +#ifdef MSGID_INT + return i1==i2; +#else + // If MSGID is const char*, we cannot compare pointers only. + // Pointers to different instances of same string can differ, + // so we need to compare complete strings. + return wcscmp(i1,i2)==0; +#endif +} + +void CommandData::OutHelp(RAR_EXIT ExitCode) +{ +#if !defined(SILENT) + OutTitle(); + static MSGID Help[]={ +#ifdef SFX_MODULE + // Console SFX switches definition. + MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV +#else + // UnRAR switches definition. + MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, + MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm, + MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP, + MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU, + MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, + MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, + MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP, + MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM, + MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU, + MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal, + MCHelpSwY +#endif + }; + + for (uint I=0;ISet(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + break; + } + return true; +} #endif #ifndef SILENT -bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength) +bool getwstr(wchar *str,size_t n) { - Alarm(); - while (true) + // Print buffered prompt title function before waiting for input. + fflush(stderr); + + *str=0; +#if defined(_WIN_ALL) + // fgetws does not work well with non-English text in Windows, + // so we do not use it. + if (StdinRedirected) // ReadConsole does not work if redirected. { - char PromptStr[NM+256]; -#if defined(_EMX) || defined(_BEOS) - strcpy(PromptStr,St(MAskPswEcho)); -#else - strcpy(PromptStr,St(MAskPsw)); -#endif - if (Type!=PASSWORD_GLOBAL) + // fgets does not work well with pipes in Windows in our test. + // Let's use files. + Array StrA(n*4); // Up to 4 UTF-8 characters per wchar_t. + File SrcFile; + SrcFile.SetHandleType(FILE_HANDLESTD); + int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1); + if (ReadSize<=0) { - strcat(PromptStr,St(MFor)); - char *NameOnly=PointToName(FileName); - if (strlen(PromptStr)+strlen(NameOnly)4 ? "\n":" "):", "); + eprintf(I==0 ? (NumItems>4 ? L"\n":L" "):L", "); int KeyPos=ItemKeyPos[I]; for (int J=0;J[{key};"{string}"p used to redefine + // a keyboard key on some terminals. + if (Data[J]=='\"') + return true; + if (!IsDigit(Data[J]) && Data[J]!=';') break; } - RetCode=1; - } -#endif - return(RetCode); + return false; } -void OutComment(char *Comment,size_t Size) +void OutComment(const wchar *Comment,size_t Size) { -#ifndef GUI - if (KbdAnsi(Comment,Size)==2) + if (IsCommentUnsafe(Comment,Size)) return; const size_t MaxOutSize=0x400; for (size_t I=0;I>1)^0xEDB88320L : (C>>1); + for (uint J=0;J<8;J++) + C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1); CRCTab[I]=C; } } -uint CRC(uint StartCRC,const void *Addr,size_t Size) +static void InitTables() { - if (CRCTab[1]==0) - InitCRC(); - byte *Data=(byte *)Addr; + InitCRC32(crc_tables[0]); -#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) - while (Size>0 && ((long)Data & 7)) + for (uint I=0;I<256;I++) // Build additional lookup tables. { - StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8); - Size--; - Data++; + uint C=crc_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc_tables[0][(byte)C]^(C>>8); + crc_tables[J][I]=C; + } } - while (Size>=8) - { - StartCRC^=*(uint32 *)Data; - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC^=*(uint32 *)(Data+4); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - Data+=8; - Size-=8; - } -#endif - - for (size_t I=0;I>8); - return(StartCRC); } + +struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32; + +uint CRC32(uint StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + + // Align Data to 8 for better performance. + for (;Size>0 && ((size_t)Data & 7);Size--,Data++) + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (;Size>=8;Size-=8,Data+=8) + { +#ifdef BIG_ENDIAN + StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24); + uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24); +#else + StartCRC ^= *(uint32 *) Data; + uint NextData = *(uint32 *) (Data+4); +#endif + StartCRC = crc_tables[7][(byte) StartCRC ] ^ + crc_tables[6][(byte)(StartCRC >> 8) ] ^ + crc_tables[5][(byte)(StartCRC >> 16)] ^ + crc_tables[4][(byte)(StartCRC >> 24)] ^ + crc_tables[3][(byte) NextData ] ^ + crc_tables[2][(byte)(NextData >> 8) ] ^ + crc_tables[1][(byte)(NextData >> 16)] ^ + crc_tables[0][(byte)(NextData >> 24)]; + } + + for (;Size>0;Size--,Data++) // Process left data. + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return StartCRC; +} + + #ifndef SFX_MODULE -ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size) +// For RAR 1.4 archives in case somebody still has them. +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size) { byte *Data=(byte *)Addr; for (size_t I=0;I>15))&0xffff; } - return(StartCRC); + return StartCRC; } #endif + + diff --git a/libunrar/crc.hpp b/libunrar/crc.hpp index a632a54..d8fea28 100644 --- a/libunrar/crc.hpp +++ b/libunrar/crc.hpp @@ -1,10 +1,15 @@ #ifndef _RAR_CRC_ #define _RAR_CRC_ -extern uint CRCTab[256]; +// This function is only to intialize external CRC tables. We do not need to +// call it before calculating CRC32. +void InitCRC32(uint *CRCTab); + +uint CRC32(uint StartCRC,const void *Addr,size_t Size); + +#ifndef SFX_MODULE +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size); +#endif -void InitCRC(); -uint CRC(uint StartCRC,const void *Addr,size_t Size); -ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size); #endif diff --git a/libunrar/crypt.cpp b/libunrar/crypt.cpp index b6a56db..fc2126d 100644 --- a/libunrar/crypt.cpp +++ b/libunrar/crypt.cpp @@ -1,381 +1,134 @@ #include "rar.hpp" #ifndef SFX_MODULE -extern uint CRCTab[256]; +#include "crypt1.cpp" +#include "crypt2.cpp" #endif - -#define NROUNDS 32 - -#define rol(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) -#define ror(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) - -#define substLong(t) ( (uint)SubstTable[(uint)t&255] | \ - ((uint)SubstTable[(int)(t>> 8)&255]<< 8) | \ - ((uint)SubstTable[(int)(t>>16)&255]<<16) | \ - ((uint)SubstTable[(int)(t>>24)&255]<<24) ) - -CryptKeyCacheItem CryptData::Cache[4]; -int CryptData::CachePos=0; +#include "crypt3.cpp" +#include "crypt5.cpp" -#ifndef SFX_MODULE -static byte InitSubstTable[256]={ - 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, - 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, - 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, - 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, - 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, - 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, - 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, - 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, - 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, - 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, - 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, - 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, - 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, - 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, - 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, - 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 -}; -#endif +CryptData::CryptData() +{ + Method=CRYPT_NONE; + memset(KDF3Cache,0,sizeof(KDF3Cache)); + memset(KDF5Cache,0,sizeof(KDF5Cache)); + KDF3CachePos=0; + KDF5CachePos=0; + memset(CRCTab,0,sizeof(CRCTab)); +} + + +CryptData::~CryptData() +{ + cleandata(KDF3Cache,sizeof(KDF3Cache)); + cleandata(KDF5Cache,sizeof(KDF5Cache)); +} + void CryptData::DecryptBlock(byte *Buf,size_t Size) { - rin.blockDecrypt(Buf,Size,Buf); -} - - -#ifndef SFX_MODULE -void CryptData::EncryptBlock20(byte *Buf) -{ - uint A,B,C,D,T,TA,TB; -#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) - A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0]; - B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1]; - C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2]; - D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3]; -#else - uint32 *BufPtr=(uint32 *)Buf; - A=BufPtr[0]^Key[0]; - B=BufPtr[1]^Key[1]; - C=BufPtr[2]^Key[2]; - D=BufPtr[3]^Key[3]; -#endif - for(int I=0;I>8); - Buf[2]=(byte)(C>>16); - Buf[3]=(byte)(C>>24); - D^=Key[1]; - Buf[4]=(byte)D; - Buf[5]=(byte)(D>>8); - Buf[6]=(byte)(D>>16); - Buf[7]=(byte)(D>>24); - A^=Key[2]; - Buf[8]=(byte)A; - Buf[9]=(byte)(A>>8); - Buf[10]=(byte)(A>>16); - Buf[11]=(byte)(A>>24); - B^=Key[3]; - Buf[12]=(byte)B; - Buf[13]=(byte)(B>>8); - Buf[14]=(byte)(B>>16); - Buf[15]=(byte)(B>>24); -#else - BufPtr[0]=C^Key[0]; - BufPtr[1]=D^Key[1]; - BufPtr[2]=A^Key[2]; - BufPtr[3]=B^Key[3]; -#endif - UpdKeys(Buf); -} - - -void CryptData::DecryptBlock20(byte *Buf) -{ - byte InBuf[16]; - uint A,B,C,D,T,TA,TB; -#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) - A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0]; - B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1]; - C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2]; - D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3]; -#else - uint32 *BufPtr=(uint32 *)Buf; - A=BufPtr[0]^Key[0]; - B=BufPtr[1]^Key[1]; - C=BufPtr[2]^Key[2]; - D=BufPtr[3]^Key[3]; -#endif - memcpy(InBuf,Buf,sizeof(InBuf)); - for(int I=NROUNDS-1;I>=0;I--) - { - T=((C+rol(D,11,32))^Key[I&3]); - TA=A^substLong(T); - T=((D^rol(C,17,32))+Key[I&3]); - TB=B^substLong(T); - A=C; - B=D; - C=TA; - D=TB; - } -#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) - C^=Key[0]; - Buf[0]=(byte)C; - Buf[1]=(byte)(C>>8); - Buf[2]=(byte)(C>>16); - Buf[3]=(byte)(C>>24); - D^=Key[1]; - Buf[4]=(byte)D; - Buf[5]=(byte)(D>>8); - Buf[6]=(byte)(D>>16); - Buf[7]=(byte)(D>>24); - A^=Key[2]; - Buf[8]=(byte)A; - Buf[9]=(byte)(A>>8); - Buf[10]=(byte)(A>>16); - Buf[11]=(byte)(A>>24); - B^=Key[3]; - Buf[12]=(byte)B; - Buf[13]=(byte)(B>>8); - Buf[14]=(byte)(B>>16); - Buf[15]=(byte)(B>>24); -#else - BufPtr[0]=C^Key[0]; - BufPtr[1]=D^Key[1]; - BufPtr[2]=A^Key[2]; - BufPtr[3]=B^Key[3]; -#endif - UpdKeys(InBuf); -} - - -void CryptData::UpdKeys(byte *Buf) -{ - for (int I=0;I<16;I+=4) - { - Key[0]^=CRCTab[Buf[I]]; - Key[1]^=CRCTab[Buf[I+1]]; - Key[2]^=CRCTab[Buf[I+2]]; - Key[3]^=CRCTab[Buf[I+3]]; - } -} - - -void CryptData::Swap(byte *Ch1,byte *Ch2) -{ - byte Ch=*Ch1; - *Ch1=*Ch2; - *Ch2=Ch; -} -#endif - - -void CryptData::SetCryptKeys(const char *Password,const byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash) -{ - if (*Password==0) - return; - if (OldOnly) + switch(Method) { #ifndef SFX_MODULE - if (CRCTab[1]==0) - InitCRC(); - byte Psw[MAXPASSWORD]; - SetOldKeys(Password); - Key[0]=0xD3A3B879L; - Key[1]=0x3F6D12F7L; - Key[2]=0x7515A235L; - Key[3]=0xA4E7F123L; - memset(Psw,0,sizeof(Psw)); -#if defined(_WIN_32) && !defined(GUI) - CharToOemBuff(Password,(char*)Psw,(DWORD)strlen(Password)); -#else - strncpyz((char *)Psw,Password,ASIZE(Psw)); -#endif - size_t PswLength=strlen(Password); - memcpy(SubstTable,InitSubstTable,sizeof(SubstTable)); - for (int J=0;J<256;J++) - for (size_t I=0;I>8); - PswNum[2]=(byte)(I>>16); - hash_process( &c, PswNum, 3, HandsOffHash); - if (I%(HashRounds/16)==0) - { - hash_context tempc=c; - uint32 digest[5]; - hash_final( &tempc, digest, HandsOffHash); - AESInit[I/(HashRounds/16)]=(byte)digest[4]; - } - } - uint32 digest[5]; - hash_final( &c, digest, HandsOffHash); - for (int I=0;I<4;I++) - for (int J=0;J<4;J++) - AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); - - strcpy(Cache[CachePos].Password,Password); - if ((Cache[CachePos].SaltPresent=(Salt!=NULL))==true) - memcpy(Cache[CachePos].Salt,Salt,SALT_SIZE); - Cache[CachePos].HandsOffHash=HandsOffHash; - memcpy(Cache[CachePos].AESKey,AESKey,sizeof(AESKey)); - memcpy(Cache[CachePos].AESInit,AESInit,sizeof(AESInit)); - CachePos=(CachePos+1)%(sizeof(Cache)/sizeof(Cache[0])); - } - rin.init(Encrypt ? Rijndael::Encrypt : Rijndael::Decrypt,AESKey,AESInit); -} - - -#ifndef SFX_MODULE -void CryptData::SetOldKeys(const char *Password) -{ - uint PswCRC=CRC(0xffffffff,Password,strlen(Password)); - OldKey[0]=PswCRC&0xffff; - OldKey[1]=(PswCRC>>16)&0xffff; - OldKey[2]=OldKey[3]=0; - PN1=PN2=PN3=0; - byte Ch; - while ((Ch=*Password)!=0) - { - PN1+=Ch; - PN2^=Ch; - PN3+=Ch; - PN3=(byte)rol(PN3,1,8); - OldKey[2]^=Ch^CRCTab[Ch]; - OldKey[3]+=Ch+(CRCTab[Ch]>>16); - Password++; - } -} - - -void CryptData::SetAV15Encryption() -{ - OldKey[0]=0x4765; - OldKey[1]=0x9021; - OldKey[2]=0x7382; - OldKey[3]=0x5215; -} - - -void CryptData::SetCmt13Encryption() -{ - PN1=0; - PN2=7; - PN3=77; -} - - -void CryptData::Crypt(byte *Data,uint Count,int Method) -{ - if (Method==OLD_DECODE) - Decode13(Data,Count); - else - if (Method==OLD_ENCODE) - Encode13(Data,Count); - else - Crypt15(Data,Count); -} - - -void CryptData::Encode13(byte *Data,uint Count) -{ - while (Count--) - { - PN2+=PN3; - PN1+=PN2; - *Data+=PN1; - Data++; - } -} - - -void CryptData::Decode13(byte *Data,uint Count) -{ - while (Count--) - { - PN2+=PN3; - PN1+=PN2; - *Data-=PN1; - Data++; - } -} - - -void CryptData::Crypt15(byte *Data,uint Count) -{ - while (Count--) - { - OldKey[0]+=0x1234; - OldKey[1]^=CRCTab[(OldKey[0] & 0x1fe)>>1]; - OldKey[2]-=CRCTab[(OldKey[0] & 0x1fe)>>1]>>16; - OldKey[0]^=OldKey[2]; - OldKey[3]=ror(OldKey[3]&0xffff,1,16)^OldKey[1]; - OldKey[3]=ror(OldKey[3]&0xffff,1,16); - OldKey[0]^=OldKey[3]; - *Data^=(byte)(OldKey[0]>>8); - Data++; - } -} #endif + case CRYPT_RAR30: + case CRYPT_RAR50: + rin.blockDecrypt(Buf,Size,Buf); + break; + } +} +bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, + SecPassword *Password,const byte *Salt, + const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck) +{ + if (!Password->IsSet() || Method==CRYPT_NONE) + return false; + + CryptData::Method=Method; + + wchar PwdW[MAXPASSWORD]; + Password->Get(PwdW,ASIZE(PwdW)); + char PwdA[MAXPASSWORD]; + WideToChar(PwdW,PwdA,ASIZE(PwdA)); + + switch(Method) + { +#ifndef SFX_MODULE + case CRYPT_RAR13: + SetKey13(PwdA); + break; + case CRYPT_RAR15: + SetKey15(PwdA); + break; + case CRYPT_RAR20: + SetKey20(PwdA); + break; +#endif + case CRYPT_RAR30: + SetKey30(Encrypt,Password,PwdW,Salt); + break; + case CRYPT_RAR50: + SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); + break; + } + cleandata(PwdA,sizeof(PwdA)); + cleandata(PwdW,sizeof(PwdW)); + return true; +} + + +// Use the current system time to additionally randomize data. +static void TimeRandomize(byte *RndBuf,size_t BufSize) +{ + static uint Count=0; + RarTime CurTime; + CurTime.SetCurrentTime(); + uint64 Random=CurTime.GetWin()+clock(); + for (size_t I=0;I> ( (I & 7) * 8 )); + RndBuf[I]=byte( (RndByte ^ I) + Count++); + } +} + + + + +// Fill buffer with random data. +void GetRnd(byte *RndBuf,size_t BufSize) +{ + bool Success=false; +#if defined(_WIN_ALL) + HCRYPTPROV hProvider = 0; + if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE; + CryptReleaseContext(hProvider, 0); + } +#elif defined(_UNIX) + FILE *rndf = fopen("/dev/urandom", "r"); + if (rndf!=NULL) + { + Success=fread(RndBuf, BufSize, 1, rndf) == BufSize; + fclose(rndf); + } +#endif + // We use this code only as the last resort if code above failed. + if (!Success) + TimeRandomize(RndBuf,BufSize); +} diff --git a/libunrar/crypt.hpp b/libunrar/crypt.hpp index b16e650..f6382ef 100644 --- a/libunrar/crypt.hpp +++ b/libunrar/crypt.hpp @@ -1,62 +1,101 @@ #ifndef _RAR_CRYPT_ #define _RAR_CRYPT_ -enum { OLD_DECODE=0,OLD_ENCODE=1,NEW_CRYPT=2 }; - -struct CryptKeyCacheItem -{ -#ifndef _SFX_RTL_ - CryptKeyCacheItem() - { - *Password=0; - } - - ~CryptKeyCacheItem() - { - memset(AESKey,0,sizeof(AESKey)); - memset(AESInit,0,sizeof(AESInit)); - memset(Password,0,sizeof(Password)); - } -#endif - byte AESKey[16],AESInit[16]; - char Password[MAXPASSWORD]; - bool SaltPresent; - byte Salt[SALT_SIZE]; - bool HandsOffHash; +enum CRYPT_METHOD { + CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50 }; +#define SIZE_SALT50 16 +#define SIZE_SALT30 8 +#define SIZE_INITV 16 +#define SIZE_PSWCHECK 8 +#define SIZE_PSWCHECK_CSUM 4 + +#define CRYPT_BLOCK_SIZE 16 +#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf + +#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count. +#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count. +#define CRYPT_VERSION 0 // Supported encryption version. + + class CryptData { + struct KDF5CacheItem + { + SecPassword Pwd; + byte Salt[SIZE_SALT50]; + byte Key[32]; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte PswCheckValue[SHA256_DIGEST_SIZE]; + byte HashKeyValue[SHA256_DIGEST_SIZE]; + }; + + struct KDF3CacheItem + { + SecPassword Pwd; + byte Salt[SIZE_SALT30]; + byte Key[16]; + byte Init[16]; + bool SaltPresent; + }; + + private: - void Encode13(byte *Data,uint Count); - void Decode13(byte *Data,uint Count); - void Crypt15(byte *Data,uint Count); - void UpdKeys(byte *Buf); - void Swap(byte *Ch1,byte *Ch2); - void SetOldKeys(const char *Password); + void SetKey13(const char *Password); + void Decrypt13(byte *Data,size_t Count); - Rijndael rin; - - byte SubstTable[256]; - uint Key[4]; - ushort OldKey[4]; - byte PN1,PN2,PN3; + void SetKey15(const char *Password); + void Crypt15(byte *Data,size_t Count); - byte AESKey[16],AESInit[16]; - - static CryptKeyCacheItem Cache[4]; - static int CachePos; - public: - void SetCryptKeys(const char *Password,const byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash); - void SetAV15Encryption(); - void SetCmt13Encryption(); + void SetKey20(const char *Password); + void Swap20(byte *Ch1,byte *Ch2); + void UpdKeys20(byte *Buf); void EncryptBlock20(byte *Buf); void DecryptBlock20(byte *Buf); + + void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt); + void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); + + KDF3CacheItem KDF3Cache[4]; + uint KDF3CachePos; + + KDF5CacheItem KDF5Cache[4]; + uint KDF5CachePos; + + CRYPT_METHOD Method; + + Rijndael rin; + + uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption. + + byte SubstTable20[256]; + uint Key20[4]; + + byte Key13[3]; + ushort Key15[4]; + public: + CryptData(); + ~CryptData(); + bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, + const byte *Salt,const byte *InitV,uint Lg2Cnt, + byte *HashKey,byte *PswCheck); + void SetAV15Encryption(); + void SetCmt13Encryption(); void EncryptBlock(byte *Buf,size_t Size); void DecryptBlock(byte *Buf,size_t Size); - void Crypt(byte *Data,uint Count,int Method); - static void SetSalt(byte *Salt,int SaltSize); + static void SetSalt(byte *Salt,size_t SaltSize); }; +void GetRnd(byte *RndBuf,size_t BufSize); + +void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest); +void pbkdf2(const byte *pass, size_t pass_len, const byte *salt, + size_t salt_len,byte *key, byte *Value1, byte *Value2, + uint rounds); + +void ConvertHashToMAC(HashValue *Value,byte *Key); + #endif diff --git a/libunrar/crypt1.cpp b/libunrar/crypt1.cpp new file mode 100644 index 0000000..1426393 --- /dev/null +++ b/libunrar/crypt1.cpp @@ -0,0 +1,79 @@ +extern uint CRCTab[256]; + +void CryptData::SetKey13(const char *Password) +{ + Key13[0]=Key13[1]=Key13[2]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key13[0]+=P; + Key13[1]^=P; + Key13[2]+=P; + Key13[2]=(byte)rotls(Key13[2],1,8); + } +} + + +void CryptData::SetKey15(const char *Password) +{ + InitCRC32(CRCTab); + uint PswCRC=CRC32(0xffffffff,Password,strlen(Password)); + Key15[0]=PswCRC&0xffff; + Key15[1]=(PswCRC>>16)&0xffff; + Key15[2]=Key15[3]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key15[2]^=P^CRCTab[P]; + Key15[3]+=P+(CRCTab[P]>>16); + } +} + + +void CryptData::SetAV15Encryption() +{ + InitCRC32(CRCTab); + Method=CRYPT_RAR15; + Key15[0]=0x4765; + Key15[1]=0x9021; + Key15[2]=0x7382; + Key15[3]=0x5215; +} + + +void CryptData::SetCmt13Encryption() +{ + Method=CRYPT_RAR13; + Key13[0]=0; + Key13[1]=7; + Key13[2]=77; +} + + +void CryptData::Decrypt13(byte *Data,size_t Count) +{ + while (Count--) + { + Key13[1]+=Key13[2]; + Key13[0]+=Key13[1]; + *Data-=Key13[0]; + Data++; + } +} + + +void CryptData::Crypt15(byte *Data,size_t Count) +{ + while (Count--) + { + Key15[0]+=0x1234; + Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1]; + Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16; + Key15[0]^=Key15[2]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16); + Key15[0]^=Key15[3]; + *Data^=(byte)(Key15[0]>>8); + Data++; + } +} diff --git a/libunrar/crypt2.cpp b/libunrar/crypt2.cpp new file mode 100644 index 0000000..5fa4a97 --- /dev/null +++ b/libunrar/crypt2.cpp @@ -0,0 +1,133 @@ +#define NROUNDS 32 + +#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \ + ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \ + ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \ + ((uint)SubstTable20[(int)(t>>24)&255]<<24) ) + + +static byte InitSubstTable20[256]={ + 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, + 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, + 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, + 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, + 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, + 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, + 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, + 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, + 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, + 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, + 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, + 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, + 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, + 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, + 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, + 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 +}; + + +void CryptData::SetKey20(const char *Password) +{ + InitCRC32(CRCTab); + + char Psw[MAXPASSWORD]; + strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below. + size_t PswLength=strlen(Psw); + + Key20[0]=0xD3A3B879L; + Key20[1]=0x3F6D12F7L; + Key20[2]=0x7515A235L; + Key20[3]=0xA4E7F123L; + + memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20)); + for (uint J=0;J<256;J++) + for (size_t I=0;I=0;I--) + { + T=((C+rotls(D,11,32))^Key20[I&3]); + TA=A^substLong(T); + T=((D^rotls(C,17,32))+Key20[I&3]); + TB=B^substLong(T); + A=C; + B=D; + C=TA; + D=TB; + } + RawPut4(C^Key20[0],Buf+0); + RawPut4(D^Key20[1],Buf+4); + RawPut4(A^Key20[2],Buf+8); + RawPut4(B^Key20[3],Buf+12); + UpdKeys20(InBuf); +} + + +void CryptData::UpdKeys20(byte *Buf) +{ + for (int I=0;I<16;I+=4) + { + Key20[0]^=CRCTab[Buf[I]]; + Key20[1]^=CRCTab[Buf[I+1]]; + Key20[2]^=CRCTab[Buf[I+2]]; + Key20[3]^=CRCTab[Buf[I+3]]; + } +} + + +void CryptData::Swap20(byte *Ch1,byte *Ch2) +{ + byte Ch=*Ch1; + *Ch1=*Ch2; + *Ch2=Ch; +} diff --git a/libunrar/crypt3.cpp b/libunrar/crypt3.cpp new file mode 100644 index 0000000..fe3bf97 --- /dev/null +++ b/libunrar/crypt3.cpp @@ -0,0 +1,68 @@ +void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt) +{ + byte AESKey[16],AESInit[16]; + + bool Cached=false; + for (uint I=0;I>8); + PswNum[2]=(byte)(I>>16); + sha1_process(&c, PswNum, 3); + if (I%(HashRounds/16)==0) + { + sha1_context tempc=c; + uint32 digest[5]; + sha1_done( &tempc, digest ); + AESInit[I/(HashRounds/16)]=(byte)digest[4]; + } + } + uint32 digest[5]; + sha1_done( &c, digest ); + for (uint I=0;I<4;I++) + for (uint J=0;J<4;J++) + AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); + + KDF3Cache[KDF3CachePos].Pwd=*Password; + if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true) + memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30); + memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey)); + SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false); + memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit)); + KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache); + + cleandata(RawPsw,sizeof(RawPsw)); + } + rin.Init(Encrypt, AESKey, 128, AESInit); + cleandata(AESKey,sizeof(AESKey)); + cleandata(AESInit,sizeof(AESInit)); +} + diff --git a/libunrar/crypt5.cpp b/libunrar/crypt5.cpp new file mode 100644 index 0000000..7562469 --- /dev/null +++ b/libunrar/crypt5.cpp @@ -0,0 +1,233 @@ +static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest, + sha256_context *ICtxOpt,bool *SetIOpt, + sha256_context *RCtxOpt,bool *SetROpt) +{ + const size_t Sha256BlockSize=64; // As defined in RFC 4868. + + byte KeyHash[SHA256_DIGEST_SIZE]; + if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash. + { + sha256_context KCtx; + sha256_init(&KCtx); + sha256_process(&KCtx, Key, KeyLength); + sha256_done(&KCtx, KeyHash); + + Key = KeyHash; + KeyLength = SHA256_DIGEST_SIZE; + } + + byte KeyBuf[Sha256BlockSize]; // Store the padded key here. + sha256_context ICtx; + + if (ICtxOpt!=NULL && *SetIOpt) + ICtx=*ICtxOpt; // Use already calculated first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest. + KeyBuf[I] = Key[I] ^ 0x36; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x36; + + sha256_init(&ICtx); + sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse. + { + *ICtxOpt=ICtx; + *SetIOpt=true; + } + + sha256_process(&ICtx, Data, DataLength); // Hash data. + + byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data. + sha256_done(&ICtx, IDig); + + sha256_context RCtx; + + if (RCtxOpt!=NULL && *SetROpt) + RCtx=*RCtxOpt; // Use already calculated first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding. + KeyBuf[I] = Key[I] ^ 0x5c; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x5c; + + sha256_init(&RCtx); + sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse. + { + *RCtxOpt=RCtx; + *SetROpt=true; + } + + sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest. + + sha256_done(&RCtx, ResDigest); +} + + +// PBKDF2 for 32 byte key length. We generate the key for specified number +// of iteration count also as two supplementary values (key for checksums +// and password verification) for iterations+16 and iterations+32. +void pbkdf2(const byte *Pwd, size_t PwdLength, + const byte *Salt, size_t SaltLength, + byte *Key, byte *V1, byte *V2, uint Count) +{ + const size_t MaxSalt=64; + byte SaltData[MaxSalt+4]; + memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); + + SaltData[SaltLength + 0] = 0; // Salt concatenated to 1. + SaltData[SaltLength + 1] = 0; + SaltData[SaltLength + 2] = 0; + SaltData[SaltLength + 3] = 1; + + // First iteration: HMAC of password, salt and block index (1). + byte U1[SHA256_DIGEST_SIZE]; + hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL); + byte Fn[SHA256_DIGEST_SIZE]; // Current function value. + memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration. + + uint CurCount[] = { Count-1, 16, 16 }; + byte *CurValue[] = { Key , V1, V2 }; + + sha256_context ICtxOpt,RCtxOpt; + bool SetIOpt=false,SetROpt=false; + + byte U2[SHA256_DIGEST_SIZE]; + for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values. + { + for (uint J = 0; J < CurCount[I]; J++) + { + // U2 = PRF (P, U1). + hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt); + memcpy(U1, U2, sizeof(U1)); + for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U. + Fn[K] ^= U1[K]; + } + memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE); + } + + cleandata(SaltData, sizeof(SaltData)); + cleandata(Fn, sizeof(Fn)); + cleandata(U1, sizeof(U1)); + cleandata(U2, sizeof(U2)); +} + + +void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, + const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey, + byte *PswCheck) +{ + if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX) + return; + + byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; + bool Found=false; + for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password && + memcmp(Item->Salt,Salt,SIZE_SALT50)==0) + { + memcpy(Key,Item->Key,sizeof(Key)); + SecHideData(Key,sizeof(Key),false,false); + + memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue)); + memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue)); + Found=true; + break; + } + } + + if (!Found) + { + char PwdUtf[MAXPASSWORD*4]; + WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf)); + + pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<Lg2Count=Lg2Cnt; + Item->Pwd=*Password; + memcpy(Item->Salt,Salt,SIZE_SALT50); + memcpy(Item->Key,Key,sizeof(Item->Key)); + memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue)); + memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue)); + SecHideData(Item->Key,sizeof(Item->Key),true,false); + } + if (HashKey!=NULL) + memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE); + if (PswCheck!=NULL) + { + memset(PswCheck,0,SIZE_PSWCHECK); + for (uint I=0;IType==HASH_CRC32) + { + byte RawCRC[4]; + RawPut4(Value->CRC32,RawCRC); + byte Digest[SHA256_DIGEST_SIZE]; + hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL); + Value->CRC32=0; + for (uint I=0;ICRC32^=Digest[I] << ((I & 3) * 8); + } + if (Value->Type==HASH_BLAKE2) + { + byte Digest[BLAKE2_DIGEST_SIZE]; + hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL); + memcpy(Value->Digest,Digest,sizeof(Value->Digest)); + } +} + + +#if 0 +static void TestPBKDF2(); +struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF; + +void TestPBKDF2() // Test PBKDF2 HMAC-SHA256 +{ + byte Key[32],V1[32],V2[32]; + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1); + byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b }; + mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096); + byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a }; + mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536); + byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf}; + mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed"); +} +#endif diff --git a/libunrar/dll.cpp b/libunrar/dll.cpp index be5120d..61a68b4 100644 --- a/libunrar/dll.cpp +++ b/libunrar/dll.cpp @@ -1,17 +1,16 @@ #include "rar.hpp" -#include "dll.hpp" -static int RarErrorToDll(int ErrCode); +static int RarErrorToDll(RAR_EXIT ErrCode); struct DataSet { CommandData Cmd; - CmdExtract Extract; Archive Arc; + CmdExtract Extract; int OpenMode; int HeaderSize; - DataSet():Arc(&Cmd) {}; + DataSet():Arc(&Cmd),Extract(&Cmd) {}; }; @@ -27,124 +26,183 @@ HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r) r->OpenResult=rx.OpenResult; r->CmtSize=rx.CmtSize; r->CmtState=rx.CmtState; - return(hArc); + return hArc; } HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) { + DataSet *Data=NULL; try { + ErrHandler.Clean(); + r->OpenResult=0; - DataSet *Data=new DataSet; + Data=new DataSet; Data->Cmd.DllError=0; Data->OpenMode=r->OpenMode; - Data->Cmd.FileArgs->AddString("*"); + Data->Cmd.FileArgs.AddString(L"*"); + Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0; - char an[NM]; - if (r->ArcName==NULL && r->ArcNameW!=NULL) + char AnsiArcName[NM]; + *AnsiArcName=0; + if (r->ArcName!=NULL) { - WideToChar(r->ArcNameW,an,NM); - r->ArcName=an; + strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName)); +#ifdef _WIN_ALL + if (!AreFileApisANSI()) + { + OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName)); + AnsiArcName[ASIZE(AnsiArcName)-1]=0; + } +#endif } - Data->Cmd.AddArcName(r->ArcName,r->ArcNameW); + wchar ArcName[NM]; + GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName)); + + Data->Cmd.AddArcName(ArcName); Data->Cmd.Overwrite=OVERWRITE_ALL; Data->Cmd.VersionControl=1; - if (!Data->Arc.Open(r->ArcName,r->ArcNameW)) + + Data->Cmd.Callback=r->Callback; + Data->Cmd.UserData=r->UserData; + + // Open shared mode is added by request of dll users, who need to + // browse and unpack archives while downloading. + Data->Cmd.OpenShared = true; + if (!Data->Arc.Open(ArcName,FMF_OPENSHARED)) { r->OpenResult=ERAR_EOPEN; delete Data; - return(NULL); + return NULL; } - if (!Data->Arc.IsArchive(false)) + if (!Data->Arc.IsArchive(true)) { - r->OpenResult=Data->Cmd.DllError!=0 ? Data->Cmd.DllError:ERAR_BAD_ARCHIVE; + if (Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + { + RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); + if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING) + r->OpenResult=RarErrorToDll(ErrCode); + else + r->OpenResult=ERAR_BAD_ARCHIVE; + } delete Data; - return(NULL); + return NULL; } - r->Flags=Data->Arc.NewMhd.Flags; - Array CmtData; - if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtData,NULL)) + r->Flags=0; + + if (Data->Arc.Volume) + r->Flags|=ROADF_VOLUME; + if (Data->Arc.MainComment) + r->Flags|=ROADF_COMMENT; + if (Data->Arc.Locked) + r->Flags|=ROADF_LOCK; + if (Data->Arc.Solid) + r->Flags|=ROADF_SOLID; + if (Data->Arc.NewNumbering) + r->Flags|=ROADF_NEWNUMBERING; + if (Data->Arc.Signed) + r->Flags|=ROADF_SIGNED; + if (Data->Arc.Protected) + r->Flags|=ROADF_RECOVERY; + if (Data->Arc.Encrypted) + r->Flags|=ROADF_ENCHEADERS; + if (Data->Arc.FirstVolume) + r->Flags|=ROADF_FIRSTVOLUME; + + Array CmtDataW; + if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) { - r->Flags|=2; - size_t Size=CmtData.Size()+1; - r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; - r->CmtSize=(uint)Min(Size,r->CmtBufSize); - memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); - if (Size<=r->CmtBufSize) - r->CmtBuf[r->CmtSize-1]=0; + if (r->CmtBufW!=NULL) + { + CmtDataW.Push(0); + size_t Size=wcslen(&CmtDataW[0])+1; + + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW)); + r->CmtBufW[r->CmtSize-1]=0; + } + else + if (r->CmtBuf!=NULL) + { + Array CmtData(CmtDataW.Size()*4+1); + memset(&CmtData[0],0,CmtData.Size()); + WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1); + size_t Size=strlen(&CmtData[0])+1; + + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); + r->CmtBuf[r->CmtSize-1]=0; + } } else r->CmtState=r->CmtSize=0; - if (Data->Arc.Signed) - r->Flags|=0x20; - Data->Extract.ExtractArchiveInit(&Data->Cmd,Data->Arc); - return((HANDLE)Data); + Data->Extract.ExtractArchiveInit(Data->Arc); + return (HANDLE)Data; } - catch (int ErrCode) + catch (RAR_EXIT ErrCode) { - r->OpenResult=RarErrorToDll(ErrCode); - return(NULL); + if (Data!=NULL && Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + r->OpenResult=RarErrorToDll(ErrCode); + if (Data != NULL) + delete Data; + return NULL; } + catch (std::bad_alloc&) // Catch 'new' exception. + { + r->OpenResult=ERAR_NO_MEMORY; + if (Data != NULL) + delete Data; + } + return NULL; // To make compilers happy. } int PASCAL RARCloseArchive(HANDLE hArcData) { DataSet *Data=(DataSet *)hArcData; - bool Success=Data==NULL ? false:Data->Arc.Close(); - delete Data; - return(Success ? 0:ERAR_ECLOSE); + try + { + bool Success=Data==NULL ? false:Data->Arc.Close(); + delete Data; + return Success ? ERAR_SUCCESS : ERAR_ECLOSE; + } + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } } int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) { - DataSet *Data=(DataSet *)hArcData; - try - { - if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(FILE_HEAD))<=0) - { - if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD && - (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)) - if (MergeArchive(Data->Arc,NULL,false,'L')) - { - Data->Extract.SignatureFound=false; - Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); - return(RARReadHeader(hArcData,D)); - } - else - return(ERAR_EOPEN); - return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE); - } - if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)) - { - int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); - if (Code==0) - return(RARReadHeader(hArcData,D)); - else - return(Code); - } - strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName)); - strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName)); - D->Flags=Data->Arc.NewLhd.Flags; - D->PackSize=Data->Arc.NewLhd.PackSize; - D->UnpSize=Data->Arc.NewLhd.UnpSize; - D->HostOS=Data->Arc.NewLhd.HostOS; - D->FileCRC=Data->Arc.NewLhd.FileCRC; - D->FileTime=Data->Arc.NewLhd.FileTime; - D->UnpVer=Data->Arc.NewLhd.UnpVer; - D->Method=Data->Arc.NewLhd.Method; - D->FileAttr=Data->Arc.NewLhd.FileAttr; - D->CmtSize=0; - D->CmtState=0; - } - catch (int ErrCode) - { - return(RarErrorToDll(ErrCode)); - } - return(0); + struct RARHeaderDataEx X; + memset(&X,0,sizeof(X)); + + int Code=RARReadHeaderEx(hArcData,&X); + + strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName)); + strncpyz(D->FileName,X.FileName,ASIZE(D->FileName)); + D->Flags=X.Flags; + D->PackSize=X.PackSize; + D->UnpSize=X.UnpSize; + D->HostOS=X.HostOS; + D->FileCRC=X.FileCRC; + D->FileTime=X.FileTime; + D->UnpVer=X.UnpVer; + D->Method=X.Method; + D->FileAttr=X.FileAttr; + D->CmtSize=0; + D->CmtState=0; + + return Code; } @@ -153,65 +211,114 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) DataSet *Data=(DataSet *)hArcData; try { - if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(FILE_HEAD))<=0) + if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0) { - if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD && - (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)) + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC && + Data->Arc.EndArcHead.NextVolume) if (MergeArchive(Data->Arc,NULL,false,'L')) { - Data->Extract.SignatureFound=false; Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); - return(RARReadHeaderEx(hArcData,D)); + return RARReadHeaderEx(hArcData,D); } else - return(ERAR_EOPEN); - return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE); + return ERAR_EOPEN; + + if (Data->Arc.BrokenHeader) + return ERAR_BAD_DATA; + + // Might be necessary if RARSetPassword is still called instead of + // open callback for RAR5 archives and if password is invalid. + if (Data->Arc.FailedHeaderDecryption) + return ERAR_BAD_PASSWORD; + + return ERAR_END_ARCHIVE; } - if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)) + FileHeader *hd=&Data->Arc.FileHead; + if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore) { int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); if (Code==0) - return(RARReadHeaderEx(hArcData,D)); + return RARReadHeaderEx(hArcData,D); else - return(Code); + return Code; } - strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName)); - if (*Data->Arc.FileNameW) - strncpyw(D->ArcNameW,Data->Arc.FileNameW,sizeof(D->ArcNameW)); - else - CharToWide(Data->Arc.FileName,D->ArcNameW); - strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName)); - if (*Data->Arc.NewLhd.FileNameW) - strncpyw(D->FileNameW,Data->Arc.NewLhd.FileNameW,sizeof(D->FileNameW)); - else - { -#ifdef _WIN_32 - char AnsiName[NM]; - OemToChar(Data->Arc.NewLhd.FileName,AnsiName); - CharToWide(AnsiName,D->FileNameW); -#else - CharToWide(Data->Arc.NewLhd.FileName,D->FileNameW); + wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW)); + WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); + + wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW)); + WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); +#ifdef _WIN_ALL + CharToOemA(D->FileName,D->FileName); #endif - } - D->Flags=Data->Arc.NewLhd.Flags; - D->PackSize=Data->Arc.NewLhd.PackSize; - D->PackSizeHigh=Data->Arc.NewLhd.HighPackSize; - D->UnpSize=Data->Arc.NewLhd.UnpSize; - D->UnpSizeHigh=Data->Arc.NewLhd.HighUnpSize; - D->HostOS=Data->Arc.NewLhd.HostOS; - D->FileCRC=Data->Arc.NewLhd.FileCRC; - D->FileTime=Data->Arc.NewLhd.FileTime; - D->UnpVer=Data->Arc.NewLhd.UnpVer; - D->Method=Data->Arc.NewLhd.Method; - D->FileAttr=Data->Arc.NewLhd.FileAttr; + + D->Flags=0; + if (hd->SplitBefore) + D->Flags|=RHDF_SPLITBEFORE; + if (hd->SplitAfter) + D->Flags|=RHDF_SPLITAFTER; + if (hd->Encrypted) + D->Flags|=RHDF_ENCRYPTED; + if (hd->Solid) + D->Flags|=RHDF_SOLID; + if (hd->Dir) + D->Flags|=RHDF_DIRECTORY; + + D->PackSize=uint(hd->PackSize & 0xffffffff); + D->PackSizeHigh=uint(hd->PackSize>>32); + D->UnpSize=uint(hd->UnpSize & 0xffffffff); + D->UnpSizeHigh=uint(hd->UnpSize>>32); + D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX; + D->UnpVer=Data->Arc.FileHead.UnpVer; + D->FileCRC=hd->FileHash.CRC32; + D->FileTime=hd->mtime.GetDos(); + + uint64 MRaw=hd->mtime.GetWin(); + D->MtimeLow=(uint)MRaw; + D->MtimeHigh=(uint)(MRaw>>32); + uint64 CRaw=hd->ctime.GetWin(); + D->CtimeLow=(uint)CRaw; + D->CtimeHigh=(uint)(CRaw>>32); + uint64 ARaw=hd->atime.GetWin(); + D->AtimeLow=(uint)ARaw; + D->AtimeHigh=(uint)(ARaw>>32); + + D->Method=hd->Method+0x30; + D->FileAttr=hd->FileAttr; D->CmtSize=0; D->CmtState=0; + + D->DictSize=uint(hd->WinSize/1024); + + switch (hd->FileHash.Type) + { + case HASH_RAR14: + case HASH_CRC32: + D->HashType=RAR_HASH_CRC32; + break; + case HASH_BLAKE2: + D->HashType=RAR_HASH_BLAKE2; + memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + break; + default: + D->HashType=RAR_HASH_NONE; + break; + } + + D->RedirType=hd->RedirType; + // RedirNameSize sanity check is useful in case some developer + // did not initialize Reserved area with 0 as required in docs. + // We have taken 'Redir*' fields from Reserved area. We may remove + // this RedirNameSize check sometimes later. + if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL && + D->RedirNameSize>0 && D->RedirNameSize<100000) + wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize); + D->DirTarget=hd->DirTarget; } - catch (int ErrCode) + catch (RAR_EXIT ErrCode) { - return(RarErrorToDll(ErrCode)); + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } - return(0); + return ERAR_SUCCESS; } @@ -224,88 +331,101 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT || Operation==RAR_SKIP && !Data->Arc.Solid) { - if (Data->Arc.Volume && - Data->Arc.GetHeaderType()==FILE_HEAD && - (Data->Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0) + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE && + Data->Arc.FileHead.SplitAfter) if (MergeArchive(Data->Arc,NULL,false,'L')) { - Data->Extract.SignatureFound=false; Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); - return(0); + return ERAR_SUCCESS; } else - return(ERAR_EOPEN); + return ERAR_EOPEN; Data->Arc.SeekToNext(); } else { Data->Cmd.DllOpMode=Operation; - if (DestPath!=NULL || DestName!=NULL) + *Data->Cmd.ExtrPath=0; + *Data->Cmd.DllDestName=0; + + if (DestPath!=NULL) { -#ifdef _WIN_32 - OemToChar(NullToEmpty(DestPath),Data->Cmd.ExtrPath); -#else - strcpy(Data->Cmd.ExtrPath,NullToEmpty(DestPath)); + char ExtrPathA[NM]; + strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestPath, + // because we do not know DestPath length and OemToCharBuffA + // does not stop at 0. + OemToCharA(ExtrPathA,ExtrPathA); #endif - AddEndSlash(Data->Cmd.ExtrPath); -#ifdef _WIN_32 - OemToChar(NullToEmpty(DestName),Data->Cmd.DllDestName); -#else - strcpy(Data->Cmd.DllDestName,NullToEmpty(DestName)); + CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + } + if (DestName!=NULL) + { + char DestNameA[NM]; + strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestName, + // because we do not know DestName length and OemToCharBuffA + // does not stop at 0. + OemToCharA(DestNameA,DestNameA); #endif - } - else - { - *Data->Cmd.ExtrPath=0; - *Data->Cmd.DllDestName=0; + CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName)); } - if (DestPathW!=NULL || DestNameW!=NULL) + if (DestPathW!=NULL) { - strncpyw(Data->Cmd.ExtrPathW,NullToEmpty(DestPathW),NM-2); - AddEndSlash(Data->Cmd.ExtrPathW); - strncpyw(Data->Cmd.DllDestNameW,NullToEmpty(DestNameW),NM-1); - - if (*Data->Cmd.DllDestNameW!=0 && *Data->Cmd.DllDestName==0) - WideToChar(Data->Cmd.DllDestNameW,Data->Cmd.DllDestName); - } - else - { - *Data->Cmd.ExtrPathW=0; - *Data->Cmd.DllDestNameW=0; + wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); } - strcpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? "X":"T"); + if (DestNameW!=NULL) + wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName)); + + wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command)); Data->Cmd.Test=Operation!=RAR_EXTRACT; bool Repeat=false; - Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat); + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); - while (Data->Arc.ReadHeader()!=0 && Data->Arc.GetHeaderType()==NEWSUB_HEAD) + // Now we process extra file information if any. + // + // Archive can be closed if we process volumes, next volume is missing + // and current one is already removed or deleted. So we need to check + // if archive is still open to avoid calling file operations on + // the invalid file handle. Some of our file operations like Seek() + // process such invalid handle correctly, some not. + while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && + Data->Arc.GetHeaderType()==HEAD_SERVICE) { - Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat); + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); Data->Arc.SeekToNext(); } Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); } } - catch (int ErrCode) + catch (std::bad_alloc&) { - return(RarErrorToDll(ErrCode)); + return ERAR_NO_MEMORY; } - return(Data->Cmd.DllError); + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } + return Data->Cmd.DllError; } int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName) { - return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL)); + return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL); } int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName) { - return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName)); + return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName); } @@ -330,40 +450,46 @@ void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataPro Data->Cmd.ProcessDataProc=ProcessDataProc; } -#ifndef NOCRYPT + void PASCAL RARSetPassword(HANDLE hArcData,char *Password) { +#ifndef RAR_NOCRYPT DataSet *Data=(DataSet *)hArcData; - strncpyz(Data->Cmd.Password,Password,ASIZE(Data->Cmd.Password)); -} + wchar PasswordW[MAXPASSWORD]; + GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); + Data->Cmd.Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); #endif +} int PASCAL RARGetDllVersion() { - return(RAR_DLL_VERSION); + return RAR_DLL_VERSION; } -static int RarErrorToDll(int ErrCode) +static int RarErrorToDll(RAR_EXIT ErrCode) { switch(ErrCode) { - case FATAL_ERROR: - return(ERAR_EREAD); - case CRC_ERROR: - return(ERAR_BAD_DATA); - case WRITE_ERROR: - return(ERAR_EWRITE); - case OPEN_ERROR: - return(ERAR_EOPEN); - case CREATE_ERROR: - return(ERAR_ECREATE); - case MEMORY_ERROR: - return(ERAR_NO_MEMORY); - case SUCCESS: - return(0); + case RARX_FATAL: + return ERAR_EREAD; + case RARX_CRC: + return ERAR_BAD_DATA; + case RARX_WRITE: + return ERAR_EWRITE; + case RARX_OPEN: + return ERAR_EOPEN; + case RARX_CREATE: + return ERAR_ECREATE; + case RARX_MEMORY: + return ERAR_NO_MEMORY; + case RARX_BADPWD: + return ERAR_BAD_PASSWORD; + case RARX_SUCCESS: + return ERAR_SUCCESS; // 0. default: - return(ERAR_UNKNOWN); + return ERAR_UNKNOWN; } } diff --git a/libunrar/dll.def b/libunrar/dll.def index 660f69b..3c9a2c8 100644 --- a/libunrar/dll.def +++ b/libunrar/dll.def @@ -5,6 +5,7 @@ EXPORTS RARReadHeader RARReadHeaderEx RARProcessFile + RARProcessFileW RARSetCallback RARSetChangeVolProc RARSetProcessDataProc diff --git a/libunrar/dll.hpp b/libunrar/dll.hpp index 4582f2c..c785ff1 100644 --- a/libunrar/dll.hpp +++ b/libunrar/dll.hpp @@ -1,6 +1,9 @@ #ifndef _UNRAR_DLL_ #define _UNRAR_DLL_ +#pragma pack(push, 1) + +#define ERAR_SUCCESS 0 #define ERAR_END_ARCHIVE 10 #define ERAR_NO_MEMORY 11 #define ERAR_BAD_DATA 12 @@ -14,6 +17,8 @@ #define ERAR_SMALL_BUF 20 #define ERAR_UNKNOWN 21 #define ERAR_MISSING_PASSWORD 22 +#define ERAR_EREFERENCE 23 +#define ERAR_BAD_PASSWORD 24 #define RAR_OM_LIST 0 #define RAR_OM_EXTRACT 1 @@ -26,7 +31,12 @@ #define RAR_VOL_ASK 0 #define RAR_VOL_NOTIFY 1 -#define RAR_DLL_VERSION 4 +#define RAR_DLL_VERSION 8 + +#define RAR_HASH_NONE 0 +#define RAR_HASH_CRC32 1 +#define RAR_HASH_BLAKE2 2 + #ifdef _UNIX #define CALLBACK @@ -37,6 +47,13 @@ #define UINT unsigned int #endif +#define RHDF_SPLITBEFORE 0x01 +#define RHDF_SPLITAFTER 0x02 +#define RHDF_ENCRYPTED 0x04 +#define RHDF_SOLID 0x10 +#define RHDF_DIRECTORY 0x20 + + struct RARHeaderData { char ArcName[260]; @@ -78,7 +95,20 @@ struct RARHeaderDataEx unsigned int CmtBufSize; unsigned int CmtSize; unsigned int CmtState; - unsigned int Reserved[1024]; + unsigned int DictSize; + unsigned int HashType; + char Hash[32]; + unsigned int RedirType; + wchar_t *RedirName; + unsigned int RedirNameSize; + unsigned int DirTarget; + unsigned int MtimeLow; + unsigned int MtimeHigh; + unsigned int CtimeLow; + unsigned int CtimeHigh; + unsigned int AtimeLow; + unsigned int AtimeHigh; + unsigned int Reserved[988]; }; @@ -93,26 +123,43 @@ struct RAROpenArchiveData unsigned int CmtState; }; +typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); + +#define ROADF_VOLUME 0x0001 +#define ROADF_COMMENT 0x0002 +#define ROADF_LOCK 0x0004 +#define ROADF_SOLID 0x0008 +#define ROADF_NEWNUMBERING 0x0010 +#define ROADF_SIGNED 0x0020 +#define ROADF_RECOVERY 0x0040 +#define ROADF_ENCHEADERS 0x0080 +#define ROADF_FIRSTVOLUME 0x0100 + +#define ROADOF_KEEPBROKEN 0x0001 + struct RAROpenArchiveDataEx { char *ArcName; wchar_t *ArcNameW; - unsigned int OpenMode; - unsigned int OpenResult; + unsigned int OpenMode; + unsigned int OpenResult; char *CmtBuf; - unsigned int CmtBufSize; - unsigned int CmtSize; - unsigned int CmtState; - unsigned int Flags; - unsigned int Reserved[32]; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Flags; + UNRARCALLBACK Callback; + LPARAM UserData; + unsigned int OpFlags; + wchar_t *CmtBufW; + unsigned int Reserved[25]; }; enum UNRARCALLBACK_MESSAGES { - UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD + UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, + UCM_NEEDPASSWORDW }; -typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); - typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); @@ -137,4 +184,6 @@ int PASCAL RARGetDllVersion(); } #endif +#pragma pack(pop) + #endif diff --git a/libunrar/dll.rc b/libunrar/dll.rc new file mode 100644 index 0000000..7ff2021 --- /dev/null +++ b/libunrar/dll.rc @@ -0,0 +1,28 @@ +#include +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 5, 91, 100, 3470 +PRODUCTVERSION 5, 91, 100, 3470 +FILEOS VOS__WINDOWS32 +FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "CompanyName", "Alexander Roshal\0" + VALUE "ProductName", "RAR decompression library\0" + VALUE "FileDescription", "RAR decompression library\0" + VALUE "FileVersion", "5.91.0\0" + VALUE "ProductVersion", "5.91.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2020\0" + VALUE "OriginalFilename", "Unrar.dll\0" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04E4 + } +} + diff --git a/libunrar/dll_nocrypt.def b/libunrar/dll_nocrypt.def new file mode 100644 index 0000000..d473e97 --- /dev/null +++ b/libunrar/dll_nocrypt.def @@ -0,0 +1,13 @@ +EXPORTS + RAROpenArchive + RAROpenArchiveEx + RARCloseArchive + RARReadHeader + RARReadHeaderEx + RARProcessFile + RARProcessFileW + RARSetCallback + RARSetChangeVolProc + RARSetProcessDataProc +; RARSetPassword + RARGetDllVersion diff --git a/libunrar/encname.cpp b/libunrar/encname.cpp index 4e42a75..84731a7 100644 --- a/libunrar/encname.cpp +++ b/libunrar/encname.cpp @@ -11,41 +11,53 @@ EncodeFileName::EncodeFileName() -void EncodeFileName::Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW, - size_t MaxDecSize) +void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize, + wchar *NameW,size_t MaxDecSize) { size_t EncPos=0,DecPos=0; - byte HighByte=EncName[EncPos++]; + byte HighByte=EncPos=EncSize) + break; Flags=EncName[EncPos++]; FlagBits=8; } switch(Flags>>6) { case 0: + if (EncPos>=EncSize) + break; NameW[DecPos++]=EncName[EncPos++]; break; case 1: + if (EncPos>=EncSize) + break; NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); break; case 2: + if (EncPos+1>=EncSize) + break; NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); EncPos+=2; break; case 3: { + if (EncPos>=EncSize) + break; int Length=EncName[EncPos++]; - if (Length & 0x80) + if ((Length & 0x80)!=0) { + if (EncPos>=EncSize) + break; byte Correction=EncName[EncPos++]; - for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPos0 && DecPos0 && DecPosMAX_PATH) - { - Log(ArcName && *ArcName ? ArcName:NULL,St(MMaxPathLimit),MAX_PATH); - } - } -#endif + uiMsg(UIERROR_FILECREATE,ArcName,FileName); SysErrMsg(); -#endif + SetErrorCode(RARX_CREATE); } -void ErrorHandler::ReadErrorMsg(const char *ArcName,const char *FileName) +void ErrorHandler::ReadErrorMsg(const wchar *FileName) { -#ifndef SILENT - ErrMsg(ArcName,St(MErrRead),FileName); - SysErrMsg(); -#endif + ReadErrorMsg(NULL,FileName); } -void ErrorHandler::WriteErrorMsg(const char *ArcName,const char *FileName) +void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName) { -#ifndef SILENT - ErrMsg(ArcName,St(MErrWrite),FileName); + uiMsg(UIERROR_FILEREAD,ArcName,FileName); SysErrMsg(); -#endif + SetErrorCode(RARX_FATAL); } -void ErrorHandler::Exit(int ExitCode) +void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName) { -#ifndef SFX_MODULE - Alarm(); -#endif + uiMsg(UIERROR_FILEWRITE,ArcName,FileName); + SysErrMsg(); + SetErrorCode(RARX_WRITE); +} + + +void ErrorHandler::ArcBrokenMsg(const wchar *ArcName) +{ + uiMsg(UIERROR_ARCBROKEN,ArcName); + SetErrorCode(RARX_CRC); +} + + +void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_CHECKSUM,ArcName,FileName); + SetErrorCode(RARX_CRC); +} + + +void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName); + ErrHandler.SetErrorCode(RARX_FATAL); +} + + +void ErrorHandler::Exit(RAR_EXIT ExitCode) +{ + uiAlarm(UIALARM_ERROR); Throw(ExitCode); } -#ifndef GUI -void ErrorHandler::ErrMsg(const char *ArcName,const char *fmt,...) -{ - safebuf char Msg[NM+1024]; - va_list argptr; - va_start(argptr,fmt); - vsprintf(Msg,fmt,argptr); - va_end(argptr); -#ifdef _WIN_32 - if (UserBreak) - Sleep(5000); -#endif - Alarm(); - if (*Msg) - { - Log(ArcName,"\n%s",Msg); - mprintf("\n%s\n",St(MProgAborted)); - } -} -#endif - - -void ErrorHandler::SetErrorCode(int Code) +void ErrorHandler::SetErrorCode(RAR_EXIT Code) { switch(Code) { - case WARNING: - case USER_BREAK: - if (ExitCode==SUCCESS) + case RARX_WARNING: + case RARX_USERBREAK: + if (ExitCode==RARX_SUCCESS) ExitCode=Code; break; - case FATAL_ERROR: - if (ExitCode==SUCCESS || ExitCode==WARNING) - ExitCode=FATAL_ERROR; + case RARX_CRC: + if (ExitCode!=RARX_BADPWD) + ExitCode=Code; + break; + case RARX_FATAL: + if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING) + ExitCode=RARX_FATAL; break; default: ExitCode=Code; @@ -266,8 +254,7 @@ void ErrorHandler::SetErrorCode(int Code) } -#if !defined(GUI) && !defined(_SFX_RTL_) -#ifdef _WIN_32 +#ifdef _WIN_ALL BOOL __stdcall ProcessSignal(DWORD SigType) #else #if defined(__sun) @@ -276,102 +263,147 @@ extern "C" void _stdfunction ProcessSignal(int SigType) #endif { -#ifdef _WIN_32 +#ifdef _WIN_ALL + // When a console application is run as a service, this allows the service + // to continue running after the user logs off. if (SigType==CTRL_LOGOFF_EVENT) - return(TRUE); + return TRUE; #endif - UserBreak=true; + + ErrHandler.UserBreak=true; + ErrHandler.SetDisableShutdown(); mprintf(St(MBreak)); - for (int I=0;!File::RemoveCreated() && I<3;I++) - { -#ifdef _WIN_32 + +#ifdef _WIN_ALL + // Let the main thread to handle 'throw' and destroy file objects. + for (uint I=0;!ErrHandler.MainExit && I<50;I++) Sleep(100); -#endif - } -#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(_WIN_CE) +#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL) ExtRes.UnloadDLL(); #endif - exit(USER_BREAK); -#if defined(_WIN_32) && !defined(_MSC_VER) - // never reached, just to avoid a compiler warning - return(TRUE); + exit(RARX_USERBREAK); +#endif + +#ifdef _UNIX + static uint BreakCount=0; + // User continues to press Ctrl+C, exit immediately without cleanup. + if (++BreakCount>1) + exit(RARX_USERBREAK); + // Otherwise return from signal handler and let Wait() function to close + // files and quit. We cannot use the same approach as in Windows, + // because Unix signal handler can block execution of our main code. +#endif + +#if defined(_WIN_ALL) && !defined(_MSC_VER) + // Never reached, just to avoid a compiler warning + return TRUE; #endif } -#endif void ErrorHandler::SetSignalHandlers(bool Enable) { EnableBreak=Enable; -#if !defined(GUI) && !defined(_SFX_RTL_) -#ifdef _WIN_32 +#ifdef _WIN_ALL SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE); -// signal(SIGBREAK,Enable ? ProcessSignal:SIG_IGN); #else signal(SIGINT,Enable ? ProcessSignal:SIG_IGN); signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN); #endif -#endif } -void ErrorHandler::Throw(int Code) +void ErrorHandler::Throw(RAR_EXIT Code) { - if (Code==USER_BREAK && !EnableBreak) + if (Code==RARX_USERBREAK && !EnableBreak) return; - ErrHandler.SetErrorCode(Code); -#ifdef ALLOW_EXCEPTIONS - throw Code; -#else - File::RemoveCreated(); - exit(Code); +#if !defined(SILENT) + // Do not write "aborted" when just displaying online help. + if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR) + mprintf(L"\n%s\n",St(MProgAborted)); #endif + SetErrorCode(Code); + throw Code; +} + + +bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) +{ +#ifndef SILENT +#ifdef _WIN_ALL + int ErrType=GetLastError(); + if (ErrType!=0) + return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), + Msg,(DWORD)Size,NULL)!=0; +#endif + +#if defined(_UNIX) || defined(_EMX) + if (errno!=0) + { + char *err=strerror(errno); + if (err!=NULL) + { + CharToWide(err,Msg,Size); + return true; + } + } +#endif +#endif + return false; } void ErrorHandler::SysErrMsg() { #if !defined(SFX_MODULE) && !defined(SILENT) -#ifdef _WIN_32 - #define STRCHR strchr - #define ERRCHAR char - ERRCHAR *lpMsgBuf=NULL; - int ErrType=GetLastError(); - if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, - NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&lpMsgBuf,0,NULL)) + wchar Msg[1024]; + if (!GetSysErrMsg(Msg,ASIZE(Msg))) + return; +#ifdef _WIN_ALL + wchar *CurMsg=Msg; + while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines. { - ERRCHAR *CurMsg=lpMsgBuf; - while (CurMsg!=NULL) + while (*CurMsg=='\r' || *CurMsg=='\n') + CurMsg++; + if (*CurMsg==0) + break; + wchar *EndMsg=wcschr(CurMsg,'\r'); + if (EndMsg==NULL) + EndMsg=wcschr(CurMsg,'\n'); + if (EndMsg!=NULL) { - while (*CurMsg=='\r' || *CurMsg=='\n') - CurMsg++; - if (*CurMsg==0) - break; - ERRCHAR *EndMsg=STRCHR(CurMsg,'\r'); - if (EndMsg==NULL) - EndMsg=STRCHR(CurMsg,'\n'); - if (EndMsg!=NULL) - { - *EndMsg=0; - EndMsg++; - } - Log(NULL,"\n%s",CurMsg); - CurMsg=EndMsg; + *EndMsg=0; + EndMsg++; } + uiMsg(UIERROR_SYSERRMSG,CurMsg); + CurMsg=EndMsg; } - LocalFree( lpMsgBuf ); #endif #if defined(_UNIX) || defined(_EMX) - char *err=strerror(errno); - if (err!=NULL) - Log(NULL,"\n%s",err); + uiMsg(UIERROR_SYSERRMSG,Msg); #endif #endif } +int ErrorHandler::GetSystemErrorCode() +{ +#ifdef _WIN_ALL + return GetLastError(); +#else + return errno; +#endif +} +void ErrorHandler::SetSystemErrorCode(int Code) +{ +#ifdef _WIN_ALL + SetLastError(Code); +#else + errno=Code; +#endif +} diff --git a/libunrar/errhnd.hpp b/libunrar/errhnd.hpp index 28e9843..3455dac 100644 --- a/libunrar/errhnd.hpp +++ b/libunrar/errhnd.hpp @@ -1,63 +1,72 @@ #ifndef _RAR_ERRHANDLER_ #define _RAR_ERRHANDLER_ -#if (defined(GUI) || !defined(_WIN_32)) && !defined(SFX_MODULE) && !defined(_WIN_CE) || defined(RARDLL) -#define ALLOW_EXCEPTIONS -#endif - - - -#define rarmalloc malloc -#define rarcalloc calloc -#define rarrealloc realloc -#define rarfree free -#define rarstrdup strdup -#define rarstrdupw strdupw - - - -enum { SUCCESS,WARNING,FATAL_ERROR,CRC_ERROR,LOCK_ERROR,WRITE_ERROR, - OPEN_ERROR,USER_ERROR,MEMORY_ERROR,CREATE_ERROR,USER_BREAK=255}; +enum RAR_EXIT // RAR exit code. +{ + RARX_SUCCESS = 0, + RARX_WARNING = 1, + RARX_FATAL = 2, + RARX_CRC = 3, + RARX_LOCK = 4, + RARX_WRITE = 5, + RARX_OPEN = 6, + RARX_USERERROR = 7, + RARX_MEMORY = 8, + RARX_CREATE = 9, + RARX_NOFILES = 10, + RARX_BADPWD = 11, + RARX_USERBREAK = 255 +}; class ErrorHandler { private: - void ErrMsg(const char *ArcName,const char *fmt,...); - - int ExitCode; - int ErrCount; + RAR_EXIT ExitCode; + uint ErrCount; bool EnableBreak; bool Silent; - bool DoShutdown; + bool DisableShutdown; // Shutdown is not suitable after last error. public: ErrorHandler(); void Clean(); void MemoryError(); - void OpenError(const char *FileName); - void CloseError(const char *FileName); - void ReadError(const char *FileName); - bool AskRepeatRead(const char *FileName); - void WriteError(const char *ArcName,const char *FileName); - void WriteErrorFAT(const char *FileName); - bool AskRepeatWrite(const char *FileName,bool DiskFull); - void SeekError(const char *FileName); - void GeneralErrMsg(const char *Msg); + void OpenError(const wchar *FileName); + void CloseError(const wchar *FileName); + void ReadError(const wchar *FileName); + bool AskRepeatRead(const wchar *FileName); + void WriteError(const wchar *ArcName,const wchar *FileName); + void WriteErrorFAT(const wchar *FileName); + bool AskRepeatWrite(const wchar *FileName,bool DiskFull); + void SeekError(const wchar *FileName); + void GeneralErrMsg(const wchar *fmt,...); void MemoryErrorMsg(); - void OpenErrorMsg(const char *FileName); - void OpenErrorMsg(const char *ArcName,const char *FileName); - void CreateErrorMsg(const char *FileName); - void CreateErrorMsg(const char *ArcName,const char *FileName); - void ReadErrorMsg(const char *ArcName,const char *FileName); - void WriteErrorMsg(const char *ArcName,const char *FileName); - void Exit(int ExitCode); - void SetErrorCode(int Code); - int GetErrorCode() {return(ExitCode);} - int GetErrorCount() {return(ErrCount);} + void OpenErrorMsg(const wchar *FileName); + void OpenErrorMsg(const wchar *ArcName,const wchar *FileName); + void CreateErrorMsg(const wchar *FileName); + void CreateErrorMsg(const wchar *ArcName,const wchar *FileName); + void ReadErrorMsg(const wchar *FileName); + void ReadErrorMsg(const wchar *ArcName,const wchar *FileName); + void WriteErrorMsg(const wchar *ArcName,const wchar *FileName); + void ArcBrokenMsg(const wchar *ArcName); + void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName); + void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName); + void Exit(RAR_EXIT ExitCode); + void SetErrorCode(RAR_EXIT Code); + RAR_EXIT GetErrorCode() {return ExitCode;} + uint GetErrorCount() {return ErrCount;} void SetSignalHandlers(bool Enable); - void Throw(int Code); - void SetSilent(bool Mode) {Silent=Mode;}; - void SetShutdown(bool Mode) {DoShutdown=Mode;}; + void Throw(RAR_EXIT Code); + void SetSilent(bool Mode) {Silent=Mode;} + bool GetSysErrMsg(wchar *Msg,size_t Size); void SysErrMsg(); + int GetSystemErrorCode(); + void SetSystemErrorCode(int Code); + void SetDisableShutdown() {DisableShutdown=true;} + bool IsShutdownEnabled() {return !DisableShutdown;} + + bool UserBreak; // Ctrl+Break is pressed. + bool MainExit; // main() is completed. }; + #endif diff --git a/libunrar/extinfo.cpp b/libunrar/extinfo.cpp index 13239fe..5cb90a4 100644 --- a/libunrar/extinfo.cpp +++ b/libunrar/extinfo.cpp @@ -1,51 +1,43 @@ #include "rar.hpp" -#ifdef _WIN_32 -#include "win32acl.cpp" +#include "hardlinks.cpp" #include "win32stm.cpp" + +#ifdef _WIN_ALL +#include "win32acl.cpp" +#include "win32lnk.cpp" #endif -#ifdef _BEOS -#include "beosea.cpp" -#endif -#if defined(_EMX) && !defined(_DJGPP) -#include "os2ea.cpp" -#endif + #ifdef _UNIX #include "uowners.cpp" +#ifdef SAVE_LINKS +#include "ulinks.cpp" +#endif #endif +// RAR2 service header extra records. #ifndef SFX_MODULE -void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW) +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) { + if (Cmd->Test) + return; switch(Arc.SubBlockHead.SubType) { -#if defined(_EMX) && !defined(_DJGPP) - case EA_HEAD: - if (Cmd->ProcessEA) - ExtractOS2EA(Arc,Name); - break; -#endif #ifdef _UNIX case UO_HEAD: if (Cmd->ProcessOwners) - ExtractUnixOwner(Arc,Name); + ExtractUnixOwner20(Arc,Name); break; #endif -#ifdef _BEOS - case BEEA_HEAD: - if (Cmd->ProcessEA) - ExtractBeEA(Arc,Name); - break; -#endif -#ifdef _WIN_32 +#ifdef _WIN_ALL case NTACL_HEAD: if (Cmd->ProcessOwners) - ExtractACL(Arc,Name,NameW); + ExtractACL20(Arc,Name); break; case STREAM_HEAD: - ExtractStreams(Arc,Name,NameW); + ExtractStreams20(Arc,Name); break; #endif } @@ -53,24 +45,134 @@ void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW) #endif -void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW) +// RAR3 and RAR5 service header extra records. +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) { -#if defined(_EMX) && !defined(_DJGPP) - if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_OS2EA)) - ExtractOS2EANew(Arc,Name); -#endif #ifdef _UNIX - if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) - ExtractUnixOwnerNew(Arc,Name); + if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && + Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) + ExtractUnixOwner30(Arc,Name); #endif -#ifdef _BEOS - if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) - ExtractUnixOwnerNew(Arc,Name); -#endif -#ifdef _WIN_32 - if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) - ExtractACLNew(Arc,Name,NameW); +#ifdef _WIN_ALL + if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) + ExtractACL(Arc,Name); if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) - ExtractStreamsNew(Arc,Name,NameW); + ExtractStreams(Arc,Name,Cmd->Test); #endif } + + +// Extra data stored directly in file header. +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) +{ +#ifdef _UNIX + if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) + SetUnixOwner(Arc,Name); +#endif +} + + + + +// Calculate a number of path components except \. and \.. +static int CalcAllowedDepth(const wchar *Name) +{ + int AllowedDepth=0; + while (*Name!=0) + { + if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1])) + { + bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0); + bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0); + if (!Dot && !Dot2) + AllowedDepth++; + } + Name++; + } + return AllowedDepth; +} + + +// Check if all existing path components are directories and not links. +static bool LinkInPath(const wchar *Name) +{ + wchar Path[NM]; + if (wcslen(Name)>=ASIZE(Path)) + return true; // It should not be that long, skip. + wcsncpyz(Path,Name,ASIZE(Path)); + for (wchar *s=Path+wcslen(Path)-1;s>Path;s--) + if (IsPathDiv(*s)) + { + *s=0; + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) + return true; + } + return false; +} + + +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) +{ + // Catch root dir based /path/file paths also as stuff like \\?\. + // Do not check PrepSrcName here, it can be root based if destination path + // is a root based. + if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName)) + return false; + + // Number of ".." in link target. + int UpLevels=0; + for (int Pos=0;*TargetName!=0;Pos++) + { + bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && + (IsPathDiv(TargetName[2]) || TargetName[2]==0) && + (Pos==0 || IsPathDiv(*(TargetName-1))); + if (Dot2) + UpLevels++; + TargetName++; + } + // If link target includes "..", it must not have another links + // in the path, because they can bypass our safety check. For example, + // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next + // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next. + if (UpLevels>0 && LinkInPath(PrepSrcName)) + return false; + + // We could check just prepared src name, but for extra safety + // we check both original (as from archive header) and prepared + // (after applying the destination path and -ep switches) names. + + int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth. + + // Remove the destination path from prepared name if any. We should not + // count the destination path depth, because the link target must point + // inside of this path, not outside of it. + size_t ExtrPathLength=wcslen(Cmd->ExtrPath); + if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0) + { + PrepSrcName+=ExtrPathLength; + while (IsPathDiv(*PrepSrcName)) + PrepSrcName++; + } + int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); + + return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels; +} + + +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +{ +#if defined(SAVE_LINKS) && defined(_UNIX) + // For RAR 3.x archives we process links even in test mode to skip link data. + if (Arc.Format==RARFMT15) + return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName); + if (Arc.Format==RARFMT50) + return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); +#elif defined _WIN_ALL + // RAR 5.0 archives store link information in file header, so there is + // no need to additionally test it if we do not create a file. + if (Arc.Format==RARFMT50) + return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead); +#endif + return false; +} diff --git a/libunrar/extinfo.hpp b/libunrar/extinfo.hpp index db7cea5..2b0005d 100644 --- a/libunrar/extinfo.hpp +++ b/libunrar/extinfo.hpp @@ -1,8 +1,23 @@ #ifndef _RAR_EXTINFO_ #define _RAR_EXTINFO_ +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName); +#ifdef _UNIX +void SetUnixOwner(Archive &Arc,const wchar *FileName); +#endif + +bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + +void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize); + +#ifdef _WIN_ALL +bool SetPrivilege(LPCTSTR PrivName); +#endif + +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name); -void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW); -void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW); #endif diff --git a/libunrar/extract.cpp b/libunrar/extract.cpp index 992c87d..76ee2d4 100644 --- a/libunrar/extract.cpp +++ b/libunrar/extract.cpp @@ -1,75 +1,85 @@ #include "rar.hpp" -CmdExtract::CmdExtract() +CmdExtract::CmdExtract(CommandData *Cmd) { + CmdExtract::Cmd=Cmd; + + *ArcName=0; + + *DestFileName=0; + TotalFileCount=0; - *Password=0; Unp=new Unpack(&DataIO); - Unp->Init(NULL); +#ifdef RAR_SMP + Unp->SetThreads(Cmd->Threads); +#endif } CmdExtract::~CmdExtract() { delete Unp; - memset(Password,0,sizeof(Password)); } -void CmdExtract::DoExtract(CommandData *Cmd) +void CmdExtract::DoExtract() { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + Fat32=NotFat32=false; +#endif PasswordCancelled=false; - DataIO.SetCurrentCommand(*Cmd->Command); + DataIO.SetCurrentCommand(Cmd->Command[0]); - struct FindData FD; - while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName))) - if (FindFile::FastFind(ArcName,ArcNameW,&FD)) + FindData FD; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + if (FindFile::FastFind(ArcName,&FD)) DataIO.TotalArcSize+=FD.Size; - Cmd->ArcNames->Rewind(); - while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName))) + Cmd->ArcNames.Rewind(); + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + + ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit(). while (true) { - char PrevCmdPassword[MAXPASSWORD]; - strcpy(PrevCmdPassword,Cmd->Password); - - EXTRACT_ARC_CODE Code=ExtractArchive(Cmd); - - // Restore Cmd->Password, which could be changed in IsArchive() call - // for next header encrypted archive. - strcpy(Cmd->Password,PrevCmdPassword); - + EXTRACT_ARC_CODE Code=ExtractArchive(); if (Code!=EXTRACT_ARC_REPEAT) break; } - if (FindFile::FastFind(ArcName,ArcNameW,&FD)) + if (FindFile::FastFind(ArcName,&FD)) DataIO.ProcessedArcSize+=FD.Size; } - if (TotalFileCount==0 && *Cmd->Command!='I') + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (TotalFileCount==0 && Cmd->Command[0]!='I' && + ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password. { if (!PasswordCancelled) - { - mprintf(St(MExtrNoFiles)); - } - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName); + + // Other error codes may explain a reason of "no files extracted" clearer, + // so set it only if no other errors found (wrong mask set by user). + if (ErrHandler.GetErrorCode()==RARX_SUCCESS) + ErrHandler.SetErrorCode(RARX_NOFILES); } -#ifndef GUI else if (!Cmd->DisableDone) - if (*Cmd->Command=='I') + if (Cmd->Command[0]=='I') mprintf(St(MDone)); else if (ErrHandler.GetErrorCount()==0) mprintf(St(MExtrAllOk)); else mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount()); -#endif } -void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc) +void CmdExtract::ExtractArchiveInit(Archive &Arc) { DataIO.UnpArcSize=Arc.FileLength(); @@ -79,56 +89,68 @@ void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc) FirstFile=true; #endif - if (*Cmd->Password!=0) - strcpy(Password,Cmd->Password); - PasswordAll=(*Cmd->Password!=0); + GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); DataIO.UnpVolume=false; - PrevExtracted=false; - SignatureFound=false; + PrevProcessed=false; AllMatchesExact=true; - ReconstructDone=false; + AnySolidDataUnpackedWell=false; StartTime.SetCurrentTime(); } -EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) +EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); - if (!Arc.WOpen(ArcName,ArcNameW)) - { - ErrHandler.SetErrorCode(OPEN_ERROR); - return(EXTRACT_ARC_NEXT); - } + if (!Arc.WOpen(ArcName)) + return EXTRACT_ARC_NEXT; if (!Arc.IsArchive(true)) { -#ifndef GUI - mprintf(St(MNotRAR),ArcName); -#endif - if (CmpExt(ArcName,"rar")) - ErrHandler.SetErrorCode(WARNING); - return(EXTRACT_ARC_NEXT); - } +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (CmpExt(ArcName,L"rev")) + { + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true); - // archive with corrupt encrypted header can be closed in IsArchive() call - if (!Arc.IsOpened()) - return(EXTRACT_ARC_NEXT); + // If several volume names from same volume set are specified + // and current volume is not first in set and first volume is present + // and specified too, let's skip the current volume. + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; + RecVolumesTest(Cmd,NULL,ArcName); + TotalFileCount++; // Suppress "No files to extract" message. + return EXTRACT_ARC_NEXT; + } +#endif + + mprintf(St(MNotRAR),ArcName); #ifndef SFX_MODULE - if (Arc.Volume && Arc.NotFirstVolume) + if (CmpExt(ArcName,L"rar")) +#endif + ErrHandler.SetErrorCode(RARX_WARNING); + return EXTRACT_ARC_NEXT; + } + + if (Arc.FailedHeaderDecryption) // Bad archive password. + return EXTRACT_ARC_NEXT; + +#ifndef SFX_MODULE + if (Arc.Volume && !Arc.FirstVolume) { - char FirstVolName[NM]; - VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0); + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. - if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && - Cmd->ArcNames->Search(FirstVolName,NULL,false)) - return(EXTRACT_ARC_NEXT); + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; } #endif @@ -139,20 +161,17 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. - char NextName[NM]; - wchar NextNameW[NM]; - - strcpy(NextName,Arc.FileName); - strcpyw(NextNameW,Arc.FileNameW); + wchar NextName[NM]; + wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. - NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); - struct FindData FD; - if (FindFile::FastFind(NextName,NextNameW,&FD)) + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + FindData FD; + if (FindFile::FastFind(NextName,&FD)) VolumeSetSize+=FD.Size; else break; @@ -160,33 +179,29 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) DataIO.TotalArcSize+=VolumeSetSize; } - ExtractArchiveInit(Cmd,Arc); + ExtractArchiveInit(Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; -#ifndef GUI + if (*Cmd->Command=='I') + { Cmd->DisablePercentage=true; + } else - if (Cmd->Test) - mprintf(St(MExtrTest),ArcName); - else - mprintf(St(MExtracting),ArcName); -#endif + uiStartArchiveExtract(!Cmd->Test,ArcName); Arc.ViewComment(); - // RAR can close a corrupt encrypted archive - if (!Arc.IsOpened()) - return(EXTRACT_ARC_NEXT); - while (1) { size_t Size=Arc.ReadHeader(); + + bool Repeat=false; - if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat)) + if (!ExtractCurrentFile(Arc,Size,Repeat)) if (Repeat) { // If we started extraction from not first volume and need to @@ -194,373 +209,208 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) // for correct total progress display. We subtract the size // of current volume and all volumes after it and add the size // of new (first) volume. - struct FindData OldArc,NewArc; - if (FindFile::FastFind(Arc.FileName,Arc.FileNameW,&OldArc) && - FindFile::FastFind(ArcName,ArcNameW,&NewArc)) + FindData OldArc,NewArc; + if (FindFile::FastFind(Arc.FileName,&OldArc) && + FindFile::FastFind(ArcName,&NewArc)) DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; - return(EXTRACT_ARC_REPEAT); + return EXTRACT_ARC_REPEAT; } else break; } - return(EXTRACT_ARC_NEXT); + +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Cmd->Test && Arc.Volume) + RecVolumesTest(Cmd,&Arc,ArcName); +#endif + + return EXTRACT_ARC_NEXT; } -bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderSize,bool &Repeat) +bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) { - char Command=*Cmd->Command; + wchar Command=Cmd->Command[0]; if (HeaderSize==0) if (DataIO.UnpVolume) { #ifdef NOVOLUME - return(false); + return false; #else + // Supposing we unpack an old RAR volume without the end of archive + // record and last file is not split between volumes. if (!MergeArchive(Arc,&DataIO,false,Command)) { - ErrHandler.SetErrorCode(WARNING); - return(false); + ErrHandler.SetErrorCode(RARX_WARNING); + return false; } - SignatureFound=false; #endif } else - return(false); - int HeadType=Arc.GetHeaderType(); - if (HeadType!=FILE_HEAD) + return false; + + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType!=HEAD_FILE) { - if (HeadType==AV_HEAD || HeadType==SIGN_HEAD) - SignatureFound=true; -#if !defined(SFX_MODULE) && !defined(_WIN_CE) - if (HeadType==SUB_HEAD && PrevExtracted) - SetExtraInfo(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL); +#ifndef SFX_MODULE + if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) + SetExtraInfo20(Cmd,Arc,DestFileName); #endif - if (HeadType==NEWSUB_HEAD) - { - if (Arc.SubHead.CmpName(SUBHEAD_TYPE_AV)) - SignatureFound=true; -#if !defined(NOSUBBLOCKS) && !defined(_WIN_CE) - if (PrevExtracted) - SetExtraInfoNew(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL); -#endif - } - if (HeadType==ENDARC_HEAD) - if (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME) + if (HeaderType==HEAD_SERVICE && PrevProcessed) + SetExtraInfo(Cmd,Arc,DestFileName); + if (HeaderType==HEAD_ENDARC) + if (Arc.EndArcHead.NextVolume) { -#ifndef NOVOLUME +#ifdef NOVOLUME + return false; +#else if (!MergeArchive(Arc,&DataIO,false,Command)) { - ErrHandler.SetErrorCode(WARNING); - return(false); + ErrHandler.SetErrorCode(RARX_WARNING); + return false; } - SignatureFound=false; -#endif Arc.Seek(Arc.CurBlockPos,SEEK_SET); - return(true); + return true; +#endif } else - return(false); + return false; Arc.SeekToNext(); - return(true); + return true; } - PrevExtracted=false; + PrevProcessed=false; - if (SignatureFound || - !Cmd->Recurse && MatchedArgs>=Cmd->FileArgs->ItemsCount() && - AllMatchesExact) - return(false); + // We can get negative sizes in corrupt archive and it is unacceptable + // for size comparisons in ComprDataIO::UnpRead, where we cast sizes + // to size_t and can exceed another read or available size. We could fix it + // when reading an archive. But we prefer to do it here, because this + // function is called directly in unrar.dll, so we fix bad parameters + // passed to dll. Also we want to see real negative sizes in the listing + // of corrupt archive. To prevent uninitialized data access perform + // these checks after rejecting zero length and non-file headers above. + if (Arc.FileHead.PackSize<0) + Arc.FileHead.PackSize=0; + if (Arc.FileHead.UnpSize<0) + Arc.FileHead.UnpSize=0; - char ArcFileName[NM]; - IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName); - strcpy(ArcFileName,Arc.NewLhd.FileName); - - wchar ArcFileNameW[NM]; - *ArcFileNameW=0; + if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) + return false; int MatchType=MATCH_WILDSUBPATH; bool EqualNames=false; - int MatchNumber=Cmd->IsProcessFile(Arc.NewLhd,&EqualNames,MatchType); - bool ExactMatch=MatchNumber!=0; -#if !defined(SFX_MODULE) && !defined(_WIN_CE) + wchar MatchedArg[NM]; + int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg)); + bool MatchFound=MatchNumber!=0; +#ifndef SFX_MODULE if (Cmd->ExclPath==EXCL_BASEPATH) { - *Cmd->ArcPath=0; - if (ExactMatch) - { - Cmd->FileArgs->Rewind(); - if (Cmd->FileArgs->GetString(Cmd->ArcPath,NULL,sizeof(Cmd->ArcPath),MatchNumber-1)) - *PointToName(Cmd->ArcPath)=0; - } + wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath)); + *PointToName(Cmd->ArcPath)=0; + if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here. + *Cmd->ArcPath=0; } #endif - if (ExactMatch && !EqualNames) + if (MatchFound && !EqualNames) AllMatchesExact=false; -#ifdef UNICODE_SUPPORTED - bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled(); -#else - bool WideName=false; -#endif + Arc.ConvertAttributes(); -#ifdef _APPLE - if (WideName) +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Arc.FileHead.SplitBefore && FirstFile) { - WideToUtf(Arc.NewLhd.FileNameW,ArcFileName,sizeof(ArcFileName)); - WideName=false; + wchar CurVolName[NM]; + wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); + VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering); + + if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) + { + wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction". + // If first volume name does not match the current name and if such + // volume name really exists, let's unpack from this first volume. + Repeat=true; + return false; + } +#ifndef RARDLL + if (!ReconstructDone) + { + ReconstructDone=true; + if (RecVolumesRestore(Cmd,Arc.FileName,true)) + { + Repeat=true; + return false; + } + } +#endif + wcsncpyz(ArcName,CurVolName,ASIZE(ArcName)); } #endif - wchar *DestNameW=WideName ? DestFileNameW:NULL; + wchar ArcFileName[NM]; + ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName)); -#ifdef UNICODE_SUPPORTED - if (WideName) - { - ConvertPath(Arc.NewLhd.FileNameW,ArcFileNameW); - char Name[NM]; - if (WideToChar(ArcFileNameW,Name) && IsNameUsable(Name)) - strcpy(ArcFileName,Name); - } -#endif - - ConvertPath(ArcFileName,ArcFileName); - - if (Arc.IsArcLabel()) - return(true); - - if (Arc.NewLhd.Flags & LHD_VERSION) + if (Arc.FileHead.Version) { if (Cmd->VersionControl!=1 && !EqualNames) { if (Cmd->VersionControl==0) - ExactMatch=false; - int Version=ParseVersionFileName(ArcFileName,ArcFileNameW,false); + MatchFound=false; + int Version=ParseVersionFileName(ArcFileName,false); if (Cmd->VersionControl-1==Version) - ParseVersionFileName(ArcFileName,ArcFileNameW,true); + ParseVersionFileName(ArcFileName,true); else - ExactMatch=false; + MatchFound=false; } } else if (!Arc.IsArcDir() && Cmd->VersionControl>1) - ExactMatch=false; + MatchFound=false; - Arc.ConvertAttributes(); - -#ifndef SFX_MODULE - if ((Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/)) && FirstFile) - { - char CurVolName[NM]; - strcpy(CurVolName,ArcName); - - VolNameToFirstName(ArcName,ArcName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0); - if (stricomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) - { - *ArcNameW=0; - Repeat=true; - return(false); - } -#if !defined(RARDLL) && !defined(_WIN_CE) - if (!ReconstructDone) - { - ReconstructDone=true; - - RecVolumes RecVol; - if (RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true)) - { - Repeat=true; - return(false); - } - } -#endif - strcpy(ArcName,CurVolName); - } -#endif - DataIO.UnpVolume=(Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0; + DataIO.UnpVolume=Arc.FileHead.SplitAfter; DataIO.NextVolumeMissing=false; - Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET); + Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); - bool TestMode=false; bool ExtrFile=false; bool SkipSolid=false; #ifndef SFX_MODULE - if (FirstFile && (ExactMatch || Arc.Solid) && (Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/))!=0) + if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore) { - if (ExactMatch) + if (MatchFound) { - Log(Arc.FileName,St(MUnpCannotMerge),ArcFileName); + uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName); #ifdef RARDLL Cmd->DllError=ERAR_BAD_DATA; #endif - ErrHandler.SetErrorCode(OPEN_ERROR); + ErrHandler.SetErrorCode(RARX_OPEN); } - ExactMatch=false; + MatchFound=false; } FirstFile=false; #endif - if (ExactMatch || (SkipSolid=Arc.Solid)!=0) + if (MatchFound || (SkipSolid=Arc.Solid)!=0) { - if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0) -#ifndef RARDLL - if (*Password==0) -#endif - { -#ifdef RARDLL - if (*Cmd->Password==0) - if (Cmd->Callback==NULL || - Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1) - return(false); - strcpy(Password,Cmd->Password); + // First common call of uiStartFileExtract. It is done before overwrite + // prompts, so if SkipSolid state is changed below, we'll need to make + // additional uiStartFileExtract calls with updated parameters. + if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) + return false; -#else - if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password))) - { - PasswordCancelled=true; - return(false); - } -#endif - } -#if !defined(GUI) && !defined(SILENT) - else - if (!PasswordAll && (!Arc.Solid || Arc.NewLhd.UnpVer>=20 && (Arc.NewLhd.Flags & LHD_SOLID)==0)) - { - eprintf(St(MUseCurPsw),ArcFileName); - switch(Cmd->AllYes ? 1:Ask(St(MYesNoAll))) - { - case -1: - ErrHandler.Exit(USER_BREAK); - case 2: - if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password))) - { - return(false); - } - break; - case 3: - PasswordAll=true; - break; - } - } -#endif + ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); -#ifndef SFX_MODULE - if (*Cmd->ExtrPath==0 && *Cmd->ExtrPathW!=0) - WideToChar(Cmd->ExtrPathW,DestFileName); - else -#endif - strcpy(DestFileName,Cmd->ExtrPath); - - -#ifndef SFX_MODULE - if (Cmd->AppendArcNameToPath) - { - strcat(DestFileName,PointToName(Arc.FirstVolumeName)); - SetExt(DestFileName,NULL); - AddEndSlash(DestFileName); - } -#endif - - char *ExtrName=ArcFileName; - - bool EmptyName=false; -#ifndef SFX_MODULE - size_t Length=strlen(Cmd->ArcPath); - if (Length>1 && IsPathDiv(Cmd->ArcPath[Length-1]) && - strlen(ArcFileName)==Length-1) - Length--; - if (Length>0 && strnicomp(Cmd->ArcPath,ArcFileName,Length)==0) - { - ExtrName+=Length; - while (*ExtrName==CPATHDIVIDER) - ExtrName++; - if (*ExtrName==0) - EmptyName=true; - } -#endif - - bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); - if (AbsPaths) - *DestFileName=0; - - if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) - strcat(DestFileName,PointToName(ExtrName)); - else - strcat(DestFileName,ExtrName); - - char DiskLetter=etoupper(DestFileName[0]); - - if (AbsPaths && DestFileName[1]=='_' && IsPathDiv(DestFileName[2]) && - DiskLetter>='A' && DiskLetter<='Z') - DestFileName[1]=':'; - -#ifndef SFX_MODULE - if (!WideName && *Cmd->ExtrPathW!=0) - { - DestNameW=DestFileNameW; - WideName=true; - CharToWide(ArcFileName,ArcFileNameW); - } -#endif - - if (WideName) - { - if (*Cmd->ExtrPathW!=0) - strcpyw(DestFileNameW,Cmd->ExtrPathW); - else - CharToWide(Cmd->ExtrPath,DestFileNameW); - -#ifndef SFX_MODULE - if (Cmd->AppendArcNameToPath) - { - wchar FileNameW[NM]; - if (*Arc.FirstVolumeNameW!=0) - strcpyw(FileNameW,Arc.FirstVolumeNameW); - else - CharToWide(Arc.FirstVolumeName,FileNameW); - strcatw(DestFileNameW,PointToName(FileNameW)); - SetExt(DestFileNameW,NULL); - AddEndSlash(DestFileNameW); - } -#endif - wchar *ExtrNameW=ArcFileNameW; -#ifndef SFX_MODULE - if (Length>0) - { - wchar ArcPathW[NM]; - GetWideName(Cmd->ArcPath,Cmd->ArcPathW,ArcPathW); - Length=strlenw(ArcPathW); - } - ExtrNameW+=Length; - while (*ExtrNameW==CPATHDIVIDER) - ExtrNameW++; -#endif - - if (AbsPaths) - *DestFileNameW=0; - - if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) - strcatw(DestFileNameW,PointToName(ExtrNameW)); - else - strcatw(DestFileNameW,ExtrNameW); - - if (AbsPaths && DestFileNameW[1]=='_' && IsPathDiv(DestFileNameW[2])) - DestFileNameW[1]=':'; - } - else - *DestFileNameW=0; - - ExtrFile=!SkipSolid && !EmptyName && (Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0; + // DestFileName can be set empty in case of excessive -ap switch. + ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X')) { - struct FindData FD; - if (FindFile::FastFind(DestFileName,DestNameW,&FD)) + FindData FD; + if (FindFile::FastFind(DestFileName,&FD)) { - if (FD.mtime >= Arc.NewLhd.mtime) + if (FD.mtime >= Arc.FileHead.mtime) { // If directory already exists and its modification time is newer // than start of extraction, it is likely it was created @@ -577,197 +427,159 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderS ExtrFile=false; } - // Skip encrypted file if no password is specified. - if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0 && *Password==0) + if (!CheckUnpVer(Arc,ArcFileName)) { - ErrHandler.SetErrorCode(WARNING); -#ifdef RARDLL - Cmd->DllError=ERAR_MISSING_PASSWORD; -#endif - ExtrFile=false; - } - -#ifdef RARDLL - if (*Cmd->DllDestName) - { - strncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); - *DestFileNameW=0; - if (Cmd->DllOpMode!=RAR_EXTRACT) - ExtrFile=false; - } - if (*Cmd->DllDestNameW) - { - strncpyzw(DestFileNameW,Cmd->DllDestNameW,ASIZE(DestFileNameW)); - DestNameW=DestFileNameW; - if (Cmd->DllOpMode!=RAR_EXTRACT) - ExtrFile=false; - } -#endif - -#ifdef SFX_MODULE - if ((Arc.NewLhd.UnpVer!=UNP_VER && Arc.NewLhd.UnpVer!=29) && - Arc.NewLhd.Method!=0x30) -#else - if (Arc.NewLhd.UnpVer<13 || Arc.NewLhd.UnpVer>UNP_VER) -#endif - { -#ifndef SILENT - Log(Arc.FileName,St(MUnknownMeth),ArcFileName); -#ifndef SFX_MODULE - Log(Arc.FileName,St(MVerRequired),Arc.NewLhd.UnpVer/10,Arc.NewLhd.UnpVer%10); -#endif -#endif - ExtrFile=false; - ErrHandler.SetErrorCode(WARNING); + ErrHandler.SetErrorCode(RARX_FATAL); #ifdef RARDLL Cmd->DllError=ERAR_UNKNOWN_FORMAT; #endif + Arc.SeekToNext(); + return !Arc.Solid; // Can try extracting next file only in non-solid archive. } + while (true) // Repeat the password prompt for wrong and empty passwords. + { + if (Arc.FileHead.Encrypted) + { + // Stop archive extracting if user cancelled a password prompt. +#ifdef RARDLL + if (!ExtrDllGetPassword()) + { + Cmd->DllError=ERAR_MISSING_PASSWORD; + return false; + } +#else + if (!ExtrGetPassword(Arc,ArcFileName)) + { + PasswordCancelled=true; + return false; + } +#endif + } + + // Set a password before creating the file, so we can skip creating + // in case of wrong password. + SecPassword FilePassword=Cmd->Password; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + ConvertDosPassword(Arc,FilePassword); +#endif + + byte PswCheck[SIZE_PSWCHECK]; + DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, + Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, + Arc.FileHead.InitV,Arc.FileHead.Lg2Count, + Arc.FileHead.HashKey,PswCheck); + + // If header is damaged, we cannot rely on password check value, + // because it can be damaged too. + if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && + memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && + !Arc.BrokenHeader) + { + if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop. + { + // This message is used by Android GUI to reset cached passwords. + // Update appropriate code if changed. + uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); + } + else // For passwords entered manually. + { + // This message is used by Android GUI and Windows GUI and SFX to + // reset cached passwords. Update appropriate code if changed. + uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); + Cmd->Password.Clean(); + + // Avoid new requests for unrar.dll to prevent the infinite loop + // if app always returns the same password. +#ifndef RARDLL + continue; // Request a password again. +#endif + } +#ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume, + // we should not replace it with less precise ERAR_BAD_PASSWORD. + if (Cmd->DllError!=ERAR_EOPEN) + Cmd->DllError=ERAR_BAD_PASSWORD; +#endif + ErrHandler.SetErrorCode(RARX_BADPWD); + ExtrFile=false; + } + break; + } + +#ifdef RARDLL + if (*Cmd->DllDestName!=0) + wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); +#endif + File CurFile; - if (!IsLink(Arc.NewLhd.FileAttr)) + bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; + if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY) + { + if (ExtrFile && Command!='P' && !Cmd->Test) + { + // Overwrite prompt for symbolic and hard links. + bool UserReject=false; + if (FileExist(DestFileName) && !UserReject) + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + if (UserReject) + ExtrFile=false; + } + } + else if (Arc.IsArcDir()) { - if (!ExtrFile || Command=='P' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) - return(true); - if (SkipSolid) - { -#ifndef GUI - mprintf(St(MExtrSkipFile),ArcFileName); -#endif - return(true); - } + if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + return true; TotalFileCount++; - if (Cmd->Test) - { -#ifndef GUI - mprintf(St(MExtrTestFile),ArcFileName); - mprintf(" %s",St(MOk)); -#endif - return(true); - } - MKDIR_CODE MDCode=MakeDir(DestFileName,DestNameW,!Cmd->IgnoreGeneralAttr,Arc.NewLhd.FileAttr); - bool DirExist=false; - if (MDCode!=MKDIR_SUCCESS) - { - DirExist=FileExist(DestFileName,DestNameW); - if (DirExist && !IsDir(GetFileAttr(DestFileName,DestNameW))) - { - bool UserReject; - FileCreate(Cmd,NULL,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime); - DirExist=false; - } - CreatePath(DestFileName,DestNameW,true); - MDCode=MakeDir(DestFileName,DestNameW,!Cmd->IgnoreGeneralAttr,Arc.NewLhd.FileAttr); - } - if (MDCode==MKDIR_SUCCESS) - { -#ifndef GUI - mprintf(St(MCreatDir),DestFileName); - mprintf(" %s",St(MOk)); -#endif - PrevExtracted=true; - } - else - if (DirExist) - { - if (!Cmd->IgnoreGeneralAttr) - SetFileAttr(DestFileName,DestNameW,Arc.NewLhd.FileAttr); - PrevExtracted=true; - } - else - { - Log(Arc.FileName,St(MExtrErrMkDir),DestFileName); - ErrHandler.SysErrMsg(); -#ifdef RARDLL - Cmd->DllError=ERAR_ECREATE; -#endif - ErrHandler.SetErrorCode(CREATE_ERROR); - } - if (PrevExtracted) - { -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) - if (Cmd->SetCompressedAttr && - (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) - SetFileCompression(DestFileName,DestNameW,true); -#endif - SetDirTime(DestFileName,DestNameW, - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, - Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); - } - return(true); + ExtrCreateDir(Arc,ArcFileName); + // It is important to not increment MatchedArgs here, so we extract + // dir with its entire contents and not dir record only even if + // dir record precedes files. + return true; } else - { - if (Cmd->Test && ExtrFile) - TestMode=true; -#if !defined(GUI) && !defined(SFX_MODULE) - if (Command=='P' && ExtrFile) - CurFile.SetHandleType(FILE_HANDLESTD); -#endif - if ((Command=='E' || Command=='X') && ExtrFile && !Cmd->Test) - { - bool UserReject; - if (!FileCreate(Cmd,&CurFile,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime)) - { - ExtrFile=false; - if (!UserReject) - { - ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); - ErrHandler.SetErrorCode(CREATE_ERROR); -#ifdef RARDLL - Cmd->DllError=ERAR_ECREATE; -#endif - if (!IsNameUsable(DestFileName)) - { - Log(Arc.FileName,St(MCorrectingName)); - char OrigName[sizeof(DestFileName)]; - strncpyz(OrigName,DestFileName,ASIZE(OrigName)); - - MakeNameUsable(DestFileName,true); - CreatePath(DestFileName,NULL,true); - if (FileCreate(Cmd,&CurFile,DestFileName,NULL,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime)) - { -#ifndef SFX_MODULE - Log(Arc.FileName,St(MRenaming),OrigName,DestFileName); -#endif - ExtrFile=true; - } - else - ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); - } - } - } - } - } + if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY). + ExtrFile=ExtrCreateFile(Arc,CurFile); if (!ExtrFile && Arc.Solid) { SkipSolid=true; - TestMode=true; ExtrFile=true; + // We changed SkipSolid, so we need to call uiStartFileExtract + // with "Skip" parameter to change the operation status + // from "extracting" to "skipping". For example, it can be necessary + // if user answered "No" to overwrite prompt when unpacking + // a solid archive. + if (!uiStartFileExtract(ArcFileName,false,false,true)) + return false; } if (ExtrFile) { + // Set it in test mode, so we also test subheaders such as NTFS streams + // after tested file. + if (Cmd->Test) + PrevProcessed=true; + + bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk. + if (!SkipSolid) { if (!TestMode && Command!='P' && CurFile.IsDevice()) { - Log(Arc.FileName,St(MInvalidName),DestFileName); + uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName); ErrHandler.WriteError(Arc.FileName,DestFileName); } TotalFileCount++; } FileCount++; -#ifndef GUI if (Command!='I') if (SkipSolid) mprintf(St(MExtrSkipFile),ArcFileName); else - switch(Cmd->Test ? 'T':Command) + switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch. { case 'T': mprintf(St(MExtrTestFile),ArcFileName); @@ -783,146 +595,604 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderS break; } if (!Cmd->DisablePercentage) - mprintf(" "); -#endif + mprintf(L" "); + DataIO.CurUnpRead=0; DataIO.CurUnpWrite=0; - DataIO.UnpFileCRC=Arc.OldFormat ? 0 : 0xffffffff; - DataIO.PackedCRC=0xffffffff; - DataIO.SetEncryption( - (Arc.NewLhd.Flags & LHD_PASSWORD) ? Arc.NewLhd.UnpVer:0,Password, - (Arc.NewLhd.Flags & LHD_SALT) ? Arc.NewLhd.Salt:NULL,false, - Arc.NewLhd.UnpVer>=36); - DataIO.SetPackedSizeToRead(Arc.NewLhd.FullPackSize); + DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize); DataIO.SetFiles(&Arc,&CurFile); DataIO.SetTestMode(TestMode); DataIO.SetSkipUnpCRC(SkipSolid); -#ifndef _WIN_CE - if (!TestMode && !Arc.BrokenFileHeader && - (Arc.NewLhd.FullPackSize<<11)>Arc.NewLhd.FullUnpSize && - (Arc.NewLhd.FullUnpSize<100000000 || Arc.FileLength()>Arc.NewLhd.FullPackSize)) - CurFile.Prealloc(Arc.NewLhd.FullUnpSize); + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + if (!TestMode && !Arc.BrokenHeader && + Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32)) + { + if (!Fat32) // Not detected yet. + NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath)); + if (Fat32) + uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit. + } #endif + uint64 Preallocated=0; + if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 && + Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && + (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) + { + CurFile.Prealloc(Arc.FileHead.UnpSize); + Preallocated=Arc.FileHead.UnpSize; + } CurFile.SetAllowDelete(!Cmd->KeepBroken); - bool LinkCreateMode=!Cmd->Test && !SkipSolid; - if (ExtractLink(DataIO,Arc,DestFileName,DataIO.UnpFileCRC,LinkCreateMode)) - PrevExtracted=LinkCreateMode; - else - if ((Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0) - if (Arc.NewLhd.Method==0x30) - UnstoreFile(DataIO,Arc.NewLhd.FullUnpSize); + bool FileCreateMode=!TestMode && !SkipSolid && Command!='P'; + bool ShowChecksum=true; // Display checksum verification result. + + bool LinkSuccess=true; // Assume success for test mode. + if (LinkEntry) + { + FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType; + + if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) + { + wchar NameExisting[NM]; + ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting)); + if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. + if (Type==FSREDIR_HARDLINK) + LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting)); + else + LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); + } + else + if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) + { + if (FileCreateMode) + LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName); + } else { - Unp->SetDestSize(Arc.NewLhd.FullUnpSize); + uiMsg(UIERROR_UNKNOWNEXTRA, Arc.FileName, DestFileName); + LinkSuccess=false; + } + + if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode) + { + // RAR 5.x links have a valid data checksum even in case of + // failure, because they do not store any data. + // We do not want to display "OK" in this case. + // For 4.x symlinks we verify the checksum only when extracting, + // but not when testing an archive. + ShowChecksum=false; + } + PrevProcessed=FileCreateMode && LinkSuccess; + } + else + if (!Arc.FileHead.SplitBefore) + if (Arc.FileHead.Method==0) + UnstoreFile(DataIO,Arc.FileHead.UnpSize); + else + { + Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); + Unp->SetDestSize(Arc.FileHead.UnpSize); #ifndef SFX_MODULE - if (Arc.NewLhd.UnpVer<=15) + if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) Unp->DoUnpack(15,FileCount>1 && Arc.Solid); else #endif - Unp->DoUnpack(Arc.NewLhd.UnpVer,(Arc.NewLhd.Flags & LHD_SOLID)!=0); + Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid); } - if (Arc.IsOpened()) - Arc.SeekToNext(); + Arc.SeekToNext(); + // We check for "split after" flag to detect partially extracted files + // from incomplete volume sets. For them file header contains packed + // data hash, which must not be compared against unpacked data hash + // to prevent accidental match. Moreover, for -m0 volumes packed data + // hash would match truncated unpacked data hash and lead to fake "OK" + // in incomplete volume set. + bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL); + + // We set AnySolidDataUnpackedWell to true if we found at least one + // valid non-zero solid file in preceding solid stream. If it is true + // and if current encrypted file is broken, we do not need to hint + // about a wrong password and can report CRC error only. + if (!Arc.FileHead.Solid) + AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found. + else + if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC) + AnySolidDataUnpackedWell=true; + bool BrokenFile=false; - if (!SkipSolid) + + // Checksum is not calculated in skip solid mode for performance reason. + if (!SkipSolid && ShowChecksum) { - if (Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC) || - !Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC^0xffffffff)) + if (ValidCRC) { -#ifndef GUI if (Command!='P' && Command!='I') - mprintf("%s%s ",Cmd->DisablePercentage ? " ":"\b\b\b\b\b ",St(MOk)); -#endif + mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ", + Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk)); } else { - char *BadArcName=/*(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE) ? NULL:*/Arc.FileName; - if (Arc.NewLhd.Flags & LHD_PASSWORD) - { - Log(BadArcName,St(MEncrBadCRC),ArcFileName); - } + if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck || + Arc.BrokenHeader) && !AnySolidDataUnpackedWell) + uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName); else - { - Log(BadArcName,St(MCRCFailed),ArcFileName); - } + uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName); BrokenFile=true; - ErrHandler.SetErrorCode(CRC_ERROR); + ErrHandler.SetErrorCode(RARX_CRC); #ifdef RARDLL - Cmd->DllError=ERAR_BAD_DATA; + // If we already have ERAR_EOPEN as result of missing volume + // or ERAR_BAD_PASSWORD for RAR5 wrong password, + // we should not replace it with less precise ERAR_BAD_DATA. + if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD) + Cmd->DllError=ERAR_BAD_DATA; #endif - Alarm(); } } -#ifndef GUI else - mprintf("\b\b\b\b\b "); -#endif + mprintf(L"\b\b\b\b\b "); + + // If we successfully unpacked a hard link, we wish to set its file + // attributes. Hard link shares file metadata with link target, + // so we do not need to set link time or owner. But when we overwrite + // an existing link, we can call PrepareToDelete(), which affects + // link target attributes as well. So we set link attributes to restore + // both target and link attributes if PrepareToDelete() changed them. + bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess; if (!TestMode && (Command=='X' || Command=='E') && - !IsLink(Arc.NewLhd.FileAttr)) + (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && + (!BrokenFile || Cmd->KeepBroken)) { -#if defined(_WIN_32) || defined(_EMX) - if (Cmd->ClearArc) - Arc.NewLhd.FileAttr&=~FA_ARCH; -/* - else - Arc.NewLhd.FileAttr|=FA_ARCH; //set archive bit for unpacked files (file is not backed up) -*/ -#endif - if (!BrokenFile || Cmd->KeepBroken) + // Below we use DestFileName instead of CurFile.FileName, + // so we can set file attributes also for hard links, which do not + // have the open CurFile. These strings are the same for other items. + + if (!SetAttrOnly) { - if (BrokenFile) + // We could preallocate more space that really written to broken file + // or file with crafted header. + if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated)) CurFile.Truncate(); + + CurFile.SetOpenFileTime( - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, - Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); CurFile.Close(); -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) - if (Cmd->SetCompressedAttr && - (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) - SetFileCompression(CurFile.FileName,CurFile.FileNameW,true); -#endif + + SetFileHeaderExtra(Cmd,Arc,DestFileName); + CurFile.SetCloseFileTime( - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); - if (!Cmd->IgnoreGeneralAttr) - SetFileAttr(CurFile.FileName,CurFile.FileNameW,Arc.NewLhd.FileAttr); - PrevExtracted=true; + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); } + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0) + SetFileCompression(DestFileName,true); + if (Cmd->ClearArc) + Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; +#endif + if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr)) + uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName); + + PrevProcessed=true; } } } - if (ExactMatch) + // It is important to increment it for files, but not dirs. So we extract + // dir with its entire contents, not just dir record only even if dir + // record precedes files. + if (MatchFound) MatchedArgs++; - if (DataIO.NextVolumeMissing || !Arc.IsOpened()) - return(false); + if (DataIO.NextVolumeMissing) + return false; if (!ExtrFile) if (!Arc.Solid) Arc.SeekToNext(); else if (!SkipSolid) - return(false); - return(true); + return false; + return true; } void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) { - Array Buffer(0x10000); - while (1) + Array Buffer(File::CopyBufferSize()); + while (true) { - uint Code=DataIO.UnpRead(&Buffer[0],Buffer.Size()); - if (Code==0 || (int)Code==-1) + int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size()); + if (ReadSize<=0) break; - Code=Code=0) - DestUnpSize-=Code; + int WriteSize=ReadSize0) + { + DataIO.UnpWrite(&Buffer[0],WriteSize); + DestUnpSize-=WriteSize; + } } } + +bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +{ + SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. + + File Existing; + if (!Existing.WOpen(NameExisting)) + { + uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); + uiMsg(UIERROR_FILECOPYHINT,ArcName); +#ifdef RARDLL + Cmd->DllError=ERAR_EREFERENCE; +#endif + return false; + } + + Array Buffer(0x100000); + int64 CopySize=0; + + while (true) + { + Wait(); + int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); + if (ReadSize==0) + break; + New.Write(&Buffer[0],ReadSize); + CopySize+=ReadSize; + } + + return true; +} + + +void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) +{ + wcsncpyz(DestName,Cmd->ExtrPath,DestSize); + + if (*Cmd->ExtrPath!=0) + { + wchar LastChar=*PointToLastChar(Cmd->ExtrPath); + // We need IsPathDiv check here to correctly handle Unix forward slash + // in the end of destination path in Windows: rar x arc dest/ + // IsDriveDiv is needed for current drive dir: rar x arc d: + if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar)) + { + // Destination path can be without trailing slash if it come from GUI shell. + AddEndSlash(DestName,DestSize); + } + } + +#ifndef SFX_MODULE + if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE) + { + if (Cmd->AppendArcNameToPath==APPENDARCNAME_DESTPATH) + wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); + else + wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); // To archive own dir. + SetExt(DestName,NULL,DestSize); + AddEndSlash(DestName,DestSize); + } +#endif + +#ifndef SFX_MODULE + size_t ArcPathLength=wcslen(Cmd->ArcPath); + if (ArcPathLength>0) + { + size_t NameLength=wcslen(ArcFileName); + + // Earlier we compared lengths only here, but then noticed a cosmetic bug + // in WinRAR. When extracting a file reference from subfolder with + // "Extract relative paths", so WinRAR sets ArcPath, if reference target + // is missing, error message removed ArcPath both from reference and target + // names. If target was stored in another folder, its name looked wrong. + if (NameLength>=ArcPathLength && + wcsnicompc(Cmd->ArcPath,ArcFileName,ArcPathLength)==0 && + (IsPathDiv(Cmd->ArcPath[ArcPathLength-1]) || + IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0)) + { + ArcFileName+=Min(ArcPathLength,NameLength); + while (IsPathDiv(*ArcFileName)) + ArcFileName++; + if (*ArcFileName==0) // Excessive -ap switch. + { + *DestName=0; + return; + } + } + } +#endif + + wchar Command=Cmd->Command[0]; + // Use -ep3 only in systems, where disk letters are exist, not in Unix. + bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); + + // We do not use any user specified destination paths when extracting + // absolute paths in -ep3 mode. + if (AbsPaths) + *DestName=0; + + if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + wcsncatz(DestName,PointToName(ArcFileName),DestSize); + else + wcsncatz(DestName,ArcFileName,DestSize); + +#ifdef _WIN_ALL + // Must do after Cmd->ArcPath processing above, so file name and arc path + // trailing spaces are in sync. + if (!Cmd->AllowIncompatNames) + MakeNameCompatible(DestName); +#endif + + wchar DiskLetter=toupperw(DestName[0]); + + if (AbsPaths) + { + if (DestName[1]=='_' && IsPathDiv(DestName[2]) && + DiskLetter>='A' && DiskLetter<='Z') + DestName[1]=':'; + else + if (DestName[0]=='_' && DestName[1]=='_') + { + // Convert __server\share to \\server\share. + DestName[0]=CPATHDIVIDER; + DestName[1]=CPATHDIVIDER; + } + } +} + + +#ifdef RARDLL +bool CmdExtract::ExtrDllGetPassword() +{ + if (!Cmd->Password.IsSet()) + { + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + Cmd->ManualPassword=true; + } + if (!Cmd->Password.IsSet()) + return false; + } + return true; +} +#endif + + +#ifndef RARDLL +bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) +{ + if (!Cmd->Password.IsSet()) + { + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/) + { + // Suppress "test is ok" message if user cancelled the password prompt. +// 2019.03.23: If some archives are tested ok and prompt is cancelled for others, +// do we really need to suppress "test is ok"? Also if we set an empty password +// and "Use for all archives" in WinRAR Ctrl+P and skip some encrypted archives. +// We commented out this UIERROR_INCERRCOUNT for now. +// uiMsg(UIERROR_INCERRCOUNT); + return false; + } + Cmd->ManualPassword=true; + } +#if !defined(SILENT) + else + if (!GlobalPassword && !Arc.FileHead.Solid) + { + eprintf(St(MUseCurPsw),ArcFileName); + switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll))) + { + case -1: + ErrHandler.Exit(RARX_USERBREAK); + case 2: + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) + return false; + break; + case 3: + GlobalPassword=true; + break; + } + } +#endif + return true; +} +#endif + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd) +{ + if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS) + { + // We need the password in OEM encoding if file was encrypted by + // native RAR/DOS (not extender based). Let's make the conversion. + wchar PlainPsw[MAXPASSWORD]; + Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw)); + char PswA[MAXPASSWORD]; + CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA)); + PswA[ASIZE(PswA)-1]=0; + CharToWide(PswA,PlainPsw,ASIZE(PlainPsw)); + DestPwd.Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(PswA,sizeof(PswA)); + } +} +#endif + + +void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) +{ + if (Cmd->Test) + { + mprintf(St(MExtrTestFile),ArcFileName); + mprintf(L" %s",St(MOk)); + return; + } + + MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + bool DirExist=false; + if (MDCode!=MKDIR_SUCCESS) + { + DirExist=FileExist(DestFileName); + if (DirExist && !IsDir(GetFileAttr(DestFileName))) + { + // File with name same as this directory exists. Propose user + // to overwrite it. + bool UserReject; + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + DirExist=false; + } + if (!DirExist) + { + CreatePath(DestFileName,true); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + if (MDCode!=MKDIR_SUCCESS) + { + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + MakeNameUsable(DestFileName,true); + CreatePath(DestFileName,true); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); +#ifndef SFX_MODULE + if (MDCode==MKDIR_SUCCESS) + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + } + } + } + if (MDCode==MKDIR_SUCCESS) + { + mprintf(St(MCreatDir),DestFileName); + mprintf(L" %s",St(MOk)); + PrevProcessed=true; + } + else + if (DirExist) + { + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(DestFileName,Arc.FileHead.FileAttr); + PrevProcessed=true; + } + else + { + uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName); + ErrHandler.SysErrMsg(); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + ErrHandler.SetErrorCode(RARX_CREATE); + } + if (PrevProcessed) + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE) + SetFileCompression(DestFileName,true); +#endif + SetFileHeaderExtra(Cmd,Arc,DestFileName); + SetDirTime(DestFileName, + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + } +} + + +bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) +{ + bool Success=true; + wchar Command=Cmd->Command[0]; +#if !defined(SFX_MODULE) + if (Command=='P') + CurFile.SetHandleType(FILE_HANDLESTD); +#endif + if ((Command=='E' || Command=='X') && !Cmd->Test) + { + bool UserReject; + // Specify "write only" mode to avoid OpenIndiana NAS problems + // with SetFileTime and read+write files. + if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { + Success=false; + if (!UserReject) + { + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + if (!IsNameUsable(DestFileName)) + { + uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); + + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + + MakeNameUsable(DestFileName,true); + + CreatePath(DestFileName,true); + if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { +#ifndef SFX_MODULE + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + Success=true; + } + else + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); + } + } + } + } + return Success; +} + + +bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) +{ + bool WrongVer; + if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives. + WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5; + else + { +#ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives. + WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK; +#else // All formats since 1.3 for RAR. + WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK; +#endif + } + + // We can unpack stored files regardless of compression version field. + if (Arc.FileHead.Method==0) + WrongVer=false; + + if (WrongVer) + { + ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName); + uiMsg(UIERROR_NEWERRAR,Arc.FileName); + } + return !WrongVer; +} diff --git a/libunrar/extract.hpp b/libunrar/extract.hpp index 40f9cc0..325928d 100644 --- a/libunrar/extract.hpp +++ b/libunrar/extract.hpp @@ -6,9 +6,25 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; class CmdExtract { private: - EXTRACT_ARC_CODE ExtractArchive(CommandData *Cmd); + EXTRACT_ARC_CODE ExtractArchive(); + bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); +#ifdef RARDLL + bool ExtrDllGetPassword(); +#else + bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName); +#endif +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); +#endif + void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName); + bool ExtrCreateFile(Archive &Arc,File &CurFile); + bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); + RarTime StartTime; // time when extraction started + CommandData *Cmd; + ComprDataIO DataIO; Unpack *Unp; unsigned long TotalFileCount; @@ -19,25 +35,28 @@ class CmdExtract bool AllMatchesExact; bool ReconstructDone; - char ArcName[NM]; - wchar ArcNameW[NM]; + // If any non-zero solid file was successfully unpacked before current. + // If true and if current encrypted file is broken, obviously + // the password is correct and we can report broken CRC without + // any wrong password hints. + bool AnySolidDataUnpackedWell; - char Password[MAXPASSWORD]; - bool PasswordAll; - bool PrevExtracted; - char DestFileName[NM]; - wchar DestFileNameW[NM]; + wchar ArcName[NM]; + + bool GlobalPassword; + bool PrevProcessed; // If previous file was successfully extracted or tested. + wchar DestFileName[NM]; bool PasswordCancelled; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + bool Fat32,NotFat32; +#endif public: - CmdExtract(); + CmdExtract(CommandData *Cmd); ~CmdExtract(); - void DoExtract(CommandData *Cmd); - void ExtractArchiveInit(CommandData *Cmd,Archive &Arc); - bool ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderSize, - bool &Repeat); + void DoExtract(); + void ExtractArchiveInit(Archive &Arc); + bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat); static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize); - - bool SignatureFound; }; #endif diff --git a/libunrar/filcreat.cpp b/libunrar/filcreat.cpp index c570cd3..a64a7d4 100644 --- a/libunrar/filcreat.cpp +++ b/libunrar/filcreat.cpp @@ -1,245 +1,163 @@ #include "rar.hpp" -bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW, - OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize, - uint FileTime) +// If NewFile==NULL, we delete created file after user confirmation. +// It is useful we we need to overwrite an existing folder or file, +// but need user confirmation for that. +bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) { if (UserReject!=NULL) *UserReject=false; -#if defined(_WIN_32) && !defined(_WIN_CE) +#ifdef _WIN_ALL bool ShortNameChanged=false; #endif - while (FileExist(Name,NameW)) + while (FileExist(Name)) { -#if defined(_WIN_32) && !defined(_WIN_CE) +#if defined(_WIN_ALL) if (!ShortNameChanged) { + // Avoid the infinite loop if UpdateExistingShortName returns + // the same name. ShortNameChanged=true; - if (UpdateExistingShortName(Name,NameW)) + + // Maybe our long name matches the short name of existing file. + // Let's check if we can change the short name. + if (UpdateExistingShortName(Name)) continue; } + // Allow short name check again. It is necessary, because rename and + // autorename below can change the name, so we need to check it again. + ShortNameChanged=false; #endif - if (Mode==OVERWRITE_NONE) + UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); + + if (Choice==UIASKREP_R_REPLACE) + break; + if (Choice==UIASKREP_R_SKIP) { if (UserReject!=NULL) *UserReject=true; - return(false); - } -#ifdef SILENT - Mode=OVERWRITE_ALL; -#endif - if (Cmd->AllYes || Mode==OVERWRITE_ALL) - break; - if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK) - { - eprintf(St(MFileExists),Name); - int Choice=Ask(St(MYesNoAllRenQ)); - if (Choice==1) - break; - if (Choice==2) - { - if (UserReject!=NULL) - *UserReject=true; - return(false); - } - if (Choice==3) - { - Cmd->Overwrite=OVERWRITE_ALL; - break; - } - if (Choice==4) - { - if (UserReject!=NULL) - *UserReject=true; - Cmd->Overwrite=OVERWRITE_NONE; - return(false); - } - if (Choice==5) - { - mprintf(St(MAskNewName)); - - char NewName[NM]; -#ifdef _WIN_32 - File SrcFile; - SrcFile.SetHandleType(FILE_HANDLESTD); - int Size=SrcFile.Read(NewName,sizeof(NewName)-1); - NewName[Size]=0; - OemToChar(NewName,NewName); -#else - if (fgets(NewName,sizeof(NewName),stdin)==NULL) - { - // Process fgets failure as if user answered 'No'. - if (UserReject!=NULL) - *UserReject=true; - return(false); - } -#endif - RemoveLF(NewName); - if (PointToName(NewName)==NewName) - strcpy(PointToName(Name),NewName); - else - strcpy(Name,NewName); - if (NameW!=NULL) - *NameW=0; - continue; - } - if (Choice==6) - ErrHandler.Exit(USER_BREAK); - } - if (Mode==OVERWRITE_AUTORENAME) - { - if (GetAutoRenamedName(Name)) - { - if (NameW!=NULL) - *NameW=0; - } - else - Mode=OVERWRITE_DEFAULT; - continue; + return false; } + if (Choice==UIASKREP_R_CANCEL) + ErrHandler.Exit(RARX_USERBREAK); } - if (NewFile!=NULL && NewFile->Create(Name,NameW)) - return(true); - PrepareToDelete(Name,NameW); - CreatePath(Name,NameW,true); - return(NewFile!=NULL ? NewFile->Create(Name,NameW):DelFile(Name,NameW)); + + // Try to truncate the existing file first instead of delete, + // so we preserve existing file permissions such as NTFS permissions. + uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; + if (NewFile!=NULL && NewFile->Create(Name,FileMode)) + return true; + + CreatePath(Name,true); + return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); } -bool GetAutoRenamedName(char *Name) +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) { - char NewName[NM]; - - if (strlen(Name)>sizeof(NewName)-10) - return(false); - char *Ext=GetExt(Name); + wchar NewName[NM]; + size_t NameLength=wcslen(Name); + wchar *Ext=GetExt(Name); if (Ext==NULL) - Ext=Name+strlen(Name); - for (int FileVer=1;;FileVer++) + Ext=Name+NameLength; + for (uint FileVer=1;;FileVer++) { - sprintf(NewName,"%.*s(%d)%s",int(Ext-Name),Name,FileVer,Ext); + swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); if (!FileExist(NewName)) { - strcpy(Name,NewName); + wcsncpyz(Name,NewName,MaxNameSize); break; } if (FileVer>=1000000) - return(false); + return false; } - return(true); + return true; } -#if defined(_WIN_32) && !defined(_WIN_CE) -bool UpdateExistingShortName(char *Name,wchar *NameW) +#if defined(_WIN_ALL) +// If we find a file, which short name is equal to 'Name', we try to change +// its short name, while preserving the long name. It helps when unpacking +// an archived file, which long name is equal to short name of already +// existing file. Otherwise we would overwrite the already existing file, +// even though its long name does not match the name of unpacking file. +bool UpdateExistingShortName(const wchar *Name) { - FindData fd; - if (!FindFile::FastFind(Name,NameW,&fd)) - return(false); - if (*fd.Name==0 || *fd.ShortName==0) - return(false); - if (stricomp(PointToName(fd.Name),fd.ShortName)==0 || - stricomp(PointToName(Name),fd.ShortName)!=0) - return(false); + wchar LongPathName[NM]; + DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName)); + if (Res==0 || Res>=ASIZE(LongPathName)) + return false; + wchar ShortPathName[NM]; + Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName)); + if (Res==0 || Res>=ASIZE(ShortPathName)) + return false; + wchar *LongName=PointToName(LongPathName); + wchar *ShortName=PointToName(ShortPathName); - char NewName[NM]; - for (int I=0;I<10000;I+=123) + // We continue only if file has a short name, which does not match its + // long name, and this short name is equal to name of file which we need + // to create. + if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 || + wcsicomp(PointToName(Name),ShortName)!=0) + return false; + + // Generate the temporary new name for existing file. + wchar NewName[NM]; + *NewName=0; + for (int I=0;I<10000 && *NewName==0;I+=123) { - strncpyz(NewName,Name,ASIZE(NewName)); - sprintf(PointToName(NewName),"rtmp%d",I); - if (!FileExist(NewName)) - break; + // Here we copy the path part of file to create. We'll make the temporary + // file in the same folder. + wcsncpyz(NewName,Name,ASIZE(NewName)); + + // Here we set the random name part. + swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); + + // If such file is already exist, try next random name. + if (FileExist(NewName)) + *NewName=0; } - if (FileExist(NewName)) - return(false); - char FullName[NM]; - strncpyz(FullName,Name,ASIZE(FullName)); - strcpy(PointToName(FullName),PointToName(fd.Name)); + + // If we could not generate the name not used by any other file, we return. + if (*NewName==0) + return false; + + // FastFind returns the name without path, but we need the fully qualified + // name for renaming, so we use the path from file to create and long name + // from existing file. + wchar FullName[NM]; + wcsncpyz(FullName,Name,ASIZE(FullName)); + SetName(FullName,LongName,ASIZE(FullName)); + + // Rename the existing file to randomly generated name. Normally it changes + // the short name too. if (!MoveFile(FullName,NewName)) - return(false); + return false; + + // Now we need to create the temporary empty file with same name as + // short name of our already existing file. We do it to occupy its previous + // short name and not allow to use it again when renaming the file back to + // its original long name. File KeepShortFile; bool Created=false; if (!FileExist(Name)) - Created=KeepShortFile.Create(Name); + Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD); + + // Now we rename the existing file from temporary name to original long name. + // Since its previous short name is occupied by another file, it should + // get another short name. MoveFile(NewName,FullName); + if (Created) { + // Delete the temporary zero length file occupying the short name, KeepShortFile.Close(); KeepShortFile.Delete(); } - return(true); + // We successfully changed the short name. Maybe sometimes we'll simplify + // this function by use of SetFileShortName Windows API call. + // But SetFileShortName is not available in older Windows. + return true; } - -/* -bool UpdateExistingShortName(char *Name,wchar *NameW) -{ - if (WinNT()<5) - return(false); - FindData fd; - if (!FindFile::FastFind(Name,NameW,&fd)) - return(false); - if (*fd.Name==0 || *fd.ShortName==0) - return(false); - if (stricomp(PointToName(fd.Name),fd.ShortName)==0 || - stricomp(PointToName(Name),fd.ShortName)!=0) - return(false); - - typedef BOOL (WINAPI *SETFILESHORTNAME)(HANDLE,LPCSTR); - static SETFILESHORTNAME pSetFileShortName=NULL; - if (pSetFileShortName==NULL) - { - HMODULE hKernel=GetModuleHandle("kernel32.dll"); - if (hKernel!=NULL) - pSetFileShortName=(SETFILESHORTNAME)GetProcAddress(hKernel,"SetFileShortNameA"); - if (pSetFileShortName==NULL) - return(false); - } - static bool RestoreEnabled=false; - if (!RestoreEnabled) - { - HANDLE hToken; - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) - return(false); - - TOKEN_PRIVILEGES tp; - tp.PrivilegeCount = 1; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - if (LookupPrivilegeValue(NULL,SE_RESTORE_NAME,&tp.Privileges[0].Luid)) - AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL); - - CloseHandle(hToken); - RestoreEnabled=true; - } - - wchar FileNameW[NM]; - GetWideName(Name,NameW,FileNameW); - HANDLE hFile=CreateFileW(FileNameW,GENERIC_WRITE|DELETE,FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); - if (hFile==INVALID_HANDLE_VALUE) - return(false); - - bool RetCode=false; - - char FullName[NM]; - wchar FullNameW[NM]; - strcpy(FullName,Name); - strcpyw(FullNameW,NullToEmpty(NameW)); - for (int I=1;I<1000000;I++) - { - char NewName[NM]; - sprintf(NewName,"NAME~%d.%d",I%1000,I/1000+1); - strcpy(PointToName(FullName),NewName); - if (*FullNameW) - CharToWide(NewName,PointToName(FullNameW)); - if (!FileExist(FullName,FullNameW)) - { - RetCode=pSetFileShortName(hFile,NewName); - break; - } - } - CloseHandle(hFile); - return(RetCode); -} -*/ #endif diff --git a/libunrar/filcreat.hpp b/libunrar/filcreat.hpp index 5c8e8f5..44f801d 100644 --- a/libunrar/filcreat.hpp +++ b/libunrar/filcreat.hpp @@ -1,13 +1,14 @@ #ifndef _RAR_FILECREATE_ #define _RAR_FILECREATE_ -bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW, - OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize=INT64NDF, - uint FileTime=0); -bool GetAutoRenamedName(char *Name); +bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize=INT64NDF, + RarTime *FileTime=NULL,bool WriteOnly=false); -#if defined(_WIN_32) && !defined(_WIN_CE) -bool UpdateExistingShortName(char *Name,wchar *NameW); +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize); + +#if defined(_WIN_ALL) +bool UpdateExistingShortName(const wchar *Name); #endif #endif diff --git a/libunrar/file.cpp b/libunrar/file.cpp index bd54c06..e506fde 100644 --- a/libunrar/file.cpp +++ b/libunrar/file.cpp @@ -1,13 +1,9 @@ #include "rar.hpp" -static File *CreatedFiles[256]; -static int RemoveCreatedActive=0; - File::File() { - hFile=BAD_HANDLE; + hFile=FILE_BAD_HANDLE; *FileName=0; - *FileNameW=0; NewFile=false; LastWrite=false; HandleType=FILE_HANDLENORMAL; @@ -16,17 +12,18 @@ File::File() ErrorType=FILE_SUCCESS; OpenShared=false; AllowDelete=true; - CloseCount=0; AllowExceptions=true; -#ifdef _WIN_32 + PreserveAtime=false; +#ifdef _WIN_ALL NoSequentialRead=false; + CreateMode=FMF_UNDEFINED; #endif } File::~File() { - if (hFile!=BAD_HANDLE && !SkipClose) + if (hFile!=FILE_BAD_HANDLE && !SkipClose) if (NewFile) Delete(); else @@ -37,287 +34,314 @@ File::~File() void File::operator = (File &SrcFile) { hFile=SrcFile.hFile; - strcpy(FileName,SrcFile.FileName); NewFile=SrcFile.NewFile; LastWrite=SrcFile.LastWrite; HandleType=SrcFile.HandleType; + wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName)); SrcFile.SkipClose=true; } -bool File::Open(const char *Name,const wchar *NameW,bool OpenShared,bool Update) +bool File::Open(const wchar *Name,uint Mode) { ErrorType=FILE_SUCCESS; FileHandle hNewFile; - if (File::OpenShared) - OpenShared=true; -#ifdef _WIN_32 - uint Access=GENERIC_READ; - if (Update) + bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0; + bool UpdateMode=(Mode & FMF_UPDATE)!=0; + bool WriteMode=(Mode & FMF_WRITE)!=0; +#ifdef _WIN_ALL + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ; + if (UpdateMode) Access|=GENERIC_WRITE; - uint ShareMode=FILE_SHARE_READ; + uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ; if (OpenShared) ShareMode|=FILE_SHARE_WRITE; uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN; - if (WinNT() && NameW!=NULL && *NameW!=0) - hNewFile=CreateFileW(NameW,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); - else - hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + FindData FD; + if (PreserveAtime) + Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime. + hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); - if (hNewFile==BAD_HANDLE && GetLastError()==ERROR_FILE_NOT_FOUND) + DWORD LastError; + if (hNewFile==FILE_BAD_HANDLE) + { + LastError=GetLastError(); + + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + { + hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + // For archive names longer than 260 characters first CreateFile + // (without \\?\) fails and sets LastError to 3 (access denied). + // We need the correct "file not found" error code to decide + // if we create a new archive or quit with "cannot create" error. + // So we need to check the error code after \\?\ CreateFile again, + // otherwise we'll fail to create new archives with long names. + // But we cannot simply assign the new code to LastError, + // because it would break "..\arcname.rar" relative names processing. + // First CreateFile returns the correct "file not found" code for such + // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating + // dots as a directory name. So we check only for "file not found" + // error here and for other errors use the first CreateFile result. + if (GetLastError()==ERROR_FILE_NOT_FOUND) + LastError=ERROR_FILE_NOT_FOUND; + } + } + if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND) ErrorType=FILE_NOTFOUND; + if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE) + { + FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification. + SetFileTime(hNewFile,NULL,&ft,NULL); + } + #else - int flags=Update ? O_RDWR:O_RDONLY; + int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); #ifdef O_BINARY flags|=O_BINARY; #if defined(_AIX) && defined(_LARGE_FILE_API) flags|=O_LARGEFILE; #endif #endif -#if defined(_EMX) && !defined(_DJGPP) - int sflags=OpenShared ? SH_DENYNO:SH_DENYWR; - int handle=sopen(Name,flags,sflags); -#else - int handle=open(Name,flags); + // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+. +#if defined(O_NOATIME) + if (PreserveAtime) + flags|=O_NOATIME; +#endif + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + + int handle=open(NameA,flags); #ifdef LOCK_EX #ifdef _OSF_SOURCE extern "C" int flock(int, int); #endif - if (!OpenShared && Update && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) + if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) { close(handle); - return(false); + return false; } #endif + if (handle==-1) + hNewFile=FILE_BAD_HANDLE; + else + { +#ifdef FILE_USE_OPEN + hNewFile=handle; +#else + hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY); #endif - hNewFile=handle==-1 ? BAD_HANDLE:fdopen(handle,Update ? UPDATEBINARY:READBINARY); - if (hNewFile==BAD_HANDLE && errno==ENOENT) + } + if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT) ErrorType=FILE_NOTFOUND; #endif NewFile=false; HandleType=FILE_HANDLENORMAL; SkipClose=false; - bool Success=hNewFile!=BAD_HANDLE; + bool Success=hNewFile!=FILE_BAD_HANDLE; if (Success) { hFile=hNewFile; - if (NameW!=NULL) - strcpyw(FileNameW,NameW); - else - *FileNameW=0; - if (Name!=NULL) - strcpy(FileName,Name); - else - WideToChar(NameW,FileName); - AddFileToList(hFile); + wcsncpyz(FileName,Name,ASIZE(FileName)); } - return(Success); + return Success; } -#if !defined(SHELL_EXT) && !defined(SFX_MODULE) -void File::TOpen(const char *Name,const wchar *NameW) +#if !defined(SFX_MODULE) +void File::TOpen(const wchar *Name) { - if (!WOpen(Name,NameW)) - ErrHandler.Exit(OPEN_ERROR); + if (!WOpen(Name)) + ErrHandler.Exit(RARX_OPEN); } #endif -bool File::WOpen(const char *Name,const wchar *NameW) +bool File::WOpen(const wchar *Name) { - if (Open(Name,NameW)) - return(true); + if (Open(Name)) + return true; ErrHandler.OpenErrorMsg(Name); - return(false); + return false; } -bool File::Create(const char *Name,const wchar *NameW,bool ShareRead) +bool File::Create(const wchar *Name,uint Mode) { -#ifdef _WIN_32 - DWORD ShareMode=(ShareRead || File::OpenShared) ? FILE_SHARE_READ:0; - if (WinNT() && NameW!=NULL && *NameW!=0) - hFile=CreateFileW(NameW,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL, - CREATE_ALWAYS,0,NULL); + // OpenIndiana based NAS and CIFS shares fail to set the file time if file + // was created in read+write mode and some data was written and not flushed + // before SetFileTime call. So we should use the write only mode if we plan + // SetFileTime call and do not need to read from file. + bool WriteMode=(Mode & FMF_WRITE)!=0; + bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared; +#ifdef _WIN_ALL + CreateMode=Mode; + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE; + DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0; + + // Windows automatically removes dots and spaces in the end of file name, + // So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + + if (Special && (Mode & FMF_STANDARDNAMES)==0) + hFile=FILE_BAD_HANDLE; else - hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL, - CREATE_ALWAYS,0,NULL); + hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + + if (hFile==FILE_BAD_HANDLE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + } + #else - hFile=fopen(Name,CREATEBINARY); + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); +#ifdef FILE_USE_OPEN + hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); +#else + hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY); +#endif #endif NewFile=true; HandleType=FILE_HANDLENORMAL; SkipClose=false; - if (NameW!=NULL) - strcpyw(FileNameW,NameW); - else - *FileNameW=0; - if (Name!=NULL) - strcpy(FileName,Name); - else - WideToChar(NameW,FileName); - AddFileToList(hFile); - return(hFile!=BAD_HANDLE); + wcsncpyz(FileName,Name,ASIZE(FileName)); + return hFile!=FILE_BAD_HANDLE; } -void File::AddFileToList(FileHandle hFile) +#if !defined(SFX_MODULE) +void File::TCreate(const wchar *Name,uint Mode) { - if (hFile!=BAD_HANDLE) - for (int I=0;I0) @@ -339,12 +363,13 @@ void File::Write(const void *Data,size_t Size) break; } LastWrite=true; + return Success; // It can return false only if AllowExceptions is disabled. } int File::Read(void *Data,size_t Size) { - int64 FilePos=0; //initialized only to suppress some compilers warning + int64 FilePos=0; // Initialized only to suppress some compilers warning. if (IgnoreReadErrors) FilePos=Tell(); @@ -376,39 +401,61 @@ int File::Read(void *Data,size_t Size) } break; } - return(ReadSize); + return ReadSize; // It can return -1 only if AllowExceptions is disabled. } // Returns -1 in case of error. int File::DirectRead(void *Data,size_t Size) { -#ifdef _WIN_32 +#ifdef _WIN_ALL const size_t MaxDeviceRead=20000; + const size_t MaxLockedRead=32768; #endif -#ifndef _WIN_CE if (HandleType==FILE_HANDLESTD) { -#ifdef _WIN_32 - if (Size>MaxDeviceRead) - Size=MaxDeviceRead; +#ifdef _WIN_ALL +// if (Size>MaxDeviceRead) +// Size=MaxDeviceRead; hFile=GetStdHandle(STD_INPUT_HANDLE); +#else +#ifdef FILE_USE_OPEN + hFile=STDIN_FILENO; #else hFile=stdin; #endif - } #endif -#ifdef _WIN_32 + } +#ifdef _WIN_ALL + // For pipes like 'type file.txt | rar -si arcname' ReadFile may return + // data in small ~4KB blocks. It may slightly reduce the compression ratio. DWORD Read; if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL)) { if (IsDevice() && Size>MaxDeviceRead) - return(DirectRead(Data,MaxDeviceRead)); + return DirectRead(Data,MaxDeviceRead); if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE) - return(0); - return(-1); + return 0; + + // We had a bug report about failure to archive 1C database lock file + // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB + // permanently locked. If our first read request uses too large buffer + // and if we are in -dh mode, so we were able to open the file, + // we'll fail with "Read error". So now we use try a smaller buffer size + // in case of lock error. + if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead && + GetLastError()==ERROR_LOCK_VIOLATION) + return DirectRead(Data,MaxLockedRead); + + return -1; } - return(Read); + return Read; +#else +#ifdef FILE_USE_OPEN + ssize_t ReadSize=read(hFile,Data,Size); + if (ReadSize==-1) + return -1; + return (int)ReadSize; #else if (LastWrite) { @@ -418,8 +465,9 @@ int File::DirectRead(void *Data,size_t Size) clearerr(hFile); size_t ReadSize=fread(Data,1,Size,hFile); if (ferror(hFile)) - return(-1); - return((int)ReadSize); + return -1; + return (int)ReadSize; +#endif #endif } @@ -433,47 +481,58 @@ void File::Seek(int64 Offset,int Method) bool File::RawSeek(int64 Offset,int Method) { - if (hFile==BAD_HANDLE) - return(true); + if (hFile==FILE_BAD_HANDLE) + return true; if (Offset<0 && Method!=SEEK_SET) { Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset; Method=SEEK_SET; } -#ifdef _WIN_32 +#ifdef _WIN_ALL LONG HighDist=(LONG)(Offset>>32); if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff && GetLastError()!=NO_ERROR) - return(false); + return false; #else LastWrite=false; -#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) +#ifdef FILE_USE_OPEN + if (lseek(hFile,(off_t)Offset,Method)==-1) + return false; +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) if (fseeko(hFile,Offset,Method)!=0) + return false; #else if (fseek(hFile,(long)Offset,Method)!=0) + return false; #endif - return(false); #endif - return(true); + return true; } int64 File::Tell() { -#ifdef _WIN_32 + if (hFile==FILE_BAD_HANDLE) + if (AllowExceptions) + ErrHandler.SeekError(FileName); + else + return -1; +#ifdef _WIN_ALL LONG HighDist=0; uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT); if (LowDist==0xffffffff && GetLastError()!=NO_ERROR) if (AllowExceptions) ErrHandler.SeekError(FileName); else - return(-1); - return(INT32TO64(HighDist,LowDist)); + return -1; + return INT32TO64(HighDist,LowDist); #else -#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) - return(ftello(hFile)); +#ifdef FILE_USE_OPEN + return lseek(hFile,0,SEEK_CUR); +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) + return ftello(hFile); #else - return(ftell(hFile)); + return ftell(hFile); #endif #endif } @@ -481,13 +540,21 @@ int64 File::Tell() void File::Prealloc(int64 Size) { -#ifdef _WIN_32 +#ifdef _WIN_ALL if (RawSeek(Size,SEEK_SET)) { Truncate(); Seek(0,SEEK_SET); } #endif + +#if defined(_UNIX) && defined(USE_FALLOCATE) + // fallocate is rather new call. Only latest kernels support it. + // So we are not using it by default yet. + int fd = GetFD(); + if (fd >= 0) + fallocate(fd, 0, 0, Size); +#endif } @@ -495,7 +562,7 @@ byte File::GetByte() { byte Byte=0; Read(&Byte,1); - return(Byte); + return Byte; } @@ -507,27 +574,46 @@ void File::PutByte(byte Byte) bool File::Truncate() { -#ifdef _WIN_32 - return(SetEndOfFile(hFile)==TRUE); +#ifdef _WIN_ALL + return SetEndOfFile(hFile)==TRUE; #else - return(false); + return ftruncate(GetFD(),(off_t)Tell())==0; +#endif +} + + +void File::Flush() +{ +#ifdef _WIN_ALL + FlushFileBuffers(hFile); +#else +#ifndef FILE_USE_OPEN + fflush(hFile); +#endif + fsync(GetFD()); #endif } void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) { -#ifdef _WIN_32 +#ifdef _WIN_ALL + // Workaround for OpenIndiana NAS time bug. If we cannot create a file + // in write only mode, we need to flush the write buffer before calling + // SetFileTime or file time will not be changed. + if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) + FlushFileBuffers(hFile); + bool sm=ftm!=NULL && ftm->IsSet(); bool sc=ftc!=NULL && ftc->IsSet(); bool sa=fta!=NULL && fta->IsSet(); FILETIME fm,fc,fa; if (sm) - ftm->GetWin32(&fm); + ftm->GetWinFT(&fm); if (sc) - ftc->GetWin32(&fc); + ftc->GetWinFT(&fc); if (sa) - fta->GetWin32(&fa); + fta->GetWinFT(&fa); SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); #endif } @@ -535,29 +621,47 @@ void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) void File::SetCloseFileTime(RarTime *ftm,RarTime *fta) { -#if defined(_UNIX) || defined(_EMX) +// Android APP_PLATFORM := android-14 does not support futimens and futimes. +// Newer platforms support futimens, but fail on Android 4.2. +// We have to use utime for Android. +// Also we noticed futimens fail to set timestamps on NTFS partition +// mounted to virtual Linux x86 machine, but utimensat worked correctly. +// So we set timestamps for already closed files in Unix. +#ifdef _UNIX SetCloseFileTimeByName(FileName,ftm,fta); #endif } -void File::SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta) +void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) { -#if defined(_UNIX) || defined(_EMX) +#ifdef _UNIX bool setm=ftm!=NULL && ftm->IsSet(); bool seta=fta!=NULL && fta->IsSet(); if (setm || seta) { - struct utimbuf ut; + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + +#ifdef UNIX_TIME_NS + timespec times[2]; + times[0].tv_sec=seta ? fta->GetUnix() : 0; + times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; + times[1].tv_sec=setm ? ftm->GetUnix() : 0; + times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; + utimensat(AT_FDCWD,NameA,times,0); +#else + utimbuf ut; if (setm) ut.modtime=ftm->GetUnix(); else - ut.modtime=fta->GetUnix(); + ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0. if (seta) ut.actime=fta->GetUnix(); else - ut.actime=ut.modtime; - utime(Name,&ut); + ut.actime=ut.modtime; // Need to set something, cannot left it 0. + utime(NameA,&ut); +#endif } #endif } @@ -565,99 +669,46 @@ void File::SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta) void File::GetOpenFileTime(RarTime *ft) { -#ifdef _WIN_32 +#ifdef _WIN_ALL FILETIME FileTime; GetFileTime(hFile,NULL,NULL,&FileTime); - *ft=FileTime; + ft->SetWinFT(&FileTime); #endif #if defined(_UNIX) || defined(_EMX) struct stat st; - fstat(fileno(hFile),&st); - *ft=st.st_mtime; + fstat(GetFD(),&st); + ft->SetUnix(st.st_mtime); #endif } int64 File::FileLength() { - SaveFilePos SavePos(*this); + int64 SavePos=Tell(); Seek(0,SEEK_END); - return(Tell()); -} - - -void File::SetHandleType(FILE_HANDLETYPE Type) -{ - HandleType=Type; + int64 Length=Tell(); + Seek(SavePos,SEEK_SET); + return Length; } bool File::IsDevice() { - if (hFile==BAD_HANDLE) - return(false); -#ifdef _WIN_32 + if (hFile==FILE_BAD_HANDLE) + return false; +#ifdef _WIN_ALL uint Type=GetFileType(hFile); - return(Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE); + return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE; #else - return(isatty(fileno(hFile))); + return isatty(GetFD()); #endif } -#ifndef SFX_MODULE -void File::fprintf(const char *fmt,...) -{ - va_list argptr; - va_start(argptr,fmt); - safebuf char Msg[2*NM+1024],OutMsg[2*NM+1024]; - vsprintf(Msg,fmt,argptr); -#ifdef _WIN_32 - for (int Src=0,Dest=0;;Src++) - { - char CurChar=Msg[Src]; - if (CurChar=='\n') - OutMsg[Dest++]='\r'; - OutMsg[Dest++]=CurChar; - if (CurChar==0) - break; - } -#else - strcpy(OutMsg,Msg); -#endif - Write(OutMsg,strlen(OutMsg)); - va_end(argptr); -} -#endif - - -bool File::RemoveCreated() -{ - RemoveCreatedActive++; - bool RetCode=true; - for (int I=0;ISetExceptions(false); - bool Success; - if (CreatedFiles[I]->NewFile) - Success=CreatedFiles[I]->Delete(); - else - Success=CreatedFiles[I]->Close(); - if (Success) - CreatedFiles[I]=NULL; - else - RetCode=false; - } - RemoveCreatedActive--; - return(RetCode); -} - - #ifndef SFX_MODULE int64 File::Copy(File &Dest,int64 Length) { - Array Buffer(0x10000); + Array Buffer(File::CopyBufferSize()); int64 CopySize=0; bool CopyAll=(Length==INT64NDF); @@ -665,14 +716,30 @@ int64 File::Copy(File &Dest,int64 Length) { Wait(); size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size(); - int ReadSize=Read(&Buffer[0],SizeToRead); + byte *Buf=&Buffer[0]; + int ReadSize=Read(Buf,SizeToRead); if (ReadSize==0) break; - Dest.Write(&Buffer[0],ReadSize); + size_t WriteSize=ReadSize; +#ifdef _WIN_ALL + // For FAT32 USB flash drives in Windows if first write is 4 KB or more, + // write caching is disabled and "write through" is enabled, resulting + // in bad performance, especially for many small files. It happens when + // we create SFX archive on USB drive, because SFX module is written first. + // So we split the first write to small 1 KB followed by rest of data. + if (CopySize==0 && WriteSize>=4096) + { + const size_t FirstWrite=1024; + Dest.Write(Buf,FirstWrite); + Buf+=FirstWrite; + WriteSize-=FirstWrite; + } +#endif + Dest.Write(Buf,WriteSize); CopySize+=ReadSize; if (!CopyAll) Length-=ReadSize; } - return(CopySize); + return CopySize; } #endif diff --git a/libunrar/file.hpp b/libunrar/file.hpp index 3e17ae9..a343ce6 100644 --- a/libunrar/file.hpp +++ b/libunrar/file.hpp @@ -1,34 +1,55 @@ #ifndef _RAR_FILE_ #define _RAR_FILE_ -#ifdef _WIN_32 -typedef HANDLE FileHandle; -#define BAD_HANDLE INVALID_HANDLE_VALUE +#define FILE_USE_OPEN + +#ifdef _WIN_ALL + typedef HANDLE FileHandle; + #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE +#elif defined(FILE_USE_OPEN) + typedef off_t FileHandle; + #define FILE_BAD_HANDLE -1 #else -typedef FILE* FileHandle; -#define BAD_HANDLE NULL + typedef FILE* FileHandle; + #define FILE_BAD_HANDLE NULL #endif class RAROptions; -enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD,FILE_HANDLEERR}; +enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; -struct FileStat -{ - uint FileAttr; - uint FileTime; - int64 FileSize; - bool IsDir; +enum FILE_MODE_FLAGS { + // Request read only access to file. Default for Open. + FMF_READ=0, + + // Request both read and write access to file. Default for Create. + FMF_UPDATE=1, + + // Request write only access to file. + FMF_WRITE=2, + + // Open files which are already opened for write by other programs. + FMF_OPENSHARED=4, + + // Open files only if no other program is opened it even in shared mode. + FMF_OPENEXCLUSIVE=8, + + // Provide read access to created file for other programs. + FMF_SHAREREAD=16, + + // Use standard NTFS names without trailing dots and spaces. + FMF_STANDARDNAMES=32, + + // Mode flags are not defined yet. + FMF_UNDEFINED=256 }; class File { private: - void AddFileToList(FileHandle hFile); - FileHandle hFile; bool LastWrite; FILE_HANDLETYPE HandleType; @@ -37,62 +58,85 @@ class File bool NewFile; bool AllowDelete; bool AllowExceptions; -#ifdef _WIN_32 +#ifdef _WIN_ALL bool NoSequentialRead; + uint CreateMode; #endif + bool PreserveAtime; protected: - bool OpenShared; + bool OpenShared; // Set by 'Archive' class. public: - char FileName[NM]; - wchar FileNameW[NM]; + wchar FileName[NM]; FILE_ERRORTYPE ErrorType; - - uint CloseCount; public: File(); virtual ~File(); void operator = (File &SrcFile); - bool Open(const char *Name,const wchar *NameW=NULL,bool OpenShared=false,bool Update=false); - void TOpen(const char *Name,const wchar *NameW=NULL); - bool WOpen(const char *Name,const wchar *NameW=NULL); - bool Create(const char *Name,const wchar *NameW=NULL,bool ShareRead=true); - void TCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true); - bool WCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true); - bool Close(); - void Flush(); + + // Several functions below are 'virtual', because they are redefined + // by Archive for QOpen and by MultiFile for split files in WinRAR. + virtual bool Open(const wchar *Name,uint Mode=FMF_READ); + void TOpen(const wchar *Name); + bool WOpen(const wchar *Name); + bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + virtual bool Close(); // 'virtual' for MultiFile class. bool Delete(); - bool Rename(const char *NewName,const wchar *NewNameW=NULL); - void Write(const void *Data,size_t Size); - int Read(void *Data,size_t Size); + bool Rename(const wchar *NewName); + bool Write(const void *Data,size_t Size); + virtual int Read(void *Data,size_t Size); int DirectRead(void *Data,size_t Size); - void Seek(int64 Offset,int Method); + virtual void Seek(int64 Offset,int Method); bool RawSeek(int64 Offset,int Method); - int64 Tell(); + virtual int64 Tell(); void Prealloc(int64 Size); byte GetByte(); void PutByte(byte Byte); bool Truncate(); + void Flush(); void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); - static void SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta); + static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); void GetOpenFileTime(RarTime *ft); - bool IsOpened() {return(hFile!=BAD_HANDLE);}; + virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class. int64 FileLength(); - void SetHandleType(FILE_HANDLETYPE Type); - FILE_HANDLETYPE GetHandleType() {return(HandleType);}; + void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} + FILE_HANDLETYPE GetHandleType() {return HandleType;} bool IsDevice(); - void fprintf(const char *fmt,...); static bool RemoveCreated(); - FileHandle GetHandle() {return(hFile);}; - void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;}; - char *GetName() {return(FileName);} + FileHandle GetHandle() {return hFile;} + void SetHandle(FileHandle Handle) {Close();hFile=Handle;} + void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;} int64 Copy(File &Dest,int64 Length=INT64NDF); void SetAllowDelete(bool Allow) {AllowDelete=Allow;} void SetExceptions(bool Allow) {AllowExceptions=Allow;} -#ifdef _WIN_32 +#ifdef _WIN_ALL void RemoveSequentialFlag() {NoSequentialRead=true;} #endif + void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;} +#ifdef _UNIX + int GetFD() + { +#ifdef FILE_USE_OPEN + return hFile; +#else + return fileno(hFile); +#endif + } +#endif + static size_t CopyBufferSize() + { +#ifdef _WIN_ALL + // USB flash performance is poor with 64 KB buffer, 256+ KB resolved it. + // For copying from HDD to same HDD the best performance was with 256 KB + // buffer in XP and with 1 MB buffer in Win10. + return WinNT()==WNT_WXP ? 0x40000:0x100000; +#else + return 0x100000; +#endif + } }; #endif diff --git a/libunrar/filefn.cpp b/libunrar/filefn.cpp index 215f797..57a334d 100644 --- a/libunrar/filefn.cpp +++ b/libunrar/filefn.cpp @@ -1,147 +1,126 @@ #include "rar.hpp" -MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr) +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) { -#ifdef _WIN_32 - int Success; - if (WinNT() && NameW!=NULL && *NameW!=0) - Success=CreateDirectoryW(NameW,NULL); - else - Success=CreateDirectory(Name,NULL); - if (Success) +#ifdef _WIN_ALL + // Windows automatically removes dots and spaces in the end of directory + // name. So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL); + if (RetCode==0 && !FileExist(Name)) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + RetCode=CreateDirectory(LongName,NULL); + } + if (RetCode!=0) // Non-zero return code means success for CreateDirectory. { if (SetAttr) - SetFileAttr(Name,NameW,Attr); - return(MKDIR_SUCCESS); + SetFileAttr(Name,Attr); + return MKDIR_SUCCESS; } int ErrCode=GetLastError(); if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) - return(MKDIR_BADPATH); - return(MKDIR_ERROR); -#endif -#ifdef _EMX -#ifdef _DJGPP - if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0) -#else - if (__mkdir(Name)==0) -#endif - { - if (SetAttr) - SetFileAttr(Name,NameW,Attr); - return(MKDIR_SUCCESS); - } - return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); -#endif -#ifdef _UNIX + return MKDIR_BADPATH; + return MKDIR_ERROR; +#elif defined(_UNIX) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); mode_t uattr=SetAttr ? (mode_t)Attr:0777; - int ErrCode=Name==NULL ? -1:mkdir(Name,uattr); + int ErrCode=mkdir(NameA,uattr); if (ErrCode==-1) - return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); - return(MKDIR_SUCCESS); + return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; + return MKDIR_SUCCESS; +#else + return MKDIR_ERROR; #endif } -bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName) +bool CreatePath(const wchar *Path,bool SkipLastName) { -#if defined(_WIN_32) || defined(_EMX) + if (Path==NULL || *Path==0) + return false; + +#if defined(_WIN_ALL) || defined(_EMX) uint DirAttr=0; #else uint DirAttr=0777; #endif -#ifdef UNICODE_SUPPORTED - bool Wide=PathW!=NULL && *PathW!=0 && UnicodeEnabled(); -#else - bool Wide=false; -#endif - bool IgnoreAscii=false; + bool Success=true; - const char *s=Path; - for (int PosW=0;;PosW++) + for (const wchar *s=Path;*s!=0;s++) { - if (s==NULL || s-Path>=NM || *s==0) - IgnoreAscii=true; - if (Wide && (PosW>=NM || PathW[PosW]==0) || !Wide && IgnoreAscii) + wchar DirName[NM]; + if (s-Path>=ASIZE(DirName)) break; - if (Wide && PathW[PosW]==CPATHDIVIDER || !Wide && *s==CPATHDIVIDER) + + // Process all kinds of path separators, so user can enter Unix style + // path in Windows or Windows in Unix. s>Path check avoids attempting + // creating an empty directory for paths starting from path separator. + if (IsPathDiv(*s) && s>Path) { - wchar *DirPtrW=NULL,DirNameW[NM]; - if (Wide) - { - strncpyw(DirNameW,PathW,PosW); - DirNameW[PosW]=0; - DirPtrW=DirNameW; - } - char DirName[NM]; - if (IgnoreAscii) - WideToChar(DirPtrW,DirName); - else - { -#ifndef DBCS_SUPPORTED - if (*s!=CPATHDIVIDER) - for (const char *n=s;*n!=0 && n-PathIsSet(); bool sc=ftc!=NULL && ftc->IsSet(); bool sa=fta!=NULL && fta->IsSet(); - unsigned int DirAttr=GetFileAttr(Name,NameW); - bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FA_RDONLY)!=0); + uint DirAttr=GetFileAttr(Name); + bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0); if (ResetAttr) - SetFileAttr(Name,NameW,0); + SetFileAttr(Name,0); - wchar DirNameW[NM]; - GetWideName(Name,NameW,DirNameW); - HANDLE hFile=CreateFileW(DirNameW,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + if (hFile==INVALID_HANDLE_VALUE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + } + if (hFile==INVALID_HANDLE_VALUE) return; FILETIME fm,fc,fa; if (sm) - ftm->GetWin32(&fm); + ftm->GetWinFT(&fm); if (sc) - ftc->GetWin32(&fc); + ftc->GetWinFT(&fc); if (sa) - fta->GetWin32(&fa); + fta->GetWinFT(&fa); SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); CloseHandle(hFile); if (ResetAttr) - SetFileAttr(Name,NameW,DirAttr); + SetFileAttr(Name,DirAttr); #endif #if defined(_UNIX) || defined(_EMX) File::SetCloseFileTimeByName(Name,ftm,fta); @@ -149,146 +128,100 @@ void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,Ra } -bool IsRemovable(const char *Name) +bool IsRemovable(const wchar *Name) { -#ifdef _WIN_32 - char Root[NM]; - GetPathRoot(Name,Root); - int Type=GetDriveType(*Root ? Root:NULL); - return(Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM); -#elif defined(_EMX) - char Drive=etoupper(Name[0]); - return((Drive=='A' || Drive=='B') && Name[1]==':'); +#if defined(_WIN_ALL) + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + int Type=GetDriveType(*Root!=0 ? Root:NULL); + return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; #else - return(false); + return false; #endif } #ifndef SFX_MODULE -int64 GetFreeDisk(const char *Name) +int64 GetFreeDisk(const wchar *Name) { -#ifdef _WIN_32 - char Root[NM]; - GetPathRoot(Name,Root); - - typedef BOOL (WINAPI *GETDISKFREESPACEEX)( - LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER - ); - static GETDISKFREESPACEEX pGetDiskFreeSpaceEx=NULL; - - if (pGetDiskFreeSpaceEx==NULL) - { - HMODULE hKernel=GetModuleHandle("kernel32.dll"); - if (hKernel!=NULL) - pGetDiskFreeSpaceEx=(GETDISKFREESPACEEX)GetProcAddress(hKernel,"GetDiskFreeSpaceExA"); - } - if (pGetDiskFreeSpaceEx!=NULL) - { - GetFilePath(Name,Root,ASIZE(Root)); - ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; - uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; - if (pGetDiskFreeSpaceEx(*Root ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && - uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) - return(INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart)); - } - - // We are here if we failed to load GetDiskFreeSpaceExA. - DWORD SectorsPerCluster,BytesPerSector,FreeClusters,TotalClusters; - if (!GetDiskFreeSpace(*Root ? Root:NULL,&SectorsPerCluster,&BytesPerSector,&FreeClusters,&TotalClusters)) - return(1457664); - int64 FreeSize=SectorsPerCluster*BytesPerSector; - FreeSize=FreeSize*FreeClusters; - return(FreeSize); -#elif defined(_BEOS) - char Root[NM]; +#ifdef _WIN_ALL + wchar Root[NM]; GetFilePath(Name,Root,ASIZE(Root)); - dev_t Dev=dev_for_path(*Root ? Root:"."); - if (Dev<0) - return(1457664); - fs_info Info; - if (fs_stat_dev(Dev,&Info)!=0) - return(1457664); - int64 FreeSize=Info.block_size; - FreeSize=FreeSize*Info.free_blocks; - return(FreeSize); + + ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; + uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; + if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && + uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) + return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); + return 0; #elif defined(_UNIX) - return(1457664); -#elif defined(_EMX) - int Drive=IsDiskLetter(Name) ? etoupper(Name[0])-'A'+1:0; -#ifndef _DJGPP - if (_osmode == OS2_MODE) - { - FSALLOCATE fsa; - if (DosQueryFSInfo(Drive,1,&fsa,sizeof(fsa))!=0) - return(1457664); - int64 FreeSize=fsa.cSectorUnit*fsa.cbSector; - FreeSize=FreeSize*fsa.cUnitAvail; - return(FreeSize); - } - else -#endif - { - union REGS regs,outregs; - memset(®s,0,sizeof(regs)); - regs.h.ah=0x36; - regs.h.dl=Drive; -#ifdef _DJGPP - int86 (0x21,®s,&outregs); + wchar Root[NM]; + GetFilePath(Name,Root,ASIZE(Root)); + char RootA[NM]; + WideToChar(Root,RootA,ASIZE(RootA)); + struct statvfs sfs; + if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0) + return 0; + int64 FreeSize=sfs.f_bsize; + FreeSize=FreeSize*sfs.f_bavail; + return FreeSize; #else - _int86 (0x21,®s,&outregs); -#endif - if (outregs.x.ax==0xffff) - return(1457664); - int64 FreeSize=outregs.x.ax*outregs.x.cx; - FreeSize=FreeSize*outregs.x.bx; - return(FreeSize); - } -#else - #define DISABLEAUTODETECT - return(1457664); + return 0; #endif } #endif -bool FileExist(const char *Name,const wchar *NameW) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +// Return 'true' for FAT and FAT32, so we can adjust the maximum supported +// file size to 4 GB for these file systems. +bool IsFAT(const wchar *Name) { -#ifdef _WIN_32 - if (WinNT() && NameW!=NULL && *NameW!=0) - return(GetFileAttributesW(NameW)!=0xffffffff); - else - return(GetFileAttributes(Name)!=0xffffffff); + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + wchar FileSystem[MAX_PATH+1]; + if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) + return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; + return false; +} +#endif + + +bool FileExist(const wchar *Name) +{ +#ifdef _WIN_ALL + return GetFileAttr(Name)!=0xffffffff; #elif defined(ENABLE_ACCESS) - return(access(Name,0)==0); + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return access(NameA,0)==0; #else - struct FindData FD; - return(FindFile::FastFind(Name,NameW,&FD)); + FindData FD; + return FindFile::FastFind(Name,&FD); #endif } + - -bool WildFileExist(const char *Name,const wchar *NameW) +bool WildFileExist(const wchar *Name) { - if (IsWildcard(Name,NameW)) + if (IsWildcard(Name)) { FindFile Find; Find.SetMask(Name); - Find.SetMaskW(NameW); - struct FindData fd; - return(Find.Next(&fd)); + FindData fd; + return Find.Next(&fd); } - return(FileExist(Name,NameW)); + return FileExist(Name); } bool IsDir(uint Attr) { -#if defined (_WIN_32) || defined(_EMX) - return(Attr!=0xffffffff && (Attr & 0x10)!=0); +#ifdef _WIN_ALL + return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0; #endif #if defined(_UNIX) - return((Attr & 0xF000)==0x4000); + return (Attr & 0xF000)==0x4000; #endif } @@ -296,28 +229,20 @@ bool IsDir(uint Attr) bool IsUnreadable(uint Attr) { #if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) - return(S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr)); -#endif - return(false); -} - - -bool IsLabel(uint Attr) -{ -#if defined (_WIN_32) || defined(_EMX) - return((Attr & 8)!=0); -#else - return(false); + return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); #endif + return false; } bool IsLink(uint Attr) { #ifdef _UNIX - return((Attr & 0xF000)==0xA000); + return (Attr & 0xF000)==0xA000; +#elif defined(_WIN_ALL) + return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0; #else - return(false); + return false; #endif } @@ -328,180 +253,134 @@ bool IsLink(uint Attr) bool IsDeleteAllowed(uint FileAttr) { -#if defined(_WIN_32) || defined(_EMX) - return((FileAttr & (FA_RDONLY|FA_SYSTEM|FA_HIDDEN))==0); +#ifdef _WIN_ALL + return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0; #else - return((FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR)); + return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR); #endif } -void PrepareToDelete(const char *Name,const wchar *NameW) +void PrepareToDelete(const wchar *Name) { -#if defined(_WIN_32) || defined(_EMX) - SetFileAttr(Name,NameW,0); +#if defined(_WIN_ALL) || defined(_EMX) + SetFileAttr(Name,0); #endif #ifdef _UNIX - chmod(Name,S_IRUSR|S_IWUSR|S_IXUSR); + if (Name!=NULL) + { + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR); + } #endif } -uint GetFileAttr(const char *Name,const wchar *NameW) +uint GetFileAttr(const wchar *Name) { -#ifdef _WIN_32 - if (WinNT() && NameW!=NULL && *NameW!=0) - return(GetFileAttributesW(NameW)); - else - return(GetFileAttributes(Name)); -#elif defined(_DJGPP) - return(_chmod(Name,0)); +#ifdef _WIN_ALL + DWORD Attr=GetFileAttributes(Name); + if (Attr==0xffffffff) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Attr=GetFileAttributes(LongName); + } + return Attr; #else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); struct stat st; - if (stat(Name,&st)!=0) - return(0); -#ifdef _EMX - return(st.st_attr); -#else - return(st.st_mode); -#endif + if (stat(NameA,&st)!=0) + return 0; + return st.st_mode; #endif } -bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr) +bool SetFileAttr(const wchar *Name,uint Attr) { - bool Success; -#ifdef _WIN_32 - if (WinNT() && NameW!=NULL && *NameW!=0) - Success=SetFileAttributesW(NameW,Attr)!=0; - else - Success=SetFileAttributes(Name,Attr)!=0; -#elif defined(_DJGPP) - Success=_chmod(Name,1,Attr)!=-1; -#elif defined(_EMX) - Success=__chmod(Name,1,Attr)!=-1; +#ifdef _WIN_ALL + bool Success=SetFileAttributes(Name,Attr)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=SetFileAttributes(LongName,Attr)!=0; + } + return Success; #elif defined(_UNIX) - Success=chmod(Name,(mode_t)Attr)==0; + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return chmod(NameA,(mode_t)Attr)==0; #else - Success=false; -#endif - return(Success); -} - - -void ConvertNameToFull(const char *Src,char *Dest) -{ -#ifdef _WIN_32 -#ifndef _WIN_CE - char FullName[NM],*NamePtr; - if (GetFullPathName(Src,sizeof(FullName),FullName,&NamePtr)) - strcpy(Dest,FullName); - else -#endif - if (Src!=Dest) - strcpy(Dest,Src); -#else - char FullName[NM]; - if (IsPathDiv(*Src) || IsDiskLetter(Src)) - strcpy(FullName,Src); - else - { - if (getcwd(FullName,sizeof(FullName))==NULL) - *FullName=0; - else - AddEndSlash(FullName); - strcat(FullName,Src); - } - strcpy(Dest,FullName); + return false; #endif } -#ifndef SFX_MODULE -void ConvertNameToFull(const wchar *Src,wchar *Dest) +#if 0 +wchar *MkTemp(wchar *Name,size_t MaxSize) { - if (Src==NULL || *Src==0) - { - *Dest=0; - return; - } -#ifdef _WIN_32 -#ifndef _WIN_CE - if (WinNT()) -#endif - { -#ifndef _WIN_CE - wchar FullName[NM],*NamePtr; - if (GetFullPathNameW(Src,sizeof(FullName)/sizeof(FullName[0]),FullName,&NamePtr)) - strcpyw(Dest,FullName); - else -#endif - if (Src!=Dest) - strcpyw(Dest,Src); - } -#ifndef _WIN_CE - else - { - char AnsiName[NM]; - WideToChar(Src,AnsiName); - ConvertNameToFull(AnsiName,AnsiName); - CharToWide(AnsiName,Dest); - } -#endif -#else - char AnsiName[NM]; - WideToChar(Src,AnsiName); - ConvertNameToFull(AnsiName,AnsiName); - CharToWide(AnsiName,Dest); -#endif -} + size_t Length=wcslen(Name); + + RarTime CurTime; + CurTime.SetCurrentTime(); + + // We cannot use CurTime.GetWin() as is, because its lowest bits can + // have low informational value, like being a zero or few fixed numbers. + uint Random=(uint)(CurTime.GetWin()/100000); + + // Using PID we guarantee that different RAR copies use different temp names + // even if started in exactly the same time. + uint PID=0; +#ifdef _WIN_ALL + PID=(uint)GetCurrentProcessId(); +#elif defined(_UNIX) + PID=(uint)getpid(); #endif - -#ifndef SFX_MODULE -char *MkTemp(char *Name) -{ - size_t Length=strlen(Name); - if (Length<=6) - return(NULL); - int Random=clock(); - for (int Attempt=0;;Attempt++) + for (uint Attempt=0;;Attempt++) { - sprintf(Name+Length-6,"%06u",Random+Attempt); - Name[Length-4]='.'; + uint Ext=Random%50000+Attempt; + wchar RndText[50]; + swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext); + if (Length+wcslen(RndText)>=MaxSize || Attempt==1000) + return NULL; + wcsncpyz(Name+Length,RndText,MaxSize-Length); if (!FileExist(Name)) break; - if (Attempt==1000) - return(NULL); } - return(Name); + return Name; } #endif - - -#ifndef SFX_MODULE -uint CalcFileCRC(File *SrcFile,int64 Size,CALCCRC_SHOWMODE ShowMode) +#if !defined(SFX_MODULE) +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) { - SaveFilePos SavePos(*SrcFile); - const size_t BufSize=0x10000; - Array Data(BufSize); - int64 BlockCount=0; - uint DataCRC=0xffffffff; - -#if !defined(SILENT) && !defined(_WIN_CE) - int64 FileLength=SrcFile->FileLength(); - if (ShowMode!=CALCCRC_SHOWNONE) - { - mprintf(St(MCalcCRC)); - mprintf(" "); - } - + int64 SavePos=SrcFile->Tell(); +#ifndef SILENT + int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; #endif - SrcFile->Seek(0,SEEK_SET); + if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0) + uiMsg(UIEVENT_FILESUMSTART); + + if ((Flags & CALCFSUM_CURPOS)==0) + SrcFile->Seek(0,SEEK_SET); + + const size_t BufSize=0x100000; + Array Data(BufSize); + + + DataHash HashCRC,HashBlake2; + HashCRC.Init(HASH_CRC32,Threads); + HashBlake2.Init(HASH_BLAKE2,Threads); + + int64 BlockCount=0; + int64 TotalRead=0; while (true) { size_t SizeToRead; @@ -512,67 +391,113 @@ uint CalcFileCRC(File *SrcFile,int64 Size,CALCCRC_SHOWMODE ShowMode) int ReadSize=SrcFile->Read(&Data[0],SizeToRead); if (ReadSize==0) break; + TotalRead+=ReadSize; - ++BlockCount; - if ((BlockCount & 15)==0) + if ((++BlockCount & 0xf)==0) { -#if !defined(SILENT) && !defined(_WIN_CE) - if (ShowMode==CALCCRC_SHOWALL) - mprintf("\b\b\b\b%3d%%",ToPercent(BlockCount*int64(BufSize),FileLength)); +#ifndef SILENT + if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) + uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength); + else + { + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); + } #endif Wait(); } - DataCRC=CRC(DataCRC,&Data[0],ReadSize); + + if (CRC32!=NULL) + HashCRC.Update(&Data[0],ReadSize); + if (Blake2!=NULL) + HashBlake2.Update(&Data[0],ReadSize); + if (Size!=INT64NDF) Size-=ReadSize; } -#if !defined(SILENT) && !defined(_WIN_CE) - if (ShowMode==CALCCRC_SHOWALL) - mprintf("\b\b\b\b "); -#endif - return(DataCRC^0xffffffff); + SrcFile->Seek(SavePos,SEEK_SET); + + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMEND); + + if (CRC32!=NULL) + *CRC32=HashCRC.GetCRC32(); + if (Blake2!=NULL) + { + HashValue Result; + HashBlake2.Result(&Result); + memcpy(Blake2,Result.Digest,sizeof(Result.Digest)); + } } #endif -bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW) +bool RenameFile(const wchar *SrcName,const wchar *DestName) { - return(rename(SrcName,DestName)==0); +#ifdef _WIN_ALL + bool Success=MoveFile(SrcName,DestName)!=0; + if (!Success) + { + wchar LongName1[NM],LongName2[NM]; + if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) && + GetWinLongPath(DestName,LongName2,ASIZE(LongName2))) + Success=MoveFile(LongName1,LongName2)!=0; + } + return Success; +#else + char SrcNameA[NM],DestNameA[NM]; + WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA)); + WideToChar(DestName,DestNameA,ASIZE(DestNameA)); + bool Success=rename(SrcNameA,DestNameA)==0; + return Success; +#endif } -bool DelFile(const char *Name) +bool DelFile(const wchar *Name) { - return(DelFile(Name,NULL)); -} - - -bool DelFile(const char *Name,const wchar *NameW) -{ - return(remove(Name)==0); +#ifdef _WIN_ALL + bool Success=DeleteFile(Name)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=DeleteFile(LongName)!=0; + } + return Success; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + bool Success=remove(NameA)==0; + return Success; +#endif } - - -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) -bool SetFileCompression(char *Name,wchar *NameW,bool State) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State) { - wchar FileNameW[NM]; - GetWideName(Name,NameW,FileNameW); - HANDLE hFile=CreateFileW(FileNameW,FILE_READ_DATA|FILE_WRITE_DATA, + HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); if (hFile==INVALID_HANDLE_VALUE) - return(false); + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); + } + if (hFile==INVALID_HANDLE_VALUE) + return false; SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; DWORD Result; int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, sizeof(NewState),NULL,0,&Result,NULL); CloseHandle(hFile); - return(RetCode!=0); + return RetCode!=0; } #endif @@ -583,3 +508,5 @@ bool SetFileCompression(char *Name,wchar *NameW,bool State) + + diff --git a/libunrar/filefn.hpp b/libunrar/filefn.hpp index 77df847..1bda9b5 100644 --- a/libunrar/filefn.hpp +++ b/libunrar/filefn.hpp @@ -3,42 +3,48 @@ enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; -MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr); -bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName); -void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta); -bool IsRemovable(const char *Name); -int64 GetFreeDisk(const char *Name); -bool FileExist(const char *Name,const wchar *NameW=NULL); -bool WildFileExist(const char *Name,const wchar *NameW=NULL); +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr); +bool CreatePath(const wchar *Path,bool SkipLastName); +void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta); +bool IsRemovable(const wchar *Name); + +#ifndef SFX_MODULE +int64 GetFreeDisk(const wchar *Name); +#endif + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +bool IsFAT(const wchar *Root); +#endif + +bool FileExist(const wchar *Name); +bool WildFileExist(const wchar *Name); bool IsDir(uint Attr); bool IsUnreadable(uint Attr); -bool IsLabel(uint Attr); bool IsLink(uint Attr); -void SetSFXMode(const char *FileName); -void EraseDiskContents(const char *FileName); +void SetSFXMode(const wchar *FileName); +void EraseDiskContents(const wchar *FileName); bool IsDeleteAllowed(uint FileAttr); -void PrepareToDelete(const char *Name,const wchar *NameW=NULL); -uint GetFileAttr(const char *Name,const wchar *NameW=NULL); -bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr); -void ConvertNameToFull(const char *Src,char *Dest); -void ConvertNameToFull(const wchar *Src,wchar *Dest); -char* MkTemp(char *Name); +void PrepareToDelete(const wchar *Name); +uint GetFileAttr(const wchar *Name); +bool SetFileAttr(const wchar *Name,uint Attr); +#if 0 +wchar* MkTemp(wchar *Name,size_t MaxSize); +#endif +enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; -enum CALCCRC_SHOWMODE {CALCCRC_SHOWNONE,CALCCRC_SHOWTEXT,CALCCRC_SHOWALL}; -uint CalcFileCRC(File *SrcFile,int64 Size=INT64NDF,CALCCRC_SHOWMODE ShowMode=CALCCRC_SHOWNONE); +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0); -bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW); -bool DelFile(const char *Name); -bool DelFile(const char *Name,const wchar *NameW); -bool DelDir(const char *Name); -bool DelDir(const char *Name,const wchar *NameW); +bool RenameFile(const wchar *SrcName,const wchar *DestName); +bool DelFile(const wchar *Name); +bool DelDir(const wchar *Name); -#if defined(_WIN_32) && !defined(_WIN_CE) -bool SetFileCompression(char *Name,wchar *NameW,bool State); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State); #endif + #endif diff --git a/libunrar/filestr.cpp b/libunrar/filestr.cpp index c5fba51..a5d29d7 100644 --- a/libunrar/filestr.cpp +++ b/libunrar/filestr.cpp @@ -1,189 +1,166 @@ #include "rar.hpp" -static bool IsUnicode(byte *Data,int Size); - -bool ReadTextFile(const char *Name,StringList *List,bool Config, - bool AbortOnError,RAR_CHARSET SrcCharset,bool Unquote, - bool SkipComments,bool ExpandEnvStr) +bool ReadTextFile( + const wchar *Name, + StringList *List, + bool Config, + bool AbortOnError, + RAR_CHARSET SrcCharset, + bool Unquote, + bool SkipComments, + bool ExpandEnvStr) { - char FileName[NM]; - if (Config) - GetConfigName(Name,FileName,true); - else - strcpy(FileName,Name); + wchar FileName[NM]; + *FileName=0; + + if (Name!=NULL) + if (Config) + GetConfigName(Name,FileName,ASIZE(FileName),true,false); + else + wcsncpyz(FileName,Name,ASIZE(FileName)); File SrcFile; - if (*FileName) + if (*FileName!=0) { - bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName); + bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0); if (!OpenCode) { if (AbortOnError) - ErrHandler.Exit(OPEN_ERROR); - return(false); + ErrHandler.Exit(RARX_OPEN); + return false; } } else SrcFile.SetHandleType(FILE_HANDLESTD); - unsigned int DataSize=0,ReadSize; - const int ReadBlock=1024; - Array Data(ReadBlock+5); + uint DataSize=0,ReadSize; + const int ReadBlock=4096; + + Array Data(ReadBlock); while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) { DataSize+=ReadSize; - Data.Add(ReadSize); + Data.Add(ReadSize); // Always have ReadBlock available for next data. } + // Set to really read size, so we can zero terminate it correctly. + Data.Alloc(DataSize); - memset(&Data[DataSize],0,5); + int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0; + int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0; + bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf; - if (SrcCharset==RCH_UNICODE || - SrcCharset==RCH_DEFAULT && IsUnicode((byte *)&Data[0],DataSize)) + if (SrcCharset==RCH_DEFAULT) + SrcCharset=DetectTextEncoding(&Data[0],DataSize); + + Array DataW; + + if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI) { - // Unicode in native system format, can be more than 2 bytes per character. - Array DataW(Data.Size()/2+1); - for (size_t I=2;I AnsiName; - - while (*CurStr!=0) - { - wchar *NextStr=CurStr,*CmtPtr=NULL; - while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) - { - if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') - { - *NextStr=0; - CmtPtr=NextStr; - } - NextStr++; - } - *NextStr=0; - for (wchar *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) - { - if (*SpacePtr!=' ' && *SpacePtr!='\t') - break; - *SpacePtr=0; - } - if (*CurStr) - { - // Length and AddSize must be defined as signed, because AddSize - // can be negative. - int Length=(int)strlenw(CurStr); - int AddSize=4*(Length-(int)AnsiName.Size()+1); - - if (AddSize>0) - AnsiName.Add(AddSize); - if (Unquote && *CurStr=='\"' && CurStr[Length-1]=='\"') - { - CurStr[Length-1]=0; - CurStr++; - } - WideToChar(CurStr,&AnsiName[0],AnsiName.Size()); - - bool Expanded=false; -#if defined(_WIN_32) && !defined(_WIN_CE) - if (ExpandEnvStr && *CurStr=='%') - { - // expanding environment variables in Windows version - - char ExpName[NM]; - wchar ExpNameW[NM]; - *ExpNameW=0; - int ret,retw=1; - ret=ExpandEnvironmentStrings(&AnsiName[0],ExpName,ASIZE(ExpName)); - if (ret!=0 && WinNT()) - retw=ExpandEnvironmentStringsW(CurStr,ExpNameW,ASIZE(ExpNameW)); - Expanded=ret!=0 && retAddString(ExpName,ExpNameW); - } + Data.Push(0); // Zero terminate. +#if defined(_WIN_ALL) + if (SrcCharset==RCH_OEM) + OemToCharA((char *)&Data[0],(char *)&Data[0]); #endif - if (!Expanded) - List->AddString(&AnsiName[0],CurStr); - } - CurStr=NextStr+1; - while (*CurStr=='\r' || *CurStr=='\n') - CurStr++; - } + DataW.Alloc(Data.Size()); + CharToWide((char *)&Data[0],&DataW[0],DataW.Size()); } - else + + if (SrcCharset==RCH_UNICODE) { - char *CurStr=&Data[0]; - while (*CurStr!=0) + size_t Start=2; // Skip byte order mark. + if (!LittleEndian && !BigEndian) // No byte order mask. { - char *NextStr=CurStr,*CmtPtr=NULL; - while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) - { - if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') - { - *NextStr=0; - CmtPtr=NextStr; - } - NextStr++; - } - *NextStr=0; - for (char *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) - { - if (*SpacePtr!=' ' && *SpacePtr!='\t') - break; - *SpacePtr=0; - } - if (*CurStr) - { - if (Unquote && *CurStr=='\"') - { - size_t Length=strlen(CurStr); - if (CurStr[Length-1]=='\"') - { - CurStr[Length-1]=0; - CurStr++; - } - } -#if defined(_WIN_32) - if (SrcCharset==RCH_OEM) - OemToChar(CurStr,CurStr); -#endif - - bool Expanded=false; -#if defined(_WIN_32) && !defined(_WIN_CE) - if (ExpandEnvStr && *CurStr=='%') - { - // expanding environment variables in Windows version - - char ExpName[NM]; - int ret=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName)); - Expanded=ret!=0 && retAddString(ExpName); - } -#endif - if (!Expanded) - List->AddString(CurStr); - } - CurStr=NextStr+1; - while (*CurStr=='\r' || *CurStr=='\n') - CurStr++; + Start=0; + LittleEndian=1; } + + DataW.Alloc(Data.Size()/2+1); + size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16. + for (size_t I=Start;I=CurStr;SpacePtr--) + { + if (*SpacePtr!=' ' && *SpacePtr!='\t') + break; + *SpacePtr=0; + } + + if (Unquote && *CurStr=='\"') + { + size_t Length=wcslen(CurStr); + if (CurStr[Length-1]=='\"') + { + CurStr[Length-1]=0; + CurStr++; + } + } + + bool Expanded=false; +#if defined(_WIN_ALL) + if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows. + { + wchar ExpName[NM]; + *ExpName=0; + DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName)); + Expanded=Result!=0 && ResultAddString(ExpName); + } +#endif + if (!Expanded && *CurStr!=0) + List->AddString(CurStr); + + if (Done) + break; + CurStr=NextStr+1; + while (*CurStr=='\r' || *CurStr=='\n') + CurStr++; + } + return true; } -bool IsUnicode(byte *Data,int Size) +RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize) { - if (Size<4 || Data[0]!=0xff || Data[1]!=0xfe) - return(false); - for (int I=2;I3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf && + IsTextUtf8(Data+3,DataSize-3)) + return RCH_UTF8; + + bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254; + bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255; + + if (LittleEndian || BigEndian) + for (size_t I=LittleEndian ? 3 : 2;IError=false; if (*FindMask==0) - return(false); -#ifdef _WIN_32 + return false; +#ifdef _WIN_ALL if (FirstCall) { - if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd))==INVALID_HANDLE_VALUE) - return(false); + if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE) + return false; } else - if (Win32Find(hFind,FindMask,FindMaskW,fd)==INVALID_HANDLE_VALUE) - return(false); + if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE) + return false; #else if (FirstCall) { - char DirName[NM]; - strcpy(DirName,FindMask); + wchar DirName[NM]; + wcsncpyz(DirName,FindMask,ASIZE(DirName)); RemoveNameFromPath(DirName); if (*DirName==0) - strcpy(DirName,"."); -/* - else - { - int Length=strlen(DirName); - if (Length>1 && DirName[Length-1]==CPATHDIVIDER && (Length!=3 || !IsDriveDiv(DirName[1]))) - DirName[Length-1]=0; - } -*/ - if ((dirp=opendir(DirName))==NULL) + wcsncpyz(DirName,L".",ASIZE(DirName)); + char DirNameA[NM]; + WideToChar(DirName,DirNameA,ASIZE(DirNameA)); + if ((dirp=opendir(DirNameA))==NULL) { fd->Error=(errno!=ENOENT); - return(false); + return false; } } while (1) { + wchar Name[NM]; struct dirent *ent=readdir(dirp); if (ent==NULL) - return(false); + return false; if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) continue; - if (CmpName(FindMask,ent->d_name,MATCH_NAMES)) + if (!CharToWide(ent->d_name,Name,ASIZE(Name))) + uiMsg(UIERROR_INVALIDNAME,UINULL,Name); + + if (CmpName(FindMask,Name,MATCH_NAMES)) { - char FullName[NM]; - strcpy(FullName,FindMask); + wchar FullName[NM]; + wcsncpyz(FullName,FindMask,ASIZE(FullName)); *PointToName(FullName)=0; - if (strlen(FullName)+strlen(ent->d_name)>=ASIZE(FullName)-1) + if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1) { -#ifndef SILENT - Log(NULL,"\n%s%s",FullName,ent->d_name); - Log(NULL,St(MPathTooLong)); -#endif - return(false); + uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name); + return false; } - strcat(FullName,ent->d_name); - if (!FastFind(FullName,NULL,fd,GetSymLink)) + wcsncatz(FullName,Name,ASIZE(FullName)); + if (!FastFind(FullName,fd,GetSymLink)) { ErrHandler.OpenErrorMsg(FullName); continue; } - strcpy(fd->Name,FullName); + wcsncpyz(fd->Name,FullName,ASIZE(fd->Name)); break; } } - *fd->NameW=0; -#ifdef _APPLE - if (!LowAscii(fd->Name)) - UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW)); -#elif defined(UNICODE_SUPPORTED) - if (!LowAscii(fd->Name) && UnicodeEnabled()) - CharToWide(fd->Name,fd->NameW); -#endif #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); + fd->IsLink=IsLink(fd->FileAttr); + FirstCall=false; - char *Name=PointToName(fd->Name); - if (strcmp(Name,".")==0 || strcmp(Name,"..")==0) - return(Next(fd)); - return(true); + wchar *NameOnly=PointToName(fd->Name); + if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0) + return Next(fd); + return true; } -bool FindFile::FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink) +bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) { fd->Error=false; #ifndef _UNIX - if (IsWildcard(FindMask,FindMaskW)) - return(false); + if (IsWildcard(FindMask)) + return false; #endif -#ifdef _WIN_32 - HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd); +#ifdef _WIN_ALL + HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd); if (hFind==INVALID_HANDLE_VALUE) - return(false); + return false; FindClose(hFind); #else + char FindMaskA[NM]; + WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); + struct stat st; if (GetSymLink) { #ifdef SAVE_LINKS - if (lstat(FindMask,&st)!=0) + if (lstat(FindMaskA,&st)!=0) #else - if (stat(FindMask,&st)!=0) + if (stat(FindMaskA,&st)!=0) #endif { fd->Error=(errno!=ENOENT); - return(false); + return false; } } else - if (stat(FindMask,&st)!=0) + if (stat(FindMaskA,&st)!=0) { fd->Error=(errno!=ENOENT); - return(false); + return false; } -#ifdef _DJGPP - fd->FileAttr=_chmod(FindMask,0); -#elif defined(_EMX) - fd->FileAttr=st.st_attr; -#else fd->FileAttr=st.st_mode; -#endif - fd->IsDir=IsDir(st.st_mode); fd->Size=st.st_size; - fd->mtime=st.st_mtime; - fd->atime=st.st_atime; - fd->ctime=st.st_ctime; - fd->FileTime=fd->mtime.GetDos(); - strcpy(fd->Name,FindMask); - *fd->NameW=0; -#ifdef _APPLE - if (!LowAscii(fd->Name)) - UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW)); -#elif defined(UNICODE_SUPPORTED) - if (!LowAscii(fd->Name) && UnicodeEnabled()) - CharToWide(fd->Name,fd->NameW); +#ifdef UNIX_TIME_NS + fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); + fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); + fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); +#else + fd->mtime.SetUnix(st.st_mtime); + fd->atime.SetUnix(st.st_atime); + fd->ctime.SetUnix(st.st_ctime); #endif + + wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); - return(true); + fd->IsLink=IsLink(fd->FileAttr); + + return true; } -#ifdef _WIN_32 -HANDLE FindFile::Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd) +#ifdef _WIN_ALL +HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd) { -#ifndef _WIN_CE - if (WinNT()) -#endif + WIN32_FIND_DATA FindData; + if (hFind==INVALID_HANDLE_VALUE) { - wchar WideMask[NM]; - if (MaskW!=NULL && *MaskW!=0) - strcpyw(WideMask,MaskW); - else - CharToWide(Mask,WideMask); - - WIN32_FIND_DATAW FindData; + hFind=FindFirstFile(Mask,&FindData); if (hFind==INVALID_HANDLE_VALUE) { - hFind=FindFirstFileW(WideMask,&FindData); - if (hFind==INVALID_HANDLE_VALUE) - { - int SysErr=GetLastError(); - fd->Error=(SysErr!=ERROR_FILE_NOT_FOUND && - SysErr!=ERROR_PATH_NOT_FOUND && - SysErr!=ERROR_NO_MORE_FILES); - } + wchar LongMask[NM]; + if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask))) + hFind=FindFirstFile(LongMask,&FindData); } - else - if (!FindNextFileW(hFind,&FindData)) - { - hFind=INVALID_HANDLE_VALUE; - fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; - } - - if (hFind!=INVALID_HANDLE_VALUE) + if (hFind==INVALID_HANDLE_VALUE) { - strcpyw(fd->NameW,WideMask); - strcpyw(PointToName(fd->NameW),FindData.cFileName); - WideToChar(fd->NameW,fd->Name); - fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); - fd->FileAttr=FindData.dwFileAttributes; - WideToChar(FindData.cAlternateFileName,fd->ShortName); - fd->ftCreationTime=FindData.ftCreationTime; - fd->ftLastAccessTime=FindData.ftLastAccessTime; - fd->ftLastWriteTime=FindData.ftLastWriteTime; - fd->mtime=FindData.ftLastWriteTime; - fd->ctime=FindData.ftCreationTime; - fd->atime=FindData.ftLastAccessTime; - fd->FileTime=fd->mtime.GetDos(); - -#ifndef _WIN_CE - if (LowAscii(fd->NameW)) - *fd->NameW=0; -#endif + int SysErr=GetLastError(); + // We must not issue an error for "file not found" and "path not found", + // because it is normal to not find anything for wildcard mask when + // archiving. Also searching for non-existent file is normal in some + // other modules, like WinRAR scanning for winrar_theme_description.txt + // to check if any themes are available. + fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && + SysErr!=ERROR_PATH_NOT_FOUND && + SysErr!=ERROR_NO_MORE_FILES; } } -#ifndef _WIN_CE else + if (!FindNextFile(hFind,&FindData)) + { + hFind=INVALID_HANDLE_VALUE; + fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; + } + + if (hFind!=INVALID_HANDLE_VALUE) { - char CharMask[NM]; - if (Mask!=NULL && *Mask!=0) - strcpy(CharMask,Mask); - else - WideToChar(MaskW,CharMask); + wcsncpyz(fd->Name,Mask,ASIZE(fd->Name)); + SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name)); + fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); + fd->FileAttr=FindData.dwFileAttributes; + fd->ftCreationTime=FindData.ftCreationTime; + fd->ftLastAccessTime=FindData.ftLastAccessTime; + fd->ftLastWriteTime=FindData.ftLastWriteTime; + fd->mtime.SetWinFT(&FindData.ftLastWriteTime); + fd->ctime.SetWinFT(&FindData.ftCreationTime); + fd->atime.SetWinFT(&FindData.ftLastAccessTime); - WIN32_FIND_DATA FindData; - if (hFind==INVALID_HANDLE_VALUE) - { - hFind=FindFirstFile(CharMask,&FindData); - if (hFind==INVALID_HANDLE_VALUE) - { - int SysErr=GetLastError(); - fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && SysErr!=ERROR_PATH_NOT_FOUND; - } - } - else - if (!FindNextFile(hFind,&FindData)) - { - hFind=INVALID_HANDLE_VALUE; - fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; - } - if (hFind!=INVALID_HANDLE_VALUE) - { - strcpy(fd->Name,CharMask); - strcpy(PointToName(fd->Name),FindData.cFileName); - CharToWide(fd->Name,fd->NameW); - fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); - fd->FileAttr=FindData.dwFileAttributes; - strcpy(fd->ShortName,FindData.cAlternateFileName); - fd->ftCreationTime=FindData.ftCreationTime; - fd->ftLastAccessTime=FindData.ftLastAccessTime; - fd->ftLastWriteTime=FindData.ftLastWriteTime; - fd->mtime=FindData.ftLastWriteTime; - fd->ctime=FindData.ftCreationTime; - fd->atime=FindData.ftLastAccessTime; - fd->FileTime=fd->mtime.GetDos(); - if (LowAscii(fd->Name)) - *fd->NameW=0; - } } -#endif fd->Flags=0; - return(hFind); + return hFind; } #endif diff --git a/libunrar/find.hpp b/libunrar/find.hpp index 99e804a..250637f 100644 --- a/libunrar/find.hpp +++ b/libunrar/find.hpp @@ -2,22 +2,20 @@ #define _RAR_FINDDATA_ enum FINDDATA_FLAGS { - FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode + FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode. }; struct FindData { - char Name[NM]; - wchar NameW[NM]; - int64 Size; + wchar Name[NM]; + uint64 Size; uint FileAttr; - uint FileTime; bool IsDir; + bool IsLink; RarTime mtime; RarTime ctime; RarTime atime; -#ifdef _WIN_32 - char ShortName[NM]; +#ifdef _WIN_ALL FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; @@ -29,14 +27,13 @@ struct FindData class FindFile { private: -#ifdef _WIN_32 - static HANDLE Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd); +#ifdef _WIN_ALL + static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd); #endif - char FindMask[NM]; - wchar FindMaskW[NM]; + wchar FindMask[NM]; bool FirstCall; -#ifdef _WIN_32 +#ifdef _WIN_ALL HANDLE hFind; #else DIR *dirp; @@ -44,10 +41,9 @@ class FindFile public: FindFile(); ~FindFile(); - void SetMask(const char *FindMask); - void SetMaskW(const wchar *FindMaskW); - bool Next(struct FindData *fd,bool GetSymLink=false); - static bool FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink=false); + void SetMask(const wchar *Mask); + bool Next(FindData *fd,bool GetSymLink=false); + static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false); }; #endif diff --git a/libunrar/getbits.cpp b/libunrar/getbits.cpp index e5d590a..e4db269 100644 --- a/libunrar/getbits.cpp +++ b/libunrar/getbits.cpp @@ -1,18 +1,30 @@ #include "rar.hpp" -BitInput::BitInput() +BitInput::BitInput(bool AllocBuffer) { - // getbits attempts to read data from InAddr, InAddr+1, InAddr+2 positions. - // So let's allocate two additional bytes for situation, when we need to - // read only 1 byte from the last position of buffer and avoid a crash - // from access to next 2 bytes, which contents we do not need. - InBuf=new byte[MAX_SIZE+2]; + ExternalBuffer=false; + if (AllocBuffer) + { + // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. + // So let's allocate 3 additional bytes for situation, when we need to + // read only 1 byte from the last position of buffer and avoid a crash + // from access to next 3 bytes, which contents we do not need. + size_t BufSize=MAX_SIZE+3; + InBuf=new byte[BufSize]; + + // Ensure that we get predictable results when accessing bytes in area + // not filled with read data. + memset(InBuf,0,BufSize); + } + else + InBuf=NULL; } BitInput::~BitInput() { - delete[] InBuf; + if (!ExternalBuffer) + delete[] InBuf; } @@ -26,5 +38,15 @@ void BitInput::faddbits(uint Bits) uint BitInput::fgetbits() { // Function wrapped version of inline getbits to save code size. - return(getbits()); + return getbits(); } + + +void BitInput::SetExternalBuffer(byte *Buf) +{ + if (InBuf!=NULL && !ExternalBuffer) + delete[] InBuf; + InBuf=Buf; + ExternalBuffer=true; +} + diff --git a/libunrar/getbits.hpp b/libunrar/getbits.hpp index d44fb9f..2e151da 100644 --- a/libunrar/getbits.hpp +++ b/libunrar/getbits.hpp @@ -5,11 +5,13 @@ class BitInput { public: enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer. - protected: + int InAddr; // Curent byte position in the buffer. int InBit; // Current bit position in the current byte. + + bool ExternalBuffer; public: - BitInput(); + BitInput(bool AllocBuffer); ~BitInput(); byte *InBuf; // Dynamically allocated input buffer. @@ -35,7 +37,20 @@ class BitInput BitField|=(uint)InBuf[InAddr+1] << 8; BitField|=(uint)InBuf[InAddr+2]; BitField >>= (8-InBit); - return(BitField & 0xffff); + return BitField & 0xffff; + } + + // Return 32 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits32() + { + uint BitField=(uint)InBuf[InAddr] << 24; + BitField|=(uint)InBuf[InAddr+1] << 16; + BitField|=(uint)InBuf[InAddr+2] << 8; + BitField|=(uint)InBuf[InAddr+3]; + BitField <<= InBit; + BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); + return BitField & 0xffffffff; } void faddbits(uint Bits); @@ -45,7 +60,9 @@ class BitInput // if buffer will be overflown. bool Overflow(uint IncPtr) { - return(InAddr+IncPtr>=MAX_SIZE); + return InAddr+IncPtr>=MAX_SIZE; } + + void SetExternalBuffer(byte *Buf); }; #endif diff --git a/libunrar/global.cpp b/libunrar/global.cpp index 593a057..3975813 100644 --- a/libunrar/global.cpp +++ b/libunrar/global.cpp @@ -1,4 +1,7 @@ #define INCLUDEGLOBAL +#if defined(__BORLANDC__) || defined(_MSC_VER) +#pragma hdrstop +#endif #include "rar.hpp" diff --git a/libunrar/hardlinks.cpp b/libunrar/hardlinks.cpp new file mode 100644 index 0000000..946a395 --- /dev/null +++ b/libunrar/hardlinks.cpp @@ -0,0 +1,39 @@ +bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +{ + SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. + + if (!FileExist(NameExisting)) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + uiMsg(UIERROR_NOLINKTARGET); + ErrHandler.SetErrorCode(RARX_CREATE); + return false; + } + CreatePath(NameNew,true); + +#ifdef _WIN_ALL + bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#elif defined(_UNIX) + char NameExistingA[NM],NameNewA[NM]; + WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA)); + WideToChar(NameNew,NameNewA,ASIZE(NameNewA)); + bool Success=link(NameExistingA,NameNewA)==0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#else + return false; +#endif +} + diff --git a/libunrar/hash.cpp b/libunrar/hash.cpp new file mode 100644 index 0000000..a4559e0 --- /dev/null +++ b/libunrar/hash.cpp @@ -0,0 +1,135 @@ +#include "rar.hpp" + +void HashValue::Init(HASH_TYPE Type) +{ + HashValue::Type=Type; + + // Zero length data CRC32 is 0. It is important to set it when creating + // headers with no following data like directories or symlinks. + if (Type==HASH_RAR14 || Type==HASH_CRC32) + CRC32=0; + if (Type==HASH_BLAKE2) + { + // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f + // is BLAKE2sp hash of empty data. We init the structure to this value, + // so if we create a file or service header with no following data like + // "file copy" or "symlink", we set the checksum to proper value avoiding + // additional header type or size checks when extracting. + static byte EmptyHash[32]={ + 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43, + 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25, + 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1, + 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f + }; + memcpy(Digest,EmptyHash,sizeof(Digest)); + } +} + + +bool HashValue::operator == (const HashValue &cmp) +{ + if (Type==HASH_NONE || cmp.Type==HASH_NONE) + return true; + if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 || + Type==HASH_CRC32 && cmp.Type==HASH_CRC32) + return CRC32==cmp.CRC32; + if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2) + return memcmp(Digest,cmp.Digest,sizeof(Digest))==0; + return false; +} + + +DataHash::DataHash() +{ + blake2ctx=NULL; + HashType=HASH_NONE; +#ifdef RAR_SMP + ThPool=NULL; + MaxThreads=0; +#endif +} + + +DataHash::~DataHash() +{ +#ifdef RAR_SMP + delete ThPool; +#endif + cleandata(&CurCRC32, sizeof(CurCRC32)); + if (blake2ctx!=NULL) + { + cleandata(blake2ctx, sizeof(blake2sp_state)); + delete blake2ctx; + } +} + + +void DataHash::Init(HASH_TYPE Type,uint MaxThreads) +{ + if (blake2ctx==NULL) + blake2ctx=new blake2sp_state; + HashType=Type; + if (Type==HASH_RAR14) + CurCRC32=0; + if (Type==HASH_CRC32) + CurCRC32=0xffffffff; // Initial CRC32 value. + if (Type==HASH_BLAKE2) + blake2sp_init(blake2ctx); +#ifdef RAR_SMP + DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads); +#endif +} + + +void DataHash::Update(const void *Data,size_t DataSize) +{ +#ifndef SFX_MODULE + if (HashType==HASH_RAR14) + CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize); +#endif + if (HashType==HASH_CRC32) + CurCRC32=CRC32(CurCRC32,Data,DataSize); + + if (HashType==HASH_BLAKE2) + { +#ifdef RAR_SMP + if (MaxThreads>1 && ThPool==NULL) + ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER); + blake2ctx->ThPool=ThPool; + blake2ctx->MaxThreads=MaxThreads; +#endif + blake2sp_update( blake2ctx, (byte *)Data, DataSize); + } +} + + +void DataHash::Result(HashValue *Result) +{ + Result->Type=HashType; + if (HashType==HASH_RAR14) + Result->CRC32=CurCRC32; + if (HashType==HASH_CRC32) + Result->CRC32=CurCRC32^0xffffffff; + if (HashType==HASH_BLAKE2) + { + // Preserve the original context, so we can continue hashing if necessary. + blake2sp_state res=*blake2ctx; + blake2sp_final(&res,Result->Digest); + } +} + + +uint DataHash::GetCRC32() +{ + return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0; +} + + +bool DataHash::Cmp(HashValue *CmpValue,byte *Key) +{ + HashValue Final; + Result(&Final); + if (Key!=NULL) + ConvertHashToMAC(&Final,Key); + return Final==*CmpValue; +} diff --git a/libunrar/hash.hpp b/libunrar/hash.hpp new file mode 100644 index 0000000..b7d879f --- /dev/null +++ b/libunrar/hash.hpp @@ -0,0 +1,52 @@ +#ifndef _RAR_DATAHASH_ +#define _RAR_DATAHASH_ + +enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2}; + +struct HashValue +{ + void Init(HASH_TYPE Type); + bool operator == (const HashValue &cmp); + bool operator != (const HashValue &cmp) {return !(*this==cmp);} + + HASH_TYPE Type; + union + { + uint CRC32; + byte Digest[SHA256_DIGEST_SIZE]; + }; +}; + + +#ifdef RAR_SMP +class ThreadPool; +class DataHash; +#endif + + +class DataHash +{ + private: + HASH_TYPE HashType; + uint CurCRC32; + blake2sp_state *blake2ctx; + +#ifdef RAR_SMP + ThreadPool *ThPool; + + uint MaxThreads; + // Upper limit for maximum threads to prevent wasting threads in pool. + static const uint MaxHashThreads=8; +#endif + public: + DataHash(); + ~DataHash(); + void Init(HASH_TYPE Type,uint MaxThreads); + void Update(const void *Data,size_t DataSize); + void Result(HashValue *Result); + uint GetCRC32(); + bool Cmp(HashValue *CmpValue,byte *Key); + HASH_TYPE Type() {return HashType;} +}; + +#endif diff --git a/libunrar/headers.cpp b/libunrar/headers.cpp new file mode 100644 index 0000000..b042dc3 --- /dev/null +++ b/libunrar/headers.cpp @@ -0,0 +1,61 @@ +#include "rar.hpp" + +void FileHeader::Reset(size_t SubDataSize) +{ + SubData.Alloc(SubDataSize); + BaseBlock::Reset(); + FileHash.Init(HASH_NONE); + mtime.Reset(); + atime.Reset(); + ctime.Reset(); + SplitBefore=false; + SplitAfter=false; + + UnknownUnpSize=0; + + SubFlags=0; // Important for RAR 3.0 subhead. + + CryptMethod=CRYPT_NONE; + Encrypted=false; + SaltSet=false; + UsePswCheck=false; + UseHashKey=false; + Lg2Count=0; + + Solid=false; + Dir=false; + WinSize=0; + Inherited=false; + SubBlock=false; + CommentInHeader=false; + Version=false; + LargeFile=false; + + RedirType=FSREDIR_NONE; + DirTarget=false; + UnixOwnerSet=false; +} + + +FileHeader& FileHeader::operator = (FileHeader &hd) +{ + SubData.Reset(); + memcpy(this,&hd,sizeof(*this)); + SubData.CleanData(); + SubData=hd.SubData; + return *this; +} + + +void MainHeader::Reset() +{ + HighPosAV=0; + PosAV=0; + CommentInHeader=false; + PackComment=false; + Locator=false; + QOpenOffset=0; + QOpenMaxSize=0; + RROffset=0; + RRMaxSize=0; +} diff --git a/libunrar/headers.hpp b/libunrar/headers.hpp index 9e632f4..6af453a 100644 --- a/libunrar/headers.hpp +++ b/libunrar/headers.hpp @@ -1,33 +1,31 @@ #ifndef _RAR_HEADERS_ #define _RAR_HEADERS_ -#define SIZEOF_MARKHEAD 7 -#define SIZEOF_OLDMHD 7 -#define SIZEOF_NEWMHD 13 -#define SIZEOF_OLDLHD 21 -#define SIZEOF_NEWLHD 32 +#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header. +#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header. +#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. +#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. +#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header. #define SIZEOF_SHORTBLOCKHEAD 7 #define SIZEOF_LONGBLOCKHEAD 11 #define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_COMMHEAD 13 #define SIZEOF_PROTECTHEAD 26 -#define SIZEOF_AVHEAD 14 -#define SIZEOF_SIGNHEAD 15 #define SIZEOF_UOHEAD 18 -#define SIZEOF_MACHEAD 22 -#define SIZEOF_EAHEAD 24 -#define SIZEOF_BEEAHEAD 24 #define SIZEOF_STREAMHEAD 26 -#define PACK_VER 29 -#define PACK_CRYPT_VER 29 -#define UNP_VER 36 -#define CRYPT_VER 29 -#define AV_VER 20 -#define PROTECT_VER 20 +#define VER_PACK 29U +#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive. +#define VER_UNPACK 29U +#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive. +#define VER_UNKNOWN 9999U // Just some large value. #define MHD_VOLUME 0x0001U + +// Old style main archive comment embed into main archive header. Must not +// be used in new archives anymore. #define MHD_COMMENT 0x0002U + #define MHD_LOCK 0x0004U #define MHD_SOLID 0x0008U #define MHD_PACK_COMMENT 0x0010U @@ -36,14 +34,19 @@ #define MHD_PROTECT 0x0040U #define MHD_PASSWORD 0x0080U #define MHD_FIRSTVOLUME 0x0100U -#define MHD_ENCRYPTVER 0x0200U #define LHD_SPLIT_BEFORE 0x0001U #define LHD_SPLIT_AFTER 0x0002U #define LHD_PASSWORD 0x0004U + +// Old style file comment embed into file header. Must not be used +// in new archives anymore. #define LHD_COMMENT 0x0008U + +// For non-file subheaders it denotes 'subblock having a parent file' flag. #define LHD_SOLID 0x0010U + #define LHD_WINDOWMASK 0x00e0U #define LHD_WINDOW64 0x0000U #define LHD_WINDOW128 0x0020U @@ -59,96 +62,98 @@ #define LHD_SALT 0x0400U #define LHD_VERSION 0x0800U #define LHD_EXTTIME 0x1000U -#define LHD_EXTFLAGS 0x2000U #define SKIP_IF_UNKNOWN 0x4000U #define LONG_BLOCK 0x8000U -#define EARC_NEXT_VOLUME 0x0001U // not last volume -#define EARC_DATACRC 0x0002U // store CRC32 of RAR archive (now used only in volumes) -#define EARC_REVSPACE 0x0004U // reserve space for end of REV file 7 byte record -#define EARC_VOLNUMBER 0x0008U // store a number of current volume +#define EARC_NEXT_VOLUME 0x0001U // Not last volume. +#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes). +#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record. +#define EARC_VOLNUMBER 0x0008U // Store a number of current volume. enum HEADER_TYPE { - MARK_HEAD=0x72,MAIN_HEAD=0x73,FILE_HEAD=0x74,COMM_HEAD=0x75,AV_HEAD=0x76, - SUB_HEAD=0x77,PROTECT_HEAD=0x78,SIGN_HEAD=0x79,NEWSUB_HEAD=0x7a, - ENDARC_HEAD=0x7b + // RAR 5.0 header types. + HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03, + HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff, + + // RAR 1.5 - 4.x header types. + HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75, + HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79, + HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b }; + +// RAR 2.9 and earlier. enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103, NTACL_HEAD=0x104,STREAM_HEAD=0x105 }; + +// Internal implementation, depends on archive format version. enum HOST_SYSTEM { + // RAR 5.0 host OS + HOST5_WINDOWS=0,HOST5_UNIX=1, + + // RAR 3.0 host OS. HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, HOST_BEOS=5,HOST_MAX }; -#define SUBHEAD_TYPE_CMT "CMT" -#define SUBHEAD_TYPE_ACL "ACL" -#define SUBHEAD_TYPE_STREAM "STM" -#define SUBHEAD_TYPE_UOWNER "UOW" -#define SUBHEAD_TYPE_AV "AV" -#define SUBHEAD_TYPE_RR "RR" -#define SUBHEAD_TYPE_OS2EA "EA2" -#define SUBHEAD_TYPE_BEOSEA "EABE" +// Unified archive format independent implementation. +enum HOST_SYSTEM_TYPE { + HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN +}; + + +// We also use these values in extra field, so do not modify them. +enum FILE_SYSTEM_REDIRECT { + FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION, + FSREDIR_HARDLINK, FSREDIR_FILECOPY +}; + + +#define SUBHEAD_TYPE_CMT L"CMT" +#define SUBHEAD_TYPE_QOPEN L"QO" +#define SUBHEAD_TYPE_ACL L"ACL" +#define SUBHEAD_TYPE_STREAM L"STM" +#define SUBHEAD_TYPE_UOWNER L"UOW" +#define SUBHEAD_TYPE_AV L"AV" +#define SUBHEAD_TYPE_RR L"RR" +#define SUBHEAD_TYPE_OS2EA L"EA2" /* new file inherits a subblock when updating a host file */ #define SUBHEAD_FLAGS_INHERITED 0x80000000 #define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001 -struct OldMainHeader -{ - byte Mark[4]; - ushort HeadSize; - byte Flags; -}; - - -struct OldFileHeader -{ - uint PackSize; - uint UnpSize; - ushort FileCRC; - ushort HeadSize; - uint FileTime; - byte FileAttr; - byte Flags; - byte UnpVer; - byte NameSize; - byte Method; -}; - struct MarkHeader { - byte Mark[7]; + byte Mark[8]; + + // Following fields are virtual and not present in real blocks. + uint HeadSize; }; struct BaseBlock { - ushort HeadCRC; - HEADER_TYPE HeadType;//byte - ushort Flags; - ushort HeadSize; + uint HeadCRC; // 'ushort' for RAR 1.5. + HEADER_TYPE HeaderType; // 1 byte for RAR 1.5. + uint Flags; // 'ushort' for RAR 1.5. + uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0. - bool IsSubBlock() + bool SkipIfUnknown; + + void Reset() { - if (HeadType==SUB_HEAD) - return(true); - if (HeadType==NEWSUB_HEAD && (Flags & LHD_SOLID)!=0) - return(true); - return(false); + SkipIfUnknown=false; } }; + struct BlockHeader:BaseBlock { - union { - uint DataSize; - uint PackSize; - }; + uint DataSize; }; @@ -156,75 +161,141 @@ struct MainHeader:BaseBlock { ushort HighPosAV; uint PosAV; - byte EncryptVer; + bool CommentInHeader; + bool PackComment; // For RAR 1.4 archive format only. + bool Locator; + uint64 QOpenOffset; // Offset of quick list record. + uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. + uint64 RROffset; // Offset of recovery record. + uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. + void Reset(); }; -#define SALT_SIZE 8 - struct FileHeader:BlockHeader { - uint UnpSize; byte HostOS; - uint FileCRC; - uint FileTime; - byte UnpVer; + uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5. byte Method; - ushort NameSize; union { uint FileAttr; uint SubFlags; }; -/* optional */ - uint HighPackSize; - uint HighUnpSize; -/* names */ - char FileName[NM]; - wchar FileNameW[NM]; -/* optional */ + wchar FileName[NM]; + Array SubData; - byte Salt[SALT_SIZE]; RarTime mtime; RarTime ctime; RarTime atime; - RarTime arctime; -/* dummy */ - int64 FullPackSize; - int64 FullUnpSize; - void Clear(size_t SubDataSize) + int64 PackSize; + int64 UnpSize; + int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size. + + HashValue FileHash; + + uint FileFlags; + + bool SplitBefore; + bool SplitAfter; + + bool UnknownUnpSize; + + bool Encrypted; + CRYPT_METHOD CryptMethod; + bool SaltSet; + byte Salt[SIZE_SALT50]; + byte InitV[SIZE_INITV]; + bool UsePswCheck; + byte PswCheck[SIZE_PSWCHECK]; + + // Use HMAC calculated from HashKey and checksum instead of plain checksum. + bool UseHashKey; + + // Key to convert checksum to HMAC. Derived from password with PBKDF2 + // using additional iterations. + byte HashKey[SHA256_DIGEST_SIZE]; + + uint Lg2Count; // Log2 of PBKDF2 repetition count. + + bool Solid; + bool Dir; + bool CommentInHeader; // RAR 2.0 file comment. + bool Version; // name.ext;ver file name containing the version number. + size_t WinSize; + bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only). + + // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0. + bool LargeFile; + + // 'true' for HEAD_SERVICE block, which is a child of preceding file block. + // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives. + bool SubBlock; + + HOST_SYSTEM_TYPE HSType; + + FILE_SYSTEM_REDIRECT RedirType; + wchar RedirName[NM]; + bool DirTarget; + + bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric; + char UnixOwnerName[256],UnixGroupName[256]; +#ifdef _UNIX + uid_t UnixOwnerID; + gid_t UnixGroupID; +#else // Need these Unix fields in Windows too for 'list' command. + uint UnixOwnerID; + uint UnixGroupID; +#endif + + void Reset(size_t SubDataSize=0); + + bool CmpName(const wchar *Name) { - SubData.Alloc(SubDataSize); - Flags=LONG_BLOCK; - SubFlags=0; + return(wcscmp(FileName,Name)==0); } - bool CmpName(const char *Name) - { - return(strcmp(FileName,Name)==0); - } - - FileHeader& operator = (FileHeader &hd) - { - SubData.Reset(); - memcpy(this,&hd,sizeof(*this)); - SubData.CleanData(); - SubData=hd.SubData; - return(*this); - } + FileHeader& operator = (FileHeader &hd); }; struct EndArcHeader:BaseBlock { - uint ArcDataCRC; // optional archive CRC32 - ushort VolNumber; // optional current volume number + // Optional CRC32 of entire archive up to start of EndArcHeader block. + // Present in RAR 4.x archives if EARC_DATACRC flag is set. + uint ArcDataCRC; + + uint VolNumber; // Optional number of current volume. + + // 7 additional zero bytes can be stored here if EARC_REVSPACE is set. + + bool NextVolume; // Not last volume. + bool DataCRC; + bool RevSpace; + bool StoreVolNumber; + void Reset() + { + BaseBlock::Reset(); + NextVolume=false; + DataCRC=false; + RevSpace=false; + StoreVolNumber=false; + } +}; + + +struct CryptHeader:BaseBlock +{ + bool UsePswCheck; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte Salt[SIZE_SALT50]; + byte PswCheck[SIZE_PSWCHECK]; }; // SubBlockHeader and its successors were used in RAR 2.x format. -// RAR 3.x uses FileHeader with NEWSUB_HEAD HeadType for subblocks. +// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks. struct SubBlockHeader:BlockHeader { ushort SubType; @@ -250,30 +321,13 @@ struct ProtectHeader:BlockHeader }; -struct AVHeader:BaseBlock -{ - byte UnpVer; - byte Method; - byte AVVer; - uint AVInfoCRC; -}; - - -struct SignHeader:BaseBlock -{ - uint CreationTime; - ushort ArcNameSize; - ushort UserNameSize; -}; - - struct UnixOwnersHeader:SubBlockHeader { ushort OwnerNameSize; ushort GroupNameSize; /* dummy */ - char OwnerName[NM]; - char GroupName[NM]; + char OwnerName[256]; + char GroupName[256]; }; @@ -293,15 +347,7 @@ struct StreamHeader:SubBlockHeader byte Method; uint StreamCRC; ushort StreamNameSize; -/* dummy */ - byte StreamName[NM]; -}; - - -struct MacFInfoHeader:SubBlockHeader -{ - uint fileType; - uint fileCreator; + char StreamName[260]; }; diff --git a/libunrar/headers5.hpp b/libunrar/headers5.hpp new file mode 100644 index 0000000..9ea8d97 --- /dev/null +++ b/libunrar/headers5.hpp @@ -0,0 +1,100 @@ +#ifndef _RAR_HEADERS5_ +#define _RAR_HEADERS5_ + +#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length. +#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size. + +// RAR 5.0 block flags common for all blocks. + +// Additional extra area is present in the end of block header. +#define HFL_EXTRA 0x0001 +// Additional data area is present in the end of block header. +#define HFL_DATA 0x0002 +// Unknown blocks with this flag must be skipped when updating an archive. +#define HFL_SKIPIFUNKNOWN 0x0004 +// Data area of this block is continuing from previous volume. +#define HFL_SPLITBEFORE 0x0008 +// Data area of this block is continuing in next volume. +#define HFL_SPLITAFTER 0x0010 +// Block depends on preceding file block. +#define HFL_CHILD 0x0020 +// Preserve a child block if host is modified. +#define HFL_INHERITED 0x0040 + +// RAR 5.0 main archive header specific flags. +#define MHFL_VOLUME 0x0001 // Volume. +#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first. +#define MHFL_SOLID 0x0004 // Solid archive. +#define MHFL_PROTECT 0x0008 // Recovery record is present. +#define MHFL_LOCK 0x0010 // Locked archive. + +// RAR 5.0 file header specific flags. +#define FHFL_DIRECTORY 0x0001 // Directory. +#define FHFL_UTIME 0x0002 // Time field in Unix format is present. +#define FHFL_CRC32 0x0004 // CRC32 field is present. +#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size. + +// RAR 5.0 end of archive header specific flags. +#define EHFL_NEXTVOLUME 0x0001 // Not last volume. + +// RAR 5.0 archive encryption header specific flags. +#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present. + + +// RAR 5.0 file compression flags. +#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm. +#define FCI_ALGO_BIT1 0x0002 // 0 .. 63. +#define FCI_ALGO_BIT2 0x0004 +#define FCI_ALGO_BIT3 0x0008 +#define FCI_ALGO_BIT4 0x0010 +#define FCI_ALGO_BIT5 0x0020 +#define FCI_SOLID 0x0040 // Solid flag. +#define FCI_METHOD_BIT0 0x0080 // Compression method. +#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used). +#define FCI_METHOD_BIT2 0x0200 +#define FCI_DICT_BIT0 0x0400 // Dictionary size. +#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB. +#define FCI_DICT_BIT2 0x1000 +#define FCI_DICT_BIT3 0x2000 + +// Main header extra field values. +#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. + +// Flags for MHEXTRA_LOCATOR. +#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. +#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present. + +// File and service header extra field values. +#define FHEXTRA_CRYPT 0x01 // Encryption parameters. +#define FHEXTRA_HASH 0x02 // File hash. +#define FHEXTRA_HTIME 0x03 // High precision file time. +#define FHEXTRA_VERSION 0x04 // File version information. +#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.). +#define FHEXTRA_UOWNER 0x06 // Unix owner and group information. +#define FHEXTRA_SUBDATA 0x07 // Service header subdata array. + + +// Hash type values for FHEXTRA_HASH. +#define FHEXTRA_HASH_BLAKE2 0x00 + +// Flags for FHEXTRA_HTIME. +#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format. +#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present. +#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present. +#define FHEXTRA_HTIME_ATIME 0x08 // atime is present. +#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision. + +// Flags for FHEXTRA_CRYPT. +#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data. +#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums. + +// Flags for FHEXTRA_REDIR. +#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory. + +// Flags for FHEXTRA_UOWNER. +#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present. +#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present. +#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present. +#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present. + +#endif diff --git a/libunrar/isnt.cpp b/libunrar/isnt.cpp index 493a9eb..6fadec0 100644 --- a/libunrar/isnt.cpp +++ b/libunrar/isnt.cpp @@ -1,9 +1,10 @@ #include "rar.hpp" -#ifdef _WIN_32 -int WinNT() +#ifdef _WIN_ALL +DWORD WinNT() { - static int dwPlatformId=-1,dwMajorVersion; + static int dwPlatformId=-1; + static DWORD dwMajorVersion,dwMinorVersion; if (dwPlatformId==-1) { OSVERSIONINFO WinVer; @@ -11,7 +12,13 @@ int WinNT() GetVersionEx(&WinVer); dwPlatformId=WinVer.dwPlatformId; dwMajorVersion=WinVer.dwMajorVersion; + dwMinorVersion=WinVer.dwMinorVersion; } - return(dwPlatformId==VER_PLATFORM_WIN32_NT ? dwMajorVersion:0); + DWORD Result=0; + if (dwPlatformId==VER_PLATFORM_WIN32_NT) + Result=dwMajorVersion*0x100+dwMinorVersion; + + + return Result; } #endif diff --git a/libunrar/isnt.hpp b/libunrar/isnt.hpp index 0265236..85790da 100644 --- a/libunrar/isnt.hpp +++ b/libunrar/isnt.hpp @@ -1,6 +1,13 @@ #ifndef _RAR_ISNT_ #define _RAR_ISNT_ -int WinNT(); +enum WINNT_VERSION { + WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500, + WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601, + WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00 +}; + +DWORD WinNT(); + #endif diff --git a/libunrar/libunrar.xcodeproj/project.pbxproj b/libunrar/libunrar.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ada8774 --- /dev/null +++ b/libunrar/libunrar.xcodeproj/project.pbxproj @@ -0,0 +1,551 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXFileReference section */ + E296814A24BE5E1200974229 /* UnRARDll.vcxproj */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = UnRARDll.vcxproj; sourceTree = ""; }; + E296814B24BE5E1200974229 /* scantree.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = scantree.cpp; sourceTree = ""; }; + E296814C24BE5E1200974229 /* rarlang.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rarlang.hpp; sourceTree = ""; }; + E296814D24BE5E1200974229 /* threadpool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = threadpool.hpp; sourceTree = ""; }; + E296814E24BE5E1200974229 /* unpack30.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack30.cpp; sourceTree = ""; }; + E296814F24BE5E1200974229 /* crypt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt.cpp; sourceTree = ""; }; + E296815024BE5E1200974229 /* qopen.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = qopen.cpp; sourceTree = ""; }; + E296815124BE5E1200974229 /* filcreat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = filcreat.cpp; sourceTree = ""; }; + E296815224BE5E1200974229 /* crypt1.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt1.cpp; sourceTree = ""; }; + E296815324BE5E1200974229 /* filestr.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = filestr.hpp; sourceTree = ""; }; + E296815424BE5E1200974229 /* suballoc.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = suballoc.hpp; sourceTree = ""; }; + E296815524BE5E1200974229 /* ui.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ui.cpp; sourceTree = ""; }; + E296815624BE5E1200974229 /* blake2sp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = blake2sp.cpp; sourceTree = ""; }; + E296815724BE5E1200974229 /* filefn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = filefn.o; sourceTree = ""; }; + E296815824BE5E1200974229 /* archive.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = archive.o; sourceTree = ""; }; + E296815924BE5E1200974229 /* crypt3.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt3.cpp; sourceTree = ""; }; + E296815A24BE5E1200974229 /* crc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crc.cpp; sourceTree = ""; }; + E296815B24BE5E1200974229 /* strlist.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = strlist.hpp; sourceTree = ""; }; + E296815C24BE5E1200974229 /* hash.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = hash.hpp; sourceTree = ""; }; + E296815D24BE5E1200974229 /* headers.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = headers.hpp; sourceTree = ""; }; + E296815E24BE5E1200974229 /* uicommon.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uicommon.cpp; sourceTree = ""; }; + E296815F24BE5E1200974229 /* global.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = global.hpp; sourceTree = ""; }; + E296816024BE5E1200974229 /* win32lnk.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = win32lnk.cpp; sourceTree = ""; }; + E296816124BE5E1200974229 /* log.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = log.cpp; sourceTree = ""; }; + E296816224BE5E1200974229 /* crypt2.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt2.cpp; sourceTree = ""; }; + E296816324BE5E1200974229 /* errhnd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = errhnd.cpp; sourceTree = ""; }; + E296816424BE5E1200974229 /* raros.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = raros.hpp; sourceTree = ""; }; + E296816524BE5E1200974229 /* acknow.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = acknow.txt; sourceTree = ""; }; + E296816624BE5E1200974229 /* strfn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = strfn.cpp; sourceTree = ""; }; + E296816724BE5E1200974229 /* .DS_Store */ = {isa = PBXFileReference; lastKnownFileType = file; path = .DS_Store; sourceTree = ""; }; + E296816824BE5E1200974229 /* dll.rc */ = {isa = PBXFileReference; lastKnownFileType = text; path = dll.rc; sourceTree = ""; }; + E296816924BE5E1200974229 /* cmddata.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = cmddata.hpp; sourceTree = ""; }; + E296816A24BE5E1200974229 /* unpack50frag.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack50frag.cpp; sourceTree = ""; }; + E296816B24BE5E1200974229 /* ui.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = ui.o; sourceTree = ""; }; + E296816C24BE5E1200974229 /* rdwrfn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rdwrfn.cpp; sourceTree = ""; }; + E296816D24BE5E1200974229 /* unicode.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = unicode.hpp; sourceTree = ""; }; + E296816E24BE5E1200974229 /* crypt5.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt5.cpp; sourceTree = ""; }; + E296816F24BE5E1200974229 /* uowners.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uowners.cpp; sourceTree = ""; }; + E296817024BE5E1200974229 /* dll.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = dll.hpp; sourceTree = ""; }; + E296817124BE5E1200974229 /* extract.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = extract.hpp; sourceTree = ""; }; + E296817224BE5E1200974229 /* cmdfilter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = cmdfilter.cpp; sourceTree = ""; }; + E296817324BE5E1200974229 /* threadpool.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = threadpool.o; sourceTree = ""; }; + E296817424BE5E1200974229 /* unpack20.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack20.cpp; sourceTree = ""; }; + E296817524BE5E1200974229 /* compress.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = compress.hpp; sourceTree = ""; }; + E296817624BE5E1200974229 /* libunrar.so */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libunrar.so; sourceTree = ""; }; + E296817724BE5E1200974229 /* archive.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = archive.hpp; sourceTree = ""; }; + E296817824BE5E1200974229 /* pathfn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = pathfn.o; sourceTree = ""; }; + E296817924BE5E1200974229 /* blake2s_sse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = blake2s_sse.cpp; sourceTree = ""; }; + E296817A24BE5E1200974229 /* loclang.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = loclang.hpp; sourceTree = ""; }; + E296817B24BE5E1200974229 /* resource.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = resource.cpp; sourceTree = ""; }; + E296817C24BE5E1200974229 /* libunrar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libunrar.a; sourceTree = ""; }; + E296817D24BE5E1200974229 /* rarvm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rarvm.cpp; sourceTree = ""; }; + E296817E24BE5E1200974229 /* coder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = coder.hpp; sourceTree = ""; }; + E296817F24BE5E1200974229 /* volume.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = volume.hpp; sourceTree = ""; }; + E296818024BE5E1200974229 /* recvol.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = recvol.hpp; sourceTree = ""; }; + E296818124BE5E1200974229 /* getbits.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = getbits.o; sourceTree = ""; }; + E296818224BE5E1200974229 /* filefn.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = filefn.hpp; sourceTree = ""; }; + E296818324BE5E1200974229 /* threadmisc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = threadmisc.cpp; sourceTree = ""; }; + E296818424BE5E1200974229 /* global.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = global.o; sourceTree = ""; }; + E296818524BE5E1200974229 /* find.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = find.o; sourceTree = ""; }; + E296818624BE5E1200974229 /* isnt.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = isnt.hpp; sourceTree = ""; }; + E296818724BE5E1200974229 /* makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = makefile; sourceTree = ""; }; + E296818824BE5E1200974229 /* blake2s.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = blake2s.o; sourceTree = ""; }; + E296818924BE5E1200974229 /* array.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = array.hpp; sourceTree = ""; }; + E296818A24BE5E1200974229 /* dll_nocrypt.def */ = {isa = PBXFileReference; lastKnownFileType = text; path = dll_nocrypt.def; sourceTree = ""; }; + E296818B24BE5E1200974229 /* crc.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = crc.o; sourceTree = ""; }; + E296818C24BE5E1200974229 /* consio.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = consio.hpp; sourceTree = ""; }; + E296818D24BE5E1200974229 /* file.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = file.o; sourceTree = ""; }; + E296818E24BE5E1200974229 /* encname.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = encname.hpp; sourceTree = ""; }; + E296818F24BE5E1200974229 /* find.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = find.cpp; sourceTree = ""; }; + E296819024BE5E1200974229 /* timefn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timefn.cpp; sourceTree = ""; }; + E296819124BE5E1200974229 /* file.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = file.hpp; sourceTree = ""; }; + E296819224BE5E1200974229 /* unpack.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = unpack.hpp; sourceTree = ""; }; + E296819324BE5E1200974229 /* qopen.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = qopen.o; sourceTree = ""; }; + E296819424BE5E1200974229 /* unpack50.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack50.cpp; sourceTree = ""; }; + E296819524BE5E1200974229 /* rs.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rs.hpp; sourceTree = ""; }; + E296819624BE5E1200974229 /* pathfn.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = pathfn.hpp; sourceTree = ""; }; + E296819724BE5E1200974229 /* sha256.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = sha256.o; sourceTree = ""; }; + E296819824BE5E1200974229 /* arccmt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = arccmt.cpp; sourceTree = ""; }; + E296819924BE5E1200974229 /* sha256.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = sha256.hpp; sourceTree = ""; }; + E296819A24BE5E1200974229 /* sha1.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sha1.cpp; sourceTree = ""; }; + E296819B24BE5E1200974229 /* rdwrfn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = rdwrfn.o; sourceTree = ""; }; + E296819C24BE5E1200974229 /* system.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = system.hpp; sourceTree = ""; }; + E296819D24BE5E1200974229 /* rawint.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rawint.hpp; sourceTree = ""; }; + E296819E24BE5E1200974229 /* rawread.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rawread.hpp; sourceTree = ""; }; + E296819F24BE5E1200974229 /* list.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = list.hpp; sourceTree = ""; }; + E29681A024BE5E1200974229 /* match.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = match.cpp; sourceTree = ""; }; + E29681A124BE5E1200974229 /* filestr.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = filestr.o; sourceTree = ""; }; + E29681A224BE5E1200974229 /* strfn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = strfn.o; sourceTree = ""; }; + E29681A324BE5E1200974229 /* rs16.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rs16.hpp; sourceTree = ""; }; + E29681A424BE5E1200974229 /* options.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = options.cpp; sourceTree = ""; }; + E29681A524BE5E1200974229 /* rijndael.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rijndael.hpp; sourceTree = ""; }; + E29681A624BE5E1200974229 /* dll.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = dll.o; sourceTree = ""; }; + E29681A724BE5E1200974229 /* rawread.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = rawread.o; sourceTree = ""; }; + E29681A824BE5E1200974229 /* rar.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = rar.o; sourceTree = ""; }; + E29681A924BE5E1200974229 /* rar.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rar.hpp; sourceTree = ""; }; + E29681AA24BE5E1200974229 /* extinfo.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = extinfo.hpp; sourceTree = ""; }; + E29681AB24BE5E1200974229 /* model.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = model.hpp; sourceTree = ""; }; + E29681AC24BE5E1200974229 /* options.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = options.o; sourceTree = ""; }; + E29681AD24BE5E1200974229 /* blake2s.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = blake2s.cpp; sourceTree = ""; }; + E29681AE24BE5E1200974229 /* headers.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = headers.o; sourceTree = ""; }; + E29681AF24BE5E1200974229 /* dll.def */ = {isa = PBXFileReference; lastKnownFileType = text; path = dll.def; sourceTree = ""; }; + E29681B024BE5E1200974229 /* smallfn.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = smallfn.hpp; sourceTree = ""; }; + E29681B124BE5E1200974229 /* getbits.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = getbits.hpp; sourceTree = ""; }; + E29681B224BE5E1200974229 /* timefn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = timefn.o; sourceTree = ""; }; + E29681B324BE5E1200974229 /* secpassword.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = secpassword.hpp; sourceTree = ""; }; + E29681B424BE5E1200974229 /* extract.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = extract.o; sourceTree = ""; }; + E29681B524BE5E1200974229 /* filefn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = filefn.cpp; sourceTree = ""; }; + E29681B624BE5E1200974229 /* recvol.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = recvol.cpp; sourceTree = ""; }; + E29681B724BE5E1200974229 /* isnt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = isnt.cpp; sourceTree = ""; }; + E29681B824BE5E1200974229 /* version.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = version.hpp; sourceTree = ""; }; + E29681B924BE5E1200974229 /* savepos.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = savepos.hpp; sourceTree = ""; }; + E29681BA24BE5E1200974229 /* unicode.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = unicode.o; sourceTree = ""; }; + E29681BB24BE5E1200974229 /* consio.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = consio.cpp; sourceTree = ""; }; + E29681BC24BE5E1200974229 /* list.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = list.o; sourceTree = ""; }; + E29681BD24BE5E1200974229 /* UnRAR.vcxproj */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = UnRAR.vcxproj; sourceTree = ""; }; + E29681BE24BE5E1200974229 /* rartypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rartypes.hpp; sourceTree = ""; }; + E29681BF24BE5E1200974229 /* crypt.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = crypt.o; sourceTree = ""; }; + E29681C024BE5E1200974229 /* encname.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = encname.cpp; sourceTree = ""; }; + E29681C124BE5E1200974229 /* secpassword.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = secpassword.o; sourceTree = ""; }; + E29681C224BE5E1200974229 /* uiconsole.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uiconsole.cpp; sourceTree = ""; }; + E29681C324BE5E1200974229 /* file.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = file.cpp; sourceTree = ""; }; + E29681C424BE5E1200974229 /* timefn.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = timefn.hpp; sourceTree = ""; }; + E29681C524BE5E1200974229 /* find.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = find.hpp; sourceTree = ""; }; + E29681C624BE5E1200974229 /* unpack.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack.cpp; sourceTree = ""; }; + E29681C724BE5E1200974229 /* match.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = match.o; sourceTree = ""; }; + E29681C824BE5E1200974229 /* filcreat.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = filcreat.o; sourceTree = ""; }; + E29681C924BE5E1200974229 /* rs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rs.cpp; sourceTree = ""; }; + E29681CA24BE5E1200974229 /* pathfn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = pathfn.cpp; sourceTree = ""; }; + E29681CB24BE5E1200974229 /* arcread.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = arcread.o; sourceTree = ""; }; + E29681CC24BE5E1200974229 /* consio.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = consio.o; sourceTree = ""; }; + E29681CD24BE5E1200974229 /* sha1.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = sha1.hpp; sourceTree = ""; }; + E29681CE24BE5E1200974229 /* sha256.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sha256.cpp; sourceTree = ""; }; + E29681CF24BE5E1200974229 /* errhnd.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = errhnd.o; sourceTree = ""; }; + E29681D024BE5E1200974229 /* rs16.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = rs16.o; sourceTree = ""; }; + E29681D124BE5E1200974229 /* unpackinline.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpackinline.cpp; sourceTree = ""; }; + E29681D224BE5E1200974229 /* list.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = list.cpp; sourceTree = ""; }; + E29681D324BE5E1200974229 /* rawread.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rawread.cpp; sourceTree = ""; }; + E29681D424BE5E1200974229 /* match.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = match.hpp; sourceTree = ""; }; + E29681D524BE5E1200974229 /* system.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = system.cpp; sourceTree = ""; }; + E29681D624BE5E1200974229 /* hardlinks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = hardlinks.cpp; sourceTree = ""; }; + E29681D724BE5E1200974229 /* options.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = options.hpp; sourceTree = ""; }; + E29681D824BE5E1200974229 /* rs16.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rs16.cpp; sourceTree = ""; }; + E29681D924BE5E1200974229 /* rijndael.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rijndael.cpp; sourceTree = ""; }; + E29681DA24BE5E1200974229 /* smallfn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = smallfn.o; sourceTree = ""; }; + E29681DB24BE5E1200974229 /* ulinks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ulinks.cpp; sourceTree = ""; }; + E29681DC24BE5E1200974229 /* headers5.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = headers5.hpp; sourceTree = ""; }; + E29681DD24BE5E1200974229 /* resource.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = resource.o; sourceTree = ""; }; + E29681DE24BE5E1200974229 /* blake2s.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = blake2s.hpp; sourceTree = ""; }; + E29681DF24BE5E1200974229 /* extinfo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = extinfo.cpp; sourceTree = ""; }; + E29681E024BE5E1200974229 /* model.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = model.cpp; sourceTree = ""; }; + E29681E124BE5E1200974229 /* rar.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rar.cpp; sourceTree = ""; }; + E29681E224BE5E1200974229 /* rijndael.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = rijndael.o; sourceTree = ""; }; + E29681E324BE5E1200974229 /* win32acl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = win32acl.cpp; sourceTree = ""; }; + E29681E424BE5E1200974229 /* getbits.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = getbits.cpp; sourceTree = ""; }; + E29681E524BE5E1200974229 /* rarpch.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rarpch.cpp; sourceTree = ""; }; + E29681E624BE5E1200974229 /* secpassword.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = secpassword.cpp; sourceTree = ""; }; + E29681E724BE5E1200974229 /* readme.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = readme.txt; sourceTree = ""; }; + E29681E824BE5E1200974229 /* smallfn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = smallfn.cpp; sourceTree = ""; }; + E29681E924BE5E1200974229 /* scantree.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = scantree.hpp; sourceTree = ""; }; + E29681EA24BE5E1200974229 /* sha1.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = sha1.o; sourceTree = ""; }; + E29681EB24BE5E1200974229 /* threadpool.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = threadpool.cpp; sourceTree = ""; }; + E29681EC24BE5E1200974229 /* volume.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = volume.o; sourceTree = ""; }; + E29681ED24BE5E1200974229 /* encname.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = encname.o; sourceTree = ""; }; + E29681EE24BE5E1200974229 /* system.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = system.o; sourceTree = ""; }; + E29681EF24BE5E1200974229 /* filcreat.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = filcreat.hpp; sourceTree = ""; }; + E29681F024BE5E1200974229 /* rardefs.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rardefs.hpp; sourceTree = ""; }; + E29681F124BE5E1200974229 /* ui.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ui.hpp; sourceTree = ""; }; + E29681F224BE5E1200974229 /* suballoc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = suballoc.cpp; sourceTree = ""; }; + E29681F324BE5E1200974229 /* filestr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = filestr.cpp; sourceTree = ""; }; + E29681F424BE5E1200974229 /* qopen.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = qopen.hpp; sourceTree = ""; }; + E29681F524BE5E1200974229 /* crypt.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = crypt.hpp; sourceTree = ""; }; + E29681F624BE5E1200974229 /* crc.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = crc.hpp; sourceTree = ""; }; + E29681F724BE5E1200974229 /* unpack.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = unpack.o; sourceTree = ""; }; + E29681F824BE5E1200974229 /* hash.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = hash.cpp; sourceTree = ""; }; + E29681F924BE5E1200974229 /* unpack50mt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack50mt.cpp; sourceTree = ""; }; + E29681FA24BE5E1200974229 /* strlist.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = strlist.cpp; sourceTree = ""; }; + E29681FB24BE5E1200974229 /* license.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = license.txt; sourceTree = ""; }; + E29681FC24BE5E1200974229 /* global.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = global.cpp; sourceTree = ""; }; + E29681FD24BE5E1200974229 /* recvol3.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = recvol3.cpp; sourceTree = ""; }; + E29681FE24BE5E1200974229 /* headers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = headers.cpp; sourceTree = ""; }; + E29681FF24BE5E1200974229 /* strlist.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = strlist.o; sourceTree = ""; }; + E296820024BE5E1200974229 /* uisilent.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uisilent.cpp; sourceTree = ""; }; + E296820124BE5E1200974229 /* log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = log.hpp; sourceTree = ""; }; + E296820224BE5E1200974229 /* cmddata.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = cmddata.o; sourceTree = ""; }; + E296820324BE5E1200974229 /* cmdmix.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = cmdmix.cpp; sourceTree = ""; }; + E296820424BE5E1200974229 /* errhnd.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = errhnd.hpp; sourceTree = ""; }; + E296820524BE5E1200974229 /* os.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = os.hpp; sourceTree = ""; }; + E296820624BE5E1200974229 /* rarvm.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = rarvm.o; sourceTree = ""; }; + E296820724BE5E1200974229 /* win32stm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = win32stm.cpp; sourceTree = ""; }; + E296820824BE5E1200974229 /* scantree.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = scantree.o; sourceTree = ""; }; + E296820924BE5E1200974229 /* strfn.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = strfn.hpp; sourceTree = ""; }; + E296820A24BE5E1200974229 /* cmddata.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = cmddata.cpp; sourceTree = ""; }; + E296820B24BE5E1200974229 /* rdwrfn.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rdwrfn.hpp; sourceTree = ""; }; + E296820C24BE5E1200974229 /* hash.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = hash.o; sourceTree = ""; }; + E296820D24BE5E1200974229 /* extract.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = extract.cpp; sourceTree = ""; }; + E296820E24BE5E1200974229 /* dll.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dll.cpp; sourceTree = ""; }; + E296820F24BE5E1200974229 /* unicode.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unicode.cpp; sourceTree = ""; }; + E296821024BE5E1200974229 /* unpack15.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack15.cpp; sourceTree = ""; }; + E296821124BE5E1200974229 /* archive.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = archive.cpp; sourceTree = ""; }; + E296821224BE5E1200974229 /* extinfo.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = extinfo.o; sourceTree = ""; }; + E296821324BE5E1200974229 /* recvol5.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = recvol5.cpp; sourceTree = ""; }; + E296821424BE5E1200974229 /* rarvm.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rarvm.hpp; sourceTree = ""; }; + E296821524BE5E1200974229 /* resource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = resource.hpp; sourceTree = ""; }; + E296821624BE5E1200974229 /* volume.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = volume.cpp; sourceTree = ""; }; + E296821724BE5E1200974229 /* isnt.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = isnt.o; sourceTree = ""; }; + E296821824BE5E1200974229 /* coder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = coder.cpp; sourceTree = ""; }; + E296821924BE5E1200974229 /* arcread.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = arcread.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + E296814424BE5E1200974229 = { + isa = PBXGroup; + children = ( + E296814A24BE5E1200974229 /* UnRARDll.vcxproj */, + E296814B24BE5E1200974229 /* scantree.cpp */, + E296814C24BE5E1200974229 /* rarlang.hpp */, + E296814D24BE5E1200974229 /* threadpool.hpp */, + E296814E24BE5E1200974229 /* unpack30.cpp */, + E296814F24BE5E1200974229 /* crypt.cpp */, + E296815024BE5E1200974229 /* qopen.cpp */, + E296815124BE5E1200974229 /* filcreat.cpp */, + E296815224BE5E1200974229 /* crypt1.cpp */, + E296815324BE5E1200974229 /* filestr.hpp */, + E296815424BE5E1200974229 /* suballoc.hpp */, + E296815524BE5E1200974229 /* ui.cpp */, + E296815624BE5E1200974229 /* blake2sp.cpp */, + E296815724BE5E1200974229 /* filefn.o */, + E296815824BE5E1200974229 /* archive.o */, + E296815924BE5E1200974229 /* crypt3.cpp */, + E296815A24BE5E1200974229 /* crc.cpp */, + E296815B24BE5E1200974229 /* strlist.hpp */, + E296815C24BE5E1200974229 /* hash.hpp */, + E296815D24BE5E1200974229 /* headers.hpp */, + E296815E24BE5E1200974229 /* uicommon.cpp */, + E296815F24BE5E1200974229 /* global.hpp */, + E296816024BE5E1200974229 /* win32lnk.cpp */, + E296816124BE5E1200974229 /* log.cpp */, + E296816224BE5E1200974229 /* crypt2.cpp */, + E296816324BE5E1200974229 /* errhnd.cpp */, + E296816424BE5E1200974229 /* raros.hpp */, + E296816524BE5E1200974229 /* acknow.txt */, + E296816624BE5E1200974229 /* strfn.cpp */, + E296816724BE5E1200974229 /* .DS_Store */, + E296816824BE5E1200974229 /* dll.rc */, + E296816924BE5E1200974229 /* cmddata.hpp */, + E296816A24BE5E1200974229 /* unpack50frag.cpp */, + E296816B24BE5E1200974229 /* ui.o */, + E296816C24BE5E1200974229 /* rdwrfn.cpp */, + E296816D24BE5E1200974229 /* unicode.hpp */, + E296816E24BE5E1200974229 /* crypt5.cpp */, + E296816F24BE5E1200974229 /* uowners.cpp */, + E296817024BE5E1200974229 /* dll.hpp */, + E296817124BE5E1200974229 /* extract.hpp */, + E296817224BE5E1200974229 /* cmdfilter.cpp */, + E296817324BE5E1200974229 /* threadpool.o */, + E296817424BE5E1200974229 /* unpack20.cpp */, + E296817524BE5E1200974229 /* compress.hpp */, + E296817624BE5E1200974229 /* libunrar.so */, + E296817724BE5E1200974229 /* archive.hpp */, + E296817824BE5E1200974229 /* pathfn.o */, + E296817924BE5E1200974229 /* blake2s_sse.cpp */, + E296817A24BE5E1200974229 /* loclang.hpp */, + E296817B24BE5E1200974229 /* resource.cpp */, + E296817C24BE5E1200974229 /* libunrar.a */, + E296817D24BE5E1200974229 /* rarvm.cpp */, + E296817E24BE5E1200974229 /* coder.hpp */, + E296817F24BE5E1200974229 /* volume.hpp */, + E296818024BE5E1200974229 /* recvol.hpp */, + E296818124BE5E1200974229 /* getbits.o */, + E296818224BE5E1200974229 /* filefn.hpp */, + E296818324BE5E1200974229 /* threadmisc.cpp */, + E296818424BE5E1200974229 /* global.o */, + E296818524BE5E1200974229 /* find.o */, + E296818624BE5E1200974229 /* isnt.hpp */, + E296818724BE5E1200974229 /* makefile */, + E296818824BE5E1200974229 /* blake2s.o */, + E296818924BE5E1200974229 /* array.hpp */, + E296818A24BE5E1200974229 /* dll_nocrypt.def */, + E296818B24BE5E1200974229 /* crc.o */, + E296818C24BE5E1200974229 /* consio.hpp */, + E296818D24BE5E1200974229 /* file.o */, + E296818E24BE5E1200974229 /* encname.hpp */, + E296818F24BE5E1200974229 /* find.cpp */, + E296819024BE5E1200974229 /* timefn.cpp */, + E296819124BE5E1200974229 /* file.hpp */, + E296819224BE5E1200974229 /* unpack.hpp */, + E296819324BE5E1200974229 /* qopen.o */, + E296819424BE5E1200974229 /* unpack50.cpp */, + E296819524BE5E1200974229 /* rs.hpp */, + E296819624BE5E1200974229 /* pathfn.hpp */, + E296819724BE5E1200974229 /* sha256.o */, + E296819824BE5E1200974229 /* arccmt.cpp */, + E296819924BE5E1200974229 /* sha256.hpp */, + E296819A24BE5E1200974229 /* sha1.cpp */, + E296819B24BE5E1200974229 /* rdwrfn.o */, + E296819C24BE5E1200974229 /* system.hpp */, + E296819D24BE5E1200974229 /* rawint.hpp */, + E296819E24BE5E1200974229 /* rawread.hpp */, + E296819F24BE5E1200974229 /* list.hpp */, + E29681A024BE5E1200974229 /* match.cpp */, + E29681A124BE5E1200974229 /* filestr.o */, + E29681A224BE5E1200974229 /* strfn.o */, + E29681A324BE5E1200974229 /* rs16.hpp */, + E29681A424BE5E1200974229 /* options.cpp */, + E29681A524BE5E1200974229 /* rijndael.hpp */, + E29681A624BE5E1200974229 /* dll.o */, + E29681A724BE5E1200974229 /* rawread.o */, + E29681A824BE5E1200974229 /* rar.o */, + E29681A924BE5E1200974229 /* rar.hpp */, + E29681AA24BE5E1200974229 /* extinfo.hpp */, + E29681AB24BE5E1200974229 /* model.hpp */, + E29681AC24BE5E1200974229 /* options.o */, + E29681AD24BE5E1200974229 /* blake2s.cpp */, + E29681AE24BE5E1200974229 /* headers.o */, + E29681AF24BE5E1200974229 /* dll.def */, + E29681B024BE5E1200974229 /* smallfn.hpp */, + E29681B124BE5E1200974229 /* getbits.hpp */, + E29681B224BE5E1200974229 /* timefn.o */, + E29681B324BE5E1200974229 /* secpassword.hpp */, + E29681B424BE5E1200974229 /* extract.o */, + E29681B524BE5E1200974229 /* filefn.cpp */, + E29681B624BE5E1200974229 /* recvol.cpp */, + E29681B724BE5E1200974229 /* isnt.cpp */, + E29681B824BE5E1200974229 /* version.hpp */, + E29681B924BE5E1200974229 /* savepos.hpp */, + E29681BA24BE5E1200974229 /* unicode.o */, + E29681BB24BE5E1200974229 /* consio.cpp */, + E29681BC24BE5E1200974229 /* list.o */, + E29681BD24BE5E1200974229 /* UnRAR.vcxproj */, + E29681BE24BE5E1200974229 /* rartypes.hpp */, + E29681BF24BE5E1200974229 /* crypt.o */, + E29681C024BE5E1200974229 /* encname.cpp */, + E29681C124BE5E1200974229 /* secpassword.o */, + E29681C224BE5E1200974229 /* uiconsole.cpp */, + E29681C324BE5E1200974229 /* file.cpp */, + E29681C424BE5E1200974229 /* timefn.hpp */, + E29681C524BE5E1200974229 /* find.hpp */, + E29681C624BE5E1200974229 /* unpack.cpp */, + E29681C724BE5E1200974229 /* match.o */, + E29681C824BE5E1200974229 /* filcreat.o */, + E29681C924BE5E1200974229 /* rs.cpp */, + E29681CA24BE5E1200974229 /* pathfn.cpp */, + E29681CB24BE5E1200974229 /* arcread.o */, + E29681CC24BE5E1200974229 /* consio.o */, + E29681CD24BE5E1200974229 /* sha1.hpp */, + E29681CE24BE5E1200974229 /* sha256.cpp */, + E29681CF24BE5E1200974229 /* errhnd.o */, + E29681D024BE5E1200974229 /* rs16.o */, + E29681D124BE5E1200974229 /* unpackinline.cpp */, + E29681D224BE5E1200974229 /* list.cpp */, + E29681D324BE5E1200974229 /* rawread.cpp */, + E29681D424BE5E1200974229 /* match.hpp */, + E29681D524BE5E1200974229 /* system.cpp */, + E29681D624BE5E1200974229 /* hardlinks.cpp */, + E29681D724BE5E1200974229 /* options.hpp */, + E29681D824BE5E1200974229 /* rs16.cpp */, + E29681D924BE5E1200974229 /* rijndael.cpp */, + E29681DA24BE5E1200974229 /* smallfn.o */, + E29681DB24BE5E1200974229 /* ulinks.cpp */, + E29681DC24BE5E1200974229 /* headers5.hpp */, + E29681DD24BE5E1200974229 /* resource.o */, + E29681DE24BE5E1200974229 /* blake2s.hpp */, + E29681DF24BE5E1200974229 /* extinfo.cpp */, + E29681E024BE5E1200974229 /* model.cpp */, + E29681E124BE5E1200974229 /* rar.cpp */, + E29681E224BE5E1200974229 /* rijndael.o */, + E29681E324BE5E1200974229 /* win32acl.cpp */, + E29681E424BE5E1200974229 /* getbits.cpp */, + E29681E524BE5E1200974229 /* rarpch.cpp */, + E29681E624BE5E1200974229 /* secpassword.cpp */, + E29681E724BE5E1200974229 /* readme.txt */, + E29681E824BE5E1200974229 /* smallfn.cpp */, + E29681E924BE5E1200974229 /* scantree.hpp */, + E29681EA24BE5E1200974229 /* sha1.o */, + E29681EB24BE5E1200974229 /* threadpool.cpp */, + E29681EC24BE5E1200974229 /* volume.o */, + E29681ED24BE5E1200974229 /* encname.o */, + E29681EE24BE5E1200974229 /* system.o */, + E29681EF24BE5E1200974229 /* filcreat.hpp */, + E29681F024BE5E1200974229 /* rardefs.hpp */, + E29681F124BE5E1200974229 /* ui.hpp */, + E29681F224BE5E1200974229 /* suballoc.cpp */, + E29681F324BE5E1200974229 /* filestr.cpp */, + E29681F424BE5E1200974229 /* qopen.hpp */, + E29681F524BE5E1200974229 /* crypt.hpp */, + E29681F624BE5E1200974229 /* crc.hpp */, + E29681F724BE5E1200974229 /* unpack.o */, + E29681F824BE5E1200974229 /* hash.cpp */, + E29681F924BE5E1200974229 /* unpack50mt.cpp */, + E29681FA24BE5E1200974229 /* strlist.cpp */, + E29681FB24BE5E1200974229 /* license.txt */, + E29681FC24BE5E1200974229 /* global.cpp */, + E29681FD24BE5E1200974229 /* recvol3.cpp */, + E29681FE24BE5E1200974229 /* headers.cpp */, + E29681FF24BE5E1200974229 /* strlist.o */, + E296820024BE5E1200974229 /* uisilent.cpp */, + E296820124BE5E1200974229 /* log.hpp */, + E296820224BE5E1200974229 /* cmddata.o */, + E296820324BE5E1200974229 /* cmdmix.cpp */, + E296820424BE5E1200974229 /* errhnd.hpp */, + E296820524BE5E1200974229 /* os.hpp */, + E296820624BE5E1200974229 /* rarvm.o */, + E296820724BE5E1200974229 /* win32stm.cpp */, + E296820824BE5E1200974229 /* scantree.o */, + E296820924BE5E1200974229 /* strfn.hpp */, + E296820A24BE5E1200974229 /* cmddata.cpp */, + E296820B24BE5E1200974229 /* rdwrfn.hpp */, + E296820C24BE5E1200974229 /* hash.o */, + E296820D24BE5E1200974229 /* extract.cpp */, + E296820E24BE5E1200974229 /* dll.cpp */, + E296820F24BE5E1200974229 /* unicode.cpp */, + E296821024BE5E1200974229 /* unpack15.cpp */, + E296821124BE5E1200974229 /* archive.cpp */, + E296821224BE5E1200974229 /* extinfo.o */, + E296821324BE5E1200974229 /* recvol5.cpp */, + E296821424BE5E1200974229 /* rarvm.hpp */, + E296821524BE5E1200974229 /* resource.hpp */, + E296821624BE5E1200974229 /* volume.cpp */, + E296821724BE5E1200974229 /* isnt.o */, + E296821824BE5E1200974229 /* coder.cpp */, + E296821924BE5E1200974229 /* arcread.cpp */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXLegacyTarget section */ + E296814924BE5E1200974229 /* libunrar */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "$(ACTION)"; + buildConfigurationList = E296821A24BE5E1200974229 /* Build configuration list for PBXLegacyTarget "libunrar" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = /Users/tarasis/Programming/Projects/QuietUnrar/libunrar; + dependencies = ( + ); + name = libunrar; + passBuildSettingsInEnvironment = 1; + productName = libunrar; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXProject section */ + E296814524BE5E1200974229 /* Project object */ = { + isa = PBXProject; + attributes = { + }; + buildConfigurationList = E296814824BE5E1200974229 /* Build configuration list for PBXProject "libunrar" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E296814424BE5E1200974229; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E296814924BE5E1200974229 /* libunrar */, + ); + }; +/* End PBXProject section */ + +/* Begin XCBuildConfiguration section */ + E296814624BE5E1200974229 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx10.6; + }; + name = Debug; + }; + E296814724BE5E1200974229 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + COPY_PHASE_STRIP = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + SDKROOT = macosx10.6; + }; + name = Release; + }; + E296821B24BE5E1200974229 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = libunrar; + }; + name = Debug; + }; + E296821C24BE5E1200974229 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = libunrar; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E296814824BE5E1200974229 /* Build configuration list for PBXProject "libunrar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E296814624BE5E1200974229 /* Debug */, + E296814724BE5E1200974229 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E296821A24BE5E1200974229 /* Build configuration list for PBXLegacyTarget "libunrar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E296821B24BE5E1200974229 /* Debug */, + E296821C24BE5E1200974229 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E296814524BE5E1200974229 /* Project object */; +} diff --git a/libunrar/license.txt b/libunrar/license.txt index ec35e09..0811276 100644 --- a/libunrar/license.txt +++ b/libunrar/license.txt @@ -10,13 +10,15 @@ 1. All copyrights to RAR and the utility UnRAR are exclusively owned by the author - Alexander Roshal. - 2. The UnRAR sources may be used in any software to handle RAR - archives without limitations free of charge, but cannot be used - to re-create the RAR compression algorithm, which is proprietary. - Distribution of modified UnRAR sources in separate form or as a - part of other software is permitted, provided that it is clearly - stated in the documentation and source comments that the code may - not be used to develop a RAR (WinRAR) compatible archiver. + 2. UnRAR source code may be used in any software to handle + RAR archives without limitations free of charge, but cannot be + used to develop RAR (WinRAR) compatible archiver and to + re-create RAR compression algorithm, which is proprietary. + Distribution of modified UnRAR source code in separate form + or as a part of other software is permitted, provided that + full text of this paragraph, starting from "UnRAR source code" + words, is included in license, or in documentation if license + is not available, and in source code comments of resulting package. 3. The UnRAR utility may be freely distributed. It is allowed to distribute UnRAR inside of other software packages. @@ -37,4 +39,4 @@ Thank you for your interest in RAR and UnRAR. - Alexander L. Roshal \ No newline at end of file + Alexander L. Roshal diff --git a/libunrar/list.cpp b/libunrar/list.cpp index ba02674..77c1041 100644 --- a/libunrar/list.cpp +++ b/libunrar/list.cpp @@ -1,29 +1,31 @@ #include "rar.hpp" -static void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare); +static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare); static void ListSymLink(Archive &Arc); -static void ListFileAttr(uint A,int HostOS); +static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize); static void ListOldSubHeader(Archive &Arc); -static void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical); +static void ListNewSubHeader(CommandData *Cmd,Archive &Arc); void ListArchive(CommandData *Cmd) { int64 SumPackSize=0,SumUnpSize=0; uint ArcCount=0,SumFileCount=0; bool Technical=(Cmd->Command[1]=='T'); + bool ShowService=Technical && Cmd->Command[2]=='A'; bool Bare=(Cmd->Command[1]=='B'); - bool Verbose=(*Cmd->Command=='V'); + bool Verbose=(Cmd->Command[0]=='V'); - char ArcName[NM]; - wchar ArcNameW[NM]; - - while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName))) + wchar ArcName[NM]; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + Archive Arc(Cmd); -#ifdef _WIN_32 +#ifdef _WIN_ALL Arc.RemoveSequentialFlag(); #endif - if (!Arc.WOpen(ArcName,ArcNameW)) + if (!Arc.WOpen(ArcName)) continue; bool FileMatched=true; while (1) @@ -32,110 +34,112 @@ void ListArchive(CommandData *Cmd) uint FileCount=0; if (Arc.IsArchive(true)) { - if (!Arc.IsOpened()) - break; bool TitleShown=false; if (!Bare) { Arc.ViewComment(); - - // RAR can close a corrupt encrypted archive - if (!Arc.IsOpened()) - break; - - mprintf("\n"); + mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); + mprintf(L"\n%s: ",St(MListDetails)); + uint SetCount=0; + const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt); if (Arc.Solid) - mprintf(St(MListSolid)); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid)); if (Arc.SFXSize>0) - mprintf(St(MListSFX)); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX)); if (Arc.Volume) - if (Arc.Solid) - mprintf(St(MListVol1)); + if (Arc.Format==RARFMT50) + { + // RAR 5.0 archives store the volume number in main header, + // so it is already available now. + if (SetCount++ > 0) + mprintf(L", "); + mprintf(St(MVolumeNumber),Arc.VolNumber+1); + } else - mprintf(St(MListVol2)); - else - if (Arc.Solid) - mprintf(St(MListArc1)); - else - mprintf(St(MListArc2)); - mprintf(" %s\n",Arc.FileName); - if (Technical) - { - if (Arc.Protected) - mprintf(St(MListRecRec)); - if (Arc.Locked) - mprintf(St(MListLock)); - } + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume)); + if (Arc.Protected) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR)); + if (Arc.Locked) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); + if (Arc.Encrypted) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); + mprintf(L"\n"); } + + wchar VolNumText[50]; + *VolNumText=0; while(Arc.ReadHeader()>0) { - int HeaderType=Arc.GetHeaderType(); - if (HeaderType==ENDARC_HEAD) + Wait(); // Allow quit listing with Ctrl+C. + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_ENDARC) + { +#ifndef SFX_MODULE + // Only RAR 1.5 archives store the volume number in end record. + if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15) + swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1); +#endif + if (Technical && ShowService) + { + mprintf(L"\n%12ls: %ls",St(MListService),L"EOF"); + if (*VolNumText!=0) + mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText); + mprintf(L"\n"); + } break; + } switch(HeaderType) { - case FILE_HEAD: - IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName); - FileMatched=Cmd->IsProcessFile(Arc.NewLhd)!=0; - if (FileMatched) + case HEAD_FILE: + FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0; + if (FileMatched) { - ListFileHeader(Arc.NewLhd,Verbose,Technical,TitleShown,Bare); - if (!(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)) + ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare); + if (!Arc.FileHead.SplitBefore) { - TotalUnpSize+=Arc.NewLhd.FullUnpSize; + TotalUnpSize+=Arc.FileHead.UnpSize; FileCount++; } - TotalPackSize+=Arc.NewLhd.FullPackSize; - if (Technical) - ListSymLink(Arc); -#ifndef SFX_MODULE - if (Verbose) - Arc.ViewFileComment(); -#endif + TotalPackSize+=Arc.FileHead.PackSize; } break; -#ifndef SFX_MODULE - case SUB_HEAD: - if (Technical && FileMatched && !Bare) - ListOldSubHeader(Arc); - break; -#endif - case NEWSUB_HEAD: + case HEAD_SERVICE: if (FileMatched && !Bare) { - if (Technical) - ListFileHeader(Arc.SubHead,Verbose,true,TitleShown,false); - ListNewSubHeader(Cmd,Arc,Technical); + if (Technical && ShowService) + ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false); } break; } Arc.SeekToNext(); } - if (!Bare) + if (!Bare && !Technical) if (TitleShown) { - mprintf("\n"); - for (int I=0;I<79;I++) - mprintf("-"); - char UnpSizeText[20]; - itoa(TotalUnpSize,UnpSizeText); + wchar UnpSizeText[20]; + itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText)); - char PackSizeText[20]; - itoa(TotalPackSize,PackSizeText); + wchar PackSizeText[20]; + itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText)); - mprintf("\n%5lu %16s %8s %3d%%",FileCount,UnpSizeText, - PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize)); + if (Verbose) + { + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText, + PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize), + VolNumText,FileCount); + } + else + { + mprintf(L"\n----------- --------- ---------- ----- ----"); + mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount); + } + SumFileCount+=FileCount; SumUnpSize+=TotalUnpSize; SumPackSize+=TotalPackSize; -#ifndef SFX_MODULE - if (Arc.EndArcHead.Flags & EARC_VOLNUMBER) - { - mprintf(" "); - mprintf(St(MVolumeNumber),Arc.EndArcHead.VolNumber+1); - } -#endif - mprintf("\n"); + mprintf(L"\n"); } else mprintf(St(MListNoFiles)); @@ -143,254 +147,328 @@ void ListArchive(CommandData *Cmd) ArcCount++; #ifndef NOVOLUME - if (Cmd->VolSize!=0 && ((Arc.NewLhd.Flags & LHD_SPLIT_AFTER) || - Arc.GetHeaderType()==ENDARC_HEAD && - (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)!=0) && - MergeArchive(Arc,NULL,false,*Cmd->Command)) - { + if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter || + Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) && + MergeArchive(Arc,NULL,false,Cmd->Command[0])) Arc.Seek(0,SEEK_SET); - } else #endif break; } else { - if (Cmd->ArcNames->ItemsCount()<2 && !Bare) + if (Cmd->ArcNames.ItemsCount()<2 && !Bare) mprintf(St(MNotRAR),Arc.FileName); break; } } } - if (ArcCount>1 && !Bare) + + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (ArcCount>1 && !Bare && !Technical) { - char UnpSizeText[20],PackSizeText[20]; - itoa(SumUnpSize,UnpSizeText); - itoa(SumPackSize,PackSizeText); - mprintf("\n%5lu %16s %8s %3d%%\n",SumFileCount,UnpSizeText, - PackSizeText,ToPercentUnlim(SumPackSize,SumUnpSize)); + wchar UnpSizeText[20],PackSizeText[20]; + itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText)); + + if (Verbose) + mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText, + ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount); + else + mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount); } } -void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare) +enum LISTCOL_TYPE { + LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR +}; + + +void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare) { - if (!Bare) - { - if (!TitleShown) - { - if (Verbose) - mprintf(St(MListPathComm)); - else - mprintf(St(MListName)); - mprintf(St(MListTitle)); - if (Technical) - mprintf(St(MListTechTitle)); - for (int I=0;I<79;I++) - mprintf("-"); - TitleShown=true; - } - - if (hd.HeadType==NEWSUB_HEAD) - mprintf(St(MSubHeadType),hd.FileName); - - mprintf("\n%c",(hd.Flags & LHD_PASSWORD) ? '*' : ' '); - } - - char *Name=hd.FileName; - -#ifdef UNICODE_SUPPORTED - char ConvertedName[NM]; - if ((hd.Flags & LHD_UNICODE)!=0 && *hd.FileNameW!=0 && UnicodeEnabled()) - { - if (WideToChar(hd.FileNameW,ConvertedName) && *ConvertedName!=0) - Name=ConvertedName; - } -#endif + wchar *Name=hd.FileName; + RARFORMAT Format=Arc.Format; if (Bare) { - mprintf("%s\n",Verbose ? Name:PointToName(Name)); + mprintf(L"%s\n",Name); return; } - if (Verbose) - mprintf("%s\n%12s ",Name,""); - else - mprintf("%-12s",PointToName(Name)); - - char UnpSizeText[20],PackSizeText[20]; - if (hd.FullUnpSize==INT64NDF) - strcpy(UnpSizeText,"?"); - else - itoa(hd.FullUnpSize,UnpSizeText); - itoa(hd.FullPackSize,PackSizeText); - - mprintf(" %8s %8s ",UnpSizeText,PackSizeText); - - if ((hd.Flags & LHD_SPLIT_BEFORE) && (hd.Flags & LHD_SPLIT_AFTER)) - mprintf(" <->"); - else - if (hd.Flags & LHD_SPLIT_BEFORE) - mprintf(" <--"); - else - if (hd.Flags & LHD_SPLIT_AFTER) - mprintf(" -->"); - else - mprintf("%3d%%",ToPercentUnlim(hd.FullPackSize,hd.FullUnpSize)); - - char DateStr[50]; - hd.mtime.GetText(DateStr,false); - mprintf(" %s ",DateStr); - - if (hd.HeadType==NEWSUB_HEAD) - mprintf(" %c....B ",(hd.SubFlags & SUBHEAD_FLAGS_INHERITED) ? 'I' : '.'); - else - ListFileAttr(hd.FileAttr,hd.HostOS); - - mprintf(" %8.8X",hd.FileCRC); - mprintf(" m%d",hd.Method-0x30); - if ((hd.Flags & LHD_WINDOWMASK)<=6*32) - mprintf("%c",((hd.Flags&LHD_WINDOWMASK)>>5)+'a'); - else - mprintf(" "); - mprintf(" %d.%d",hd.UnpVer/10,hd.UnpVer%10); - - static const char *RarOS[]={ - "DOS","OS/2","Win95/NT","Unix","MacOS","BeOS","WinCE","","","" - }; - - if (Technical) - mprintf("\n%22s %8s %4s", - (hd.HostOS",FileName); + mprintf(L"\n%ls",St(MListTitleV)); + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); } else + { + mprintf(L"\n%ls",St(MListTitleL)); + mprintf(L"\n----------- --------- ---------- ----- ----"); + } + TitleShown=true; + } + + wchar UnpSizeText[30],PackSizeText[30]; + if (hd.UnpSize==INT64NDF) + wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText)); + else + itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText)); + + wchar AttrStr[30]; + if (hd.HeaderType==HEAD_SERVICE) + swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.'); + else + ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr)); + + wchar RatioStr[10]; + + if (hd.SplitBefore && hd.SplitAfter) + wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr)); + else + if (hd.SplitBefore) + wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr)); + else + if (hd.SplitAfter) + wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr)); + else + swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize)); + + wchar DateStr[50]; + hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical); + + if (Technical) + { + mprintf(L"\n%12s: %s",St(MListName),Name); + + bool FileBlock=hd.HeaderType==HEAD_FILE; + + if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + { + mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream)); + wchar StreamName[NM]; + GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); + mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName); + } + else + { + const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService); + + if (hd.RedirType!=FSREDIR_NONE) + switch(hd.RedirType) + { + case FSREDIR_UNIXSYMLINK: + Type=St(MListUSymlink); break; + case FSREDIR_WINSYMLINK: + Type=St(MListWSymlink); break; + case FSREDIR_JUNCTION: + Type=St(MListJunction); break; + case FSREDIR_HARDLINK: + Type=St(MListHardlink); break; + case FSREDIR_FILECOPY: + Type=St(MListCopy); break; + } + mprintf(L"\n%12ls: %ls",St(MListType),Type); + if (hd.RedirType!=FSREDIR_NONE) + if (Format==RARFMT15) + { + char LinkTargetA[NM]; + if (Arc.FileHead.Encrypted) + { + // Link data are encrypted. We would need to ask for password + // and initialize decryption routine to display the link target. + strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA)); + } + else + { + int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1); + Arc.Read(LinkTargetA,DataSize); + LinkTargetA[DataSize > 0 ? DataSize : 0] = 0; + } + wchar LinkTarget[NM]; + CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget)); + mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget); + } + else + mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName); + } + if (!hd.Dir) + { + mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText); + mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText); + mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr); + } + if (hd.mtime.IsSet()) + mprintf(L"\n%12ls: %ls",St(MListMtime),DateStr); + if (hd.ctime.IsSet()) + { + hd.ctime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(MListCtime),DateStr); + } + if (hd.atime.IsSet()) + { + hd.atime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(MListAtime),DateStr); + } + mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr); + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"\n%12ls: %8.8X", + hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32", + hd.FileHash.CRC32); + if (hd.FileHash.Type==HASH_BLAKE2) + { + wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1]; + BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr)); + mprintf(L"\n%12ls: %ls", + hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2", + BlakeStr); + } + + const wchar *HostOS=L""; + if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN) + HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix"; + if (Format==RARFMT15) + { + static const wchar *RarOS[]={ + L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L"" + }; + if (hd.HostOS=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400, + hd.WinSize>=0x100000 ? L"M":L"K"); + + if (hd.Solid || hd.Encrypted) + { + mprintf(L"\n%12ls: ",St(MListFlags)); + if (hd.Solid) + mprintf(L"%ls ",St(MListSolid)); + if (hd.Encrypted) + mprintf(L"%ls ",St(MListEnc)); + } + + if (hd.Version) + { + uint Version=ParseVersionFileName(Name,false); + if (Version!=0) + mprintf(L"\n%12ls: %u",St(MListFileVer),Version); + } + + if (hd.UnixOwnerSet) + { + mprintf(L"\n%12ls: ",L"Unix owner"); + if (*hd.UnixOwnerName!=0) + mprintf(L"%ls:",GetWide(hd.UnixOwnerName)); + if (*hd.UnixGroupName!=0) + mprintf(L"%ls",GetWide(hd.UnixGroupName)); + if ((*hd.UnixOwnerName!=0 || *hd.UnixGroupName!=0) && (hd.UnixOwnerNumeric || hd.UnixGroupNumeric)) + mprintf(L" "); + if (hd.UnixOwnerNumeric) + mprintf(L"#%d:",hd.UnixOwnerID); + if (hd.UnixGroupNumeric) + mprintf(L"#%d:",hd.UnixGroupID); + } + + mprintf(L"\n"); + return; + } + + mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText); + + if (Verbose) + mprintf(L"%9ls %4ls ",PackSizeText,RatioStr); + + mprintf(L" %ls ",DateStr); + + if (Verbose) + { + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"%8.8X ",hd.FileHash.CRC32); + else + if (hd.FileHash.Type==HASH_BLAKE2) + { + byte *S=hd.FileHash.Digest; + mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]); + } + else + mprintf(L"???????? "); + } + mprintf(L"%ls",Name); +} + +/* +void ListSymLink(Archive &Arc) +{ + if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000) + if (Arc.FileHead.Encrypted) { // Link data are encrypted. We would need to ask for password // and initialize decryption routine to display the link target. - mprintf("\n%22s %s","-->","*<-?->"); + mprintf(L"\n%22ls %ls",L"-->",L"*<-?->"); + } + else + { + char FileName[NM]; + uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1); + Arc.Read(FileName,DataSize); + FileName[DataSize]=0; + mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName)); } } +*/ - -void ListFileAttr(uint A,int HostOS) +void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize) { - switch(HostOS) + switch(HostType) { - case HOST_MSDOS: - case HOST_OS2: - case HOST_WIN32: - case HOST_MACOS: - mprintf(" %c%c%c%c%c%c%c ", - (A & 0x08) ? 'V' : '.', - (A & 0x10) ? 'D' : '.', - (A & 0x01) ? 'R' : '.', - (A & 0x02) ? 'H' : '.', - (A & 0x04) ? 'S' : '.', - (A & 0x20) ? 'A' : '.', - (A & 0x800) ? 'C' : '.'); + case HSYS_WINDOWS: + swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c", + (A & 0x2000)!=0 ? 'I' : '.', // Not content indexed. + (A & 0x0800)!=0 ? 'C' : '.', // Compressed. + (A & 0x0020)!=0 ? 'A' : '.', // Archive. + (A & 0x0010)!=0 ? 'D' : '.', // Directory. + (A & 0x0004)!=0 ? 'S' : '.', // System. + (A & 0x0002)!=0 ? 'H' : '.', // Hidden. + (A & 0x0001)!=0 ? 'R' : '.'); // Read-only. break; - case HOST_UNIX: - case HOST_BEOS: + case HSYS_UNIX: switch (A & 0xF000) { case 0x4000: - mprintf("d"); + AttrStr[0]='d'; break; case 0xA000: - mprintf("l"); + AttrStr[0]='l'; break; default: - mprintf("-"); + AttrStr[0]='-'; break; } - mprintf("%c%c%c%c%c%c%c%c%c", + swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c", (A & 0x0100) ? 'r' : '-', (A & 0x0080) ? 'w' : '-', - (A & 0x0040) ? ((A & 0x0800) ? 's':'x'):((A & 0x0800) ? 'S':'-'), + (A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'), (A & 0x0020) ? 'r' : '-', (A & 0x0010) ? 'w' : '-', - (A & 0x0008) ? ((A & 0x0400) ? 's':'x'):((A & 0x0400) ? 'S':'-'), + (A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'), (A & 0x0004) ? 'r' : '-', (A & 0x0002) ? 'w' : '-', - (A & 0x0001) ? 'x' : '-'); + (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-'); + break; + case HSYS_UNKNOWN: + wcsncpyz(AttrStr,L"?",AttrSize); break; } } - - -#ifndef SFX_MODULE -void ListOldSubHeader(Archive &Arc) -{ - switch(Arc.SubBlockHead.SubType) - { - case EA_HEAD: - mprintf(St(MListEAHead)); - break; - case UO_HEAD: - mprintf(St(MListUOHead),Arc.UOHead.OwnerName,Arc.UOHead.GroupName); - break; - case MAC_HEAD: - mprintf(St(MListMACHead1),Arc.MACHead.fileType>>24,Arc.MACHead.fileType>>16,Arc.MACHead.fileType>>8,Arc.MACHead.fileType); - mprintf(St(MListMACHead2),Arc.MACHead.fileCreator>>24,Arc.MACHead.fileCreator>>16,Arc.MACHead.fileCreator>>8,Arc.MACHead.fileCreator); - break; - case BEEA_HEAD: - mprintf(St(MListBeEAHead)); - break; - case NTACL_HEAD: - mprintf(St(MListNTACLHead)); - break; - case STREAM_HEAD: - mprintf(St(MListStrmHead),Arc.StreamHead.StreamName); - break; - default: - mprintf(St(MListUnkHead),Arc.SubBlockHead.SubType); - break; - } -} -#endif - - -void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical) -{ - if (Arc.SubHead.CmpName(SUBHEAD_TYPE_CMT) && - (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0 && !Cmd->DisableComment) - { - Array CmtData; - size_t ReadSize=Arc.ReadCommentData(&CmtData,NULL); - if (ReadSize!=0) - { - mprintf(St(MFileComment)); - OutComment((char *)&CmtData[0],ReadSize); - } - } - if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM) && - (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0) - { - size_t DestSize=Arc.SubHead.SubData.Size()/2; - wchar DestNameW[NM]; - char DestName[NM]; - if (DestSize - - " -#define MUNRARTitle1 "\nUsage: unrar - - " -#define MRARTitle2 "\n <@listfiles...> " -#define MCHelpCmd "\n\n" -#define MCHelpCmdA "\n a Add files to archive" -#define MCHelpCmdC "\n c Add archive comment" -#define MCHelpCmdCF "\n cf Add files comment" -#define MCHelpCmdCH "\n ch Change archive parameters" -#define MCHelpCmdCW "\n cw Write archive comment to file" -#define MCHelpCmdD "\n d Delete files from archive" -#define MCHelpCmdE "\n e Extract files to current directory" -#define MCHelpCmdF "\n f Freshen files in archive" -#define MCHelpCmdI "\n i[par]= Find string in archives" -#define MCHelpCmdK "\n k Lock archive" -#define MCHelpCmdL "\n l[t,b] List archive [technical, bare]" -#define MCHelpCmdM "\n m[f] Move to archive [files only]" -#define MCHelpCmdP "\n p Print file to stdout" -#define MCHelpCmdR "\n r Repair archive" -#define MCHelpCmdRC "\n rc Reconstruct missing volumes" -#define MCHelpCmdRN "\n rn Rename archived files" -#define MCHelpCmdRR "\n rr[N] Add data recovery record" -#define MCHelpCmdRV "\n rv[N] Create recovery volumes" -#define MCHelpCmdS "\n s[name|-] Convert archive to or from SFX" -#define MCHelpCmdT "\n t Test archive files" -#define MCHelpCmdU "\n u Update files in archive" -#define MCHelpCmdV "\n v[t,b] Verbosely list archive [technical,bare]" -#define MCHelpCmdX "\n x Extract files with full path" -#define MCHelpSw "\n\n" -#define MCHelpSwm "\n - Stop switches scanning" -#define MCHelpSwAC "\n ac Clear Archive attribute after compression or extraction" -#define MCHelpSwAD "\n ad Append archive name to destination path" -#define MCHelpSwAG "\n ag[format] Generate archive name using the current date" -#define MCHelpSwAI "\n ai Ignore file attributes" -#define MCHelpSwAO "\n ao Add files with Archive attribute set" -#define MCHelpSwAP "\n ap Set path inside archive" -#define MCHelpSwAS "\n as Synchronize archive contents" -#define MCHelpSwAV "\n av Put authenticity verification (registered versions only)" -#define MCHelpSwAVm "\n av- Disable authenticity verification check" -#define MCHelpSwCm "\n c- Disable comments show" -#define MCHelpSwCFGm "\n cfg- Disable read configuration" -#define MCHelpSwCL "\n cl Convert names to lower case" -#define MCHelpSwCU "\n cu Convert names to upper case" -#define MCHelpSwDF "\n df Delete files after archiving" -#define MCHelpSwDH "\n dh Open shared files" -#define MCHelpSwDR "\n dr Delete files to Recycle Bin" -#define MCHelpSwDS "\n ds Disable name sort for solid archive" -#define MCHelpSwDW "\n dw Wipe files after archiving" -#define MCHelpSwEa "\n e[+] Set file exclude and include attributes" -#define MCHelpSwED "\n ed Do not add empty directories" -#define MCHelpSwEE "\n ee Do not save and extract extended attributes" -#define MCHelpSwEN "\n en Do not put 'end of archive' block" -#define MCHelpSwEP "\n ep Exclude paths from names" -#define MCHelpSwEP1 "\n ep1 Exclude base directory from names" -#define MCHelpSwEP2 "\n ep2 Expand paths to full" -#define MCHelpSwEP3 "\n ep3 Expand paths to full including the drive letter" -#define MCHelpSwF "\n f Freshen files" -#define MCHelpSwHP "\n hp[password] Encrypt both file data and headers" -#define MCHelpSwIDP "\n id[c,d,p,q] Disable messages" -#define MCHelpSwIEML "\n ieml[addr] Send archive by email" -#define MCHelpSwIERR "\n ierr Send all messages to stderr" -#define MCHelpSwILOG "\n ilog[name] Log errors to file (registered versions only)" -#define MCHelpSwINUL "\n inul Disable all messages" -#define MCHelpSwIOFF "\n ioff Turn PC off after completing an operation" -#define MCHelpSwISND "\n isnd Enable sound" -#define MCHelpSwK "\n k Lock archive" -#define MCHelpSwKB "\n kb Keep broken extracted files" -#define MCHelpSwMn "\n m<0..5> Set compression level (0-store...3-default...5-maximal)" -#define MCHelpSwMC "\n mc Set advanced compression parameters" -#define MCHelpSwMD "\n md Dictionary size in KB (64,128,256,512,1024,2048,4096 or A-G)" -#define MCHelpSwMS "\n ms[ext;ext] Specify file types to store" -#define MCHelpSwMT "\n mt Set the number of threads" -#define MCHelpSwN "\n n Include only specified file" -#define MCHelpSwNa "\n n@ Read file names to include from stdin" -#define MCHelpSwNal "\n n@ Include files listed in specified list file" -#define MCHelpSwO "\n o[+|-] Set the overwrite mode" -#define MCHelpSwOC "\n oc Set NTFS Compressed attribute" -#define MCHelpSwOL "\n ol Save symbolic links as the link instead of the file" -#define MCHelpSwOR "\n or Rename files automatically" -#define MCHelpSwOS "\n os Save NTFS streams" -#define MCHelpSwOW "\n ow Save or restore file owner and group" -#define MCHelpSwP "\n p[password] Set password" -#define MCHelpSwPm "\n p- Do not query password" -#define MCHelpSwR "\n r Recurse subdirectories" -#define MCHelpSwRm "\n r- Disable recursion" -#define MCHelpSwR0 "\n r0 Recurse subdirectories for wildcard names only" -#define MCHelpSwRI "\n ri

[:] Set priority (0-default,1-min..15-max) and sleep time in ms" -#define MCHelpSwRR "\n rr[N] Add data recovery record" -#define MCHelpSwRV "\n rv[N] Create recovery volumes" -#define MCHelpSwS "\n s[,v[-],e] Create solid archive" -#define MCHelpSwSm "\n s- Disable solid archiving" -#define MCHelpSwSC "\n sc[obj] Specify the character set" -#define MCHelpSwSFX "\n sfx[name] Create SFX archive" -#define MCHelpSwSI "\n si[name] Read data from standard input (stdin)" -#define MCHelpSwSL "\n sl Process files with size less than specified" -#define MCHelpSwSM "\n sm Process files with size more than specified" -#define MCHelpSwT "\n t Test files after archiving" -#define MCHelpSwTK "\n tk Keep original archive time" -#define MCHelpSwTL "\n tl Set archive time to latest file" -#define MCHelpSwTN "\n tn

[:] Set priority (0-default,1-min..15-max) and sleep time in ms" +#define MCHelpSwRR L"\n rr[N] Add data recovery record" +#define MCHelpSwRV L"\n rv[N] Create recovery volumes" +#define MCHelpSwS L"\n s[,v[-],e] Create solid archive" +#define MCHelpSwSm L"\n s- Disable solid archiving" +#define MCHelpSwSC L"\n sc[obj] Specify the character set" +#define MCHelpSwSFX L"\n sfx[name] Create SFX archive" +#define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)" +#define MCHelpSwSL L"\n sl Process files with size less than specified" +#define MCHelpSwSM L"\n sm Process files with size more than specified" +#define MCHelpSwT L"\n t Test files after archiving" +#define MCHelpSwTK L"\n tk Keep original archive time" +#define MCHelpSwTL L"\n tl Set archive time to latest file" +#define MCHelpSwTN L"\n tn[mcao] Process files newer than time" +#define MCHelpSwTO L"\n to[mcao] Process files older than time" +#define MCHelpSwTA L"\n ta[mcao] Process files modified after YYYYMMDDHHMMSS date" +#define MCHelpSwTB L"\n tb[mcao] Process files modified before YYYYMMDDHHMMSS date" +#define MCHelpSwTS L"\n ts[m,c,a,p] Save or restore time (modification, creation, access, preserve)" +#define MCHelpSwU L"\n u Update files" +#define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes" +#define MCHelpSwVUnr L"\n v List all volumes" +#define MCHelpSwVn L"\n v[k,b] Create volumes with size=*1000 [*1024, *1]" +#define MCHelpSwVD L"\n vd Erase disk contents before creating volume" +#define MCHelpSwVER L"\n ver[n] File version control" +#define MCHelpSwVN L"\n vn Use the old style volume naming scheme" +#define MCHelpSwVP L"\n vp Pause before each volume" +#define MCHelpSwW L"\n w Assign work directory" +#define MCHelpSwX L"\n x Exclude specified file" +#define MCHelpSwXa L"\n x@ Read file names to exclude from stdin" +#define MCHelpSwXal L"\n x@ Exclude files listed in specified list file" +#define MCHelpSwY L"\n y Assume Yes on all queries" +#define MCHelpSwZ L"\n z[file] Read archive comment from file" +#define MBadArc L"\nERROR: Bad archive %s\n" +#define MAskPsw L"Enter password (will not be echoed)" +#define MAskPswFor L"\nEnter password (will not be echoed) for %s: " +#define MReAskPsw L"\nReenter password: " +#define MNotMatchPsw L"\nERROR: Passwords do not match\n" +#define MErrWrite L"Write error in the file %s" +#define MErrRead L"Read error in the file %s" +#define MErrSeek L"Seek error in the file %s" +#define MErrFClose L"Cannot close the file %s" +#define MErrOutMem L"Not enough memory" +#define MErrBrokenArc L"Corrupt archive - use 'Repair' command" +#define MProgAborted L"Program aborted" +#define MErrRename L"\nCannot rename %s to %s" +#define MAbsNextVol L"\nCannot find volume %s" +#define MBreak L"\nUser break\n" +#define MAskCreatVol L"\nCreate next volume ?" +#define MAskNextDisk L"\nDisk full. Insert next" +#define MCreatVol L"\n\nCreating %sarchive %s\n" +#define MAskNextVol L"\nInsert disk with %s" +#define MTestVol L"\n\nTesting archive %s\n" +#define MExtrVol L"\n\nExtracting from %s\n" +#define MConverting L"\nConverting %s" +#define MCvtToSFX L"\nConvert archives to SFX" +#define MCvtFromSFX L"\nRemoving SFX module" +#define MNotSFX L"\n%s is not SFX archive" +#define MNotRAR L"\n%s is not RAR archive" +#define MNotFirstVol L"\n%s is not the first volume" +#define MCvtOldFormat L"\n%s - cannot convert to SFX archive with old format" +#define MCannotCreate L"\nCannot create %s" +#define MCannotOpen L"\nCannot open %s" +#define MUnknownMeth L"\nUnknown method in %s" +#define MNewRarFormat L"\nUnsupported archive format. Please update RAR to a newer version." +#define MOk L" OK" +#define MDone L"\nDone" +#define MLockingArc L"\nLocking archive" +#define MNotMdfOld L"\n\nERROR: Cannot modify old format archive" +#define MNotMdfLock L"\n\nERROR: Locked archive" +#define MNotMdfVol L"\n\nERROR: Cannot modify volume" +#define MPackAskReg L"\nEvaluation copy. Please register.\n" +#define MCreateArchive L"\nCreating %sarchive %s\n" +#define MUpdateArchive L"\nUpdating %sarchive %s\n" +#define MAddSolid L"solid " +#define MAddFile L"\nAdding %-58s " +#define MUpdFile L"\nUpdating %-58s " +#define MAddPoints L"\n... %-58s " +#define MMoveDelFiles L"\n\nDeleting files %s..." +#define MMoveDelDirs L"and directories" +#define MMoveDelFile L"\nDeleting %-30s" +#define MMoveDeleted L" deleted" +#define MMoveNotDeleted L" NOT DELETED" +#define MClearAttrib L"\n\nClearing attributes..." +#define MMoveDelDir L"\nDeleting directory %-30s" +#define MWarErrFOpen L"\nWARNING: Cannot open %d %s" +#define MErrOpenFiles L"files" +#define MErrOpenFile L"file" +#define MAddNoFiles L"\nWARNING: No files" +#define MMdfEncrSol L"\n%s: encrypted" +#define MAddAnalyze L"\nAnalyzing archived files: " +#define MRepacking L"\nRepacking archived files: " +#define MCRCFailed L"\n%-20s - checksum error" +#define MExtrTest L"\n\nTesting archive %s\n" +#define MExtracting L"\n\nExtracting from %s\n" +#define MUseCurPsw L"\n%s - use current password ?" +#define MCreatDir L"\nCreating %-56s" +#define MExtrSkipFile L"\nSkipping %-56s" +#define MExtrTestFile L"\nTesting %-56s" +#define MExtrFile L"\nExtracting %-56s" +#define MExtrPoints L"\n... %-56s" +#define MExtrErrMkDir L"\nCannot create directory %s" +#define MExtrPrinting L"\n------ Printing %s\n\n" +#define MEncrBadCRC L"\nChecksum error in the encrypted file %s. Corrupt file or wrong password." +#define MExtrNoFiles L"\nNo files to extract" +#define MExtrAllOk L"\nAll OK" +#define MExtrTotalErr L"\nTotal errors: %ld" +#define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n" +#define MAskOverwrite L"\nOverwrite %s ?" +#define MAskNewName L"\nEnter new name: " +#define MHeaderBroken L"\nCorrupt header is found" +#define MMainHeaderBroken L"\nMain archive header is corrupt" +#define MLogFileHead L"\n%s - the file header is corrupt" +#define MLogProtectHead L"The data recovery header is corrupt" +#define MReadStdinCmt L"\nReading comment from stdin\n" +#define MReadCommFrom L"\nReading comment from %s" +#define MDelComment L"\nDeleting comment from %s" +#define MAddComment L"\nAdding comment to %s" +#define MFCommAdd L"\nAdding file comments" +#define MAskFComm L"\n\nReading comment for %s : %s from stdin\n" +#define MLogCommBrk L"\nThe archive comment is corrupt" +#define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:" +#define MWriteCommTo L"\nWrite comment to %s" +#define MCommNotPres L"\nComment is not present" +#define MDelFrom L"\nDeleting from %s" +#define MDeleting L"\nDeleting %s" +#define MEraseArc L"\nErasing empty archive %s" +#define MNoDelFiles L"\nNo files to delete" +#define MLogTitle L"-------- %2d %s %d, archive %s" +#define MPathTooLong L"\nERROR: Path too long\n" +#define MListArchive L"Archive" +#define MListDetails L"Details" +#define MListSolid L"solid" +#define MListSFX L"SFX" +#define MListVolume L"volume" +#define MListRR L"recovery record" +#define MListLock L"lock" +#define MListEnc L"encrypted" +#define MListEncHead L"encrypted headers" +#define MListTitleL L" Attributes Size Date Time Name" +#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name" +#define MListName L"Name" +#define MListType L"Type" +#define MListFile L"File" +#define MListDir L"Directory" +#define MListUSymlink L"Unix symbolic link" +#define MListWSymlink L"Windows symbolic link" +#define MListJunction L"NTFS junction point" +#define MListHardlink L"Hard link" +#define MListCopy L"File reference" +#define MListStream L"NTFS alternate data stream" +#define MListTarget L"Target" +#define MListSize L"Size" +#define MListPacked L"Packed size" +#define MListRatio L"Ratio" +#define MListMtime L"mtime" +#define MListCtime L"ctime" +#define MListAtime L"atime" +#define MListAttr L"Attributes" +#define MListFlags L"Flags" +#define MListCompInfo L"Compression" +#define MListHostOS L"Host OS" +#define MListFileVer L"File version" +#define MListService L"Service" +#define MListUOHead L"\n Unix Owner/Group data: %-14s %-14s" +#define MListNTACLHead L"\n NTFS security data" +#define MListStrmHead L"\n NTFS stream: %s" +#define MListUnkHead L"\n Unknown subheader type: 0x%04x" +#define MFileComment L"\nComment: " +#define MYes L"Yes" +#define MNo L"No" +#define MListNoFiles L" 0 files\n" +#define MRprReconstr L"\nReconstructing %s" +#define MRprBuild L"\nBuilding %s" +#define MRprOldFormat L"\nCannot repair archive with old format" +#define MRprFind L"\nFound %s" +#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid ?" +#define MRprNoFiles L"\nNo files found" +#define MLogUnexpEOF L"\nUnexpected end of archive" +#define MRepAskReconst L"\nReconstruct archive structure ?" +#define MRRSearch L"\nSearching for recovery record" +#define MAnalyzeFileData L"\nAnalyzing file data" +#define MRecRNotFound L"\nData recovery record not found" +#define MRecRFound L"\nData recovery record found" +#define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged" +#define MRecCorrected L" - data recovered" +#define MRecFailed L" - cannot recover data" +#define MAddRecRec L"\nAdding data recovery record" +#define MEraseForVolume L"\n\nErasing contents of drive %c:\n" +#define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n" +#define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n" +#define MErrGetGroupID L"\nWARNING: Cannot get group %s ID\n" +#define MOwnersBroken L"\nERROR: %s group and owner data are corrupt\n" +#define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n" +#define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s" +#define MSymLinkExists L"\nWARNING: Symbolic link %s already exists" +#define MAskRetryCreate L"\nCannot create %s. Retry ?" +#define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s" +#define MFileRO L"\n%s is read-only" +#define MACLGetError L"\nWARNING: Cannot get %s security data\n" +#define MACLSetError L"\nWARNING: Cannot set %s security data\n" +#define MACLBroken L"\nERROR: %s security data are corrupt\n" +#define MACLUnknown L"\nWARNING: Unknown format of %s security data\n" +#define MStreamBroken L"\nERROR: %s stream data are corrupt\n" +#define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n" +#define MInvalidName L"\nERROR: Invalid file name %s" +#define MProcessArc L"\n\nProcessing archive %s" +#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file name" +#define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s" +#define MUnknownOption L"\nERROR: Unknown option: %s" +#define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored" +#define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored" +#define MSubHeadDataCRC L"\nERROR: Corrupt %s data block" +#define MSubHeadType L"\nData header type: %s" +#define MScanError L"\nCannot read contents of %s" +#define MNotVolume L"\n%s is not volume" +#define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets" +#define MRecVolMissing L"\n%d volumes missing" +#define MRecVolFound L"\n%d recovery volumes found" +#define MRecVolAllExist L"\nNothing to reconstruct" +#define MRecVolCannotFix L"\nReconstruction impossible" +#define MReconstructing L"\nReconstructing..." +#define MCreating L"\nCreating %s" +#define MRenaming L"\nRenaming %s to %s" +#define MNTFSRequired L"\nWrite error: only NTFS file system supports files larger than 4 GB" +#define MFAT32Size L"\nWARNING: FAT32 file system does not support 4 GB or larger files" +#define MErrChangeAttr L"\nWARNING: Cannot change attributes of %s" +#define MWrongSFXVer L"\nERROR: default SFX module does not support RAR %d.%d archives" +#define MHeadEncMismatch L"\nCannot change the header encryption mode in already encrypted archive" +#define MCannotEmail L"\nCannot email the file %s" +#define MCopyrightS L"\nRAR SFX archive" +#define MSHelpCmd L"\n\n" +#define MSHelpCmdE L"\n -x Extract from archive (default)" +#define MSHelpCmdT L"\n -t Test archive files" +#define MSHelpCmdV L"\n -v Verbosely list contents of archive" +#define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d" +#define MVolumeNumber L"volume %d" +#define MCannotDelete L"\nCannot delete %s" +#define MRecycleFailed L"\nCannot move some files and folders to Recycle Bin" +#define MCalcCRC L"\nCalculating the checksum" +#define MTooLargeSFXArc L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB." +#define MCalcCRCAllVol L"\nCalculating checksums of all volumes." +#define MNotEnoughDisk L"\nERROR: Not enough disk space for %s." +#define MNewerRAR L"\nYou may need a newer version of RAR." +#define MUnkEncMethod L"\nUnknown encryption method in %s" +#define MWrongPassword L"\nThe specified password is incorrect." +#define MWrongFilePassword L"\nIncorrect password for %s" +#define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x" +#define MBlocksRecovered L"\n%u blocks are recovered, %u blocks are relocated" +#define MRRDamaged L"\nRecovery record is corrupt." +#define MTestingRR L"\nTesting the recovery record" +#define MFailed L"Failed" +#define MIncompatSwitch L"\n%s switch is not supported for RAR %d.x archive format." +#define MSearchDupFiles L"\nSearching for identical files" +#define MNumFound L"%d found." +#define MUnknownExtra L"\nUnknown extra field in %s." +#define MCorruptExtra L"\nCorrupt %s extra field in %s." +#define MCopyError L"\nCannot copy %s to %s." +#define MCopyErrorHint L"\nYou need to unpack the entire archive to create file reference entries." +#define MCopyingData L"\nCopying data" +#define MErrCreateLnkS L"\nCannot create symbolic link %s" +#define MErrCreateLnkH L"\nCannot create hard link %s" +#define MErrLnkTarget L"\nYou need to unpack the link target first" +#define MNeedAdmin L"\nYou may need to run RAR as administrator" +#define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB." +#define MUseSmalllerDict L"\nPlease use a smaller compression dictionary." +#define MOpenErrAtime L"\nYou may need to remove -tsp switch to open this file." diff --git a/libunrar/log.cpp b/libunrar/log.cpp index 966e9de..8bbe8ee 100644 --- a/libunrar/log.cpp +++ b/libunrar/log.cpp @@ -1,23 +1,36 @@ #include "rar.hpp" -static char LogName[NM]; +static wchar LogName[NM]; +static RAR_CHARSET LogCharset=RCH_DEFAULT; -void InitLogOptions(char *LogName) +void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet) { - strcpy(::LogName,LogName); + wcsncpyz(LogName,LogFileName,ASIZE(LogName)); + LogCharset=CSet; } #ifndef SILENT -void Log(const char *ArcName,const char *Format,...) +void Log(const wchar *ArcName,const wchar *fmt,...) { - safebuf char Msg[2*NM+1024]; - va_list ArgPtr; - va_start(ArgPtr,Format); - vsprintf(Msg,Format,ArgPtr); - va_end(ArgPtr); - eprintf("%s",Msg); + // Preserve the error code for possible following system error message. + int Code=ErrHandler.GetSystemErrorCode(); + + uiAlarm(UIALARM_ERROR); + + // This buffer is for format string only, not for entire output, + // so it can be short enough. + wchar fmtw[1024]; + PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); + + safebuf wchar Msg[2*NM+1024]; + va_list arglist; + va_start(arglist,fmt); + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + va_end(arglist); + eprintf(L"%ls",Msg); + ErrHandler.SetSystemErrorCode(Code); } #endif diff --git a/libunrar/log.hpp b/libunrar/log.hpp index 52d6b8d..008ef11 100644 --- a/libunrar/log.hpp +++ b/libunrar/log.hpp @@ -1,18 +1,12 @@ #ifndef _RAR_LOG_ #define _RAR_LOG_ -void InitLogOptions(char *LogName); - -#ifndef SILENT -void Log(const char *ArcName,const char *Format,...); -#endif +void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet); #ifdef SILENT -#ifdef __GNUC__ -#define Log(args...) +inline void Log(const wchar *ArcName,const wchar *fmt,...) {} #else -inline void Log(const char *a,const char *b,const char *c=NULL,const char *d=NULL) {} -#endif +void Log(const wchar *ArcName,const wchar *fmt,...); #endif #endif diff --git a/libunrar/makefile.unix b/libunrar/makefile similarity index 69% rename from libunrar/makefile.unix rename to libunrar/makefile index 78ecec0..214f87e 100644 --- a/libunrar/makefile.unix +++ b/libunrar/makefile @@ -1,15 +1,14 @@ # # Makefile for UNIX - unrar -# -# Note: you have to 'make clean' before you can build -# the sfx module -# # Linux using GCC -#CXX=g++ -#CXXFLAGS=-O2 -DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DGUI -DSILENT +CXX=c++ +CXXFLAGS=-O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else +LIBFLAGS=-fPIC +DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP STRIP=strip +AR=ar +LDFLAGS=-pthread DESTDIR=/usr # Linux using LCC @@ -17,6 +16,17 @@ DESTDIR=/usr #CXXFLAGS=-O2 #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=strip +#AR=ar +#DESTDIR=/usr + +# CYGWIN using GCC +#CXX=c++ +#CXXFLAGS=-O2 +#LIBFLAGS= +#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP +#STRIP=strip +#AR=ar +#LDFLAGS=-pthread #DESTDIR=/usr # HP UX using aCC @@ -24,6 +34,7 @@ DESTDIR=/usr #CXXFLAGS=-AA +O2 +Onolimit #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=strip +#AR=ar #DESTDIR=/usr # IRIX using GCC @@ -31,6 +42,7 @@ DESTDIR=/usr #CXXFLAGS=-O2 #DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1 #STRIP=strip +#AR=ar #DESTDIR=/usr # IRIX using MIPSPro (experimental) @@ -38,6 +50,7 @@ DESTDIR=/usr #CXXFLAGS=-O2 -mips3 -woff 1234,1156,3284 -LANG:std #DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -Dint64=int64_t #STRIP=strip +#AR=ar #DESTDIR=/usr # AIX using xlC (IBM VisualAge C++ 5.0) @@ -46,6 +59,7 @@ DESTDIR=/usr #DEFINES=-D_LARGE_FILES -D_LARGE_FILE_API #LIBS=-lbsd #STRIP=strip +#AR=ar #DESTDIR=/usr # Solaris using CC @@ -53,6 +67,7 @@ DESTDIR=/usr #CXXFLAGS=-fast -erroff=wvarhidemem #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=strip +#AR=ar #DESTDIR=/usr # Solaris using GCC (optimized for UltraSPARC 1 CPU) @@ -60,12 +75,14 @@ DESTDIR=/usr #CXXFLAGS=-O3 -mcpu=v9 -mtune=ultrasparc -m32 #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=/usr/ccs/bin/strip +#AR=/usr/ccs/bin/ar #DESTDIR=/usr # Tru64 5.1B using GCC3 #CXX=g++ #CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_XOPEN_SOURCE=500 #STRIP=strip +#AR=ar #LDFLAGS=-rpath /usr/local/gcc/lib #DESTDIR=/usr @@ -73,6 +90,7 @@ DESTDIR=/usr #CXX=cxx #CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -Dint64=long #STRIP=strip +#AR=ar #LDFLAGS= #DESTDIR=/usr @@ -80,6 +98,7 @@ DESTDIR=/usr #CXX=g++ #CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fexceptions #STRIP=strip +#AR=ar #LDFLAGS=-fexceptions #DESTDIR=/usr @@ -89,6 +108,7 @@ DESTDIR=/usr #CXXFLAGS=-O2 #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=arm-linux-strip +#AR=arm-linux-ar #LDFLAGS=-static #DESTDIR=/usr @@ -99,13 +119,14 @@ LINK=$(CXX) WHAT=UNRAR -UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o -LIB_OBJ=filestr.o scantree.o dll.o +UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o +LIB_OBJ=filestr.o scantree.o dll.o qopen.o -OBJECTS=rar.o strlist.o strfn.o pathfn.o savepos.o smallfn.o global.o file.o filefn.o filcreat.o \ +OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \ archive.o arcread.o unicode.o system.o isnt.o crypt.o crc.o rawread.o encname.o \ - resource.o match.o timefn.o rdwrfn.o consio.o options.o ulinks.o errhnd.o rarvm.o \ - rijndael.o getbits.o sha1.o extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o + resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \ + rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \ + list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o .cpp.o: $(COMPILE) -D$(WHAT) -c $< @@ -117,32 +138,37 @@ install: install-unrar uninstall: uninstall-unrar clean: - @rm -f *.o *.bak *~ + @rm -f *.bak *~ + @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ) + @rm -f unrar libunrar.* -unrar: $(OBJECTS) $(UNRAR_OBJ) +unrar: clean $(OBJECTS) $(UNRAR_OBJ) @rm -f unrar $(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) $(STRIP) unrar sfx: WHAT=SFX_MODULE -sfx: $(OBJECTS) +sfx: clean $(OBJECTS) @rm -f default.sfx $(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS) $(STRIP) default.sfx lib: WHAT=RARDLL -lib: $(OBJECTS) $(LIB_OBJ) - @rm -f libunrar.so +lib: CXXFLAGS+=$(LIBFLAGS) +lib: clean $(OBJECTS) $(LIB_OBJ) + @rm -f libunrar.* $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) + $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ) install-unrar: - install unrar $(DESTDIR)/bin + install -D unrar $(DESTDIR)/bin/unrar uninstall-unrar: rm -f $(DESTDIR)/bin/unrar install-lib: install libunrar.so $(DESTDIR)/lib + install libunrar.a $(DESTDIR)/lib uninstall-lib: rm -f $(DESTDIR)/lib/libunrar.so diff --git a/libunrar/makefile.bcc b/libunrar/makefile.bcc deleted file mode 100644 index 456f6e0..0000000 --- a/libunrar/makefile.bcc +++ /dev/null @@ -1,501 +0,0 @@ -.AUTODEPEND - -basepath = $(BASEPATHCC) -binpath = $(basepath)\bin -libpath = $(basepath)\lib -rarpath = . -incpath = $(basepath)\include;$(rarpath) - -cc = $(binpath)\bcc32 -link = $(binpath)\ilink32 - -objpath = . -guiopt = -WC -H=$(objpath)\rar.csm - -!ifndef RARDLL -!ifndef GUI -guiopt=$(guiopt) -x- -!endif -!ifdef SFX_MODULE -guiopt=$(guiopt) -x- -!endif -!endif - -!ifdef DEBUG -optdeb=-Od -k -vi- -DDEBUG -!else -# -O is not safe to use with -pr and int64 return values, so let's turn it off -optdeb=-O1 -O- -k- -#optdeb=-Ob -Oe -Og -Oi -Ol -Om -Op -OS -Ov -Z -Oc -!endif - - -optunrar=-DUNRAR -linkdest=unrar.exe - -!ifdef SFX_MODULE -optunrar=-DUNRAR -DSFX_MODULE -linkdest=sfx.exe -!endif - -linkopt = -L$(libpath) -ap -c -v -s -V4.0 -Gn -compopt = -P -c -I$(incpath) -R -v -vi -w-pch -w-par -K -f-\ - -ff- -a4 -pr -RT- $(optdeb) $(guiopt) $(optunrar) -d -w-8072 - -!ifdef RARDLL -SILENT=true -linkdest=unrar.dll -linkopt=$(linkopt) -Tpd -compopt=$(compopt) -DRARDLL -!else -linkopt=$(linkopt) -Tpe -B:0x400000 -!endif - -!ifdef SILENT -compopt=$(compopt) -DSILENT -!endif - - -rar: $(linkdest) - -Dep_SFX= \ - $(objpath)\strlist.obj\ - $(objpath)\strfn.obj\ - $(objpath)\pathfn.obj\ - $(objpath)\cmddata.obj\ - $(objpath)\consio.obj\ - $(objpath)\savepos.obj\ - $(objpath)\smallfn.obj\ - $(objpath)\file.obj\ - $(objpath)\filefn.obj\ - $(objpath)\filcreat.obj\ - $(objpath)\sha1.obj\ - $(objpath)\archive.obj\ - $(objpath)\arcread.obj\ - $(objpath)\unicode.obj\ - $(objpath)\system.obj\ - $(objpath)\isnt.obj\ - $(objpath)\crc.obj\ - $(objpath)\crypt.obj\ - $(objpath)\rijndael.obj\ - $(objpath)\rawread.obj\ - $(objpath)\encname.obj\ - $(objpath)\resource.obj\ - $(objpath)\match.obj\ - $(objpath)\find.obj\ - $(objpath)\timefn.obj\ - $(objpath)\getbits.obj\ - $(objpath)\rarvm.obj\ - $(objpath)\rdwrfn.obj\ - $(objpath)\options.obj\ - $(objpath)\ulinks.obj\ - $(objpath)\errhnd.obj\ - $(objpath)\volume.obj\ - $(objpath)\rs.obj\ - $(objpath)\recvol.obj\ - $(objpath)\extinfo.obj\ - $(objpath)\extract.obj\ - $(objpath)\unpack.obj\ - $(objpath)\rar.obj\ - $(objpath)\global.obj - -Dep_Unrar = \ - $(objpath)\filestr.obj\ - $(objpath)\scantree.obj - -Dep_Dll = \ - $(objpath)\dll.obj - -#Dep_SFXOnly = $(objpath)\rtl.obj - -!ifndef GUI -!ifndef SILENT -Dep_Console = \ - $(objpath)\list.obj -!endif -!endif - -!ifdef SFX_MODULE -Dep = $(Dep_SFX) $(Dep_SFXOnly) -!else -Dep = $(Dep_SFX) $(Dep_Unrar) -!endif - -!ifndef GUI -Dep = $(Dep) $(Dep_Console) -!endif - -!ifdef RARDLL -Dep = $(Dep) $(Dep_Dll) -!endif - -!ifdef GUI -$(linkdest) : $(Dep) - echo Done -!else -$(linkdest) : $(Dep) - $(link) @&&| - $(linkopt) + -#!ifdef SFX_MODULE -#$(objpath)\dummy.obj+ -#$(objpath)\ll.obj+ -#$(objpath)\rtl.obj+ -#!else -!ifdef RARDLL -$(libpath)\c0d32.obj+ -!else -$(libpath)\c0x32.obj+ -!endif -#!endif -$(objpath)\strlist.obj+ -$(objpath)\strfn.obj+ -$(objpath)\pathfn.obj+ -$(objpath)\savepos.obj+ -$(objpath)\smallfn.obj+ -$(objpath)\global.obj+ -$(objpath)\file.obj+ -$(objpath)\filefn.obj+ -$(objpath)\filcreat.obj+ -$(objpath)\sha1.obj+ -$(objpath)\archive.obj+ -$(objpath)\arcread.obj+ -$(objpath)\unicode.obj+ -$(objpath)\system.obj+ -$(objpath)\isnt.obj+ -$(objpath)\crc.obj+ -$(objpath)\crypt.obj+ -$(objpath)\rijndael.obj+ -$(objpath)\rawread.obj+ -$(objpath)\encname.obj+ -$(objpath)\resource.obj+ -$(objpath)\match.obj+ -$(objpath)\find.obj+ -!ifndef SFX_MODULE -$(objpath)\filestr.obj+ -$(objpath)\scantree.obj+ -!endif -$(objpath)\timefn.obj+ -$(objpath)\getbits.obj+ -$(objpath)\rarvm.obj+ -$(objpath)\rdwrfn.obj+ -$(objpath)\consio.obj+ -$(objpath)\cmddata.obj+ -$(objpath)\options.obj+ -$(objpath)\ulinks.obj+ -$(objpath)\volume.obj+ -$(objpath)\extinfo.obj+ -$(objpath)\extract.obj+ -$(objpath)\rs.obj+ -$(objpath)\recvol.obj+ -!ifndef SILENT -!ifndef GUI -$(objpath)\list.obj+ -!endif -!endif -!ifdef RARDLL -$(objpath)\dll.obj+ -!endif -$(objpath)\errhnd.obj+ -$(objpath)\unpack.obj+ -$(objpath)\rar.obj -$<,$* -$(libpath)\cw32.lib+ -$(libpath)\import32.lib -!ifdef RARDLL -$(rarpath)\dll.def -!else - -!endif -| -!endif - -$(objpath)\rar.obj : $(rarpath)\rar.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rar.cpp -| - -$(objpath)\strlist.obj : $(rarpath)\strlist.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\strlist.cpp -| - -$(objpath)\strfn.obj : $(rarpath)\strfn.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\strfn.cpp -| - -$(objpath)\pathfn.obj : $(rarpath)\pathfn.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\pathfn.cpp -| - -$(objpath)\savepos.obj : $(rarpath)\savepos.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\savepos.cpp -| - -$(objpath)\smallfn.obj : $(rarpath)\smallfn.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\smallfn.cpp -| - -$(objpath)\global.obj : $(rarpath)\global.cpp - $(cc) -q @&&| - $(compopt) -H- -o$@ $(rarpath)\global.cpp -| - -$(objpath)\file.obj : $(rarpath)\file.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\file.cpp -| - -$(objpath)\filefn.obj : $(rarpath)\filefn.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\filefn.cpp -| - -$(objpath)\filestr.obj : $(rarpath)\filestr.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\filestr.cpp -| - -$(objpath)\filcreat.obj : $(rarpath)\filcreat.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\filcreat.cpp -| - -$(objpath)\sha1.obj : $(rarpath)\sha1.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\sha1.cpp -| - -$(objpath)\ec.obj : $(rarpath)\ec.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\ec.cpp -| - -$(objpath)\av.obj : $(rarpath)\av.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\av.cpp -| - -$(objpath)\archive.obj : $(rarpath)\archive.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\archive.cpp -| - -$(objpath)\arcread.obj : $(rarpath)\arcread.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\arcread.cpp -| - -$(objpath)\unicode.obj : $(rarpath)\unicode.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\unicode.cpp -| - -$(objpath)\system.obj : $(rarpath)\system.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\system.cpp -| - -$(objpath)\isnt.obj : $(rarpath)\isnt.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\isnt.cpp -| - -$(objpath)\crc.obj : $(rarpath)\crc.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\crc.cpp -| - -$(objpath)\crypt.obj : $(rarpath)\crypt.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\crypt.cpp -| - -$(objpath)\rijndael.obj : $(rarpath)\rijndael.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rijndael.cpp -| - -$(objpath)\rawread.obj : $(rarpath)\rawread.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rawread.cpp -| - -$(objpath)\rawwrite.obj : $(rarpath)\rawwrite.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rawwrite.cpp -| - -$(objpath)\encname.obj : $(rarpath)\encname.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\encname.cpp -| - -$(objpath)\resource.obj : $(rarpath)\resource.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\resource.cpp -| - -$(objpath)\match.obj : $(rarpath)\match.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\match.cpp -| - -$(objpath)\find.obj : $(rarpath)\find.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\find.cpp -| - -$(objpath)\scantree.obj : $(rarpath)\scantree.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\scantree.cpp -| - -$(objpath)\timefn.obj : $(rarpath)\timefn.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\timefn.cpp -| - -$(objpath)\getbits.obj : $(rarpath)\getbits.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\getbits.cpp -| - -$(objpath)\rarvm.obj : $(rarpath)\rarvm.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rarvm.cpp -| - -$(objpath)\putbits.obj : $(rarpath)\putbits.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\putbits.cpp -| - -$(objpath)\pack.obj : $(rarpath)\pack.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\pack.cpp -| - -$(objpath)\packbord.obj : $(rarpath)\packbord.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\packbord.cpp -| - -$(objpath)\packanlz.obj : $(rarpath)\packanlz.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\packanlz.cpp -| - -$(objpath)\cblock.obj : $(rarpath)\cblock.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\cblock.cpp -| - -$(objpath)\add.obj : $(rarpath)\add.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\add.cpp -| - -$(objpath)\addlist.obj : $(rarpath)\addlist.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\addlist.cpp -| - -$(objpath)\procarc.obj : $(rarpath)\procarc.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\procarc.cpp -| - -$(objpath)\sfx.obj : $(rarpath)\sfx.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\sfx.cpp -| - -$(objpath)\comment.obj : $(rarpath)\comment.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\comment.cpp -| - -$(objpath)\rs.obj : $(rarpath)\rs.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rs.cpp -| - -$(objpath)\recvol.obj : $(rarpath)\recvol.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\recvol.cpp -| - -$(objpath)\repair.obj : $(rarpath)\repair.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\repair.cpp -| - -$(objpath)\rdwrfn.obj : $(rarpath)\rdwrfn.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rdwrfn.cpp -| - -$(objpath)\consio.obj : $(rarpath)\consio.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\consio.cpp -| - -$(objpath)\cmddata.obj : $(rarpath)\cmddata.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\cmddata.cpp -| - -$(objpath)\options.obj : $(rarpath)\options.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\options.cpp -| - -$(objpath)\ulinks.obj : $(rarpath)\ulinks.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\ulinks.cpp -| - -$(objpath)\errhnd.obj : $(rarpath)\errhnd.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\errhnd.cpp -| - -$(objpath)\volume.obj : $(rarpath)\volume.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\volume.cpp -| - -$(objpath)\extinfo.obj : $(rarpath)\extinfo.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\extinfo.cpp -| - - -$(objpath)\extract.obj : $(rarpath)\extract.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\extract.cpp -| - -$(objpath)\list.obj : $(rarpath)\list.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\list.cpp -| - -$(objpath)\rtl.obj : $(rarpath)\rtl.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\rtl.cpp -| - -$(objpath)\unpack.obj : $(rarpath)\unpack.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\unpack.cpp -| - -$(objpath)\dll.obj : $(rarpath)\dll.cpp - $(cc) -q @&&| - $(compopt) -o$@ $(rarpath)\dll.cpp -| \ No newline at end of file diff --git a/libunrar/makefile.cygmin b/libunrar/makefile.cygmin deleted file mode 100644 index 0d88d77..0000000 --- a/libunrar/makefile.cygmin +++ /dev/null @@ -1,54 +0,0 @@ -# -# Makefile for cygmin/mingw - unrar -# -# Note: you have to 'make clean' before you can build -# the sfx module -# - -# POSIX using Cygmin GCC 3.3.1 -#CXX = g++ -#CXXFLAGS = -O2 -Wno-deprecated -#DEFINES = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DLITTLE_ENDIAN - -# Win32 using Cygmin GCC 3.3.1 -CXX = g++ -mno-cygwin -CXXFLAGS = -O2 -Wno-deprecated -DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE - -# Win32 using Mingw32 GCC 3.3.2 -#CXX = g++ -#CXXFLAGS = -O2 -Wno-deprecated -#DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE - -########################## - -COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES) -LINK=$(CXX) - -UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o - -OBJECTS=rar.o strlist.o strfn.o pathfn.o savepos.o smallfn.o global.o \ - file.o filefn.o filcreat.o archive.o arcread.o unicode.o \ - system.o isnt.o crypt.o crc.o rawread.o encname.o \ - resource.o match.o timefn.o rdwrfn.o consio.o options.o \ - ulinks.o errhnd.o rarvm.o rijndael.o getbits.o sha1.o \ - extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o - -.cpp.o: - $(COMPILE) -D$(WHAT) -c $< - -all: unrar - -clean: - @rm -f *.o *.bak *~ - -unrar: WHAT=UNRAR -unrar: $(OBJECTS) $(UNRAR_OBJ) - @rm -f makeunrar - $(LINK) -Wl,-s -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) - -sfx: WHAT=SFX_MODULE -sfx: $(OBJECTS) - @rm -f default.sfx - $(LINK) -Wl,-s -o default.sfx $(LDFLAGS) $(OBJECTS) -DSFX_MODULE - diff --git a/libunrar/makefile.dj b/libunrar/makefile.dj deleted file mode 100644 index e56d5e2..0000000 --- a/libunrar/makefile.dj +++ /dev/null @@ -1,50 +0,0 @@ -# -# Makefile for DJGPP - unrar -# -# Note: you have to 'make clean' before you can build -# the sfx module -# - -# DOS32 using DJGPP 2.03 Patchlevel 2 and GCC 2.95.3 -CXX = gpp -CXXFLAGS = -O2 -Wno-deprecated -#DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE - -########################## - -.PHONY: all clean veryclean - -COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES) -LINK=$(CXX) - -UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o - -OBJECTS=rar.o strlist.o strfn.o pathfn.o savepos.o smallfn.o global.o \ - file.o filefn.o filcreat.o archive.o arcread.o unicode.o \ - system.o isnt.o crypt.o crc.o rawread.o encname.o \ - resource.o match.o timefn.o rdwrfn.o consio.o options.o \ - ulinks.o errhnd.o rarvm.o rijndael.o getbits.o sha1.o \ - extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o - -.cpp.o: - $(COMPILE) -D$(WHAT) -c $< - -all: unrar - -unrar: WHAT=UNRAR -unrar: $(OBJECTS) $(UNRAR_OBJ) - $(LINK) -Wl,-s -o unrar.exe $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) - exe2coff unrar.exe - cp -u $(DJDIR)/bin/cwsdstub.exe . - copy /b cwsdstub.exe+unrar unrar.exe - -upx --ultra-brute unrar.exe - -sfx: WHAT=SFX_MODULE -sfx: $(OBJECTS) - $(LINK) -Wl,-s -o default.sfx $(LDFLAGS) $(OBJECTS) -DSFX_MODULE - -clean: - $(RM) $(OBJECTS) $(UNRAR_OBJ) - -veryclean: clean - $(RM) unrar.exe default.sfx cwsdstub.exe unrar diff --git a/libunrar/makefile.dmc b/libunrar/makefile.dmc deleted file mode 100644 index 46bb8df..0000000 --- a/libunrar/makefile.dmc +++ /dev/null @@ -1,54 +0,0 @@ -# Makefile for Digital Mars C++ Compiler -# http://www.rarlab.com -# http://www.digitalmars.com -# -# DEFINES: UNRAR RARDLL GUI SFX_MODULE SILENT - -NAME = unrar -EXT = exe - -CPP = dmc - -LINK = link - -# -------------- -# Release Build -# -------------- -DEFINES = -DNDEBUG -D_MSC_VER -DUNRAR -CPPFLAGS = -o+all -ff -Nc -g- -Ae -LNKFLAGS = /EXETYPE:NT /MACHINE:i386 /SUBSYSTEM:CONSOLE /NOLOGO /NODEBUG /NOCODEVIEW /PACKFUNCTIONS - -# -------------- -# Debug Build -# -------------- -#DEFINES = -D_DEBUG -D_MSC_VER -DUNRAR -#CPPFLAGS = -o+none -Nc -S -gf -Ae -#LNKFLAGS = /EXETYPE:NT /MACHINE:i386 /SUBSYSTEM:CONSOLE /NOLOGO /DEBUG - -OBJ = rar.obj strlist.obj strfn.obj pathfn.obj savepos.obj smallfn.o global.obj \ - file.obj filefn.obj filcreat.obj archive.obj arcread.obj unicode.obj \ - system.obj isnt.obj crypt.obj crc.obj rawread.obj encname.obj \ - resource.obj match.obj timefn.obj rdwrfn.obj consio.obj options.obj \ - ulinks.obj errhnd.obj rarvm.obj rijndael.obj getbits.obj sha1.obj \ - extinfo.obj extract.obj volume.obj find.obj unpack.obj cmddata.obj \ - filestr.obj recvol.obj rs.obj scantree.obj \ - list.obj \ -# dll.obj \ - -LIB = kernel32.lib+user32.lib+advapi32.lib - -#DEF = dll.def - -link: $(OBJ) - $(LINK) $(LNKFLAGS) $(OBJ), $(NAME).$(EXT), $(NAME).map, $(LIB), $(DEF) - -.c.obj: - $(CPP) $(CPPFLAGS) $(DEFINES) -c $< -o $@ - -.cpp.obj: - $(CPP) $(CPPFLAGS) $(DEFINES) -c $< -o $@ - -clean: - del $(OBJ) - del $(NAME).$(EXT) - del $(NAME).map diff --git a/libunrar/makefile.msc b/libunrar/makefile.msc deleted file mode 100644 index a5a9255..0000000 --- a/libunrar/makefile.msc +++ /dev/null @@ -1,564 +0,0 @@ -# Microsoft Developer Studio Generated NMAKE File, Based on unrar.dsp -!IF "$(CFG)" == "" -CFG=unrar - Win32 Release -!MESSAGE No configuration specified. Defaulting to unrar - Win32 Release. -!ENDIF - -!IF "$(CFG)" != "unrar - Win32 Release" && "$(CFG)" != "unrar - Win32 Debug" -!MESSAGE Invalid configuration "$(CFG)" specified. -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "unrar.mak" CFG="unrar - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "unrar - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "unrar - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE -!ERROR An invalid configuration is specified. -!ENDIF - -!IF "$(OS)" == "Windows_NT" -NULL= -!ELSE -NULL=nul -!ENDIF - -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "unrar - Win32 Release" - -OUTDIR=.\Release -INTDIR=.\Release -# Begin Custom Macros -OutDir=.\Release -# End Custom Macros - -ALL : "$(OUTDIR)\unrar.exe" - - -CLEAN : - -@erase "$(INTDIR)\archive.obj" - -@erase "$(INTDIR)\arcread.obj" - -@erase "$(INTDIR)\cmddata.obj" - -@erase "$(INTDIR)\consio.obj" - -@erase "$(INTDIR)\crc.obj" - -@erase "$(INTDIR)\crypt.obj" - -@erase "$(INTDIR)\encname.obj" - -@erase "$(INTDIR)\errhnd.obj" - -@erase "$(INTDIR)\extinfo.obj" - -@erase "$(INTDIR)\extract.obj" - -@erase "$(INTDIR)\filcreat.obj" - -@erase "$(INTDIR)\file.obj" - -@erase "$(INTDIR)\filefn.obj" - -@erase "$(INTDIR)\filestr.obj" - -@erase "$(INTDIR)\find.obj" - -@erase "$(INTDIR)\getbits.obj" - -@erase "$(INTDIR)\global.obj" - -@erase "$(INTDIR)\isnt.obj" - -@erase "$(INTDIR)\list.obj" - -@erase "$(INTDIR)\match.obj" - -@erase "$(INTDIR)\options.obj" - -@erase "$(INTDIR)\pathfn.obj" - -@erase "$(INTDIR)\rar.obj" - -@erase "$(INTDIR)\rarvm.obj" - -@erase "$(INTDIR)\rawread.obj" - -@erase "$(INTDIR)\rdwrfn.obj" - -@erase "$(INTDIR)\recvol.obj" - -@erase "$(INTDIR)\resource.obj" - -@erase "$(INTDIR)\rijndael.obj" - -@erase "$(INTDIR)\rs.obj" - -@erase "$(INTDIR)\savepos.obj" - -@erase "$(INTDIR)\smallfn.obj" - -@erase "$(INTDIR)\scantree.obj" - -@erase "$(INTDIR)\sha1.obj" - -@erase "$(INTDIR)\strfn.obj" - -@erase "$(INTDIR)\strlist.obj" - -@erase "$(INTDIR)\system.obj" - -@erase "$(INTDIR)\timefn.obj" - -@erase "$(INTDIR)\ulinks.obj" - -@erase "$(INTDIR)\unicode.obj" - -@erase "$(INTDIR)\unpack.obj" - -@erase "$(INTDIR)\vc60.idb" - -@erase "$(INTDIR)\volume.obj" - -@erase "$(OUTDIR)\unrar.exe" - -@erase "$(OUTDIR)\unrar.map" - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP_PROJ=/nologo /W3 /EHsc /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "UNRAR" /D _CRT_SECURE_NO_WARNINGS /Fp"$(INTDIR)\unrar.pch" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\unrar.bsc" -BSC32_SBRS= \ - -LINK32=link.exe -LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\unrar.pdb" /map:"$(INTDIR)\unrar.map" /machine:I386 /out:"$(OUTDIR)\unrar.exe" -LINK32_OBJS= \ - "$(INTDIR)\rar.obj" \ - "$(INTDIR)\strlist.obj" \ - "$(INTDIR)\strfn.obj" \ - "$(INTDIR)\pathfn.obj" \ - "$(INTDIR)\savepos.obj" \ - "$(INTDIR)\smallfn.obj" \ - "$(INTDIR)\global.obj" \ - "$(INTDIR)\file.obj" \ - "$(INTDIR)\filefn.obj" \ - "$(INTDIR)\filcreat.obj" \ - "$(INTDIR)\archive.obj" \ - "$(INTDIR)\arcread.obj" \ - "$(INTDIR)\unicode.obj" \ - "$(INTDIR)\system.obj" \ - "$(INTDIR)\isnt.obj" \ - "$(INTDIR)\crypt.obj" \ - "$(INTDIR)\crc.obj" \ - "$(INTDIR)\rawread.obj" \ - "$(INTDIR)\encname.obj" \ - "$(INTDIR)\resource.obj" \ - "$(INTDIR)\match.obj" \ - "$(INTDIR)\timefn.obj" \ - "$(INTDIR)\rdwrfn.obj" \ - "$(INTDIR)\consio.obj" \ - "$(INTDIR)\options.obj" \ - "$(INTDIR)\ulinks.obj" \ - "$(INTDIR)\errhnd.obj" \ - "$(INTDIR)\rarvm.obj" \ - "$(INTDIR)\rijndael.obj" \ - "$(INTDIR)\getbits.obj" \ - "$(INTDIR)\sha1.obj" \ - "$(INTDIR)\extinfo.obj" \ - "$(INTDIR)\extract.obj" \ - "$(INTDIR)\volume.obj" \ - "$(INTDIR)\list.obj" \ - "$(INTDIR)\find.obj" \ - "$(INTDIR)\unpack.obj" \ - "$(INTDIR)\cmddata.obj" \ - "$(INTDIR)\filestr.obj" \ - "$(INTDIR)\recvol.obj" \ - "$(INTDIR)\rs.obj" \ - "$(INTDIR)\scantree.obj" - -"$(OUTDIR)\unrar.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) - $(LINK32) @<< - $(LINK32_FLAGS) $(LINK32_OBJS) -<< - -!ELSEIF "$(CFG)" == "unrar - Win32 Debug" - -OUTDIR=.\Debug -INTDIR=.\Debug -# Begin Custom Macros -OutDir=.\Debug -# End Custom Macros - -ALL : "$(OUTDIR)\unrar.exe" - - -CLEAN : - -@erase "$(INTDIR)\archive.obj" - -@erase "$(INTDIR)\arcread.obj" - -@erase "$(INTDIR)\cmddata.obj" - -@erase "$(INTDIR)\consio.obj" - -@erase "$(INTDIR)\crc.obj" - -@erase "$(INTDIR)\crypt.obj" - -@erase "$(INTDIR)\encname.obj" - -@erase "$(INTDIR)\errhnd.obj" - -@erase "$(INTDIR)\extinfo.obj" - -@erase "$(INTDIR)\extract.obj" - -@erase "$(INTDIR)\filcreat.obj" - -@erase "$(INTDIR)\file.obj" - -@erase "$(INTDIR)\filefn.obj" - -@erase "$(INTDIR)\filestr.obj" - -@erase "$(INTDIR)\find.obj" - -@erase "$(INTDIR)\getbits.obj" - -@erase "$(INTDIR)\global.obj" - -@erase "$(INTDIR)\isnt.obj" - -@erase "$(INTDIR)\list.obj" - -@erase "$(INTDIR)\match.obj" - -@erase "$(INTDIR)\options.obj" - -@erase "$(INTDIR)\pathfn.obj" - -@erase "$(INTDIR)\rar.obj" - -@erase "$(INTDIR)\rarvm.obj" - -@erase "$(INTDIR)\rawread.obj" - -@erase "$(INTDIR)\rdwrfn.obj" - -@erase "$(INTDIR)\recvol.obj" - -@erase "$(INTDIR)\resource.obj" - -@erase "$(INTDIR)\rijndael.obj" - -@erase "$(INTDIR)\rs.obj" - -@erase "$(INTDIR)\savepos.obj" - -@erase "$(INTDIR)\smallfn.obj" - -@erase "$(INTDIR)\scantree.obj" - -@erase "$(INTDIR)\sha1.obj" - -@erase "$(INTDIR)\strfn.obj" - -@erase "$(INTDIR)\strlist.obj" - -@erase "$(INTDIR)\system.obj" - -@erase "$(INTDIR)\timefn.obj" - -@erase "$(INTDIR)\ulinks.obj" - -@erase "$(INTDIR)\unicode.obj" - -@erase "$(INTDIR)\unpack.obj" - -@erase "$(INTDIR)\vc60.idb" - -@erase "$(INTDIR)\vc60.pdb" - -@erase "$(INTDIR)\volume.obj" - -@erase "$(OUTDIR)\unrar.exe" - -@erase "$(OUTDIR)\unrar.ilk" - -@erase "$(OUTDIR)\unrar.pdb" - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP_PROJ=/nologo /W3 /Gm /EHsc /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "UNRAR" /D _CRT_SECURE_NO_WARNINGS /Fp"$(INTDIR)\unrar.pch" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\unrar.bsc" -BSC32_SBRS= \ - -LINK32=link.exe -LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\unrar.pdb" /debug /machine:I386 /out:"$(OUTDIR)\unrar.exe" /pdbtype:sept -LINK32_OBJS= \ - "$(INTDIR)\rar.obj" \ - "$(INTDIR)\strlist.obj" \ - "$(INTDIR)\strfn.obj" \ - "$(INTDIR)\pathfn.obj" \ - "$(INTDIR)\savepos.obj" \ - "$(INTDIR)\smallfn.obj" \ - "$(INTDIR)\global.obj" \ - "$(INTDIR)\file.obj" \ - "$(INTDIR)\filefn.obj" \ - "$(INTDIR)\filcreat.obj" \ - "$(INTDIR)\archive.obj" \ - "$(INTDIR)\arcread.obj" \ - "$(INTDIR)\unicode.obj" \ - "$(INTDIR)\system.obj" \ - "$(INTDIR)\isnt.obj" \ - "$(INTDIR)\crypt.obj" \ - "$(INTDIR)\crc.obj" \ - "$(INTDIR)\rawread.obj" \ - "$(INTDIR)\encname.obj" \ - "$(INTDIR)\resource.obj" \ - "$(INTDIR)\match.obj" \ - "$(INTDIR)\timefn.obj" \ - "$(INTDIR)\rdwrfn.obj" \ - "$(INTDIR)\consio.obj" \ - "$(INTDIR)\options.obj" \ - "$(INTDIR)\ulinks.obj" \ - "$(INTDIR)\errhnd.obj" \ - "$(INTDIR)\rarvm.obj" \ - "$(INTDIR)\rijndael.obj" \ - "$(INTDIR)\getbits.obj" \ - "$(INTDIR)\sha1.obj" \ - "$(INTDIR)\extinfo.obj" \ - "$(INTDIR)\extract.obj" \ - "$(INTDIR)\volume.obj" \ - "$(INTDIR)\list.obj" \ - "$(INTDIR)\find.obj" \ - "$(INTDIR)\unpack.obj" \ - "$(INTDIR)\cmddata.obj" \ - "$(INTDIR)\filestr.obj" \ - "$(INTDIR)\recvol.obj" \ - "$(INTDIR)\rs.obj" \ - "$(INTDIR)\scantree.obj" - -"$(OUTDIR)\unrar.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) - $(LINK32) @<< - $(LINK32_FLAGS) $(LINK32_OBJS) -<< - -!ENDIF - -.c{$(INTDIR)}.obj:: - $(CPP) @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.obj:: - $(CPP) @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.obj:: - $(CPP) @<< - $(CPP_PROJ) $< -<< - -.c{$(INTDIR)}.sbr:: - $(CPP) @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.sbr:: - $(CPP) @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.sbr:: - $(CPP) @<< - $(CPP_PROJ) $< -<< - - -!IF "$(NO_EXTERNAL_DEPS)" != "1" -!IF EXISTS("msc.dep") -!INCLUDE "msc.dep" -!ELSE -!MESSAGE Warning: cannot find "msc.dep" -!ENDIF -!ENDIF - - -!IF "$(CFG)" == "unrar - Win32 Release" || "$(CFG)" == "unrar - Win32 Debug" -SOURCE=.\archive.cpp - -"$(INTDIR)\archive.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\arcread.cpp - -"$(INTDIR)\arcread.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\cmddata.cpp - -"$(INTDIR)\cmddata.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\consio.cpp - -"$(INTDIR)\consio.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\crc.cpp - -"$(INTDIR)\crc.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\crypt.cpp - -"$(INTDIR)\crypt.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\encname.cpp - -"$(INTDIR)\encname.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\errhnd.cpp - -"$(INTDIR)\errhnd.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\extinfo.cpp - -"$(INTDIR)\extinfo.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\extract.cpp - -"$(INTDIR)\extract.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\filcreat.cpp - -"$(INTDIR)\filcreat.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\file.cpp - -"$(INTDIR)\file.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\filefn.cpp - -"$(INTDIR)\filefn.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\filestr.cpp - -"$(INTDIR)\filestr.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\find.cpp - -"$(INTDIR)\find.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\getbits.cpp - -"$(INTDIR)\getbits.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\global.cpp - -"$(INTDIR)\global.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\isnt.cpp - -"$(INTDIR)\isnt.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\list.cpp - -"$(INTDIR)\list.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\match.cpp - -"$(INTDIR)\match.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\options.cpp - -"$(INTDIR)\options.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\pathfn.cpp - -"$(INTDIR)\pathfn.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\rar.cpp - -"$(INTDIR)\rar.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\rarvm.cpp - -"$(INTDIR)\rarvm.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\rawread.cpp - -"$(INTDIR)\rawread.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\rdwrfn.cpp - -"$(INTDIR)\rdwrfn.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\recvol.cpp - -"$(INTDIR)\recvol.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\resource.cpp - -"$(INTDIR)\resource.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\rijndael.cpp - -"$(INTDIR)\rijndael.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\rs.cpp - -"$(INTDIR)\rs.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\savepos.cpp - -"$(INTDIR)\savepos.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\smallfn.cpp - -"$(INTDIR)\smallfn.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\scantree.cpp - -"$(INTDIR)\scantree.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\sha1.cpp - -"$(INTDIR)\sha1.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\strfn.cpp - -"$(INTDIR)\strfn.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\strlist.cpp - -"$(INTDIR)\strlist.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\system.cpp - -"$(INTDIR)\system.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\timefn.cpp - -"$(INTDIR)\timefn.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\ulinks.cpp - -"$(INTDIR)\ulinks.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\unicode.cpp - -"$(INTDIR)\unicode.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\unpack.cpp - -"$(INTDIR)\unpack.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=.\volume.cpp - -"$(INTDIR)\volume.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - - -!ENDIF - diff --git a/libunrar/match.cpp b/libunrar/match.cpp index bd1c02f..ec88fa6 100644 --- a/libunrar/match.cpp +++ b/libunrar/match.cpp @@ -1,183 +1,82 @@ #include "rar.hpp" -static bool match(char *pattern,char *string,bool ForceCase); -static bool match(wchar *pattern,wchar *string,bool ForceCase); - -static int mstricompc(const char *Str1,const char *Str2,bool ForceCase); -static int mstricompcw(const wchar *Str1,const wchar *Str2,bool ForceCase); -static int mstrnicompc(const char *Str1,const char *Str2,size_t N,bool ForceCase); -static int mstrnicompcw(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase); - -inline uint toupperc(byte ch,bool ForceCase) -{ - if (ForceCase) - return(ch); -#ifdef _WIN_32 - return((uint)(LPARAM)CharUpper((LPTSTR)(ch))); -#elif defined(_UNIX) - return(ch); -#else - return(toupper(ch)); -#endif -} - +static bool match(const wchar *pattern,const wchar *string,bool ForceCase); +static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase); +static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase); inline uint touppercw(uint ch,bool ForceCase) { if (ForceCase) - return(ch); + return ch; #if defined(_UNIX) - return(ch); + return ch; #else - return(toupperw(ch)); + return toupperw(ch); #endif } -bool CmpName(char *Wildcard,char *Name,int CmpPath) +bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode) { - bool ForceCase=(CmpPath&MATCH_FORCECASESENSITIVE)!=0; + bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0; - CmpPath&=MATCH_MODEMASK; - - if (CmpPath!=MATCH_NAMES) + CmpMode&=MATCH_MODEMASK; + + if (CmpMode!=MATCH_NAMES) { - size_t WildLength=strlen(Wildcard); - if (CmpPath!=MATCH_EXACTPATH && mstrnicompc(Wildcard,Name,WildLength,ForceCase)==0) - { - char NextCh=Name[WildLength]; - if (NextCh=='\\' || NextCh=='/' || NextCh==0) - return(true); - } - char Path1[NM],Path2[NM]; - GetFilePath(Wildcard,Path1,ASIZE(Path1)); - GetFilePath(Name,Path2,ASIZE(Path1)); - if (mstricompc(Wildcard,Path2,ForceCase)==0) - return(true); - if ((CmpPath==MATCH_PATH || CmpPath==MATCH_EXACTPATH) && mstricompc(Path1,Path2,ForceCase)!=0) - return(false); - if (CmpPath==MATCH_SUBPATH || CmpPath==MATCH_WILDSUBPATH) - if (IsWildcard(Path1)) - return(match(Wildcard,Name,ForceCase)); - else - if (CmpPath==MATCH_SUBPATH || IsWildcard(Wildcard)) - { - if (*Path1 && mstrnicompc(Path1,Path2,strlen(Path1),ForceCase)!=0) - return(false); - } - else - if (mstricompc(Path1,Path2,ForceCase)!=0) - return(false); - } - char *Name1=PointToName(Wildcard); - char *Name2=PointToName(Name); - - // always return false for RAR temporary files to exclude them - // from archiving operations - if (mstrnicompc("__rar_",Name2,6,false)==0) - return(false); - - return(match(Name1,Name2,ForceCase)); -} - - -#ifndef SFX_MODULE -bool CmpName(wchar *Wildcard,wchar *Name,int CmpPath) -{ - bool ForceCase=(CmpPath&MATCH_FORCECASESENSITIVE)!=0; - - CmpPath&=MATCH_MODEMASK; - - if (CmpPath!=MATCH_NAMES) - { - size_t WildLength=strlenw(Wildcard); - if (CmpPath!=MATCH_EXACTPATH && mstrnicompcw(Wildcard,Name,WildLength,ForceCase)==0) + size_t WildLength=wcslen(Wildcard); + if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH && CmpMode!=MATCH_ALLWILD && + mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0) { + // For all modes except MATCH_NAMES, MATCH_EXACT, MATCH_EXACTPATH, MATCH_ALLWILD, + // "path1" mask must match "path1\path2\filename.ext" and "path1" names. wchar NextCh=Name[WildLength]; if (NextCh==L'\\' || NextCh==L'/' || NextCh==0) return(true); } + + // Nothing more to compare for MATCH_SUBPATHONLY. + if (CmpMode==MATCH_SUBPATHONLY) + return(false); + wchar Path1[NM],Path2[NM]; GetFilePath(Wildcard,Path1,ASIZE(Path1)); GetFilePath(Name,Path2,ASIZE(Path2)); - if ((CmpPath==MATCH_PATH || CmpPath==MATCH_EXACTPATH) && mstricompcw(Path1,Path2,ForceCase)!=0) + + if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) && + mwcsicompc(Path1,Path2,ForceCase)!=0) return(false); - if (CmpPath==MATCH_SUBPATH || CmpPath==MATCH_WILDSUBPATH) - if (IsWildcard(NULL,Path1)) + if (CmpMode==MATCH_ALLWILD) + return match(Wildcard,Name,ForceCase); + if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH) + if (IsWildcard(Path1)) return(match(Wildcard,Name,ForceCase)); else - if (CmpPath==MATCH_SUBPATH || IsWildcard(NULL,Wildcard)) + if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard)) { - if (*Path1 && mstrnicompcw(Path1,Path2,strlenw(Path1),ForceCase)!=0) + if (*Path1 && mwcsnicompc(Path1,Path2,wcslen(Path1),ForceCase)!=0) return(false); } else - if (mstricompcw(Path1,Path2,ForceCase)!=0) + if (mwcsicompc(Path1,Path2,ForceCase)!=0) return(false); } wchar *Name1=PointToName(Wildcard); wchar *Name2=PointToName(Name); - // always return false for RAR temporary files to exclude them - // from archiving operations - if (mstrnicompcw(L"__rar_",Name2,6,false)==0) - return(false); + // Always return false for RAR temporary files to exclude them + // from archiving operations. +// if (mwcsnicompc(L"__rar_",Name2,6,false)==0) +// return(false); + + if (CmpMode==MATCH_EXACT) + return(mwcsicompc(Name1,Name2,ForceCase)==0); return(match(Name1,Name2,ForceCase)); } -#endif -bool match(char *pattern,char *string,bool ForceCase) -{ - for (;; ++string) - { - char stringc=toupperc(*string,ForceCase); - char patternc=toupperc(*pattern++,ForceCase); - switch (patternc) - { - case 0: - return(stringc==0); - case '?': - if (stringc == 0) - return(false); - break; - case '*': - if (*pattern==0) - return(true); - if (*pattern=='.') - { - if (pattern[1]=='*' && pattern[2]==0) - return(true); - char *dot=strchr(string,'.'); - if (pattern[1]==0) - return (dot==NULL || dot[1]==0); - if (dot!=NULL) - { - string=dot; - if (strpbrk(pattern,"*?")==NULL && strchr(string+1,'.')==NULL) - return(mstricompc(pattern+1,string+1,ForceCase)==0); - } - } - - while (*string) - if (match(pattern,string++,ForceCase)) - return(true); - return(false); - default: - if (patternc != stringc) - if (patternc=='.' && stringc==0) - return(match(pattern,string,ForceCase)); - else - return(false); - break; - } - } -} - - -#ifndef SFX_MODULE -bool match(wchar *pattern,wchar *string,bool ForceCase) +bool match(const wchar *pattern,const wchar *string,bool ForceCase) { for (;; ++string) { @@ -198,14 +97,14 @@ bool match(wchar *pattern,wchar *string,bool ForceCase) { if (pattern[1]=='*' && pattern[2]==0) return(true); - wchar *dot=strchrw(string,'.'); + const wchar *dot=wcschr(string,'.'); if (pattern[1]==0) return (dot==NULL || dot[1]==0); if (dot!=NULL) { string=dot; - if (strpbrkw(pattern,L"*?")==NULL && strchrw(string+1,'.')==NULL) - return(mstricompcw(pattern+1,string+1,ForceCase)==0); + if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL) + return(mwcsicompc(pattern+1,string+1,ForceCase)==0); } } @@ -215,56 +114,34 @@ bool match(wchar *pattern,wchar *string,bool ForceCase) return(false); default: if (patternc != stringc) - if (patternc=='.' && stringc==0) + { + // Allow "name." mask match "name" and "name.\" match "name\". + if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.')) return(match(pattern,string,ForceCase)); else return(false); + } break; } } } -#endif -int mstricompc(const char *Str1,const char *Str2,bool ForceCase) +int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase) { if (ForceCase) - return(strcmp(Str1,Str2)); - return(stricompc(Str1,Str2)); + return wcscmp(Str1,Str2); + return wcsicompc(Str1,Str2); } -#ifndef SFX_MODULE -int mstricompcw(const wchar *Str1,const wchar *Str2,bool ForceCase) +int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase) { if (ForceCase) - return(strcmpw(Str1,Str2)); - return(stricompcw(Str1,Str2)); -} -#endif - - -int mstrnicompc(const char *Str1,const char *Str2,size_t N,bool ForceCase) -{ - if (ForceCase) - return(strncmp(Str1,Str2,N)); + return wcsncmp(Str1,Str2,N); #if defined(_UNIX) - return(strncmp(Str1,Str2,N)); + return wcsncmp(Str1,Str2,N); #else - return(strnicomp(Str1,Str2,N)); + return wcsnicomp(Str1,Str2,N); #endif } - - -#ifndef SFX_MODULE -int mstrnicompcw(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase) -{ - if (ForceCase) - return(strncmpw(Str1,Str2,N)); -#if defined(_UNIX) - return(strncmpw(Str1,Str2,N)); -#else - return(strnicmpw(Str1,Str2,N)); -#endif -} -#endif diff --git a/libunrar/match.hpp b/libunrar/match.hpp index 0e43514..1e65a3c 100644 --- a/libunrar/match.hpp +++ b/libunrar/match.hpp @@ -2,26 +2,37 @@ #define _RAR_MATCH_ enum { - MATCH_NAMES, // Compare names only. + MATCH_NAMES, // Paths are ignored. + // Compares names only using wildcards. - MATCH_PATH, // Compares names and paths. Both must match exactly. - // Unlike MATCH_EXACTPATH, also matches names if - // mask contains path only and this path is a part - // of name path. + MATCH_SUBPATHONLY, // Paths must match either exactly or path in wildcard + // must be present in the beginning of file path. + // For example, "c:\path1\*" or "c:\path1" will match + // "c:\path1\path2\file". + // Names are not compared. - MATCH_EXACTPATH, // Compares names and paths. Both must match exactly. + MATCH_EXACT, // Paths must match exactly. + // Names must match exactly. + + MATCH_ALLWILD, // Paths and names are compared using wildcards. + // Unlike MATCH_SUBPATH, paths do not match subdirs + // unless a wildcard tells so. + + MATCH_EXACTPATH, // Paths must match exactly. + // Names are compared using wildcards. MATCH_SUBPATH, // Names must be the same, but path in mask is allowed - // to be only a part of name path. + // to be only a part of name path. In other words, + // we match all files matching the file mask + // in current folder and subfolders. - MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if mask contains wildcards - // and as MATCH_PATH otherwise. + MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if file mask contains + // wildcards and as MATCH_EXACTPATH otherwise. }; #define MATCH_MODEMASK 0x0000ffff #define MATCH_FORCECASESENSITIVE 0x80000000 -bool CmpName(char *Wildcard,char *Name,int CmpPath); -bool CmpName(wchar *Wildcard,wchar *Name,int CmpPath); +bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode); #endif diff --git a/libunrar/model.cpp b/libunrar/model.cpp index 5041837..83391c5 100644 --- a/libunrar/model.cpp +++ b/libunrar/model.cpp @@ -5,10 +5,17 @@ * Contents: model description and encoding/decoding routines * ****************************************************************************/ -inline PPM_CONTEXT* PPM_CONTEXT::createChild(ModelPPM *Model,STATE* pStats, - STATE& FirstState) +static const int MAX_O=64; /* maximum allowed model order */ +const uint TOP=1 << 24, BOT=1 << 15; + +template +inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; } + + +inline RARPPM_CONTEXT* RARPPM_CONTEXT::createChild(ModelPPM *Model,RARPPM_STATE* pStats, + RARPPM_STATE& FirstState) { - PPM_CONTEXT* pc = (PPM_CONTEXT*) Model->SubAlloc.AllocContext(); + RARPPM_CONTEXT* pc = (RARPPM_CONTEXT*) Model->SubAlloc.AllocContext(); if ( pc ) { pc->NumStats=1; @@ -34,11 +41,15 @@ void ModelPPM::RestartModelRare() memset(CharMask,0,sizeof(CharMask)); SubAlloc.InitSubAllocator(); InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1; - MinContext = MaxContext = (PPM_CONTEXT*) SubAlloc.AllocContext(); + MinContext = MaxContext = (RARPPM_CONTEXT*) SubAlloc.AllocContext(); + if (MinContext == NULL) + throw std::bad_alloc(); MinContext->Suffix=NULL; OrderFall=MaxOrder; MinContext->U.SummFreq=(MinContext->NumStats=256)+1; - FoundState=MinContext->U.Stats=(STATE*)SubAlloc.AllocUnits(256/2); + FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2); + if (FoundState == NULL) + throw std::bad_alloc(); for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++) { MinContext->U.Stats[i].Symbol=i; @@ -105,10 +116,10 @@ void ModelPPM::StartModelRare(int MaxOrder) } -void PPM_CONTEXT::rescale(ModelPPM *Model) +void RARPPM_CONTEXT::rescale(ModelPPM *Model) { int OldNS=NumStats, i=NumStats-1, Adder, EscFreq; - STATE* p1, * p; + RARPPM_STATE* p1, * p; for (p=Model->FoundState;p != U.Stats;p--) _PPMD_SWAP(p[0],p[-1]); U.Stats->Freq += 4; @@ -122,7 +133,7 @@ void PPM_CONTEXT::rescale(ModelPPM *Model) U.SummFreq += (p->Freq=(p->Freq+Adder) >> 1); if (p[0].Freq > p[-1].Freq) { - STATE tmp=*(p1=p); + RARPPM_STATE tmp=*(p1=p); do { p1[0]=p1[-1]; @@ -139,7 +150,7 @@ void PPM_CONTEXT::rescale(ModelPPM *Model) EscFreq += i; if ((NumStats -= i) == 1) { - STATE tmp=*U.Stats; + RARPPM_STATE tmp=*U.Stats; do { tmp.Freq-=(tmp.Freq >> 1); @@ -152,19 +163,16 @@ void PPM_CONTEXT::rescale(ModelPPM *Model) U.SummFreq += (EscFreq -= (EscFreq >> 1)); int n0=(OldNS+1) >> 1, n1=(NumStats+1) >> 1; if (n0 != n1) - U.Stats = (STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1); + U.Stats = (RARPPM_STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1); Model->FoundState=U.Stats; } -inline PPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,STATE* p1) +inline RARPPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,RARPPM_STATE* p1) { -#ifdef __ICL - static -#endif - STATE UpState; - PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; - STATE * p, * ps[MAX_O], ** pps=ps; + RARPPM_STATE UpState; + RARPPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; + RARPPM_STATE * p, * ps[MAX_O], ** pps=ps; if ( !Skip ) { *pps++ = FoundState; @@ -195,14 +203,21 @@ LOOP_ENTRY: { pc=p->Successor; break; + } + // We ensure that PPM order input parameter does not exceed MAX_O (64), + // so we do not really need this check and added it for extra safety. + // See CVE-2017-17969 for details. + if (pps>=ps+ASIZE(ps)) + return NULL; + *pps++ = p; } while ( pc->Suffix ); NO_LOOP: if (pps == ps) return pc; UpState.Symbol=*(byte*) UpBranch; - UpState.Successor=(PPM_CONTEXT*) (((byte*) UpBranch)+1); + UpState.Successor=(RARPPM_CONTEXT*) (((byte*) UpBranch)+1); if (pc->NumStats != 1) { if ((byte*) pc <= SubAlloc.pText) @@ -230,8 +245,8 @@ NO_LOOP: inline void ModelPPM::UpdateModel() { - STATE fs = *FoundState, *p = NULL; - PPM_CONTEXT *pc, *Successor; + RARPPM_STATE fs = *FoundState, *p = NULL; + RARPPM_CONTEXT *pc, *Successor; uint ns1, ns, cf, sf, s0; if (fs.Freq < MAX_FREQ/4 && (pc=MinContext->Suffix) != NULL) { @@ -269,7 +284,7 @@ inline void ModelPPM::UpdateModel() return; } *SubAlloc.pText++ = fs.Symbol; - Successor = (PPM_CONTEXT*) SubAlloc.pText; + Successor = (RARPPM_CONTEXT*) SubAlloc.pText; if (SubAlloc.pText >= SubAlloc.FakeUnitsStart) goto RESTART_MODEL; if ( fs.Successor ) @@ -295,7 +310,7 @@ inline void ModelPPM::UpdateModel() { if ((ns1 & 1) == 0) { - pc->U.Stats=(STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1); + pc->U.Stats=(RARPPM_STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1); if ( !pc->U.Stats ) goto RESTART_MODEL; } @@ -303,7 +318,7 @@ inline void ModelPPM::UpdateModel() } else { - p=(STATE*) SubAlloc.AllocUnits(1); + p=(RARPPM_STATE*) SubAlloc.AllocUnits(1); if ( !p ) goto RESTART_MODEL; *p=pc->OneState; @@ -346,9 +361,9 @@ static const byte ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, -inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) +inline void RARPPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) { - STATE& rs=OneState; + RARPPM_STATE& rs=OneState; Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol]; ushort& bs=Model->BinSumm[rs.Freq-1][Model->PrevSuccess+ Model->NS2BSIndx[Suffix->NumStats-1]+ @@ -360,14 +375,14 @@ inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) rs.Freq += (rs.Freq < 128); Model->Coder.SubRange.LowCount=0; Model->Coder.SubRange.HighCount=bs; - bs = SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2)); + bs = GET_SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2)); Model->PrevSuccess=1; Model->RunLength++; } else { Model->Coder.SubRange.LowCount=bs; - bs = SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2)); + bs = GET_SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2)); Model->Coder.SubRange.HighCount=BIN_SCALE; Model->InitEsc=ExpEscape[bs >> 10]; Model->NumMasked=1; @@ -378,7 +393,7 @@ inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) } -inline void PPM_CONTEXT::update1(ModelPPM *Model,STATE* p) +inline void RARPPM_CONTEXT::update1(ModelPPM *Model,RARPPM_STATE* p) { (Model->FoundState=p)->Freq += 4; U.SummFreq += 4; @@ -394,10 +409,10 @@ inline void PPM_CONTEXT::update1(ModelPPM *Model,STATE* p) -inline bool PPM_CONTEXT::decodeSymbol1(ModelPPM *Model) +inline bool RARPPM_CONTEXT::decodeSymbol1(ModelPPM *Model) { Model->Coder.SubRange.scale=U.SummFreq; - STATE* p=U.Stats; + RARPPM_STATE* p=U.Stats; int i, HiCnt; int count=Model->Coder.GetCurrentCount(); if (count>=(int)Model->Coder.SubRange.scale) @@ -439,7 +454,7 @@ inline bool PPM_CONTEXT::decodeSymbol1(ModelPPM *Model) } -inline void PPM_CONTEXT::update2(ModelPPM *Model,STATE* p) +inline void RARPPM_CONTEXT::update2(ModelPPM *Model,RARPPM_STATE* p) { (Model->FoundState=p)->Freq += 4; U.SummFreq += 4; @@ -450,9 +465,9 @@ inline void PPM_CONTEXT::update2(ModelPPM *Model,STATE* p) } -inline SEE2_CONTEXT* PPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) +inline RARPPM_SEE2_CONTEXT* RARPPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) { - SEE2_CONTEXT* psee2c; + RARPPM_SEE2_CONTEXT* psee2c; if (NumStats != 256) { psee2c=Model->SEE2Cont[Model->NS2Indx[Diff-1]]+ @@ -472,11 +487,11 @@ inline SEE2_CONTEXT* PPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) -inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) +inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model) { int count, HiCnt, i=NumStats-Model->NumMasked; - SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i); - STATE* ps[256], ** pps=ps, * p=U.Stats-1; + RARPPM_SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i); + RARPPM_STATE* ps[256], ** pps=ps, * p=U.Stats-1; HiCnt=0; do { @@ -485,6 +500,12 @@ inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) p++; } while (Model->CharMask[p->Symbol] == Model->EscCount); HiCnt += p->Freq; + + // We do not reuse PPMd coder in unstable state, so we do not really need + // this check and added it for extra safety. See CVE-2017-17969 for details. + if (pps>=ps+ASIZE(ps)) + return false; + *pps++ = p; } while ( --i ); Model->Coder.SubRange.scale += HiCnt; @@ -496,7 +517,12 @@ inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) { HiCnt=0; while ((HiCnt += p->Freq) <= count) - p=*++pps; + { + pps++; + if (pps>=ps+ASIZE(ps)) // Extra safety check. + return false; + p=*pps; + } Model->Coder.SubRange.LowCount = (Model->Coder.SubRange.HighCount=HiCnt)-p->Freq; psee2c->update(); update2(Model,p); @@ -509,12 +535,15 @@ inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) pps--; do { - Model->CharMask[(*++pps)->Symbol]=Model->EscCount; + pps++; + if (pps>=ps+ASIZE(ps)) // Extra safety check. + return false; + Model->CharMask[(*pps)->Symbol]=Model->EscCount; } while ( --i ); psee2c->Summ += Model->Coder.SubRange.scale; Model->NumMasked = NumStats; } - return(true); + return true; } diff --git a/libunrar/model.hpp b/libunrar/model.hpp index 5b4fa4f..52abc89 100644 --- a/libunrar/model.hpp +++ b/libunrar/model.hpp @@ -4,16 +4,17 @@ #include "coder.hpp" #include "suballoc.hpp" -const int MAX_O=64; /* maximum allowed model order */ - -const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS, - INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124; - -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #pragma pack(1) #endif -struct SEE2_CONTEXT +struct RARPPM_DEF +{ + static const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS, + INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124; +}; + +struct RARPPM_SEE2_CONTEXT : RARPPM_DEF { // SEE-contexts for PPM-contexts with masked symbols ushort Summ; byte Shift, Count; @@ -24,7 +25,7 @@ struct SEE2_CONTEXT } uint getMean() { - uint RetVal=SHORT16(Summ) >> Shift; + uint RetVal=GET_SHORT16(Summ) >> Shift; Summ -= RetVal; return RetVal+(RetVal == 0); } @@ -40,45 +41,47 @@ struct SEE2_CONTEXT class ModelPPM; -struct PPM_CONTEXT; +struct RARPPM_CONTEXT; -struct STATE +struct RARPPM_STATE { byte Symbol; byte Freq; - PPM_CONTEXT* Successor; + RARPPM_CONTEXT* Successor; }; -struct FreqData -{ - ushort SummFreq; - STATE _PACK_ATTR * Stats; -}; -struct PPM_CONTEXT +struct RARPPM_CONTEXT : RARPPM_DEF { ushort NumStats; + + struct FreqData + { + ushort SummFreq; + RARPPM_STATE RARPPM_PACK_ATTR * Stats; + }; + union { FreqData U; - STATE OneState; + RARPPM_STATE OneState; }; - PPM_CONTEXT* Suffix; + RARPPM_CONTEXT* Suffix; inline void encodeBinSymbol(ModelPPM *Model,int symbol); // MaxOrder: inline void encodeSymbol1(ModelPPM *Model,int symbol); // ABCD context inline void encodeSymbol2(ModelPPM *Model,int symbol); // BCD suffix inline void decodeBinSymbol(ModelPPM *Model); // BCDE successor inline bool decodeSymbol1(ModelPPM *Model); // other orders: inline bool decodeSymbol2(ModelPPM *Model); // BCD context - inline void update1(ModelPPM *Model,STATE* p); // CD suffix - inline void update2(ModelPPM *Model,STATE* p); // BCDE successor + inline void update1(ModelPPM *Model,RARPPM_STATE* p); // CD suffix + inline void update2(ModelPPM *Model,RARPPM_STATE* p); // BCDE successor void rescale(ModelPPM *Model); - inline PPM_CONTEXT* createChild(ModelPPM *Model,STATE* pStats,STATE& FirstState); - inline SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff); + inline RARPPM_CONTEXT* createChild(ModelPPM *Model,RARPPM_STATE* pStats,RARPPM_STATE& FirstState); + inline RARPPM_SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff); }; -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #ifdef _AIX #pragma pack(pop) #else @@ -86,28 +89,15 @@ struct PPM_CONTEXT #endif #endif -const uint UNIT_SIZE=Max(sizeof(PPM_CONTEXT),sizeof(RAR_MEM_BLK)); -const uint FIXED_UNIT_SIZE=12; - -/* -inline PPM_CONTEXT::PPM_CONTEXT(STATE* pStats,PPM_CONTEXT* ShorterContext): - NumStats(1), Suffix(ShorterContext) { pStats->Successor=this; } -inline PPM_CONTEXT::PPM_CONTEXT(): NumStats(0) {} -*/ - -template -inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; } - - -class ModelPPM +class ModelPPM : RARPPM_DEF { private: - friend struct PPM_CONTEXT; + friend struct RARPPM_CONTEXT; - SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont; + RARPPM_SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont; - struct PPM_CONTEXT *MinContext, *MedContext, *MaxContext; - STATE* FoundState; // found next state transition + struct RARPPM_CONTEXT *MinContext, *MedContext, *MaxContext; + RARPPM_STATE* FoundState; // found next state transition int NumMasked, InitEsc, OrderFall, MaxOrder, RunLength, InitRL; byte CharMask[256], NS2Indx[256], NS2BSIndx[256], HB2Flag[256]; byte EscCount, PrevSuccess, HiBitsFlag; @@ -118,7 +108,7 @@ class ModelPPM void RestartModelRare(); void StartModelRare(int MaxOrder); - inline PPM_CONTEXT* CreateSuccessors(bool Skip,STATE* p1); + inline RARPPM_CONTEXT* CreateSuccessors(bool Skip,RARPPM_STATE* p1); inline void UpdateModel(); inline void ClearMask(); diff --git a/libunrar/msc.dep b/libunrar/msc.dep deleted file mode 100644 index 53d1a50..0000000 --- a/libunrar/msc.dep +++ /dev/null @@ -1,2390 +0,0 @@ -# Microsoft Developer Studio Generated Dependency File, included by unrar.mak - -.\archive.cpp : \ - ".\arccmt.cpp"\ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\arcread.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\cmddata.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\consio.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.cpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\crc.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\crypt.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\encname.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\errhnd.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\extinfo.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - ".\win32acl.cpp"\ - ".\win32stm.cpp"\ - - -.\extract.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\filcreat.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\file.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\filefn.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\filestr.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\find.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\getbits.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\global.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\isnt.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\list.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\match.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\options.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\pathfn.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\rar.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.cpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\rarvm.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rarvmtbl.cpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\rawread.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\rdwrfn.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\recvol.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\resource.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\rijndael.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\rs.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\savepos.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\scantree.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\sha1.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\strfn.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\strlist.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\system.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\timefn.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\ulinks.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\unicode.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\unpack.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.cpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.cpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.cpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\unpack15.cpp"\ - ".\unpack20.cpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - - -.\volume.cpp : \ - ".\archive.hpp"\ - ".\array.hpp"\ - ".\cmddata.hpp"\ - ".\coder.hpp"\ - ".\compress.hpp"\ - ".\consio.hpp"\ - ".\crc.hpp"\ - ".\crypt.hpp"\ - ".\encname.hpp"\ - ".\errhnd.hpp"\ - ".\extinfo.hpp"\ - ".\extract.hpp"\ - ".\filcreat.hpp"\ - ".\file.hpp"\ - ".\filefn.hpp"\ - ".\filestr.hpp"\ - ".\find.hpp"\ - ".\getbits.hpp"\ - ".\global.hpp"\ - ".\headers.hpp"\ - ".\isnt.hpp"\ - ".\list.hpp"\ - ".\loclang.hpp"\ - ".\log.hpp"\ - ".\match.hpp"\ - ".\model.hpp"\ - ".\options.hpp"\ - ".\os.hpp"\ - ".\pathfn.hpp"\ - ".\rar.hpp"\ - ".\rardefs.hpp"\ - ".\rarlang.hpp"\ - ".\raros.hpp"\ - ".\rartypes.hpp"\ - ".\rarvm.hpp"\ - ".\rawread.hpp"\ - ".\rdwrfn.hpp"\ - ".\recvol.hpp"\ - ".\resource.hpp"\ - ".\rijndael.hpp"\ - ".\rs.hpp"\ - ".\savepos.hpp"\ - ".\scantree.hpp"\ - ".\sha1.hpp"\ - ".\smallfn.hpp"\ - ".\strfn.hpp"\ - ".\strlist.hpp"\ - ".\suballoc.hpp"\ - ".\system.hpp"\ - ".\timefn.hpp"\ - ".\ulinks.hpp"\ - ".\unicode.hpp"\ - ".\unpack.hpp"\ - ".\version.hpp"\ - ".\volume.hpp"\ - diff --git a/libunrar/options.cpp b/libunrar/options.cpp index 8124631..40323be 100644 --- a/libunrar/options.cpp +++ b/libunrar/options.cpp @@ -8,6 +8,8 @@ RAROptions::RAROptions() RAROptions::~RAROptions() { + // It is important for security reasons, so we do not have the unnecessary + // password data left in memory. memset(this,0,sizeof(RAROptions)); } @@ -15,14 +17,19 @@ RAROptions::~RAROptions() void RAROptions::Init() { memset(this,0,sizeof(RAROptions)); - WinSize=0x400000; + WinSize=0x2000000; Overwrite=OVERWRITE_DEFAULT; Method=3; MsgStream=MSG_STDOUT; ConvertNames=NAMES_ORIGINALCASE; - ProcessEA=true; - xmtime=EXTTIME_HIGH3; - CurVolNum=0; + xmtime=EXTTIME_MAX; FileSizeLess=INT64NDF; FileSizeMore=INT64NDF; + HashType=HASH_CRC32; +#ifdef RAR_SMP + Threads=GetNumberOfThreads(); +#endif +#ifdef USE_QOPEN + QOpenMode=QOPEN_AUTO; +#endif } diff --git a/libunrar/options.hpp b/libunrar/options.hpp index c9746df..fd33d3d 100644 --- a/libunrar/options.hpp +++ b/libunrar/options.hpp @@ -1,27 +1,32 @@ #ifndef _RAR_OPTIONS_ #define _RAR_OPTIONS_ -#define DEFAULT_RECOVERY -1 +#define DEFAULT_RECOVERY -3 #define DEFAULT_RECVOLUMES -10 +#define VOLSIZE_AUTO INT64NDF // Automatically detect the volume size. + enum PATH_EXCL_MODE { - EXCL_NONE,EXCL_BASEPATH,EXCL_SKIPWHOLEPATH,EXCL_SAVEFULLPATH, - EXCL_SKIPABSPATH,EXCL_ABSPATH + EXCL_UNCHANGED=0, // Process paths as is (default). + EXCL_SKIPWHOLEPATH, // -ep (exclude the path completely) + EXCL_BASEPATH, // -ep1 (exclude the base part of path) + EXCL_SAVEFULLPATH, // -ep2 (the full path without the disk letter) + EXCL_ABSPATH // -ep3 (the full path with the disk letter) }; enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4, SOLID_VOLUME_DEPENDENT=8,SOLID_VOLUME_INDEPENDENT=16}; -enum {ARCTIME_NONE,ARCTIME_KEEP,ARCTIME_LATEST}; +enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST}; enum EXTTIME_MODE { - EXTTIME_NONE,EXTTIME_1S,EXTTIME_HIGH1,EXTTIME_HIGH2,EXTTIME_HIGH3 + EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX }; -enum {NAMES_ORIGINALCASE,NAMES_UPPERCASE,NAMES_LOWERCASE}; +enum {NAMES_ORIGINALCASE=0,NAMES_UPPERCASE,NAMES_LOWERCASE}; -enum MESSAGE_TYPE {MSG_STDOUT,MSG_STDERR,MSG_ERRONLY,MSG_NULL}; +enum MESSAGE_TYPE {MSG_STDOUT=0,MSG_STDERR,MSG_ERRONLY,MSG_NULL}; enum RECURSE_MODE { @@ -33,19 +38,41 @@ enum RECURSE_MODE enum OVERWRITE_MODE { - OVERWRITE_DEFAULT=0, // ask for extraction, silently overwrite for archiving + OVERWRITE_DEFAULT=0, // Ask when extracting, silently overwrite when archiving. OVERWRITE_ALL, OVERWRITE_NONE, OVERWRITE_AUTORENAME, OVERWRITE_FORCE_ASK }; -enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE }; -#define MAX_FILTERS 16 +enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS }; + +enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 }; + +#define MAX_FILTER_TYPES 16 enum FilterState {FILTER_DEFAULT=0,FILTER_AUTO,FILTER_FORCE,FILTER_DISABLE}; +enum SAVECOPY_MODE { + SAVECOPY_NONE=0, SAVECOPY_SILENT, SAVECOPY_LIST, SAVECOPY_LISTEXIT, + SAVECOPY_DUPLISTEXIT +}; + +enum APPENDARCNAME_MODE +{ + APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNDIR +}; + +enum POWER_MODE { + POWERMODE_KEEP=0,POWERMODE_OFF,POWERMODE_HIBERNATE,POWERMODE_SLEEP, + POWERMODE_RESTART +}; + + +// Need "forced off" state to turn off sound in GUI command line. +enum SOUND_NOTIFY_MODE {SOUND_NOTIFY_DEFAULT=0,SOUND_NOTIFY_ON,SOUND_NOTIFY_OFF}; + struct FilterMode { FilterState State; @@ -53,6 +80,8 @@ struct FilterMode int Param2; }; +#define MAX_GENERATE_MASK 128 + class RAROptions { @@ -63,34 +92,51 @@ class RAROptions uint ExclFileAttr; uint InclFileAttr; + + // We handle -ed and -e+d with special flags instead of attribute mask, + // so it works with both Windows and Unix archives. + bool ExclDir; + bool InclDir; + bool InclAttrSet; - uint WinSize; - char TempPath[NM]; - char SFXModule[NM]; - char ExtrPath[NM]; - wchar ExtrPathW[NM]; - char CommentFile[NM]; + size_t WinSize; + wchar TempPath[NM]; + wchar SFXModule[NM]; + +#ifdef USE_QOPEN + QOPEN_MODE QOpenMode; +#endif + + bool ConfigDisabled; // Switch -cfg-. + wchar ExtrPath[NM]; + wchar CommentFile[NM]; RAR_CHARSET CommentCharset; RAR_CHARSET FilelistCharset; - char ArcPath[NM]; - wchar ArcPathW[NM]; - char Password[MAXPASSWORD]; + RAR_CHARSET ErrlogCharset; + RAR_CHARSET RedirectCharset; + + wchar ArcPath[NM]; + SecPassword Password; bool EncryptHeaders; - char LogName[NM]; + + bool ManualPassword; // Password entered manually during operation, might need to clean for next archive. + + wchar LogName[NM]; MESSAGE_TYPE MsgStream; - bool Sound; + SOUND_NOTIFY_MODE Sound; OVERWRITE_MODE Overwrite; int Method; + HASH_TYPE HashType; int Recovery; int RecVolNumber; bool DisablePercentage; bool DisableCopyright; bool DisableDone; + bool PrintVersion; int Solid; int SolidCount; bool ClearArc; bool AddArcOnly; - bool AV; bool DisableComment; bool FreshFiles; bool UpdateFiles; @@ -100,55 +146,63 @@ class RAROptions Array NextVolSizes; uint CurVolNum; bool AllYes; - bool DisableViewAV; + bool MoreInfo; // -im, show more information, used only in "WinRAR t" now. bool DisableSortSolid; int ArcTime; int ConvertNames; bool ProcessOwners; - bool SaveLinks; + bool SaveSymLinks; + bool SaveHardLinks; + bool AbsoluteLinks; int Priority; int SleepTime; bool KeepBroken; - bool EraseDisk; bool OpenShared; bool DeleteFiles; - bool SyncFiles; + +#ifdef _WIN_ALL + bool AllowIncompatNames; // Allow names with trailing dots and spaces. +#endif + + +#ifndef SFX_MODULE bool GenerateArcName; - char GenerateMask[80]; + wchar GenerateMask[MAX_GENERATE_MASK]; + wchar DefGenerateMask[MAX_GENERATE_MASK]; +#endif + bool SyncFiles; bool ProcessEA; bool SaveStreams; bool SetCompressedAttr; bool IgnoreGeneralAttr; - RarTime FileTimeBefore; - RarTime FileTimeAfter; + RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore; + bool FileMtimeBeforeOR,FileCtimeBeforeOR,FileAtimeBeforeOR; + RarTime FileMtimeAfter,FileCtimeAfter,FileAtimeAfter; + bool FileMtimeAfterOR,FileCtimeAfterOR,FileAtimeAfterOR; int64 FileSizeLess; int64 FileSizeMore; - bool OldNumbering; bool Lock; bool Test; bool VolumePause; - FilterMode FilterModes[MAX_FILTERS]; - char EmailTo[NM]; + FilterMode FilterModes[MAX_FILTER_TYPES]; + wchar EmailTo[NM]; uint VersionControl; - bool NoEndBlock; - bool AppendArcNameToPath; - bool Shutdown; - EXTTIME_MODE xmtime; + APPENDARCNAME_MODE AppendArcNameToPath; + POWER_MODE Shutdown; + EXTTIME_MODE xmtime; // Extended time modes (time precision to store). EXTTIME_MODE xctime; EXTTIME_MODE xatime; - EXTTIME_MODE xarctime; - char CompressStdin[NM]; + bool PreserveAtime; + wchar CompressStdin[NM]; + + uint Threads; // We use it to init hash even if RAR_SMP is not defined. -#ifdef PACK_SMP - uint Threads; -#endif #ifdef RARDLL - char DllDestName[NM]; - wchar DllDestNameW[NM]; + wchar DllDestName[NM]; int DllOpMode; int DllError; LPARAM UserData; diff --git a/libunrar/os.hpp b/libunrar/os.hpp index 76297e0..b69f348 100644 --- a/libunrar/os.hpp +++ b/libunrar/os.hpp @@ -8,146 +8,132 @@ #define INCL_BASE #endif -#if defined(_WIN_32) || defined(_EMX) -#define ENABLE_BAD_ALLOC +#if defined(RARDLL) && !defined(SILENT) +#define SILENT #endif +#include -#if defined(_WIN_32) || defined(_EMX) + +#if defined(_WIN_ALL) || defined(_EMX) #define LITTLE_ENDIAN -#define NM 1024 +#define NM 2048 -#ifdef _WIN_32 +#ifdef _WIN_ALL - #define STRICT - #undef WINVER - #undef _WIN32_WINNT - #define WINVER 0x0400 - #define _WIN32_WINNT 0x0300 +// We got a report that just "#define STRICT" is incompatible with +// "#define STRICT 1" in Windows 10 SDK minwindef.h and depending on the order +// in which these statements are reached this may cause a compiler warning +// and build break for other projects incorporating this source. +// So we changed it to "#define STRICT 1". +#ifndef STRICT +#define STRICT 1 +#endif + +// 'ifndef' check here is needed for unrar.dll header to avoid macro +// re-definition warnings in third party projects. +#ifndef UNICODE +#define UNICODE +#endif + +#undef WINVER +#undef _WIN32_WINNT +#define WINVER 0x0501 +#define _WIN32_WINNT 0x0501 + +#if !defined(ZIPSFX) +#define RAR_SMP +#endif #define WIN32_LEAN_AND_MEAN #include #include - -#ifndef _WIN_CE - #include - #include - #include +#include +#pragma comment(lib, "Shlwapi.lib") +#include +#pragma comment(lib, "PowrProf.lib") +#include +#include +#include +#include +#include +#include -#endif // _WIN_CE +#endif // _WIN_ALL +#include +#include +#include -#endif // _WIN_32 - -#ifndef _WIN_CE - #include - #include - #include -#endif // _WIN_CE - -#if !defined(_EMX) && !defined(_MSC_VER) && !defined(_WIN_CE) +#if !defined(_EMX) && !defined(_MSC_VER) #include #endif #ifdef _MSC_VER #if _MSC_VER<1500 #define for if (0) ; else for #endif - #ifndef _WIN_CE - #include - #endif + #include + #include + + #define USE_SSE + #define SSE_ALIGNMENT 16 #else #include #endif // _MSC_VER -#ifndef _WIN_CE - #include -#endif // _WIN_CE - -#if defined(ENABLE_BAD_ALLOC) && !defined(_WIN_CE) - #include -#endif - -#ifdef _EMX - #include - #include - #include - #include - #ifdef _DJGPP - #include - #else - #include - #include - #include - #endif -#else - #if defined(_MSC_VER) || defined(__MINGW32__) - #include - #else - #include - #endif -#endif - #include #include #include #include #include -#ifndef _WIN_CE - #include - #include - #include - #include - #include -#endif +#include +#include +#include +#include +#include -/* -#ifdef _WIN_32 -#pragma hdrstop -#endif // _WIN_32 -*/ + +#define SAVE_LINKS #define ENABLE_ACCESS -#define DefConfigName "rar.ini" -#define DefLogName "rar.log" +#define DefConfigName L"rar.ini" +#define DefLogName L"rar.log" -#define PATHDIVIDER "\\" -#define PATHDIVIDERW L"\\" +#define SPATHDIVIDER L"\\" #define CPATHDIVIDER '\\' -#define MASKALL "*" -#define MASKALLW L"*" +#define MASKALL L"*" #define READBINARY "rb" #define READTEXT "rt" #define UPDATEBINARY "r+b" #define CREATEBINARY "w+b" +#define WRITEBINARY "wb" #define APPENDTEXT "at" -#if defined(_WIN_32) +#if defined(_WIN_ALL) #ifdef _MSC_VER #define _stdfunction __cdecl + #define _forceinline __forceinline #else #define _stdfunction _USERENTRY + #define _forceinline inline #endif #else #define _stdfunction + #define _forceinline inline #endif -#endif +#endif // defined(_WIN_ALL) || defined(_EMX) #ifdef _UNIX -#define NM 1024 - -#ifdef _BEOS -#include -#include -#endif +#define NM 2048 #include #include @@ -156,13 +142,16 @@ #if defined(__QNXNTO__) #include #endif -#if defined(__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined(__APPLE__) - #include - #include -#else +#if defined(RAR_SMP) && defined(__APPLE__) + #include +#endif +#ifndef SFX_MODULE + #include #endif #include #include +#include +#include #include #include #include @@ -176,29 +165,35 @@ #include #include + #ifdef S_IFLNK #define SAVE_LINKS #endif +#if defined(__linux) || defined(__FreeBSD__) +#include +#define USE_LUTIMES +#endif + #define ENABLE_ACCESS -#define DefConfigName ".rarrc" -#define DefLogName ".rarlog" +#define DefConfigName L".rarrc" +#define DefLogName L".rarlog" -#define PATHDIVIDER "/" -#define PATHDIVIDERW L"/" +#define SPATHDIVIDER L"/" #define CPATHDIVIDER '/' -#define MASKALL "*" -#define MASKALLW L"*" +#define MASKALL L"*" #define READBINARY "r" #define READTEXT "r" #define UPDATEBINARY "r+" #define CREATEBINARY "w+" +#define WRITEBINARY "w" #define APPENDTEXT "a" #define _stdfunction +#define _forceinline inline #ifdef _APPLE #if defined(__BIG_ENDIAN__) && !defined(BIG_ENDIAN) @@ -217,18 +212,39 @@ #endif #endif +#if _POSIX_C_SOURCE >= 200809L + #define UNIX_TIME_NS // Nanosecond time precision in Unix. #endif - typedef const char* MSGID; +#endif // _UNIX + +#if 0 + #define MSGID_INT + typedef int MSGID; +#else + typedef const wchar* MSGID; +#endif + +#ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required. + #define SSE_ALIGNMENT 1 +#endif #define safebuf static +// Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN. +#if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN) + #define LITTLE_ENDIAN +#endif +#if defined(_BIG_ENDIAN) && !defined(BIG_ENDIAN) + #define BIG_ENDIAN +#endif + #if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) - #if defined(__i386) || defined(i386) || defined(__i386__) + #if defined(__i386) || defined(i386) || defined(__i386__) || defined(__x86_64) #define LITTLE_ENDIAN - #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN + #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) #define LITTLE_ENDIAN - #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN + #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN || defined(__BIG_ENDIAN__) #define BIG_ENDIAN #else #error "Neither LITTLE_ENDIAN nor BIG_ENDIAN are defined. Define one of them." @@ -245,15 +261,9 @@ #endif #endif -#if !defined(BIG_ENDIAN) && !defined(_WIN_CE) && defined(_WIN_32) -/* allow not aligned integer access, increases speed in some operations */ -#define ALLOW_NOT_ALIGNED_INT -#endif - -#if defined(__sparc) || defined(sparc) || defined(__sparcv9) -/* prohibit not aligned access to data structures in text comression - algorithm, increases memory requirements */ -#define STRICT_ALIGNMENT_REQUIRED +#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__) +// Allow not aligned integer access, increases speed in some operations. +#define ALLOW_MISALIGNED #endif #endif // _RAR_OS_ diff --git a/libunrar/os2ea.cpp b/libunrar/os2ea.cpp deleted file mode 100644 index f418615..0000000 --- a/libunrar/os2ea.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include - - - -void ExtractOS2EA(Archive &Arc,char *FileName) -{ - if (_osmode != OS2_MODE) - { - mprintf(St(MSkipEA)); - return; - } - - if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - - if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER) - { - Log(Arc.FileName,St(MEAUnknHeader),FileName); - ErrHandler.SetErrorCode(WARNING); - return; - } - - struct StructEAOP2 - { - char *GEAPtr; - char *FEAPtr; - unsigned long Error; - } EAOP2; - - ComprDataIO DataIO; - Unpack Unpack(&DataIO); - Unpack.Init(); - - Array UnpData(Arc.EAHead.UnpSize); - DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); - DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); - DataIO.EnableShowProgress(false); - DataIO.SetFiles(&Arc,NULL); - Unpack.SetDestSize(Arc.EAHead.UnpSize); - Unpack.DoUnpack(Arc.EAHead.UnpVer,false); - - if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - - EAOP2.FEAPtr=(char *)&UnpData[0]; - EAOP2.GEAPtr=NULL; - if (DosSetPathInfo((unsigned char *)FileName,2,&EAOP2,sizeof(EAOP2),0x10)!=0) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - } - File::SetCloseFileTimeByName(FileName,&Arc.NewLhd.mtime,&Arc.NewLhd.atime); - mprintf(St(MShowEA)); -} - - -void ExtractOS2EANew(Archive &Arc,char *FileName) -{ - if (_osmode != OS2_MODE) - { - mprintf(St(MSkipEA)); - return; - } - - Array SubData; - if (!Arc.ReadSubData(&SubData,NULL)) - return; - - struct StructEAOP2 - { - char *GEAPtr; - char *FEAPtr; - unsigned long Error; - } EAOP2; - - EAOP2.FEAPtr=(char *)&SubData[0]; - EAOP2.GEAPtr=NULL; - if (DosSetPathInfo((unsigned char *)FileName,2,&EAOP2,sizeof(EAOP2),0x10)!=0) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - } - File::SetCloseFileTimeByName(FileName,&Arc.NewLhd.mtime,&Arc.NewLhd.atime); - mprintf(St(MShowEA)); -} - diff --git a/libunrar/patch-makefile.unix b/libunrar/patch-makefile.unix deleted file mode 100644 index 4363684..0000000 --- a/libunrar/patch-makefile.unix +++ /dev/null @@ -1,44 +0,0 @@ ---- makefile.unix.orig 2008-10-09 15:43:06.000000000 +0200 -+++ makefile.unix 2008-11-06 01:43:52.000000000 +0100 -@@ -7,10 +7,11 @@ - - # Linux using GCC - #CXX=g++ --#CXXFLAGS=-O2 --DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -+CXXFLAGS=$(CFLAGS) -fPIC -DPIC -+DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DGUI -DSILENT - STRIP=strip - DESTDIR=/usr -+RANLIB=ranlib - - # Linux using LCC - #CXX=lcc -@@ -100,7 +101,7 @@ - WHAT=UNRAR - - UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o --LIB_OBJ=filestr.o scantree.o dll.o -+LIB_OBJ=dll.o - - OBJECTS=rar.o strlist.o strfn.o pathfn.o int64.o savepos.o global.o file.o filefn.o filcreat.o \ - archive.o arcread.o unicode.o system.o isnt.o crypt.o crc.o rawread.o encname.o \ -@@ -131,9 +132,15 @@ - $(STRIP) default.sfx - - lib: WHAT=RARDLL --lib: $(OBJECTS) $(LIB_OBJ) -- @rm -f libunrar.so -- $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) -+lib: $(OBJECTS) $(LIB_OBJ) $(UNRAR_OBJ) -+ @rm -f libunrar.so.3 -+ $(LINK) -shared -o libunrar.so.3 $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) $(UNRAR_OBJ) -+ -+liba: WHAT=RARDLL -+liba: $(OBJECTS) $(LIB_OBJ) $(UNRAR_OBJ) -+ @rm -f libunrar.a -+ $(AR) rc libunrar.a $(OBJECTS) $(LIB_OBJ) $(UNRAR_OBJ) -+ $(RANLIB) libunrar.a - - install-unrar: - install unrar $(DESTDIR)/bin diff --git a/libunrar/pathfn.cpp b/libunrar/pathfn.cpp index f6ec66d..278863c 100644 --- a/libunrar/pathfn.cpp +++ b/libunrar/pathfn.cpp @@ -1,59 +1,43 @@ #include "rar.hpp" -char* PointToName(const char *Path) -{ - const char *Found=NULL; - for (const char *s=Path;*s!=0;s=charnext(s)) - if (IsPathDiv(*s)) - Found=(char*)(s+1); - if (Found!=NULL) - return((char*)Found); - return (char*)((*Path && IsDriveDiv(Path[1]) && charnext(Path)==Path+1) ? Path+2:Path); -} - - wchar* PointToName(const wchar *Path) { - for (int I=(int)strlenw(Path)-1;I>=0;I--) + for (int I=(int)wcslen(Path)-1;I>=0;I--) if (IsPathDiv(Path[I])) return (wchar*)&Path[I+1]; return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path); } -char* PointToLastChar(const char *Path) +wchar* PointToLastChar(const wchar *Path) { - for (const char *s=Path,*p=Path;;p=s,s=charnext(s)) - if (*s==0) - return((char *)p); + size_t Length=wcslen(Path); + return (wchar*)(Length>0 ? Path+Length-1:Path); } - - -char* ConvertPath(const char *SrcPath,char *DestPath) +wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize) { - const char *DestPtr=SrcPath; + const wchar *DestPtr=SrcPath; // Prevent \..\ in any part of path string. - for (const char *s=DestPtr;*s!=0;s++) + for (const wchar *s=DestPtr;*s!=0;s++) if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) DestPtr=s+4; - // Remove any sequence of . and \ in the beginning of path string. - while (*DestPtr) + // Remove any amount of :\ and any sequence of . and \ in the beginning of path string. + while (*DestPtr!=0) { - const char *s=DestPtr; - if (s[0] && IsDriveDiv(s[1])) + const wchar *s=DestPtr; + if (s[0]!=0 && IsDriveDiv(s[1])) s+=2; - else - if (s[0]=='\\' && s[1]=='\\') - { - const char *Slash=strchr(s+2,'\\'); - if (Slash!=NULL && (Slash=strchr(Slash+1,'\\'))!=NULL) - s=Slash+1; - } - for (const char *t=s;*t!=0;t++) + if (s[0]=='\\' && s[1]=='\\') + { + const wchar *Slash=wcschr(s+2,'\\'); + if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL) + s=Slash+1; + } + for (const wchar *t=s;*t!=0;t++) if (IsPathDiv(*t)) s=t+1; else @@ -67,162 +51,92 @@ char* ConvertPath(const char *SrcPath,char *DestPath) // Code above does not remove last "..", doing here. if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0) DestPtr+=2; - + if (DestPath!=NULL) { // SrcPath and DestPath can point to same memory area, // so we use the temporary buffer for copying. - char TmpStr[NM]; - strncpyz(TmpStr,DestPtr,ASIZE(TmpStr)); - strcpy(DestPath,TmpStr); - } - return((char *)DestPtr); -} - - -wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath) -{ - const wchar *DestPtr=SrcPath; - for (const wchar *s=DestPtr;*s!=0;s++) - if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) - DestPtr=s+4; - while (*DestPtr) - { - const wchar *s=DestPtr; - if (s[0] && IsDriveDiv(s[1])) - s+=2; - if (s[0]=='\\' && s[1]=='\\') - { - const wchar *Slash=strchrw(s+2,'\\'); - if (Slash!=NULL && (Slash=strchrw(Slash+1,'\\'))!=NULL) - s=Slash+1; - } - for (const wchar *t=s;*t!=0;t++) - if (IsPathDiv(*t)) - s=t+1; - else - if (*t!='.') - break; - if (s==DestPtr) - break; - DestPtr=s; - } - if (DestPath!=NULL) - { wchar TmpStr[NM]; - strncpyw(TmpStr,DestPtr,sizeof(TmpStr)/sizeof(TmpStr[0])-1); - strcpyw(DestPath,TmpStr); + wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr)); + wcsncpyz(DestPath,TmpStr,DestSize); } - return((wchar *)DestPtr); + return (wchar *)DestPtr; } -void SetExt(char *Name,const char *NewExt) +void SetName(wchar *FullName,const wchar *Name,size_t MaxSize) { - char *Dot=GetExt(Name); - if (NewExt==NULL) - { - if (Dot!=NULL) - *Dot=0; - } - else - if (Dot==NULL) - { - strcat(Name,"."); - strcat(Name,NewExt); - } - else - strcpy(Dot+1,NewExt); + wchar *NamePtr=PointToName(FullName); + wcsncpyz(NamePtr,Name,MaxSize-(NamePtr-FullName)); } -#ifndef SFX_MODULE -void SetExt(wchar *Name,const wchar *NewExt) +void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize) { if (Name==NULL || *Name==0) return; wchar *Dot=GetExt(Name); - if (NewExt==NULL) + if (Dot!=NULL) + *Dot=0; + if (NewExt!=NULL) { - if (Dot!=NULL) - *Dot=0; + wcsncatz(Name,L".",MaxSize); + wcsncatz(Name,NewExt,MaxSize); } - else - if (Dot==NULL) - { - strcatw(Name,L"."); - strcatw(Name,NewExt); - } - else - strcpyw(Dot+1,NewExt); } -#endif #ifndef SFX_MODULE -void SetSFXExt(char *SFXName) -{ -#ifdef _UNIX - SetExt(SFXName,"sfx"); -#endif - -#if defined(_WIN_32) || defined(_EMX) - SetExt(SFXName,"exe"); -#endif -} -#endif - - -#ifndef SFX_MODULE -void SetSFXExt(wchar *SFXName) +void SetSFXExt(wchar *SFXName,size_t MaxSize) { if (SFXName==NULL || *SFXName==0) return; #ifdef _UNIX - SetExt(SFXName,L"sfx"); + SetExt(SFXName,L"sfx",MaxSize); #endif -#if defined(_WIN_32) || defined(_EMX) - SetExt(SFXName,L"exe"); +#if defined(_WIN_ALL) || defined(_EMX) + SetExt(SFXName,L"exe",MaxSize); #endif } #endif -char *GetExt(const char *Name) -{ - return(strrchrd(PointToName(Name),'.')); -} - - +// 'Ext' is an extension with the leading dot, like L".rar". wchar *GetExt(const wchar *Name) { - return(Name==NULL ? (wchar *)L"":strrchrw(PointToName(Name),'.')); + return Name==NULL ? NULL:wcsrchr(PointToName(Name),'.'); } -bool CmpExt(const char *Name,const char *Ext) +// 'Ext' is an extension without the leading dot, like L"rar". +bool CmpExt(const wchar *Name,const wchar *Ext) { - char *NameExt=GetExt(Name); - return(NameExt!=NULL && stricomp(NameExt+1,Ext)==0); + wchar *NameExt=GetExt(Name); + return NameExt!=NULL && wcsicomp(NameExt+1,Ext)==0; } -bool IsWildcard(const char *Str,const wchar *StrW) +bool IsWildcard(const wchar *Str) { - if (StrW!=NULL && *StrW!=0) - return(strpbrkw(StrW,L"*?")!=NULL); - return(Str==NULL ? false:strpbrk(Str,"*?")!=NULL); + if (Str==NULL) + return false; +#ifdef _WIN_ALL + // Not treat the special NTFS \\?\d: path prefix as a wildcard. + if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\') + Str+=4; +#endif + return wcspbrk(Str,L"*?")!=NULL; } bool IsPathDiv(int Ch) { -#if defined(_WIN_32) || defined(_EMX) - return(Ch=='\\' || Ch=='/'); +#ifdef _WIN_ALL + return Ch=='\\' || Ch=='/'; #else - return(Ch==CPATHDIVIDER); + return Ch==CPATHDIVIDER; #endif } @@ -230,68 +144,63 @@ bool IsPathDiv(int Ch) bool IsDriveDiv(int Ch) { #ifdef _UNIX - return(false); + return false; #else - return(Ch==':'); + return Ch==':'; #endif } -int GetPathDisk(const char *Path) +bool IsDriveLetter(const wchar *Path) { - if (IsDiskLetter(Path)) - return(etoupper(*Path)-'A'); + wchar Letter=etoupperw(Path[0]); + return Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]); +} + + +int GetPathDisk(const wchar *Path) +{ + if (IsDriveLetter(Path)) + return etoupperw(*Path)-'A'; else - return(-1); + return -1; } -void AddEndSlash(char *Path) +void AddEndSlash(wchar *Path,size_t MaxLength) { - char *LastChar=PointToLastChar(Path); - if (*LastChar!=0 && *LastChar!=CPATHDIVIDER) - strcat(LastChar,PATHDIVIDER); + size_t Length=wcslen(Path); + if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+10 && Path[Length-1]!=CPATHDIVIDER) - strcatw(Path,PATHDIVIDERW); + // 'Path', 'Name' and 'Pathname' can point to same memory area. So we use + // the temporary buffer instead of constructing the name in 'Pathname'. + wchar OutName[NM]; + wcsncpyz(OutName,Path,ASIZE(OutName)); + AddEndSlash(OutName,ASIZE(OutName)); + wcsncatz(OutName,Name,ASIZE(OutName)); + wcsncpyz(Pathname,OutName,MaxSize); } // Returns file path including the trailing path separator symbol. -void GetFilePath(const char *FullName,char *Path,int MaxLength) +void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength) { - size_t PathLength=Min(MaxLength-1,PointToName(FullName)-FullName); - strncpy(Path,FullName,PathLength); + if (MaxLength==0) + return; + size_t PathLength=Min(MaxLength-1,size_t(PointToName(FullName)-FullName)); + wcsncpy(Path,FullName,PathLength); Path[PathLength]=0; } -// Returns file path including the trailing path separator symbol. -void GetFilePath(const wchar *FullName,wchar *Path,int MaxLength) -{ - size_t PathLength=Min(MaxLength-1,PointToName(FullName)-FullName); - strncpyw(Path,FullName,PathLength); - Path[PathLength]=0; -} - - -// Removes name and returns file path without the trailing -// path separator symbol. -void RemoveNameFromPath(char *Path) -{ - char *Name=PointToName(Path); - if (Name>=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4)) - Name--; - *Name=0; -} - - -#ifndef SFX_MODULE // Removes name and returns file path without the trailing // path separator symbol. void RemoveNameFromPath(wchar *Path) @@ -301,11 +210,10 @@ void RemoveNameFromPath(wchar *Path) Name--; *Name=0; } -#endif -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) -void GetAppDataPath(char *Path) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); @@ -315,106 +223,89 @@ void GetAppDataPath(char *Path) if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathFromIDList(ppidl,Path) && *Path!=0) { - AddEndSlash(Path); - strcat(Path,"WinRAR"); - Success=FileExist(Path) || MakeDir(Path,NULL,false,0)==MKDIR_SUCCESS; - } - if (!Success) - { - GetModuleFileName(NULL,Path,NM); - RemoveNameFromPath(Path); + AddEndSlash(Path,MaxSize); + wcsncatz(Path,L"WinRAR",MaxSize); + Success=FileExist(Path); + if (!Success && Create) + Success=MakeDir(Path,false,0)==MKDIR_SUCCESS; } g_pMalloc->Free(ppidl); + return Success; } #endif -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) -void GetRarDataPath(char *Path) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create) { *Path=0; HKEY hKey; - if (RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\WinRAR\\Paths",0, + if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) { - DWORD DataSize=NM,Type; - RegQueryValueEx(hKey,"AppData",0,&Type,(BYTE *)Path,&DataSize); + DWORD DataSize=(DWORD)MaxSize,Type; + RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize); RegCloseKey(hKey); } if (*Path==0 || !FileExist(Path)) - GetAppDataPath(Path); + if (!GetAppDataPath(Path,MaxSize,Create)) + { + GetModuleFileName(NULL,Path,(DWORD)MaxSize); + RemoveNameFromPath(Path); + } } #endif #ifndef SFX_MODULE -bool EnumConfigPaths(char *Path,int Number) +bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create) { -#ifdef _EMX - static char RARFileName[NM]; - if (Number==-1) - strcpy(RARFileName,Path); - if (Number!=0) - return(false); -#ifndef _DJGPP - if (_osmode==OS2_MODE) - { - PTIB ptib; - PPIB ppib; - DosGetInfoBlocks(&ptib, &ppib); - DosQueryModuleName(ppib->pib_hmte,NM,Path); - } - else -#endif - strcpy(Path,RARFileName); - RemoveNameFromPath(Path); - return(true); -#elif defined(_UNIX) - static const char *AltPath[]={ - "/etc","/etc/rar","/usr/lib","/usr/local/lib","/usr/local/etc" +#ifdef _UNIX + static const wchar *ConfPath[]={ + L"/etc", L"/etc/rar", L"/usr/lib", L"/usr/local/lib", L"/usr/local/etc" }; if (Number==0) { char *EnvStr=getenv("HOME"); - strncpy(Path, (EnvStr==NULL) ? AltPath[0] : EnvStr, NM-1); - Path[NM-1]=0; - return(true); + if (EnvStr!=NULL) + CharToWide(EnvStr,Path,MaxSize); + else + wcsncpyz(Path,ConfPath[0],MaxSize); + return true; } Number--; - if (Number<0 || Number>=sizeof(AltPath)/sizeof(AltPath[0])) - return(false); - strcpy(Path,AltPath[Number]); - return(true); -#elif defined(_WIN_32) - - if (Number<0 || Number>1) - return(false); + if (Number>=ASIZE(ConfPath)) + return false; + wcsncpyz(Path,ConfPath[Number], MaxSize); + return true; +#elif defined(_WIN_ALL) + if (Number>1) + return false; if (Number==0) - GetRarDataPath(Path); + GetRarDataPath(Path,MaxSize,Create); else { - GetModuleFileName(NULL,Path,NM); + GetModuleFileName(NULL,Path,(DWORD)MaxSize); RemoveNameFromPath(Path); } - return(true); - + return true; #else - return(false); + return false; #endif } #endif #ifndef SFX_MODULE -void GetConfigName(const char *Name,char *FullName,bool CheckExist) +void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create) { *FullName=0; - for (int I=0;EnumConfigPaths(FullName,I);I++) + for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++) { - AddEndSlash(FullName); - strcat(FullName,Name); + AddEndSlash(FullName,MaxSize); + wcsncatz(FullName,Name,MaxSize); if (!CheckExist || WildFileExist(FullName)) break; } @@ -422,18 +313,22 @@ void GetConfigName(const char *Name,char *FullName,bool CheckExist) #endif -// Returns a pointer to rightmost digit of volume number. -char* GetVolNumPart(char *ArcName) +// Returns a pointer to rightmost digit of volume number or to beginning +// of file name if numeric part is missing. +wchar* GetVolNumPart(const wchar *ArcName) { + if (*ArcName==0) + return (wchar *)ArcName; + // Pointing to last name character. - char *ChPtr=ArcName+strlen(ArcName)-1; + const wchar *ChPtr=ArcName+wcslen(ArcName)-1; // Skipping the archive extension. while (!IsDigit(*ChPtr) && ChPtr>ArcName) ChPtr--; // Skipping the numeric part of name. - char *NumPtr=ChPtr; + const wchar *NumPtr=ChPtr; while (IsDigit(*NumPtr) && NumPtr>ArcName) NumPtr--; @@ -443,41 +338,56 @@ char* GetVolNumPart(char *ArcName) { if (IsDigit(*NumPtr)) { - // Validate the first numeric part only if it has a dot somwhere + // Validate the first numeric part only if it has a dot somewhere // before it. - char *Dot=strchrd(PointToName(ArcName),'.'); + wchar *Dot=wcschr(PointToName(ArcName),'.'); if (Dot!=NULL && DotArcName && IsDigit(*NumPtr) && IsDigit(*(NumPtr-1))) - NumPtr--; - - // also copy the first character before volume number, - // because it can be changed when going from .r99 to .s00 - if (NumPtr>ArcName) - NumPtr--; - - int CharsToCopy=(int)(strlen(ArcName)-(NumPtr-ArcName)); - int DestPos=(int)(strlenw(ArcNameW)-CharsToCopy); - if (DestPos>=0) - { - CharToWide(NumPtr,ArcNameW+DestPos,MaxLength-DestPos-1); - ArcNameW[MaxLength-1]=0; - } - } } -bool IsNameUsable(const char *Name) +bool IsNameUsable(const wchar *Name) { #ifndef _UNIX - if (Name[0] && Name[1] && strchr(Name+2,':')!=NULL) - return(false); - for (const char *s=Name;*s!=0;s=charnext(s)) + if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL) + return false; + for (const wchar *s=Name;*s!=0;s++) { - if ((byte)*s<32) - return(false); - if (*s==' ' && IsPathDiv(s[1])) - return(false); + if ((uint)*s<32) + return false; + if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) + return false; } #endif - return(*Name!=0 && strpbrk(Name,"?*<>|\"")==NULL); + return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL; } void MakeNameUsable(char *Name,bool Extended) { +#ifdef _WIN_ALL + // In Windows we also need to convert characters not defined in current + // code page. This double conversion changes them to '?', which is + // catched by code below. + size_t NameLength=strlen(Name); + wchar NameW[NM]; + CharToWide(Name,NameW,ASIZE(NameW)); + WideToChar(NameW,Name,NameLength+1); + Name[NameLength]=0; +#endif for (char *s=Name;*s!=0;s=charnext(s)) { if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && (byte)*s<32) @@ -558,235 +455,555 @@ void MakeNameUsable(char *Name,bool Extended) #ifndef _UNIX if (s-Name>1 && *s==':') *s='_'; - if (*s==' ' && IsPathDiv(s[1])) + // Remove ' ' and '.' before path separator, but allow .\ and ..\. + if ((*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.') && IsPathDiv(s[1])) *s='_'; #endif } } -char* UnixSlashToDos(char *SrcName,char *DestName,uint MaxLength) +void MakeNameUsable(wchar *Name,bool Extended) { - if (DestName!=NULL && DestName!=SrcName) - if (strlen(SrcName)>=MaxLength) - { - *DestName=0; - return(DestName); - } - else - strcpy(DestName,SrcName); - for (char *s=SrcName;*s!=0;s=charnext(s)) + for (wchar *s=Name;*s!=0;s++) { - if (*s=='/') - if (DestName==NULL) - *s='\\'; - else - DestName[s-SrcName]='\\'; + if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32) + *s='_'; +#ifndef _UNIX + if (s-Name>1 && *s==':') + *s='_'; +#if 0 // We already can create such files. + // Remove ' ' and '.' before path separator, but allow .\ and ..\. + if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name && + !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2])))) + *s='_'; +#endif +#endif } - return(DestName==NULL ? SrcName:DestName); } -char* DosSlashToUnix(char *SrcName,char *DestName,uint MaxLength) +void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength) { - if (DestName!=NULL && DestName!=SrcName) - if (strlen(SrcName)>=MaxLength) - { - *DestName=0; - return(DestName); - } - else - strcpy(DestName,SrcName); - for (char *s=SrcName;*s!=0;s=charnext(s)) - { - if (*s=='\\') - if (DestName==NULL) - *s='/'; - else - DestName[s-SrcName]='/'; - } - return(DestName==NULL ? SrcName:DestName); + size_t Copied=0; + for (;Copied=MaxLength) - { - *DestName=0; - return(DestName); - } - else - strcpyw(DestName,SrcName); - for (wchar *s=SrcName;*s!=0;s++) - { - if (*s=='/') - if (DestName==NULL) - *s='\\'; - else - DestName[s-SrcName]='\\'; - } - return(DestName==NULL ? SrcName:DestName); + size_t Copied=0; + for (;Copied0) + *Dest=0; + return; + } +#ifdef _WIN_ALL + { + wchar FullName[NM],*NamePtr; + DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr); + if (Code==0 || Code>ASIZE(FullName)) + { + wchar LongName[NM]; + if (GetWinLongPath(Src,LongName,ASIZE(LongName))) + Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr); + } + if (Code!=0 && Code='A' && Letter<='Z' && IsDriveDiv(Path[1])); + return IsFullPath(Path) || IsPathDiv(Path[0]); } -bool IsDiskLetter(const wchar *Path) -{ - wchar Letter=etoupperw(Path[0]); - return(Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1])); -} - - -void GetPathRoot(const char *Path,char *Root) +void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize) { *Root=0; - if (IsDiskLetter(Path)) - sprintf(Root,"%c:\\",*Path); + if (IsDriveLetter(Path)) + swprintf(Root,MaxSize,L"%c:\\",*Path); else if (Path[0]=='\\' && Path[1]=='\\') { - const char *Slash=strchr(Path+2,'\\'); + const wchar *Slash=wcschr(Path+2,'\\'); if (Slash!=NULL) { size_t Length; - if ((Slash=strchr(Slash+1,'\\'))!=NULL) + if ((Slash=wcschr(Slash+1,'\\'))!=NULL) Length=Slash-Path+1; else - Length=strlen(Path); - strncpy(Root,Path,Length); + Length=wcslen(Path); + if (Length>=MaxSize) + Length=0; + wcsncpy(Root,Path,Length); Root[Length]=0; } } } -int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate) +int ParseVersionFileName(wchar *Name,bool Truncate) { int Version=0; - char *VerText=strrchrd(Name,';'); + wchar *VerText=wcsrchr(Name,';'); if (VerText!=NULL) { - Version=atoi(VerText+1); + Version=atoiw(VerText+1); if (Truncate) *VerText=0; } - if (NameW!=NULL) - { - wchar *VerTextW=strrchrw(NameW,';'); - if (VerTextW!=NULL) - { - if (Version==0) - Version=atoiw(VerTextW+1); - if (Truncate) - *VerTextW=0; - } - } - return(Version); + return Version; } -#if !defined(SFX_MODULE) && !defined(SETUP) -char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering) +#if !defined(SFX_MODULE) +// Get the name of first volume. Return the leftmost digit of volume number. +wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering) { if (FirstName!=VolName) - strcpy(FirstName,VolName); - char *VolNumStart=FirstName; + wcsncpyz(FirstName,VolName,MaxSize); + wchar *VolNumStart=FirstName; if (NewNumbering) { - int N='1'; - for (char *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) + wchar N='1'; + + // From the rightmost digit of volume number to the left. + for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) if (IsDigit(*ChPtr)) { - *ChPtr=N; + *ChPtr=N; // Set the rightmost digit to '1' and others to '0'. N='0'; } else if (N=='0') { - VolNumStart=ChPtr+1; + VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number. break; } } else { - SetExt(FirstName,"rar"); + // Old volume numbering scheme. Just set the extension to ".rar". + SetExt(FirstName,L"rar",MaxSize); VolNumStart=GetExt(FirstName); } if (!FileExist(FirstName)) { - char Mask[NM]; - strcpy(Mask,FirstName); - SetExt(Mask,"*"); + // If the first volume, which name we just generated, is not exist, + // check if volume with same name and any other extension is available. + // It can help in case of *.exe or *.sfx first volume. + wchar Mask[NM]; + wcsncpyz(Mask,FirstName,ASIZE(Mask)); + SetExt(Mask,L"*",ASIZE(Mask)); FindFile Find; Find.SetMask(Mask); - struct FindData FD; + FindData FD; while (Find.Next(&FD)) { Archive Arc; - if (Arc.Open(FD.Name,FD.NameW) && Arc.IsArchive(true) && !Arc.NotFirstVolume) + if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume) { - strcpy(FirstName,FD.Name); + wcsncpyz(FirstName,FD.Name,MaxSize); break; } } } - return(VolNumStart); + return VolNumStart; } #endif +#ifndef SFX_MODULE +static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent) +{ + bool Prefix=false; + if (*GenerateMask=='+') + { + Prefix=true; // Add the time string before the archive name. + GenerateMask++; // Skip '+' in the beginning of time mask. + } + + wchar Mask[MAX_GENERATE_MASK]; + wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask)); + + bool QuoteMode=false,Hours=false; + for (uint I=0;Mask[I]!=0;I++) + { + if (Mask[I]=='{' || Mask[I]=='}') + { + QuoteMode=(Mask[I]=='{'); + continue; + } + if (QuoteMode) + continue; + int CurChar=toupperw(Mask[I]); + if (CurChar=='H') + Hours=true; + + if (Hours && CurChar=='M') + { + // Replace minutes with 'I'. We use 'M' both for months and minutes, + // so we treat as minutes only those 'M' which are found after hours. + Mask[I]='I'; + } + if (CurChar=='N') + { + uint Digits=GetDigits(ArcNumber); + uint NCount=0; + while (toupperw(Mask[I+NCount])=='N') + NCount++; + + // Here we ensure that we have enough 'N' characters to fit all digits + // of archive number. We'll replace them by actual number later + // in this function. + if (NCount=4) + CurWeek++; + + char Field[10][6]; + + sprintf(Field[0],"%04u",rlt.Year); + sprintf(Field[1],"%02u",rlt.Month); + sprintf(Field[2],"%02u",rlt.Day); + sprintf(Field[3],"%02u",rlt.Hour); + sprintf(Field[4],"%02u",rlt.Minute); + sprintf(Field[5],"%02u",rlt.Second); + sprintf(Field[6],"%02u",(uint)CurWeek); + sprintf(Field[7],"%u",(uint)WeekDay+1); + sprintf(Field[8],"%03u",rlt.yDay+1); + sprintf(Field[9],"%05u",ArcNumber); + + const wchar *MaskChars=L"YMDHISWAEN"; + + int CField[sizeof(Field)/sizeof(Field[0])]; + memset(CField,0,sizeof(CField)); + QuoteMode=false; + for (uint I=0;Mask[I]!=0;I++) + { + if (Mask[I]=='{' || Mask[I]=='}') + { + QuoteMode=(Mask[I]=='{'); + continue; + } + if (QuoteMode) + continue; + const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I])); + if (ChPtr!=NULL) + CField[ChPtr-MaskChars]++; + } + + wchar DateText[MAX_GENERATE_MASK]; + *DateText=0; + QuoteMode=false; + for (size_t I=0,J=0;Mask[I]!=0 && J1) + { + // If we perform non-archiving operation, we need to use the last + // existing archive before the first unused name. So we generate + // the name for (ArcNumber-1) below. + wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName)); + GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber-1,ArcNumPresent); + } + break; + } + ArcNumber++; + } + wcsncpyz(ArcName,NewName,MaxSize); +} +#endif + + +wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize) { if (NameW!=NULL && *NameW!=0) { if (DestW!=NameW) - strcpyw(DestW,NameW); + wcsncpy(DestW,NameW,DestSize); } else - CharToWide(Name,DestW); - return(DestW); + if (Name!=NULL) + CharToWide(Name,DestW,DestSize); + else + *DestW=0; + + // Ensure that we return a zero terminate string for security reasons. + if (DestSize>0) + DestW[DestSize-1]=0; + + return DestW; } +#ifdef _WIN_ALL +// We should return 'true' even if resulting path is shorter than MAX_PATH, +// because we can also use this function to open files with non-standard +// characters, even if their path length is normal. +bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize) +{ + if (*Src==0) + return false; + const wchar *Prefix=L"\\\\?\\"; + const size_t PrefixLength=4; + bool FullPath=IsDriveLetter(Src) && IsPathDiv(Src[2]); + size_t SrcLength=wcslen(Src); + if (IsFullPath(Src)) // Paths in d:\path\name format. + { + if (IsDriveLetter(Src)) + { + if (MaxSize<=PrefixLength+SrcLength) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,Src,MaxSize); // "\\?\D:\very long path". + return true; + } + else + if (Src[0]=='\\' && Src[1]=='\\') + { + if (MaxSize<=PrefixLength+SrcLength+2) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,L"UNC",MaxSize); + wcsncatz(Dest,Src+1,MaxSize); // "\\?\UNC\server\share". + return true; + } + // We may be here only if we modify IsFullPath in the future. + return false; + } + else + { + wchar CurDir[NM]; + DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir); + if (DirCode==0 || DirCode>ASIZE(CurDir)-1) + return false; + + if (IsPathDiv(Src[0])) // Paths in \path\name format. + { + if (MaxSize<=PrefixLength+SrcLength+2) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + CurDir[2]=0; + wcsncatz(Dest,CurDir,MaxSize); // Copy drive letter 'd:'. + wcsncatz(Dest,Src,MaxSize); + return true; + } + else // Paths in path\name format. + { + AddEndSlash(CurDir,ASIZE(CurDir)); + if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,CurDir,MaxSize); + + if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname. + Src+=2; + + wcsncatz(Dest,Src,MaxSize); + return true; + } + } + return false; +} +// Convert Unix, OS X and Android decomposed chracters to Windows precomposed. +void ConvertToPrecomposed(wchar *Name,size_t NameSize) +{ + wchar FileName[NM]; + if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP. + FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0) + { + FileName[ASIZE(FileName)-1]=0; + wcsncpyz(Name,FileName,NameSize); + } +} + + +// Remove trailing spaces and dots in file name and in dir names in path. +void MakeNameCompatible(wchar *Name) +{ + int Src=0,Dest=0; + while (true) + { + if (IsPathDiv(Name[Src]) || Name[Src]==0) + for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--) + { + // Permit path1/./path2 and ../path1 paths. + if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) + break; + Dest--; + } + Name[Dest]=Name[Src]; + if (Name[Src]==0) + break; + Src++; + Dest++; + } +} +#endif diff --git a/libunrar/pathfn.hpp b/libunrar/pathfn.hpp index 6b0ac31..63813d8 100644 --- a/libunrar/pathfn.hpp +++ b/libunrar/pathfn.hpp @@ -1,49 +1,76 @@ #ifndef _RAR_PATHFN_ #define _RAR_PATHFN_ -char* PointToName(const char *Path); wchar* PointToName(const wchar *Path); -char* PointToLastChar(const char *Path); -char* ConvertPath(const char *SrcPath,char *DestPath); -wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath); -void SetExt(char *Name,const char *NewExt); -void SetExt(wchar *Name,const wchar *NewExt); -void SetSFXExt(char *SFXName); -void SetSFXExt(wchar *SFXName); -char *GetExt(const char *Name); +wchar* PointToLastChar(const wchar *Path); +wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize); +void SetName(wchar *FullName,const wchar *Name,size_t MaxSize); +void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize); +void SetSFXExt(wchar *SFXName,size_t MaxSize); wchar *GetExt(const wchar *Name); -bool CmpExt(const char *Name,const char *Ext); -bool IsWildcard(const char *Str,const wchar *StrW=NULL); +bool CmpExt(const wchar *Name,const wchar *Ext); +bool IsWildcard(const wchar *Str); bool IsPathDiv(int Ch); bool IsDriveDiv(int Ch); -int GetPathDisk(const char *Path); -void AddEndSlash(char *Path); -void AddEndSlash(wchar *Path); -void GetFilePath(const char *FullName,char *Path,int MaxLength); -void GetFilePath(const wchar *FullName,wchar *Path,int MaxLength); -void RemoveNameFromPath(char *Path); +bool IsDriveLetter(const wchar *Path); +int GetPathDisk(const wchar *Path); +void AddEndSlash(wchar *Path,size_t MaxLength); +void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize); +void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength); void RemoveNameFromPath(wchar *Path); -void GetAppDataPath(char *Path); -void GetRarDataPath(char *Path); -bool EnumConfigPaths(char *Path,int Number); -void GetConfigName(const char *Name,char *FullName,bool CheckExist); -char* GetVolNumPart(char *ArcName); -void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumbering); -bool IsNameUsable(const char *Name); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create); +void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create); +#endif +#ifndef SFX_MODULE +bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create); +void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create); +#endif +wchar* GetVolNumPart(const wchar *ArcName); +void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering); +bool IsNameUsable(const wchar *Name); void MakeNameUsable(char *Name,bool Extended); -char* UnixSlashToDos(char *SrcName,char *DestName=NULL,uint MaxLength=NM); -char* DosSlashToUnix(char *SrcName,char *DestName=NULL,uint MaxLength=NM); -wchar* UnixSlashToDos(wchar *SrcName,wchar *DestName=NULL,uint MaxLength=NM); -bool IsFullPath(const char *Path); +void MakeNameUsable(wchar *Name,bool Extended); + +void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength); +void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength); +void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength); +void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength); + +inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength) +{ +#ifdef _WIN_ALL + UnixSlashToDos(SrcName,DestName,MaxLength); +#else + DosSlashToUnix(SrcName,DestName,MaxLength); +#endif +} + +inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength) +{ +#ifdef _WIN_ALL + UnixSlashToDos(SrcName,DestName,MaxLength); +#else + DosSlashToUnix(SrcName,DestName,MaxLength); +#endif +} + +void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize); bool IsFullPath(const wchar *Path); -bool IsDiskLetter(const char *Path); -bool IsDiskLetter(const wchar *Path); -void GetPathRoot(const char *Path,char *Root); -int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate); -char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering); -wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW); +bool IsFullRootPath(const wchar *Path); +void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize); +int ParseVersionFileName(wchar *Name,bool Truncate); +wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering); +wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize); +#ifndef SFX_MODULE +void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving); +#endif -inline char* GetOutputName(const char *Name) {return((char *)Name);}; +#ifdef _WIN_ALL +bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize); +void ConvertToPrecomposed(wchar *Name,size_t NameSize); +void MakeNameCompatible(wchar *Name); +#endif #endif diff --git a/libunrar/qopen.cpp b/libunrar/qopen.cpp new file mode 100644 index 0000000..43346b0 --- /dev/null +++ b/libunrar/qopen.cpp @@ -0,0 +1,300 @@ +#include "rar.hpp" + +QuickOpen::QuickOpen() +{ + Buf=NULL; + Init(NULL,false); +} + + +QuickOpen::~QuickOpen() +{ + Close(); + delete[] Buf; +} + + +void QuickOpen::Init(Archive *Arc,bool WriteMode) +{ + if (Arc!=NULL) // Unless called from constructor. + Close(); + + QuickOpen::Arc=Arc; + QuickOpen::WriteMode=WriteMode; + + ListStart=NULL; + ListEnd=NULL; + + if (Buf==NULL) + Buf=new byte[MaxBufSize]; + + CurBufSize=0; // Current size of buffered data in write mode. + + Loaded=false; +} + + +void QuickOpen::Close() +{ + QuickOpenItem *Item=ListStart; + while (Item!=NULL) + { + QuickOpenItem *Next=Item->Next; + delete[] Item->Header; + delete Item; + Item=Next; + } +} + + + + + + + + + + + + + + +void QuickOpen::Load(uint64 BlockPos) +{ + if (!Loaded) + { + // If loading for the first time, perform additional intialization. + SeekPos=Arc->Tell(); + UnsyncSeekPos=false; + + int64 SavePos=SeekPos; + Arc->Seek(BlockPos,SEEK_SET); + + // If BlockPos points to original main header, we'll have the infinite + // recursion, because ReadHeader() for main header will attempt to load + // QOpen and call QuickOpen::Load again. If BlockPos points to long chain + // of other main headers, we'll have multiple recursive calls of this + // function wasting resources. So we prohibit QOpen temporarily to + // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator + // and QOpenOffset fields, so we cannot use them to prohibit QOpen. + Arc->SetProhibitQOpen(true); + size_t ReadSize=Arc->ReadHeader(); + Arc->SetProhibitQOpen(false); + + if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE || + !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN)) + { + Arc->Seek(SavePos,SEEK_SET); + return; + } + QOHeaderPos=Arc->CurBlockPos; + RawDataStart=Arc->Tell(); + RawDataSize=Arc->SubHead.UnpSize; + Arc->Seek(SavePos,SEEK_SET); + + Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader. + } + + if (Arc->SubHead.Encrypted) + { + RAROptions *Cmd=Arc->GetRAROptions(); +#ifndef RAR_NOCRYPT + if (Cmd->Password.IsSet()) + Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt, + Arc->SubHead.InitV,Arc->SubHead.Lg2Count, + Arc->SubHead.HashKey,Arc->SubHead.PswCheck); + else +#endif + { + Loaded=false; + return; + } + } + + RawDataPos=0; + ReadBufSize=0; + ReadBufPos=0; + LastReadHeader.Reset(); + LastReadHeaderPos=0; + + ReadBuffer(); +} + + +bool QuickOpen::Read(void *Data,size_t Size,size_t &Result) +{ + if (!Loaded) + return false; + // Find next suitable cached block. + while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos) + if (!ReadNext()) + break; + if (!Loaded) + { + // If something wrong happened, let's set the correct file pointer + // and stop further quick open processing. + if (UnsyncSeekPos) + Arc->File::Seek(SeekPos,SEEK_SET); + return false; + } + + if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size()) + { + memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size); + Result=Size; + SeekPos+=Size; + UnsyncSeekPos=true; + } + else + { + if (UnsyncSeekPos) + { + Arc->File::Seek(SeekPos,SEEK_SET); + UnsyncSeekPos=false; + } + int ReadSize=Arc->File::Read(Data,Size); + if (ReadSize<0) + { + Loaded=false; + return false; + } + Result=ReadSize; + SeekPos+=ReadSize; + } + + return true; +} + + +bool QuickOpen::Seek(int64 Offset,int Method) +{ + if (!Loaded) + return false; + + // Normally we process an archive sequentially from beginning to end, + // so we read quick open data sequentially. But some operations like + // archive updating involve several passes. So if we detect that file + // pointer is moved back, we reload quick open data from beginning. + if (Method==SEEK_SET && (uint64)OffsetFile::Seek(Offset,SEEK_END); + SeekPos=Arc->File::Tell(); + UnsyncSeekPos=false; + } + return true; +} + + +bool QuickOpen::Tell(int64 *Pos) +{ + if (!Loaded) + return false; + *Pos=SeekPos; + return true; +} + + +uint QuickOpen::ReadBuffer() +{ + int64 SavePos=Arc->Tell(); + Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET); + size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize); + if (Arc->SubHead.Encrypted) + SizeToRead &= ~CRYPT_BLOCK_MASK; + int ReadSize=0; + if (SizeToRead!=0) + { + ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead); + if (ReadSize<=0) + ReadSize=0; + else + { +#ifndef RAR_NOCRYPT + if (Arc->SubHead.Encrypted) + Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK); +#endif + RawDataPos+=ReadSize; + ReadBufSize+=ReadSize; + } + } + Arc->Seek(SavePos,SEEK_SET); + return ReadSize; +} + + +// Fill RawRead object from buffer. +bool QuickOpen::ReadRaw(RawRead &Raw) +{ + if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer. + { + // Ensure that we have enough data to read CRC and header size. + size_t DataLeft=ReadBufSize-ReadBufPos; + memcpy(Buf,Buf+ReadBufPos,DataLeft); + ReadBufPos=0; + ReadBufSize=DataLeft; + ReadBuffer(); + } + const size_t FirstReadSize=7; + if (ReadBufPos+FirstReadSize>ReadBufSize) + return false; + Raw.Read(Buf+ReadBufPos,FirstReadSize); + ReadBufPos+=FirstReadSize; + + uint SavedCRC=Raw.Get4(); + uint SizeBytes=Raw.GetVSize(4); + uint64 BlockSize=Raw.GetV(); + int SizeToRead=int(BlockSize); + SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any. + if (SizeToRead<0 || SizeBytes==0 || BlockSize==0) + { + Loaded=false; // Invalid data. + return false; + } + + // If rest of block data crosses Buf boundary, read it in loop. + while (SizeToRead>0) + { + size_t DataLeft=ReadBufSize-ReadBufPos; + size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead); + Raw.Read(Buf+ReadBufPos,CurSizeToRead); + ReadBufPos+=CurSizeToRead; + SizeToRead-=int(CurSizeToRead); + if (SizeToRead>0) // We read the entire buffer and still need more data. + { + ReadBufPos=0; + ReadBufSize=0; + if (ReadBuffer()==0) + return false; + } + } + + return SavedCRC==Raw.GetCRC50(); +} + + +// Read next cached header. +bool QuickOpen::ReadNext() +{ + RawRead Raw(NULL); + if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block. + return false; + uint Flags=(uint)Raw.GetV(); + uint64 Offset=Raw.GetV(); + size_t HeaderSize=(size_t)Raw.GetV(); + if (HeaderSize>MAX_HEADER_SIZE_RAR5) + return false; + LastReadHeader.Alloc(HeaderSize); + Raw.GetB(&LastReadHeader[0],HeaderSize); + // Calculate the absolute position as offset from quick open service header. + LastReadHeaderPos=QOHeaderPos-Offset; + return true; +} diff --git a/libunrar/qopen.hpp b/libunrar/qopen.hpp new file mode 100644 index 0000000..d745cea --- /dev/null +++ b/libunrar/qopen.hpp @@ -0,0 +1,61 @@ +#ifndef _RAR_QOPEN_ +#define _RAR_QOPEN_ + +struct QuickOpenItem +{ + byte *Header; + size_t HeaderSize; + uint64 ArcPos; + QuickOpenItem *Next; +}; + + +class Archive; +class RawRead; + +class QuickOpen +{ + private: + void Close(); + + + uint ReadBuffer(); + bool ReadRaw(RawRead &Raw); + bool ReadNext(); + + Archive *Arc; + bool WriteMode; + + QuickOpenItem *ListStart; + QuickOpenItem *ListEnd; + + byte *Buf; // Read quick open data here. + static const size_t MaxBufSize=0x10000; // Buf size, must be multiple of CRYPT_BLOCK_SIZE. + size_t CurBufSize; // Current size of buffered data in write mode. +#ifndef RAR_NOCRYPT // For shell extension. + CryptData Crypt; +#endif + + bool Loaded; + uint64 QOHeaderPos; // Main QO header position. + uint64 RawDataStart; // Start of QO data, just after the main header. + uint64 RawDataSize; // Size of entire QO data. + uint64 RawDataPos; // Current read position in QO data. + size_t ReadBufSize; // Size of Buf data currently read from QO. + size_t ReadBufPos; // Current read position in Buf data. + Array LastReadHeader; + uint64 LastReadHeaderPos; + uint64 SeekPos; + bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer. + public: + QuickOpen(); + ~QuickOpen(); + void Init(Archive *Arc,bool WriteMode); + void Load(uint64 BlockPos); + void Unload() { Loaded=false; } + bool Read(void *Data,size_t Size,size_t &Result); + bool Seek(int64 Offset,int Method); + bool Tell(int64 *Pos); +}; + +#endif diff --git a/libunrar/rar.cpp b/libunrar/rar.cpp index 4eda0bf..34b4b27 100644 --- a/libunrar/rar.cpp +++ b/libunrar/rar.cpp @@ -1,135 +1,106 @@ #include "rar.hpp" -#if !defined(GUI) && !defined(RARDLL) +#if !defined(RARDLL) int main(int argc, char *argv[]) { + #ifdef _UNIX setlocale(LC_ALL,""); #endif -#if defined(_EMX) && !defined(_DJGPP) - uni_init(0); -#endif - -#if !defined(_SFX_RTL_) && !defined(_WIN_32) - setbuf(stdout,NULL); -#endif - -#if !defined(SFX_MODULE) && defined(_EMX) - EnumConfigPaths(argv[0],-1); -#endif - + InitConsole(); ErrHandler.SetSignalHandlers(true); - RARInitData(); - #ifdef SFX_MODULE - char ModuleName[NM]; -#ifdef _WIN_32 - GetModuleFileName(NULL,ModuleName,sizeof(ModuleName)); + wchar ModuleName[NM]; +#ifdef _WIN_ALL + GetModuleFileName(NULL,ModuleName,ASIZE(ModuleName)); #else - strcpy(ModuleName,argv[0]); + CharToWide(argv[0],ModuleName,ASIZE(ModuleName)); #endif #endif -#ifdef _WIN_32 +#ifdef _WIN_ALL SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); #endif -#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT) - bool ShutdownOnClose; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + // Must be initialized, normal initialization can be skipped in case of + // exception. + POWER_MODE ShutdownOnClose=POWERMODE_KEEP; #endif -#ifdef ALLOW_EXCEPTIONS try -#endif { - CommandData Cmd; + CommandData *Cmd=new CommandData; #ifdef SFX_MODULE - strcpy(Cmd.Command,"X"); - char *Switch=NULL; -#ifdef _SFX_RTL_ - char *CmdLine=GetCommandLine(); - if (CmdLine!=NULL && *CmdLine=='\"') - CmdLine=strchr(CmdLine+1,'\"'); - if (CmdLine!=NULL && (CmdLine=strpbrk(CmdLine," /"))!=NULL) - { - while (IsSpace(*CmdLine)) - CmdLine++; - Switch=CmdLine; - } -#else - Switch=argc>1 ? argv[1]:NULL; -#endif - if (Switch!=NULL && Cmd.IsSwitch(Switch[0])) + wcsncpyz(Cmd->Command,L"X",ASIZE(Cmd->Command)); + char *Switch=argc>1 ? argv[1]:NULL; + if (Switch!=NULL && Cmd->IsSwitch(Switch[0])) { int UpperCmd=etoupper(Switch[1]); switch(UpperCmd) { case 'T': case 'V': - Cmd.Command[0]=UpperCmd; + Cmd->Command[0]=UpperCmd; break; case '?': - Cmd.OutHelp(); + Cmd->OutHelp(RARX_SUCCESS); break; } } - Cmd.AddArcName(ModuleName,NULL); -#else - if (Cmd.IsConfigEnabled(argc,argv)) + Cmd->AddArcName(ModuleName); + Cmd->ParseDone(); + Cmd->AbsoluteLinks=true; // If users runs SFX, he trusts an archive source. +#else // !SFX_MODULE + Cmd->ParseCommandLine(true,argc,argv); + if (!Cmd->ConfigDisabled) { - Cmd.ReadConfig(argc,argv); - Cmd.ParseEnvVar(); + Cmd->ReadConfig(); + Cmd->ParseEnvVar(); } - for (int I=1;IParseCommandLine(false,argc,argv); #endif - InitConsoleOptions(Cmd.MsgStream,Cmd.Sound); - InitLogOptions(Cmd.LogName); - ErrHandler.SetSilent(Cmd.AllYes || Cmd.MsgStream==MSG_NULL); - ErrHandler.SetShutdown(Cmd.Shutdown); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + ShutdownOnClose=Cmd->Shutdown; + if (ShutdownOnClose) + ShutdownCheckAnother(true); +#endif - Cmd.OutTitle(); - Cmd.ProcessCommand(); + uiInit(Cmd->Sound); + InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset); + ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL); + + Cmd->OutTitle(); + Cmd->ProcessCommand(); + delete Cmd; } -#ifdef ALLOW_EXCEPTIONS - catch (int ErrCode) + catch (RAR_EXIT ErrCode) { ErrHandler.SetErrorCode(ErrCode); } -#ifdef ENABLE_BAD_ALLOC - catch (bad_alloc) + catch (std::bad_alloc&) { - ErrHandler.SetErrorCode(MEMORY_ERROR); + ErrHandler.MemoryErrorMsg(); + ErrHandler.SetErrorCode(RARX_MEMORY); } -#endif catch (...) { - ErrHandler.SetErrorCode(FATAL_ERROR); + ErrHandler.SetErrorCode(RARX_FATAL); } + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (ShutdownOnClose!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled() && + !ShutdownCheckAnother(false)) + Shutdown(ShutdownOnClose); #endif - File::RemoveCreated(); -#if defined(SFX_MODULE) && defined(_DJGPP) - _chmod(ModuleName,1,0x20); -#endif -#if defined(_EMX) && !defined(_DJGPP) - uni_done(); -#endif -#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT) - if (ShutdownOnClose) - Shutdown(); -#endif - return(ErrHandler.GetErrorCode()); + ErrHandler.MainExit=true; + return ErrHandler.GetErrorCode(); } #endif diff --git a/libunrar/rar.hpp b/libunrar/rar.hpp index 218830c..3f7414c 100644 --- a/libunrar/rar.hpp +++ b/libunrar/rar.hpp @@ -2,61 +2,70 @@ #define _RAR_RARCOMMON_ #include "raros.hpp" +#include "rartypes.hpp" #include "os.hpp" #ifdef RARDLL #include "dll.hpp" #endif - -#ifndef _WIN_CE #include "version.hpp" -#endif -#include "rartypes.hpp" #include "rardefs.hpp" #include "rarlang.hpp" #include "unicode.hpp" #include "errhnd.hpp" +#include "secpassword.hpp" #include "array.hpp" #include "timefn.hpp" +#include "sha1.hpp" +#include "sha256.hpp" +#include "blake2s.hpp" +#include "hash.hpp" #include "options.hpp" +#include "rijndael.hpp" +#include "crypt.hpp" +#include "headers5.hpp" #include "headers.hpp" #include "pathfn.hpp" #include "strfn.hpp" #include "strlist.hpp" +#ifdef _WIN_ALL +#include "isnt.hpp" +#endif #include "file.hpp" -#include "sha1.hpp" #include "crc.hpp" -#include "rijndael.hpp" -#include "crypt.hpp" +#include "ui.hpp" #include "filefn.hpp" #include "filestr.hpp" #include "find.hpp" #include "scantree.hpp" -#include "savepos.hpp" #include "getbits.hpp" #include "rdwrfn.hpp" +#ifdef USE_QOPEN +#include "qopen.hpp" +#endif #include "archive.hpp" #include "match.hpp" #include "cmddata.hpp" #include "filcreat.hpp" #include "consio.hpp" #include "system.hpp" -#include "isnt.hpp" #include "log.hpp" +#include "rawint.hpp" #include "rawread.hpp" #include "encname.hpp" #include "resource.hpp" #include "compress.hpp" - #include "rarvm.hpp" #include "model.hpp" +#include "threadpool.hpp" #include "unpack.hpp" + #include "extinfo.hpp" #include "extract.hpp" @@ -66,12 +75,22 @@ #include "rs.hpp" +#include "rs16.hpp" + + + #include "recvol.hpp" #include "volume.hpp" #include "smallfn.hpp" -#include "ulinks.hpp" #include "global.hpp" +#if 0 +#include "benchmark.hpp" +#endif + + + + #endif diff --git a/libunrar/rardefs.hpp b/libunrar/rardefs.hpp index 24f97b5..095792a 100644 --- a/libunrar/rardefs.hpp +++ b/libunrar/rardefs.hpp @@ -4,21 +4,28 @@ #define Min(x,y) (((x)<(y)) ? (x):(y)) #define Max(x,y) (((x)>(y)) ? (x):(y)) +// Universal replacement of abs function. +#define Abs(x) (((x)<0) ? -(x):(x)) + #define ASIZE(x) (sizeof(x)/sizeof(x[0])) +// MAXPASSWORD is expected to be multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE (16) +// for CryptProtectMemory in SecPassword. #define MAXPASSWORD 128 -#define MAXSFXSIZE 0x80000 -#define DefSFXName "default.sfx" -#define DefSortListName "rarfiles.lst" +#define MAXSFXSIZE 0x200000 -#ifndef FA_RDONLY - #define FA_RDONLY 0x01 - #define FA_HIDDEN 0x02 - #define FA_SYSTEM 0x04 - #define FA_LABEL 0x08 - #define FA_DIREC 0x10 - #define FA_ARCH 0x20 +#define MAXCMTSIZE 0x40000 + +#define DefSFXName L"default.sfx" +#define DefSortListName L"rarfiles.lst" + + +#ifndef SFX_MODULE +#define USE_QOPEN #endif +// Produce the value, which is equal or larger than 'v' and aligned to 'a'. +#define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) ) + #endif diff --git a/libunrar/raros.hpp b/libunrar/raros.hpp index e686798..4f4f2ae 100644 --- a/libunrar/raros.hpp +++ b/libunrar/raros.hpp @@ -11,22 +11,17 @@ #endif #if defined(__WIN32__) || defined(_WIN32) - #define _WIN_32 -#endif - -#ifdef _WIN32_WCE - #define _WIN_32 - #define _WIN_CE - #ifdef WM_FILECHANGEINFO - #define PC2002 + #define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop. + #ifdef _M_X64 + #define _WIN_64 #else - #undef PC2002 + #define _WIN_32 #endif #endif -#ifdef __BEOS__ +#if defined(ANDROID) || defined(__ANDROID__) #define _UNIX - #define _BEOS + #define _ANDROID #endif #ifdef __APPLE__ @@ -34,7 +29,7 @@ #define _APPLE #endif -#if !defined(_EMX) && !defined(_WIN_32) && !defined(_BEOS) && !defined(_APPLE) +#if !defined(_EMX) && !defined(_WIN_ALL) && !defined(_BEOS) && !defined(_APPLE) #define _UNIX #endif diff --git a/libunrar/rarpch.cpp b/libunrar/rarpch.cpp new file mode 100644 index 0000000..c070cf7 --- /dev/null +++ b/libunrar/rarpch.cpp @@ -0,0 +1,2 @@ +// We use rarpch.cpp to create precompiled headers for MS Visual C++. +#include "rar.hpp" diff --git a/libunrar/rartypes.hpp b/libunrar/rartypes.hpp index 595592e..3d3111b 100644 --- a/libunrar/rartypes.hpp +++ b/libunrar/rartypes.hpp @@ -1,47 +1,32 @@ #ifndef _RAR_TYPES_ #define _RAR_TYPES_ -typedef unsigned char byte; // unsigned 8 bits -typedef unsigned short ushort; // preferably 16 bits, but can be more -typedef unsigned int uint; // 32 bits or more +#include -#define PRESENT_INT32 // undefine if signed 32 bits is not available - -typedef unsigned int uint32; // 32 bits exactly -typedef signed int int32; // signed 32 bits exactly - -// If compiler does not support 64 bit variables, we can define -// uint64 and int64 as 32 bit, but it will limit the maximum processed -// file size to 2 GB. -#if defined(__BORLANDC__) || defined(_MSC_VER) -typedef unsigned __int64 uint64; // unsigned 64 bits -typedef signed __int64 int64; // signed 64 bits -#else -typedef unsigned long long uint64; // unsigned 64 bits -typedef signed long long int64; // signed 64 bits -#endif - - -#if defined(_WIN_32) || defined(__GNUC__) || defined(__sgi) || defined(_AIX) || defined(__sun) || defined(__hpux) || defined(_OSF_SOURCE) -typedef wchar_t wchar; -#else -typedef ushort wchar; -#endif +typedef uint8_t byte; // Unsigned 8 bits. +typedef uint16_t ushort; // Preferably 16 bits, but can be more. +typedef unsigned int uint; // 32 bits or more. +typedef uint32_t uint32; // 32 bits exactly. +typedef int32_t int32; // Signed 32 bits exactly. +typedef uint64_t uint64; // 64 bits exactly. +typedef int64_t int64; // Signed 64 bits exactly. +typedef wchar_t wchar; // Unicode character // Get lowest 16 bits. -#define SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff)) - -// Get lowest 32 bits. -#define UINT32(x) (sizeof(uint32)==4 ? (uint32)(x):((x)&0xffffffff)) +#define GET_SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff)) // Make 64 bit integer from two 32 bit. #define INT32TO64(high,low) ((((uint64)(high))<<32)+((uint64)low)) -// Special int64 value, large enough to be never found in real life. +// Maximum int64 value. +#define MAX_INT64 int64(INT32TO64(0x7fffffff,0xffffffff)) + +// Special int64 value, large enough to never be found in real life +// and small enough to fit to both signed and unsigned 64-bit ints. // We use it in situations, when we need to indicate that parameter // is not defined and probably should be calculated inside of function. // Lower part is intentionally 0x7fffffff, not 0xffffffff, to make it -// compatible with 32 bit int64. +// compatible with 32 bit int64 if 64 bit type is not supported. #define INT64NDF INT32TO64(0x7fffffff,0x7fffffff) #endif diff --git a/libunrar/rarvm.cpp b/libunrar/rarvm.cpp index 44c1151..8d8675a 100644 --- a/libunrar/rarvm.cpp +++ b/libunrar/rarvm.cpp @@ -1,7 +1,5 @@ #include "rar.hpp" -#include "rarvmtbl.cpp" - RarVM::RarVM() { Mem=NULL; @@ -20,711 +18,53 @@ void RarVM::Init() Mem=new byte[VM_MEMSIZE+4]; } -/********************************************************************* - IS_VM_MEM macro checks if address belongs to VM memory pool (Mem). - Only Mem data are always low endian regardless of machine architecture, - so we need to convert them to native format when reading or writing. - VM registers have endianness of host machine. -**********************************************************************/ -#define IS_VM_MEM(a) (((byte*)a)>=Mem && ((byte*)a)>8); - ((byte *)Addr)[2]=(byte)(Value>>16); - ((byte *)Addr)[3]=(byte)(Value>>24); - } - else - *(uint *)Addr=Value; -#else - *(uint32 *)Addr=Value; -#endif - } -} - -#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) - #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint *)Addr,Value) -#else - #define SET_VALUE(ByteMode,Addr,Value) ((ByteMode) ? (*(byte *)(Addr)=((byte)(Value))):(*(uint32 *)(Addr)=((uint32)(Value)))) -#endif - - -void RarVM::SetLowEndianValue(uint *Addr,uint Value) -{ -#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) - ((byte *)Addr)[0]=(byte)Value; - ((byte *)Addr)[1]=(byte)(Value>>8); - ((byte *)Addr)[2]=(byte)(Value>>16); - ((byte *)Addr)[3]=(byte)(Value>>24); -#else - *(uint32 *)Addr=Value; -#endif -} - - -inline uint* RarVM::GetOperand(VM_PreparedOperand *CmdOp) -{ - if (CmdOp->Type==VM_OPREGMEM) - return((uint *)&Mem[(*CmdOp->Addr+CmdOp->Base)&VM_MEMMASK]); - else - return(CmdOp->Addr); -} - void RarVM::Execute(VM_PreparedProgram *Prg) { memcpy(R,Prg->InitR,sizeof(Prg->InitR)); - size_t GlobalSize=Min(Prg->GlobalData.Size(),VM_GLOBALMEMSIZE); - if (GlobalSize) - memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize); - size_t StaticSize=Min(Prg->StaticData.Size(),VM_GLOBALMEMSIZE-GlobalSize); - if (StaticSize) - memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize); - - R[7]=VM_MEMSIZE; - Flags=0; - - VM_PreparedCommand *PreparedCode=Prg->AltCmd ? Prg->AltCmd:&Prg->Cmd[0]; - if (Prg->CmdCount>0 && !ExecuteCode(PreparedCode,Prg->CmdCount)) + Prg->FilteredData=NULL; + if (Prg->Type!=VMSF_NONE) { - // Invalid VM program. Let's replace it with 'return' command. - PreparedCode[0].OpCode=VM_RET; - } - uint NewBlockPos=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20])&VM_MEMMASK; - uint NewBlockSize=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c])&VM_MEMMASK; - if (NewBlockPos+NewBlockSize>=VM_MEMSIZE) - NewBlockPos=NewBlockSize=0; - Prg->FilteredData=Mem+NewBlockPos; - Prg->FilteredDataSize=NewBlockSize; - - Prg->GlobalData.Reset(); - - uint DataSize=Min(GET_VALUE(false,(uint*)&Mem[VM_GLOBALMEMADDR+0x30]),VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE); - if (DataSize!=0) - { - Prg->GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE); - memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE); + bool Success=ExecuteStandardFilter(Prg->Type); + uint BlockSize=Prg->InitR[4] & VM_MEMMASK; + Prg->FilteredDataSize=BlockSize; + if (Prg->Type==VMSF_DELTA || Prg->Type==VMSF_RGB || Prg->Type==VMSF_AUDIO) + Prg->FilteredData=2*BlockSize>VM_MEMSIZE || !Success ? Mem:Mem+BlockSize; + else + Prg->FilteredData=Mem; } } -/* -Note: - Due to performance considerations RAR VM may set VM_FS, VM_FC, VM_FZ - incorrectly for byte operands. These flags are always valid only - for 32-bit operands. Check implementation of concrete VM command - to see if it sets flags right. -*/ - -#define SET_IP(IP) \ - if ((IP)>=CodeSize) \ - return(true); \ - if (--MaxOpCount<=0) \ - return(false); \ - Cmd=PreparedCode+(IP); - -bool RarVM::ExecuteCode(VM_PreparedCommand *PreparedCode,uint CodeSize) -{ - int MaxOpCount=25000000; - VM_PreparedCommand *Cmd=PreparedCode; - while (1) - { -#ifndef NORARVM - // Get addresses to quickly access operands. - uint *Op1=GetOperand(&Cmd->Op1); - uint *Op2=GetOperand(&Cmd->Op2); -#endif - switch(Cmd->OpCode) - { -#ifndef NORARVM - case VM_MOV: - SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); - break; -#ifdef VM_OPTIMIZE - case VM_MOVB: - SET_VALUE(true,Op1,GET_VALUE(true,Op2)); - break; - case VM_MOVD: - SET_VALUE(false,Op1,GET_VALUE(false,Op2)); - break; -#endif - case VM_CMP: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - } - break; -#ifdef VM_OPTIMIZE - case VM_CMPB: - { - uint Value1=GET_VALUE(true,Op1); - uint Result=UINT32(Value1-GET_VALUE(true,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - } - break; - case VM_CMPD: - { - uint Value1=GET_VALUE(false,Op1); - uint Result=UINT32(Value1-GET_VALUE(false,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - } - break; -#endif - case VM_ADD: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)); - if (Cmd->ByteMode) - { - Result&=0xff; - Flags=(ResultByteMode,Op1,Result); - } - break; -#ifdef VM_OPTIMIZE - case VM_ADDB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)+GET_VALUE(true,Op2)); - break; - case VM_ADDD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)+GET_VALUE(false,Op2)); - break; -#endif - case VM_SUB: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; -#ifdef VM_OPTIMIZE - case VM_SUBB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)-GET_VALUE(true,Op2)); - break; - case VM_SUBD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)-GET_VALUE(false,Op2)); - break; -#endif - case VM_JZ: - if ((Flags & VM_FZ)!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JNZ: - if ((Flags & VM_FZ)==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_INC: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)+1); - if (Cmd->ByteMode) - Result&=0xff; - SET_VALUE(Cmd->ByteMode,Op1,Result); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - } - break; -#ifdef VM_OPTIMIZE - case VM_INCB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)+1); - break; - case VM_INCD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)+1); - break; -#endif - case VM_DEC: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)-1); - SET_VALUE(Cmd->ByteMode,Op1,Result); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - } - break; -#ifdef VM_OPTIMIZE - case VM_DECB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)-1); - break; - case VM_DECD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)-1); - break; -#endif - case VM_JMP: - SET_IP(GET_VALUE(false,Op1)); - continue; - case VM_XOR: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)^GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_AND: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_OR: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)|GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_TEST: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - } - break; - case VM_JS: - if ((Flags & VM_FS)!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JNS: - if ((Flags & VM_FS)==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JB: - if ((Flags & VM_FC)!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JBE: - if ((Flags & (VM_FC|VM_FZ))!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JA: - if ((Flags & (VM_FC|VM_FZ))==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JAE: - if ((Flags & VM_FC)==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_PUSH: - R[7]-=4; - SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],GET_VALUE(false,Op1)); - break; - case VM_POP: - SET_VALUE(false,Op1,GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); - R[7]+=4; - break; - case VM_CALL: - R[7]-=4; - SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Cmd-PreparedCode+1); - SET_IP(GET_VALUE(false,Op1)); - continue; - case VM_NOT: - SET_VALUE(Cmd->ByteMode,Op1,~GET_VALUE(Cmd->ByteMode,Op1)); - break; - case VM_SHL: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Value2=GET_VALUE(Cmd->ByteMode,Op2); - uint Result=UINT32(Value1<ByteMode,Op1,Result); - } - break; - case VM_SHR: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Value2=GET_VALUE(Cmd->ByteMode,Op2); - uint Result=UINT32(Value1>>Value2); - Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_SAR: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Value2=GET_VALUE(Cmd->ByteMode,Op2); - uint Result=UINT32(((int)Value1)>>Value2); - Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_NEG: - { - // We use "0-value" expression to suppress "unary minus to unsigned" - // compiler warning. - uint Result=UINT32(0-GET_VALUE(Cmd->ByteMode,Op1)); - Flags=Result==0 ? VM_FZ:VM_FC|(Result&VM_FS); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; -#ifdef VM_OPTIMIZE - case VM_NEGB: - SET_VALUE(true,Op1,0-GET_VALUE(true,Op1)); - break; - case VM_NEGD: - SET_VALUE(false,Op1,0-GET_VALUE(false,Op1)); - break; -#endif - case VM_PUSHA: - { - const int RegCount=sizeof(R)/sizeof(R[0]); - for (int I=0,SP=R[7]-4;IByteMode,Op1); - SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); - SET_VALUE(Cmd->ByteMode,Op2,Value1); - } - break; - case VM_MUL: - { - uint Result=GET_VALUE(Cmd->ByteMode,Op1)*GET_VALUE(Cmd->ByteMode,Op2); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_DIV: - { - uint Divider=GET_VALUE(Cmd->ByteMode,Op2); - if (Divider!=0) - { - uint Result=GET_VALUE(Cmd->ByteMode,Op1)/Divider; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - } - break; - case VM_ADC: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint FC=(Flags&VM_FC); - uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)+FC); - if (Cmd->ByteMode) - Result&=0xff; - Flags=(ResultByteMode,Op1,Result); - } - break; - case VM_SBB: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint FC=(Flags&VM_FC); - uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)-FC); - if (Cmd->ByteMode) - Result&=0xff; - Flags=(Result>Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS)); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; -#endif // for #ifndef NORARVM - case VM_RET: - if (R[7]>=VM_MEMSIZE) - return(true); - SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); - R[7]+=4; - continue; -#ifdef VM_STANDARDFILTERS - case VM_STANDARD: - ExecuteStandardFilter((VM_StandardFilters)Cmd->Op1.Data); - break; -#endif - case VM_PRINT: - break; - } - Cmd++; - --MaxOpCount; - } -} - - - - void RarVM::Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg) { - InitBitInput(); - memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); - // Calculate the single byte XOR checksum to check validity of VM code. byte XorSum=0; for (uint I=1;ICmdCount=0; - if (XorSum==Code[0]) // VM code is valid if equal. + struct StandardFilters { -#ifdef VM_STANDARDFILTERS - VM_StandardFilters FilterType=IsStandardFilter(Code,CodeSize); - if (FilterType!=VMSF_NONE) + uint Length; + uint CRC; + VM_StandardFilters Type; + } static StdList[]={ + 53, 0xad576887, VMSF_E8, + 57, 0x3cd7e57e, VMSF_E8E9, + 120, 0x3769893f, VMSF_ITANIUM, + 29, 0x0e06077d, VMSF_DELTA, + 149, 0x1c2c5dc8, VMSF_RGB, + 216, 0xbc85e701, VMSF_AUDIO + }; + uint CodeCRC=CRC32(0xffffffff,Code,CodeSize)^0xffffffff; + for (uint I=0;ICmd.Add(1); - VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; - CurCmd->OpCode=VM_STANDARD; - CurCmd->Op1.Data=FilterType; - CurCmd->Op1.Addr=&CurCmd->Op1.Data; - CurCmd->Op2.Addr=&CurCmd->Op2.Data; - CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; - CodeSize=0; - } -#endif - uint DataFlag=fgetbits(); - faddbits(1); - - // Read static data contained in DB operators. This data cannot be - // changed, it is a part of VM code, not a filter parameter. - - if (DataFlag&0x8000) - { - uint DataSize=ReadData(*this)+1; - for (uint I=0;(uint)InAddrStaticData.Add(1); - Prg->StaticData[I]=fgetbits()>>8; - faddbits(8); - } - } - - while ((uint)InAddrCmd.Add(1); - VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount]; - uint Data=fgetbits(); - if ((Data&0x8000)==0) - { - CurCmd->OpCode=(VM_Commands)(Data>>12); - faddbits(4); - } - else - { - CurCmd->OpCode=(VM_Commands)((Data>>10)-24); - faddbits(6); - } - if (VM_CmdFlags[CurCmd->OpCode] & VMCF_BYTEMODE) - { - CurCmd->ByteMode=(fgetbits()>>15)!=0; - faddbits(1); - } - else - CurCmd->ByteMode=0; - CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; - int OpNum=(VM_CmdFlags[CurCmd->OpCode] & VMCF_OPMASK); - CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; - if (OpNum>0) - { - DecodeArg(CurCmd->Op1,CurCmd->ByteMode); // reading the first operand - if (OpNum==2) - DecodeArg(CurCmd->Op2,CurCmd->ByteMode); // reading the second operand - else - { - if (CurCmd->Op1.Type==VM_OPINT && (VM_CmdFlags[CurCmd->OpCode]&(VMCF_JUMP|VMCF_PROC))) - { - // Calculating jump distance. - int Distance=CurCmd->Op1.Data; - if (Distance>=256) - Distance-=256; - else - { - if (Distance>=136) - Distance-=264; - else - if (Distance>=16) - Distance-=8; - else - if (Distance>=8) - Distance-=16; - Distance+=Prg->CmdCount; - } - CurCmd->Op1.Data=Distance; - } - } - } - Prg->CmdCount++; - } - } - - // Adding RET command at the end of program. - Prg->Cmd.Add(1); - VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; - CurCmd->OpCode=VM_RET; - CurCmd->Op1.Addr=&CurCmd->Op1.Data; - CurCmd->Op2.Addr=&CurCmd->Op2.Data; - CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; - - // If operand 'Addr' field has not been set by DecodeArg calls above, - // let's set it to point to operand 'Data' field. It is necessary for - // VM_OPINT type operands (usual integers) or maybe if something was - // not set properly for other operands. 'Addr' field is required - // for quicker addressing of operand data. - for (int I=0;ICmdCount;I++) - { - VM_PreparedCommand *Cmd=&Prg->Cmd[I]; - if (Cmd->Op1.Addr==NULL) - Cmd->Op1.Addr=&Cmd->Op1.Data; - if (Cmd->Op2.Addr==NULL) - Cmd->Op2.Addr=&Cmd->Op2.Data; - } - -#ifdef VM_OPTIMIZE - if (CodeSize!=0) - Optimize(Prg); -#endif -} - - -void RarVM::DecodeArg(VM_PreparedOperand &Op,bool ByteMode) -{ - uint Data=fgetbits(); - if (Data & 0x8000) - { - Op.Type=VM_OPREG; // Operand is register (R[0]..R[7]) - Op.Data=(Data>>12)&7; // Register number - Op.Addr=&R[Op.Data]; // Register address - faddbits(4); // 1 flag bit and 3 register number bits - } - else - if ((Data & 0xc000)==0) - { - Op.Type=VM_OPINT; // Operand is integer - if (ByteMode) - { - Op.Data=(Data>>6) & 0xff; // Byte integer. - faddbits(10); - } - else - { - faddbits(2); - Op.Data=ReadData(*this); // 32 bit integer. - } - } - else - { - // Operand is data addressed by register data, base address or both. - Op.Type=VM_OPREGMEM; - if ((Data & 0x2000)==0) - { - // Base address is zero, just use the address from register. - Op.Data=(Data>>10)&7; - Op.Addr=&R[Op.Data]; - Op.Base=0; - faddbits(6); - } - else - { - if ((Data & 0x1000)==0) - { - // Use both register and base address. - Op.Data=(Data>>9)&7; - Op.Addr=&R[Op.Data]; - faddbits(7); - } - else - { - // Use base address only. Access memory by fixed address. - Op.Data=0; - faddbits(4); - } - Op.Base=ReadData(*this); // Read base address. - } + Prg->Type=StdList[I].Type; + break; } } @@ -736,7 +76,7 @@ uint RarVM::ReadData(BitInput &Inp) { case 0: Inp.faddbits(6); - return((Data>>10)&0xf); + return (Data>>10)&0xf; case 0x4000: if ((Data&0x3c00)==0) { @@ -748,125 +88,38 @@ uint RarVM::ReadData(BitInput &Inp) Data=(Data>>6)&0xff; Inp.faddbits(10); } - return(Data); + return Data; case 0x8000: Inp.faddbits(2); Data=Inp.fgetbits(); Inp.faddbits(16); - return(Data); + return Data; default: Inp.faddbits(2); Data=(Inp.fgetbits()<<16); Inp.faddbits(16); Data|=Inp.fgetbits(); Inp.faddbits(16); - return(Data); + return Data; } } -void RarVM::SetMemory(uint Pos,byte *Data,uint DataSize) +void RarVM::SetMemory(size_t Pos,byte *Data,size_t DataSize) { if (PosCmd[0]; - uint CodeSize=Prg->CmdCount; - - for (uint I=0;IOpCode) - { - case VM_MOV: - Cmd->OpCode=Cmd->ByteMode ? VM_MOVB:VM_MOVD; - continue; - case VM_CMP: - Cmd->OpCode=Cmd->ByteMode ? VM_CMPB:VM_CMPD; - continue; - } - if ((VM_CmdFlags[Cmd->OpCode] & VMCF_CHFLAGS)==0) - continue; - - // If we do not have jump commands between the current operation - // and next command which will modify processor flags, we can replace - // the current command with faster version which does not need to - // modify flags. - bool FlagsRequired=false; - for (uint J=I+1;JOpCode) - { - case VM_ADD: - Cmd->OpCode=Cmd->ByteMode ? VM_ADDB:VM_ADDD; - continue; - case VM_SUB: - Cmd->OpCode=Cmd->ByteMode ? VM_SUBB:VM_SUBD; - continue; - case VM_INC: - Cmd->OpCode=Cmd->ByteMode ? VM_INCB:VM_INCD; - continue; - case VM_DEC: - Cmd->OpCode=Cmd->ByteMode ? VM_DECB:VM_DECD; - continue; - case VM_NEG: - Cmd->OpCode=Cmd->ByteMode ? VM_NEGB:VM_NEGD; - continue; - } + // We can have NULL Data for invalid filters with DataSize==0. While most + // sensible memmove implementations do not care about data if size is 0, + // let's follow the standard and check the size first. + size_t CopySize=Min(DataSize,VM_MEMSIZE-Pos); + if (CopySize!=0) + memmove(Mem+Pos,Data,CopySize); } } -#endif -#ifdef VM_STANDARDFILTERS -VM_StandardFilters RarVM::IsStandardFilter(byte *Code,uint CodeSize) -{ - struct StandardFilterSignature - { - int Length; - uint CRC; - VM_StandardFilters Type; - } static StdList[]={ - 53, 0xad576887, VMSF_E8, - 57, 0x3cd7e57e, VMSF_E8E9, - 120, 0x3769893f, VMSF_ITANIUM, - 29, 0x0e06077d, VMSF_DELTA, - 149, 0x1c2c5dc8, VMSF_RGB, - 216, 0xbc85e701, VMSF_AUDIO, - 40, 0x46b9c560, VMSF_UPCASE - }; - uint CodeCRC=CRC(0xffffffff,Code,CodeSize)^0xffffffff; - for (uint I=0;I=VM_GLOBALMEMADDR || DataSize<4) - break; + if (DataSize>VM_MEMSIZE || DataSize<4) + return false; - const int FileSize=0x1000000; + const uint FileSize=0x1000000; byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8; - for (int CurPos=0;CurPos=0) - SET_VALUE(false,Data,Addr+FileSize); + if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0 + RawPut4(Addr+FileSize,Data); } else - if (Addr=VM_GLOBALMEMADDR || DataSize<21) - break; + if (DataSize>VM_MEMSIZE || DataSize<21) + return false; - int CurPos=0; + uint CurPos=0; FileOffset>>=4; @@ -938,14 +179,14 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0}; byte CmdMask=Masks[Byte]; if (CmdMask!=0) - for (int I=0;I<=2;I++) + for (uint I=0;I<=2;I++) if (CmdMask & (1<=VM_GLOBALMEMADDR/2) - break; + uint DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2; + if (DataSize>VM_MEMSIZE/2 || Channels>MAX3_UNPACK_CHANNELS || Channels==0) + return false; // Bytes from same channels are grouped to continual data blocks, // so we need to place them back to their interleaving positions. - for (int CurChannel=0;CurChannelVM_MEMSIZE/2 || DataSize<3 || Width>DataSize || PosR>2) + return false; byte *SrcData=Mem,*DestData=SrcData+DataSize; - const int Channels=3; - SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - if ((uint)DataSize>=VM_GLOBALMEMADDR/2 || PosR<0) - break; - for (int CurChannel=0;CurChannel=3) + if (I>=Width+3) { - byte *UpperData=DestData+UpperPos; + byte *UpperData=DestData+I-Width; uint UpperByte=*UpperData; uint UpperLeftByte=*(UpperData-3); Predicted=PrevByte+UpperByte-UpperLeftByte; @@ -1011,7 +249,7 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); } } - for (int I=PosR,Border=DataSize-2;I=VM_GLOBALMEMADDR/2) - break; - for (int CurChannel=0;CurChannelVM_MEMSIZE/2 || Channels>128 || Channels==0) + return false; + for (uint CurChannel=0;CurChannel=VM_GLOBALMEMADDR/2) - break; - while (SrcPos>= InBit; - return(BitField & (0xffffffff>>(32-BitCount))); + return BitField & (0xffffffff>>(32-BitCount)); } -void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,int BitPos,int BitCount) +void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount) { - int InAddr=BitPos/8; - int InBit=BitPos&7; + uint InAddr=BitPos/8; + uint InBit=BitPos&7; uint AndMask=0xffffffff>>(32-BitCount); AndMask=~(AndMask<>=8; } } -#endif diff --git a/libunrar/rarvm.hpp b/libunrar/rarvm.hpp index 96d42c2..e65c4b1 100644 --- a/libunrar/rarvm.hpp +++ b/libunrar/rarvm.hpp @@ -1,112 +1,43 @@ #ifndef _RAR_VM_ #define _RAR_VM_ -#define VM_STANDARDFILTERS - -#ifndef SFX_MODULE -#define VM_OPTIMIZE -#endif - - #define VM_MEMSIZE 0x40000 #define VM_MEMMASK (VM_MEMSIZE-1) -#define VM_GLOBALMEMADDR 0x3C000 -#define VM_GLOBALMEMSIZE 0x2000 -#define VM_FIXEDGLOBALSIZE 64 - -enum VM_Commands -{ - VM_MOV, VM_CMP, VM_ADD, VM_SUB, VM_JZ, VM_JNZ, VM_INC, VM_DEC, - VM_JMP, VM_XOR, VM_AND, VM_OR, VM_TEST, VM_JS, VM_JNS, VM_JB, - VM_JBE, VM_JA, VM_JAE, VM_PUSH, VM_POP, VM_CALL, VM_RET, VM_NOT, - VM_SHL, VM_SHR, VM_SAR, VM_NEG, VM_PUSHA,VM_POPA, VM_PUSHF,VM_POPF, - VM_MOVZX,VM_MOVSX,VM_XCHG, VM_MUL, VM_DIV, VM_ADC, VM_SBB, VM_PRINT, - -#ifdef VM_OPTIMIZE - VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD, - - VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD, - VM_NEGB, VM_NEGD, -#endif - - VM_STANDARD -}; enum VM_StandardFilters { VMSF_NONE, VMSF_E8, VMSF_E8E9, VMSF_ITANIUM, VMSF_RGB, VMSF_AUDIO, - VMSF_DELTA, VMSF_UPCASE + VMSF_DELTA }; -enum VM_Flags {VM_FC=1,VM_FZ=2,VM_FS=0x80000000}; - -enum VM_OpType {VM_OPREG,VM_OPINT,VM_OPREGMEM,VM_OPNONE}; - -struct VM_PreparedOperand -{ - VM_OpType Type; - uint Data; - uint Base; - uint *Addr; -}; - -struct VM_PreparedCommand -{ - VM_Commands OpCode; - bool ByteMode; - VM_PreparedOperand Op1,Op2; -}; - - struct VM_PreparedProgram { VM_PreparedProgram() { - AltCmd=NULL; FilteredDataSize=0; - CmdCount=0; + Type=VMSF_NONE; } - - Array Cmd; - VM_PreparedCommand *AltCmd; - int CmdCount; - - Array GlobalData; - Array StaticData; // static data contained in DB operators + VM_StandardFilters Type; uint InitR[7]; - byte *FilteredData; uint FilteredDataSize; }; -class RarVM:private BitInput +class RarVM { private: - inline uint GetValue(bool ByteMode,uint *Addr); - inline void SetValue(bool ByteMode,uint *Addr,uint Value); - inline uint* GetOperand(VM_PreparedOperand *CmdOp); - void DecodeArg(VM_PreparedOperand &Op,bool ByteMode); -#ifdef VM_OPTIMIZE - void Optimize(VM_PreparedProgram *Prg); -#endif - bool ExecuteCode(VM_PreparedCommand *PreparedCode,uint CodeSize); -#ifdef VM_STANDARDFILTERS - VM_StandardFilters IsStandardFilter(byte *Code,uint CodeSize); - void ExecuteStandardFilter(VM_StandardFilters FilterType); - uint FilterItanium_GetBits(byte *Data,int BitPos,int BitCount); - void FilterItanium_SetBits(byte *Data,uint BitField,int BitPos,int BitCount); -#endif + bool ExecuteStandardFilter(VM_StandardFilters FilterType); + uint FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount); + void FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount); byte *Mem; uint R[8]; - uint Flags; public: RarVM(); ~RarVM(); void Init(); void Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg); void Execute(VM_PreparedProgram *Prg); - void SetLowEndianValue(uint *Addr,uint Value); - void SetMemory(uint Pos,byte *Data,uint DataSize); + void SetMemory(size_t Pos,byte *Data,size_t DataSize); static uint ReadData(BitInput &Inp); }; diff --git a/libunrar/rarvmtbl.cpp b/libunrar/rarvmtbl.cpp deleted file mode 100644 index b5e6c72..0000000 --- a/libunrar/rarvmtbl.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#define VMCF_OP0 0 -#define VMCF_OP1 1 -#define VMCF_OP2 2 -#define VMCF_OPMASK 3 -#define VMCF_BYTEMODE 4 -#define VMCF_JUMP 8 -#define VMCF_PROC 16 -#define VMCF_USEFLAGS 32 -#define VMCF_CHFLAGS 64 - -static byte VM_CmdFlags[]= -{ - /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JMP */ VMCF_OP1 | VMCF_JUMP , - /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_PUSH */ VMCF_OP1 , - /* VM_POP */ VMCF_OP1 , - /* VM_CALL */ VMCF_OP1 | VMCF_PROC , - /* VM_RET */ VMCF_OP0 | VMCF_PROC , - /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE , - /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_PUSHA */ VMCF_OP0 , - /* VM_POPA */ VMCF_OP0 , - /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS , - /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS , - /* VM_MOVZX */ VMCF_OP2 , - /* VM_MOVSX */ VMCF_OP2 , - /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , - /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , - /* VM_PRINT */ VMCF_OP0 -}; diff --git a/libunrar/rawint.hpp b/libunrar/rawint.hpp new file mode 100644 index 0000000..3037988 --- /dev/null +++ b/libunrar/rawint.hpp @@ -0,0 +1,122 @@ +#ifndef _RAR_RAWINT_ +#define _RAR_RAWINT_ + +#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) +#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) +#define rotl32(x,n) rotls(x,n,32) +#define rotr32(x,n) rotrs(x,n,32) + +inline uint RawGet2(const void *Data) +{ + byte *D=(byte *)Data; + return D[0]+(D[1]<<8); +} + + +inline uint32 RawGet4(const void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + return D[0]+(D[1]<<8)+(D[2]<<16)+(D[3]<<24); +#else + return *(uint32 *)Data; +#endif +} + + +inline uint64 RawGet8(const void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + return INT32TO64(RawGet4(D+4),RawGet4(D)); +#else + return *(uint64 *)Data; +#endif +} + + +inline void RawPut2(uint Field,void *Data) +{ + byte *D=(byte *)Data; + D[0]=(byte)(Field); + D[1]=(byte)(Field>>8); +} + + +inline void RawPut4(uint32 Field,void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + D[0]=(byte)(Field); + D[1]=(byte)(Field>>8); + D[2]=(byte)(Field>>16); + D[3]=(byte)(Field>>24); +#else + *(uint32 *)Data=Field; +#endif +} + + +inline void RawPut8(uint64 Field,void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + D[0]=(byte)(Field); + D[1]=(byte)(Field>>8); + D[2]=(byte)(Field>>16); + D[3]=(byte)(Field>>24); + D[4]=(byte)(Field>>32); + D[5]=(byte)(Field>>40); + D[6]=(byte)(Field>>48); + D[7]=(byte)(Field>>56); +#else + *(uint64 *)Data=Field; +#endif +} + + +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) +#define USE_MEM_BYTESWAP +#endif + +// Load 4 big endian bytes from memory and return uint32. +inline uint32 RawGetBE4(const byte *m) +{ +#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + return _byteswap_ulong(*(uint32 *)m); +#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) + return __builtin_bswap32(*(uint32 *)m); +#else + return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; +#endif +} + + +// Save integer to memory as big endian. +inline void RawPutBE4(uint32 i,byte *mem) +{ +#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + *(uint32*)mem = _byteswap_ulong(i); +#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) + *(uint32*)mem = __builtin_bswap32(i); +#else + mem[0]=byte(i>>24); + mem[1]=byte(i>>16); + mem[2]=byte(i>>8); + mem[3]=byte(i); +#endif +} + + +inline uint32 ByteSwap32(uint32 i) +{ +#ifdef _MSC_VER + return _byteswap_ulong(i); +#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) + return __builtin_bswap32(i); +#else + return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); +#endif +} + +#endif diff --git a/libunrar/rawread.cpp b/libunrar/rawread.cpp index 15aba35..d99bac8 100644 --- a/libunrar/rawread.cpp +++ b/libunrar/rawread.cpp @@ -1,41 +1,65 @@ #include "rar.hpp" +RawRead::RawRead() +{ + RawRead::SrcFile=NULL; + Reset(); +} + + RawRead::RawRead(File *SrcFile) { RawRead::SrcFile=SrcFile; - ReadPos=0; - DataSize=0; -#ifndef SHELL_EXT - Crypt=NULL; -#endif + Reset(); } -void RawRead::Read(size_t Size) +void RawRead::Reset() { -#if !defined(SHELL_EXT) && !defined(NOCRYPT) + Data.SoftReset(); + ReadPos=0; + DataSize=0; + Crypt=NULL; +} + + +size_t RawRead::Read(size_t Size) +{ + size_t ReadSize=0; +#if !defined(RAR_NOCRYPT) if (Crypt!=NULL) { - size_t CurSize=Data.Size(); - size_t SizeToRead=Size-(CurSize-DataSize); - if (SizeToRead>0) + // Full size of buffer with already read data including data read + // for encryption block alignment. + size_t FullSize=Data.Size(); + + // Data read for alignment and not processed yet. + size_t DataLeft=FullSize-DataSize; + + if (Size>DataLeft) // Need to read more than we already have. { - size_t AlignedReadSize=SizeToRead+((~SizeToRead+1)&0xf); + size_t SizeToRead=Size-DataLeft; + size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK); Data.Add(AlignedReadSize); - size_t ReadSize=SrcFile->Read(&Data[CurSize],AlignedReadSize); - Crypt->DecryptBlock(&Data[CurSize],AlignedReadSize); + ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize); + Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize); DataSize+=ReadSize==0 ? 0:Size; } - else + else // Use buffered data, no real read. + { + ReadSize=Size; DataSize+=Size; + } } else #endif if (Size!=0) { Data.Add(Size); - DataSize+=SrcFile->Read(&Data[DataSize],Size); + ReadSize=SrcFile->Read(&Data[DataSize],Size); + DataSize+=ReadSize; } + return ReadSize; } @@ -50,77 +74,124 @@ void RawRead::Read(byte *SrcData,size_t Size) } -void RawRead::Get(byte &Field) +byte RawRead::Get1() { - if (ReadPos0) + memcpy(F,&Data[ReadPos],CopySize); + if (Size>CopySize) + memset(F+CopySize,0,Size-CopySize); + ReadPos+=CopySize; + return CopySize; +} + + +void RawRead::GetW(wchar *Field,size_t Size) { if (ReadPos+2*Size-12 ? CRC(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2):0xffffffff); + if (DataSize<=2) + return 0; + uint HeaderCRC=CRC32(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2); + return ~HeaderCRC & 0xffff; +} + + +uint RawRead::GetCRC50() // RAR 5.0 block CRC. +{ + if (DataSize<=4) + return 0xffffffff; + return CRC32(0xffffffff,&Data[4],DataSize-4) ^ 0xffffffff; +} + + +// Read vint from arbitrary byte array. +uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow) +{ + Overflow=false; + uint64 Result=0; + for (uint Shift=0;ReadPos 0) { Archive *SrcArc=(Archive *)SrcFile; - size_t ReadSize=((int64)Count>UnpPackedSize) ? (size_t)UnpPackedSize:Count; if (UnpackFromMemory) { memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize); - RetCode=(int)UnpackFromMemorySize; + ReadSize=(int)UnpackFromMemorySize; UnpackFromMemorySize=0; } else { - if (!SrcFile->IsOpened()) - return(-1); - RetCode=SrcFile->Read(ReadAddr,ReadSize); - FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->NewLhd; - if (hd->Flags & LHD_SPLIT_AFTER) - PackedCRC=CRC(PackedCRC,ReadAddr,RetCode); + size_t SizeToRead=((int64)Count>UnpPackedSize) ? (size_t)UnpPackedSize:Count; + if (SizeToRead > 0) + { + if (UnpVolume && Decryption && (int64)Count>UnpPackedSize) + { + // We need aligned blocks for decryption and we want "Keep broken + // files" to work efficiently with missing encrypted volumes. + // So for last data block in volume we adjust the size to read to + // next equal or smaller block producing aligned total block size. + // So we'll ask for next volume only when processing few unaligned + // bytes left in the end, when most of data is already extracted. + size_t NewTotalRead = TotalRead + SizeToRead; + size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK); + size_t NewSizeToRead = SizeToRead - Adjust; + if ((int)NewSizeToRead > 0) + SizeToRead = NewSizeToRead; + } + + if (!SrcFile->IsOpened()) + return -1; + ReadSize=SrcFile->Read(ReadAddr,SizeToRead); + FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead; + if (!NoFileHeader && hd->SplitAfter) + PackedDataHash.Update(ReadAddr,ReadSize); + } } - CurUnpRead+=RetCode; - TotalRead+=RetCode; + CurUnpRead+=ReadSize; + TotalRead+=ReadSize; #ifndef NOVOLUME // These variable are not used in NOVOLUME mode, so it is better // to exclude commands below to avoid compiler warnings. - ReadAddr+=RetCode; - Count-=RetCode; + ReadAddr+=ReadSize; + Count-=ReadSize; #endif - UnpPackedSize-=RetCode; - if (UnpPackedSize == 0 && UnpVolume) + UnpPackedSize-=ReadSize; + + // Do not ask for next volume if we read something from current volume. + // If next volume is missing, we need to process all data from current + // volume before aborting. It helps to recover all possible data + // in "Keep broken files" mode. But if we process encrypted data, + // we ask for next volume also if we have non-aligned encryption block. + // Since we adjust data size for decryption earlier above, + // it does not hurt "Keep broken files" mode efficiency. + if (UnpVolume && UnpPackedSize == 0 && + (ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) ) { #ifndef NOVOLUME if (!MergeArchive(*SrcArc,this,true,CurrentCommand)) #endif { NextVolumeMissing=true; - return(-1); + return -1; } } else @@ -86,32 +135,20 @@ int ComprDataIO::UnpRead(byte *Addr,size_t Count) Archive *SrcArc=(Archive *)SrcFile; if (SrcArc!=NULL) ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize); - if (RetCode!=-1) + if (ReadSize!=-1) { - RetCode=TotalRead; -#ifndef NOCRYPT + ReadSize=TotalRead; +#ifndef RAR_NOCRYPT if (Decryption) -#ifndef SFX_MODULE - if (Decryption<20) - Decrypt.Crypt(Addr,RetCode,(Decryption==15) ? NEW_CRYPT : OLD_DECODE); - else - if (Decryption==20) - for (int I=0;IDecryptBlock(Addr,ReadSize); #endif } Wait(); - return(RetCode); + return ReadSize; } -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_M_X64) +#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) // Disable the run time stack check for unrar.dll, so we can manipulate // with ProcessDataProc call type below. Run time check would intercept // a wrong ESP before we restore it. @@ -127,7 +164,7 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count) { if (Cmd->Callback!=NULL && Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1) - ErrHandler.Exit(USER_BREAK); + ErrHandler.Exit(RARX_USERBREAK); if (Cmd->ProcessDataProc!=NULL) { // Here we preserve ESP value. It is necessary for those developers, @@ -135,10 +172,10 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count) // even though in year 2001 we announced in unrar.dll whatsnew.txt // that it will be PASCAL type (for compatibility with Visual Basic). #if defined(_MSC_VER) -#ifndef _M_X64 +#ifndef _WIN_64 __asm mov ebx,esp #endif -#elif defined(_WIN_32) && defined(__BORLANDC__) +#elif defined(_WIN_ALL) && defined(__BORLANDC__) _EBX=_ESP; #endif int RetCode=Cmd->ProcessDataProc(Addr,(int)Count); @@ -146,14 +183,14 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count) // Restore ESP after ProcessDataProc with wrongly defined calling // convention broken it. #if defined(_MSC_VER) -#ifndef _M_X64 +#ifndef _WIN_64 __asm mov esp,ebx #endif -#elif defined(_WIN_32) && defined(__BORLANDC__) +#elif defined(_WIN_ALL) && defined(__BORLANDC__) _ESP=_EBX; #endif if (RetCode==0) - ErrHandler.Exit(USER_BREAK); + ErrHandler.Exit(RARX_USERBREAK); } } #endif // RARDLL @@ -174,17 +211,12 @@ void ComprDataIO::UnpWrite(byte *Addr,size_t Count) DestFile->Write(Addr,Count); CurUnpWrite+=Count; if (!SkipUnpCRC) -#ifndef SFX_MODULE - if (((Archive *)SrcFile)->OldFormat) - UnpFileCRC=OldCRC((ushort)UnpFileCRC,Addr,Count); - else -#endif - UnpFileCRC=CRC(UnpFileCRC,Addr,Count); + UnpHash.Update(Addr,Count); ShowUnpWrite(); Wait(); } -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_M_X64) +#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) // Restore the run time stack check for unrar.dll. #pragma runtime_checks( "s", restore ) #endif @@ -211,7 +243,7 @@ void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize) int CurPercent=ToPercent(ArcPos,ArcSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { - mprintf("\b\b\b\b%3d%%",CurPercent); + uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize); LastPercent=CurPercent; } } @@ -229,6 +261,8 @@ void ComprDataIO::ShowUnpWrite() + + void ComprDataIO::SetFiles(File *SrcFile,File *DestFile) { if (SrcFile!=NULL) @@ -246,39 +280,33 @@ void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size) } -void ComprDataIO::SetEncryption(int Method,const char *Password,const byte *Salt,bool Encrypt,bool HandsOffHash) +void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method, + SecPassword *Password,const byte *Salt,const byte *InitV, + uint Lg2Cnt,byte *HashKey,byte *PswCheck) { +#ifndef RAR_NOCRYPT if (Encrypt) - { - Encryption=*Password ? Method:0; -#ifndef NOCRYPT - Crypt.SetCryptKeys(Password,Salt,Encrypt,false,HandsOffHash); -#endif - } + Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); else - { - Decryption=*Password ? Method:0; -#ifndef NOCRYPT - Decrypt.SetCryptKeys(Password,Salt,Encrypt,Method<29,HandsOffHash); + Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); #endif - } } -#if !defined(SFX_MODULE) && !defined(NOCRYPT) +#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) void ComprDataIO::SetAV15Encryption() { - Decryption=15; - Decrypt.SetAV15Encryption(); + Decryption=true; + Decrypt->SetAV15Encryption(); } #endif -#if !defined(SFX_MODULE) && !defined(NOCRYPT) +#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) void ComprDataIO::SetCmt13Encryption() { - Decryption=13; - Decrypt.SetCmt13Encryption(); + Decryption=true; + Decrypt->SetCmt13Encryption(); } #endif @@ -291,5 +319,3 @@ void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size) UnpackToMemoryAddr=Addr; UnpackToMemorySize=Size; } - - diff --git a/libunrar/rdwrfn.hpp b/libunrar/rdwrfn.hpp index 4f4b43b..fc38fd3 100644 --- a/libunrar/rdwrfn.hpp +++ b/libunrar/rdwrfn.hpp @@ -3,7 +3,12 @@ class CmdAdd; class Unpack; +class ArcFileSearch; +#if 0 +// We use external i/o calls for Benchmark command. +#define COMPRDATAIO_EXTIO +#endif class ComprDataIO { @@ -28,6 +33,7 @@ class ComprDataIO bool ShowProgress; bool TestMode; bool SkipUnpCRC; + bool NoFileHeader; File *SrcFile; File *DestFile; @@ -37,18 +43,19 @@ class ComprDataIO FileHeader *SubHead; int64 *SubHeadPos; -#ifndef NOCRYPT - CryptData Crypt; - CryptData Decrypt; +#ifndef RAR_NOCRYPT + CryptData *Crypt; + CryptData *Decrypt; #endif int LastPercent; - char CurrentCommand; + wchar CurrentCommand; public: ComprDataIO(); + ~ComprDataIO(); void Init(); int UnpRead(byte *Addr,size_t Count); void UnpWrite(byte *Addr,size_t Count); @@ -57,32 +64,37 @@ class ComprDataIO void SetPackedSizeToRead(int64 Size) {UnpPackedSize=Size;} void SetTestMode(bool Mode) {TestMode=Mode;} void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;} + void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;} void SetFiles(File *SrcFile,File *DestFile); void SetCommand(CmdAdd *Cmd) {Command=Cmd;} void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;} - void SetEncryption(int Method,const char *Password,const byte *Salt,bool Encrypt,bool HandsOffHash); + void SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, + const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); void SetAV15Encryption(); void SetCmt13Encryption(); void SetUnpackToMemory(byte *Addr,uint Size); - void SetCurrentCommand(char Cmd) {CurrentCommand=Cmd;} + void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;} + bool PackVolume; bool UnpVolume; bool NextVolumeMissing; - int64 TotalPackRead; int64 UnpArcSize; int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite; + // Size of already processed archives. // Used to calculate the total operation progress. int64 ProcessedArcSize; int64 TotalArcSize; - uint PackFileCRC,UnpFileCRC,PackedCRC; + DataHash PackedDataHash; // Packed write and unpack read hash. + DataHash PackHash; // Pack read hash. + DataHash UnpHash; // Unpack write hash. - int Encryption; - int Decryption; + bool Encryption; + bool Decryption; }; #endif diff --git a/libunrar/readme.txt b/libunrar/readme.txt index 41ee05c..a1f820a 100644 --- a/libunrar/readme.txt +++ b/libunrar/readme.txt @@ -4,12 +4,13 @@ 1. General - This package includes freeware Unrar C++ source and a few makefiles - (makefile.bcc, makefile.msc+msc.dep, makefile.unix). Unrar source - is subset of RAR and generated from RAR source automatically, + This package includes freeware Unrar C++ source and makefile for + several Unix compilers. + + Unrar source is subset of RAR and generated from RAR source automatically, by a small program removing blocks like '#ifndef UNRAR ... #endif'. - Such method is not perfect and you may find some RAR related - stuff unnecessary in Unrar, especially in header files. + Such method is not perfect and you may find some RAR related stuff + unnecessary in Unrar, especially in header files. If you wish to port Unrar to a new platform, you may need to edit '#define LITTLE_ENDIAN' in os.hpp and data type definitions @@ -17,16 +18,10 @@ if computer architecture does not allow not aligned data access, you need to undefine ALLOW_NOT_ALIGNED_INT and define - STRICT_ALIGNMENT_REQUIRED in os.h. Note that it will increase memory - requirements. + STRICT_ALIGNMENT_REQUIRED in os.h. - If you use Borland C++ makefile (makefile.bcc), you need to define - BASEPATHCC environment (or makefile) variable containing - the path to Borland C++ installation. - - Makefile.unix contains numerous compiler option sets. - GCC Linux is selected by default. If you need to compile Unrar - for other platforms, uncomment corresponding lines. + UnRAR.vcproj and UnRARDll.vcproj are projects for Microsoft Visual C++. + UnRARDll.vcproj lets to build unrar.dll library. 2. Unrar binaries @@ -38,16 +33,8 @@ 3. Acknowledgements - This source includes parts of code written by the following authors: - - Dmitry Shkarin PPMII v.H text compression - Dmitry Subbotin Carryless rangecoder - Szymon Stefanek AES encryption - Brian Gladman AES encryption - Steve Reid SHA-1 hash function - Marcus Herbert makefile.unix file - Tomasz Klim fixes for libunrar.so - Robert Riebisch makefile.dj and patches for DJGPP + This source includes parts of code written by other authors. + Please see acknow.txt file for details. 4. Legal stuff diff --git a/libunrar/recvol.cpp b/libunrar/recvol.cpp index f020abf..adf5840 100644 --- a/libunrar/recvol.cpp +++ b/libunrar/recvol.cpp @@ -1,372 +1,111 @@ #include "rar.hpp" -#define RECVOL_BUFSIZE 0x8000 +#include "recvol3.cpp" +#include "recvol5.cpp" -RecVolumes::RecVolumes() + + +bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent) { - Buf.Alloc(RECVOL_BUFSIZE*256); - memset(SrcFile,0,sizeof(SrcFile)); -} - - -RecVolumes::~RecVolumes() -{ - for (int I=0;IArcName && DigitGroup<3;Ext--) - if (!IsDigit(*Ext)) - if (IsDigit(*(Ext-1)) && (*Ext=='_' || DigitGroup<2)) - DigitGroup++; - else - if (DigitGroup<2) - { - NewStyle=true; - break; - } - while (IsDigit(*Ext) && Ext>ArcName+1) - Ext--; - strcpy(Ext,"*.*"); + if (!Silent) + ErrHandler.OpenErrorMsg(Name); + return false; + } + + RARFORMAT Fmt=RARFMT15; + if (Arc.IsArchive(true)) + Fmt=Arc.Format; + else + { + byte Sign[REV5_SIGN_SIZE]; + Arc.Seek(0,SEEK_SET); + if (Arc.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0) + Fmt=RARFMT50; + } + Arc.Close(); + + // We define RecVol as local variable for proper stack unwinding when + // handling exceptions. So it can close and delete files on Cancel. + if (Fmt==RARFMT15) + { + RecVolumes3 RecVol(Cmd,false); + return RecVol.Restore(Cmd,Name,Silent); + } + else + { + RecVolumes5 RecVol(Cmd,false); + return RecVol.Restore(Cmd,Name,Silent); + } +} + + +void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name) +{ + wchar RevName[NM]; + *RevName=0; + if (Arc!=NULL) + { + // We received .rar or .exe volume as a parameter, trying to find + // the matching .rev file number 1. + bool NewNumbering=Arc->NewNumbering; + + wchar ArcName[NM]; + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + + wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); + wchar RecVolMask[NM]; + wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); + size_t BaseNamePartLength=VolNumStart-ArcName; + wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); + FindFile Find; - Find.SetMask(ArcName); - struct FindData FD; - while (Find.Next(&FD)) + Find.SetMask(RecVolMask); + FindData RecData; + + while (Find.Next(&RecData)) { - Archive Arc(Cmd); - if (Arc.WOpen(FD.Name,FD.NameW) && Arc.IsArchive(true)) + wchar *Num=GetVolNumPart(RecData.Name); + if (*Num!='1') // Name must have "0...01" numeric part. + continue; + bool FirstVol=true; + while (--Num>=RecData.Name && IsDigit(*Num)) + if (*Num!='0') + { + FirstVol=false; + break; + } + if (FirstVol) { - strcpy(ArcName,FD.Name); - *ArcNameW=0; + wcsncpyz(RevName,RecData.Name,ASIZE(RevName)); + Name=RevName; break; } } + if (*RevName==0) // First .rev file not found. + return; } - - Archive Arc(Cmd); - if (!Arc.WCheckOpen(ArcName,ArcNameW)) - return(false); - if (!Arc.Volume) + + File RevFile; + if (!RevFile.Open(Name)) { -#ifndef SILENT - Log(ArcName,St(MNotVolume),ArcName); -#endif - return(false); + ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN. + return; } - bool NewNumbering=(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0; - Arc.Close(); - char *VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering); - char RecVolMask[NM]; - strcpy(RecVolMask,ArcName); - size_t BaseNamePartLength=VolNumStart-ArcName; - strcpy(RecVolMask+BaseNamePartLength,"*.rev"); - -#ifndef SILENT - int64 RecFileSize=0; -#endif - -#ifndef SILENT - mprintf(St(MCalcCRCAllVol)); -#endif - - FindFile Find; - Find.SetMask(RecVolMask); - struct FindData RecData; - int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0; - char PrevName[NM]; - while (Find.Next(&RecData)) + mprintf(L"\n"); + byte Sign[REV5_SIGN_SIZE]; + bool Rev5=RevFile.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0; + RevFile.Close(); + if (Rev5) { - char *Name=RecData.Name; - int P[3]; - if (!RevName && !NewStyle) - { - NewStyle=true; - char *Dot=GetExt(Name); - if (Dot!=NULL) - { - int LineCount=0; - Dot--; - while (Dot>Name && *Dot!='.') - { - if (*Dot=='_') - LineCount++; - Dot--; - } - if (LineCount==2) - NewStyle=false; - } - } - if (NewStyle) - { - File CurFile; - CurFile.TOpen(Name); - CurFile.Seek(0,SEEK_END); - int64 Length=CurFile.Tell(); - CurFile.Seek(Length-7,SEEK_SET); - for (int I=0;I<3;I++) - P[2-I]=CurFile.GetByte()+1; - uint FileCRC=0; - for (int I=0;I<4;I++) - FileCRC|=CurFile.GetByte()<<(I*8); - if (FileCRC!=CalcFileCRC(&CurFile,Length-4)) - { -#ifndef SILENT - mprintf(St(MCRCFailed),Name); -#endif - continue; - } - } - else - { - char *Dot=GetExt(Name); - if (Dot==NULL) - continue; - bool WrongParam=false; - for (int I=0;I=Name+BaseNamePartLength); - P[I]=atoi(Dot+1); - if (P[I]==0 || P[I]>255) - WrongParam=true; - } - if (WrongParam) - continue; - } - if (P[1]+P[2]>255) - continue; - if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2]) - { -#ifndef SILENT - Log(NULL,St(MRecVolDiffSets),Name,PrevName); -#endif - return(false); - } - RecVolNumber=P[1]; - FileNumber=P[2]; - strcpy(PrevName,Name); - File *NewFile=new File; - NewFile->TOpen(Name); - SrcFile[FileNumber+P[0]-1]=NewFile; - FoundRecVolumes++; -#ifndef SILENT - if (RecFileSize==0) - RecFileSize=NewFile->FileLength(); -#endif + RecVolumes5 RecVol(Cmd,true); + RecVol.Test(Cmd,Name); } -#ifndef SILENT - if (!Silent || FoundRecVolumes!=0) + else { - mprintf(St(MRecVolFound),FoundRecVolumes); + RecVolumes3 RecVol(Cmd,true); + RecVol.Test(Cmd,Name); } -#endif - if (FoundRecVolumes==0) - return(false); - - bool WriteFlags[256]; - memset(WriteFlags,0,sizeof(WriteFlags)); - - char LastVolName[NM]; - *LastVolName=0; - - for (int CurArcNum=0;CurArcNumTOpen(ArcName); - ValidVolume=NewFile->IsArchive(false); - if (ValidVolume) - { - while (NewFile->ReadHeader()!=0) - { - if (NewFile->GetHeaderType()==ENDARC_HEAD) - { - if ((NewFile->EndArcHead.Flags&EARC_DATACRC)!=0 && - NewFile->EndArcHead.ArcDataCRC!=CalcFileCRC(NewFile,NewFile->CurBlockPos)) - { - ValidVolume=false; -#ifndef SILENT - mprintf(St(MCRCFailed),ArcName); -#endif - } - break; - } - NewFile->SeekToNext(); - } - } - if (!ValidVolume) - { - NewFile->Close(); - char NewName[NM]; - strcpy(NewName,ArcName); - strcat(NewName,".bad"); -#ifndef SILENT - mprintf(St(MBadArc),ArcName); - mprintf(St(MRenaming),ArcName,NewName); -#endif - rename(ArcName,NewName); - } - NewFile->Seek(0,SEEK_SET); - } - if (!ValidVolume) - { - NewFile->TCreate(ArcName); - WriteFlags[CurArcNum]=true; - MissingVolumes++; - - if (CurArcNum==FileNumber-1) - strcpy(LastVolName,ArcName); - -#ifndef SILENT - mprintf(St(MAbsNextVol),ArcName); -#endif - } - SrcFile[CurArcNum]=(File*)NewFile; - NextVolumeName(ArcName,ArcNameW,ASIZE(ArcName),!NewNumbering); - } - -#ifndef SILENT - mprintf(St(MRecVolMissing),MissingVolumes); -#endif - - if (MissingVolumes==0) - { -#ifndef SILENT - mprintf(St(MRecVolAllExist)); -#endif - return(false); - } - - if (MissingVolumes>FoundRecVolumes) - { -#ifndef SILENT - mprintf(St(MRecVolCannotFix)); -#endif - return(false); - } -#ifndef SILENT - mprintf(St(MReconstructing)); -#endif - - RSCoder RSC(RecVolNumber); - - int TotalFiles=FileNumber+RecVolNumber; - int Erasures[256],EraSize=0; - - for (int I=0;IRead(&Buf[I*RECVOL_BUFSIZE],RECVOL_BUFSIZE); - if (ReadSize!=RECVOL_BUFSIZE) - memset(&Buf[I*RECVOL_BUFSIZE+ReadSize],0,RECVOL_BUFSIZE-ReadSize); - if (ReadSize>MaxRead) - MaxRead=ReadSize; - } - if (MaxRead==0) - break; -#ifndef SILENT - int CurPercent=ToPercent(ProcessedSize,RecFileSize); - if (!Cmd->DisablePercentage && CurPercent!=LastPercent) - { - mprintf("\b\b\b\b%3d%%",CurPercent); - LastPercent=CurPercent; - } - ProcessedSize+=MaxRead; -#endif - for (int BufPos=0;BufPosWrite(&Buf[I*RECVOL_BUFSIZE],MaxRead); - } - for (int I=0;ITell(); - CurFile->Seek(Length-7,SEEK_SET); - for (int J=0;J<7;J++) - CurFile->PutByte(0); - } - CurFile->Close(); - SrcFile[I]=NULL; - } - if (*LastVolName) - { - Archive Arc(Cmd); - if (Arc.Open(LastVolName,NULL,false,true) && Arc.IsArchive(true) && - Arc.SearchBlock(ENDARC_HEAD)) - { - Arc.Seek(Arc.NextBlockPos,SEEK_SET); - char Buf[8192]; - int ReadSize=Arc.Read(Buf,sizeof(Buf)); - int ZeroCount=0; - while (ZeroCountDisablePercentage) - mprintf("\b\b\b\b100%%"); - if (!Silent && !Cmd->DisableDone) - mprintf(St(MDone)); -#endif - return(true); } diff --git a/libunrar/recvol.hpp b/libunrar/recvol.hpp index 5a0abe5..06510a2 100644 --- a/libunrar/recvol.hpp +++ b/libunrar/recvol.hpp @@ -1,16 +1,88 @@ #ifndef _RAR_RECVOL_ #define _RAR_RECVOL_ -class RecVolumes +#define REV5_SIGN "Rar!\x1aRev" +#define REV5_SIGN_SIZE 8 + +class RecVolumes3 { private: File *SrcFile[256]; Array Buf; + +#ifdef RAR_SMP + ThreadPool *RSThreadPool; +#endif public: - RecVolumes(); - ~RecVolumes(); - void Make(RAROptions *Cmd,char *ArcName,wchar *ArcNameW); - bool Restore(RAROptions *Cmd,const char *Name,const wchar *NameW,bool Silent); + RecVolumes3(RAROptions *Cmd,bool TestOnly); + ~RecVolumes3(); + void Make(RAROptions *Cmd,wchar *ArcName); + bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); + void Test(RAROptions *Cmd,const wchar *Name); }; + +struct RecVolItem +{ + File *f; + wchar Name[NM]; + uint CRC; + uint64 FileSize; + bool New; // Newly created RAR volume. + bool Valid; // If existing RAR volume is valid. +}; + + +class RecVolumes5; +struct RecRSThreadData +{ + RecVolumes5 *RecRSPtr; + RSCoder16 *RS; + bool Encode; + uint DataNum; + const byte *Data; + size_t StartPos; + size_t Size; +}; + +class RecVolumes5 +{ + private: + void ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode); + void ProcessRS(RAROptions *Cmd,uint MaxRead,bool Encode); + uint ReadHeader(File *RecFile,bool FirstRev); + + Array RecItems; + + byte *RealReadBuffer; // Real pointer returned by 'new'. + byte *ReadBuffer; // Pointer aligned for SSE instructions. + + byte *RealBuf; // Real pointer returned by 'new'. + byte *Buf; // Store ECC or recovered data here, aligned for SSE. + size_t RecBufferSize; // Buffer area allocated for single volume. + + uint DataCount; // Number of archives. + uint RecCount; // Number of recovery volumes. + uint TotalCount; // Total number of archives and recovery volumes. + + bool *ValidFlags; // Volume validity flags for recovering. + uint MissingVolumes; // Number of missing or bad RAR volumes. + +#ifdef RAR_SMP + ThreadPool *RecThreadPool; +#endif + uint MaxUserThreads; // Maximum number of threads defined by user. + RecRSThreadData *ThreadData; // Array to store thread parameters. + public: // 'public' only because called from thread functions. + void ProcessAreaRS(RecRSThreadData *td); + public: + RecVolumes5(RAROptions *Cmd,bool TestOnly); + ~RecVolumes5(); + bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent); + void Test(RAROptions *Cmd,const wchar *Name); +}; + +bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent); +void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name); + #endif diff --git a/libunrar/recvol3.cpp b/libunrar/recvol3.cpp new file mode 100644 index 0000000..9fb846a --- /dev/null +++ b/libunrar/recvol3.cpp @@ -0,0 +1,544 @@ +// Buffer size for all volumes involved. +static const size_t TotalBufferSize=0x4000000; + +class RSEncode // Encode or decode data area, one object per one thread. +{ + private: + RSCoder RSC; + public: + void EncodeBuf(); + void DecodeBuf(); + + void Init(int RecVolNumber) {RSC.Init(RecVolNumber);} + byte *Buf; + byte *OutBuf; + int BufStart; + int BufEnd; + int FileNumber; + int RecVolNumber; + size_t RecBufferSize; + int *Erasures; + int EraSize; +}; + + +#ifdef RAR_SMP +THREAD_PROC(RSEncodeThread) +{ + RSEncode *rs=(RSEncode *)Data; + rs->EncodeBuf(); +} + +THREAD_PROC(RSDecodeThread) +{ + RSEncode *rs=(RSEncode *)Data; + rs->DecodeBuf(); +} +#endif + +RecVolumes3::RecVolumes3(RAROptions *Cmd,bool TestOnly) +{ + memset(SrcFile,0,sizeof(SrcFile)); + if (TestOnly) + { +#ifdef RAR_SMP + RSThreadPool=NULL; +#endif + } + else + { + Buf.Alloc(TotalBufferSize); + memset(SrcFile,0,sizeof(SrcFile)); +#ifdef RAR_SMP + RSThreadPool=new ThreadPool(Cmd->Threads); +#endif + } +} + + +RecVolumes3::~RecVolumes3() +{ + for (size_t I=0;IName;Ext--) + if (!IsDigit(*Ext)) + if (*Ext=='_' && IsDigit(*(Ext-1))) + DigitGroup++; + else + break; + return DigitGroup<2; +} + + +bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) +{ + wchar ArcName[NM]; + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + wchar *Ext=GetExt(ArcName); + bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10. + bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0; + if (RevName) + { + NewStyle=IsNewStyleRev(ArcName); + while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_')) + Ext--; + wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName)); + + FindFile Find; + Find.SetMask(ArcName); + FindData fd; + while (Find.Next(&fd)) + { + Archive Arc(Cmd); + if (Arc.WOpen(fd.Name) && Arc.IsArchive(true)) + { + wcsncpyz(ArcName,fd.Name,ASIZE(ArcName)); + break; + } + } + } + + Archive Arc(Cmd); + if (!Arc.WCheckOpen(ArcName)) + return false; + if (!Arc.Volume) + { + uiMsg(UIERROR_NOTVOLUME,ArcName); + return false; + } + bool NewNumbering=Arc.NewNumbering; + Arc.Close(); + + wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); + wchar RecVolMask[NM]; + wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); + size_t BaseNamePartLength=VolNumStart-ArcName; + wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); + + int64 RecFileSize=0; + + // We cannot display "Calculating CRC..." message here, because we do not + // know if we'll find any recovery volumes. We'll display it after finding + // the first recovery volume. + bool CalcCRCMessageDone=false; + + FindFile Find; + Find.SetMask(RecVolMask); + FindData RecData; + int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0; + wchar PrevName[NM]; + while (Find.Next(&RecData)) + { + wchar *CurName=RecData.Name; + int P[3]; + if (!RevName && !NewStyle) + { + NewStyle=true; + + wchar *Dot=GetExt(CurName); + if (Dot!=NULL) + { + int LineCount=0; + Dot--; + while (Dot>CurName && *Dot!='.') + { + if (*Dot=='_') + LineCount++; + Dot--; + } + if (LineCount==2) + NewStyle=false; + } + } + if (NewStyle) + { + if (!CalcCRCMessageDone) + { + uiMsg(UIMSG_RECVOLCALCCHECKSUM); + CalcCRCMessageDone=true; + } + + uiMsg(UIMSG_STRING,CurName); + + File CurFile; + CurFile.TOpen(CurName); + CurFile.Seek(0,SEEK_END); + int64 Length=CurFile.Tell(); + CurFile.Seek(Length-7,SEEK_SET); + for (int I=0;I<3;I++) + P[2-I]=CurFile.GetByte()+1; + uint FileCRC=0; + for (int I=0;I<4;I++) + FileCRC|=CurFile.GetByte()<<(I*8); + uint CalcCRC; + CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4); + if (FileCRC!=CalcCRC) + { + uiMsg(UIMSG_CHECKSUM,CurName); + continue; + } + } + else + { + wchar *Dot=GetExt(CurName); + if (Dot==NULL) + continue; + bool WrongParam=false; + for (size_t I=0;I=CurName+BaseNamePartLength); + P[I]=atoiw(Dot+1); + if (P[I]==0 || P[I]>255) + WrongParam=true; + } + if (WrongParam) + continue; + } + if (P[1]+P[2]>255) + continue; + if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2]) + { + uiMsg(UIERROR_RECVOLDIFFSETS,CurName,PrevName); + return false; + } + RecVolNumber=P[1]; + FileNumber=P[2]; + wcsncpyz(PrevName,CurName,ASIZE(PrevName)); + File *NewFile=new File; + NewFile->TOpen(CurName); + SrcFile[FileNumber+P[0]-1]=NewFile; + FoundRecVolumes++; + + if (RecFileSize==0) + RecFileSize=NewFile->FileLength(); + } + if (!Silent || FoundRecVolumes!=0) + uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); + if (FoundRecVolumes==0) + return false; + + bool WriteFlags[256]; + memset(WriteFlags,0,sizeof(WriteFlags)); + + wchar LastVolName[NM]; + *LastVolName=0; + + for (int CurArcNum=0;CurArcNumTOpen(ArcName); + ValidVolume=NewFile->IsArchive(false); + if (ValidVolume) + { + while (NewFile->ReadHeader()!=0) + { + if (NewFile->GetHeaderType()==HEAD_ENDARC) + { + uiMsg(UIMSG_STRING,ArcName); + + if (NewFile->EndArcHead.DataCRC) + { + uint CalcCRC; + CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos); + if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC) + { + ValidVolume=false; + uiMsg(UIMSG_CHECKSUM,ArcName); + } + } + break; + } + NewFile->SeekToNext(); + } + } + if (!ValidVolume) + { + NewFile->Close(); + wchar NewName[NM]; + wcsncpyz(NewName,ArcName,ASIZE(NewName)); + wcsncatz(NewName,L".bad",ASIZE(NewName)); + + uiMsg(UIMSG_BADARCHIVE,ArcName); + uiMsg(UIMSG_RENAMING,ArcName,NewName); + RenameFile(ArcName,NewName); + } + NewFile->Seek(0,SEEK_SET); + } + if (!ValidVolume) + { + // It is important to return 'false' instead of aborting here, + // so if we are called from extraction, we will be able to continue + // extracting. It may happen if .rar and .rev are on read-only disks + // like CDs. + if (!NewFile->Create(ArcName,FMF_WRITE|FMF_SHAREREAD)) + { + // We need to display the title of operation before the error message, + // to make clear for user that create error is related to recovery + // volumes. This is why we cannot use WCreate call here. Title must be + // before create error, not after that. + + uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. + uiMsg(UIERROR_RECONSTRUCTING); + ErrHandler.CreateErrorMsg(ArcName); + return false; + } + + WriteFlags[CurArcNum]=true; + MissingVolumes++; + + if (CurArcNum==FileNumber-1) + wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName)); + + uiMsg(UIMSG_MISSINGVOL,ArcName); + uiMsg(UIEVENT_NEWARCHIVE,ArcName); + } + SrcFile[CurArcNum]=(File*)NewFile; + NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering); + } + + uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); + + if (MissingVolumes==0) + { + uiMsg(UIERROR_RECVOLALLEXIST); + return false; + } + + if (MissingVolumes>FoundRecVolumes) + { + uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. + uiMsg(UIERROR_RECVOLCANNOTFIX); + return false; + } + + uiMsg(UIMSG_RECONSTRUCTING); + + int TotalFiles=FileNumber+RecVolNumber; + int Erasures[256],EraSize=0; + + for (int I=0;IThreads; +#else + uint ThreadNumber=1; +#endif + RSEncode *rse=new RSEncode[ThreadNumber]; + for (uint I=0;IRead(&Buf[I*RecBufferSize],RecBufferSize); + if ((size_t)ReadSize!=RecBufferSize) + memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize); + if (ReadSize>MaxRead) + MaxRead=ReadSize; + } + if (MaxRead==0) + break; + + int CurPercent=ToPercent(ProcessedSize,RecFileSize); + if (!Cmd->DisablePercentage && CurPercent!=LastPercent) + { + uiProcessProgress("RC",ProcessedSize,RecFileSize); + LastPercent=CurPercent; + } + ProcessedSize+=MaxRead; + + int BlockStart=0; + int BlockSize=MaxRead/ThreadNumber; + if (BlockSize<0x100) + BlockSize=MaxRead; + + for (uint CurThread=0;BlockStartBuf=&Buf[0]; + curenc->BufStart=BlockStart; + curenc->BufEnd=BlockStart+BlockSize; + curenc->FileNumber=TotalFiles; + curenc->RecBufferSize=RecBufferSize; + curenc->Erasures=Erasures; + curenc->EraSize=EraSize; + +#ifdef RAR_SMP + if (ThreadNumber>1) + RSThreadPool->AddTask(RSDecodeThread,(void*)curenc); + else + curenc->DecodeBuf(); +#else + curenc->DecodeBuf(); +#endif + + BlockStart+=BlockSize; + } + +#ifdef RAR_SMP + RSThreadPool->WaitDone(); +#endif // RAR_SMP + + for (int I=0;IWrite(&Buf[I*RecBufferSize],MaxRead); + } + delete[] rse; + + for (int I=0;ITell(); + CurFile->Seek(Length-7,SEEK_SET); + for (int J=0;J<7;J++) + CurFile->PutByte(0); + } + CurFile->Close(); + SrcFile[I]=NULL; + } + if (*LastVolName!=0) + { + // Truncate the last volume to its real size. + Archive Arc(Cmd); + if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) && + Arc.SearchBlock(HEAD_ENDARC)) + { + Arc.Seek(Arc.NextBlockPos,SEEK_SET); + char Buf[8192]; + int ReadSize=Arc.Read(Buf,sizeof(Buf)); + int ZeroCount=0; + while (ZeroCountDisablePercentage) + mprintf(L"\b\b\b\b100%%"); + if (!Silent && !Cmd->DisableDone) + mprintf(St(MDone)); +#endif + return true; +} + + +void RSEncode::DecodeBuf() +{ + for (int BufPos=BufStart;BufPosDisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS); + if (FileCRC==CalcCRC) + { + mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk)); + } + else + { + uiMsg(UIERROR_CHECKSUM,VolName,VolName); + ErrHandler.SetErrorCode(RARX_CRC); + } + + NextVolumeName(VolName,ASIZE(VolName),false); + } +} diff --git a/libunrar/recvol5.cpp b/libunrar/recvol5.cpp new file mode 100644 index 0000000..3c524d8 --- /dev/null +++ b/libunrar/recvol5.cpp @@ -0,0 +1,523 @@ +static const uint MaxVolumes=65535; + +RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly) +{ + RealBuf=NULL; + RealReadBuffer=NULL; + + DataCount=0; + RecCount=0; + TotalCount=0; + RecBufferSize=0; + +#ifdef RAR_SMP + MaxUserThreads=Cmd->Threads; +#else + MaxUserThreads=1; +#endif + + ThreadData=new RecRSThreadData[MaxUserThreads]; + for (uint I=0;IRecRSPtr->ProcessAreaRS(td); +} +#endif + + +void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) +{ +/* + RSCoder16 RS; + RS.Init(DataCount,RecCount,Encode ? NULL:ValidFlags); + uint Count=Encode ? RecCount : MissingVolumes; + for (uint I=0;IRS==NULL) + { + td->RS=new RSCoder16; + td->RS->Init(DataCount,RecCount,Encode ? NULL:ValidFlags); + } + td->DataNum=DataNum; + td->Data=Data; + td->Encode=Encode; + td->StartPos=CurPos; + + size_t EndPos=CurPos+ThreadDataSize; + if (EndPos>MaxRead || I==ThreadNumber-1) + EndPos=MaxRead; + + td->Size=EndPos-CurPos; + + CurPos=EndPos; + +#ifdef RAR_SMP + if (ThreadNumber>1) + RecThreadPool->AddTask(RecThreadRS,(void*)td); + else + ProcessAreaRS(td); +#else + ProcessAreaRS(td); +#endif + } +#ifdef RAR_SMP + RecThreadPool->WaitDone(); +#endif // RAR_SMP +} + + +void RecVolumes5::ProcessAreaRS(RecRSThreadData *td) +{ + uint Count=td->Encode ? RecCount : MissingVolumes; + for (uint I=0;IRS->UpdateECC(td->DataNum, I, td->Data+td->StartPos, Buf+I*RecBufferSize+td->StartPos, td->Size); +} + + + + +bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent) +{ + wchar ArcName[NM]; + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + + wchar *Num=GetVolNumPart(ArcName); + while (Num>ArcName && IsDigit(*(Num-1))) + Num--; + if (Num==ArcName) + return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume. + wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); + + wchar FirstVolName[NM]; + *FirstVolName=0; + + int64 RecFileSize=0; + + FindFile VolFind; + VolFind.SetMask(ArcName); + FindData fd; + uint FoundRecVolumes=0; + while (VolFind.Next(&fd)) + { + Wait(); + + Archive *Vol=new Archive(Cmd); + int ItemPos=-1; + if (Vol->WOpen(fd.Name)) + { + if (CmpExt(fd.Name,L"rev")) + { + uint RecNum=ReadHeader(Vol,FoundRecVolumes==0); + if (RecNum!=0) + { + if (FoundRecVolumes==0) + RecFileSize=Vol->FileLength(); + + ItemPos=RecNum; + FoundRecVolumes++; + } + } + else + if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar"))) + { + if (!Vol->Volume && !Vol->BrokenHeader) + { + uiMsg(UIERROR_NOTVOLUME,ArcName); + return false; + } + // We work with archive as with raw data file, so we do not want + // to spend time to QOpen I/O redirection. + Vol->QOpenUnload(); + + Vol->Seek(0,SEEK_SET); + + // RAR volume found. Get its number, store the handle in appropriate + // array slot, clean slots in between if we had to grow the array. + wchar *Num=GetVolNumPart(fd.Name); + uint VolNum=0; + for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--) + VolNum+=(*Num-'0')*K; + if (VolNum==0 || VolNum>MaxVolumes) + continue; + size_t CurSize=RecItems.Size(); + if (VolNum>CurSize) + { + RecItems.Alloc(VolNum); + for (size_t I=CurSize;If=Vol; + Item->New=false; + wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name)); + } + } + + if (!Silent || FoundRecVolumes!=0) + uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); + if (FoundRecVolumes==0) + return false; + + uiMsg(UIMSG_RECVOLCALCCHECKSUM); + + MissingVolumes=0; + for (uint I=0;If!=NULL) + { + uiMsg(UIMSG_STRING,Item->Name); + + uint RevCRC; + CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,INT64NDF,CALCFSUM_CURPOS); + Item->Valid=RevCRC==Item->CRC; + if (!Item->Valid) + { + uiMsg(UIMSG_CHECKSUM,Item->Name); + + // Close only corrupt REV volumes here. We'll close and rename corrupt + // RAR volumes later, if we'll know that recovery is possible. + if (I>=DataCount) + { + Item->f->Close(); + Item->f=NULL; + FoundRecVolumes--; + } + } + } + if (If==NULL || !Item->Valid)) + MissingVolumes++; + } + + uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); + + if (MissingVolumes==0) + { + uiMsg(UIERROR_RECVOLALLEXIST); + return false; + } + + if (MissingVolumes>FoundRecVolumes) + { + uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. + uiMsg(UIERROR_RECVOLCANNOTFIX); + return false; + } + + uiMsg(UIMSG_RECONSTRUCTING); + + // Create missing and rename bad volumes. + uint64 MaxVolSize=0; + for (uint I=0;IFileSize>MaxVolSize) + MaxVolSize=Item->FileSize; + if (Item->f!=NULL && !Item->Valid) + { + Item->f->Close(); + + wchar NewName[NM]; + wcsncpyz(NewName,Item->Name,ASIZE(NewName)); + wcsncatz(NewName,L".bad",ASIZE(NewName)); + + uiMsg(UIMSG_BADARCHIVE,Item->Name); + uiMsg(UIMSG_RENAMING,Item->Name,NewName); + RenameFile(Item->Name,NewName); + delete Item->f; + Item->f=NULL; + } + + if ((Item->New=(Item->f==NULL))) // Additional parentheses to avoid GCC warning. + { + wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name)); + uiMsg(UIMSG_CREATING,Item->Name); + uiMsg(UIEVENT_NEWARCHIVE,Item->Name); + File *NewVol=new File; + bool UserReject; + if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject)) + { + if (!UserReject) + ErrHandler.CreateErrorMsg(Item->Name); + ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE); + } + NewVol->Prealloc(Item->FileSize); + Item->f=NewVol; + Item->New=true; + } + NextVolumeName(FirstVolName,ASIZE(FirstVolName),false); + } + + + int64 ProcessedSize=0; + int LastPercent=-1; + mprintf(L" "); + + // Even though we already preliminary calculated missing volume number, + // let's do it again now, when we have the final and exact information. + MissingVolumes=0; + + ValidFlags=new bool[TotalCount]; + for (uint I=0;If!=NULL && !Item->New) + ReadSize=Item->f->Read(B,RecBufferSize); + if (ReadSize!=RecBufferSize) + memset(B+ReadSize,0,RecBufferSize-ReadSize); + if (ReadSize>MaxRead) + MaxRead=ReadSize; + + // We can have volumes of different size. Let's use data chunk + // for largest volume size. + uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize); + ProcessRS(Cmd,I,B,DataToProcess,false); + } + if (MaxRead==0) + break; + + for (uint I=0,J=0;IFileSize); + Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize); + Item->FileSize-=WriteSize; + } + + int CurPercent=ToPercent(ProcessedSize,RecFileSize); + if (!Cmd->DisablePercentage && CurPercent!=LastPercent) + { + uiProcessProgress("RV",ProcessedSize,RecFileSize); + LastPercent=CurPercent; + } + ProcessedSize+=MaxRead; + } + + for (uint I=0;IClose(); + + delete[] ValidFlags; + delete[] Data; +#if !defined(SILENT) + if (!Cmd->DisablePercentage) + mprintf(L"\b\b\b\b100%%"); + if (!Silent && !Cmd->DisableDone) + mprintf(St(MDone)); +#endif + return true; +} + + +uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev) +{ + const size_t FirstReadSize=REV5_SIGN_SIZE+8; + byte ShortBuf[FirstReadSize]; + if (RecFile->Read(ShortBuf,FirstReadSize)!=FirstReadSize) + return 0; + if (memcmp(ShortBuf,REV5_SIGN,REV5_SIGN_SIZE)!=0) + return 0; + uint HeaderSize=RawGet4(ShortBuf+REV5_SIGN_SIZE+4); + if (HeaderSize>0x100000 || HeaderSize<=5) + return 0; + uint BlockCRC=RawGet4(ShortBuf+REV5_SIGN_SIZE); + + RawRead Raw(RecFile); + if (Raw.Read(HeaderSize)!=HeaderSize) + return 0; + + // Calculate CRC32 of entire header including 4 byte size field. + uint CalcCRC=CRC32(0xffffffff,ShortBuf+REV5_SIGN_SIZE+4,4); + if ((CRC32(CalcCRC,Raw.GetDataPtr(),HeaderSize)^0xffffffff)!=BlockCRC) + return 0; + + if (Raw.Get1()!=1) // Version check. + return 0; + DataCount=Raw.Get2(); + RecCount=Raw.Get2(); + TotalCount=DataCount+RecCount; + uint RecNum=Raw.Get2(); // Number of recovery volume. + if (RecNum>=TotalCount || TotalCount>MaxVolumes) + return 0; + uint RevCRC=Raw.Get4(); // CRC of current REV volume. + + if (FirstRev) + { + // If we have read the first valid REV file, init data structures + // using information from REV header. + size_t CurSize=RecItems.Size(); + RecItems.Alloc(TotalCount); + for (size_t I=CurSize;IDisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS)); + Valid=RevCRC==RecItems[RecNum].CRC; + } + + if (Valid) + { + mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk)); + } + else + { + uiMsg(UIERROR_CHECKSUM,VolName,VolName); + ErrHandler.SetErrorCode(RARX_CRC); + } + + NextVolumeName(VolName,ASIZE(VolName),false); + } +} diff --git a/libunrar/resource.cpp b/libunrar/resource.cpp index f2a4657..dadd072 100644 --- a/libunrar/resource.cpp +++ b/libunrar/resource.cpp @@ -2,11 +2,21 @@ -#if !defined(SILENT) || !defined(RARDLL) -const char *St(MSGID StringId) + + +#ifndef RARDLL +const wchar* St(MSGID StringId) { - return(StringId); + return StringId; +} + + +// Needed for Unix swprintf to convert %s to %ls in legacy language resources. +const wchar *StF(MSGID StringId) +{ + static wchar FormattedStr[512]; + PrintfPrepareFmt(St(StringId),FormattedStr,ASIZE(FormattedStr)); + return FormattedStr; } #endif - diff --git a/libunrar/resource.hpp b/libunrar/resource.hpp index 581b34b..62c5bf4 100644 --- a/libunrar/resource.hpp +++ b/libunrar/resource.hpp @@ -1,14 +1,13 @@ #ifndef _RAR_RESOURCE_ #define _RAR_RESOURCE_ -#if defined(SILENT) && defined(RARDLL) -#define St(x) ("") +#ifdef RARDLL +#define St(x) (L"") +#define StF(x) (L"") #else -const char *St(MSGID StringId); +const wchar *St(MSGID StringId); +const wchar *StF(MSGID StringId); #endif -inline const char *StT(MSGID StringId) {return(St(StringId));} - - #endif diff --git a/libunrar/rijndael.cpp b/libunrar/rijndael.cpp index 67434ba..dd19750 100644 --- a/libunrar/rijndael.cpp +++ b/libunrar/rijndael.cpp @@ -1,13 +1,15 @@ -/************************************************************************** - * This code is based on Szymon Stefanek AES implementation: * - * http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael-cpplib.tar.gz * - * * - * Dynamic tables generation is based on the Brian Gladman work: * - * http://fp.gladman.plus.com/cryptography_technology/rijndael * - **************************************************************************/ +/*************************************************************************** + * This code is based on public domain Szymon Stefanek AES implementation: * + * http://www.pragmaware.net/software/rijndael/index.php * + * * + * Dynamic tables generation is based on the Brian Gladman work: * + * http://fp.gladman.plus.com/cryptography_technology/rijndael * + ***************************************************************************/ #include "rar.hpp" -const int uKeyLenInBytes=16, m_uRounds=10; +#ifdef USE_SSE +#include +#endif static byte S[256],S5[256],rcon[30]; static byte T1[256][4],T2[256][4],T3[256][4],T4[256][4]; @@ -15,16 +17,16 @@ static byte T5[256][4],T6[256][4],T7[256][4],T8[256][4]; static byte U1[256][4],U2[256][4],U3[256][4],U4[256][4]; -inline void Xor128(byte *dest,const byte *arg1,const byte *arg2) +inline void Xor128(void *dest,const void *arg1,const void *arg2) { -#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) +#ifdef ALLOW_MISALIGNED ((uint32*)dest)[0]=((uint32*)arg1)[0]^((uint32*)arg2)[0]; ((uint32*)dest)[1]=((uint32*)arg1)[1]^((uint32*)arg2)[1]; ((uint32*)dest)[2]=((uint32*)arg1)[2]^((uint32*)arg2)[2]; ((uint32*)dest)[3]=((uint32*)arg1)[3]^((uint32*)arg2)[3]; #else for (int I=0;I<16;I++) - dest[I]=arg1[I]^arg2[I]; + ((byte*)dest)[I]=((byte*)arg1)[I]^((byte*)arg2)[I]; #endif } @@ -32,7 +34,7 @@ inline void Xor128(byte *dest,const byte *arg1,const byte *arg2) inline void Xor128(byte *dest,const byte *arg1,const byte *arg2, const byte *arg3,const byte *arg4) { -#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) +#ifdef ALLOW_MISALIGNED (*(uint32*)dest)=(*(uint32*)arg1)^(*(uint32*)arg2)^(*(uint32*)arg3)^(*(uint32*)arg4); #else for (int I=0;I<4;I++) @@ -43,7 +45,7 @@ inline void Xor128(byte *dest,const byte *arg1,const byte *arg2, inline void Copy128(byte *dest,const byte *src) { -#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) +#ifdef ALLOW_MISALIGNED ((uint32*)dest)[0]=((uint32*)src)[0]; ((uint32*)dest)[1]=((uint32*)src)[1]; ((uint32*)dest)[2]=((uint32*)src)[2]; @@ -63,59 +65,265 @@ Rijndael::Rijndael() { if (S[0]==0) GenerateTables(); + CBCMode = true; // Always true for RAR. } -void Rijndael::init(Direction dir,const byte * key,byte * initVector) +void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector) { - m_direction = dir; +#ifdef USE_SSE + // Check SSE here instead of constructor, so if object is a part of some + // structure memset'ed before use, this variable is not lost. + int CPUInfo[4]; + __cpuid(CPUInfo, 0x80000000); // Get the maximum supported cpuid function. + if ((CPUInfo[0] & 0x7fffffff)>=1) + { + __cpuid(CPUInfo, 1); + AES_NI=(CPUInfo[2] & 0x2000000)!=0; + } + else + AES_NI=0; +#endif + + // Other developers asked us to initialize it to suppress "may be used + // uninitialized" warning in code below in some compilers. + uint uKeyLenInBytes=0; + + switch(keyLen) + { + case 128: + uKeyLenInBytes = 16; + m_uRounds = 10; + break; + case 192: + uKeyLenInBytes = 24; + m_uRounds = 12; + break; + case 256: + uKeyLenInBytes = 32; + m_uRounds = 14; + break; + } byte keyMatrix[_MAX_KEY_COLUMNS][4]; - for(uint i = 0;i < uKeyLenInBytes;i++) + for(uint i = 0; i < uKeyLenInBytes; i++) keyMatrix[i >> 2][i & 3] = key[i]; - for(int i = 0;i < MAX_IV_SIZE;i++) - m_initVector[i] = initVector[i]; + if (initVector==NULL) + memset(m_initVector, 0, sizeof(m_initVector)); + else + for(int i = 0; i < MAX_IV_SIZE; i++) + m_initVector[i] = initVector[i]; keySched(keyMatrix); - if(m_direction == Decrypt) + if(!Encrypt) keyEncToDec(); } +void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer) +{ + if (inputLen <= 0) + return; + + size_t numBlocks = inputLen/16; +#ifdef USE_SSE + if (AES_NI) + { + blockEncryptSSE(input,numBlocks,outBuffer); + return; + } +#endif + + byte *prevBlock = m_initVector; + for(size_t i = numBlocks;i > 0;i--) + { + byte block[16]; + if (CBCMode) + Xor128(block,prevBlock,input); + else + Copy128(block,input); + + byte temp[4][4]; + + Xor128(temp,block,m_expandedKey[0]); + Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]); + Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]); + Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]); + Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]); + + for(int r = 1; r < m_uRounds-1; r++) + { + Xor128(temp,outBuffer,m_expandedKey[r]); + Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]); + Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]); + Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]); + Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]); + } + Xor128(temp,outBuffer,m_expandedKey[m_uRounds-1]); + outBuffer[ 0] = T1[temp[0][0]][1]; + outBuffer[ 1] = T1[temp[1][1]][1]; + outBuffer[ 2] = T1[temp[2][2]][1]; + outBuffer[ 3] = T1[temp[3][3]][1]; + outBuffer[ 4] = T1[temp[1][0]][1]; + outBuffer[ 5] = T1[temp[2][1]][1]; + outBuffer[ 6] = T1[temp[3][2]][1]; + outBuffer[ 7] = T1[temp[0][3]][1]; + outBuffer[ 8] = T1[temp[2][0]][1]; + outBuffer[ 9] = T1[temp[3][1]][1]; + outBuffer[10] = T1[temp[0][2]][1]; + outBuffer[11] = T1[temp[1][3]][1]; + outBuffer[12] = T1[temp[3][0]][1]; + outBuffer[13] = T1[temp[0][1]][1]; + outBuffer[14] = T1[temp[1][2]][1]; + outBuffer[15] = T1[temp[2][3]][1]; + Xor128(outBuffer,outBuffer,m_expandedKey[m_uRounds]); + prevBlock=outBuffer; + + outBuffer += 16; + input += 16; + } + Copy128(m_initVector,prevBlock); +} + + +#ifdef USE_SSE +void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer) +{ + __m128i v = _mm_loadu_si128((__m128i*)m_initVector); + __m128i *src=(__m128i*)input; + __m128i *dest=(__m128i*)outBuffer; + __m128i *rkey=(__m128i*)m_expandedKey; + while (numBlocks > 0) + { + __m128i d = _mm_loadu_si128(src++); + if (CBCMode) + v = _mm_xor_si128(v, d); + else + v = d; + __m128i r0 = _mm_loadu_si128(rkey); + v = _mm_xor_si128(v, r0); + + for (int i=1; i 0; i--) { - decrypt(input, block); - Xor128(block,block,(byte*)iv); -#if STRICT_ALIGN - memcpy(iv, input, 16); - memcpy(outBuf, block, 16); -#else + byte temp[4][4]; + + Xor128(temp,input,m_expandedKey[m_uRounds]); + + Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); + Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); + Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); + Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); + + for(int r = m_uRounds-1; r > 1; r--) + { + Xor128(temp,block,m_expandedKey[r]); + Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); + Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); + Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); + Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); + } + + Xor128(temp,block,m_expandedKey[1]); + block[ 0] = S5[temp[0][0]]; + block[ 1] = S5[temp[3][1]]; + block[ 2] = S5[temp[2][2]]; + block[ 3] = S5[temp[1][3]]; + block[ 4] = S5[temp[1][0]]; + block[ 5] = S5[temp[0][1]]; + block[ 6] = S5[temp[3][2]]; + block[ 7] = S5[temp[2][3]]; + block[ 8] = S5[temp[2][0]]; + block[ 9] = S5[temp[1][1]]; + block[10] = S5[temp[0][2]]; + block[11] = S5[temp[3][3]]; + block[12] = S5[temp[3][0]]; + block[13] = S5[temp[2][1]]; + block[14] = S5[temp[1][2]]; + block[15] = S5[temp[0][3]]; + Xor128(block,block,m_expandedKey[0]); + + if (CBCMode) + Xor128(block,block,iv); + Copy128((byte*)iv,input); Copy128(outBuffer,block); -#endif + input += 16; outBuffer += 16; } memcpy(m_initVector,iv,16); - - return 16*numBlocks; } +#ifdef USE_SSE +void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer) +{ + __m128i initVector = _mm_loadu_si128((__m128i*)m_initVector); + __m128i *src=(__m128i*)input; + __m128i *dest=(__m128i*)outBuffer; + __m128i *rkey=(__m128i*)m_expandedKey; + while (numBlocks > 0) + { + __m128i rl = _mm_loadu_si128(rkey + m_uRounds); + __m128i d = _mm_loadu_si128(src++); + __m128i v = _mm_xor_si128(rl, d); + + for (int i=m_uRounds-1; i>0; i--) + { + __m128i ri = _mm_loadu_si128(rkey + i); + v = _mm_aesdec_si128(v, ri); + } + + __m128i r0 = _mm_loadu_si128(rkey); + v = _mm_aesdeclast_si128(v, r0); + + if (CBCMode) + v = _mm_xor_si128(v, initVector); + initVector = d; + _mm_storeu_si128(dest++,v); + numBlocks--; + } + _mm_storeu_si128((__m128i*)m_initVector,initVector); +} +#endif + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ALGORITHM ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -197,8 +405,8 @@ void Rijndael::keyEncToDec() for(int r = 1; r < m_uRounds; r++) { byte n_expandedKey[4][4]; - for (int i=0;i<4;i++) - for (int j=0;j<4;j++) + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) { byte *w=m_expandedKey[r][j]; n_expandedKey[j][i]=U1[w[0]][i]^U2[w[1]][i]^U3[w[2]][i]^U4[w[3]][i]; @@ -208,47 +416,6 @@ void Rijndael::keyEncToDec() } -void Rijndael::decrypt(const byte a[16], byte b[16]) -{ - int r; - byte temp[4][4]; - - Xor128((byte*)temp,(byte*)a,(byte*)m_expandedKey[m_uRounds]); - - Xor128(b, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); - Xor128(b+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); - Xor128(b+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); - Xor128(b+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); - - for(r = m_uRounds-1; r > 1; r--) - { - Xor128((byte*)temp,(byte*)b,(byte*)m_expandedKey[r]); - Xor128(b, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); - Xor128(b+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); - Xor128(b+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); - Xor128(b+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); - } - - Xor128((byte*)temp,(byte*)b,(byte*)m_expandedKey[1]); - b[ 0] = S5[temp[0][0]]; - b[ 1] = S5[temp[3][1]]; - b[ 2] = S5[temp[2][2]]; - b[ 3] = S5[temp[1][3]]; - b[ 4] = S5[temp[1][0]]; - b[ 5] = S5[temp[0][1]]; - b[ 6] = S5[temp[3][2]]; - b[ 7] = S5[temp[2][3]]; - b[ 8] = S5[temp[2][0]]; - b[ 9] = S5[temp[1][1]]; - b[10] = S5[temp[0][2]]; - b[11] = S5[temp[3][3]]; - b[12] = S5[temp[3][0]]; - b[13] = S5[temp[2][1]]; - b[14] = S5[temp[1][2]]; - b[15] = S5[temp[0][3]]; - Xor128((byte*)b,(byte*)b,(byte*)m_expandedKey[0]); -} - #define ff_poly 0x011b #define ff_hi 0x80 @@ -296,3 +463,53 @@ void Rijndael::GenerateTables() U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[i][0]=T6[i][1]=T7[i][2]=T8[i][3]=FFmul0e(b); } } + + +#if 0 +static void TestRijndael(); +struct TestRij {TestRij() {TestRijndael();exit(0);}} GlobalTestRij; + +// Test CBC encryption according to NIST 800-38A. +void TestRijndael() +{ + byte IV[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + byte PT[64]={ + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51, + 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11,0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef, + 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17,0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10, + }; + + byte Key128[16]={0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c}; + byte Chk128[16]={0x3f,0xf1,0xca,0xa1,0x68,0x1f,0xac,0x09,0x12,0x0e,0xca,0x30,0x75,0x86,0xe1,0xa7}; + byte Key192[24]={0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52,0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5,0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b}; + byte Chk192[16]={0x08,0xb0,0xe2,0x79,0x88,0x59,0x88,0x81,0xd9,0x20,0xa9,0xe6,0x4f,0x56,0x15,0xcd}; + byte Key256[32]={0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4}; + byte Chk256[16]={0xb2,0xeb,0x05,0xe2,0xc3,0x9b,0xe9,0xfc,0xda,0x6c,0x19,0x07,0x8c,0x6a,0x9d,0x1b}; + byte *Key[3]={Key128,Key192,Key256}; + byte *Chk[3]={Chk128,Chk192,Chk256}; + + Rijndael rij; // Declare outside of loop to test re-initialization. + for (uint L=0;L<3;L++) + { + byte Out[16]; + wchar Str[sizeof(Out)*2+1]; + + uint KeyLength=128+L*64; + rij.Init(true,Key[L],KeyLength,IV); + for (uint I=0;I MAXPAR) + J^=0x11D; // 0x11D field-generator polynomial (x^8+x^4+x^3+x^2+1). } - for (int I=MAXPAR;I0;J--) ShiftReg[J]=ShiftReg[J-1]^gfMult(GXPol[J],D); ShiftReg[0]=gfMult(GXPol[0],D); @@ -78,66 +92,69 @@ void RSCoder::Encode(byte *Data,int DataSize,byte *DestData) bool RSCoder::Decode(byte *Data,int DataSize,int *EraLoc,int EraSize) { - int SynData[MAXPOL]; + int SynData[MAXPOL]; // Syndrome data. + bool AllZeroes=true; for (int I=0;I0;I--) - PolB[I]^=gfMult(M,PolB[I-1]); + ELPol[I]^=gfMult(M,ELPol[I-1]); ErrCount=0; + + // Find roots of error locator polynomial. for (int Root=MAXPAR-DataSize;Root0) for (int I=0;I=0 && DataPosgfSize) + E^=0x1100B; // Irreducible field-generator polynomial. + } + + // log(0)+log(x) must be outside of usual log table, so we can set it + // to 0 and avoid check for 0 in multiplication parameters. + gfLog[0]= 2*gfSize; + for (uint I=2*gfSize;I<=4*gfSize;I++) // Results for log(0)+log(x). + gfExp[I]=0; +} + + +uint RSCoder16::gfAdd(uint a,uint b) // Addition in Galois field. +{ + return a^b; +} + + +uint RSCoder16::gfMul(uint a,uint b) // Multiplication in Galois field. +{ + return gfExp[gfLog[a]+gfLog[b]]; +} + + +uint RSCoder16::gfInv(uint a) // Inverse element in Galois field. +{ + return a==0 ? 0:gfExp[gfSize-gfLog[a]]; +} + + +bool RSCoder16::Init(uint DataCount, uint RecCount, bool *ValidityFlags) +{ + ND = DataCount; + NR = RecCount; + NE = 0; + + Decoding=ValidityFlags!=NULL; + if (Decoding) + { + delete[] ValidFlags; + ValidFlags=new bool[ND + NR]; + + for (uint I = 0; I < ND + NR; I++) + ValidFlags[I]=ValidityFlags[I]; + for (uint I = 0; I < ND; I++) + if (!ValidFlags[I]) + NE++; + uint ValidECC=0; + for (uint I = ND; I < ND + NR; I++) + if (ValidFlags[I]) + ValidECC++; + if (NE > ValidECC || NE == 0 || ValidECC == 0) + return false; + } + if (ND + NR > gfSize || NR > ND || ND == 0 || NR == 0) + return false; + + delete[] MX; + if (Decoding) + { + MX=new uint[NE * ND]; + MakeDecoderMatrix(); + InvertDecoderMatrix(); + } + else + { + MX=new uint[NR * ND]; + MakeEncoderMatrix(); + } + return true; +} + + +void RSCoder16::MakeEncoderMatrix() +{ + // Create Cauchy encoder generator matrix. Skip trivial "1" diagonal rows, + // which would just copy source data to destination. + for (uint I = 0; I < NR; I++) + for (uint J = 0; J < ND; J++) + MX[I * ND + J] = gfInv( gfAdd( (I+ND), J) ); +} + + +void RSCoder16::MakeDecoderMatrix() +{ + // Create Cauchy decoder matrix. Skip trivial rows matching valid data + // units and containing "1" on main diagonal. Such rows would just copy + // source data to destination and they have no real value for us. + // Include rows only for broken data units and replace them by first + // available valid recovery code rows. + for (uint Flag=0, R=ND, Dest=0; Flag < ND; Flag++) + if (!ValidFlags[Flag]) // For every broken data unit. + { + while (!ValidFlags[R]) // Find a valid recovery unit. + R++; + for (uint J = 0; J < ND; J++) // And place its row to matrix. + MX[Dest*ND + J] = gfInv( gfAdd(R,J) ); + Dest++; + R++; + } +} + + +// Apply Gauss–Jordan elimination to find inverse of decoder matrix. +// We have the square NDxND matrix, but we do not store its trivial +// diagonal "1" rows matching valid data, so we work with NExND matrix. +// Our original Cauchy matrix does not contain 0, so we skip search +// for non-zero pivot. +void RSCoder16::InvertDecoderMatrix() +{ + uint *MI=new uint[NE * ND]; // We'll create inverse matrix here. + memset(MI, 0, ND * NE * sizeof(*MI)); // Initialize to identity matrix. + for (uint Kr = 0, Kf = 0; Kr < NE; Kr++, Kf++) + { + while (ValidFlags[Kf]) // Skip trivial rows. + Kf++; + MI[Kr * ND + Kf] = 1; // Set diagonal 1. + } + + // Kr is the number of row in our actual reduced NE x ND matrix, + // which does not contain trivial diagonal 1 rows. + // Kf is the number of row in full ND x ND matrix with all trivial rows + // included. + for (uint Kr = 0, Kf = 0; Kf < ND; Kr++, Kf++) // Select pivot row. + { + while (ValidFlags[Kf] && Kf < ND) + { + // Here we process trivial diagonal 1 rows matching valid data units. + // Their processing can be simplified comparing to usual rows. + // In full version of elimination we would set MX[I * ND + Kf] to zero + // after MI[..]^=, but we do not need it for matrix inversion. + for (uint I = 0; I < NE; I++) + MI[I * ND + Kf] ^= MX[I * ND + Kf]; + Kf++; + } + + if (Kf == ND) + break; + + uint *MXk = MX + Kr * ND; // k-th row of main matrix. + uint *MIk = MI + Kr * ND; // k-th row of inversion matrix. + + uint PInv = gfInv( MXk[Kf] ); // Pivot inverse. + // Divide the pivot row by pivot, so pivot cell contains 1. + for (uint I = 0; I < ND; I++) + { + MXk[I] = gfMul( MXk[I], PInv ); + MIk[I] = gfMul( MIk[I], PInv ); + } + + for (uint I = 0; I < NE; I++) + if (I != Kr) // For all rows except containing the pivot cell. + { + // Apply Gaussian elimination Mij -= Mkj * Mik / pivot. + // Since pivot is already 1, it is reduced to Mij -= Mkj * Mik. + uint *MXi = MX + I * ND; // i-th row of main matrix. + uint *MIi = MI + I * ND; // i-th row of inversion matrix. + uint Mik = MXi[Kf]; // Cell in pivot position. + for (uint J = 0; J < ND; J++) + { + MXi[J] ^= gfMul(MXk[J] , Mik); + MIi[J] ^= gfMul(MIk[J] , Mik); + } + } + } + + // Copy data to main matrix. + for (uint I = 0; I < NE * ND; I++) + MX[I] = MI[I]; + + delete[] MI; +} + + +#if 0 +// Multiply matrix to data vector. When encoding, it contains data in Data +// and stores error correction codes in Out. When decoding it contains +// broken data followed by ECC in Data and stores recovered data to Out. +// We do not use this function now, everything is moved to UpdateECC. +void RSCoder16::Process(const uint *Data, uint *Out) +{ + uint ProcData[gfSize]; + + for (uint I = 0; I < ND; I++) + ProcData[I]=Data[I]; + + if (Decoding) + { + // Replace broken data units with first available valid recovery codes. + // 'Data' array must contain recovery codes after data. + for (uint I=0, R=ND, Dest=0; I < ND; I++) + if (!ValidFlags[I]) // For every broken data unit. + { + while (!ValidFlags[R]) // Find a valid recovery unit. + R++; + ProcData[I]=Data[R]; + R++; + } + } + + uint H=Decoding ? NE : NR; + for (uint I = 0; I < H; I++) + { + uint R = 0; // Result of matrix row multiplication to data. + + uint *MXi=MX + I * ND; + for (uint J = 0; J < ND; J++) + R ^= gfMul(MXi[J], ProcData[J]); + + Out[I] = R; + } +} +#endif + + +// We update ECC in blocks by applying every data block to all ECC blocks. +// This function applies one data block to one ECC block. +void RSCoder16::UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize) +{ + if (DataNum==0) // Init ECC data. + memset(ECC, 0, BlockSize); + + bool DirectAccess; +#ifdef LITTLE_ENDIAN + // We can access data and ECC directly if we have little endian 16 bit uint. + DirectAccess=sizeof(ushort)==2; +#else + DirectAccess=false; +#endif + +#ifdef USE_SSE + if (DirectAccess && SSE_UpdateECC(DataNum,ECCNum,Data,ECC,BlockSize)) + return; +#endif + + if (ECCNum==0) + { + if (DataLogSize!=BlockSize) + { + delete[] DataLog; + DataLog=new uint[BlockSize]; + DataLogSize=BlockSize; + + } + if (DirectAccess) + for (size_t I=0; I>8; + ((byte *)&T1L)[I]=gfMul(I<<4,M); + ((byte *)&T1H)[I]=gfMul(I<<4,M)>>8; + ((byte *)&T2L)[I]=gfMul(I<<8,M); + ((byte *)&T2H)[I]=gfMul(I<<8,M)>>8; + ((byte *)&T3L)[I]=gfMul(I<<12,M); + ((byte *)&T3H)[I]=gfMul(I<<12,M)>>8; + } + + size_t Pos=0; + + __m128i LowByteMask=_mm_set1_epi16(0xff); // 00ff00ff...00ff + __m128i Low4Mask=_mm_set1_epi8(0xf); // 0f0f0f0f...0f0f + __m128i High4Mask=_mm_slli_epi16(Low4Mask,4); // f0f0f0f0...f0f0 + + for (; Pos+2*sizeof(__m128i)<=BlockSize; Pos+=2*sizeof(__m128i)) + { + // We process two 128 bit chunks of source data at once. + __m128i *D=(__m128i *)(Data+Pos); + + // Place high bytes of both chunks to one variable and low bytes to + // another, so we can use the table lookup multiplication for 16 values + // 4 bit length each at once. + __m128i HighBytes0=_mm_srli_epi16(D[0],8); + __m128i LowBytes0=_mm_and_si128(D[0],LowByteMask); + __m128i HighBytes1=_mm_srli_epi16(D[1],8); + __m128i LowBytes1=_mm_and_si128(D[1],LowByteMask); + __m128i HighBytes=_mm_packus_epi16(HighBytes0,HighBytes1); + __m128i LowBytes=_mm_packus_epi16(LowBytes0,LowBytes1); + + // Multiply bits 0..3 of low bytes. Store low and high product bytes + // separately in cumulative sum variables. + __m128i LowBytesLow4=_mm_and_si128(LowBytes,Low4Mask); + __m128i LowBytesMultSum=_mm_shuffle_epi8(T0L,LowBytesLow4); + __m128i HighBytesMultSum=_mm_shuffle_epi8(T0H,LowBytesLow4); + + // Multiply bits 4..7 of low bytes. Store low and high product bytes separately. + __m128i LowBytesHigh4=_mm_and_si128(LowBytes,High4Mask); + LowBytesHigh4=_mm_srli_epi16(LowBytesHigh4,4); + __m128i LowBytesHigh4MultLow=_mm_shuffle_epi8(T1L,LowBytesHigh4); + __m128i LowBytesHigh4MultHigh=_mm_shuffle_epi8(T1H,LowBytesHigh4); + + // Add new product to existing sum, low and high bytes separately. + LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,LowBytesHigh4MultLow); + HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,LowBytesHigh4MultHigh); + + // Multiply bits 0..3 of high bytes. Store low and high product bytes separately. + __m128i HighBytesLow4=_mm_and_si128(HighBytes,Low4Mask); + __m128i HighBytesLow4MultLow=_mm_shuffle_epi8(T2L,HighBytesLow4); + __m128i HighBytesLow4MultHigh=_mm_shuffle_epi8(T2H,HighBytesLow4); + + // Add new product to existing sum, low and high bytes separately. + LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesLow4MultLow); + HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesLow4MultHigh); + + // Multiply bits 4..7 of high bytes. Store low and high product bytes separately. + __m128i HighBytesHigh4=_mm_and_si128(HighBytes,High4Mask); + HighBytesHigh4=_mm_srli_epi16(HighBytesHigh4,4); + __m128i HighBytesHigh4MultLow=_mm_shuffle_epi8(T3L,HighBytesHigh4); + __m128i HighBytesHigh4MultHigh=_mm_shuffle_epi8(T3H,HighBytesHigh4); + + // Add new product to existing sum, low and high bytes separately. + LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesHigh4MultLow); + HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesHigh4MultHigh); + + // Combine separate low and high cumulative sum bytes to 16-bit words. + __m128i HighBytesHigh4Mult0=_mm_unpacklo_epi8(LowBytesMultSum,HighBytesMultSum); + __m128i HighBytesHigh4Mult1=_mm_unpackhi_epi8(LowBytesMultSum,HighBytesMultSum); + + // Add result to ECC. + __m128i *StoreECC=(__m128i *)(ECC+Pos); + + StoreECC[0]=_mm_xor_si128(StoreECC[0],HighBytesHigh4Mult0); + StoreECC[1]=_mm_xor_si128(StoreECC[1],HighBytesHigh4Mult1); + } + + // If we have non 128 bit aligned data in the end of block, process them + // in a usual way. We cannot do the same in the beginning of block, + // because Data and ECC can have different alignment offsets. + for (; PosCloseCount) - SaveFile->Seek(SavePos,SEEK_SET); -} diff --git a/libunrar/savepos.hpp b/libunrar/savepos.hpp index b3b2373..1f8353f 100644 --- a/libunrar/savepos.hpp +++ b/libunrar/savepos.hpp @@ -6,10 +6,35 @@ class SaveFilePos private: File *SaveFile; int64 SavePos; - uint CloseCount; public: - SaveFilePos(File &SaveFile); - ~SaveFilePos(); + SaveFilePos(File &Src) + { + SaveFile=&Src; + SavePos=Src.Tell(); + } + ~SaveFilePos() + { + // Unless the file is already closed either by current exception + // processing or intentionally by external code. + if (SaveFile->IsOpened()) + { + try + { + SaveFile->Seek(SavePos,SEEK_SET); + } + catch(RAR_EXIT) + { + // Seek() can throw an exception and it terminates process + // if we are already processing another exception. Also in C++ 11 + // an exception in destructor always terminates process unless + // we mark destructor with noexcept(false). So we do not want to + // throw here. To prevent data loss we do not want to continue + // execution after seek error, so we close the file. + // Any next access to this file will return an error. + SaveFile->Close(); + } + } + } }; #endif diff --git a/libunrar/scantree.cpp b/libunrar/scantree.cpp index f371249..a13a3eb 100644 --- a/libunrar/scantree.cpp +++ b/libunrar/scantree.cpp @@ -8,15 +8,17 @@ ScanTree::ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN ScanTree::GetDirs=GetDirs; ScanEntireDisk=false; + FolderWildcards=false; SetAllMaskDepth=0; *CurMask=0; - *CurMaskW=0; memset(FindStack,0,sizeof(FindStack)); Depth=0; Errors=0; *ErrArcName=0; Cmd=NULL; + ErrDirList=NULL; + ErrDirSpecPathLength=NULL; } @@ -28,18 +30,30 @@ ScanTree::~ScanTree() } -SCAN_CODE ScanTree::GetNext(FindData *FindData) +SCAN_CODE ScanTree::GetNext(FindData *FD) { if (Depth<0) - return(SCAN_DONE); + return SCAN_DONE; + +#ifndef SILENT + uint LoopCount=0; +#endif SCAN_CODE FindCode; while (1) { if (*CurMask==0 && !GetNextMask()) - return(SCAN_DONE); + return SCAN_DONE; - FindCode=FindProc(FindData); +#ifndef SILENT + // Let's return some ticks to system or WinRAR can become irresponsible + // while scanning files in command like "winrar a -r arc c:\file.ext". + // Also we reset system sleep timer here. + if ((++LoopCount & 0x3ff)==0) + Wait(); +#endif + + FindCode=FindProc(FD); if (FindCode==SCAN_ERROR) { Errors++; @@ -47,92 +61,208 @@ SCAN_CODE ScanTree::GetNext(FindData *FindData) } if (FindCode==SCAN_NEXT) continue; - if (FindCode==SCAN_SUCCESS && FindData->IsDir && GetDirs==SCAN_SKIPDIRS) + if (FindCode==SCAN_SUCCESS && FD->IsDir && GetDirs==SCAN_SKIPDIRS) continue; if (FindCode==SCAN_DONE && GetNextMask()) continue; + if (FilterList.ItemsCount()>0 && FindCode==SCAN_SUCCESS) + if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name,false,MATCH_WILDSUBPATH)) + continue; break; } - return(FindCode); + return FindCode; +} + + +// For masks like dir1\dir2*\*.ext in non-recursive mode. +bool ScanTree::ExpandFolderMask() +{ + bool WildcardFound=false; + uint SlashPos=0; + for (int I=0;CurMask[I]!=0;I++) + { + if (CurMask[I]=='?' || CurMask[I]=='*') + WildcardFound=true; + if (WildcardFound && IsPathDiv(CurMask[I])) + { + // First path separator position after folder wildcard mask. + // In case of dir1\dir2*\dir3\name.ext mask it may point not to file + // name, so we cannot use PointToName() here. + SlashPos=I; + break; + } + } + + wchar Mask[NM]; + wcsncpyz(Mask,CurMask,ASIZE(Mask)); + Mask[SlashPos]=0; + + // Prepare the list of all folders matching the wildcard mask. + ExpandedFolderList.Reset(); + FindFile Find; + Find.SetMask(Mask); + FindData FD; + while (Find.Next(&FD)) + if (FD.IsDir) + { + wcsncatz(FD.Name,CurMask+SlashPos,ASIZE(FD.Name)); + + // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched + // by such mask. Skipping empty dir with dir*\*.* confused some users. + wchar *LastMask=PointToName(FD.Name); + if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) + RemoveNameFromPath(FD.Name); + + ExpandedFolderList.AddString(FD.Name); + } + if (ExpandedFolderList.ItemsCount()==0) + return false; + // Return the first matching folder name now. + ExpandedFolderList.GetString(CurMask,ASIZE(CurMask)); + return true; +} + + +// For masks like dir1\dir2*\file.ext this function sets 'dir1' recursive mask +// and '*\dir2*\file.ext' filter. Masks without folder wildcards are +// returned as is. +bool ScanTree::GetFilteredMask() +{ + // If we have some matching folders left for non-recursive folder wildcard + // mask, we return it here. + if (ExpandedFolderList.ItemsCount()>0 && ExpandedFolderList.GetString(CurMask,ASIZE(CurMask))) + return true; + + FolderWildcards=false; + FilterList.Reset(); + if (!FileMasks->GetString(CurMask,ASIZE(CurMask))) + return false; + + // Check if folder wildcards present. + bool WildcardFound=false; + uint FolderWildcardCount=0; + uint SlashPos=0; + uint StartPos=0; +#ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard. + if (CurMask[0]=='\\' && CurMask[1]=='\\' && CurMask[2]=='?' && CurMask[3]=='\\') + StartPos=4; +#endif + for (uint I=StartPos;CurMask[I]!=0;I++) + { + if (CurMask[I]=='?' || CurMask[I]=='*') + WildcardFound=true; + if (IsPathDiv(CurMask[I]) || IsDriveDiv(CurMask[I])) + { + if (WildcardFound) + { + // Calculate a number of folder wildcards in current mask. + FolderWildcardCount++; + WildcardFound=false; + } + if (FolderWildcardCount==0) + SlashPos=I; // Slash position before first folder wildcard mask. + } + } + if (FolderWildcardCount==0) + return true; + FolderWildcards=true; // Global folder wildcards flag. + + // If we have only one folder wildcard component and -r is missing or -r- + // is specified, prepare matching folders in non-recursive mode. + // We assume -r for masks like dir1*\dir2*\file*, because it is complicated + // to fast find them using OS file find API call. + if ((Recurse==RECURSE_NONE || Recurse==RECURSE_DISABLE) && FolderWildcardCount==1) + return ExpandFolderMask(); + + wchar Filter[NM]; + // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders. + wcsncpyz(Filter,L"*",ASIZE(Filter)); + AddEndSlash(Filter,ASIZE(Filter)); + // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*' + wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos; + wcsncatz(Filter,WildName,ASIZE(Filter)); + + // Treat dir*\* or dir*\*.* as dir\, so empty 'dir' is also matched + // by such mask. Skipping empty dir with dir*\*.* confused some users. + wchar *LastMask=PointToName(Filter); + if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) + *LastMask=0; + + FilterList.AddString(Filter); + + bool RelativeDrive=IsDriveDiv(CurMask[SlashPos]); + if (RelativeDrive) + SlashPos++; // Use "d:" instead of "d" for d:* mask. + + CurMask[SlashPos]=0; + + if (!RelativeDrive) // Keep d: mask as is, not convert to d:\* + { + // We need to append "\*" both for -ep1 to work correctly and to + // convert d:\* masks previously truncated to d: back to original form. + AddEndSlash(CurMask,ASIZE(CurMask)); + wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); + } + return true; } bool ScanTree::GetNextMask() { - if (!FileMasks->GetString(CurMask,CurMaskW,sizeof(CurMask))) - return(false); - CurMask[ASIZE(CurMask)-1]=0; - CurMaskW[ASIZE(CurMaskW)-1]=0; -#ifdef _WIN_32 - UnixSlashToDos(CurMask); + if (!GetFilteredMask()) + return false; +#ifdef _WIN_ALL + UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask)); #endif // We wish to scan entire disk if mask like c:\ is specified // regardless of recursion mode. Use c:\*.* mask when need to scan only // the root directory. - ScanEntireDisk=IsDiskLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; - char *Name=PointToName(CurMask); + wchar *Name=PointToName(CurMask); if (*Name==0) - strcat(CurMask,MASKALL); + wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0)) { - AddEndSlash(CurMask); - strcat(CurMask,MASKALL); + AddEndSlash(CurMask,ASIZE(CurMask)); + wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); } SpecPathLength=Name-CurMask; - - bool WideName=(*CurMaskW!=0); - - if (WideName) - { - wchar *NameW=PointToName(CurMaskW); - if (*NameW==0) - strcatw(CurMaskW,MASKALLW); - if (NameW[0]=='.' && (NameW[1]==0 || NameW[1]=='.' && NameW[2]==0)) - { - AddEndSlash(CurMaskW); - strcatw(CurMaskW,MASKALLW); - } - SpecPathLengthW=NameW-CurMaskW; - } - else - { - wchar WideMask[NM]; - CharToWide(CurMask,WideMask); - SpecPathLengthW=PointToName(WideMask)-WideMask; - } Depth=0; - strcpy(OrigCurMask,CurMask); - strcpyw(OrigCurMaskW,CurMaskW); + wcsncpyz(OrigCurMask,CurMask,ASIZE(OrigCurMask)); - return(true); + return true; } -SCAN_CODE ScanTree::FindProc(FindData *FindData) +SCAN_CODE ScanTree::FindProc(FindData *FD) { if (*CurMask==0) - return(SCAN_NEXT); + return SCAN_NEXT; bool FastFindFile=false; if (FindStack[Depth]==NULL) // No FindFile object for this depth yet. { - bool Wildcards=IsWildcard(CurMask,CurMaskW); + bool Wildcards=IsWildcard(CurMask); // If we have a file name without wildcards, we can try to use // FastFind to optimize speed. For example, in Unix it results in // stat call instead of opendir/readdir/closedir. - bool FindCode=!Wildcards && FindFile::FastFind(CurMask,CurMaskW,FindData,GetLinks); + bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks); - bool IsDir=FindCode && FindData->IsDir; + // Link check is important for NTFS, where links can have "Directory" + // attribute, but we do not want to recurse to them in "get links" mode. + bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink); // SearchAll means that we'll use "*" mask for search, so we'll find // subdirectories and will be able to recurse into them. // We do not use "*" for directories at any level or for files - // at top level in recursion mode. + // at top level in recursion mode. We always comrpess the entire directory + // if folder wildcard is specified. bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS || + FolderWildcards && Recurse!=RECURSE_DISABLE || Wildcards && Recurse==RECURSE_WILDCARDS || ScanEntireDisk && Recurse!=RECURSE_DISABLE); if (Depth==0) @@ -141,19 +271,12 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) { // Create the new FindFile object for wildcard based search. FindStack[Depth]=new FindFile; - char SearchMask[NM]; - strcpy(SearchMask,CurMask); + + wchar SearchMask[NM]; + wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask)); if (SearchAll) - strcpy(PointToName(SearchMask),MASKALL); + SetName(SearchMask,MASKALL,ASIZE(SearchMask)); FindStack[Depth]->SetMask(SearchMask); - if (*CurMaskW) - { - wchar SearchMaskW[NM]; - strcpyw(SearchMaskW,CurMaskW); - if (SearchAll) - strcpyw(PointToName(SearchMaskW),MASKALLW); - FindStack[Depth]->SetMaskW(SearchMaskW); - } } else { @@ -161,7 +284,7 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) // a directory in RECURSE_DISABLE mode, so we do not need to scan it. // We can return here and do not need to process further. // We need to process further only if we fast found a directory. - if (!FindCode || !FindData->IsDir || Recurse==RECURSE_DISABLE) + if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE) { // Return SCAN_SUCCESS if we found a file. SCAN_CODE RetCode=SCAN_SUCCESS; @@ -170,14 +293,18 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) { // Return SCAN_ERROR if problem is more serious than just // "file not found". - RetCode=FindData->Error ? SCAN_ERROR:SCAN_NEXT; + RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT; // If we failed to find an object, but our current mask is excluded, // we skip this object and avoid indicating an error. - if (Cmd!=NULL && Cmd->ExclCheck(CurMask,true,true)) + if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true)) RetCode=SCAN_NEXT; else + { ErrHandler.OpenErrorMsg(ErrArcName,CurMask); + // User asked to return RARX_NOFILES and not RARX_OPEN here. + ErrHandler.SetErrorCode(RARX_NOFILES); + } } // If we searched only for one file or directory in "fast find" @@ -188,9 +315,8 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) // mode, directory recursing will quit by (Depth < 0) condition, // which returns SCAN_DONE to calling function. *CurMask=0; - *CurMaskW=0; - return(RetCode); + return RetCode; } // We found a directory using only FindFile::FastFind function. @@ -198,43 +324,17 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) } } - if (!FastFindFile && !FindStack[Depth]->Next(FindData,GetLinks)) + if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks)) { // We cannot find anything more in directory either because of // some error or just as result of all directory entries already read. - bool Error=FindData->Error; - -#ifdef _WIN_32 + bool Error=FD->Error; if (Error) - { - // Do not display an error if we cannot scan contents of reparse - // point. Vista contains a lot of reparse (or junction) points, - // which are not accessible. - if ((FindData->FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) - Error=false; + ScanError(Error); - // Do not display an error if we cannot scan contents of - // "System Volume Information" folder. Normally it is not accessible. - if (strstr(CurMask,"System Volume Information\\")!=NULL) - Error=false; - } -#endif - - if (Error && Cmd!=NULL && Cmd->ExclCheck(CurMask,true,true)) - Error=false; - -#ifndef SILENT - if (Error) - { - Log(NULL,St(MScanError),CurMask); - } -#endif - - char DirName[NM]; - wchar DirNameW[NM]; + wchar DirName[NM]; *DirName=0; - *DirNameW=0; // Going to at least one directory level higher. delete FindStack[Depth]; @@ -248,56 +348,38 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) if (Error) Errors++; - return(SCAN_DONE); + return SCAN_DONE; } - char *Slash=strrchrd(CurMask,CPATHDIVIDER); + + wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER); if (Slash!=NULL) { - char Mask[NM]; - strcpy(Mask,Slash); + wchar Mask[NM]; + wcsncpyz(Mask,Slash,ASIZE(Mask)); if (DepthIsDir) + FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir) { - FindData->Flags|=FDDF_SECONDDIR; - return(Error ? SCAN_ERROR:SCAN_SUCCESS); + FD->Flags|=FDDF_SECONDDIR; + return Error ? SCAN_ERROR:SCAN_SUCCESS; } - return(Error ? SCAN_ERROR:SCAN_NEXT); + return Error ? SCAN_ERROR:SCAN_NEXT; } - if (FindData->IsDir) + // Link check is required for NTFS links, not for Unix. + if (FD->IsDir && (!GetLinks || !FD->IsLink)) { // If we found the directory in top (Depth==0) directory // and if we are not in "fast find" (directory name only as argument) @@ -305,15 +387,12 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) // we do not recurse into this directory. We either return it by itself // or skip it. if (!FastFindFile && Depth==0 && !SearchAllInRoot) - return(GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT); + return GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT; // Let's check if directory name is excluded, so we do not waste // time searching in directory, which will be excluded anyway. - // We set CheckInclList parameter of ExclCheck to 'true' to ignore - // the inclusion list here. We do it to correctly handle the situation, - // when a user added files in the directory to inclusion list, - // but did not add their parent directory to this list. - if (Cmd!=NULL && Cmd->ExclCheck(FindData->Name,false,false)) + if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) || + Cmd->ExclDirByAttr(FD->FileAttr))) { // If we are here in "fast find" mode, it means that entire directory // specified in command line is excluded. Then we need to return @@ -321,42 +400,23 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) // in GetNext() function. Such loop would be possible in case of // SCAN_NEXT code and "rar a arc dir -xdir" command. - return(FastFindFile ? SCAN_DONE:SCAN_NEXT); + return FastFindFile ? SCAN_DONE:SCAN_NEXT; } - char Mask[NM]; + wchar Mask[NM]; - strcpy(Mask,FastFindFile ? MASKALL:PointToName(CurMask)); - strcpy(CurMask,FindData->Name); + wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask)); + wcsncpyz(CurMask,FD->Name,ASIZE(CurMask)); - if (strlen(CurMask)+strlen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1) + if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1) { -#ifndef SILENT - Log(NULL,"\n%s%c%s",CurMask,CPATHDIVIDER,Mask); - Log(NULL,St(MPathTooLong)); -#endif - return(SCAN_ERROR); + uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask); + return SCAN_ERROR; } - AddEndSlash(CurMask); - strcat(CurMask,Mask); + AddEndSlash(CurMask,ASIZE(CurMask)); + wcsncatz(CurMask,Mask,ASIZE(CurMask)); - if (*CurMaskW && *FindData->NameW==0) - CharToWide(FindData->Name,FindData->NameW); - if (*FindData->NameW!=0) - { - wchar Mask[NM]; - if (FastFindFile) - strcpyw(Mask,MASKALLW); - else - if (*CurMaskW) - strcpyw(Mask,PointToName(CurMaskW)); - else - CharToWide(PointToName(CurMask),Mask); - strcpyw(CurMaskW,FindData->NameW); - AddEndSlash(CurMaskW); - strcatw(CurMaskW,Mask); - } Depth++; // We need to use OrigCurMask for depths less than SetAllMaskDepth @@ -380,8 +440,55 @@ SCAN_CODE ScanTree::FindProc(FindData *FindData) if (FastFindFile) SetAllMaskDepth=Depth; } - if (!FastFindFile && !CmpName(CurMask,FindData->Name,MATCH_NAMES)) - return(SCAN_NEXT); + if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES)) + return SCAN_NEXT; - return(SCAN_SUCCESS); + return SCAN_SUCCESS; +} + + +void ScanTree::ScanError(bool &Error) +{ +#ifdef _WIN_ALL + if (Error) + { + // Get attributes of parent folder and do not display an error + // if it is reparse point. We cannot scan contents of standard + // Windows reparse points like "C:\Documents and Settings" + // and we do not want to issue numerous useless errors for them. + // We cannot just check FD->FileAttr here, it can be undefined + // if we process "folder\*" mask or if we process "folder" mask, + // but "folder" is inaccessible. + wchar *Slash=PointToName(CurMask); + if (Slash>CurMask) + { + *(Slash-1)=0; + DWORD Attr=GetFileAttributes(CurMask); + *(Slash-1)=CPATHDIVIDER; + if (Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) + Error=false; + } + + // Do not display an error if we cannot scan contents of + // "System Volume Information" folder. Normally it is not accessible. + if (wcsstr(CurMask,L"System Volume Information\\")!=NULL) + Error=false; + } +#endif + + if (Error && Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true)) + Error=false; + + if (Error) + { + if (ErrDirList!=NULL) + ErrDirList->AddString(CurMask); + if (ErrDirSpecPathLength!=NULL) + ErrDirSpecPathLength->Push((uint)SpecPathLength); + wchar FullName[NM]; + // This conversion works for wildcard masks too. + ConvertNameToFull(CurMask,FullName,ASIZE(FullName)); + uiMsg(UIERROR_DIRSCAN,FullName); + ErrHandler.SysErrMsg(); + } } diff --git a/libunrar/scantree.hpp b/libunrar/scantree.hpp index cf960bf..7ebe69a 100644 --- a/libunrar/scantree.hpp +++ b/libunrar/scantree.hpp @@ -18,8 +18,11 @@ class CommandData; class ScanTree { private: + bool ExpandFolderMask(); + bool GetFilteredMask(); bool GetNextMask(); - SCAN_CODE FindProc(FindData *FindData); + SCAN_CODE FindProc(FindData *FD); + void ScanError(bool &Error); FindFile *FindStack[MAXSCANDEPTH]; int Depth; @@ -32,29 +35,44 @@ class ScanTree SCAN_DIRS GetDirs; int Errors; - // set when processing paths like c:\ (root directory without wildcards) + // Set when processing paths like c:\ (root directory without wildcards). bool ScanEntireDisk; - char CurMask[NM]; - wchar CurMaskW[NM]; - char OrigCurMask[NM]; - wchar OrigCurMaskW[NM]; + wchar CurMask[NM]; + wchar OrigCurMask[NM]; + + // Store all folder masks generated from folder wildcard mask in non-recursive mode. + StringList ExpandedFolderList; + + // Store a filter string for folder wildcard in recursive mode. + StringList FilterList; + + // Save the list of unreadable dirs here. + StringList *ErrDirList; + Array *ErrDirSpecPathLength; + + // Set if processing a folder wildcard mask. + bool FolderWildcards; + bool SearchAllInRoot; size_t SpecPathLength; - size_t SpecPathLengthW; - char ErrArcName[NM]; + wchar ErrArcName[NM]; CommandData *Cmd; public: ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs); ~ScanTree(); SCAN_CODE GetNext(FindData *FindData); - size_t GetSpecPathLength() {return(SpecPathLength);}; - size_t GetSpecPathLengthW() {return(SpecPathLengthW);}; - int GetErrors() {return(Errors);}; - void SetErrArcName(const char *Name) {strcpy(ErrArcName,Name);} + size_t GetSpecPathLength() {return SpecPathLength;} + int GetErrors() {return Errors;}; + void SetErrArcName(const wchar *Name) {wcsncpyz(ErrArcName,Name,ASIZE(ErrArcName));} void SetCommandData(CommandData *Cmd) {ScanTree::Cmd=Cmd;} + void SetErrDirList(StringList *List,Array *Lengths) + { + ErrDirList=List; + ErrDirSpecPathLength=Lengths; + } }; #endif diff --git a/libunrar/secpassword.cpp b/libunrar/secpassword.cpp new file mode 100644 index 0000000..4865b3f --- /dev/null +++ b/libunrar/secpassword.cpp @@ -0,0 +1,216 @@ +#include "rar.hpp" + +#if defined(_WIN_ALL) +typedef BOOL (WINAPI *CRYPTPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags); +typedef BOOL (WINAPI *CRYPTUNPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags); + +#ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE +#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 +#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00 +#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x01 +#endif + +class CryptLoader +{ + private: + HMODULE hCrypt; + bool LoadCalled; + public: + CryptLoader() + { + hCrypt=NULL; + pCryptProtectMemory=NULL; + pCryptUnprotectMemory=NULL; + LoadCalled=false; + } + ~CryptLoader() + { + if (hCrypt!=NULL) + FreeLibrary(hCrypt); + hCrypt=NULL; + pCryptProtectMemory=NULL; + pCryptUnprotectMemory=NULL; + }; + void Load() + { + if (!LoadCalled) + { + hCrypt = LoadSysLibrary(L"Crypt32.dll"); + if (hCrypt != NULL) + { + // Available since Vista. + pCryptProtectMemory = (CRYPTPROTECTMEMORY)GetProcAddress(hCrypt, "CryptProtectMemory"); + pCryptUnprotectMemory = (CRYPTUNPROTECTMEMORY)GetProcAddress(hCrypt, "CryptUnprotectMemory"); + } + LoadCalled=true; + } + } + + CRYPTPROTECTMEMORY pCryptProtectMemory; + CRYPTUNPROTECTMEMORY pCryptUnprotectMemory; +}; + +// We need to call FreeLibrary when RAR is exiting. +CryptLoader GlobalCryptLoader; +#endif + +SecPassword::SecPassword() +{ + CrossProcess=false; + Set(L""); +} + + +SecPassword::~SecPassword() +{ + Clean(); +} + + +void SecPassword::Clean() +{ + PasswordSet=false; + cleandata(Password,sizeof(Password)); +} + + +// When we call memset in end of function to clean local variables +// for security reason, compiler optimizer can remove such call. +// So we use our own function for this purpose. +void cleandata(void *data,size_t size) +{ + if (data==NULL || size==0) + return; +#if defined(_WIN_ALL) && defined(_MSC_VER) + SecureZeroMemory(data,size); +#else + // 'volatile' is required. Otherwise optimizers can remove this function + // if cleaning local variables, which are not used after that. + volatile byte *d = (volatile byte *)data; + for (size_t i=0;i parameter, so we need to take into account both sizes. + memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst)); + SecHideData(Dst,DstSize*sizeof(*Dst),Encode,CrossProcess); +} + + +void SecPassword::Get(wchar *Psw,size_t MaxSize) +{ + if (PasswordSet) + { + Process(Password,ASIZE(Password),Psw,MaxSize,false); + Psw[MaxSize-1]=0; + } + else + *Psw=0; +} + + + + +void SecPassword::Set(const wchar *Psw) +{ + if (*Psw==0) + { + PasswordSet=false; + memset(Password,0,sizeof(Password)); + } + else + { + PasswordSet=true; + Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true); + } +} + + +size_t SecPassword::Length() +{ + wchar Plain[MAXPASSWORD]; + Get(Plain,ASIZE(Plain)); + size_t Length=wcslen(Plain); + cleandata(Plain,ASIZE(Plain)); + return Length; +} + + +bool SecPassword::operator == (SecPassword &psw) +{ + // We cannot compare encoded data directly, because there is no guarantee + // than encryption function will always produce the same result for same + // data (salt?) and because we do not clean the rest of password buffer + // after trailing zero before encoding password. So we decode first. + wchar Plain1[MAXPASSWORD],Plain2[MAXPASSWORD]; + Get(Plain1,ASIZE(Plain1)); + psw.Get(Plain2,ASIZE(Plain2)); + bool Result=wcscmp(Plain1,Plain2)==0; + cleandata(Plain1,ASIZE(Plain1)); + cleandata(Plain2,ASIZE(Plain2)); + return Result; +} + + +void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess) +{ + // CryptProtectMemory is not available in UWP and CryptProtectData + // increases data size not allowing in place conversion. +#if defined(_WIN_ALL) + // Try to utilize the secure Crypt[Un]ProtectMemory if possible. + if (GlobalCryptLoader.pCryptProtectMemory==NULL) + GlobalCryptLoader.Load(); + size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE; + DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS; + if (Encode) + { + if (GlobalCryptLoader.pCryptProtectMemory!=NULL) + { + if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags)) + { + ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed"); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return; + } + } + else + { + if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL) + { + if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags)) + { + ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed"); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return; + } + } +#endif + + // CryptProtectMemory is not available, so only slightly obfuscate data. + uint Key; +#ifdef _WIN_ALL + Key=GetCurrentProcessId(); +#elif defined(_UNIX) + Key=getpid(); +#else + Key=0; // Just an arbitrary value. +#endif + + for (size_t I=0;I 100% Public Domain - -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ -#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) - #if defined(_M_IX86) || defined(_M_I86) || defined(__alpha) - #define LITTLE_ENDIAN - #else - #error "LITTLE_ENDIAN or BIG_ENDIAN must be defined" - #endif +#ifndef SFX_MODULE +#define SHA1_UNROLL #endif -/* #define SHA1HANDSOFF * Copies data before messing with it. */ - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #ifdef LITTLE_ENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ - |(rol(block->l[i],8)&0x00FF00FF)) +#define blk0(i) (block->l[i] = ByteSwap32(block->l[i])) #else #define blk0(i) block->l[i] #endif -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ +#define blk(i) (block->l[i&15] = rotl32(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);} -#define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);} -#define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);} -#define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);} -#define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);} - +#define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);} +#define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);} +#define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rotl32(v,5);w=rotl32(w,30);} +#define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rotl32(v,5);w=rotl32(w,30);} +#define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rotl32(v,5);w=rotl32(w,30);} /* Hash a single 512-bit block. This is the core of the algorithm. */ - -void SHA1Transform(uint32 state[5], unsigned char buffer[64], bool handsoff) +void SHA1Transform(uint32 state[5], uint32 workspace[16], const byte buffer[64], bool inplace) { -#ifndef SFX_MODULE uint32 a, b, c, d, e; -#endif - typedef union { + + union CHAR64LONG16 + { unsigned char c[64]; uint32 l[16]; -} CHAR64LONG16; -CHAR64LONG16* block; -static unsigned char workspace[64]; - if (handsoff) - { - block = (CHAR64LONG16*)workspace; - memcpy(block, buffer, 64); - } - else - block = (CHAR64LONG16*)buffer; -#ifdef SFX_MODULE - static int pos[80][5]; - static bool pinit=false; - if (!pinit) - { - for (int I=0,P=0;I<80;I++,P=(P ? P-1:4)) - { - pos[I][0]=P; - pos[I][1]=(P+1)%5; - pos[I][2]=(P+2)%5; - pos[I][3]=(P+3)%5; - pos[I][4]=(P+4)%5; - } - pinit=true; - } - uint32 s[5]; - for (int I=0;Istate[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + +#ifdef SHA1_UNROLL + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); #else - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - - /* Wipe variables */ - a = b = c = d = e = 0; - memset(&a,0,sizeof(a)); + for (uint I=0;;I+=5) + { + R0(a,b,c,d,e, I+0); if (I==15) break; + R0(e,a,b,c,d, I+1); R0(d,e,a,b,c, I+2); + R0(c,d,e,a,b, I+3); R0(b,c,d,e,a, I+4); + } + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + for (uint I=20;I<=35;I+=5) + { + R2(a,b,c,d,e,I+0); R2(e,a,b,c,d,I+1); R2(d,e,a,b,c,I+2); + R2(c,d,e,a,b,I+3); R2(b,c,d,e,a,I+4); + } + for (uint I=40;I<=55;I+=5) + { + R3(a,b,c,d,e,I+0); R3(e,a,b,c,d,I+1); R3(d,e,a,b,c,I+2); + R3(c,d,e,a,b,I+3); R3(b,c,d,e,a,I+4); + } + for (uint I=60;I<=75;I+=5) + { + R4(a,b,c,d,e,I+0); R4(e,a,b,c,d,I+1); R4(d,e,a,b,c,I+2); + R4(c,d,e,a,b,I+3); R4(b,c,d,e,a,I+4); + } #endif + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; } /* Initialize new context */ - -void hash_initial(hash_context* context) +void sha1_init(sha1_context* context) { - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; + context->count = 0; + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; } /* Run your data through this. */ -void hash_process( hash_context * context, unsigned char * data, size_t len, - bool handsoff ) +void sha1_process( sha1_context * context, const unsigned char * data, size_t len) { -unsigned int i, j; -uint blen = ((uint)len)<<3; + size_t i, j = (size_t)(context->count & 63); + context->count += len; - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += blen) < blen ) context->count[1]++; - context->count[1] += (uint32)(len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer, handsoff); - for ( ; i + 63 < len; i += 64) { -#ifdef ALLOW_NOT_ALIGNED_INT - SHA1Transform(context->state, &data[i], handsoff); -#else - unsigned char buffer[64]; - memcpy(buffer,data+i,sizeof(buffer)); - SHA1Transform(context->state, buffer, handsoff); - memcpy(data+i,buffer,sizeof(buffer)); -#endif -#ifdef BIG_ENDIAN - if (!handsoff) - { - unsigned char *d=data+i; - for (int k=0;k<64;k+=4) - { - byte b0=d[k],b1=d[k+1]; - d[k]=d[k+3]; - d[k+1]=d[k+2]; - d[k+2]=b1; - d[k+3]=b0; - } - } -#endif - } - j = 0; + if ((j + len) > 63) + { + memcpy(context->buffer+j, data, (i = 64-j)); + uint32 workspace[16]; + SHA1Transform(context->state, workspace, context->buffer, true); + for ( ; i + 63 < len; i += 64) + SHA1Transform(context->state, workspace, data+i, false); + j = 0; + } + else + i = 0; + if (len > i) + memcpy(context->buffer+j, data+i, len - i); +} + + +void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len) +{ + size_t i, j = (size_t)(context->count & 63); + context->count += len; + + if ((j + len) > 63) + { + memcpy(context->buffer+j, data, (i = 64-j)); + uint32 workspace[16]; + SHA1Transform(context->state, workspace, context->buffer, true); + for ( ; i + 63 < len; i += 64) + { + SHA1Transform(context->state, workspace, data+i, false); + for (uint k = 0; k < 16; k++) + RawPut4(workspace[k],(void*)(data+i+k*4)); } - else i = 0; - if (len > i) - memcpy(&context->buffer[j], &data[i], len - i); + j = 0; + } + else + i = 0; + if (len > i) + memcpy(context->buffer+j, data+i, len - i); } /* Add padding and return the message digest. */ - -void hash_final( hash_context* context, uint32 digest[5], bool handsoff) +void sha1_done( sha1_context* context, uint32 digest[5]) { -uint i, j; -unsigned char finalcount[8]; + uint32 workspace[16]; + uint64 BitLength = context->count * 8; + uint BufPos = (uint)context->count & 0x3f; + context->buffer[BufPos++] = 0x80; // Padding the message with "1" bit. - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + if (BufPos!=56) // We need 56 bytes block followed by 8 byte length. + { + if (BufPos>56) + { + while (BufPos<64) + context->buffer[BufPos++] = 0; + BufPos=0; } - unsigned char ch=(unsigned char)'\200'; - hash_process(context, &ch, 1, handsoff); - while ((context->count[0] & 504) != 448) { - ch=0; - hash_process(context, &ch, 1, handsoff); - } - hash_process(context, finalcount, 8, handsoff); /* Should cause a SHA1Transform() */ - for (i = 0; i < 5; i++) { - digest[i] = context->state[i] & 0xffffffff; - } - /* Wipe variables */ - memset(&i,0,sizeof(i)); - memset(&j,0,sizeof(j)); - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(&finalcount, 0, 8); - if (handsoff) - SHA1Transform(context->state, context->buffer, true); + if (BufPos==0) + SHA1Transform(context->state, workspace, context->buffer, true); + memset(context->buffer+BufPos,0,56-BufPos); + } + + RawPutBE4((uint32)(BitLength>>32), context->buffer + 56); + RawPutBE4((uint32)(BitLength), context->buffer + 60); + + SHA1Transform(context->state, workspace, context->buffer, true); + + for (uint i = 0; i < 5; i++) + digest[i] = context->state[i]; + + /* Wipe variables */ + sha1_init(context); } diff --git a/libunrar/sha1.hpp b/libunrar/sha1.hpp index 27e15e7..7c0b7fb 100644 --- a/libunrar/sha1.hpp +++ b/libunrar/sha1.hpp @@ -1,17 +1,15 @@ #ifndef _RAR_SHA1_ #define _RAR_SHA1_ -#define HW 5 - typedef struct { uint32 state[5]; - uint32 count[2]; + uint64 count; unsigned char buffer[64]; -} hash_context; +} sha1_context; -void hash_initial( hash_context * c ); -void hash_process( hash_context * c, unsigned char * data, size_t len, - bool handsoff); -void hash_final( hash_context * c, uint32[HW], bool handsoff); +void sha1_init( sha1_context * c ); +void sha1_process(sha1_context * c, const byte *data, size_t len); +void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len); +void sha1_done( sha1_context * c, uint32 digest[5] ); #endif diff --git a/libunrar/sha256.cpp b/libunrar/sha256.cpp new file mode 100644 index 0000000..f90d2c0 --- /dev/null +++ b/libunrar/sha256.cpp @@ -0,0 +1,148 @@ +#include "rar.hpp" +#include "sha256.hpp" + +static const uint32 K[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +// SHA-256 functions. We could optimize Ch and Maj a little, +// but with no visible speed benefit. +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +// Sigma functions. +#define Sg0(x) (rotr32(x, 2) ^ rotr32(x,13) ^ rotr32(x, 22)) +#define Sg1(x) (rotr32(x, 6) ^ rotr32(x,11) ^ rotr32(x, 25)) +#define sg0(x) (rotr32(x, 7) ^ rotr32(x,18) ^ (x >> 3)) +#define sg1(x) (rotr32(x,17) ^ rotr32(x,19) ^ (x >> 10)) + +void sha256_init(sha256_context *ctx) +{ + ctx->H[0] = 0x6a09e667; // Set the initial hash value. + ctx->H[1] = 0xbb67ae85; + ctx->H[2] = 0x3c6ef372; + ctx->H[3] = 0xa54ff53a; + ctx->H[4] = 0x510e527f; + ctx->H[5] = 0x9b05688c; + ctx->H[6] = 0x1f83d9ab; + ctx->H[7] = 0x5be0cd19; + ctx->Count = 0; // Processed data counter. +} + + +static void sha256_transform(sha256_context *ctx) +{ + uint32 W[64]; // Words of message schedule. + uint32 v[8]; // FIPS a, b, c, d, e, f, g, h working variables. + + // Prepare message schedule. + for (uint I = 0; I < 16; I++) + W[I] = RawGetBE4(ctx->Buffer + I * 4); + for (uint I = 16; I < 64; I++) + W[I] = sg1(W[I-2]) + W[I-7] + sg0(W[I-15]) + W[I-16]; + + uint32 *H=ctx->H; + v[0]=H[0]; v[1]=H[1]; v[2]=H[2]; v[3]=H[3]; + v[4]=H[4]; v[5]=H[5]; v[6]=H[6]; v[7]=H[7]; + + for (uint I = 0; I < 64; I++) + { + uint T1 = v[7] + Sg1(v[4]) + Ch(v[4], v[5], v[6]) + K[I] + W[I]; + + // It is possible to eliminate variable copying if we unroll loop + // and rename variables every time. But my test did not show any speed + // gain on i7 for such full or partial unrolling. + v[7] = v[6]; + v[6] = v[5]; + v[5] = v[4]; + v[4] = v[3] + T1; + + // It works a little faster when moved here from beginning of loop. + uint T2 = Sg0(v[0]) + Maj(v[0], v[1], v[2]); + + v[3] = v[2]; + v[2] = v[1]; + v[1] = v[0]; + v[0] = T1 + T2; + } + + H[0]+=v[0]; H[1]+=v[1]; H[2]+=v[2]; H[3]+=v[3]; + H[4]+=v[4]; H[5]+=v[5]; H[6]+=v[6]; H[7]+=v[7]; +} + + +void sha256_process(sha256_context *ctx, const void *Data, size_t Size) +{ + const byte *Src=(const byte *)Data; + size_t BufPos = (uint)ctx->Count & 0x3f; + ctx->Count+=Size; + while (Size > 0) + { + size_t BufSpace=sizeof(ctx->Buffer)-BufPos; + size_t CopySize=Size>BufSpace ? BufSpace:Size; + + memcpy(ctx->Buffer+BufPos,Src,CopySize); + + Src+=CopySize; + BufPos+=CopySize; + Size-=CopySize; + if (BufPos == 64) + { + BufPos = 0; + sha256_transform(ctx); + } + } +} + + +void sha256_done(sha256_context *ctx, byte *Digest) +{ + uint64 BitLength = ctx->Count * 8; + uint BufPos = (uint)ctx->Count & 0x3f; + ctx->Buffer[BufPos++] = 0x80; // Padding the message with "1" bit. + + if (BufPos!=56) // We need 56 bytes block followed by 8 byte length. + { + if (BufPos>56) + { + while (BufPos<64) + ctx->Buffer[BufPos++] = 0; + BufPos=0; + } + if (BufPos==0) + sha256_transform(ctx); + memset(ctx->Buffer+BufPos,0,56-BufPos); + } + + RawPutBE4((uint32)(BitLength>>32), ctx->Buffer + 56); + RawPutBE4((uint32)(BitLength), ctx->Buffer + 60); + + sha256_transform(ctx); + + RawPutBE4(ctx->H[0], Digest + 0); + RawPutBE4(ctx->H[1], Digest + 4); + RawPutBE4(ctx->H[2], Digest + 8); + RawPutBE4(ctx->H[3], Digest + 12); + RawPutBE4(ctx->H[4], Digest + 16); + RawPutBE4(ctx->H[5], Digest + 20); + RawPutBE4(ctx->H[6], Digest + 24); + RawPutBE4(ctx->H[7], Digest + 28); + + sha256_init(ctx); +} diff --git a/libunrar/sha256.hpp b/libunrar/sha256.hpp new file mode 100644 index 0000000..b6837e7 --- /dev/null +++ b/libunrar/sha256.hpp @@ -0,0 +1,17 @@ +#ifndef _RAR_SHA256_ +#define _RAR_SHA256_ + +#define SHA256_DIGEST_SIZE 32 + +typedef struct +{ + uint32 H[8]; + uint64 Count; + byte Buffer[64]; +} sha256_context; + +void sha256_init(sha256_context *ctx); +void sha256_process(sha256_context *ctx, const void *Data, size_t Size); +void sha256_done(sha256_context *ctx, byte *Digest); + +#endif diff --git a/libunrar/smallfn.cpp b/libunrar/smallfn.cpp index f9beda4..81259d0 100644 --- a/libunrar/smallfn.cpp +++ b/libunrar/smallfn.cpp @@ -3,39 +3,17 @@ int ToPercent(int64 N1,int64 N2) { if (N2SrcLength) + DestSize=SrcLength; + OemToCharBuffA(Src,Dest,(DWORD)DestSize); + Dest[DestSize-1]=0; #else if (Dest!=Src) - strcpy(Dest,Src); + strncpyz(Dest,Src,DestSize); #endif } -void IntToExt(const char *Src,char *Dest) +// Convert archived names and comments to Unicode. +// Allows user to select a code page in GUI. +void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding) { -#if defined(_WIN_32) - OemToChar(Src,Dest); +#if defined(_WIN_ALL) // Console Windows RAR. + if (Encoding==ACTW_UTF8) + UtfToWide(Src,Dest,DestSize); + else + { + Array NameA; + if (Encoding==ACTW_OEM) + { + NameA.Alloc(DestSize+1); + IntToExt(Src,&NameA[0],NameA.Size()); + Src=&NameA[0]; + } + CharToWide(Src,Dest,DestSize); + } +#else // RAR for Unix. + if (Encoding==ACTW_UTF8) + UtfToWide(Src,Dest,DestSize); + else + CharToWide(Src,Dest,DestSize); +#endif + // Ensure that we return a zero terminate string for security reason. + // While [Jni]CharToWide might already do it, be protected in case of future + // changes in these functions. + if (DestSize>0) + Dest[DestSize-1]=0; +} + + + + +int stricomp(const char *s1,const char *s2) +{ +#ifdef _WIN_ALL + return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; #else - if (Dest!=Src) - strcpy(Dest,Src); + while (toupper(*s1)==toupper(*s2)) + { + if (*s1==0) + return 0; + s1++; + s2++; + } + return s1 < s2 ? -1 : 1; #endif } -char* strlower(char *Str) +int strnicomp(const char *s1,const char *s2,size_t n) { -#ifdef _WIN_32 - CharLower((LPTSTR)Str); +#ifdef _WIN_ALL + // If we specify 'n' exceeding the actual string length, CompareString goes + // beyond the trailing zero and compares garbage. So we need to limit 'n' + // to real string length. + // It is important to use strnlen (or memchr(...,0)) instead of strlen, + // because data can be not zero terminated. + size_t l1=Min(strnlen(s1,n),n); + size_t l2=Min(strnlen(s2,n),n); + return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; #else - for (char *ChPtr=Str;*ChPtr;ChPtr++) - *ChPtr=(char)loctolower(*ChPtr); + if (n==0) + return 0; + while (toupper(*s1)==toupper(*s2)) + { + if (*s1==0 || --n==0) + return 0; + s1++; + s2++; + } + return s1 < s2 ? -1 : 1; #endif - return(Str); } -char* strupper(char *Str) +wchar* RemoveEOL(wchar *Str) { -#ifdef _WIN_32 - CharUpper((LPTSTR)Str); -#else - for (char *ChPtr=Str;*ChPtr;ChPtr++) - *ChPtr=(char)loctoupper(*ChPtr); -#endif - return(Str); -} - - -int stricomp(const char *Str1,const char *Str2) -{ - char S1[NM*2],S2[NM*2]; - strncpyz(S1,Str1,ASIZE(S1)); - strncpyz(S2,Str2,ASIZE(S2)); - return(strcmp(strupper(S1),strupper(S2))); -} - - -int strnicomp(const char *Str1,const char *Str2,size_t N) -{ - char S1[NM*2],S2[NM*2]; - strncpyz(S1,Str1,ASIZE(S1)); - strncpyz(S2,Str2,ASIZE(S2)); - return(strncmp(strupper(S1),strupper(S2),N)); -} - - -char* RemoveEOL(char *Str) -{ - for (int I=(int)strlen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--) + for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--) Str[I]=0; - return(Str); + return Str; } -char* RemoveLF(char *Str) +wchar* RemoveLF(wchar *Str) { - for (int I=(int)strlen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) + for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) Str[I]=0; - return(Str); + return Str; } unsigned char loctolower(unsigned char ch) { -#ifdef _WIN_32 +#if defined(_WIN_ALL) // Convert to LPARAM first to avoid a warning in 64 bit mode. - return((int)(LPARAM)CharLower((LPTSTR)ch)); + // Convert to uintptr_t to avoid Clang/win error: cast to 'char *' from smaller integer type 'unsigned char' [-Werror,-Wint-to-pointer-cast] + return (int)(LPARAM)CharLowerA((LPSTR)(uintptr_t)ch); #else - return(tolower(ch)); + return tolower(ch); #endif } unsigned char loctoupper(unsigned char ch) { -#ifdef _WIN_32 +#if defined(_WIN_ALL) // Convert to LPARAM first to avoid a warning in 64 bit mode. - return((int)(LPARAM)CharUpper((LPTSTR)ch)); + // Convert to uintptr_t to avoid Clang/win error: cast to 'char *' from smaller integer type 'unsigned char' [-Werror,-Wint-to-pointer-cast] + return (int)(LPARAM)CharUpperA((LPSTR)(uintptr_t)ch); #else - return(toupper(ch)); + return toupper(ch); #endif } @@ -129,8 +152,8 @@ unsigned char loctoupper(unsigned char ch) unsigned char etoupper(unsigned char ch) { if (ch=='i') - return('I'); - return(toupper(ch)); + return 'I'; + return toupper(ch); } @@ -138,123 +161,306 @@ unsigned char etoupper(unsigned char ch) wchar etoupperw(wchar ch) { if (ch=='i') - return('I'); - return(toupperw(ch)); + return 'I'; + return toupperw(ch); } // We do not want to cast every signed char to unsigned when passing to // isdigit, so we implement the replacement. Shall work for Unicode too. +// If chars are signed, conversion from char to int could generate negative +// values, resulting in undefined behavior in standard isdigit. bool IsDigit(int ch) { - return(ch>='0' && ch<='9'); + return ch>='0' && ch<='9'; } // We do not want to cast every signed char to unsigned when passing to // isspace, so we implement the replacement. Shall work for Unicode too. +// If chars are signed, conversion from char to int could generate negative +// values, resulting in undefined behavior in standard isspace. bool IsSpace(int ch) { - return(ch==' ' || ch=='\t'); + return ch==' ' || ch=='\t'; +} + + +// We do not want to cast every signed char to unsigned when passing to +// isalpha, so we implement the replacement. Shall work for Unicode too. +// If chars are signed, conversion from char to int could generate negative +// values, resulting in undefined behavior in standard function. +bool IsAlpha(int ch) +{ + return ch>='A' && ch<='Z' || ch>='a' && ch<='z'; } +void BinToHex(const byte *Bin,size_t BinSize,char *HexA,wchar *HexW,size_t HexSize) +{ + uint A=0,W=0; // ASCII and Unicode hex output positions. + for (uint I=0;I> 4; + uint Low=Bin[I] & 0xf; + uint HighHex=High>9 ? 'a'+High-10:'0'+High; + uint LowHex=Low>9 ? 'a'+Low-10:'0'+Low; + if (HexA!=NULL && A0) + HexA[A]=0; + if (HexW!=NULL && HexSize>0) + HexW[W]=0; +} + + +#ifndef SFX_MODULE +uint GetDigits(uint Number) +{ + uint Digits=1; + while (Number>=10) + { + Number/=10; + Digits++; + } + return Digits; +} +#endif + bool LowAscii(const char *Str) { - for (int I=0;Str[I]!=0;I++) - if ((byte)Str[I]<32 || (byte)Str[I]>127) - return(false); - return(true); + for (size_t I=0;Str[I]!=0;I++) + if (/*(byte)Str[I]<32 || */(byte)Str[I]>127) + return false; + return true; } bool LowAscii(const wchar *Str) { - for (int I=0;Str[I]!=0;I++) + for (size_t I=0;Str[I]!=0;I++) { // We convert wchar_t to uint just in case if some compiler // uses signed wchar_t. - if ((uint)Str[I]<32 || (uint)Str[I]>127) - return(false); + if (/*(uint)Str[I]<32 || */(uint)Str[I]>127) + return false; } - return(true); + return true; } - - -int stricompc(const char *Str1,const char *Str2) +int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison. { #if defined(_UNIX) - return(strcmp(Str1,Str2)); + return wcscmp(s1,s2); #else - return(stricomp(Str1,Str2)); + return wcsicomp(s1,s2); #endif } -#ifndef SFX_MODULE -int stricompcw(const wchar *Str1,const wchar *Str2) +int wcsnicompc(const wchar *s1,const wchar *s2,size_t n) { #if defined(_UNIX) - return(strcmpw(Str1,Str2)); + return wcsncmp(s1,s2,n); #else - return(stricmpw(Str1,Str2)); + return wcsnicomp(s1,s2,n); #endif } -#endif -// safe strncpy: copies maxlen-1 max and always returns zero terminated dest -char* strncpyz(char *dest, const char *src, size_t maxlen) +// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. +void strncpyz(char *dest, const char *src, size_t maxlen) { if (maxlen>0) { - strncpy(dest,src,maxlen-1); - dest[maxlen-1]=0; + while (--maxlen>0 && *src!=0) + *dest++=*src++; + *dest=0; } - return(dest); } -// safe strncpyw: copies maxlen-1 max and always returns zero terminated dest -wchar* strncpyzw(wchar *dest, const wchar *src, size_t maxlen) + +// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. +void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen) { if (maxlen>0) { - strncpyw(dest,src,maxlen-1); - dest[maxlen-1]=0; + while (--maxlen>0 && *src!=0) + *dest++=*src++; + *dest=0; } - return(dest); } -void itoa(int64 n,char *Str) +// Safe append: resulting dest length cannot exceed maxlen and dest +// is always zero terminated. 'maxlen' parameter defines the entire +// dest buffer size and is not compatible with wcsncat. +void strncatz(char* dest, const char* src, size_t maxlen) +{ + size_t length = strlen(dest); + if (maxlen > length) + strncpyz(dest + length, src, maxlen - length); +} + + +// Safe append: resulting dest length cannot exceed maxlen and dest +// is always zero terminated. 'maxlen' parameter defines the entire +// dest buffer size and is not compatible with wcsncat. +void wcsncatz(wchar* dest, const wchar* src, size_t maxlen) +{ + size_t length = wcslen(dest); + if (maxlen > length) + wcsncpyz(dest + length, src, maxlen - length); +} + + +void itoa(int64 n,char *Str,size_t MaxSize) { char NumStr[50]; size_t Pos=0; + int Neg=n < 0 ? 1 : 0; + if (Neg) + n=-n; + do { + if (Pos+1>=MaxSize-Neg) + break; NumStr[Pos++]=char(n%10)+'0'; n=n/10; } while (n!=0); + if (Neg) + NumStr[Pos++]='-'; + for (size_t I=0;I='0' && *Str<='9') + wchar NumStr[50]; + size_t Pos=0; + + int Neg=n < 0 ? 1 : 0; + if (Neg) + n=-n; + + do { - n=n*10+*Str-'0'; - Str++; - } - return(n); + if (Pos+1>=MaxSize-Neg) + break; + NumStr[Pos++]=wchar(n%10)+'0'; + n=n/10; + } while (n!=0); + + if (Neg) + NumStr[Pos++]='-'; + + for (size_t I=0;I= ASIZE(StrTable)) + StrNum=0; + wchar *Str=StrTable[StrNum]; + CharToWide(Src,Str,MaxLength); + Str[MaxLength-1]=0; + return Str; +} + + +// Parse string containing parameters separated with spaces. +// Support quote marks. Param can be NULL to return the pointer to next +// parameter, which can be used to estimate the buffer size for Param. +const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize) +{ + while (IsSpace(*CmdLine)) + CmdLine++; + if (*CmdLine==0) + return NULL; + + size_t ParamSize=0; + bool Quote=false; + while (*CmdLine!=0 && (Quote || !IsSpace(*CmdLine))) + { + if (*CmdLine=='\"') + { + if (CmdLine[1]=='\"') + { + // Insert the quote character instead of two adjoining quote characters. + if (Param!=NULL && ParamSize StrW(strlen(Str)); + CharToWide(Str,&StrW[0],StrW.Size()); + AddString(&StrW[0]); } -size_t StringList::AddString(const char *Str,const wchar *StrW) +void StringList::AddString(const wchar *Str) { + if (Str==NULL) + Str=L""; + size_t PrevSize=StringData.Size(); - StringData.Add(strlen(Str)+1); - strcpy(&StringData[PrevSize],Str); - if (StrW!=NULL && *StrW!=0) - { - size_t PrevPos=PosDataW.Size(); - PosDataW.Add(1); - PosDataW[PrevPos]=PrevSize; + StringData.Add(wcslen(Str)+1); + wcscpy(&StringData[PrevSize],Str); - size_t PrevSizeW=StringDataW.Size(); - StringDataW.Add(strlenw(StrW)+1); - strcpyw(&StringDataW[PrevSizeW],StrW); - } StringsCount++; - return(PrevSize); } -bool StringList::GetString(char *Str,size_t MaxLength) +bool StringList::GetStringA(char *Str,size_t MaxLength) { - return(GetString(Str,NULL,MaxLength)); + Array StrW(MaxLength); + if (!GetString(&StrW[0],StrW.Size())) + return false; + WideToChar(&StrW[0],Str,MaxLength); + return true; } -bool StringList::GetString(char *Str,wchar *StrW,size_t MaxLength) +bool StringList::GetString(wchar *Str,size_t MaxLength) { - char *StrPtr; - wchar *StrPtrW; - if (Str==NULL || !GetString(&StrPtr,&StrPtrW)) - return(false); - strncpy(Str,StrPtr,MaxLength); - if (StrW!=NULL) - strncpyw(StrW,NullToEmpty(StrPtrW),MaxLength); - return(true); + wchar *StrPtr; + if (!GetString(&StrPtr)) + return false; + wcsncpyz(Str,StrPtr,MaxLength); + return true; } #ifndef SFX_MODULE -bool StringList::GetString(char *Str,wchar *StrW,size_t MaxLength,int StringNum) +bool StringList::GetString(wchar *Str,size_t MaxLength,int StringNum) { SavePosition(); Rewind(); bool RetCode=true; while (StringNum-- >=0) - if (!GetString(Str,StrW,MaxLength)) + if (!GetString(Str,MaxLength)) { RetCode=false; break; } RestorePosition(); - return(RetCode); + return RetCode; } #endif -char* StringList::GetString() +wchar* StringList::GetString() { - char *Str; - GetString(&Str,NULL); - return(Str); + wchar *Str; + GetString(&Str); + return Str; } - -bool StringList::GetString(char **Str,wchar **StrW) +bool StringList::GetString(wchar **Str) { - if (CurPos>=StringData.Size()) + if (CurPos>=StringData.Size()) // No more strings left unprocessed. { - *Str=NULL; - return(false); + if (Str!=NULL) + *Str=NULL; + return false; } - *Str=&StringData[CurPos]; - if (PosDataItem=StringData.Size()) - return(NULL); - return(&StringData[StringPos]); + return true; } void StringList::Rewind() { CurPos=0; - CurPosW=0; - PosDataItem=0; -} - - -size_t StringList::GetBufferSize() -{ - return(StringData.Size()+StringDataW.Size()); } #ifndef SFX_MODULE -bool StringList::Search(char *Str,wchar *StrW,bool CaseSensitive) +bool StringList::Search(const wchar *Str,bool CaseSensitive) { SavePosition(); Rewind(); bool Found=false; - char *CurStr; - wchar *CurStrW; - while (GetString(&CurStr,&CurStrW)) + wchar *CurStr; + while (GetString(&CurStr)) { - if ((CaseSensitive ? strcmp(Str,CurStr):stricomp(Str,CurStr))!=0) - continue; - if (StrW!=NULL && CurStrW!=NULL) - if ((CaseSensitive ? strcmpw(StrW,CurStrW):stricmpw(StrW,CurStrW))!=0) + if (Str!=NULL && CurStr!=NULL) + if ((CaseSensitive ? wcscmp(Str,CurStr):wcsicomp(Str,CurStr))!=0) continue; Found=true; break; } RestorePosition(); - return(Found); + return Found; } #endif @@ -169,8 +133,6 @@ void StringList::SavePosition() if (SavePosNumber StringData; + Array StringData; size_t CurPos; - Array StringDataW; - size_t CurPosW; + size_t StringsCount; - Array PosDataW; - size_t PosDataItem; - - uint StringsCount; - - size_t SaveCurPos[16],SaveCurPosW[16],SavePosDataItem[16],SavePosNumber; + size_t SaveCurPos[16],SavePosNumber; public: StringList(); - ~StringList(); void Reset(); - size_t AddString(const char *Str); - size_t AddString(const char *Str,const wchar *StrW); - bool GetString(char *Str,size_t MaxLength); - bool GetString(char *Str,wchar *StrW,size_t MaxLength); - bool GetString(char *Str,wchar *StrW,size_t MaxLength,int StringNum); - char* GetString(); - bool GetString(char **Str,wchar **StrW); - char* GetString(uint StringPos); + void AddStringA(const char *Str); + void AddString(const wchar *Str); + bool GetStringA(char *Str,size_t MaxLength); + bool GetString(wchar *Str,size_t MaxLength); + bool GetString(wchar *Str,size_t MaxLength,int StringNum); + wchar* GetString(); + bool GetString(wchar **Str); void Rewind(); - uint ItemsCount() {return(StringsCount);}; - size_t GetBufferSize(); - bool Search(char *Str,wchar *StrW,bool CaseSensitive); + size_t ItemsCount() {return StringsCount;}; + size_t GetCharCount() {return StringData.Size();} + bool Search(const wchar *Str,bool CaseSensitive); void SavePosition(); void RestorePosition(); }; diff --git a/libunrar/suballoc.cpp b/libunrar/suballoc.cpp index b455658..07d3285 100644 --- a/libunrar/suballoc.cpp +++ b/libunrar/suballoc.cpp @@ -5,6 +5,9 @@ * Contents: memory allocation routines * ****************************************************************************/ +static const uint UNIT_SIZE=Max(sizeof(RARPPM_CONTEXT),sizeof(RARPPM_MEM_BLK)); +static const uint FIXED_UNIT_SIZE=12; + SubAllocator::SubAllocator() { Clean(); @@ -41,11 +44,11 @@ inline uint SubAllocator::U2B(int NU) -// Calculate RAR_MEM_BLK+Items address. Real RAR_MEM_BLK size must be -// equal to UNIT_SIZE, so we cannot just add Items to RAR_MEM_BLK address. -inline RAR_MEM_BLK* SubAllocator::MBPtr(RAR_MEM_BLK *BasePtr,int Items) +// Calculate RARPPM_MEM_BLK+Items address. Real RARPPM_MEM_BLK size must be +// equal to UNIT_SIZE, so we cannot just add Items to RARPPM_MEM_BLK address. +inline RARPPM_MEM_BLK* SubAllocator::MBPtr(RARPPM_MEM_BLK *BasePtr,int Items) { - return((RAR_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) )); + return((RARPPM_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) )); } @@ -63,14 +66,12 @@ inline void SubAllocator::SplitBlock(void* pv,int OldIndx,int NewIndx) } - - void SubAllocator::StopSubAllocator() { if ( SubAllocatorSize ) { SubAllocatorSize=0; - rarfree(HeapStart); + free(HeapStart); } } @@ -79,7 +80,7 @@ bool SubAllocator::StartSubAllocator(int SASize) { uint t=SASize << 20; if (SubAllocatorSize == t) - return TRUE; + return true; StopSubAllocator(); // Original algorithm expects FIXED_UNIT_SIZE, but actual structure size @@ -87,10 +88,10 @@ bool SubAllocator::StartSubAllocator(int SASize) // units: one as reserve for HeapEnd overflow checks and another // to provide the space to correctly align UnitsStart. uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+2*UNIT_SIZE; - if ((HeapStart=(byte *)rarmalloc(AllocSize)) == NULL) + if ((HeapStart=(byte *)malloc(AllocSize)) == NULL) { ErrHandler.MemoryError(); - return FALSE; + return false; } // HeapEnd did not present in original algorithm. We added it to control @@ -98,7 +99,7 @@ bool SubAllocator::StartSubAllocator(int SASize) HeapEnd=HeapStart+AllocSize-UNIT_SIZE; SubAllocatorSize=t; - return TRUE; + return true; } @@ -109,7 +110,7 @@ void SubAllocator::InitSubAllocator() pText=HeapStart; // Original algorithm operates with 12 byte FIXED_UNIT_SIZE, but actual - // size of RAR_MEM_BLK and PPM_CONTEXT structures can exceed this value + // size of RARPPM_MEM_BLK and RARPPM_CONTEXT structures can exceed this value // because of alignment and larger pointer fields size. // So we define UNIT_SIZE for this larger size and adjust memory // pointers accordingly. @@ -166,14 +167,14 @@ void SubAllocator::InitSubAllocator() inline void SubAllocator::GlueFreeBlocks() { - RAR_MEM_BLK s0, * p, * p1; + RARPPM_MEM_BLK s0, * p, * p1; int i, k, sz; if (LoUnit != HiUnit) *LoUnit=0; for (i=0, s0.next=s0.prev=&s0;i < N_INDEXES;i++) while ( FreeList[i].next ) { - p=(RAR_MEM_BLK*)RemoveNode(i); + p=(RARPPM_MEM_BLK*)RemoveNode(i); p->insertAt(&s0); p->Stamp=0xFFFF; p->NU=Indx2Units[i]; @@ -214,13 +215,13 @@ void* SubAllocator::AllocUnitsRare(int indx) GlueCount--; i=U2B(Indx2Units[indx]); int j=FIXED_UNIT_SIZE*Indx2Units[indx]; - if (FakeUnitsStart-pText > j) + if (FakeUnitsStart - pText > j) { - FakeUnitsStart-=j; + FakeUnitsStart -= j; UnitsStart -= i; - return(UnitsStart); + return UnitsStart; } - return(NULL); + return NULL; } } while ( !FreeList[i].next ); void* RetVal=RemoveNode(i); diff --git a/libunrar/suballoc.hpp b/libunrar/suballoc.hpp index 474ee59..2a1d132 100644 --- a/libunrar/suballoc.hpp +++ b/libunrar/suballoc.hpp @@ -7,24 +7,21 @@ #if !defined(_SUBALLOC_H_) #define _SUBALLOC_H_ -const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4; -const int N_INDEXES=N1+N2+N3+N4; - -#if defined(__GNUC__) && !defined(STRICT_ALIGNMENT_REQUIRED) -#define _PACK_ATTR __attribute__ ((packed)) +#if defined(__GNUC__) && defined(ALLOW_MISALIGNED) +#define RARPPM_PACK_ATTR __attribute__ ((packed)) #else -#define _PACK_ATTR +#define RARPPM_PACK_ATTR #endif /* defined(__GNUC__) */ -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #pragma pack(1) #endif -struct RAR_MEM_BLK +struct RARPPM_MEM_BLK { ushort Stamp, NU; - RAR_MEM_BLK* next, * prev; - void insertAt(RAR_MEM_BLK* p) + RARPPM_MEM_BLK* next, * prev; + void insertAt(RARPPM_MEM_BLK* p) { next=(prev=p)->next; p->next=next->prev=this; @@ -34,9 +31,9 @@ struct RAR_MEM_BLK prev->next=next; next->prev=prev; } -} _PACK_ATTR; +} RARPPM_PACK_ATTR; -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #ifdef _AIX #pragma pack(pop) #else @@ -45,22 +42,24 @@ struct RAR_MEM_BLK #endif -struct RAR_NODE -{ - RAR_NODE* next; -}; - class SubAllocator { private: + static const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4; + static const int N_INDEXES=N1+N2+N3+N4; + + struct RAR_NODE + { + RAR_NODE* next; + }; + inline void InsertNode(void* p,int indx); inline void* RemoveNode(int indx); inline uint U2B(int NU); inline void SplitBlock(void* pv,int OldIndx,int NewIndx); - uint GetUsedMemory(); inline void GlueFreeBlocks(); void* AllocUnitsRare(int indx); - inline RAR_MEM_BLK* MBPtr(RAR_MEM_BLK *BasePtr,int Items); + inline RARPPM_MEM_BLK* MBPtr(RARPPM_MEM_BLK *BasePtr,int Items); long SubAllocatorSize; byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; @@ -78,7 +77,7 @@ class SubAllocator inline void* ExpandUnits(void* ptr,int OldNU); inline void* ShrinkUnits(void* ptr,int OldNU,int NewNU); inline void FreeUnits(void* ptr,int OldNU); - long GetAllocatedMemory() {return(SubAllocatorSize);}; + long GetAllocatedMemory() {return(SubAllocatorSize);} byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; }; diff --git a/libunrar/system.cpp b/libunrar/system.cpp index 59087cd..4ae2b89 100644 --- a/libunrar/system.cpp +++ b/libunrar/system.cpp @@ -1,19 +1,17 @@ #include "rar.hpp" -#ifndef _WIN_CE static int SleepTime=0; void InitSystemOptions(int SleepTime) { ::SleepTime=SleepTime; } -#endif -#if !defined(SFX_MODULE) && !defined(_WIN_CE) +#if !defined(SFX_MODULE) void SetPriority(int Priority) { -#ifdef _WIN_32 +#ifdef _WIN_ALL uint PriorityClass; int PriorityLevel; if (Priority<1 || Priority>15) @@ -23,6 +21,10 @@ void SetPriority(int Priority) { PriorityClass=IDLE_PRIORITY_CLASS; PriorityLevel=THREAD_PRIORITY_IDLE; + +// Background mode for Vista, can be slow for many small files. +// if (WinNT()>=WNT_VISTA) +// SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN); } else if (Priority<7) @@ -56,29 +58,51 @@ void SetPriority(int Priority) SetPriorityClass(GetCurrentProcess(),PriorityClass); SetThreadPriority(GetCurrentThread(),PriorityLevel); -// Background mode for Vista, too slow for real life use. -// if (WinNT()>=6 && Priority==1) -// SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN); +#ifdef RAR_SMP + ThreadPool::SetPriority(PriorityLevel); +#endif #endif } #endif +// Monotonic clock. Like clock(), returns time passed in CLOCKS_PER_SEC items. +// In Android 5+ and Unix usual clock() returns time spent by all threads +// together, so we cannot use it to measure time intervals anymore. +clock_t MonoClock() +{ + return clock(); +} + + + void Wait() { -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) + if (ErrHandler.UserBreak) + ErrHandler.Exit(RARX_USERBREAK); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) if (SleepTime!=0) - Sleep(SleepTime); + { + static clock_t LastTime=MonoClock(); + if (MonoClock()-LastTime>10*CLOCKS_PER_SEC/1000) + { + Sleep(SleepTime); + LastTime=MonoClock(); + } + } +#endif +#if defined(_WIN_ALL) + // Reset system sleep timer to prevent system going sleep. + SetThreadExecutionState(ES_SYSTEM_REQUIRED); #endif } -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) && !defined(SHELL_EXT) - -void Shutdown() +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void Shutdown(POWER_MODE Mode) { HANDLE hToken; TOKEN_PRIVILEGES tkp; @@ -90,6 +114,102 @@ void Shutdown() AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0); } - ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE|EWX_POWEROFF,SHTDN_REASON_FLAG_PLANNED); + if (Mode==POWERMODE_OFF) + ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); + if (Mode==POWERMODE_SLEEP) + SetSuspendState(FALSE,FALSE,FALSE); + if (Mode==POWERMODE_HIBERNATE) + SetSuspendState(TRUE,FALSE,FALSE); + if (Mode==POWERMODE_RESTART) + ExitWindowsEx(EWX_REBOOT|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); +} + + +bool ShutdownCheckAnother(bool Open) +{ + const wchar *EventName=L"rar -ioff"; + static HANDLE hEvent=NULL; + bool Result=false; // Return false if no other RAR -ioff are running. + if (Open) // Create or open the event. + hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); + else + { + if (hEvent!=NULL) + CloseHandle(hEvent); // Close our event. + // Check if other copies still own the event. While race conditions + // are possible, they are improbable and their harm is minimal. + hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); + Result=GetLastError()==ERROR_ALREADY_EXISTS; + if (hEvent!=NULL) + CloseHandle(hEvent); + } + return Result; +} +#endif + + + + +#if defined(_WIN_ALL) +// Load library from Windows System32 folder. Use this function to prevent +// loading a malicious code from current folder or same folder as exe. +HMODULE WINAPI LoadSysLibrary(const wchar *Name) +{ + wchar SysDir[NM]; + if (GetSystemDirectory(SysDir,ASIZE(SysDir))==0) + return NULL; + MakeName(SysDir,Name,SysDir,ASIZE(SysDir)); + return LoadLibrary(SysDir); +} + + +bool IsUserAdmin() +{ + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup; + BOOL b = AllocateAndInitializeSid(&NtAuthority,2,SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (b) + { + if (!CheckTokenMembership( NULL, AdministratorsGroup, &b)) + b = FALSE; + FreeSid(AdministratorsGroup); + } + return b!=FALSE; +} + +#endif + + +#ifdef USE_SSE +SSE_VERSION _SSE_Version=GetSSEVersion(); + +SSE_VERSION GetSSEVersion() +{ + int CPUInfo[4]; + __cpuid(CPUInfo, 0x80000000); + + // Maximum supported cpuid function. For example, Pentium M 755 returns 4 here. + uint MaxSupported=CPUInfo[0] & 0x7fffffff; + + if (MaxSupported>=7) + { + __cpuid(CPUInfo, 7); + if ((CPUInfo[1] & 0x20)!=0) + return SSE_AVX2; + } + if (MaxSupported>=1) + { + __cpuid(CPUInfo, 1); + if ((CPUInfo[2] & 0x80000)!=0) + return SSE_SSE41; + if ((CPUInfo[2] & 0x200)!=0) + return SSE_SSSE3; + if ((CPUInfo[3] & 0x4000000)!=0) + return SSE_SSE2; + if ((CPUInfo[3] & 0x2000000)!=0) + return SSE_SSE; + } + return SSE_NONE; } #endif diff --git a/libunrar/system.hpp b/libunrar/system.hpp index d98e5a2..a56d6b7 100644 --- a/libunrar/system.hpp +++ b/libunrar/system.hpp @@ -1,7 +1,7 @@ #ifndef _RAR_SYSTEM_ #define _RAR_SYSTEM_ -#ifdef _WIN_32 +#ifdef _WIN_ALL #ifndef BELOW_NORMAL_PRIORITY_CLASS #define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 #define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 @@ -19,8 +19,22 @@ void InitSystemOptions(int SleepTime); void SetPriority(int Priority); +clock_t MonoClock(); void Wait(); -bool EmailFile(char *FileName,char *MailTo); -void Shutdown(); +bool EmailFile(const wchar *FileName,const wchar *MailToW); +void Shutdown(POWER_MODE Mode); +bool ShutdownCheckAnother(bool Open); + +#ifdef _WIN_ALL +HMODULE WINAPI LoadSysLibrary(const wchar *Name); +bool IsUserAdmin(); +#endif + + +#ifdef USE_SSE +enum SSE_VERSION {SSE_NONE,SSE_SSE,SSE_SSE2,SSE_SSSE3,SSE_SSE41,SSE_AVX2}; +SSE_VERSION GetSSEVersion(); +extern SSE_VERSION _SSE_Version; +#endif #endif diff --git a/libunrar/threadmisc.cpp b/libunrar/threadmisc.cpp new file mode 100644 index 0000000..742eda4 --- /dev/null +++ b/libunrar/threadmisc.cpp @@ -0,0 +1,151 @@ +static inline bool CriticalSectionCreate(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + InitializeCriticalSection(CritSection); + return true; +#elif defined(_UNIX) + return pthread_mutex_init(CritSection,NULL)==0; +#endif +} + + +static inline void CriticalSectionDelete(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + DeleteCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_destroy(CritSection); +#endif +} + + +static inline void CriticalSectionStart(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + EnterCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_lock(CritSection); +#endif +} + + +static inline void CriticalSectionEnd(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + LeaveCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_unlock(CritSection); +#endif +} + + +static THREAD_HANDLE ThreadCreate(NATIVE_THREAD_PTR Proc,void *Data) +{ +#ifdef _UNIX +/* + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); +*/ + pthread_t pt; + int Code=pthread_create(&pt,NULL/*&attr*/,Proc,Data); + if (Code!=0) + { + wchar Msg[100]; + swprintf(Msg,ASIZE(Msg),L"\npthread_create failed, code %d\n",Code); + ErrHandler.GeneralErrMsg(Msg); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return pt; +#else + DWORD ThreadId; + HANDLE hThread=CreateThread(NULL,0x10000,Proc,Data,0,&ThreadId); + if (hThread==NULL) + { + ErrHandler.GeneralErrMsg(L"CreateThread failed"); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return hThread; +#endif +} + + +static void ThreadClose(THREAD_HANDLE hThread) +{ +#ifdef _UNIX + pthread_join(hThread,NULL); +#else + CloseHandle(hThread); +#endif +} + + +#ifdef _WIN_ALL +static void CWaitForSingleObject(HANDLE hHandle) +{ + DWORD rc=WaitForSingleObject(hHandle,INFINITE); + if (rc==WAIT_FAILED) + { + ErrHandler.GeneralErrMsg(L"\nWaitForMultipleObjects error %d, GetLastError %d",rc,GetLastError()); + ErrHandler.Exit(RARX_FATAL); + } +} +#endif + + +#ifdef _UNIX +static void cpthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + int rc=pthread_cond_wait(cond,mutex); + if (rc!=0) + { + ErrHandler.GeneralErrMsg(L"\npthread_cond_wait error %d",rc); + ErrHandler.Exit(RARX_FATAL); + } +} +#endif + + +uint GetNumberOfCPU() +{ +#ifndef RAR_SMP + return 1; +#else +#ifdef _UNIX +#ifdef _SC_NPROCESSORS_ONLN + uint Count=(uint)sysconf(_SC_NPROCESSORS_ONLN); + return Count<1 ? 1:Count; +#elif defined(_APPLE) + uint Count; + size_t Size=sizeof(Count); + return sysctlbyname("hw.ncpu",&Count,&Size,NULL,0)==0 ? Count:1; +#endif +#else // !_UNIX + DWORD_PTR ProcessMask; + DWORD_PTR SystemMask; + + if (!GetProcessAffinityMask(GetCurrentProcess(),&ProcessMask,&SystemMask)) + return 1; + uint Count=0; + for (DWORD_PTR Mask=1;Mask!=0;Mask<<=1) + if ((ProcessMask & Mask)!=0) + Count++; + return Count<1 ? 1:Count; +#endif + +#endif // RAR_SMP +} + + +uint GetNumberOfThreads() +{ + uint NumCPU=GetNumberOfCPU(); + if (NumCPU<1) + return 1; + if (NumCPU>MaxPoolThreads) + return MaxPoolThreads; + return NumCPU; +} + diff --git a/libunrar/threadpool.cpp b/libunrar/threadpool.cpp new file mode 100644 index 0000000..8c63a8b --- /dev/null +++ b/libunrar/threadpool.cpp @@ -0,0 +1,212 @@ +#include "rar.hpp" + +#ifdef RAR_SMP +#include "threadmisc.cpp" + +#ifdef _WIN_ALL +int ThreadPool::ThreadPriority=THREAD_PRIORITY_NORMAL; +#endif + +ThreadPool::ThreadPool(uint MaxThreads) +{ + MaxAllowedThreads = MaxThreads; + if (MaxAllowedThreads>MaxPoolThreads) + MaxAllowedThreads=MaxPoolThreads; + if (MaxAllowedThreads==0) + MaxAllowedThreads=1; + + ThreadsCreatedCount=0; + + // If we have more threads than queue size, we'll hang on pool destroying, + // not releasing all waiting threads. + if (MaxAllowedThreads>ASIZE(TaskQueue)) + MaxAllowedThreads=ASIZE(TaskQueue); + + Closing=false; + + bool Success = CriticalSectionCreate(&CritSection); +#ifdef _WIN_ALL + QueuedTasksCnt=CreateSemaphore(NULL,0,ASIZE(TaskQueue),NULL); + NoneActive=CreateEvent(NULL,TRUE,TRUE,NULL); + Success=Success && QueuedTasksCnt!=NULL && NoneActive!=NULL; +#elif defined(_UNIX) + AnyActive = false; + QueuedTasksCnt = 0; + Success=Success && pthread_cond_init(&AnyActiveCond,NULL)==0 && + pthread_mutex_init(&AnyActiveMutex,NULL)==0 && + pthread_cond_init(&QueuedTasksCntCond,NULL)==0 && + pthread_mutex_init(&QueuedTasksCntMutex,NULL)==0; +#endif + if (!Success) + { + ErrHandler.GeneralErrMsg(L"\nThread pool initialization failed."); + ErrHandler.Exit(RARX_FATAL); + } + + QueueTop = 0; + QueueBottom = 0; + ActiveThreads = 0; +} + + +ThreadPool::~ThreadPool() +{ + WaitDone(); + Closing=true; + +#ifdef _WIN_ALL + ReleaseSemaphore(QueuedTasksCnt,ASIZE(TaskQueue),NULL); +#elif defined(_UNIX) + // Threads still can access QueuedTasksCnt for a short time after WaitDone(), + // so lock is required. We would occassionally hang without it. + pthread_mutex_lock(&QueuedTasksCntMutex); + QueuedTasksCnt+=ASIZE(TaskQueue); + pthread_mutex_unlock(&QueuedTasksCntMutex); + + pthread_cond_broadcast(&QueuedTasksCntCond); +#endif + + for(uint I=0;IPoolThreadLoop(); + return 0; +} + + +void ThreadPool::PoolThreadLoop() +{ + QueueEntry Task; + while (GetQueuedTask(&Task)) + { + Task.Proc(Task.Param); + + CriticalSectionStart(&CritSection); + if (--ActiveThreads == 0) + { +#ifdef _WIN_ALL + SetEvent(NoneActive); +#elif defined(_UNIX) + pthread_mutex_lock(&AnyActiveMutex); + AnyActive=false; + pthread_cond_signal(&AnyActiveCond); + pthread_mutex_unlock(&AnyActiveMutex); +#endif + } + CriticalSectionEnd(&CritSection); + } +} + + +bool ThreadPool::GetQueuedTask(QueueEntry *Task) +{ +#ifdef _WIN_ALL + CWaitForSingleObject(QueuedTasksCnt); +#elif defined(_UNIX) + pthread_mutex_lock(&QueuedTasksCntMutex); + while (QueuedTasksCnt==0) + cpthread_cond_wait(&QueuedTasksCntCond,&QueuedTasksCntMutex); + QueuedTasksCnt--; + pthread_mutex_unlock(&QueuedTasksCntMutex); +#endif + + if (Closing) + return false; + + CriticalSectionStart(&CritSection); + + *Task = TaskQueue[QueueBottom]; + QueueBottom = (QueueBottom + 1) % ASIZE(TaskQueue); + + CriticalSectionEnd(&CritSection); + + return true; +} + + +// Add task to queue. We assume that it is always called from main thread, +// it allows to avoid any locks here. We process collected tasks only +// when WaitDone is called. +void ThreadPool::AddTask(PTHREAD_PROC Proc,void *Data) +{ + if (ThreadsCreatedCount == 0) + CreateThreads(); + + // If queue is full, wait until it is empty. + if (ActiveThreads>=ASIZE(TaskQueue)) + WaitDone(); + + TaskQueue[QueueTop].Proc = Proc; + TaskQueue[QueueTop].Param = Data; + QueueTop = (QueueTop + 1) % ASIZE(TaskQueue); + ActiveThreads++; +} + + +// Start queued tasks and wait until all threads are inactive. +// We assume that it is always called from main thread, when pool threads +// are sleeping yet. +void ThreadPool::WaitDone() +{ + if (ActiveThreads==0) + return; +#ifdef _WIN_ALL + ResetEvent(NoneActive); + ReleaseSemaphore(QueuedTasksCnt,ActiveThreads,NULL); + CWaitForSingleObject(NoneActive); +#elif defined(_UNIX) + AnyActive=true; + + // Threads reset AnyActive before accessing QueuedTasksCnt and even + // preceding WaitDone() call does not guarantee that some slow thread + // is not accessing QueuedTasksCnt now. So lock is necessary. + pthread_mutex_lock(&QueuedTasksCntMutex); + QueuedTasksCnt+=ActiveThreads; + pthread_mutex_unlock(&QueuedTasksCntMutex); + + pthread_cond_broadcast(&QueuedTasksCntCond); + + pthread_mutex_lock(&AnyActiveMutex); + while (AnyActive) + cpthread_cond_wait(&AnyActiveCond,&AnyActiveMutex); + pthread_mutex_unlock(&AnyActiveMutex); +#endif +} +#endif // RAR_SMP diff --git a/libunrar/threadpool.hpp b/libunrar/threadpool.hpp new file mode 100644 index 0000000..85ed90d --- /dev/null +++ b/libunrar/threadpool.hpp @@ -0,0 +1,107 @@ +#ifndef _RAR_THREADPOOL_ +#define _RAR_THREADPOOL_ + +#ifndef RAR_SMP +const uint MaxPoolThreads=1; // For single threaded version. +#else +// We need to use the processor groups API to increase it beyond 64. +// Also be sure to check and adjust if needed per thread and total block size +// when compressing if going above 64. +const uint MaxPoolThreads=64; + + +#ifdef _UNIX + #include + #include +#endif + +// Undefine for debugging. +#define USE_THREADS + +#ifdef _UNIX + #define NATIVE_THREAD_TYPE void* + typedef void* (*NATIVE_THREAD_PTR)(void *Data); + typedef pthread_t THREAD_HANDLE; + typedef pthread_mutex_t CRITSECT_HANDLE; +#else + #define NATIVE_THREAD_TYPE DWORD WINAPI + typedef DWORD (WINAPI *NATIVE_THREAD_PTR)(void *Data); + typedef HANDLE THREAD_HANDLE; + typedef CRITICAL_SECTION CRITSECT_HANDLE; +#endif + +typedef void (*PTHREAD_PROC)(void *Data); +#define THREAD_PROC(fn) void fn(void *Data) + +uint GetNumberOfCPU(); +uint GetNumberOfThreads(); + + +class ThreadPool +{ + private: + struct QueueEntry + { + PTHREAD_PROC Proc; + void *Param; + }; + + void CreateThreads(); + static NATIVE_THREAD_TYPE PoolThread(void *Param); + void PoolThreadLoop(); + bool GetQueuedTask(QueueEntry *Task); + + // Number of threads in the pool. Must not exceed MaxPoolThreads. + uint MaxAllowedThreads; + THREAD_HANDLE ThreadHandles[MaxPoolThreads]; + + // Number of actually created threads. + uint ThreadsCreatedCount; + + uint ActiveThreads; + + QueueEntry TaskQueue[MaxPoolThreads]; + uint QueueTop; + uint QueueBottom; + + bool Closing; // Set true to quit all threads. + +#ifdef _WIN_ALL + // Semaphore counting number of tasks stored in queue. + HANDLE QueuedTasksCnt; + + // Event signalling if no active tasks are performing now. + HANDLE NoneActive; + +#elif defined(_UNIX) + // Semaphores seem to be slower than conditional variables in pthreads, + // so we use the conditional variable to count tasks stored in queue. + uint QueuedTasksCnt; + pthread_cond_t QueuedTasksCntCond; + pthread_mutex_t QueuedTasksCntMutex; + + bool AnyActive; // Active tasks present flag. + pthread_cond_t AnyActiveCond; + pthread_mutex_t AnyActiveMutex; +#endif + + // Pool critical section. We use the single section for all branches + // to avoid deadlocks, when thread1 has section1 and wants section2 + // and thread2 has section2 and wants section1. + CRITSECT_HANDLE CritSection; + public: + ThreadPool(uint MaxThreads); + ~ThreadPool(); + void AddTask(PTHREAD_PROC Proc,void *Data); + void WaitDone(); + +#ifdef _WIN_ALL + static int ThreadPriority; + static void SetPriority(int Priority) {ThreadPriority=Priority;} +#endif +}; + +#endif // RAR_SMP + +#endif // _RAR_THREADPOOL_ + diff --git a/libunrar/timefn.cpp b/libunrar/timefn.cpp index f245921..e86d41a 100644 --- a/libunrar/timefn.cpp +++ b/libunrar/timefn.cpp @@ -1,267 +1,277 @@ #include "rar.hpp" -RarTime::RarTime() +void RarTime::GetLocal(RarLocalTime *lt) { - Reset(); -} +#ifdef _WIN_ALL + FILETIME ft; + GetWinFT(&ft); + FILETIME lft; + + if (WinNT() < WNT_VISTA) + { + // SystemTimeToTzSpecificLocalTime based code produces 1 hour error on XP. + FileTimeToLocalFileTime(&ft,&lft); + } + else + { + // We use these functions instead of FileTimeToLocalFileTime according to + // MSDN recommendation: "To account for daylight saving time + // when converting a file time to a local time ..." + SYSTEMTIME st1,st2; + FileTimeToSystemTime(&ft,&st1); + SystemTimeToTzSpecificLocalTime(NULL,&st1,&st2); + SystemTimeToFileTime(&st2,&lft); + + // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. + FILETIME rft; + SystemTimeToFileTime(&st1,&rft); + uint64 Corrected=INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime)- + INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ + INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime); + lft.dwLowDateTime=(DWORD)Corrected; + lft.dwHighDateTime=(DWORD)(Corrected>>32); + } -#ifdef _WIN_32 -RarTime& RarTime::operator =(FILETIME &ft) -{ - FILETIME lft,zft; - FileTimeToLocalFileTime(&ft,&lft); SYSTEMTIME st; FileTimeToSystemTime(&lft,&st); - rlt.Year=st.wYear; - rlt.Month=st.wMonth; - rlt.Day=st.wDay; - rlt.Hour=st.wHour; - rlt.Minute=st.wMinute; - rlt.Second=st.wSecond; - rlt.wDay=st.wDayOfWeek; - rlt.yDay=rlt.Day-1; - for (uint I=1;I2 && IsLeapYear(rlt.Year)) - rlt.yDay++; + lt->Year=st.wYear; + lt->Month=st.wMonth; + lt->Day=st.wDay; + lt->Hour=st.wHour; + lt->Minute=st.wMinute; + lt->Second=st.wSecond; + lt->wDay=st.wDayOfWeek; + lt->yDay=lt->Day-1; - st.wMilliseconds=0; - SystemTimeToFileTime(&st,&zft); - rlt.Reminder=lft.dwLowDateTime-zft.dwLowDateTime; - return(*this); + static int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31}; + for (uint I=1;IMonth && I<=ASIZE(mdays);I++) + lt->yDay+=mdays[I-1]; + + if (lt->Month>2 && IsLeapYear(lt->Year)) + lt->yDay++; +#else + time_t ut=GetUnix(); + struct tm *t; + t=localtime(&ut); + + lt->Year=t->tm_year+1900; + lt->Month=t->tm_mon+1; + lt->Day=t->tm_mday; + lt->Hour=t->tm_hour; + lt->Minute=t->tm_min; + lt->Second=t->tm_sec; + lt->wDay=t->tm_wday; + lt->yDay=t->tm_yday; +#endif + lt->Reminder=(itime % TICKS_PER_SECOND); } -void RarTime::GetWin32(FILETIME *ft) +void RarTime::SetLocal(RarLocalTime *lt) { +#ifdef _WIN_ALL SYSTEMTIME st; - st.wYear=rlt.Year; - st.wMonth=rlt.Month; - st.wDay=rlt.Day; - st.wHour=rlt.Hour; - st.wMinute=rlt.Minute; - st.wSecond=rlt.Second; + st.wYear=lt->Year; + st.wMonth=lt->Month; + st.wDay=lt->Day; + st.wHour=lt->Hour; + st.wMinute=lt->Minute; + st.wSecond=lt->Second; st.wMilliseconds=0; + st.wDayOfWeek=0; FILETIME lft; - SystemTimeToFileTime(&st,&lft); - lft.dwLowDateTime+=rlt.Reminder; - if (lft.dwLowDateTimeReminder to lft. + TzSpecificLocalTimeToSystemTime(NULL,&st2,&st1); + SystemTimeToFileTime(&st1,&ft); + + // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. + FILETIME rft; + SystemTimeToFileTime(&st2,&rft); + uint64 Corrected=INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime)- + INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ + INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime); + ft.dwLowDateTime=(DWORD)Corrected; + ft.dwHighDateTime=(DWORD)(Corrected>>32); + } + + SetWinFT(&ft); + } + else + Reset(); +#else + struct tm t; + + t.tm_sec=lt->Second; + t.tm_min=lt->Minute; + t.tm_hour=lt->Hour; + t.tm_mday=lt->Day; + t.tm_mon=lt->Month-1; + t.tm_year=lt->Year-1900; + t.tm_isdst=-1; + SetUnix(mktime(&t)); +#endif + itime+=lt->Reminder; +} + + + + +#ifdef _WIN_ALL +void RarTime::GetWinFT(FILETIME *ft) +{ + _ULARGE_INTEGER ul; + ul.QuadPart=GetWin(); + ft->dwLowDateTime=ul.LowPart; + ft->dwHighDateTime=ul.HighPart; +} + + +void RarTime::SetWinFT(FILETIME *ft) +{ + _ULARGE_INTEGER ul = {ft->dwLowDateTime, ft->dwHighDateTime}; + SetWin(ul.QuadPart); } #endif -#if defined(_UNIX) || defined(_EMX) -RarTime& RarTime::operator =(time_t ut) +// Get 64-bit representation of Windows FILETIME (100ns since 01.01.1601). +uint64 RarTime::GetWin() { - struct tm *t; - t=localtime(&ut); + return itime/(TICKS_PER_SECOND/10000000); +} - rlt.Year=t->tm_year+1900; - rlt.Month=t->tm_mon+1; - rlt.Day=t->tm_mday; - rlt.Hour=t->tm_hour; - rlt.Minute=t->tm_min; - rlt.Second=t->tm_sec; - rlt.Reminder=0; - rlt.wDay=t->tm_wday; - rlt.yDay=t->tm_yday; - return(*this); + +// Set 64-bit representation of Windows FILETIME (100ns since 01.01.1601). +void RarTime::SetWin(uint64 WinTime) +{ + itime=WinTime*(TICKS_PER_SECOND/10000000); } time_t RarTime::GetUnix() { - struct tm t; - - t.tm_sec=rlt.Second; - t.tm_min=rlt.Minute; - t.tm_hour=rlt.Hour; - t.tm_mday=rlt.Day; - t.tm_mon=rlt.Month-1; - t.tm_year=rlt.Year-1900; - t.tm_isdst=-1; - return(mktime(&t)); -} -#endif - -// Return the stored time as 64-bit number of 100-nanosecond intervals -// since January 1, 1601 for Windows and since January 1, 1970 for Unix. -// Actually we do not care since which date this time starts from -// as long as this date is the same for GetRaw and SetRaw. We use the value -// returned by GetRaw() for time comparisons and for relative operations -// like SetRaw(GetRaw()-C). -int64 RarTime::GetRaw() -{ - if (!IsSet()) - return(0); -#ifdef _WIN_32 - FILETIME ft; - GetWin32(&ft); - return(INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime)); -#elif defined(_UNIX) || defined(_EMX) - time_t ut=GetUnix(); - return(INT32TO64(0,ut)*10000000+rlt.Reminder); -#else - // We should never be here. It is better to use standard time functions. - - // Days since 1970. We do not care about leap years for code simplicity. - // It should be acceptable for comprisons. - int64 r=(rlt.Year-1970)*365; // Days since 1970. - - // Cumulative day value for beginning of every month. - static int MonthToDay[12]={0,31,60,91,121,152,182,213,244,274,305,335}; - - r+=MonthToDay[rlt.Month-1]+(rlt.Day-1); // Add days since beginning of year. - r=r*24+rlt.Hour; // Hours. - r=r*60+rlt.Minute; // Minutes. - r=r*60+rlt.Second; // Seconds. - r=r*10000000+rlt.Reminder; // 100-nanosecond intervals. - - return(r); -#endif + return time_t(GetUnixNS()/1000000000); } -#ifndef SFX_MODULE -void RarTime::SetRaw(int64 RawTime) +void RarTime::SetUnix(time_t ut) { -#ifdef _WIN_32 - FILETIME ft; - ft.dwHighDateTime=(DWORD)(RawTime>>32); - ft.dwLowDateTime=(DWORD)RawTime; - *this=ft; -#elif defined(_UNIX) || defined(_EMX) - time_t ut=(time_t)(RawTime/10000000); - *this=ut; - rlt.Reminder=(uint)(RawTime%10000000); -#else - // We should never be here. It is better to use standard time functions. - rlt.Reminder=RawTime%10000000; - RawTime/=10000000; // Seconds. - rlt.Second=uint(RawTime%60); - RawTime/=60; // Minutes. - rlt.Minute=uint(RawTime%60); - RawTime/=60; // Hours. - rlt.Hour=uint(RawTime%24); - RawTime/=24; // Days since 1970. - rlt.Year=uint(1970+RawTime/365); - RawTime%=365; // Days since beginning of year. - - // Cumulative day value for beginning of every month. - static int MonthToDay[12]={0,31,60,91,121,152,182,213,244,274,305,335}; - - for (int I=0;I<12;I++) - if (RawTime>=MonthToDay[I]) - { - rlt.Day=uint(RawTime-MonthToDay[I]+1); - rlt.Month=I+1; - } - - rlt.wDay=0; - rlt.yDay=0; -#endif -} -#endif - - -bool RarTime::operator == (RarTime &rt) -{ - return(rlt.Year==rt.rlt.Year && rlt.Month==rt.rlt.Month && - rlt.Day==rt.rlt.Day && rlt.Hour==rt.rlt.Hour && - rlt.Minute==rt.rlt.Minute && rlt.Second==rt.rlt.Second && - rlt.Reminder==rt.rlt.Reminder); + if (sizeof(ut)>4) + SetUnixNS(uint64(ut)*1000000000); + else + { + // Convert 32-bit and possibly signed time_t to uint32 first, + // uint64 cast is not enough. Otherwise sign can expand to 64 bits. + SetUnixNS(uint64(uint32(ut))*1000000000); + } } -bool RarTime::operator < (RarTime &rt) +// Get the high precision Unix time in nanoseconds since 01-01-1970. +uint64 RarTime::GetUnixNS() { - return(GetRaw() (RarTime &rt) -{ - return(GetRaw()>rt.GetRaw()); -} - - -bool RarTime::operator >= (RarTime &rt) -{ - return(*this>rt || *this==rt); + // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. + uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); + itime=(ns+ushift)/(1000000000/TICKS_PER_SECOND); } uint RarTime::GetDos() { - uint DosTime=(rlt.Second/2)|(rlt.Minute<<5)|(rlt.Hour<<11)| - (rlt.Day<<16)|(rlt.Month<<21)|((rlt.Year-1980)<<25); - return(DosTime); + RarLocalTime lt; + GetLocal(<); + uint DosTime=(lt.Second/2)|(lt.Minute<<5)|(lt.Hour<<11)| + (lt.Day<<16)|(lt.Month<<21)|((lt.Year-1980)<<25); + return DosTime; } void RarTime::SetDos(uint DosTime) { - rlt.Second=(DosTime & 0x1f)*2; - rlt.Minute=(DosTime>>5) & 0x3f; - rlt.Hour=(DosTime>>11) & 0x1f; - rlt.Day=(DosTime>>16) & 0x1f; - rlt.Month=(DosTime>>21) & 0x0f; - rlt.Year=(DosTime>>25)+1980; - rlt.Reminder=0; + RarLocalTime lt; + lt.Second=(DosTime & 0x1f)*2; + lt.Minute=(DosTime>>5) & 0x3f; + lt.Hour=(DosTime>>11) & 0x1f; + lt.Day=(DosTime>>16) & 0x1f; + lt.Month=(DosTime>>21) & 0x0f; + lt.Year=(DosTime>>25)+1980; + lt.Reminder=0; + SetLocal(<); } -#if !defined(GUI) || !defined(SFX_MODULE) -void RarTime::GetText(char *DateStr,bool FullYear) +void RarTime::GetText(wchar *DateStr,size_t MaxSize,bool FullMS) { - if (FullYear) - sprintf(DateStr,"%02u-%02u-%u %02u:%02u",rlt.Day,rlt.Month,rlt.Year,rlt.Hour,rlt.Minute); + if (IsSet()) + { + RarLocalTime lt; + GetLocal(<); + if (FullMS) + swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u:%02u,%09u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute,lt.Second,lt.Reminder*(1000000000/TICKS_PER_SECOND)); + else + swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute); + } else - sprintf(DateStr,"%02u-%02u-%02u %02u:%02u",rlt.Day,rlt.Month,rlt.Year%100,rlt.Hour,rlt.Minute); + { + // We use escape before '?' to avoid weird C trigraph characters. + wcsncpyz(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?",MaxSize); + } } -#endif #ifndef SFX_MODULE -void RarTime::SetIsoText(char *TimeText) +void RarTime::SetIsoText(const wchar *TimeText) { int Field[6]; memset(Field,0,sizeof(Field)); - for (int DigitCount=0;*TimeText!=0;TimeText++) + for (uint DigitCount=0;*TimeText!=0;TimeText++) if (IsDigit(*TimeText)) { int FieldPos=DigitCount<4 ? 0:(DigitCount-4)/2+1; - if (FieldPos (RarTime &rt) {return itime>rt.itime;} + bool operator >= (RarTime &rt) {return itime>rt.itime || itime==rt.itime;} + + void GetLocal(RarLocalTime *lt); + void SetLocal(RarLocalTime *lt); +#ifdef _WIN_ALL + void GetWinFT(FILETIME *ft); + void SetWinFT(FILETIME *ft); #endif -#if defined(_UNIX) || defined(_EMX) - RarTime& operator =(time_t ut); + uint64 GetWin(); + void SetWin(uint64 WinTime); time_t GetUnix(); -#endif - bool operator == (RarTime &rt); - bool operator < (RarTime &rt); - bool operator <= (RarTime &rt); - bool operator > (RarTime &rt); - bool operator >= (RarTime &rt); - void GetLocal(RarLocalTime *lt) {*lt=rlt;} - void SetLocal(RarLocalTime *lt) {rlt=*lt;} - int64 GetRaw(); - void SetRaw(int64 RawTime); + void SetUnix(time_t ut); + uint64 GetUnixNS(); + void SetUnixNS(uint64 ns); uint GetDos(); void SetDos(uint DosTime); - void GetText(char *DateStr,bool FullYear); - void SetIsoText(char *TimeText); - void SetAgeText(char *TimeText); + void GetText(wchar *DateStr,size_t MaxSize,bool FullMS); + void SetIsoText(const wchar *TimeText); + void SetAgeText(const wchar *TimeText); void SetCurrentTime(); - void Reset() {rlt.Year=0;} - bool IsSet() {return(rlt.Year!=0);} + void Reset() {itime=0;} + bool IsSet() {return itime!=0;} + void Adjust(int64 ns); }; -const char *GetMonthName(int Month); +const wchar *GetMonthName(int Month); bool IsLeapYear(int Year); #endif diff --git a/libunrar/ui.cpp b/libunrar/ui.cpp new file mode 100644 index 0000000..9713a88 --- /dev/null +++ b/libunrar/ui.cpp @@ -0,0 +1,14 @@ +#include "rar.hpp" + +#include "uicommon.cpp" + +#ifdef SILENT +#include "uisilent.cpp" +#else + + + + +#include "uiconsole.cpp" + +#endif diff --git a/libunrar/ui.hpp b/libunrar/ui.hpp new file mode 100644 index 0000000..6bd224d --- /dev/null +++ b/libunrar/ui.hpp @@ -0,0 +1,171 @@ +#ifndef _RAR_UI_ +#define _RAR_UI_ + +// UIERROR_ - error message; +// UIMSG_ - informational message; +// UIWAIT_ - message waiting for user confirmation; +// UIEVENT_ - if simple message is not enough; + +enum UIMESSAGE_CODE { + UIERROR_SYSERRMSG, UIERROR_GENERALERRMSG, UIERROR_INCERRCOUNT, + UIERROR_CHECKSUM, UIERROR_CHECKSUMENC, UIERROR_CHECKSUMPACKED, + UIERROR_BADPSW, UIERROR_MEMORY, UIERROR_FILEOPEN, UIERROR_FILECREATE, + UIERROR_FILECLOSE, UIERROR_FILESEEK, UIERROR_FILEREAD, UIERROR_FILEWRITE, + UIERROR_FILEDELETE, UIERROR_RECYCLEFAILED, UIERROR_FILERENAME, + UIERROR_FILEATTR, UIERROR_FILECOPY, UIERROR_FILECOPYHINT, + UIERROR_DIRCREATE, UIERROR_SLINKCREATE, UIERROR_HLINKCREATE, + UIERROR_NOLINKTARGET, UIERROR_NEEDADMIN, UIERROR_ARCBROKEN, + UIERROR_HEADERBROKEN, UIERROR_MHEADERBROKEN, UIERROR_FHEADERBROKEN, + UIERROR_SUBHEADERBROKEN, UIERROR_SUBHEADERUNKNOWN, + UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD, + UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR, + UIERROR_NOTSFX, UIERROR_OLDTOSFX, + UIERROR_WRONGSFXVER, UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM, + UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD, + UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME, + UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS, + UIERROR_RECVOLALLEXIST, UIERROR_RECVOLFOUND, UIERROR_RECONSTRUCTING, + UIERROR_RECVOLCANNOTFIX, UIERROR_OPFAILED, UIERROR_UNEXPEOF, + UIERROR_BADARCHIVE, UIERROR_CMTBROKEN, UIERROR_INVALIDNAME, + UIERROR_NEWRARFORMAT, UIERROR_NOTSUPPORTED, UIERROR_ENCRNOTSUPPORTED, + UIERROR_RARZIPONLY, UIERROR_REPAIROLDFORMAT, UIERROR_NOFILESREPAIRED, + UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE, UIERROR_NOFILESTOEXTRACT, + UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL, UIERROR_UNKNOWNEXTRA, + UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED, UIERROR_ZIPVOLSFX, + UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX, UIERROR_EMAIL, + UIERROR_ACLGET, UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN, UIERROR_ACLSET, + UIERROR_STREAMBROKEN, UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH, + UIERROR_PATHTOOLONG, UIERROR_DIRSCAN, UIERROR_UOWNERGET, + UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID, + UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST, + UIERROR_OPENPRESERVEATIME, + + UIMSG_FIRST, + UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, + UIMSG_RRFOUND, UIMSG_RRNOTFOUND, UIMSG_RRDAMAGED, UIMSG_BLOCKSRECOVERED, + UIMSG_COPYINGDATA, UIMSG_AREADAMAGED, UIMSG_SECTORDAMAGED, + UIMSG_SECTORRECOVERED, UIMSG_SECTORNOTRECOVERED, UIMSG_FOUND, + UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING, + UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING, + UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE, + + UIWAIT_FIRST, + UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, + + UIEVENT_FIRST, + UIEVENT_SEARCHDUPFILESSTART, UIEVENT_SEARCHDUPFILESEND, + UIEVENT_CLEARATTRSTART, UIEVENT_CLEARATTRFILE, + UIEVENT_DELADDEDSTART, UIEVENT_DELADDEDFILE, UIEVENT_FILESFOUND, + UIEVENT_ERASEDISK, UIEVENT_FILESUMSTART, UIEVENT_FILESUMPROGRESS, + UIEVENT_FILESUMEND, UIEVENT_PROTECTSTART, UIEVENT_PROTECTEND, + UIEVENT_TESTADDEDSTART, UIEVENT_TESTADDEDEND, UIEVENT_RRTESTINGSTART, + UIEVENT_RRTESTINGEND, UIEVENT_NEWARCHIVE, UIEVENT_NEWREVFILE +}; + +// Flags for uiAskReplace function. +enum UIASKREP_FLAGS { + UIASKREP_F_NORENAME=1,UIASKREP_F_EXCHSRCDEST=2,UIASKREP_F_SHOWNAMEONLY=4 +}; + +// Codes returned by uiAskReplace. Note that uiAskReplaceEx returns only +// UIASKREP_R_REPLACE, UIASKREP_R_SKIP and UIASKREP_R_CANCEL codes. +enum UIASKREP_RESULT { + UIASKREP_R_REPLACE,UIASKREP_R_SKIP,UIASKREP_R_REPLACEALL,UIASKREP_R_SKIPALL, + UIASKREP_R_RENAME,UIASKREP_R_RENAMEAUTO,UIASKREP_R_CANCEL,UIASKREP_R_UNUSED +}; + +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); +UIASKREP_RESULT uiAskReplaceEx(RAROptions *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); + +void uiInit(SOUND_NOTIFY_MODE Sound); + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName); +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip); +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize); +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize); + +enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE}; +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password); +bool uiIsGlobalPasswordSet(); + +enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; +void uiAlarm(UIALARM_TYPE Type); + + +bool uiAskNextVolume(wchar *VolName,size_t MaxSize); +bool uiAskRepeatRead(const wchar *FileName); +bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull); + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month); +#endif + + +class uiMsgStore +{ + private: + static const size_t MAX_MSG = 8; + const wchar *Str[MAX_MSG]; + uint Num[MAX_MSG]; + uint StrSize,NumSize; + UIMESSAGE_CODE Code; + public: + uiMsgStore(UIMESSAGE_CODE Code) + { + // Init arrays in case a caller passes fewer parameters than expected. + for (uint I=0;ICode=Code; + } + uiMsgStore& operator << (const wchar *s) + { + if (StrSize void uiMsg(UIMESSAGE_CODE Code,T1 a1) +{ + uiMsgStore Store(Code); + Store< void uiMsg(UIMESSAGE_CODE Code,T1 a1,T2 a2) +{ + uiMsgStore Store(Code); + Store< void uiMsg(UIMESSAGE_CODE code,T1 a1,T2 a2,T3 a3) +{ + uiMsgStore Store(code); + Store<Overwrite==OVERWRITE_NONE) + return UIASKREP_R_SKIP; + +#if !defined(SFX_MODULE) && !defined(SILENT) + // Must be before Cmd->AllYes check or -y switch would override -or. + if (Cmd->Overwrite==OVERWRITE_AUTORENAME && GetAutoRenamedName(Name,MaxNameSize)) + return UIASKREP_R_REPLACE; +#endif + + // This check must be after OVERWRITE_AUTORENAME processing or -y switch + // would override -or. + if (Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL) + { + PrepareToDelete(Name); + return UIASKREP_R_REPLACE; + } + + wchar NewName[NM]; + wcsncpyz(NewName,Name,ASIZE(NewName)); + UIASKREP_RESULT Choice=uiAskReplace(NewName,ASIZE(NewName),FileSize,FileTime,Flags); + + if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL) + PrepareToDelete(Name); + + if (Choice==UIASKREP_R_REPLACEALL) + { + Cmd->Overwrite=OVERWRITE_ALL; + return UIASKREP_R_REPLACE; + } + if (Choice==UIASKREP_R_SKIPALL) + { + Cmd->Overwrite=OVERWRITE_NONE; + return UIASKREP_R_SKIP; + } + if (Choice==UIASKREP_R_RENAME) + { + if (PointToName(NewName)==NewName) + SetName(Name,NewName,MaxNameSize); + else + wcsncpyz(Name,NewName,MaxNameSize); + if (FileExist(Name)) + return uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,Flags); + return UIASKREP_R_REPLACE; + } +#if !defined(SFX_MODULE) && !defined(SILENT) + if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name,MaxNameSize)) + { + Cmd->Overwrite=OVERWRITE_AUTORENAME; + return UIASKREP_R_REPLACE; + } +#endif + return Choice; +} diff --git a/libunrar/uiconsole.cpp b/libunrar/uiconsole.cpp new file mode 100644 index 0000000..7b4998d --- /dev/null +++ b/libunrar/uiconsole.cpp @@ -0,0 +1,425 @@ +// Purely user interface function. Gets and returns user input. +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + wchar SizeText1[20],DateStr1[50],SizeText2[20],DateStr2[50]; + + FindData ExistingFD; + memset(&ExistingFD,0,sizeof(ExistingFD)); // In case find fails. + FindFile::FastFind(Name,&ExistingFD); + itoa(ExistingFD.Size,SizeText1,ASIZE(SizeText1)); + ExistingFD.mtime.GetText(DateStr1,ASIZE(DateStr1),false); + + if (FileSize==INT64NDF || FileTime==NULL) + { + eprintf(L"\n"); + eprintf(St(MAskOverwrite),Name); + } + else + { + itoa(FileSize,SizeText2,ASIZE(SizeText2)); + FileTime->GetText(DateStr2,ASIZE(DateStr2),false); + if ((Flags & UIASKREP_F_EXCHSRCDEST)==0) + eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2); + else + eprintf(St(MAskReplace),Name,SizeText2,DateStr2,SizeText1,DateStr1); + } + + bool AllowRename=(Flags & UIASKREP_F_NORENAME)==0; + int Choice=0; + do + { + Choice=Ask(St(AllowRename ? MYesNoAllRenQ : MYesNoAllQ)); + } while (Choice==0); // 0 means invalid input. + switch(Choice) + { + case 1: + return UIASKREP_R_REPLACE; + case 2: + return UIASKREP_R_SKIP; + case 3: + return UIASKREP_R_REPLACEALL; + case 4: + return UIASKREP_R_SKIPALL; + } + if (AllowRename && Choice==5) + { + mprintf(St(MAskNewName)); + if (getwstr(Name,MaxNameSize)) + return UIASKREP_R_RENAME; + else + return UIASKREP_R_SKIP; // Process fwgets failure as if user answered 'No'. + } + return UIASKREP_R_CANCEL; +} + + + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +{ + mprintf(St(Extract ? MExtracting : MExtrTest), ArcName); +} + + +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +{ + return true; +} + + +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) +{ + int CurPercent=ToPercent(CurSize,TotalSize); + mprintf(L"\b\b\b\b%3d%%",CurPercent); +} + + +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) +{ + int CurPercent=ToPercent(CurSize,TotalSize); + mprintf(L"\b\b\b\b%3d%%",CurPercent); +} + + +void uiMsgStore::Msg() +{ + switch(Code) + { + case UIERROR_SYSERRMSG: + case UIERROR_GENERALERRMSG: + Log(NULL,L"\n%ls",Str[0]); + break; + case UIERROR_CHECKSUM: + Log(Str[0],St(MCRCFailed),Str[1]); + break; + case UIERROR_CHECKSUMENC: + Log(Str[0],St(MEncrBadCRC),Str[1]); + break; + case UIERROR_CHECKSUMPACKED: + Log(Str[0],St(MDataBadCRC),Str[1],Str[0]); + break; + case UIERROR_BADPSW: + Log(Str[0],St(MWrongFilePassword),Str[1]); + break; + case UIWAIT_BADPSW: + Log(Str[0],St(MWrongPassword)); + break; + case UIERROR_MEMORY: + mprintf(L"\n"); + Log(NULL,St(MErrOutMem)); + break; + case UIERROR_FILEOPEN: + Log(Str[0],St(MCannotOpen),Str[1]); + break; + case UIERROR_FILECREATE: + Log(Str[0],St(MCannotCreate),Str[1]); + break; + case UIERROR_FILECLOSE: + Log(NULL,St(MErrFClose),Str[0]); + break; + case UIERROR_FILESEEK: + Log(NULL,St(MErrSeek),Str[0]); + break; + case UIERROR_FILEREAD: + Log(Str[0],St(MErrRead),Str[1]); + break; + case UIERROR_FILEWRITE: + Log(Str[0],St(MErrWrite),Str[1]); + break; +#ifndef SFX_MODULE + case UIERROR_FILEDELETE: + Log(Str[0],St(MCannotDelete),Str[1]); + break; + case UIERROR_RECYCLEFAILED: + Log(Str[0],St(MRecycleFailed)); + break; + case UIERROR_FILERENAME: + Log(Str[0],St(MErrRename),Str[1],Str[2]); + break; +#endif + case UIERROR_FILEATTR: + Log(Str[0],St(MErrChangeAttr),Str[1]); + break; + case UIERROR_FILECOPY: + Log(Str[0],St(MCopyError),Str[1],Str[2]); + break; + case UIERROR_FILECOPYHINT: + Log(Str[0],St(MCopyErrorHint)); + mprintf(L" "); // For progress percent. + break; + case UIERROR_DIRCREATE: + Log(Str[0],St(MExtrErrMkDir),Str[1]); + break; + case UIERROR_SLINKCREATE: + Log(Str[0],St(MErrCreateLnkS),Str[1]); + break; + case UIERROR_HLINKCREATE: + Log(NULL,St(MErrCreateLnkH),Str[0]); + break; + case UIERROR_NOLINKTARGET: + Log(NULL,St(MErrLnkTarget)); + mprintf(L" "); // For progress percent. + break; + case UIERROR_NEEDADMIN: + Log(NULL,St(MNeedAdmin)); + break; + case UIERROR_ARCBROKEN: + Log(Str[0],St(MErrBrokenArc)); + break; + case UIERROR_HEADERBROKEN: + Log(Str[0],St(MHeaderBroken)); + break; + case UIERROR_MHEADERBROKEN: + Log(Str[0],St(MMainHeaderBroken)); + break; + case UIERROR_FHEADERBROKEN: + Log(Str[0],St(MLogFileHead),Str[1]); + break; + case UIERROR_SUBHEADERBROKEN: + Log(Str[0],St(MSubHeadCorrupt)); + break; + case UIERROR_SUBHEADERUNKNOWN: + Log(Str[0],St(MSubHeadUnknown)); + break; + case UIERROR_SUBHEADERDATABROKEN: + Log(Str[0],St(MSubHeadDataCRC),Str[1]); + break; + case UIERROR_RRDAMAGED: + Log(Str[0],St(MRRDamaged)); + break; + case UIERROR_UNKNOWNMETHOD: + Log(Str[0],St(MUnknownMeth),Str[1]); + break; + case UIERROR_UNKNOWNENCMETHOD: + { + wchar Msg[256]; + swprintf(Msg,ASIZE(Msg),St(MUnkEncMethod),Str[1]); + Log(Str[0],L"%s: %s",Msg,Str[2]); + } + break; +#ifndef SFX_MODULE + case UIERROR_RENAMING: + Log(Str[0],St(MRenaming),Str[1],Str[2]); + break; + case UIERROR_NEWERRAR: + Log(Str[0],St(MNewerRAR)); + break; +#endif + case UIERROR_RECVOLDIFFSETS: + Log(NULL,St(MRecVolDiffSets),Str[0],Str[1]); + break; + case UIERROR_RECVOLALLEXIST: + mprintf(St(MRecVolAllExist)); + break; + case UIERROR_RECONSTRUCTING: + mprintf(St(MReconstructing)); + break; + case UIERROR_RECVOLCANNOTFIX: + mprintf(St(MRecVolCannotFix)); + break; + case UIERROR_UNEXPEOF: + Log(Str[0],St(MLogUnexpEOF)); + break; + case UIERROR_BADARCHIVE: + Log(Str[0],St(MBadArc),Str[0]); + break; + case UIERROR_CMTBROKEN: + Log(Str[0],St(MLogCommBrk)); + break; + case UIERROR_INVALIDNAME: + Log(Str[0],St(MInvalidName),Str[1]); + mprintf(L"\n"); // Needed when called from CmdExtract::ExtractCurrentFile. + break; +#ifndef SFX_MODULE + case UIERROR_NEWRARFORMAT: + Log(Str[0],St(MNewRarFormat)); + break; +#endif + case UIERROR_NOFILESTOEXTRACT: + mprintf(St(MExtrNoFiles)); + break; + case UIERROR_MISSINGVOL: + Log(Str[0],St(MAbsNextVol),Str[0]); + break; +#ifndef SFX_MODULE + case UIERROR_NEEDPREVVOL: + Log(Str[0],St(MUnpCannotMerge),Str[1]); + break; + case UIERROR_UNKNOWNEXTRA: + Log(Str[0],St(MUnknownExtra),Str[1]); + break; + case UIERROR_CORRUPTEXTRA: + Log(Str[0],St(MCorruptExtra),Str[1],Str[2]); + break; +#endif +#if !defined(SFX_MODULE) && defined(_WIN_ALL) + case UIERROR_NTFSREQUIRED: + Log(NULL,St(MNTFSRequired),Str[0]); + break; +#endif +#if !defined(SFX_MODULE) && defined(_WIN_ALL) + case UIERROR_ACLBROKEN: + Log(Str[0],St(MACLBroken),Str[1]); + break; + case UIERROR_ACLUNKNOWN: + Log(Str[0],St(MACLUnknown),Str[1]); + break; + case UIERROR_ACLSET: + Log(Str[0],St(MACLSetError),Str[1]); + break; + case UIERROR_STREAMBROKEN: + Log(Str[0],St(MStreamBroken),Str[1]); + break; + case UIERROR_STREAMUNKNOWN: + Log(Str[0],St(MStreamUnknown),Str[1]); + break; +#endif + case UIERROR_INCOMPATSWITCH: + mprintf(St(MIncompatSwitch),Str[0],Num[0]); + break; + case UIERROR_PATHTOOLONG: + Log(NULL,L"\n%ls%ls%ls",Str[0],Str[1],Str[2]); + Log(NULL,St(MPathTooLong)); + break; +#ifndef SFX_MODULE + case UIERROR_DIRSCAN: + Log(NULL,St(MScanError),Str[0]); + break; +#endif + case UIERROR_UOWNERBROKEN: + Log(Str[0],St(MOwnersBroken),Str[1]); + break; + case UIERROR_UOWNERGETOWNERID: + Log(Str[0],St(MErrGetOwnerID),Str[1]); + break; + case UIERROR_UOWNERGETGROUPID: + Log(Str[0],St(MErrGetGroupID),Str[1]); + break; + case UIERROR_UOWNERSET: + Log(Str[0],St(MSetOwnersError),Str[1]); + break; + case UIERROR_ULINKREAD: + Log(NULL,St(MErrLnkRead),Str[0]); + break; + case UIERROR_ULINKEXIST: + Log(NULL,St(MSymLinkExists),Str[0]); + break; + + +#ifndef SFX_MODULE + case UIMSG_STRING: + mprintf(L"\n%s",Str[0]); + break; +#endif + case UIMSG_CORRECTINGNAME: + Log(Str[0],St(MCorrectingName)); + break; + case UIMSG_BADARCHIVE: + mprintf(St(MBadArc),Str[0]); + break; + case UIMSG_CREATING: + mprintf(St(MCreating),Str[0]); + break; + case UIMSG_RENAMING: + mprintf(St(MRenaming),Str[0],Str[1]); + break; + case UIMSG_RECVOLCALCCHECKSUM: + mprintf(St(MCalcCRCAllVol)); + break; + case UIMSG_RECVOLFOUND: + mprintf(St(MRecVolFound),Num[0]); + break; + case UIMSG_RECVOLMISSING: + mprintf(St(MRecVolMissing),Num[0]); + break; + case UIMSG_MISSINGVOL: + mprintf(St(MAbsNextVol),Str[0]); + break; + case UIMSG_RECONSTRUCTING: + mprintf(St(MReconstructing)); + break; + case UIMSG_CHECKSUM: + mprintf(St(MCRCFailed),Str[0]); + break; + case UIMSG_FAT32SIZE: + mprintf(St(MFAT32Size)); + mprintf(L" "); // For progress percent. + break; + + + + case UIEVENT_RRTESTINGSTART: + mprintf(L"%s ",St(MTestingRR)); + break; + } +} + + +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +{ + // Unlike GUI we cannot provide Cancel button here, so we use the empty + // password to abort. Otherwise user not knowing a password would need to + // press Ctrl+C multiple times to quit from infinite password request loop. + return GetConsolePassword(Type,FileName,Password) && Password->IsSet(); +} + + +bool uiIsGlobalPasswordSet() +{ + return false; +} + + +void uiAlarm(UIALARM_TYPE Type) +{ + if (uiSoundNotify==SOUND_NOTIFY_ON) + { + static clock_t LastTime=-10; // Negative to always beep first time. + if ((MonoClock()-LastTime)/CLOCKS_PER_SEC>5) + { +#ifdef _WIN_ALL + MessageBeep(-1); +#else + putwchar('\007'); +#endif + LastTime=MonoClock(); + } + } +} + + + + +bool uiAskNextVolume(wchar *VolName,size_t MaxSize) +{ + eprintf(St(MAskNextVol),VolName); + return Ask(St(MContinueQuit))!=2; +} + + +bool uiAskRepeatRead(const wchar *FileName) +{ + mprintf(L"\n"); + Log(NULL,St(MErrRead),FileName); + return Ask(St(MRetryAbort))==1; +} + + +bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull) +{ + mprintf(L"\n"); + Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName); + return Ask(St(MRetryAbort))==1; +} + + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month) +{ + static MSGID MonthID[12]={ + MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, + MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec + }; + return St(MonthID[Month]); +} +#endif diff --git a/libunrar/uisilent.cpp b/libunrar/uisilent.cpp new file mode 100644 index 0000000..1b5de13 --- /dev/null +++ b/libunrar/uisilent.cpp @@ -0,0 +1,69 @@ +// Purely user interface function. Gets and returns user input. +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + return UIASKREP_R_REPLACE; +} + + + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +{ +} + + +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +{ + return true; +} + + +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) +{ +} + + +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) +{ +} + + +void uiMsgStore::Msg() +{ +} + + +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +{ + return false; +} + + +bool uiIsGlobalPasswordSet() +{ + return false; +} + + +void uiAlarm(UIALARM_TYPE Type) +{ +} + + +bool uiIsAborted() +{ + return false; +} + + +void uiGiveTick() +{ +} + + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month) +{ + return L""; +} +#endif diff --git a/libunrar/ulinks.cpp b/libunrar/ulinks.cpp index b8d6aa5..1656824 100644 --- a/libunrar/ulinks.cpp +++ b/libunrar/ulinks.cpp @@ -1,32 +1,105 @@ -#include "rar.hpp" - -int ExtractLink(ComprDataIO &DataIO,Archive &Arc,char *DestName,uint &LinkCRC,bool Create) +static bool UnixSymlink(const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) { -#if defined(SAVE_LINKS) && defined(_UNIX) - char FileName[NM]; - if (IsLink(Arc.NewLhd.FileAttr)) + CreatePath(LinkName,true); + DelFile(LinkName); + char LinkNameA[NM]; + WideToChar(LinkName,LinkNameA,ASIZE(LinkNameA)); + if (symlink(Target,LinkNameA)==-1) // Error. { - int DataSize=Min(Arc.NewLhd.PackSize,sizeof(FileName)-1); - DataIO.UnpRead((byte *)FileName,DataSize); - FileName[DataSize]=0; - if (Create) + if (errno==EEXIST) + uiMsg(UIERROR_ULINKEXIST,LinkName); + else { - CreatePath(DestName,NULL,true); - if (symlink(FileName,DestName)==-1) - if (errno==EEXIST) - Log(Arc.FileName,St(MSymLinkExists),DestName); - else - { - Log(Arc.FileName,St(MErrCreateLnk),DestName); - ErrHandler.SetErrorCode(WARNING); - } + uiMsg(UIERROR_SLINKCREATE,UINULL,LinkName); + ErrHandler.SetErrorCode(RARX_WARNING); } - int NameSize=Min(DataSize,strlen(FileName)); - LinkCRC=CRC(0xffffffff,FileName,NameSize); - return(1); + return false; } +#ifdef USE_LUTIMES +#ifdef UNIX_TIME_NS + timespec times[2]; + times[0].tv_sec=fta->GetUnix(); + times[0].tv_nsec=fta->IsSet() ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; + times[1].tv_sec=ftm->GetUnix(); + times[1].tv_nsec=ftm->IsSet() ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; + utimensat(AT_FDCWD,LinkNameA,times,AT_SYMLINK_NOFOLLOW); +#else + struct timeval tv[2]; + tv[0].tv_sec=fta->GetUnix(); + tv[0].tv_usec=long(fta->GetUnixNS()%1000000000/1000); + tv[1].tv_sec=ftm->GetUnix(); + tv[1].tv_usec=long(ftm->GetUnixNS()%1000000000/1000); + lutimes(LinkNameA,tv); #endif - return(0); +#endif + + return true; +} + + +static bool IsFullPath(const char *PathA) // Unix ASCII version. +{ + return *PathA==CPATHDIVIDER; +} + + +bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +{ + char Target[NM]; + if (IsLink(Arc.FileHead.FileAttr)) + { + size_t DataSize=(size_t)Arc.FileHead.PackSize; + if (DataSize>ASIZE(Target)-1) + return false; + if ((size_t)DataIO.UnpRead((byte *)Target,DataSize)!=DataSize) + return false; + Target[DataSize]=0; + + DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1); + DataIO.UnpHash.Update(Target,strlen(Target)); + DataIO.UnpHash.Result(&Arc.FileHead.FileHash); + + // Return true in case of bad checksum, so link will be processed further + // and extraction routine will report the checksum error. + if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL)) + return true; + + wchar TargetW[NM]; + CharToWide(Target,TargetW,ASIZE(TargetW)); + // Check for *TargetW==0 to catch CharToWide failure. + // Use Arc.FileHead.FileName instead of LinkName, since LinkName + // can include the destination path as a prefix, which can + // confuse IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (*TargetW==0 || IsFullPath(TargetW) || + !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) + return false; + return UnixSymlink(Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); + } + return false; +} + + +bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) +{ + char Target[NM]; + WideToChar(hd->RedirName,Target,ASIZE(Target)); + if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION) + { + // Cannot create Windows absolute path symlinks in Unix. Only relative path + // Windows symlinks can be created here. RAR 5.0 used \??\ prefix + // for Windows absolute symlinks, since RAR 5.1 /??/ is used. + // We escape ? as \? to avoid "trigraph" warning + if (strncmp(Target,"\\??\\",4)==0 || strncmp(Target,"/\?\?/",4)==0) + return false; + DosSlashToUnix(Target,Target,ASIZE(Target)); + } + // Use hd->FileName instead of LinkName, since LinkName can include + // the destination path as a prefix, which can confuse + // IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (IsFullPath(Target) || + !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) + return false; + return UnixSymlink(Target,Name,&hd->mtime,&hd->atime); } diff --git a/libunrar/ulinks.hpp b/libunrar/ulinks.hpp deleted file mode 100644 index 69b0e9f..0000000 --- a/libunrar/ulinks.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _RAR_ULINKS_ -#define _RAR_ULINKS_ - -void SaveLinkData(ComprDataIO &DataIO,Archive &TempArc,FileHeader &hd, - char *Name); -int ExtractLink(ComprDataIO &DataIO,Archive &Arc,char *DestName, - uint &LinkCRC,bool Create); - -#endif diff --git a/libunrar/unicode.cpp b/libunrar/unicode.cpp index aeece55..df63608 100644 --- a/libunrar/unicode.cpp +++ b/libunrar/unicode.cpp @@ -1,124 +1,245 @@ #include "rar.hpp" +#define MBFUNCTIONS + +#if defined(_UNIX) && defined(MBFUNCTIONS) + +static bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success); +static void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success); + +// In Unix we map high ASCII characters which cannot be converted to Unicode +// to 0xE000 - 0xE0FF private use Unicode area. +static const uint MapAreaStart=0xE000; + +// Mapped string marker. Initially we used 0xFFFF for this purpose, +// but it causes MSVC2008 swprintf to fail (it treats 0xFFFF as error marker). +// While we could workaround it, it is safer to use another character. +static const uint MappedStringMark=0xFFFE; -#if defined(_EMX) && !defined(_DJGPP) -#include "unios2.cpp" #endif bool WideToChar(const wchar *Src,char *Dest,size_t DestSize) { bool RetCode=true; -#ifdef _WIN_32 + *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. + +#ifdef _WIN_ALL if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,(int)DestSize,NULL,NULL)==0) RetCode=false; -#else -#ifdef _APPLE + +// wcstombs is broken in Android NDK r9. +#elif defined(_APPLE) WideToUtf(Src,Dest,DestSize); -#else -#ifdef MBFUNCTIONS - size_t ResultingSize=wcstombs(Dest,Src,DestSize); - if (ResultingSize==(size_t)-1) - RetCode=false; - if (ResultingSize==0 && *Src!=0) - RetCode=false; - - if ((!RetCode || *Dest==0 && *Src!=0) && DestSize>NM && strlenw(Src)len*2) - RetCode=false; - Dest[DestSize]=0; -#endif - } - else - for (int I=0;I0) + Dest[DestSize-1]=0; + + // We tried to return the empty string if conversion is failed, + // but it does not work well. WideCharToMultiByte returns 'failed' code + // and partially converted string even if we wanted to convert only a part + // of string and passed DestSize smaller than required for fully converted + // string. Such call is the valid behavior in RAR code and we do not expect + // the empty string in this case. + + return RetCode; } bool CharToWide(const char *Src,wchar *Dest,size_t DestSize) { bool RetCode=true; -#ifdef _WIN_32 + *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. + +#ifdef _WIN_ALL if (MultiByteToWideChar(CP_ACP,0,Src,-1,Dest,(int)DestSize)==0) RetCode=false; -#else -#ifdef _APPLE - UtfToWide(Src,Dest,DestSize); -#else -#ifdef MBFUNCTIONS - size_t ResultingSize=mbstowcs(Dest,Src,DestSize); +// mbstowcs is broken in Android NDK r9. +#elif defined(_APPLE) + UtfToWide(Src,Dest,DestSize); + +#elif defined(MBFUNCTIONS) + mbstate_t ps; + memset (&ps, 0, sizeof(ps)); + const char *SrcParam=Src; // mbsrtowcs can change the pointer. + size_t ResultingSize=mbsrtowcs(Dest,&SrcParam,DestSize,&ps); if (ResultingSize==(size_t)-1) RetCode=false; if (ResultingSize==0 && *Src!=0) RetCode=false; - if ((!RetCode || *Dest==0 && *Src!=0) && DestSize>NM && strlen(Src)1) + CharToWideMap(Src,Dest,DestSize,RetCode); #else - if (UnicodeEnabled()) + for (int I=0;Ilen) - DestSize=0; - RetCode=false; -#endif + Dest[I]=(wchar_t)Src[I]; + if (Src[I]==0) + break; } - else - for (int I=0;I0) + Dest[DestSize-1]=0; + + // We tried to return the empty string if conversion is failed, + // but it does not work well. MultiByteToWideChar returns 'failed' code + // even if we wanted to convert only a part of string and passed DestSize + // smaller than required for fully converted string. Such call is the valid + // behavior in RAR code and we do not expect the empty string in this case. + + return RetCode; } -byte* WideToRaw(const wchar *Src,byte *Dest,size_t DestSize) +#if defined(_UNIX) && defined(MBFUNCTIONS) +// Convert and restore mapped inconvertible Unicode characters. +// We use it for extended ASCII names in Unix. +bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success) { - for (size_t I=0;I=MapAreaStart+0x80 && uint(Src[SrcPos])=0x80) + { + if (!MarkAdded) + { + Dest[DestPos++]=MappedStringMark; + MarkAdded=true; + if (DestPos>=DestSize) + break; + } + Dest[DestPos++]=byte(Src[SrcPos++])+MapAreaStart; + } + else + break; + } + else + { + memset(&ps,0,sizeof(ps)); + int Length=mbrlen(Src+SrcPos,MB_CUR_MAX,&ps); + SrcPos+=Max(Length,1); + DestPos++; + } + } + Dest[Min(DestPos,DestSize-1)]=0; +} +#endif + + +// SrcSize is in wide characters, not in bytes. +byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize) +{ + for (size_t I=0;I>8); if (*Src==0) break; } - return(Dest); + return Dest; } @@ -127,7 +248,7 @@ wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize) for (size_t I=0;I=0xd800 && c<=0xdbff && *Src>=0xdc00 && *Src<=0xdfff) // Surrogate pair. + { + c=((c-0xd800)<<10)+(*Src-0xdc00)+0x10000; + Src++; + } if (c<0x10000 && (dsize-=2)>=0) { *(Dest++)=(0xe0|(c>>12)); @@ -161,25 +288,57 @@ void WideToUtf(const wchar *Src,char *Dest,size_t DestSize) *(Dest++)=(0x80|((c>>6)&0x3f)); *(Dest++)=(0x80|(c&0x3f)); } + } } *Dest=0; } -void UtfToWide(const char *Src,wchar *Dest,size_t DestSize) +size_t WideToUtfSize(const wchar *Src) { + size_t Size=0; + for (;*Src!=0;Src++) + if (*Src<0x80) + Size++; + else + if (*Src<0x800) + Size+=2; + else + if ((uint)*Src<0x10000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. + { + if (Src[0]>=0xd800 && Src[0]<=0xdbff && Src[1]>=0xdc00 && Src[1]<=0xdfff) + { + Size+=4; // 4 output bytes for Unicode surrogate pair. + Src++; + } + else + Size+=3; + } + else + if ((uint)*Src<0x200000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. + Size+=4; + return Size+1; // Include terminating zero. +} + + +bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize) +{ + bool Success=true; long dsize=(long)DestSize; dsize--; while (*Src!=0) { - uint c=(byte)*(Src++),d; + uint c=byte(*(Src++)),d; if (c<0x80) d=c; else if ((c>>5)==6) { if ((*Src&0xc0)!=0x80) + { + Success=false; break; + } d=((c&0x1f)<<6)|(*Src&0x3f); Src++; } @@ -187,7 +346,10 @@ void UtfToWide(const char *Src,wchar *Dest,size_t DestSize) if ((c>>4)==14) { if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80) + { + Success=false; break; + } d=((c&0xf)<<12)|((Src[0]&0x3f)<<6)|(Src[1]&0x3f); Src+=2; } @@ -195,242 +357,220 @@ void UtfToWide(const char *Src,wchar *Dest,size_t DestSize) if ((c>>3)==30) { if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80 || (Src[2]&0xc0)!=0x80) + { + Success=false; break; + } d=((c&7)<<18)|((Src[0]&0x3f)<<12)|((Src[1]&0x3f)<<6)|(Src[2]&0x3f); Src+=3; } else + { + Success=false; break; + } if (--dsize<0) break; if (d>0xffff) { - if (--dsize<0 || d>0x10ffff) + if (--dsize<0) break; - *(Dest++)=((d-0x10000)>>10)+0xd800; - *(Dest++)=(d&0x3ff)+0xdc00; + if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. + { + Success=false; + continue; + } + if (sizeof(*Dest)==2) // Use the surrogate pair. + { + *(Dest++)=((d-0x10000)>>10)+0xd800; + *(Dest++)=(d&0x3ff)+0xdc00; + } + else + *(Dest++)=d; } else *(Dest++)=d; } *Dest=0; + return Success; } -bool UnicodeEnabled() +// For zero terminated strings. +bool IsTextUtf8(const byte *Src) { -#ifdef UNICODE_SUPPORTED - #ifdef _EMX - return(uni_ready); - #else - return(true); - #endif + return IsTextUtf8(Src,strlen((const char *)Src)); +} + + +// Source data can be both with and without UTF-8 BOM. +bool IsTextUtf8(const byte *Src,size_t SrcSize) +{ + while (SrcSize-- > 0) + { + byte C=*(Src++); + int HighOne=0; // Number of leftmost '1' bits. + for (byte Mask=0x80;Mask!=0 && (C & Mask)!=0;Mask>>=1) + HighOne++; + if (HighOne==1 || HighOne>6) + return false; + while (--HighOne > 0) + if (SrcSize-- <= 0 || (*(Src++) & 0xc0)!=0x80) + return false; + } + return true; +} + + +int wcsicomp(const wchar *s1,const wchar *s2) +{ +#ifdef _WIN_ALL + return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; #else - return(false); -#endif -} - - -size_t strlenw(const wchar *str) -{ - size_t length=0; - while (*(str++)!=0) - length++; - return(length); -} - - -wchar* strcpyw(wchar *dest,const wchar *src) -{ - do { - *(dest++)=*src; - } while (*(src++)!=0); - return(dest); -} - - -wchar* strncpyw(wchar *dest,const wchar *src,size_t n) -{ - do { - *(dest++)=*src; - } while (*(src++)!=0 && (int)(--n) > 0); - return(dest); -} - - -wchar* strcatw(wchar *dest,const wchar *src) -{ - return(strcpyw(dest+strlenw(dest),src)); -} - - -#ifndef SFX_MODULE -wchar* strncatw(wchar *dest,const wchar *src,size_t n) -{ - dest+=strlenw(dest); while (true) - if ((int)(--n)<0) + { + wchar u1 = towupper(*s1); + wchar u2 = towupper(*s2); + if (u1 != u2) + return u1 < u2 ? -1 : 1; + if (*s1==0) + break; + s1++; + s2++; + } + return 0; +#endif +} + + +int wcsnicomp(const wchar *s1,const wchar *s2,size_t n) +{ +#ifdef _WIN_ALL + // If we specify 'n' exceeding the actual string length, CompareString goes + // beyond the trailing zero and compares garbage. So we need to limit 'n' + // to real string length. + size_t l1=Min(wcslen(s1)+1,n); + size_t l2=Min(wcslen(s2)+1,n); + return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; +#else + if (n==0) + return 0; + while (true) + { + wchar u1 = towupper(*s1); + wchar u2 = towupper(*s2); + if (u1 != u2) + return u1 < u2 ? -1 : 1; + if (*s1==0 || --n==0) + break; + s1++; + s2++; + } + return 0; +#endif +} + + +const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search) +{ + for (size_t i=0;str[i]!=0;i++) + for (size_t j=0;;j++) { - *dest=0; - break; - } - else - if ((*(dest++)=*(src++))==0) + if (search[j]==0) + return str+i; + if (tolowerw(str[i+j])!=tolowerw(search[j])) break; - return(dest); -} -#endif - - -int strcmpw(const wchar *s1,const wchar *s2) -{ - while (*s1==*s2) - { - if (*s1==0) - return(0); - s1++; - s2++; - } - return(*s1<*s2 ? -1:1); -} - - -int strncmpw(const wchar *s1,const wchar *s2,size_t n) -{ - while ((int)(n--)>0) - { - if (*s1<*s2) - return(-1); - if (*s1>*s2) - return(-1); - if (*s1==0) - break; - s1++; - s2++; - } - return(0); + } + return NULL; } #ifndef SFX_MODULE -int stricmpw(const wchar *s1,const wchar *s2) +wchar* wcslower(wchar *s) { - char Ansi1[NM*sizeof(wchar)],Ansi2[NM*sizeof(wchar)]; - WideToChar(s1,Ansi1,sizeof(Ansi1)); - WideToChar(s2,Ansi2,sizeof(Ansi2)); - return(stricomp(Ansi1,Ansi2)); -} +#ifdef _WIN_ALL + // _wcslwr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharLower involves less overhead. + CharLower(s); +#else + for (wchar *c=s;*c!=0;c++) + *c=towlower(*c); #endif - - -#if !defined(SFX_MODULE) && !defined(_WIN_CE) -inline int strnicmpw_w2c(const wchar *s1,const wchar *s2,size_t n) -{ - wchar Wide1[NM*2],Wide2[NM*2]; - strncpyw(Wide1,s1,sizeof(Wide1)/sizeof(Wide1[0])-1); - strncpyw(Wide2,s2,sizeof(Wide2)/sizeof(Wide2[0])-1); - Wide1[Min(sizeof(Wide1)/sizeof(Wide1[0])-1,n)]=0; - Wide2[Min(sizeof(Wide2)/sizeof(Wide2[0])-1,n)]=0; - char Ansi1[NM*2],Ansi2[NM*2]; - WideToChar(Wide1,Ansi1,sizeof(Ansi1)); - WideToChar(Wide2,Ansi2,sizeof(Ansi2)); - return(stricomp(Ansi1,Ansi2)); + return s; } #endif #ifndef SFX_MODULE -int strnicmpw(const wchar *s1,const wchar *s2,size_t n) +wchar* wcsupper(wchar *s) { - return(strnicmpw_w2c(s1,s2,n)); +#ifdef _WIN_ALL + // _wcsupr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharUpper involves less overhead. + CharUpper(s); +#else + for (wchar *c=s;*c!=0;c++) + *c=towupper(*c); +#endif + return s; } #endif -wchar* strchrw(const wchar *s,int c) -{ - while (*s) - { - if (*s==c) - return((wchar *)s); - s++; - } - return(NULL); -} - - -wchar* strrchrw(const wchar *s,int c) -{ - for (int I=(int)(strlenw(s)-1);I>=0;I--) - if (s[I]==c) - return((wchar *)(s+I)); - return(NULL); -} - - -wchar* strpbrkw(const wchar *s1,const wchar *s2) -{ - while (*s1) - { - if (strchrw(s2,*s1)!=NULL) - return((wchar *)s1); - s1++; - } - return(NULL); -} - - -#ifndef SFX_MODULE -wchar* strlowerw(wchar *Str) -{ - for (wchar *ChPtr=Str;*ChPtr;ChPtr++) - if (*ChPtr<128) - *ChPtr=loctolower((byte)*ChPtr); - return(Str); -} -#endif - - -#ifndef SFX_MODULE -wchar* strupperw(wchar *Str) -{ - for (wchar *ChPtr=Str;*ChPtr;ChPtr++) - if (*ChPtr<128) - *ChPtr=loctoupper((byte)*ChPtr); - return(Str); -} -#endif - - -#ifndef SFX_MODULE -wchar* strdupw(const wchar *Str) -{ - if (Str==NULL) - return(NULL); - wchar *n=(wchar *)malloc((strlenw(Str)+1)*sizeof(wchar)); - if (n==NULL) - return(NULL); - strcpyw(n,Str); - return(n); -} -#endif int toupperw(int ch) { - return((ch<128) ? loctoupper(ch):ch); +#if defined(_WIN_ALL) + // CharUpper is more reliable than towupper in Windows, which seems to be + // C locale dependent even in Unicode version. For example, towupper failed + // to convert lowercase Russian characters. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)(ch&0xffff)); +#else + return towupper(ch); +#endif +} + + +int tolowerw(int ch) +{ +#if defined(_WIN_ALL) + // CharLower is more reliable than towlower in Windows. + // See comment for towupper above. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)(ch&0xffff)); +#else + return towlower(ch); +#endif } int atoiw(const wchar *s) { - int n=0; + return (int)atoilw(s); +} + + +int64 atoilw(const wchar *s) +{ + bool sign=false; + if (*s=='-') // We do use signed integers here, for example, in GUI SFX. + { + s++; + sign=true; + } + // Use unsigned type here, since long string can overflow the variable + // and signed integer overflow is undefined behavior in C++. + uint64 n=0; while (*s>='0' && *s<='9') { n=n*10+(*s-'0'); s++; } - return(n); + // Check int64(n)>=0 to avoid the signed overflow with undefined behavior + // when negating 0x8000000000000000. + return sign && int64(n)>=0 ? -int64(n) : int64(n); } @@ -455,7 +595,10 @@ void SupportDBCS::Init() char* SupportDBCS::charnext(const char *s) { - return (char *)(IsLeadByte[(byte)*s] ? s+2:s+1); + // Zero cannot be the trail byte. So if next byte after the lead byte + // is 0, the string is corrupt and we'll better return the pointer to 0, + // to break string processing loops. + return (char *)(IsLeadByte[(byte)*s] && s[1]!=0 ? s+2:s+1); } @@ -511,3 +654,5 @@ char* SupportDBCS::strrchrd(const char *s, int c) return((char *)found); } #endif + + diff --git a/libunrar/unicode.hpp b/libunrar/unicode.hpp index 9c087e8..031ac09 100644 --- a/libunrar/unicode.hpp +++ b/libunrar/unicode.hpp @@ -1,48 +1,31 @@ #ifndef _RAR_UNICODE_ #define _RAR_UNICODE_ -#ifndef _EMX -#define MBFUNCTIONS -#endif - -#if defined(MBFUNCTIONS) || defined(_WIN_32) || defined(_EMX) && !defined(_DJGPP) -#define UNICODE_SUPPORTED -#endif - -#ifdef _WIN_32 +#if defined( _WIN_ALL) #define DBCS_SUPPORTED #endif -#ifdef _EMX -int uni_init(int codepage); -int uni_done(); -#endif - -bool WideToChar(const wchar *Src,char *Dest,size_t DestSize=0x1000000); -bool CharToWide(const char *Src,wchar *Dest,size_t DestSize=0x1000000); -byte* WideToRaw(const wchar *Src,byte *Dest,size_t DestSize=0x1000000); -wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize=0x1000000); +bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); +bool CharToWide(const char *Src,wchar *Dest,size_t DestSize); +byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize); +wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize); void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); -void UtfToWide(const char *Src,wchar *Dest,size_t DestSize); -bool UnicodeEnabled(); +size_t WideToUtfSize(const wchar *Src); +bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize); +bool IsTextUtf8(const byte *Src); +bool IsTextUtf8(const byte *Src,size_t SrcSize); -size_t strlenw(const wchar *str); -wchar* strcpyw(wchar *dest,const wchar *src); -wchar* strncpyw(wchar *dest,const wchar *src,size_t n); -wchar* strcatw(wchar *dest,const wchar *src); -wchar* strncatw(wchar *dest,const wchar *src,size_t n); -int strcmpw(const wchar *s1,const wchar *s2); -int strncmpw(const wchar *s1,const wchar *s2,size_t n); -int stricmpw(const wchar *s1,const wchar *s2); -int strnicmpw(const wchar *s1,const wchar *s2,size_t n); -wchar *strchrw(const wchar *s,int c); -wchar* strrchrw(const wchar *s,int c); -wchar* strpbrkw(const wchar *s1,const wchar *s2); -wchar* strlowerw(wchar *Str); -wchar* strupperw(wchar *Str); -wchar* strdupw(const wchar *Str); +int wcsicomp(const wchar *s1,const wchar *s2); +int wcsnicomp(const wchar *s1,const wchar *s2,size_t n); +const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search); +#ifndef SFX_MODULE +wchar* wcslower(wchar *s); +wchar* wcsupper(wchar *s); +#endif int toupperw(int ch); +int tolowerw(int ch); int atoiw(const wchar *s); +int64 atoilw(const wchar *s); #ifdef DBCS_SUPPORTED class SupportDBCS @@ -80,4 +63,5 @@ inline void InitDBCS() {gdbcs.Init();} inline void copychrd(char *dest,const char *src) {*dest=*src;} #endif + #endif diff --git a/libunrar/unios2.cpp b/libunrar/unios2.cpp deleted file mode 100644 index 1261473..0000000 --- a/libunrar/unios2.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// (c) 2001,2004 by Max Alekseyev -// ver. 2.1 - -#include - -#define INCL_DOSMODULEMGR -#include - -typedef void* UconvObject; -typedef unsigned short UniChar; - -int uni_init(int codepage); - -int uni_done(); - -int uni_toucs( /* translate to Unicode */ - char*, /* I - input string */ - size_t, /* I - length of input string (chars) */ - UniChar*, /* O - output Unicode string */ - size_t* ); /* O - length of output string (UniChars) */ - -int uni_fromucs( /* translate from Unicode */ - UniChar*, /* I - input Unicode string */ - size_t, /* I - length of input string (UniChars) */ - char*, /* O - output string */ - size_t* ); /* O - length of output string (chars) */ - -/* IMPLEMENTATION */ - -static int (*uniMapCpToUcsCp) ( - unsigned long, /* I - Codepage to convert */ - UniChar*, /* O - Output buffer */ - size_t ); /* I - UniChars in output buffer */ - -static int (*uniCreateUconvObject) ( - UniChar*, /* I - Unicode name of uconv table */ - UconvObject* );/* O - Uconv object handle */ - -static int (*uniFreeUconvObject) ( - UconvObject ); /* I - Uconv object handle */ - -static int (*uniUconvToUcs) ( - UconvObject, /* I - Uconv object handle */ - void**, /* IO - Input buffer */ - size_t*, /* IO - Input buffer size (bytes) */ - UniChar**, /* IO - Output buffer size */ - size_t*, /* IO - Output size (chars) */ - size_t* ); /* IO - Substitution count */ - -static int (*uniUconvFromUcs) ( - UconvObject, /* I - Uconv object handle */ - UniChar**, /* IO - Input buffer */ - size_t*, /* IO - Input buffer size (bytes) */ - void**, /* IO - Output buffer size */ - size_t*, /* IO - Output size (chars) */ - size_t* ); /* IO - Substitution count */ - -static int uni_ready = 0; -static HMODULE uni_UCONV; -static UconvObject uni_obj; - -int uni_init(int codepage) { - UniChar unistr[256]; - - uni_ready = 0; - - if(!&DosLoadModule) { - /* DOS enviroment detected */ - return -1; - } - - if( DosLoadModule(0,0,(PCSZ)"UCONV",&uni_UCONV) ) { - /* no Unicode API found (obsolete OS/2 version) */ - return -2; - } - - if( !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniMapCpToUcsCp", (PPFN)&uniMapCpToUcsCp ) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniUconvToUcs", (PPFN)&uniUconvToUcs ) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniUconvFromUcs", (PPFN)&uniUconvFromUcs ) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniCreateUconvObject",(PPFN)&uniCreateUconvObject) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniFreeUconvObject", (PPFN)&uniFreeUconvObject ) - ) { - unistr[0] = 0; - if( (!codepage || !uniMapCpToUcsCp(codepage, unistr, 256)) && !uniCreateUconvObject(unistr,&uni_obj) ) { - uni_ready = 1; - return 0; - } - } - DosFreeModule(uni_UCONV); - return -2; -} - -int uni_toucs(char* src, size_t srclen, UniChar* dst, size_t* dstlen) { - size_t srcbytes, srcsize, dstsize, subsc=0; - - if(!uni_ready) return -1; - - dstsize = srcbytes = srclen * sizeof(UniChar); - - if( uniUconvToUcs(uni_obj,(void**)&src,&srclen,&dst,&dstsize,&subsc) ) { - return -1; - } - *dstlen = srcbytes - dstsize; - return 0; -} - -int uni_fromucs(UniChar* src, size_t srclen, char* dst, size_t* dstlen) { - size_t srcbytes, srcsize, dstsize, subsc=0; - - if(!uni_ready) return -1; - - dstsize = srcbytes = *dstlen; - - if( uniUconvFromUcs(uni_obj,&src,&srclen,(void**)&dst,&dstsize,&subsc) ) { - return -1; - } - *dstlen = srcbytes - dstsize; - return 0; -} - -int uni_done() { - if( uni_ready ) { - uniFreeUconvObject(uni_obj); - DosFreeModule(uni_UCONV); - uni_ready = 0; - } - return 0; -} diff --git a/libunrar/unpack.cpp b/libunrar/unpack.cpp index dba61f7..037c355 100644 --- a/libunrar/unpack.cpp +++ b/libunrar/unpack.cpp @@ -3,1013 +3,363 @@ #include "coder.cpp" #include "suballoc.cpp" #include "model.cpp" +#include "unpackinline.cpp" +#ifdef RAR_SMP +#include "unpack50mt.cpp" +#endif #ifndef SFX_MODULE #include "unpack15.cpp" #include "unpack20.cpp" #endif +#include "unpack30.cpp" +#include "unpack50.cpp" +#include "unpack50frag.cpp" Unpack::Unpack(ComprDataIO *DataIO) +:Inp(true),VMCodeInp(true) { UnpIO=DataIO; Window=NULL; - ExternalWindow=false; + Fragmented=false; Suspended=false; UnpAllBuf=false; UnpSomeRead=false; -} - - -Unpack::~Unpack() -{ - if (Window!=NULL && !ExternalWindow) - delete[] Window; - InitFilters(); -} - - -void Unpack::Init(byte *Window) -{ - if (Window==NULL) - { - Unpack::Window=new byte[MAXWINSIZE]; -#ifndef ALLOW_EXCEPTIONS - if (Unpack::Window==NULL) - ErrHandler.MemoryError(); +#ifdef RAR_SMP + MaxUserThreads=1; + UnpThreadPool=NULL; + ReadBufMT=NULL; + UnpThreadData=NULL; #endif - } - else - { - Unpack::Window=Window; - ExternalWindow=true; - } - UnpInitData(false); + MaxWinSize=0; + MaxWinMask=0; + // Perform initialization, which should be done only once for all files. + // It prevents crash if first DoUnpack call is later made with wrong + // (true) 'Solid' value. + UnpInitData(false); #ifndef SFX_MODULE // RAR 1.5 decompression initialization - OldUnpInitData(false); + UnpInitData15(false); InitHuff(); #endif } -void Unpack::DoUnpack(int Method,bool Solid) +Unpack::~Unpack() { + InitFilters30(false); + + if (Window!=NULL) + free(Window); +#ifdef RAR_SMP + delete UnpThreadPool; + delete[] ReadBufMT; + delete[] UnpThreadData; +#endif +} + + +#ifdef RAR_SMP +void Unpack::SetThreads(uint Threads) +{ + // More than 8 threads are unlikely to provide noticeable gain + // for unpacking, but would use the additional memory. + MaxUserThreads=Min(Threads,8); + UnpThreadPool=new ThreadPool(MaxUserThreads); +} +#endif + + +void Unpack::Init(size_t WinSize,bool Solid) +{ + // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size + // will be 0 because of size_t overflow. Let's issue the memory error. + if (WinSize==0) + ErrHandler.MemoryError(); + + // Minimum window size must be at least twice more than maximum possible + // size of filter block, which is 0x10000 in RAR now. If window size is + // smaller, we can have a block with never cleared flt->NextWindow flag + // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's + // use 0x40000 for extra safety and possible filter area size expansion. + const size_t MinAllocSize=0x40000; + if (WinSize>16)>0x10000) // Window size must not exceed 4 GB. + return; + + // Archiving code guarantees that window size does not grow in the same + // solid stream. So if we are here, we are either creating a new window + // or increasing the size of non-solid window. So we could safely reject + // current window data without copying them to a new window, though being + // extra cautious, we still handle the solid window grow case below. + bool Grow=Solid && (Window!=NULL || Fragmented); + + // We do not handle growth for existing fragmented window. + if (Grow && Fragmented) + throw std::bad_alloc(); + + byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize); + + if (NewWindow==NULL) + if (Grow || WinSize<0x1000000) + { + // We do not support growth for new fragmented window. + // Also exclude RAR4 and small dictionaries. + throw std::bad_alloc(); + } + else + { + if (Window!=NULL) // If allocated by preceding files. + { + free(Window); + Window=NULL; + } + FragWindow.Init(WinSize); + Fragmented=true; + } + + if (!Fragmented) + { + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access unused areas of sliding dictionary. + memset(NewWindow,0,WinSize); + + // If Window is not NULL, it means that window size has grown. + // In solid streams we need to copy data to a new window in such case. + // RAR archiving code does not allow it in solid streams now, + // but let's implement it anyway just in case we'll change it sometimes. + if (Grow) + for (size_t I=1;I<=MaxWinSize;I++) + NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)]; + + if (Window!=NULL) + free(Window); + Window=NewWindow; + } + + MaxWinSize=WinSize; + MaxWinMask=MaxWinSize-1; +} + + +void Unpack::DoUnpack(uint Method,bool Solid) +{ + // Methods <50 will crash in Fragmented mode when accessing NULL Window. + // They cannot be called in such mode now, but we check it below anyway + // just for extra safety. switch(Method) { #ifndef SFX_MODULE case 15: // rar 1.5 compression - Unpack15(Solid); + if (!Fragmented) + Unpack15(Solid); break; case 20: // rar 2.x compression case 26: // files larger than 2GB - Unpack20(Solid); + if (!Fragmented) + Unpack20(Solid); break; #endif case 29: // rar 3.x compression - case 36: // alternative hash - Unpack29(Solid); + if (!Fragmented) + Unpack29(Solid); + break; + case 50: // RAR 5.0 compression algorithm. +#ifdef RAR_SMP + if (MaxUserThreads>1) + { +// We do not use the multithreaded unpack routine to repack RAR archives +// in 'suspended' mode, because unlike the single threaded code it can +// write more than one dictionary for same loop pass. So we would need +// larger buffers of unknown size. Also we do not support multithreading +// in fragmented window mode. + if (!Fragmented) + { + Unpack5MT(Solid); + break; + } + } +#endif + Unpack5(Solid); break; } } -inline void Unpack::InsertOldDist(unsigned int Distance) -{ - OldDist[3]=OldDist[2]; - OldDist[2]=OldDist[1]; - OldDist[1]=OldDist[0]; - OldDist[0]=Distance; -} - - -inline void Unpack::InsertLastMatch(unsigned int Length,unsigned int Distance) -{ - LastDist=Distance; - LastLength=Length; -} - - -void Unpack::CopyString(unsigned int Length,unsigned int Distance) -{ - unsigned int DestPtr=UnpPtr-Distance; - if (DestPtr0) - Window[UnpPtr++]=Window[DestPtr++]; - } - else - while (Length--) - { - Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK]; - UnpPtr=(UnpPtr+1) & MAXWINMASK; - } -} - - -int Unpack::DecodeNumber(struct Decode *Dec) -{ - unsigned int Bits; - unsigned int BitField=getbits() & 0xfffe; - if (BitFieldDecodeLen[8]) - if (BitFieldDecodeLen[4]) - if (BitFieldDecodeLen[2]) - if (BitFieldDecodeLen[1]) - Bits=1; - else - Bits=2; - else - if (BitFieldDecodeLen[3]) - Bits=3; - else - Bits=4; - else - if (BitFieldDecodeLen[6]) - if (BitFieldDecodeLen[5]) - Bits=5; - else - Bits=6; - else - if (BitFieldDecodeLen[7]) - Bits=7; - else - Bits=8; - else - if (BitFieldDecodeLen[12]) - if (BitFieldDecodeLen[10]) - if (BitFieldDecodeLen[9]) - Bits=9; - else - Bits=10; - else - if (BitFieldDecodeLen[11]) - Bits=11; - else - Bits=12; - else - if (BitFieldDecodeLen[14]) - if (BitFieldDecodeLen[13]) - Bits=13; - else - Bits=14; - else - Bits=15; - - addbits(Bits); - unsigned int N=Dec->DecodePos[Bits]+((BitField-Dec->DecodeLen[Bits-1])>>(16-Bits)); - if (N>=Dec->MaxNum) - N=0; - return(Dec->DecodeNum[N]); -} - - -void Unpack::Unpack29(bool Solid) -{ - static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; - static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - static int DDecode[DC]; - static byte DBits[DC]; - static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; - static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; - static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; - unsigned int Bits; - - if (DDecode[1]==0) - { - int Dist=0,BitLength=0,Slot=0; - for (int I=0;IReadBorder) - { - if (!UnpReadBuf()) - break; - } - if (((WrPtr-UnpPtr) & MAXWINMASK)<260 && WrPtr!=UnpPtr) - { - UnpWriteBuf(); - if (WrittenFileSize>DestUnpSize) - return; - if (Suspended) - { - FileExtracted=false; - return; - } - } - if (UnpBlockType==BLOCK_PPM) - { - int Ch=PPM.DecodeChar(); - if (Ch==-1) - { - PPM.CleanUp(); - - // turn off PPM compression mode in case of error, so UnRAR will - // call PPM.DecodeInit in case it needs to turn it on back later. - UnpBlockType=BLOCK_LZ; - break; - } - if (Ch==PPMEscChar) - { - int NextCh=PPM.DecodeChar(); - if (NextCh==0) - { - if (!ReadTables()) - break; - continue; - } - if (NextCh==2 || NextCh==-1) - break; - if (NextCh==3) - { - if (!ReadVMCodePPM()) - break; - continue; - } - if (NextCh==4) - { - unsigned int Distance=0,Length; - bool Failed=false; - for (int I=0;I<4 && !Failed;I++) - { - int Ch=PPM.DecodeChar(); - if (Ch==-1) - Failed=true; - else - if (I==3) - Length=(byte)Ch; - else - Distance=(Distance<<8)+(byte)Ch; - } - if (Failed) - break; - - CopyString(Length+32,Distance+2); - continue; - } - if (NextCh==5) - { - int Length=PPM.DecodeChar(); - if (Length==-1) - break; - CopyString(Length+4,1); - continue; - } - } - Window[UnpPtr++]=Ch; - continue; - } - - int Number=DecodeNumber((struct Decode *)&LD); - if (Number<256) - { - Window[UnpPtr++]=(byte)Number; - continue; - } - if (Number>=271) - { - int Length=LDecode[Number-=271]+3; - if ((Bits=LBits[Number])>0) - { - Length+=getbits()>>(16-Bits); - addbits(Bits); - } - - int DistNumber=DecodeNumber((struct Decode *)&DD); - unsigned int Distance=DDecode[DistNumber]+1; - if ((Bits=DBits[DistNumber])>0) - { - if (DistNumber>9) - { - if (Bits>4) - { - Distance+=((getbits()>>(20-Bits))<<4); - addbits(Bits-4); - } - if (LowDistRepCount>0) - { - LowDistRepCount--; - Distance+=PrevLowDist; - } - else - { - int LowDist=DecodeNumber((struct Decode *)&LDD); - if (LowDist==16) - { - LowDistRepCount=LOW_DIST_REP_COUNT-1; - Distance+=PrevLowDist; - } - else - { - Distance+=LowDist; - PrevLowDist=LowDist; - } - } - } - else - { - Distance+=getbits()>>(16-Bits); - addbits(Bits); - } - } - - if (Distance>=0x2000) - { - Length++; - if (Distance>=0x40000L) - Length++; - } - - InsertOldDist(Distance); - InsertLastMatch(Length,Distance); - CopyString(Length,Distance); - continue; - } - if (Number==256) - { - if (!ReadEndOfBlock()) - break; - continue; - } - if (Number==257) - { - if (!ReadVMCode()) - break; - continue; - } - if (Number==258) - { - if (LastLength!=0) - CopyString(LastLength,LastDist); - continue; - } - if (Number<263) - { - int DistNum=Number-259; - unsigned int Distance=OldDist[DistNum]; - for (int I=DistNum;I>0;I--) - OldDist[I]=OldDist[I-1]; - OldDist[0]=Distance; - - int LengthNumber=DecodeNumber((struct Decode *)&RD); - int Length=LDecode[LengthNumber]+2; - if ((Bits=LBits[LengthNumber])>0) - { - Length+=getbits()>>(16-Bits); - addbits(Bits); - } - InsertLastMatch(Length,Distance); - CopyString(Length,Distance); - continue; - } - if (Number<272) - { - unsigned int Distance=SDDecode[Number-=263]+1; - if ((Bits=SDBits[Number])>0) - { - Distance+=getbits()>>(16-Bits); - addbits(Bits); - } - InsertOldDist(Distance); - InsertLastMatch(2,Distance); - CopyString(2,Distance); - continue; - } - } - UnpWriteBuf(); -} - - -bool Unpack::ReadEndOfBlock() -{ - unsigned int BitField=getbits(); - bool NewTable,NewFile=false; - if (BitField & 0x8000) - { - NewTable=true; - addbits(1); - } - else - { - NewFile=true; - NewTable=(BitField & 0x4000)!=0; - addbits(2); - } - TablesRead=!NewTable; - return !(NewFile || NewTable && !ReadTables()); -} - - -bool Unpack::ReadVMCode() -{ - unsigned int FirstByte=getbits()>>8; - addbits(8); - int Length=(FirstByte & 7)+1; - if (Length==7) - { - Length=(getbits()>>8)+7; - addbits(8); - } - else - if (Length==8) - { - Length=getbits(); - addbits(16); - } - Array VMCode(Length); - for (int I=0;I=ReadTop-1 && !UnpReadBuf() && I>8; - addbits(8); - } - return(AddVMCode(FirstByte,&VMCode[0],Length)); -} - - -bool Unpack::ReadVMCodePPM() -{ - unsigned int FirstByte=PPM.DecodeChar(); - if ((int)FirstByte==-1) - return(false); - int Length=(FirstByte & 7)+1; - if (Length==7) - { - int B1=PPM.DecodeChar(); - if (B1==-1) - return(false); - Length=B1+7; - } - else - if (Length==8) - { - int B1=PPM.DecodeChar(); - if (B1==-1) - return(false); - int B2=PPM.DecodeChar(); - if (B2==-1) - return(false); - Length=B1*256+B2; - } - Array VMCode(Length); - for (int I=0;IFilters.Size() || FiltPos>OldFilterLengths.Size()) - return(false); - LastFilter=FiltPos; - bool NewFilter=(FiltPos==Filters.Size()); - - UnpackFilter *StackFilter=new UnpackFilter; // new filter for PrgStack - - UnpackFilter *Filter; - if (NewFilter) // new filter code, never used before since VM reset - { - // too many different filters, corrupt archive - if (FiltPos>1024) - return(false); - - Filters.Add(1); - Filters[Filters.Size()-1]=Filter=new UnpackFilter; - StackFilter->ParentFilter=(uint)(Filters.Size()-1); - OldFilterLengths.Add(1); - Filter->ExecCount=0; - } - else // filter was used in the past - { - Filter=Filters[FiltPos]; - StackFilter->ParentFilter=FiltPos; - Filter->ExecCount++; - } - - int EmptyCount=0; - for (uint I=0;I0) - PrgStack[I]=NULL; - } - if (EmptyCount==0) - { - PrgStack.Add(1); - EmptyCount=1; - } - int StackPos=(int)(PrgStack.Size()-EmptyCount); - PrgStack[StackPos]=StackFilter; - StackFilter->ExecCount=Filter->ExecCount; - - uint BlockStart=RarVM::ReadData(Inp); - if (FirstByte & 0x40) - BlockStart+=258; - StackFilter->BlockStart=(BlockStart+UnpPtr)&MAXWINMASK; - if (FirstByte & 0x20) - StackFilter->BlockLength=RarVM::ReadData(Inp); - else - StackFilter->BlockLength=FiltPosNextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MAXWINMASK)<=BlockStart; - -// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); - - OldFilterLengths[FiltPos]=StackFilter->BlockLength; - - memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); - StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR; - StackFilter->Prg.InitR[4]=StackFilter->BlockLength; - StackFilter->Prg.InitR[5]=StackFilter->ExecCount; - - if (FirstByte & 0x10) // set registers to optional parameters if any - { - unsigned int InitMask=Inp.fgetbits()>>9; - Inp.faddbits(7); - for (int I=0;I<7;I++) - if (InitMask & (1<Prg.InitR[I]=RarVM::ReadData(Inp); - } - - if (NewFilter) - { - uint VMCodeSize=RarVM::ReadData(Inp); - if (VMCodeSize>=0x10000 || VMCodeSize==0) - return(false); - Array VMCode(VMCodeSize); - for (uint I=0;I>8; - Inp.faddbits(8); - } - VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); - } - StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0]; - StackFilter->Prg.CmdCount=Filter->Prg.CmdCount; - - size_t StaticDataSize=Filter->Prg.StaticData.Size(); - if (StaticDataSize>0 && StaticDataSizePrg.StaticData.Add(StaticDataSize); - memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize); - } - - if (StackFilter->Prg.GlobalData.Size()Prg.GlobalData.Reset(); - StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE); - } - byte *GlobalData=&StackFilter->Prg.GlobalData[0]; - for (int I=0;I<7;I++) - VM.SetLowEndianValue((uint *)&GlobalData[I*4],StackFilter->Prg.InitR[I]); - VM.SetLowEndianValue((uint *)&GlobalData[0x1c],StackFilter->BlockLength); - VM.SetLowEndianValue((uint *)&GlobalData[0x20],0); - VM.SetLowEndianValue((uint *)&GlobalData[0x2c],StackFilter->ExecCount); - memset(&GlobalData[0x30],0,16); - - if (FirstByte & 8) // put data block passed as parameter if any - { - if (Inp.Overflow(3)) - return(false); - uint DataSize=RarVM::ReadData(Inp); - if (DataSize>VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE) - return(false); - size_t CurSize=StackFilter->Prg.GlobalData.Size(); - if (CurSizePrg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize); - byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE]; - for (uint I=0;I>8; - Inp.faddbits(8); - } - } - return(true); -} - - -bool Unpack::UnpReadBuf() -{ - int DataSize=ReadTop-InAddr; // Data left to process. - if (DataSize<0) - return(false); - if (InAddr>BitInput::MAX_SIZE/2) - { - // If we already processed more than half of buffer, let's move - // remaining data into beginning to free more space for new data. - if (DataSize>0) - memmove(InBuf,InBuf+InAddr,DataSize); - InAddr=0; - ReadTop=DataSize; - } - else - DataSize=ReadTop; - int ReadCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf); - if (ReadCode>0) - ReadTop+=ReadCode; - ReadBorder=ReadTop-30; - return(ReadCode!=-1); -} - - -void Unpack::UnpWriteBuf() -{ - unsigned int WrittenBorder=WrPtr; - unsigned int WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK; - for (size_t I=0;INextWindow) - { - flt->NextWindow=false; - continue; - } - unsigned int BlockStart=flt->BlockStart; - unsigned int BlockLength=flt->BlockLength; - if (((BlockStart-WrittenBorder)&MAXWINMASK)ParentFilter]->Prg; - VM_PreparedProgram *Prg=&flt->Prg; - - if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // Copy global data from previous script execution if any. - Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); - memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } - - ExecuteCode(Prg); - - if (Prg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // Save global data for next script execution. - if (ParentPrg->GlobalData.Size()GlobalData.Size()) - ParentPrg->GlobalData.Alloc(Prg->GlobalData.Size()); - memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } - else - ParentPrg->GlobalData.Reset(); - - byte *FilteredData=Prg->FilteredData; - unsigned int FilteredDataSize=Prg->FilteredDataSize; - - delete PrgStack[I]; - PrgStack[I]=NULL; - while (I+1BlockStart!=BlockStart || - NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) - break; - - // Apply several filters to same data block. - - VM.SetMemory(0,FilteredData,FilteredDataSize); - - VM_PreparedProgram *ParentPrg=&Filters[NextFilter->ParentFilter]->Prg; - VM_PreparedProgram *NextPrg=&NextFilter->Prg; - - if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // Copy global data from previous script execution if any. - NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); - memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } - - ExecuteCode(NextPrg); - - if (NextPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // Save global data for next script execution. - if (ParentPrg->GlobalData.Size()GlobalData.Size()) - ParentPrg->GlobalData.Alloc(NextPrg->GlobalData.Size()); - memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } - else - ParentPrg->GlobalData.Reset(); - - FilteredData=NextPrg->FilteredData; - FilteredDataSize=NextPrg->FilteredDataSize; - I++; - delete PrgStack[I]; - PrgStack[I]=NULL; - } - UnpIO->UnpWrite(FilteredData,FilteredDataSize); - UnpSomeRead=true; - WrittenFileSize+=FilteredDataSize; - WrittenBorder=BlockEnd; - WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK; - } - else - { - for (size_t J=I;JNextWindow) - flt->NextWindow=false; - } - WrPtr=WrittenBorder; - return; - } - } - } - - UnpWriteArea(WrittenBorder,UnpPtr); - WrPtr=UnpPtr; -} - - -void Unpack::ExecuteCode(VM_PreparedProgram *Prg) -{ - if (Prg->GlobalData.Size()>0) - { - Prg->InitR[6]=(uint)WrittenFileSize; - VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x24],(uint)WrittenFileSize); - VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x28],(uint)(WrittenFileSize>>32)); - VM.Execute(Prg); - } -} - - -void Unpack::UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr) -{ - if (EndPtr!=StartPtr) - UnpSomeRead=true; - if (EndPtr=DestUnpSize) - return; - size_t WriteSize=Size; - int64 LeftToWrite=DestUnpSize-WrittenFileSize; - if ((int64)WriteSize>LeftToWrite) - WriteSize=(size_t)LeftToWrite; - UnpIO->UnpWrite(Data,WriteSize); - WrittenFileSize+=Size; -} - - -bool Unpack::ReadTables() -{ - byte BitLength[BC]; - unsigned char Table[HUFF_TABLE_SIZE]; - if (InAddr>ReadTop-25) - if (!UnpReadBuf()) - return(false); - faddbits((8-InBit)&7); - unsigned int BitField=fgetbits(); - if (BitField & 0x8000) - { - UnpBlockType=BLOCK_PPM; - return(PPM.DecodeInit(this,PPMEscChar)); - } - UnpBlockType=BLOCK_LZ; - - PrevLowDist=0; - LowDistRepCount=0; - - if (!(BitField & 0x4000)) - memset(UnpOldTable,0,sizeof(UnpOldTable)); - faddbits(2); - - for (int I=0;I> 12); - faddbits(4); - if (Length==15) - { - int ZeroCount=(byte)(fgetbits() >> 12); - faddbits(4); - if (ZeroCount==0) - BitLength[I]=15; - else - { - ZeroCount+=2; - while (ZeroCount-- > 0 && IReadTop-5) - if (!UnpReadBuf()) - return(false); - int Number=DecodeNumber((struct Decode *)&BD); - if (Number<16) - { - Table[I]=(Number+UnpOldTable[I]) & 0xf; - I++; - } - else - if (Number<18) - { - int N; - if (Number==16) - { - N=(fgetbits() >> 13)+3; - faddbits(3); - } - else - { - N=(fgetbits() >> 9)+11; - faddbits(7); - } - while (N-- > 0 && I> 13)+3; - faddbits(3); - } - else - { - N=(fgetbits() >> 9)+11; - faddbits(7); - } - while (N-- > 0 && IReadTop) - return(false); - MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC); - MakeDecodeTables(&Table[NC],(struct Decode *)&DD,DC); - MakeDecodeTables(&Table[NC+DC],(struct Decode *)&LDD,LDC); - MakeDecodeTables(&Table[NC+DC+LDC],(struct Decode *)&RD,RC); - memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); - return(true); -} - - -void Unpack::UnpInitData(int Solid) +void Unpack::UnpInitData(bool Solid) { if (!Solid) { - TablesRead=false; memset(OldDist,0,sizeof(OldDist)); OldDistPtr=0; LastDist=LastLength=0; -// memset(Window,0,MAXWINSIZE); - memset(UnpOldTable,0,sizeof(UnpOldTable)); - memset(&LD,0,sizeof(LD)); - memset(&DD,0,sizeof(DD)); - memset(&LDD,0,sizeof(LDD)); - memset(&RD,0,sizeof(RD)); - memset(&BD,0,sizeof(BD)); +// memset(Window,0,MaxWinSize); + memset(&BlockTables,0,sizeof(BlockTables)); UnpPtr=WrPtr=0; - PPMEscChar=2; - UnpBlockType=BLOCK_LZ; - - InitFilters(); + WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; } - InitBitInput(); + // Filters never share several solid files, so we can safely reset them + // even in solid archive. + InitFilters(); + + Inp.InitBitInput(); WrittenFileSize=0; ReadTop=0; ReadBorder=0; + + memset(&BlockHeader,0,sizeof(BlockHeader)); + BlockHeader.BlockSize=-1; // '-1' means not defined yet. #ifndef SFX_MODULE UnpInitData20(Solid); #endif + UnpInitData30(Solid); + UnpInitData50(Solid); } -void Unpack::InitFilters() +// LengthTable contains the length in bits for every element of alphabet. +// Dec is the structure to decode Huffman code/ +// Size is size of length table and DecodeNum field in Dec structure, +void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) { - OldFilterLengths.Reset(); - LastFilter=0; + // Size of alphabet and DecodePos array. + Dec->MaxNum=Size; - for (size_t I=0;IDecodeNum,0,Size*sizeof(*Dec->DecodeNum)); - for (I=0;IDecodePos[0]=Dec->DecodeLen[0]=0,N=0,I=1;I<16;I++) + // Initialize not really used entry for zero length code. + Dec->DecodePos[0]=0; + + // Start code for bit length 1 is 0. + Dec->DecodeLen[0]=0; + + // Right aligned upper limit code for current bit length. + uint UpperLimit=0; + + for (size_t I=1;I<16;I++) { - N=2*(N+LenCount[I]); - M=N<<(15-I); - if (M>0xFFFF) - M=0xFFFF; - Dec->DecodeLen[I]=(unsigned int)M; - TmpPos[I]=Dec->DecodePos[I]=Dec->DecodePos[I-1]+LenCount[I-1]; + // Adjust the upper limit code. + UpperLimit+=LengthCount[I]; + + // Left aligned upper limit code. + uint LeftAligned=UpperLimit<<(16-I); + + // Prepare the upper limit code for next bit length. + UpperLimit*=2; + + // Store the left aligned upper limit code. + Dec->DecodeLen[I]=(uint)LeftAligned; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1]; } - for (I=0;IDecodeNum[TmpPos[LenTab[I] & 0xF]++]=I; - Dec->MaxNum=Size; + // Prepare the copy of DecodePos. We'll modify this copy below, + // so we cannot use the original DecodePos. + uint CopyDecodePos[ASIZE(Dec->DecodePos)]; + memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); + + // For every bit length in the bit length table and so for every item + // of alphabet. + for (uint I=0;IDecodeNum[LastPos]=(ushort)I; + + // We'll use next position number for this bit length next time. + // So we pass through the entire range of positions available + // for every bit length. + CopyDecodePos[CurBitLength]++; + } + } + + // Define the number of bits to process in quick mode. We use more bits + // for larger alphabets. More bits means that more codes will be processed + // in quick mode, but also that more time will be spent to preparation + // of tables for quick decode. + switch (Size) + { + case NC: + case NC20: + case NC30: + Dec->QuickBits=MAX_QUICK_DECODE_BITS; + break; + default: + Dec->QuickBits=MAX_QUICK_DECODE_BITS-3; + break; + } + + // Size of tables for quick mode. + uint QuickDataSize=1<QuickBits; + + // Bit length for current code, start from 1 bit codes. It is important + // to use 1 bit instead of 0 for minimum code length, so we are moving + // forward even when processing a corrupt archive. + uint CurBitLength=1; + + // For every right aligned bit string which supports the quick decoding. + for (uint Code=0;CodeQuickBits); + + // Prepare the table for quick decoding of bit lengths. + + // Find the upper limit for current bit field and adjust the bit length + // accordingly if necessary. + while (CurBitLengthDecodeLen) && BitField>=Dec->DecodeLen[CurBitLength]) + CurBitLength++; + + // Translation of right aligned bit string to bit length. + Dec->QuickLen[Code]=CurBitLength; + + // Prepare the table for quick translation of position in code list + // to position in alphabet. + + // Calculate the distance from the start code for current bit length. + uint Dist=BitField-Dec->DecodeLen[CurBitLength-1]; + + // Right align the distance. + Dist>>=(16-CurBitLength); + + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + uint Pos; + if (CurBitLengthDecodePos) && + (Pos=Dec->DecodePos[CurBitLength]+Dist)QuickNum[Code]=Dec->DecodeNum[Pos]; + } + else + { + // Can be here for length table filled with zeroes only (empty). + Dec->QuickNum[Code]=0; + } + } } diff --git a/libunrar/unpack.hpp b/libunrar/unpack.hpp index 329b5cf..75dadb0 100644 --- a/libunrar/unpack.hpp +++ b/libunrar/unpack.hpp @@ -1,80 +1,173 @@ #ifndef _RAR_UNPACK_ #define _RAR_UNPACK_ -enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM}; +// Maximum allowed number of compressed bits processed in quick mode. +#define MAX_QUICK_DECODE_BITS 10 -struct Decode +// Maximum number of filters per entire data block. Must be at least +// twice more than MAX_PACK_FILTERS to store filters from two data blocks. +#define MAX_UNPACK_FILTERS 8192 + +// Maximum number of filters per entire data block for RAR3 unpack. +// Must be at least twice more than v3_MAX_PACK_FILTERS to store filters +// from two data blocks. +#define MAX3_UNPACK_FILTERS 8192 + +// Limit maximum number of channels in RAR3 delta filter to some reasonable +// value to prevent too slow processing of corrupt archives with invalid +// channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS. +// No need to provide it for RAR5, which uses only 5 bits to store channels. +#define MAX3_UNPACK_CHANNELS 1024 + +// Maximum size of single filter block. We restrict it to limit memory +// allocation. Must be equal or larger than MAX_ANALYZE_SIZE. +#define MAX_FILTER_BLOCK_SIZE 0x400000 + +// Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, +// so we keep a number of buffered filters in unpacker reasonable. +#define UNPACK_MAX_WRITE 0x400000 + +// Decode compressed bit fields to alphabet numbers. +struct DecodeTable:PackDef { - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[2]; + // Real size of DecodeNum table. + uint MaxNum; + + // Left aligned start and upper limit codes defining code space + // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of + // range for bit length and DecodeLen[BitLength] defines next code + // after the end of range or in other words the upper limit code + // for specified bit length. + uint DecodeLen[16]; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + uint DecodePos[16]; + + // Number of compressed bits processed in quick mode. + // Must not exceed MAX_QUICK_DECODE_BITS. + uint QuickBits; + + // Translates compressed bits (up to QuickBits length) + // to bit length in quick mode. + byte QuickLen[1< Filters; + ThreadPool *UnpThreadPool; + UnpackThreadData *UnpThreadData; + uint MaxUserThreads; + byte *ReadBufMT; +#endif - /* Filters stack, several entrances of same filter are possible */ - Array PrgStack; + Array FilterSrcMemory; + Array FilterDstMemory; - /* lengths of preceding blocks, one length per filter. Used to reduce - size required to write block length if lengths are repeating */ - Array OldFilterLengths; + // Filters code, one entry per filter. + Array Filters; - int LastFilter; + uint OldDist[4],OldDistPtr; + uint LastLength; - bool TablesRead; - struct LitDecode LD; - struct DistDecode DD; - struct LowDistDecode LDD; - struct RepDecode RD; - struct BitDecode BD; + // LastDist is necessary only for RAR2 and older with circular OldDist + // array. In RAR3 last distance is always stored in OldDist[0]. + uint LastDist; - unsigned int OldDist[4],OldDistPtr; - unsigned int LastDist,LastLength; - - unsigned int UnpPtr,WrPtr; + size_t UnpPtr,WrPtr; // Top border of read packed data. int ReadTop; @@ -149,12 +263,15 @@ class Unpack:private BitInput // unless we are at the end of file. int ReadBorder; - unsigned char UnpOldTable[HUFF_TABLE_SIZE]; + UnpackBlockHeader BlockHeader; + UnpackBlockTables BlockTables; - int UnpBlockType; + size_t WriteBorder; byte *Window; - bool ExternalWindow; + + FragmentedWindow FragWindow; + bool Fragmented; int64 DestUnpSize; @@ -165,7 +282,6 @@ class Unpack:private BitInput int64 WrittenFileSize; bool FileExtracted; - int PrevLowDist,LowDistRepCount; /***************************** Unpack v 1.5 *********************************/ void Unpack15(bool Solid); @@ -173,48 +289,115 @@ class Unpack:private BitInput void LongLZ(); void HuffDecode(); void GetFlagsBuf(); - void OldUnpInitData(int Solid); + void UnpInitData15(int Solid); void InitHuff(); - void CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace); - void OldCopyString(unsigned int Distance,unsigned int Length); + void CorrHuff(ushort *CharSet,byte *NumToPlace); + void CopyString15(uint Distance,uint Length); uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); - void OldUnpWriteBuf(); - unsigned int ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256]; - unsigned int Place[256],PlaceA[256],PlaceB[256],PlaceC[256]; - unsigned int NToPl[256],NToPlB[256],NToPlC[256]; - unsigned int FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; + ushort ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256]; + byte NToPl[256],NToPlB[256],NToPlC[256]; + uint FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; int Buf60,NumHuf,StMode,LCount,FlagsCnt; - unsigned int Nhfb,Nlzb,MaxDist3; + uint Nhfb,Nlzb,MaxDist3; /***************************** Unpack v 1.5 *********************************/ /***************************** Unpack v 2.0 *********************************/ void Unpack20(bool Solid); - struct MultDecode MD[4]; + + DecodeTable MD[4]; // Decode multimedia data, up to 4 channels. + unsigned char UnpOldTable20[MC20*4]; - int UnpAudioBlock,UnpChannels,UnpCurChannel,UnpChannelDelta; - void CopyString20(unsigned int Length,unsigned int Distance); + bool UnpAudioBlock; + uint UnpChannels,UnpCurChannel; + int UnpChannelDelta; + void CopyString20(uint Length,uint Distance); bool ReadTables20(); + void UnpWriteBuf20(); void UnpInitData20(int Solid); void ReadLastTables(); byte DecodeAudio(int Delta); struct AudioVariables AudV[4]; /***************************** Unpack v 2.0 *********************************/ +/***************************** Unpack v 3.0 *********************************/ + enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM}; + + void UnpInitData30(bool Solid); + void Unpack29(bool Solid); + void InitFilters30(bool Solid); + bool ReadEndOfBlock(); + bool ReadVMCode(); + bool ReadVMCodePPM(); + bool AddVMCode(uint FirstByte,byte *Code,uint CodeSize); + int SafePPMDecodeChar(); + bool ReadTables30(); + bool UnpReadBuf30(); + void UnpWriteBuf30(); + void ExecuteCode(VM_PreparedProgram *Prg); + + int PrevLowDist,LowDistRepCount; + + ModelPPM PPM; + int PPMEscChar; + + byte UnpOldTable[HUFF_TABLE_SIZE30]; + int UnpBlockType; + + // If we already read decoding tables for Unpack v2,v3,v5. + // We should not use a single variable for all algorithm versions, + // because we can have a corrupt archive with one algorithm file + // followed by another algorithm file with "solid" flag and we do not + // want to reuse tables from one algorithm in another. + bool TablesRead2,TablesRead3,TablesRead5; + + // Virtual machine to execute filters code. + RarVM VM; + + // Buffer to read VM filters code. We moved it here from AddVMCode + // function to reduce time spent in BitInput constructor. + BitInput VMCodeInp; + + // Filters code, one entry per filter. + Array Filters30; + + // Filters stack, several entrances of same filter are possible. + Array PrgStack; + + // Lengths of preceding data blocks, one length of one last block + // for every filter. Used to reduce the size required to write + // the data block length if lengths are repeating. + Array OldFilterLengths; + + int LastFilter; +/***************************** Unpack v 3.0 *********************************/ + public: Unpack(ComprDataIO *DataIO); ~Unpack(); - void Init(byte *Window=NULL); - void DoUnpack(int Method,bool Solid); + void Init(size_t WinSize,bool Solid); + void DoUnpack(uint Method,bool Solid); bool IsFileExtracted() {return(FileExtracted);} void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} void SetSuspended(bool Suspended) {Unpack::Suspended=Suspended;} - unsigned int GetChar() +#ifdef RAR_SMP + void SetThreads(uint Threads); + void UnpackDecode(UnpackThreadData &D); +#endif + + size_t MaxWinSize; + size_t MaxWinMask; + + uint GetChar() { - if (InAddr>BitInput::MAX_SIZE-30) + if (Inp.InAddr>BitInput::MAX_SIZE-30) + { UnpReadBuf(); - return(InBuf[InAddr++]); + if (Inp.InAddr>=BitInput::MAX_SIZE) // If nothing was read. + return 0; + } + return Inp.InBuf[Inp.InAddr++]; } }; diff --git a/libunrar/unpack15.cpp b/libunrar/unpack15.cpp index dd05788..1e7cf76 100644 --- a/libunrar/unpack15.cpp +++ b/libunrar/unpack15.cpp @@ -39,22 +39,17 @@ static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0}; void Unpack::Unpack15(bool Solid) { - if (Suspended) - UnpPtr=WrPtr; - else + UnpInitData(Solid); + UnpInitData15(Solid); + UnpReadBuf(); + if (!Solid) { - UnpInitData(Solid); - OldUnpInitData(Solid); - UnpReadBuf(); - if (!Solid) - { - InitHuff(); - UnpPtr=0; - } - else - UnpPtr=WrPtr; - --DestUnpSize; + InitHuff(); + UnpPtr=0; } + else + UnpPtr=WrPtr; + --DestUnpSize; if (DestUnpSize>=0) { GetFlagsBuf(); @@ -63,16 +58,12 @@ void Unpack::Unpack15(bool Solid) while (DestUnpSize>=0) { - UnpPtr&=MAXWINMASK; + UnpPtr&=MaxWinMask; - if (InAddr>ReadTop-30 && !UnpReadBuf()) + if (Inp.InAddr>ReadTop-30 && !UnpReadBuf()) break; - if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr) - { - OldUnpWriteBuf(); - if (Suspended) - return; - } + if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) + UnpWriteBuf20(); if (StMode) { HuffDecode(); @@ -116,23 +107,7 @@ void Unpack::Unpack15(bool Solid) } } } - OldUnpWriteBuf(); -} - - -void Unpack::OldUnpWriteBuf() -{ - if (UnpPtr!=WrPtr) - UnpSomeRead=true; - if (UnpPtrUnpWrite(&Window[WrPtr],-(int)WrPtr & MAXWINMASK); - UnpIO->UnpWrite(Window,UnpPtr); - UnpAllBuf=true; - } - else - UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); - WrPtr=UnpPtr; + UnpWriteBuf20(); } @@ -155,13 +130,13 @@ void Unpack::ShortLZ() int DistancePlace; NumHuf=0; - unsigned int BitField=fgetbits(); + unsigned int BitField=Inp.fgetbits(); if (LCount==2) { - faddbits(1); + Inp.faddbits(1); if (BitField >= 0x8000) { - OldCopyString((unsigned int)LastDist,LastLength); + CopyString15((unsigned int)LastDist,LastLength); return; } BitField <<= 1; @@ -178,14 +153,14 @@ void Unpack::ShortLZ() for (Length=0;;Length++) if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0) break; - faddbits(GetShortLen1(Length)); + Inp.faddbits(GetShortLen1(Length)); } else { for (Length=0;;Length++) if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0) break; - faddbits(GetShortLen2(Length)); + Inp.faddbits(GetShortLen2(Length)); } if (Length >= 9) @@ -193,25 +168,25 @@ void Unpack::ShortLZ() if (Length == 9) { LCount++; - OldCopyString((unsigned int)LastDist,LastLength); + CopyString15((unsigned int)LastDist,LastLength); return; } if (Length == 14) { LCount=0; - Length=DecodeNum(fgetbits(),STARTL2,DecL2,PosL2)+5; - Distance=(fgetbits()>>1) | 0x8000; - faddbits(15); + Length=DecodeNum(Inp.fgetbits(),STARTL2,DecL2,PosL2)+5; + Distance=(Inp.fgetbits()>>1) | 0x8000; + Inp.faddbits(15); LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); return; } LCount=0; SaveLength=Length; Distance=OldDist[(OldDistPtr-(Length-9)) & 3]; - Length=DecodeNum(fgetbits(),STARTL1,DecL1,PosL1)+2; + Length=DecodeNum(Inp.fgetbits(),STARTL1,DecL1,PosL1)+2; if (Length==0x101 && SaveLength==10) { Buf60 ^= 1; @@ -226,7 +201,7 @@ void Unpack::ShortLZ() OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); return; } @@ -234,13 +209,11 @@ void Unpack::ShortLZ() AvrLn1 += Length; AvrLn1 -= AvrLn1 >> 4; - DistancePlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; + DistancePlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; Distance=ChSetA[DistancePlace]; if (--DistancePlace != -1) { - PlaceA[Distance]--; LastDistance=ChSetA[DistancePlace]; - PlaceA[LastDistance]++; ChSetA[DistancePlace+1]=LastDistance; ChSetA[DistancePlace]=Distance; } @@ -249,7 +222,7 @@ void Unpack::ShortLZ() OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); } @@ -269,7 +242,7 @@ void Unpack::LongLZ() } OldAvr2=AvrLn2; - unsigned int BitField=fgetbits(); + unsigned int BitField=Inp.fgetbits(); if (AvrLn2 >= 122) Length=DecodeNum(BitField,STARTL2,DecL2,PosL2); else @@ -279,19 +252,19 @@ void Unpack::LongLZ() if (BitField < 0x100) { Length=BitField; - faddbits(16); + Inp.faddbits(16); } else { for (Length=0;((BitField<> 5; - BitField=fgetbits(); + BitField=Inp.fgetbits(); if (AvrPlcB > 0x28ff) DistancePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); else @@ -312,11 +285,11 @@ void Unpack::LongLZ() break; } - ChSetB[DistancePlace]=ChSetB[NewDistancePlace]; + ChSetB[DistancePlace & 0xff]=ChSetB[NewDistancePlace]; ChSetB[NewDistancePlace]=Distance; - Distance=((Distance & 0xff00) | (fgetbits() >> 8)) >> 1; - faddbits(7); + Distance=((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; + Inp.faddbits(7); OldAvr3=AvrLn3; if (Length!=1 && Length!=4) @@ -341,7 +314,7 @@ void Unpack::LongLZ() OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); } @@ -352,7 +325,7 @@ void Unpack::HuffDecode() unsigned int Distance; int BytePlace; - unsigned int BitField=fgetbits(); + unsigned int BitField=Inp.fgetbits(); if (AvrPlc > 0x75ff) BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); @@ -374,8 +347,8 @@ void Unpack::HuffDecode() BytePlace=0x100; if (--BytePlace==-1) { - BitField=fgetbits(); - faddbits(1); + BitField=Inp.fgetbits(); + Inp.faddbits(1); if (BitField & 0x8000) { NumHuf=StMode=0; @@ -384,11 +357,11 @@ void Unpack::HuffDecode() else { Length = (BitField & 0x4000) ? 4 : 3; - faddbits(1); - Distance=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2); - Distance = (Distance << 5) | (fgetbits() >> 11); - faddbits(5); - OldCopyString(Distance,Length); + Inp.faddbits(1); + Distance=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); + Distance = (Distance << 5) | (Inp.fgetbits() >> 11); + Inp.faddbits(5); + CopyString15(Distance,Length); return; } } @@ -426,7 +399,15 @@ void Unpack::HuffDecode() void Unpack::GetFlagsBuf() { unsigned int Flags,NewFlagsPlace; - unsigned int FlagsPlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2); + unsigned int FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); + + // Our Huffman table stores 257 items and needs all them in other parts + // of code such as when StMode is on, so the first item is control item. + // While normally we do not use the last item to code the flags byte here, + // we need to check for value 256 when unpacking in case we unpack + // a corrupt archive. + if (FlagsPlace>=sizeof(ChSetC)/sizeof(ChSetC[0])) + return; while (1) { @@ -443,7 +424,7 @@ void Unpack::GetFlagsBuf() } -void Unpack::OldUnpInitData(int Solid) +void Unpack::UnpInitData15(int Solid) { if (!Solid) { @@ -464,8 +445,6 @@ void Unpack::InitHuff() { for (unsigned int I=0;I<256;I++) { - Place[I]=PlaceA[I]=PlaceB[I]=I; - PlaceC[I]=(~I+1) & 0xff; ChSet[I]=ChSetB[I]=I<<8; ChSetA[I]=I; ChSetC[I]=((~I+1) & 0xff)<<8; @@ -477,7 +456,7 @@ void Unpack::InitHuff() } -void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace) +void Unpack::CorrHuff(ushort *CharSet,byte *NumToPlace) { int I,J; for (I=7;I>=0;I--) @@ -489,13 +468,13 @@ void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace) } -void Unpack::OldCopyString(unsigned int Distance,unsigned int Length) +void Unpack::CopyString15(uint Distance,uint Length) { DestUnpSize-=Length; while (Length--) { - Window[UnpPtr]=Window[(UnpPtr-Distance) & MAXWINMASK]; - UnpPtr=(UnpPtr+1) & MAXWINMASK; + Window[UnpPtr]=Window[(UnpPtr-Distance) & MaxWinMask]; + UnpPtr=(UnpPtr+1) & MaxWinMask; } } @@ -505,6 +484,6 @@ uint Unpack::DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab) int I; for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++) StartPos++; - faddbits(StartPos); + Inp.faddbits(StartPos); return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]); } diff --git a/libunrar/unpack20.cpp b/libunrar/unpack20.cpp index c7767fe..93c8ba0 100644 --- a/libunrar/unpack20.cpp +++ b/libunrar/unpack20.cpp @@ -1,28 +1,12 @@ #include "rar.hpp" -void Unpack::CopyString20(unsigned int Length,unsigned int Distance) +void Unpack::CopyString20(uint Length,uint Distance) { - LastDist=OldDist[OldDistPtr++ & 3]=Distance; + LastDist=OldDist[OldDistPtr++]=Distance; + OldDistPtr = OldDistPtr & 3; // Needed if RAR 1.5 file is called after RAR 2.0. LastLength=Length; DestUnpSize-=Length; - - unsigned int DestPtr=UnpPtr-Distance; - if (DestPtr2) - { - Length--; - Window[UnpPtr++]=Window[DestPtr++]; - } - } - else - while (Length--) - { - Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK]; - UnpPtr=(UnpPtr+1) & MAXWINMASK; - } + CopyString(Length,Distance); } @@ -30,11 +14,11 @@ void Unpack::Unpack20(bool Solid) { static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - static int DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; + static uint DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; static unsigned char DBits[]= {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; - unsigned int Bits; + uint Bits; if (Suspended) UnpPtr=WrPtr; @@ -43,28 +27,27 @@ void Unpack::Unpack20(bool Solid) UnpInitData(Solid); if (!UnpReadBuf()) return; - if (!Solid) - if (!ReadTables20()) - return; + if ((!Solid || !TablesRead2) && !ReadTables20()) + return; --DestUnpSize; } while (DestUnpSize>=0) { - UnpPtr&=MAXWINMASK; + UnpPtr&=MaxWinMask; - if (InAddr>ReadTop-30) + if (Inp.InAddr>ReadTop-30) if (!UnpReadBuf()) break; - if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr) + if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) { - OldUnpWriteBuf(); + UnpWriteBuf20(); if (Suspended) return; } if (UnpAudioBlock) { - int AudioNumber=DecodeNumber((struct Decode *)&MD[UnpCurChannel]); + uint AudioNumber=DecodeNumber(Inp,&MD[UnpCurChannel]); if (AudioNumber==256) { @@ -72,14 +55,14 @@ void Unpack::Unpack20(bool Solid) break; continue; } - Window[UnpPtr++]=DecodeAudio(AudioNumber); + Window[UnpPtr++]=DecodeAudio((int)AudioNumber); if (++UnpCurChannel==UnpChannels) UnpCurChannel=0; --DestUnpSize; continue; } - int Number=DecodeNumber((struct Decode *)&LD); + uint Number=DecodeNumber(Inp,&BlockTables.LD); if (Number<256) { Window[UnpPtr++]=(byte)Number; @@ -88,19 +71,19 @@ void Unpack::Unpack20(bool Solid) } if (Number>269) { - int Length=LDecode[Number-=270]+3; + uint Length=LDecode[Number-=270]+3; if ((Bits=LBits[Number])>0) { - Length+=getbits()>>(16-Bits); - addbits(Bits); + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } - int DistNumber=DecodeNumber((struct Decode *)&DD); - unsigned int Distance=DDecode[DistNumber]+1; + uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { - Distance+=getbits()>>(16-Bits); - addbits(Bits); + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } if (Distance>=0x2000) @@ -126,13 +109,13 @@ void Unpack::Unpack20(bool Solid) } if (Number<261) { - unsigned int Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; - int LengthNumber=DecodeNumber((struct Decode *)&RD); - int Length=LDecode[LengthNumber]+2; + uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; + uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + uint Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { - Length+=getbits()>>(16-Bits); - addbits(Bits); + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } if (Distance>=0x101) { @@ -149,60 +132,75 @@ void Unpack::Unpack20(bool Solid) } if (Number<270) { - unsigned int Distance=SDDecode[Number-=261]+1; + uint Distance=SDDecode[Number-=261]+1; if ((Bits=SDBits[Number])>0) { - Distance+=getbits()>>(16-Bits); - addbits(Bits); + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } CopyString20(2,Distance); continue; } } ReadLastTables(); - OldUnpWriteBuf(); + UnpWriteBuf20(); +} + + +void Unpack::UnpWriteBuf20() +{ + if (UnpPtr!=WrPtr) + UnpSomeRead=true; + if (UnpPtrUnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask); + UnpIO->UnpWrite(Window,UnpPtr); + UnpAllBuf=true; + } + else + UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); + WrPtr=UnpPtr; } bool Unpack::ReadTables20() { byte BitLength[BC20]; - unsigned char Table[MC20*4]; - int TableSize,N,I; - if (InAddr>ReadTop-25) + byte Table[MC20*4]; + if (Inp.InAddr>ReadTop-25) if (!UnpReadBuf()) - return(false); - unsigned int BitField=getbits(); - UnpAudioBlock=(BitField & 0x8000); + return false; + uint BitField=Inp.getbits(); + UnpAudioBlock=(BitField & 0x8000)!=0; if (!(BitField & 0x4000)) memset(UnpOldTable20,0,sizeof(UnpOldTable20)); - addbits(2); + Inp.addbits(2); + uint TableSize; if (UnpAudioBlock) { UnpChannels=((BitField>>12) & 3)+1; if (UnpCurChannel>=UnpChannels) UnpCurChannel=0; - addbits(2); + Inp.addbits(2); TableSize=MC20*UnpChannels; } else TableSize=NC20+DC20+RC20; - for (I=0;I> 12); - addbits(4); + BitLength[I]=(byte)(Inp.getbits() >> 12); + Inp.addbits(4); } - MakeDecodeTables(BitLength,(struct Decode *)&BD,BC20); - I=0; - while (IReadTop-5) + if (Inp.InAddr>ReadTop-5) if (!UnpReadBuf()) - return(false); - int Number=DecodeNumber((struct Decode *)&BD); + return false; + uint Number=DecodeNumber(Inp,&BlockTables.BD); if (Number<16) { Table[I]=(Number+UnpOldTable20[I]) & 0xf; @@ -211,56 +209,61 @@ bool Unpack::ReadTables20() else if (Number==16) { - N=(getbits() >> 14)+3; - addbits(2); - while (N-- > 0 && I> 14)+3; + Inp.addbits(2); + if (I==0) + return false; // We cannot have "repeat previous" code at the first position. + else + while (N-- > 0 && I> 13)+3; - addbits(3); + N=(Inp.getbits() >> 13)+3; + Inp.addbits(3); } else { - N=(getbits() >> 9)+11; - addbits(7); + N=(Inp.getbits() >> 9)+11; + Inp.addbits(7); } while (N-- > 0 && IReadTop) - return(true); + TablesRead2=true; + if (Inp.InAddr>ReadTop) + return true; if (UnpAudioBlock) - for (I=0;I=InAddr+5) + if (ReadTop>=Inp.InAddr+5) if (UnpAudioBlock) { - if (DecodeNumber((struct Decode *)&MD[UnpCurChannel])==256) + if (DecodeNumber(Inp,&MD[UnpCurChannel])==256) ReadTables20(); } else - if (DecodeNumber((struct Decode *)&LD)==269) + if (DecodeNumber(Inp,&BlockTables.LD)==269) ReadTables20(); } @@ -269,7 +272,10 @@ void Unpack::UnpInitData20(int Solid) { if (!Solid) { - UnpAudioBlock=UnpChannelDelta=UnpCurChannel=0; + TablesRead2=false; + UnpAudioBlock=false; + UnpChannelDelta=0; + UnpCurChannel=0; UnpChannels=1; memset(AudV,0,sizeof(AudV)); @@ -290,9 +296,12 @@ byte Unpack::DecodeAudio(int Delta) int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta; PCh=(PCh>>3) & 0xFF; - unsigned int Ch=PCh-Delta; + uint Ch=PCh-Delta; - int D=((signed char)Delta)<<3; + int D=(signed char)Delta; + // Left shift of negative value is undefined behavior in C++, + // so we cast it to unsigned to follow the standard. + D=(uint)D<<3; V->Dif[0]+=abs(D); V->Dif[1]+=abs(D-V->D1); @@ -311,9 +320,9 @@ byte Unpack::DecodeAudio(int Delta) if ((V->ByteCount & 0x1F)==0) { - unsigned int MinDif=V->Dif[0],NumMinDif=0; + uint MinDif=V->Dif[0],NumMinDif=0; V->Dif[0]=0; - for (int I=1;IDif)/sizeof(V->Dif[0]);I++) + for (uint I=1;IDif);I++) { if (V->Dif[I]ReadBorder) + { + if (!UnpReadBuf30()) + break; + } + if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr) + { + UnpWriteBuf30(); + if (WrittenFileSize>DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + if (UnpBlockType==BLOCK_PPM) + { + // Here speed is critical, so we do not use SafePPMDecodeChar, + // because sometimes even the inline function can introduce + // some additional penalty. + int Ch=PPM.DecodeChar(); + if (Ch==-1) // Corrupt PPM data found. + { + PPM.CleanUp(); // Reset possibly corrupt PPM data structures. + UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. + break; + } + if (Ch==PPMEscChar) + { + int NextCh=SafePPMDecodeChar(); + if (NextCh==0) // End of PPM encoding. + { + if (!ReadTables30()) + break; + continue; + } + if (NextCh==-1) // Corrupt PPM data found. + break; + if (NextCh==2) // End of file in PPM mode. + break; + if (NextCh==3) // Read VM code. + { + if (!ReadVMCodePPM()) + break; + continue; + } + if (NextCh==4) // LZ inside of PPM. + { + unsigned int Distance=0,Length; + bool Failed=false; + for (int I=0;I<4 && !Failed;I++) + { + int Ch=SafePPMDecodeChar(); + if (Ch==-1) + Failed=true; + else + if (I==3) + Length=(byte)Ch; + else + Distance=(Distance<<8)+(byte)Ch; + } + if (Failed) + break; + + CopyString(Length+32,Distance+2); + continue; + } + if (NextCh==5) // One byte distance match (RLE) inside of PPM. + { + int Length=SafePPMDecodeChar(); + if (Length==-1) + break; + CopyString(Length+4,1); + continue; + } + // If we are here, NextCh must be 1, what means that current byte + // is equal to our 'escape' byte, so we just store it to Window. + } + Window[UnpPtr++]=Ch; + continue; + } + + uint Number=DecodeNumber(Inp,&BlockTables.LD); + if (Number<256) + { + Window[UnpPtr++]=(byte)Number; + continue; + } + if (Number>=271) + { + uint Length=LDecode[Number-=271]+3; + if ((Bits=LBits[Number])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + + uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint Distance=DDecode[DistNumber]+1; + if ((Bits=DBits[DistNumber])>0) + { + if (DistNumber>9) + { + if (Bits>4) + { + Distance+=((Inp.getbits()>>(20-Bits))<<4); + Inp.addbits(Bits-4); + } + if (LowDistRepCount>0) + { + LowDistRepCount--; + Distance+=PrevLowDist; + } + else + { + uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + if (LowDist==16) + { + LowDistRepCount=LOW_DIST_REP_COUNT-1; + Distance+=PrevLowDist; + } + else + { + Distance+=LowDist; + PrevLowDist=LowDist; + } + } + } + else + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + } + + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000) + Length++; + } + + InsertOldDist(Distance); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (Number==256) + { + if (!ReadEndOfBlock()) + break; + continue; + } + if (Number==257) + { + if (!ReadVMCode()) + break; + continue; + } + if (Number==258) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + continue; + } + if (Number<263) + { + uint DistNum=Number-259; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + int Length=LDecode[LengthNumber]+2; + if ((Bits=LBits[LengthNumber])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (Number<272) + { + uint Distance=SDDecode[Number-=263]+1; + if ((Bits=SDBits[Number])>0) + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + InsertOldDist(Distance); + LastLength=2; + CopyString(2,Distance); + continue; + } + } + UnpWriteBuf30(); +} + + +// Return 'false' to quit unpacking the current file or 'true' to continue. +bool Unpack::ReadEndOfBlock() +{ + uint BitField=Inp.getbits(); + bool NewTable,NewFile=false; + + // "1" - no new file, new table just here. + // "00" - new file, no new table. + // "01" - new file, new table (in beginning of next file). + + if ((BitField & 0x8000)!=0) + { + NewTable=true; + Inp.addbits(1); + } + else + { + NewFile=true; + NewTable=(BitField & 0x4000)!=0; + Inp.addbits(2); + } + TablesRead3=!NewTable; + + // Quit immediately if "new file" flag is set. If "new table" flag + // is present, we'll read the table in beginning of next file + // based on 'TablesRead3' 'false' value. + if (NewFile) + return false; + return ReadTables30(); // Quit only if we failed to read tables. +} + + +bool Unpack::ReadVMCode() +{ + // Entire VM code is guaranteed to fully present in block defined + // by current Huffman table. Compressor checks that VM code does not cross + // Huffman block boundaries. + uint FirstByte=Inp.getbits()>>8; + Inp.addbits(8); + uint Length=(FirstByte & 7)+1; + if (Length==7) + { + Length=(Inp.getbits()>>8)+7; + Inp.addbits(8); + } + else + if (Length==8) + { + Length=Inp.getbits(); + Inp.addbits(16); + } + if (Length==0) + return false; + Array VMCode(Length); + for (uint I=0;I=ReadTop-1 && !UnpReadBuf30() && I>8; + Inp.addbits(8); + } + return AddVMCode(FirstByte,&VMCode[0],Length); +} + + +bool Unpack::ReadVMCodePPM() +{ + uint FirstByte=SafePPMDecodeChar(); + if ((int)FirstByte==-1) + return false; + uint Length=(FirstByte & 7)+1; + if (Length==7) + { + int B1=SafePPMDecodeChar(); + if (B1==-1) + return false; + Length=B1+7; + } + else + if (Length==8) + { + int B1=SafePPMDecodeChar(); + if (B1==-1) + return false; + int B2=SafePPMDecodeChar(); + if (B2==-1) + return false; + Length=B1*256+B2; + } + if (Length==0) + return false; + Array VMCode(Length); + for (uint I=0;IFilters30.Size() || FiltPos>OldFilterLengths.Size()) + return false; + LastFilter=FiltPos; + bool NewFilter=(FiltPos==Filters30.Size()); + + UnpackFilter30 *StackFilter=new UnpackFilter30; // New filter for PrgStack. + + UnpackFilter30 *Filter; + if (NewFilter) // New filter code, never used before since VM reset. + { + if (FiltPos>MAX3_UNPACK_FILTERS) + { + // Too many different filters, corrupt archive. + delete StackFilter; + return false; + } + + Filters30.Add(1); + Filters30[Filters30.Size()-1]=Filter=new UnpackFilter30; + StackFilter->ParentFilter=(uint)(Filters30.Size()-1); + + // Reserve one item to store the data block length of our new filter + // entry. We'll set it to real block length below, after reading it. + // But we need to initialize it now, because when processing corrupt + // data, we can access this item even before we set it to real value. + OldFilterLengths.Push(0); + } + else // Filter was used in the past. + { + Filter=Filters30[FiltPos]; + StackFilter->ParentFilter=FiltPos; + } + + uint EmptyCount=0; + for (uint I=0;I0) + PrgStack[I]=NULL; + } + if (EmptyCount==0) + { + if (PrgStack.Size()>MAX3_UNPACK_FILTERS) + { + delete StackFilter; + return false; + } + PrgStack.Add(1); + EmptyCount=1; + } + size_t StackPos=PrgStack.Size()-EmptyCount; + PrgStack[StackPos]=StackFilter; + + uint BlockStart=RarVM::ReadData(VMCodeInp); + if ((FirstByte & 0x40)!=0) + BlockStart+=258; + StackFilter->BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask); + if ((FirstByte & 0x20)!=0) + { + StackFilter->BlockLength=RarVM::ReadData(VMCodeInp); + + // Store the last data block length for current filter. + OldFilterLengths[FiltPos]=StackFilter->BlockLength; + } + else + { + // Set the data block size to same value as the previous block size + // for same filter. It is possible for corrupt data to access a new + // and not filled yet item of OldFilterLengths array here. This is why + // we set new OldFilterLengths items to zero above. + StackFilter->BlockLength=FiltPosNextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; + +// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); + + memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); + StackFilter->Prg.InitR[4]=StackFilter->BlockLength; + + if ((FirstByte & 0x10)!=0) // Set registers to optional parameters if any. + { + uint InitMask=VMCodeInp.fgetbits()>>9; + VMCodeInp.faddbits(7); + for (uint I=0;I<7;I++) + if (InitMask & (1<Prg.InitR[I]=RarVM::ReadData(VMCodeInp); + } + + if (NewFilter) + { + uint VMCodeSize=RarVM::ReadData(VMCodeInp); + if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize) + return false; + Array VMCode(VMCodeSize); + for (uint I=0;I>8; + VMCodeInp.faddbits(8); + } + VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); + } + StackFilter->Prg.Type=Filter->Prg.Type; + + return true; +} + + +bool Unpack::UnpReadBuf30() +{ + int DataSize=ReadTop-Inp.InAddr; // Data left to process. + if (DataSize<0) + return false; + if (Inp.InAddr>BitInput::MAX_SIZE/2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize>0) + memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Inp.InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); + if (ReadCode>0) + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + return ReadCode!=-1; +} + + +void Unpack::UnpWriteBuf30() +{ + uint WrittenBorder=(uint)WrPtr; + uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); + for (size_t I=0;INextWindow) + { + flt->NextWindow=false; + continue; + } + unsigned int BlockStart=flt->BlockStart; + unsigned int BlockLength=flt->BlockLength; + if (((BlockStart-WrittenBorder)&MaxWinMask)ParentFilter]->Prg; + VM_PreparedProgram *Prg=&flt->Prg; + + ExecuteCode(Prg); + + byte *FilteredData=Prg->FilteredData; + unsigned int FilteredDataSize=Prg->FilteredDataSize; + + delete PrgStack[I]; + PrgStack[I]=NULL; + while (I+1BlockStart!=BlockStart || + NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) + break; + + // Apply several filters to same data block. + + VM.SetMemory(0,FilteredData,FilteredDataSize); + + VM_PreparedProgram *ParentPrg=&Filters30[NextFilter->ParentFilter]->Prg; + VM_PreparedProgram *NextPrg=&NextFilter->Prg; + + ExecuteCode(NextPrg); + + FilteredData=NextPrg->FilteredData; + FilteredDataSize=NextPrg->FilteredDataSize; + I++; + delete PrgStack[I]; + PrgStack[I]=NULL; + } + UnpIO->UnpWrite(FilteredData,FilteredDataSize); + UnpSomeRead=true; + WrittenFileSize+=FilteredDataSize; + WrittenBorder=BlockEnd; + WriteSize=uint((UnpPtr-WrittenBorder)&MaxWinMask); + } + else + { + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + for (size_t J=I;JNextWindow) + flt->NextWindow=false; + } + WrPtr=WrittenBorder; + return; + } + } + } + + UnpWriteArea(WrittenBorder,UnpPtr); + WrPtr=UnpPtr; +} + + +void Unpack::ExecuteCode(VM_PreparedProgram *Prg) +{ + Prg->InitR[6]=(uint)WrittenFileSize; + VM.Execute(Prg); +} + + +bool Unpack::ReadTables30() +{ + byte BitLength[BC]; + byte Table[HUFF_TABLE_SIZE30]; + if (Inp.InAddr>ReadTop-25) + if (!UnpReadBuf30()) + return(false); + Inp.faddbits((8-Inp.InBit)&7); + uint BitField=Inp.fgetbits(); + if (BitField & 0x8000) + { + UnpBlockType=BLOCK_PPM; + return(PPM.DecodeInit(this,PPMEscChar)); + } + UnpBlockType=BLOCK_LZ; + + PrevLowDist=0; + LowDistRepCount=0; + + if (!(BitField & 0x4000)) + memset(UnpOldTable,0,sizeof(UnpOldTable)); + Inp.faddbits(2); + + for (uint I=0;I> 12); + Inp.faddbits(4); + if (Length==15) + { + uint ZeroCount=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (ZeroCount==0) + BitLength[I]=15; + else + { + ZeroCount+=2; + while (ZeroCount-- > 0 && IReadTop-5) + if (!UnpReadBuf30()) + return(false); + uint Number=DecodeNumber(Inp,&BlockTables.BD); + if (Number<16) + { + Table[I]=(Number+UnpOldTable[I]) & 0xf; + I++; + } + else + if (Number<18) + { + uint N; + if (Number==16) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + if (I==0) + return false; // We cannot have "repeat previous" code at the first position. + else + while (N-- > 0 && I> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + while (N-- > 0 && IReadTop) + return false; + MakeDecodeTables(&Table[0],&BlockTables.LD,NC30); + MakeDecodeTables(&Table[NC30],&BlockTables.DD,DC30); + MakeDecodeTables(&Table[NC30+DC30],&BlockTables.LDD,LDC30); + MakeDecodeTables(&Table[NC30+DC30+LDC30],&BlockTables.RD,RC30); + memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); + return true; +} + + +void Unpack::UnpInitData30(bool Solid) +{ + if (!Solid) + { + TablesRead3=false; + memset(UnpOldTable,0,sizeof(UnpOldTable)); + PPMEscChar=2; + UnpBlockType=BLOCK_LZ; + } + InitFilters30(Solid); +} + + +void Unpack::InitFilters30(bool Solid) +{ + if (!Solid) + { + OldFilterLengths.SoftReset(); + LastFilter=0; + + for (size_t I=0;I=ReadBorder) + { + bool FileDone=false; + + // We use 'while', because for empty block containing only Huffman table, + // we'll be on the block border once again just after reading the table. + while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 || + Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 && + Inp.InBit>=BlockHeader.BlockBitSize) + { + if (BlockHeader.LastBlockInFile) + { + FileDone=true; + break; + } + if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables)) + return; + } + if (FileDone || !UnpReadBuf()) + break; + } + + if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + + uint MainSlot=DecodeNumber(Inp,&BlockTables.LD); + if (MainSlot<256) + { + if (Fragmented) + FragWindow[UnpPtr++]=(byte)MainSlot; + else + Window[UnpPtr++]=(byte)MainSlot; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((Inp.getbits32()>>(36-DBits))<<4); + Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=Inp.getbits32()>>(32-DBits); + Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + InsertOldDist(Distance); + LastLength=Length; + if (Fragmented) + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + else + CopyString(Length,Distance); + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) + break; + continue; + } + if (MainSlot==257) + { + if (LastLength!=0) + if (Fragmented) + FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); + else + CopyString(LastLength,OldDist[0]); + continue; + } + if (MainSlot<262) + { + uint DistNum=MainSlot-258; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD); + uint Length=SlotToLength(Inp,LengthSlot); + LastLength=Length; + if (Fragmented) + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + else + CopyString(Length,Distance); + continue; + } + } + UnpWriteBuf(); +} + + +uint Unpack::ReadFilterData(BitInput &Inp) +{ + uint ByteCount=(Inp.fgetbits()>>14)+1; + Inp.addbits(2); + + uint Data=0; + for (uint I=0;I>8)<<(I*8); + Inp.addbits(8); + } + return Data; +} + + +bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter) +{ + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) + if (!UnpReadBuf()) + return false; + + Filter.BlockStart=ReadFilterData(Inp); + Filter.BlockLength=ReadFilterData(Inp); + if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) + Filter.BlockLength=0; + + Filter.Type=Inp.fgetbits()>>13; + Inp.faddbits(3); + + if (Filter.Type==FILTER_DELTA) + { + Filter.Channels=(Inp.fgetbits()>>11)+1; + Inp.faddbits(5); + } + + return true; +} + + +bool Unpack::AddFilter(UnpackFilter &Filter) +{ + if (Filters.Size()>=MAX_UNPACK_FILTERS) + { + UnpWriteBuf(); // Write data, apply and flush filters. + if (Filters.Size()>=MAX_UNPACK_FILTERS) + InitFilters(); // Still too many filters, prevent excessive memory use. + } + + // If distance to filter start is that large that due to circular dictionary + // mode now it points to old not written yet data, then we set 'NextWindow' + // flag and process this filter only after processing that older data. + Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; + + Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); + Filters.Push(Filter); + return true; +} + + +bool Unpack::UnpReadBuf() +{ + int DataSize=ReadTop-Inp.InAddr; // Data left to process. + if (DataSize<0) + return false; + BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; + if (Inp.InAddr>BitInput::MAX_SIZE/2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize>0) + memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Inp.InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=0; + if (BitInput::MAX_SIZE!=DataSize) + ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); + if (ReadCode>0) // Can be also -1. + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + BlockHeader.BlockStart=Inp.InAddr; + if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet. + { + // We may need to quit from main extraction loop and read new block header + // and trees earlier than data in input buffer ends. + ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); + } + return ReadCode!=-1; +} + + +void Unpack::UnpWriteBuf() +{ + size_t WrittenBorder=WrPtr; + size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; + size_t WriteSizeLeft=FullWriteSize; + bool NotAllFiltersProcessed=false; + for (size_t I=0;IType==FILTER_NONE) + continue; + if (flt->NextWindow) + { + // Here we skip filters which have block start in current data range + // due to address wrap around in circular dictionary, but actually + // belong to next dictionary block. If such filter start position + // is included to current write range, then we reset 'NextWindow' flag. + // In fact we can reset it even without such check, because current + // implementation seems to guarantee 'NextWindow' flag reset after + // buffer writing for all existing filters. But let's keep this check + // just in case. Compressor guarantees that distance between + // filter block start and filter storing position cannot exceed + // the dictionary size. So if we covered the filter block start with + // our write here, we can safely assume that filter is applicable + // to next block on no further wrap arounds is possible. + if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) + flt->NextWindow=false; + continue; + } + uint BlockStart=flt->BlockStart; + uint BlockLength=flt->BlockLength; + if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. + { + uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; + + FilterSrcMemory.Alloc(BlockLength); + byte *Mem=&FilterSrcMemory[0]; + if (BlockStartUnpWrite(OutMem,BlockLength); + + UnpSomeRead=true; + WrittenFileSize+=BlockLength; + WrittenBorder=BlockEnd; + WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; + } + } + else + { + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + WrPtr=WrittenBorder; + + // Since Filter start position can only increase, we quit processing + // all following filters for this data block and reset 'NextWindow' + // flag for them. + for (size_t J=I;JType!=FILTER_NONE) + flt->NextWindow=false; + } + + // Do not write data left after current filter now. + NotAllFiltersProcessed=true; + break; + } + } + } + + // Remove processed filters from queue. + size_t EmptyCount=0; + for (size_t I=0;I0) + Filters[I-EmptyCount]=Filters[I]; + if (Filters[I].Type==FILTER_NONE) + EmptyCount++; + } + if (EmptyCount>0) + Filters.Alloc(Filters.Size()-EmptyCount); + + if (!NotAllFiltersProcessed) // Only if all filters are processed. + { + // Write data left after last filter. + UnpWriteArea(WrittenBorder,UnpPtr); + WrPtr=UnpPtr; + } + + // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE + // instead of potentially huge MaxWinSize blocks. It also allows us + // to keep the size of Filters array reasonable. + WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; + + // Choose the nearest among WriteBorder and WrPtr actual written border. + // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. + if (WriteBorder==UnpPtr || + WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) + WriteBorder=WrPtr; +} + + +byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) +{ + byte *SrcData=Data; + switch(Flt->Type) + { + case FILTER_E8: + case FILTER_E8E9: + { + uint FileOffset=(uint)WrittenFileSize; + + const uint FileSize=0x1000000; + byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8; + // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" + // to avoid overflow for DataSize<4. + for (uint CurPos=0;CurPos+4=0 + RawPut4(Addr+FileSize,Data); + } + else + if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); + D[2]=(byte)(Offset>>16); + } + } + } + return SrcData; + case FILTER_DELTA: + { + // Unlike RAR3, we do not need to reject excessive channel + // values here, since RAR5 uses only 5 bits to store channel. + uint Channels=Flt->Channels,SrcPos=0; + + FilterDstMemory.Alloc(DataSize); + byte *DstData=&FilterDstMemory[0]; + + // Bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions. + for (uint CurChannel=0;CurChannel0) + { + size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); + UnpWriteData(&FragWindow[StartPtr],BlockSize); + SizeToWrite-=BlockSize; + StartPtr=(StartPtr+BlockSize) & MaxWinMask; + } + } + else + if (EndPtr=DestUnpSize) + return; + size_t WriteSize=Size; + int64 LeftToWrite=DestUnpSize-WrittenFileSize; + if ((int64)WriteSize>LeftToWrite) + WriteSize=(size_t)LeftToWrite; + UnpIO->UnpWrite(Data,WriteSize); + WrittenFileSize+=Size; +} + + +void Unpack::UnpInitData50(bool Solid) +{ + if (!Solid) + TablesRead5=false; +} + + +bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) +{ + Header.HeaderSize=0; + + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) + if (!UnpReadBuf()) + return false; + Inp.faddbits((8-Inp.InBit)&7); + + byte BlockFlags=Inp.fgetbits()>>8; + Inp.faddbits(8); + uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. + + if (ByteCount==4) + return false; + + Header.HeaderSize=2+ByteCount; + + Header.BlockBitSize=(BlockFlags&7)+1; + + byte SavedCheckSum=Inp.fgetbits()>>8; + Inp.faddbits(8); + + int BlockSize=0; + for (uint I=0;I>8)<<(I*8); + Inp.addbits(8); + } + + Header.BlockSize=BlockSize; + byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); + if (CheckSum!=SavedCheckSum) + return false; + + Header.BlockStart=Inp.InAddr; + ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); + + Header.LastBlockInFile=(BlockFlags & 0x40)!=0; + Header.TablePresent=(BlockFlags & 0x80)!=0; + return true; +} + + +bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables) +{ + if (!Header.TablePresent) + return true; + + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) + if (!UnpReadBuf()) + return false; + + byte BitLength[BC]; + for (uint I=0;I> 12); + Inp.faddbits(4); + if (Length==15) + { + uint ZeroCount=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (ZeroCount==0) + BitLength[I]=15; + else + { + ZeroCount+=2; + while (ZeroCount-- > 0 && IReadTop-5) + if (!UnpReadBuf()) + return false; + uint Number=DecodeNumber(Inp,&Tables.BD); + if (Number<16) + { + Table[I]=Number; + I++; + } + else + if (Number<18) + { + uint N; + if (Number==16) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + if (I==0) + { + // We cannot have "repeat previous" code at the first position. + // Multiple such codes would shift Inp position without changing I, + // which can lead to reading beyond of Inp boundary in mutithreading + // mode, where Inp.ExternalBuffer disables bounds check and we just + // reserve a lot of buffer space to not need such check normally. + return false; + } + else + while (N-- > 0 && I> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + while (N-- > 0 && IReadTop) + return false; + MakeDecodeTables(&Table[0],&Tables.LD,NC); + MakeDecodeTables(&Table[NC],&Tables.DD,DC); + MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); + MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); + return true; +} + + +void Unpack::InitFilters() +{ + Filters.SoftReset(); +} diff --git a/libunrar/unpack50frag.cpp b/libunrar/unpack50frag.cpp new file mode 100644 index 0000000..3c008ff --- /dev/null +++ b/libunrar/unpack50frag.cpp @@ -0,0 +1,103 @@ +FragmentedWindow::FragmentedWindow() +{ + memset(Mem,0,sizeof(Mem)); + memset(MemSize,0,sizeof(MemSize)); +} + + +FragmentedWindow::~FragmentedWindow() +{ + Reset(); +} + + +void FragmentedWindow::Reset() +{ + for (uint I=0;I=MinSize) + { + NewMem=(byte *)malloc(Size); + if (NewMem!=NULL) + break; + Size-=Size/32; + } + if (NewMem==NULL) + throw std::bad_alloc(); + + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access to unused areas of sliding dictionary. + memset(NewMem,0,Size); + + Mem[BlockNum]=NewMem; + TotalSize+=Size; + MemSize[BlockNum]=TotalSize; + BlockNum++; + } + if (TotalSize 0) + { + (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' + UnpPtr=(UnpPtr+1) & MaxWinMask; + } +} + + +void FragmentedWindow::CopyData(byte *Dest,size_t WinPos,size_t Size) +{ + for (size_t I=0;IBlockCount;I++) + DL->D->UnpackPtr->UnpackDecode(DL->D[I]); +} + + +void Unpack::InitMT() +{ + if (ReadBufMT==NULL) + { + // Even getbits32 can read up to 3 additional bytes after current + // and our block header and table reading code can look much further. + // Let's allocate the additional space here, so we do not need to check + // bounds for every bit field access. + const size_t Overflow=1024; + + ReadBufMT=new byte[UNP_READ_SIZE_MT+Overflow]; + memset(ReadBufMT,0,UNP_READ_SIZE_MT+Overflow); + } + if (UnpThreadData==NULL) + { + uint MaxItems=MaxUserThreads*UNP_BLOCKS_PER_THREAD; + UnpThreadData=new UnpackThreadData[MaxItems]; + memset(UnpThreadData,0,sizeof(UnpackThreadData)*MaxItems); + + for (uint I=0;IDecoded==NULL) + { + // Typical number of items in RAR blocks does not exceed 0x4000. + CurData->DecodedAllocated=0x4100; + // It will be freed in the object destructor, not in this file. + CurData->Decoded=(UnpackDecodedItem *)malloc(CurData->DecodedAllocated*sizeof(UnpackDecodedItem)); + if (CurData->Decoded==NULL) + ErrHandler.MemoryError(); + } + } + } +} + + +void Unpack::Unpack5MT(bool Solid) +{ + InitMT(); + UnpInitData(Solid); + + for (uint I=0;ILargeBlock=false; + CurData->Incomplete=false; + } + + UnpThreadData[0].BlockHeader=BlockHeader; + UnpThreadData[0].BlockTables=BlockTables; + uint LastBlockNum=0; + + int DataSize=0; + int BlockStart=0; + + + // 'true' if we found a block too large for multithreaded extraction, + // so we switched to single threaded mode until the end of file. + // Large blocks could cause too high memory use in multithreaded mode. + bool LargeBlock=false; + + bool Done=false; + while (!Done) + { + // Data amount, which is guaranteed to fit block header and tables, + // so we can safely read them without additional checks. + const int TooSmallToProcess=1024; + + int ReadSize=UnpIO->UnpRead(ReadBufMT+DataSize,(UNP_READ_SIZE_MT-DataSize)&~0xf); + if (ReadSize<0) + break; + DataSize+=ReadSize; + if (DataSize==0) + break; + + // First read chunk can be small if we are near the end of volume + // and we want it to fit block header and tables. + if (ReadSize>0 && DataSizeUnpackPtr=this; + + // 'Incomplete' thread is present. This is a thread processing block + // in the end of buffer, split between two read operations. + if (CurData->Incomplete) + CurData->DataSize=DataSize; + else + { + CurData->Inp.SetExternalBuffer(ReadBufMT+BlockStart); + CurData->Inp.InitBitInput(); + CurData->DataSize=DataSize-BlockStart; + if (CurData->DataSize==0) + break; + CurData->DamagedData=false; + CurData->HeaderRead=false; + CurData->TableRead=false; + } + + // We should not use 'last block in file' block flag here unless + // we'll check the block size, because even if block is last in file, + // it can exceed the current buffer and require more reading. + CurData->NoDataLeft=(ReadSize==0); + + CurData->Incomplete=false; + CurData->ThreadNumber=BlockNumber; + + if (!CurData->HeaderRead) + { + CurData->HeaderRead=true; + if (!ReadBlockHeader(CurData->Inp,CurData->BlockHeader) || + !CurData->BlockHeader.TablePresent && !TablesRead5) + { + Done=true; + break; + } + TablesRead5=true; + } + + // To prevent too high memory use we switch to single threaded mode + // if block exceeds this size. Typically RAR blocks do not exceed + // 64 KB, so this protection should not affect most of valid archives. + const int LargeBlockSize=0x20000; + if (LargeBlock || CurData->BlockHeader.BlockSize>LargeBlockSize) + LargeBlock=CurData->LargeBlock=true; + else + BlockNumberMT++; // Number of normal blocks processed in MT mode. + + BlockStart+=CurData->BlockHeader.HeaderSize+CurData->BlockHeader.BlockSize; + + BlockNumber++; + + int DataLeft=DataSize-BlockStart; + if (DataLeft>=0 && CurData->BlockHeader.LastBlockInFile) + break; + + // For second and following threads we move smaller blocks to buffer + // start to ensure that we have enough data to fit block header + // and tables. + if (DataLeftD=UnpThreadData+CurBlock; + UTD->BlockCount=Min(MaxBlockPerThread,BlockNumberMT-CurBlock); + +#ifdef USE_THREADS + if (BlockNumber==1) + UnpackDecode(*UTD->D); + else + UnpThreadPool->AddTask(UnpackDecodeThread,(void*)UTD); +#else + for (uint I=0;IBlockCount;I++) + UnpackDecode(UTD->D[I]); +#endif + } + + if (BlockNumber==0) + break; + +#ifdef USE_THREADS + UnpThreadPool->WaitDone(); +#endif + + bool IncompleteThread=false; + + for (uint Block=0;BlockLargeBlock && !ProcessDecoded(*CurData) || + CurData->LargeBlock && !UnpackLargeBlock(*CurData) || + CurData->DamagedData) + { + Done=true; + break; + } + if (CurData->Incomplete) + { + int BufPos=int(CurData->Inp.InBuf+CurData->Inp.InAddr-ReadBufMT); + if (DataSize<=BufPos) // Thread exceeded input buffer boundary. + { + Done=true; + break; + } + IncompleteThread=true; + memmove(ReadBufMT,ReadBufMT+BufPos,DataSize-BufPos); + CurData->BlockHeader.BlockSize-=CurData->Inp.InAddr-CurData->BlockHeader.BlockStart; + CurData->BlockHeader.HeaderSize=0; + CurData->BlockHeader.BlockStart=0; + CurData->Inp.InBuf=ReadBufMT; + CurData->Inp.InAddr=0; + + if (Block!=0) + { + // Move the incomplete thread entry to the first position, + // so we'll start processing from it. Preserve the original + // buffer for decoded data. + UnpackDecodedItem *Decoded=UnpThreadData[0].Decoded; + uint DecodedAllocated=UnpThreadData[0].DecodedAllocated; + UnpThreadData[0]=*CurData; + UnpThreadData[0].Decoded=Decoded; + UnpThreadData[0].DecodedAllocated=DecodedAllocated; + CurData->Incomplete=false; + } + + BlockStart=0; + DataSize-=BufPos; + break; + } + else + if (CurData->BlockHeader.LastBlockInFile) + { + Done=true; + break; + } + } + + if (IncompleteThread || Done) + break; // Current buffer is done, read more data or quit. + else + { + int DataLeft=DataSize-BlockStart; + if (DataLeft0) + memmove(ReadBufMT,ReadBufMT+BlockStart,DataLeft); + DataSize=DataLeft; + BlockStart=0; + break; // Current buffer is done, try to read more data. + } + } + } + } + UnpPtr&=MaxWinMask; // ProcessDecoded and maybe others can leave UnpPtr > MaxWinMask here. + UnpWriteBuf(); + + BlockHeader=UnpThreadData[LastBlockNum].BlockHeader; + BlockTables=UnpThreadData[LastBlockNum].BlockTables; +} + + +// Decode Huffman block and save decoded data to memory. +void Unpack::UnpackDecode(UnpackThreadData &D) +{ + if (!D.TableRead) + { + D.TableRead=true; + if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) + { + D.DamagedData=true; + return; + } + } + + if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) + { + D.DamagedData=true; + return; + } + + D.DecodedSize=0; + int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; + + // Reserve enough space even for filter entry. + int DataBorder=D.DataSize-16; + int ReadBorder=Min(BlockBorder,DataBorder); + + while (true) + { + if (D.Inp.InAddr>=ReadBorder) + { + if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && + D.Inp.InBit>=D.BlockHeader.BlockBitSize) + break; + + // If we do not have any more data in file to read, we must process + // what we have until last byte. Otherwise we can return and append + // more data to unprocessed few bytes. + if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) + { + D.Incomplete=true; + break; + } + } + if (D.DecodedSize>D.DecodedAllocated-8) // Filter can use several slots. + { + D.DecodedAllocated=D.DecodedAllocated*2; + void *Decoded=realloc(D.Decoded,D.DecodedAllocated*sizeof(UnpackDecodedItem)); + if (Decoded==NULL) + ErrHandler.MemoryError(); // D.Decoded will be freed in the destructor. + D.Decoded=(UnpackDecodedItem *)Decoded; + } + + UnpackDecodedItem *CurItem=D.Decoded+D.DecodedSize++; + + uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); + if (MainSlot<256) + { + if (D.DecodedSize>1) + { + UnpackDecodedItem *PrevItem=CurItem-1; + if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<3) + { + PrevItem->Length++; + PrevItem->Literal[PrevItem->Length]=(byte)MainSlot; + D.DecodedSize--; + continue; + } + } + CurItem->Type=UNPDT_LITERAL; + CurItem->Literal[0]=(byte)MainSlot; + CurItem->Length=0; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(D.Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + D.Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=D.Inp.getbits32()>>(32-DBits); + D.Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + CurItem->Type=UNPDT_MATCH; + CurItem->Length=(ushort)Length; + CurItem->Distance=Distance; + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + ReadFilter(D.Inp,Filter); + + CurItem->Type=UNPDT_FILTER; + CurItem->Length=Filter.Type; + CurItem->Distance=Filter.BlockStart; + + CurItem=D.Decoded+D.DecodedSize++; + + CurItem->Type=UNPDT_FILTER; + CurItem->Length=Filter.Channels; + CurItem->Distance=Filter.BlockLength; + + continue; + } + if (MainSlot==257) + { + CurItem->Type=UNPDT_FULLREP; + continue; + } + if (MainSlot<262) + { + CurItem->Type=UNPDT_REP; + CurItem->Distance=MainSlot-258; + uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); + uint Length=SlotToLength(D.Inp,LengthSlot); + CurItem->Length=(ushort)Length; + continue; + } + } +} + + +// Process decoded Huffman block data. +bool Unpack::ProcessDecoded(UnpackThreadData &D) +{ + UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize; + while (ItemDestUnpSize) + return false; + } + + if (Item->Type==UNPDT_LITERAL) + { +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + if (Item->Length==3 && UnpPtrLiteral; + UnpPtr+=4; + } + else +#endif + for (uint I=0;I<=Item->Length;I++) + Window[UnpPtr++ & MaxWinMask]=Item->Literal[I]; + } + else + if (Item->Type==UNPDT_MATCH) + { + InsertOldDist(Item->Distance); + LastLength=Item->Length; + CopyString(Item->Length,Item->Distance); + } + else + if (Item->Type==UNPDT_REP) + { + uint Distance=OldDist[Item->Distance]; + for (uint I=Item->Distance;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + LastLength=Item->Length; + CopyString(Item->Length,Distance); + } + else + if (Item->Type==UNPDT_FULLREP) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + } + else + if (Item->Type==UNPDT_FILTER) + { + UnpackFilter Filter; + + Filter.Type=(byte)Item->Length; + Filter.BlockStart=Item->Distance; + + Item++; + + Filter.Channels=(byte)Item->Length; + Filter.BlockLength=Item->Distance; + + AddFilter(Filter); + } + Item++; + } + return true; +} + + +// For large blocks we decode and process in same function in single threaded +// mode, so we do not need to store intermediate data in memory. +bool Unpack::UnpackLargeBlock(UnpackThreadData &D) +{ + if (!D.TableRead) + { + D.TableRead=true; + if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) + { + D.DamagedData=true; + return false; + } + } + + if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) + { + D.DamagedData=true; + return false; + } + + int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; + + // Reserve enough space even for filter entry. + int DataBorder=D.DataSize-16; + int ReadBorder=Min(BlockBorder,DataBorder); + + while (true) + { + UnpPtr&=MaxWinMask; + if (D.Inp.InAddr>=ReadBorder) + { + if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && + D.Inp.InBit>=D.BlockHeader.BlockBitSize) + break; + + // If we do not have any more data in file to read, we must process + // what we have until last byte. Otherwise we can return and append + // more data to unprocessed few bytes. + if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) + { + D.Incomplete=true; + break; + } + } + if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) + return false; + } + + uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); + if (MainSlot<256) + { + Window[UnpPtr++]=(byte)MainSlot; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(D.Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + D.Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=D.Inp.getbits32()>>(32-DBits); + D.Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + InsertOldDist(Distance); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + if (!ReadFilter(D.Inp,Filter) || !AddFilter(Filter)) + break; + continue; + } + if (MainSlot==257) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + continue; + } + if (MainSlot<262) + { + uint DistNum=MainSlot-258; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); + uint Length=SlotToLength(D.Inp,LengthSlot); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + } + return true; +} diff --git a/libunrar/unpackinline.cpp b/libunrar/unpackinline.cpp new file mode 100644 index 0000000..04c3d1f --- /dev/null +++ b/libunrar/unpackinline.cpp @@ -0,0 +1,147 @@ +_forceinline void Unpack::InsertOldDist(uint Distance) +{ + OldDist[3]=OldDist[2]; + OldDist[2]=OldDist[1]; + OldDist[1]=OldDist[0]; + OldDist[0]=Distance; +} + +#ifdef _MSC_VER +#define FAST_MEMCPY +#endif + +_forceinline void Unpack::CopyString(uint Length,uint Distance) +{ + size_t SrcPtr=UnpPtr-Distance; + if (SrcPtr=8) + { + Dest[0]=Src[0]; + Dest[1]=Src[1]; + Dest[2]=Src[2]; + Dest[3]=Src[3]; + Dest[4]=Src[4]; + Dest[5]=Src[5]; + Dest[6]=Src[6]; + Dest[7]=Src[7]; + + Src+=8; + Dest+=8; + Length-=8; + } +#ifdef FAST_MEMCPY + else + while (Length>=8) + { + // In theory we still could overlap here. + // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). + // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH + // always, so overlap here is impossible. + + // This memcpy expanded inline by MSVC. We could also use uint64 + // assignment, which seems to provide about the same speed. + memcpy(Dest,Src,8); + + Src+=8; + Dest+=8; + Length-=8; + } +#endif + + // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. + if (Length>0) { Dest[0]=Src[0]; + if (Length>1) { Dest[1]=Src[1]; + if (Length>2) { Dest[2]=Src[2]; + if (Length>3) { Dest[3]=Src[3]; + if (Length>4) { Dest[4]=Src[4]; + if (Length>5) { Dest[5]=Src[5]; + if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. + } + else + while (Length-- > 0) // Slow copying with all possible precautions. + { + Window[UnpPtr]=Window[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with 'Window[UnpPtr++ & MaxWinMask]' + UnpPtr=(UnpPtr+1) & MaxWinMask; + } +} + + +_forceinline uint Unpack::DecodeNumber(BitInput &Inp,DecodeTable *Dec) +{ + // Left aligned 15 bit length raw bit field. + uint BitField=Inp.getbits() & 0xfffe; + + if (BitFieldDecodeLen[Dec->QuickBits]) + { + uint Code=BitField>>(16-Dec->QuickBits); + Inp.addbits(Dec->QuickLen[Code]); + return Dec->QuickNum[Code]; + } + + // Detect the real bit length for current code. + uint Bits=15; + for (uint I=Dec->QuickBits+1;I<15;I++) + if (BitFieldDecodeLen[I]) + { + Bits=I; + break; + } + + Inp.addbits(Bits); + + // Calculate the distance from the start code for current bit length. + uint Dist=BitField-Dec->DecodeLen[Bits-1]; + + // Start codes are left aligned, but we need the normal right aligned + // number. So we shift the distance to the right. + Dist>>=(16-Bits); + + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + uint Pos=Dec->DecodePos[Bits]+Dist; + + // Out of bounds safety check required for damaged archives. + if (Pos>=Dec->MaxNum) + Pos=0; + + // Convert the position in the code list to position in alphabet + // and return it. + return Dec->DecodeNum[Pos]; +} + + +_forceinline uint Unpack::SlotToLength(BitInput &Inp,uint Slot) +{ + uint LBits,Length=2; + if (Slot<8) + { + LBits=0; + Length+=Slot; + } + else + { + LBits=Slot/4-1; + Length+=(4 | (Slot & 3)) << LBits; + } + + if (LBits>0) + { + Length+=Inp.getbits()>>(16-LBits); + Inp.addbits(LBits); + } + return Length; +} diff --git a/libunrar/uowners.cpp b/libunrar/uowners.cpp index 38471e0..9f46308 100644 --- a/libunrar/uowners.cpp +++ b/libunrar/uowners.cpp @@ -1,47 +1,57 @@ -void ExtractUnixOwner(Archive &Arc,char *FileName) +void ExtractUnixOwner20(Archive &Arc,const wchar *FileName) { - if (Arc.HeaderCRC!=Arc.UOHead.HeadCRC) + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); + + if (Arc.BrokenHeader) { - Log(Arc.FileName,St(MOwnersBroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_UOWNERBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } struct passwd *pw; + errno=0; // Required by getpwnam specification if we need to check errno. if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL) { - Log(Arc.FileName,St(MErrGetOwnerID),Arc.UOHead.OwnerName); - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(Arc.UOHead.OwnerName)); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_WARNING); return; } uid_t OwnerID=pw->pw_uid; struct group *gr; + errno=0; // Required by getgrnam specification if we need to check errno. if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL) { - Log(Arc.FileName,St(MErrGetGroupID),Arc.UOHead.GroupName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(Arc.UOHead.GroupName)); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CRC); return; } - uint Attr=GetFileAttr(FileName,NULL); + uint Attr=GetFileAttr(FileName); gid_t GroupID=gr->gr_gid; #if defined(SAVE_LINKS) && !defined(_APPLE) - if (lchown(FileName,OwnerID,GroupID)!=0) + if (lchown(NameA,OwnerID,GroupID)!=0) #else - if (chown(FileName,OwnerID,GroupID)!=0) + if (chown(NameA,OwnerID,GroupID)!=0) #endif { - Log(Arc.FileName,St(MSetOwnersError),FileName); - ErrHandler.SetErrorCode(CREATE_ERROR); + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); } - SetFileAttr(FileName,NULL,Attr); + SetFileAttr(FileName,Attr); } -void ExtractUnixOwnerNew(Archive &Arc,char *FileName) +void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) { + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); + char *OwnerName=(char *)&Arc.SubHead.SubData[0]; int OwnerSize=strlen(OwnerName)+1; int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize; @@ -52,8 +62,8 @@ void ExtractUnixOwnerNew(Archive &Arc,char *FileName) struct passwd *pw; if ((pw=getpwnam(OwnerName))==NULL) { - Log(Arc.FileName,St(MErrGetOwnerID),OwnerName); - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(OwnerName)); + ErrHandler.SetErrorCode(RARX_WARNING); return; } uid_t OwnerID=pw->pw_uid; @@ -61,20 +71,71 @@ void ExtractUnixOwnerNew(Archive &Arc,char *FileName) struct group *gr; if ((gr=getgrnam(GroupName))==NULL) { - Log(Arc.FileName,St(MErrGetGroupID),GroupName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(GroupName)); + ErrHandler.SetErrorCode(RARX_WARNING); return; } - uint Attr=GetFileAttr(FileName,NULL); + uint Attr=GetFileAttr(FileName); gid_t GroupID=gr->gr_gid; #if defined(SAVE_LINKS) && !defined(_APPLE) - if (lchown(FileName,OwnerID,GroupID)!=0) + if (lchown(NameA,OwnerID,GroupID)!=0) #else - if (chown(FileName,OwnerID,GroupID)!=0) + if (chown(NameA,OwnerID,GroupID)!=0) #endif { - Log(Arc.FileName,St(MSetOwnersError),FileName); - ErrHandler.SetErrorCode(CREATE_ERROR); + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); + } + SetFileAttr(FileName,Attr); +} + + +void SetUnixOwner(Archive &Arc,const wchar *FileName) +{ + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); + + // First, we try to resolve symbolic names. If they are missing or cannot + // be resolved, we try to use numeric values if any. If numeric values + // are missing too, function fails. + FileHeader &hd=Arc.FileHead; + if (*hd.UnixOwnerName!=0) + { + struct passwd *pw; + if ((pw=getpwnam(hd.UnixOwnerName))==NULL) + { + if (!hd.UnixOwnerNumeric) + { + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(hd.UnixOwnerName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + } + else + hd.UnixOwnerID=pw->pw_uid; + } + if (*hd.UnixGroupName!=0) + { + struct group *gr; + if ((gr=getgrnam(hd.UnixGroupName))==NULL) + { + if (!hd.UnixGroupNumeric) + { + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(hd.UnixGroupName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + } + else + hd.UnixGroupID=gr->gr_gid; + } +#if defined(SAVE_LINKS) && !defined(_APPLE) + if (lchown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) +#else + if (chown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) +#endif + { + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); } - SetFileAttr(FileName,NULL,Attr); } diff --git a/libunrar/version.hpp b/libunrar/version.hpp index ea7097a..24f0f8b 100644 --- a/libunrar/version.hpp +++ b/libunrar/version.hpp @@ -1,6 +1,6 @@ -#define RARVER_MAJOR 3 -#define RARVER_MINOR 90 +#define RARVER_MAJOR 5 +#define RARVER_MINOR 91 #define RARVER_BETA 0 -#define RARVER_DAY 16 -#define RARVER_MONTH 8 -#define RARVER_YEAR 2009 +#define RARVER_DAY 25 +#define RARVER_MONTH 6 +#define RARVER_YEAR 2020 diff --git a/libunrar/volume.cpp b/libunrar/volume.cpp index 5d9514c..6cfa772 100644 --- a/libunrar/volume.cpp +++ b/libunrar/volume.cpp @@ -1,28 +1,28 @@ #include "rar.hpp" - - - -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_M_X64) -// Disable the run time stack check for unrar.dll, so we can manipulate -// with ChangeVolProc call type below. Run time check would intercept -// a wrong ESP before we restore it. -#pragma runtime_checks( "s", off ) +#ifdef RARDLL +static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize); +static bool DllVolNotify(RAROptions *Cmd,wchar *NextName); #endif -bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Command) + + +bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) { RAROptions *Cmd=Arc.GetRAROptions(); - int HeaderType=Arc.GetHeaderType(); - FileHeader *hd=HeaderType==NEWSUB_HEAD ? &Arc.SubHead:&Arc.NewLhd; - bool SplitHeader=(HeaderType==FILE_HEAD || HeaderType==NEWSUB_HEAD) && - (hd->Flags & LHD_SPLIT_AFTER)!=0; + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead; + bool SplitHeader=(HeaderType==HEAD_FILE || HeaderType==HEAD_SERVICE) && + hd->SplitAfter; - if (DataIO!=NULL && SplitHeader && hd->UnpVer>=20 && - hd->FileCRC!=0xffffffff && DataIO->PackedCRC!=~hd->FileCRC) + if (DataIO!=NULL && SplitHeader) { - Log(Arc.FileName,St(MDataBadCRC),hd->FileName,Arc.FileName); + bool PackedHashPresent=Arc.Format==RARFMT50 || + hd->UnpVer>=20 && hd->FileHash.CRC32!=0xffffffff; + if (PackedHashPresent && + !DataIO->PackedDataHash.Cmp(&hd->FileHash,hd->UseHashKey ? hd->HashKey:NULL)) + uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName); } int64 PosBeforeClose=Arc.Tell(); @@ -30,182 +30,126 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Comman if (DataIO!=NULL) DataIO->ProcessedArcSize+=Arc.FileLength(); + Arc.Close(); - char NextName[NM]; - wchar NextNameW[NM]; - strcpy(NextName,Arc.FileName); - strcpyw(NextNameW,Arc.FileNameW); - NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); + wchar NextName[NM]; + wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); #if !defined(SFX_MODULE) && !defined(RARDLL) bool RecoveryDone=false; #endif bool FailedOpen=false,OldSchemeTested=false; - while (!Arc.Open(NextName,NextNameW)) - { - // We need to open a new volume which size was not calculated - // in total size before, so we cannot calculate the total progress - // anymore. Let's reset the total size to zero and stop - // the total progress. - if (DataIO!=NULL) - DataIO->TotalArcSize=0; +#if !defined(SILENT) + // In -vp mode we force the pause before next volume even if it is present + // and even if we are on the hard disk. It is important when user does not + // want to process partially downloaded volumes preliminary. + if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName))) + FailedOpen=true; +#endif - if (!OldSchemeTested) + uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0; + + if (!FailedOpen) + while (!Arc.Open(NextName,OpenMode)) { - // Checking for new style volumes renamed by user to old style - // name format. Some users did it for unknown reason. - char AltNextName[NM]; - wchar AltNextNameW[NM]; - strcpy(AltNextName,Arc.FileName); - strcpyw(AltNextNameW,Arc.FileNameW); - NextVolumeName(AltNextName,AltNextNameW,ASIZE(AltNextName),true); - OldSchemeTested=true; - if (Arc.Open(AltNextName,AltNextNameW)) + // We need to open a new volume which size was not calculated + // in total size before, so we cannot calculate the total progress + // anymore. Let's reset the total size to zero and stop + // the total progress. + if (DataIO!=NULL) + DataIO->TotalArcSize=0; + + if (!OldSchemeTested) { - strcpy(NextName,AltNextName); - strcpyw(NextNameW,AltNextNameW); - break; + // Checking for new style volumes renamed by user to old style + // name format. Some users did it for unknown reason. + wchar AltNextName[NM]; + wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName)); + NextVolumeName(AltNextName,ASIZE(AltNextName),true); + OldSchemeTested=true; + if (Arc.Open(AltNextName,OpenMode)) + { + wcsncpyz(NextName,AltNextName,ASIZE(NextName)); + break; + } } - } #ifdef RARDLL - if (Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL || - Cmd->Callback!=NULL && Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1) - { - Cmd->DllError=ERAR_EOPEN; - FailedOpen=true; - break; - } - if (Cmd->ChangeVolProc!=NULL) - { - // Here we preserve ESP value. It is necessary for those developers, - // who still define ChangeVolProc callback as "C" type function, - // even though in year 2001 we announced in unrar.dll whatsnew.txt - // that it will be PASCAL type (for compatibility with Visual Basic). -#if defined(_MSC_VER) -#ifndef _M_X64 - __asm mov ebx,esp -#endif -#elif defined(_WIN_32) && defined(__BORLANDC__) - _EBX=_ESP; -#endif - int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_ASK); - - // Restore ESP after ChangeVolProc with wrongly defined calling - // convention broken it. -#if defined(_MSC_VER) -#ifndef _M_X64 - __asm mov esp,ebx -#endif -#elif defined(_WIN_32) && defined(__BORLANDC__) - _ESP=_EBX; -#endif - if (RetCode==0) + if (!DllVolChange(Cmd,NextName,ASIZE(NextName))) { - Cmd->DllError=ERAR_EOPEN; FailedOpen=true; break; } - } -#else // RARDLL +#else // !RARDLL -#if !defined(SFX_MODULE) && !defined(_WIN_CE) - if (!RecoveryDone) - { - RecVolumes RecVol; - RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true); - RecoveryDone=true; - continue; - } +#ifndef SFX_MODULE + if (!RecoveryDone) + { + RecVolumesRestore(Cmd,Arc.FileName,true); + RecoveryDone=true; + continue; + } #endif -#ifndef GUI - if (!Cmd->VolumePause && !IsRemovable(NextName)) - { - FailedOpen=true; - break; - } -#endif + if (!Cmd->VolumePause && !IsRemovable(NextName)) + { + FailedOpen=true; + break; + } #ifndef SILENT - if (Cmd->AllYes || !AskNextVol(NextName)) + if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName))) #endif - { - FailedOpen=true; - break; - } + { + FailedOpen=true; + break; + } #endif // RARDLL - *NextNameW=0; - } + } + if (FailedOpen) { -#if !defined(SILENT) && !defined(_WIN_CE) - Log(Arc.FileName,St(MAbsNextVol),NextName); -#endif - Arc.Open(Arc.FileName,Arc.FileNameW); + uiMsg(UIERROR_MISSINGVOL,NextName); + Arc.Open(Arc.FileName,OpenMode); Arc.Seek(PosBeforeClose,SEEK_SET); - return(false); + return false; } - Arc.CheckArc(true); -#ifdef RARDLL - if (Cmd->Callback!=NULL && - Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1) - return(false); - if (Cmd->ChangeVolProc!=NULL) - { -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _EBX=_ESP; -#endif - int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_NOTIFY); -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _ESP=_EBX; -#endif - if (RetCode==0) - return(false); - } -#endif if (Command=='T' || Command=='X' || Command=='E') mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName); + + + Arc.CheckArc(true); +#ifdef RARDLL + if (!DllVolNotify(Cmd,NextName)) + return false; +#endif + if (SplitHeader) Arc.SearchBlock(HeaderType); else Arc.ReadHeader(); - if (Arc.GetHeaderType()==FILE_HEAD) + if (Arc.GetHeaderType()==HEAD_FILE) { Arc.ConvertAttributes(); - Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET); + Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); } -#ifndef GUI if (ShowFileName) { - char OutName[NM]; - IntToExt(Arc.NewLhd.FileName,OutName); -#ifdef UNICODE_SUPPORTED - bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled(); - if (WideName) - { - wchar NameW[NM]; - ConvertPath(Arc.NewLhd.FileNameW,NameW); - char Name[NM]; - if (WideToChar(NameW,Name) && IsNameUsable(Name)) - strcpy(OutName,Name); - } -#endif - mprintf(St(MExtrPoints),OutName); + mprintf(St(MExtrPoints),Arc.FileHead.FileName); if (!Cmd->DisablePercentage) - mprintf(" "); + mprintf(L" "); } -#endif if (DataIO!=NULL) { - if (HeaderType==ENDARC_HEAD) + if (HeaderType==HEAD_ENDARC) DataIO->UnpVolume=false; else { - DataIO->UnpVolume=(hd->Flags & LHD_SPLIT_AFTER)!=0; - DataIO->SetPackedSizeToRead(hd->FullPackSize); + DataIO->UnpVolume=hd->SplitAfter; + DataIO->SetPackedSizeToRead(hd->PackSize); } #ifdef SFX_MODULE DataIO->UnpArcSize=Arc.FileLength(); @@ -216,28 +160,129 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Comman // compensated with ProcessedArcSize, so we need to reset this variable. DataIO->CurUnpRead=0; - DataIO->PackedCRC=0xffffffff; -// DataIO->SetFiles(&Arc,NULL); + DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads); } - return(true); + return true; } -#if defined(RARDLL) && defined(_MSC_VER) && !defined(_M_X64) + + + + + +#ifdef RARDLL +#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) +// Disable the run time stack check for unrar.dll, so we can manipulate +// with ChangeVolProc call type below. Run time check would intercept +// a wrong ESP before we restore it. +#pragma runtime_checks( "s", off ) +#endif + +bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) +{ + bool DllVolChanged=false,DllVolAborted=false; + + if (Cmd->Callback!=NULL) + { + wchar OrgNextName[NM]; + wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName)); + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1) + DllVolAborted=true; + else + if (wcscmp(OrgNextName,NextName)!=0) + DllVolChanged=true; + else + { + char NextNameA[NM],OrgNextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA)); + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1) + DllVolAborted=true; + else + if (strcmp(OrgNextNameA,NextNameA)!=0) + { + // We can damage some Unicode characters by U->A->U conversion, + // so set Unicode name only if we see that ANSI name is changed. + CharToWide(NextNameA,NextName,NameSize); + DllVolChanged=true; + } + } + } + if (!DllVolChanged && Cmd->ChangeVolProc!=NULL) + { + char NextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + // Here we preserve ESP value. It is necessary for those developers, + // who still define ChangeVolProc callback as "C" type function, + // even though in year 2001 we announced in unrar.dll whatsnew.txt + // that it will be PASCAL type (for compatibility with Visual Basic). +#if defined(_MSC_VER) +#ifndef _WIN_64 + __asm mov ebx,esp +#endif +#elif defined(_WIN_ALL) && defined(__BORLANDC__) + _EBX=_ESP; +#endif + int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK); + + // Restore ESP after ChangeVolProc with wrongly defined calling + // convention broken it. +#if defined(_MSC_VER) +#ifndef _WIN_64 + __asm mov esp,ebx +#endif +#elif defined(_WIN_ALL) && defined(__BORLANDC__) + _ESP=_EBX; +#endif + if (RetCode==0) + DllVolAborted=true; + else + CharToWide(NextNameA,NextName,NameSize); + } + + // We quit only on 'abort' condition, but not on 'name not changed'. + // It is legitimate for program to return the same name when waiting + // for currently non-existent volume. + // Also we quit to prevent an infinite loop if no callback is defined. + if (DllVolAborted || Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL) + { + Cmd->DllError=ERAR_EOPEN; + return false; + } + return true; +} +#endif + + +#ifdef RARDLL +bool DllVolNotify(RAROptions *Cmd,wchar *NextName) +{ + char NextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + if (Cmd->Callback!=NULL) + { + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1) + return false; + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1) + return false; + } + if (Cmd->ChangeVolProc!=NULL) + { +#if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__) + _EBX=_ESP; +#endif + int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY); +#if defined(_WIN_ALL) && !defined(_MSC_VER) && !defined(__MINGW32__) + _ESP=_EBX; +#endif + if (RetCode==0) + return false; + } + return true; +} + +#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64) // Restore the run time stack check for unrar.dll. #pragma runtime_checks( "s", restore ) #endif - - - - - - -#ifndef SILENT -bool AskNextVol(char *ArcName) -{ - eprintf(St(MAskNextVol),ArcName); - if (Ask(St(MContinueQuit))==2) - return(false); - return(true); -} #endif diff --git a/libunrar/volume.hpp b/libunrar/volume.hpp index 6bf4c9e..2d6a6d5 100644 --- a/libunrar/volume.hpp +++ b/libunrar/volume.hpp @@ -4,8 +4,7 @@ void SplitArchive(Archive &Arc,FileHeader *fh,int64 *HeaderPos, ComprDataIO *DataIO); bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName, - char Command); + wchar Command); void SetVolWrite(Archive &Dest,int64 VolSize); -bool AskNextVol(char *ArcName); #endif diff --git a/libunrar/win32acl.cpp b/libunrar/win32acl.cpp index 37fb22b..d4797bd 100644 --- a/libunrar/win32acl.cpp +++ b/libunrar/win32acl.cpp @@ -1,47 +1,45 @@ -static void SetPrivileges(); +static void SetACLPrivileges(); static bool ReadSacl=false; #ifndef SFX_MODULE -void ExtractACL(Archive &Arc,char *FileName,wchar *FileNameW) +void ExtractACL20(Archive &Arc,const wchar *FileName) { - if (!WinNT()) - return; + SetACLPrivileges(); - SetPrivileges(); - - if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC) + if (Arc.BrokenHeader) { - Log(Arc.FileName,St(MACLBroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER) + if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>VER_PACK) { - Log(Arc.FileName,St(MACLUnknown),FileName); - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_ACLUNKNOWN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); return; } ComprDataIO DataIO; Unpack Unpack(&DataIO); - Unpack.Init(); + Unpack.Init(0x10000,false); - Array UnpData(Arc.EAHead.UnpSize); + Array UnpData(Arc.EAHead.UnpSize); DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); DataIO.EnableShowProgress(false); DataIO.SetFiles(&Arc,NULL); + DataIO.UnpHash.Init(HASH_CRC32,1); Unpack.SetDestSize(Arc.EAHead.UnpSize); Unpack.DoUnpack(Arc.EAHead.UnpVer,false); - if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC) + if (Arc.EAHead.EACRC!=DataIO.UnpHash.GetCRC32()) { - Log(Arc.FileName,St(MACLBroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } @@ -51,77 +49,87 @@ void ExtractACL(Archive &Arc,char *FileName,wchar *FileNameW) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&UnpData[0]; - int SetCode; - if (FileNameW!=NULL) - SetCode=SetFileSecurityW(FileNameW,si,sd); - else - SetCode=SetFileSecurity(FileName,si,sd); + int SetCode=SetFileSecurity(FileName,si,sd); if (!SetCode) { - Log(Arc.FileName,St(MACLSetError),FileName); + uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); + DWORD LastError=GetLastError(); ErrHandler.SysErrMsg(); - ErrHandler.SetErrorCode(WARNING); + if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SetErrorCode(RARX_WARNING); } } #endif -void ExtractACLNew(Archive &Arc,char *FileName,wchar *FileNameW) +void ExtractACL(Archive &Arc,const wchar *FileName) { - if (!WinNT()) - return; - Array SubData; - if (!Arc.ReadSubData(&SubData,NULL)) + if (!Arc.ReadSubData(&SubData,NULL,false)) return; - SetPrivileges(); + SetACLPrivileges(); - SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| - DACL_SECURITY_INFORMATION; + SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| + DACL_SECURITY_INFORMATION; if (ReadSacl) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&SubData[0]; - int SetCode; - if (FileNameW!=NULL) - SetCode=SetFileSecurityW(FileNameW,si,sd); - else - SetCode=SetFileSecurity(FileName,si,sd); + int SetCode=SetFileSecurity(FileName,si,sd); + if (!SetCode) + { + wchar LongName[NM]; + if (GetWinLongPath(FileName,LongName,ASIZE(LongName))) + SetCode=SetFileSecurity(LongName,si,sd); + } if (!SetCode) { - Log(Arc.FileName,St(MACLSetError),FileName); + uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); + DWORD LastError=GetLastError(); ErrHandler.SysErrMsg(); - ErrHandler.SetErrorCode(WARNING); + if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SetErrorCode(RARX_WARNING); } } -void SetPrivileges() +void SetACLPrivileges() { static bool InitDone=false; if (InitDone) return; + + if (SetPrivilege(SE_SECURITY_NAME)) + ReadSacl=true; + SetPrivilege(SE_RESTORE_NAME); + InitDone=true; +} + + +bool SetPrivilege(LPCTSTR PrivName) +{ + bool Success=false; HANDLE hToken; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) - return; - - TOKEN_PRIVILEGES tp; - tp.PrivilegeCount = 1; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - if (LookupPrivilegeValue(NULL,SE_SECURITY_NAME,&tp.Privileges[0].Luid)) - if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) && + if (LookupPrivilegeValue(NULL,PrivName,&tp.Privileges[0].Luid) && + AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) && GetLastError() == ERROR_SUCCESS) - ReadSacl=true; + Success=true; - if (LookupPrivilegeValue(NULL,SE_RESTORE_NAME,&tp.Privileges[0].Luid)) - AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL); + CloseHandle(hToken); + } - CloseHandle(hToken); + return Success; } diff --git a/libunrar/win32lnk.cpp b/libunrar/win32lnk.cpp new file mode 100644 index 0000000..a68ed75 --- /dev/null +++ b/libunrar/win32lnk.cpp @@ -0,0 +1,174 @@ +#define SYMLINK_FLAG_RELATIVE 1 + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + + + + +bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) +{ + static bool PrivSet=false; + if (!PrivSet) + { + SetPrivilege(SE_RESTORE_NAME); + // Not sure if we really need it, but let's request anyway. + SetPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME); + PrivSet=true; + } + + const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM+1024; + Array Buf(BufSize); + REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0]; + + wchar SubstName[NM]; + wcsncpyz(SubstName,hd->RedirName,ASIZE(SubstName)); + size_t SubstLength=wcslen(SubstName); + + wchar PrintName[NM],*PrintNameSrc=SubstName,*PrintNameDst=PrintName; + bool WinPrefix=wcsncmp(PrintNameSrc,L"\\??\\",4)==0; + if (WinPrefix) + PrintNameSrc+=4; + if (WinPrefix && wcsncmp(PrintNameSrc,L"UNC\\",4)==0) + { + *(PrintNameDst++)='\\'; // Insert second \ in beginning of share name. + PrintNameSrc+=3; + } + wcscpy(PrintNameDst,PrintNameSrc); + + size_t PrintLength=wcslen(PrintName); + + bool AbsPath=WinPrefix; + // IsFullPath is not really needed here, AbsPath check is enough. + // We added it just for extra safety, in case some Windows version would + // allow to create absolute targets with SYMLINK_FLAG_RELATIVE. + // Use hd->FileName instead of Name, since Name can include the destination + // path as a prefix, which can confuse IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (AbsPath || IsFullPath(hd->RedirName) || + !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) + return false; + + CreatePath(Name,true); + + // 'DirTarget' check is important for Unix symlinks to directories. + // Unix symlinks do not have their own 'directory' attribute. + if (hd->Dir || hd->DirTarget) + { + if (!CreateDirectory(Name,NULL)) + return false; + } + else + { + HANDLE hFile=CreateFile(Name,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); + if (hFile == INVALID_HANDLE_VALUE) + return false; + CloseHandle(hFile); + } + + + if (hd->RedirType==FSREDIR_JUNCTION) + { + rdb->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT; + rdb->ReparseDataLength=USHORT( + sizeof(rdb->MountPointReparseBuffer.SubstituteNameOffset)+ + sizeof(rdb->MountPointReparseBuffer.SubstituteNameLength)+ + sizeof(rdb->MountPointReparseBuffer.PrintNameOffset)+ + sizeof(rdb->MountPointReparseBuffer.PrintNameLength)+ + (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); + rdb->Reserved=0; + + rdb->MountPointReparseBuffer.SubstituteNameOffset=0; + rdb->MountPointReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer,SubstName); + + rdb->MountPointReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); + rdb->MountPointReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName); + } + else + if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_UNIXSYMLINK) + { + rdb->ReparseTag=IO_REPARSE_TAG_SYMLINK; + rdb->ReparseDataLength=USHORT( + sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset)+ + sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameLength)+ + sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameOffset)+ + sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameLength)+ + sizeof(rdb->SymbolicLinkReparseBuffer.Flags)+ + (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); + rdb->Reserved=0; + + rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset=0; + rdb->SymbolicLinkReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer,SubstName); + + rdb->SymbolicLinkReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); + rdb->SymbolicLinkReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName); + + rdb->SymbolicLinkReparseBuffer.Flags=AbsPath ? 0:SYMLINK_FLAG_RELATIVE; + } + else + return false; + + HANDLE hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,0,NULL, + OPEN_EXISTING,FILE_FLAG_OPEN_REPARSE_POINT| + FILE_FLAG_BACKUP_SEMANTICS,NULL); + if (hFile==INVALID_HANDLE_VALUE) + return false; + + DWORD Returned; + if (!DeviceIoControl(hFile,FSCTL_SET_REPARSE_POINT,rdb, + FIELD_OFFSET(REPARSE_DATA_BUFFER,GenericReparseBuffer)+ + rdb->ReparseDataLength,NULL,0,&Returned,NULL)) + { + CloseHandle(hFile); + uiMsg(UIERROR_SLINKCREATE,UINULL,Name); + + DWORD LastError=GetLastError(); + if ((LastError==ERROR_ACCESS_DENIED || LastError==ERROR_PRIVILEGE_NOT_HELD) && + !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + + if (hd->Dir) + RemoveDirectory(Name); + else + DeleteFile(Name); + return false; + } + File LinkFile; + LinkFile.SetHandle(hFile); + LinkFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&hd->mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&hd->ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&hd->atime); + LinkFile.Close(); + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(Name,hd->FileAttr); + return true; +} diff --git a/libunrar/win32stm.cpp b/libunrar/win32stm.cpp index 0636910..eaa43be 100644 --- a/libunrar/win32stm.cpp +++ b/libunrar/win32stm.cpp @@ -1,154 +1,152 @@ - -#ifndef SFX_MODULE -void ExtractStreams(Archive &Arc,char *FileName,wchar *FileNameW) +#if !defined(SFX_MODULE) && defined(_WIN_ALL) +void ExtractStreams20(Archive &Arc,const wchar *FileName) { - if (!WinNT()) - return; - - if (Arc.HeaderCRC!=Arc.StreamHead.HeadCRC) + if (Arc.BrokenHeader) { -#ifndef SILENT - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>PACK_VER) + if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>VER_PACK) { -#ifndef SILENT - Log(Arc.FileName,St(MStreamUnknown),FileName); -#endif - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_STREAMUNKNOWN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); return; } - char StreamName[NM+2]; + wchar StreamName[NM+2]; if (FileName[0]!=0 && FileName[1]==0) { - strcpy(StreamName,".\\"); - strcpy(StreamName+2,FileName); + // Convert single character names like f:stream to .\f:stream to + // resolve the ambiguity with drive letters. + wcsncpyz(StreamName,L".\\",ASIZE(StreamName)); + wcsncatz(StreamName,FileName,ASIZE(StreamName)); } else - strcpy(StreamName,FileName); - if (strlen(StreamName)+strlen((char *)Arc.StreamHead.StreamName)>=sizeof(StreamName) || + wcsncpyz(StreamName,FileName,ASIZE(StreamName)); + if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) || Arc.StreamHead.StreamName[0]!=':') { -#ifndef SILENT - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - ConvertPath((char *)Arc.StreamHead.StreamName+1,(char *)Arc.StreamHead.StreamName+1); + wchar StoredName[NM]; + CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); + ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1); - strcat(StreamName,(char *)Arc.StreamHead.StreamName); + wcsncatz(StreamName,StoredName,ASIZE(StreamName)); FindData fd; - bool Found=FindFile::FastFind(FileName,FileNameW,&fd); + bool Found=FindFile::FastFind(FileName,&fd); - if (fd.FileAttr & FILE_ATTRIBUTE_READONLY) - SetFileAttr(FileName,FileNameW,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(StreamName)) { ComprDataIO DataIO; Unpack Unpack(&DataIO); - Unpack.Init(); + Unpack.Init(0x10000,false); - Array UnpData(Arc.StreamHead.UnpSize); DataIO.SetPackedSizeToRead(Arc.StreamHead.DataSize); DataIO.EnableShowProgress(false); DataIO.SetFiles(&Arc,&CurFile); + DataIO.UnpHash.Init(HASH_CRC32,1); Unpack.SetDestSize(Arc.StreamHead.UnpSize); Unpack.DoUnpack(Arc.StreamHead.UnpVer,false); - if (Arc.StreamHead.StreamCRC!=~DataIO.UnpFileCRC) + if (Arc.StreamHead.StreamCRC!=DataIO.UnpHash.GetCRC32()) { -#ifndef SILENT - Log(Arc.FileName,St(MStreamBroken),StreamName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,StreamName); + ErrHandler.SetErrorCode(RARX_CRC); } else CurFile.Close(); } File HostFile; - if (Found && HostFile.Open(FileName,FileNameW,true,true)) + if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); - if (fd.FileAttr & FILE_ATTRIBUTE_READONLY) - SetFileAttr(FileName,FileNameW,fd.FileAttr); + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr); } #endif -void ExtractStreamsNew(Archive &Arc,char *FileName,wchar *FileNameW) +#ifdef _WIN_ALL +void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) { - if (!WinNT()) - return; - - wchar NameW[NM]; - if (FileNameW!=NULL && *FileNameW!=0) - strcpyw(NameW,FileNameW); - else - CharToWide(FileName,NameW); - wchar StreamNameW[NM+2]; - if (NameW[0]!=0 && NameW[1]==0) + wchar FullName[NM+2]; + if (FileName[0]!=0 && FileName[1]==0) { - strcpyw(StreamNameW,L".\\"); - strcpyw(StreamNameW+2,NameW); + // Convert single character names like f:stream to .\f:stream to + // resolve the ambiguity with drive letters. + wcsncpyz(FullName,L".\\",ASIZE(FullName)); + wcsncatz(FullName,FileName,ASIZE(FullName)); } else - strcpyw(StreamNameW,NameW); + wcsncpyz(FullName,FileName,ASIZE(FullName)); - wchar *DestName=StreamNameW+strlenw(StreamNameW); - byte *SrcName=&Arc.SubHead.SubData[0]; - size_t DestSize=Arc.SubHead.SubData.Size()/2; - - if (strlenw(StreamNameW)+DestSize>=ASIZE(StreamNameW)) + wchar StreamName[NM]; + GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); + if (*StreamName!=':') { -#if !defined(SILENT) && !defined(SFX_MODULE) - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - RawToWide(SrcName,DestName,DestSize); - DestName[DestSize]=0; - - if (*DestName!=':') + if (TestMode) { -#if !defined(SILENT) && !defined(SFX_MODULE) - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + File CurFile; + Arc.ReadSubData(NULL,&CurFile,true); return; } - ConvertPath(DestName+1,DestName+1); + wcsncatz(FullName,StreamName,ASIZE(FullName)); FindData fd; - bool Found=FindFile::FastFind(FileName,FileNameW,&fd); + bool Found=FindFile::FastFind(FileName,&fd); - if (fd.FileAttr & FILE_ATTRIBUTE_READONLY) - SetFileAttr(FileName,FileNameW,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); - char StreamName[NM]; - WideToChar(StreamNameW,StreamName); + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; - if (CurFile.WCreate(StreamName,StreamNameW) && Arc.ReadSubData(NULL,&CurFile)) + if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile,false)) CurFile.Close(); File HostFile; - if (Found && HostFile.Open(FileName,FileNameW,true,true)) + if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); // Restoring original file attributes. Important if file was read only // or did not have "Archive" attribute - SetFileAttr(FileName,FileNameW,fd.FileAttr); + SetFileAttr(FileName,fd.FileAttr); +} +#endif + + +void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize) +{ + byte *Data=&Arc.SubHead.SubData[0]; + size_t DataSize=Arc.SubHead.SubData.Size(); + if (Arc.Format==RARFMT15) + { + size_t DestSize=Min(DataSize/2,MaxSize-1); + RawToWide(Data,StreamName,DestSize); + StreamName[DestSize]=0; + } + else + { + char UtfString[NM*4]; + size_t DestSize=Min(DataSize,ASIZE(UtfString)-1); + memcpy(UtfString,Data,DestSize); + UtfString[DestSize]=0; + UtfToWide(UtfString,StreamName,MaxSize); + } }