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); + } }