From ce4719fe35adbce5c5602bb5f80ffe3f00d6af14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 27 Nov 2016 06:56:24 +0100 Subject: [PATCH] Saving files before refreshing line endings --- CHANGELOG.md | 4530 +++++----- build.xml | 2050 ++--- iggy-extract.bat | 2 +- .../src/com/jpexs/decompiler/flash/SWF.java | 7368 ++++++++--------- .../jpexs/decompiler/flash/SWFSourceInfo.java | 210 +- .../flash/iggy/AbstractDataStream.java | 220 +- .../flash/iggy/ByteArrayDataStream.java | 170 +- .../jpexs/decompiler/flash/iggy/DataType.java | 18 +- .../flash/iggy/IggyCharAdvances.java | 74 +- .../flash/iggy/IggyCharIndices.java | 88 +- .../flash/iggy/IggyCharKerning.java | 116 +- .../decompiler/flash/iggy/IggyCharOffset.java | 198 +- .../decompiler/flash/iggy/IggyDataReader.java | 178 +- .../jpexs/decompiler/flash/iggy/IggyFile.java | 1430 ++-- .../flash/iggy/IggyFlashHeader32.java | 390 +- .../flash/iggy/IggyFlashHeader64.java | 422 +- .../flash/iggy/IggyFlashHeaderInterface.java | 36 +- .../jpexs/decompiler/flash/iggy/IggyFont.java | 632 +- .../decompiler/flash/iggy/IggyHeader.java | 306 +- .../flash/iggy/IggyIndexParser.java | 330 +- .../decompiler/flash/iggy/IggyShape.java | 310 +- .../decompiler/flash/iggy/IggyShapeNode.java | 244 +- .../flash/iggy/IggySubFileEntry.java | 140 +- .../jpexs/decompiler/flash/iggy/IggyText.java | 388 +- .../jpexs/decompiler/flash/iggy/SeekMode.java | 18 +- .../flash/iggy/StructureInterface.java | 30 +- .../iggy/annotations/IggyArrayFieldType.java | 52 +- .../flash/iggy/annotations/IggyFieldType.java | 68 +- .../conversion/IggyShapeToSwfConvertor.java | 210 +- .../flash/iggy/conversion/IggySwfBundle.java | 208 +- .../iggy/conversion/IggyToSwfConvertor.java | 568 +- .../flash/tags/DefineEditTextTag.java | 2380 +++--- .../decompiler/flash/tags/base/TextTag.java | 1560 ++-- .../flash/gui/AdvancedSettingsDialog.java | 1252 +-- .../jpexs/decompiler/flash/gui/MainPanel.java | 7362 ++++++++-------- .../decompiler/flash/gui/TreeNodeType.java | 108 +- .../flash/gui/locales/MainFrame.properties | 1556 ++-- .../flash/gui/locales/MainFrame_cs.properties | 1510 ++-- .../decompiler/flash/gui/tagtree/TagTree.java | 1378 +-- 39 files changed, 19019 insertions(+), 19091 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48222c347..d83d48643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2266 +1,2266 @@ -# Change Log -All notable changes to this project will be documented in this file. - -## [Unreleased] -### Added -- Iggy Files support - reading fonts only -- optional AS3 direct editation with Flex SDK -- AS3 p-code editing - metadata read/write support -- AS3 p-code editing - end of the block command like in RABCDasm -- AS3 p-code editing - popup docs for more than instructions -- Debugger - New columns for variable details - scope, flags, trait -- Debugger - Add watch feature -- AS3 decompilation - colliding trait/class names handling - show hash suffix with namespace index on such cases -- Deobfuscation Tool - Fix colliding trait/classes via toolbar command -- Auto rename identifiers option now fixes colliding trait/classes aswell -- #1254 FLA export - detecting scripts on AS3 timeline -- #907 FFDec Library JAR file has version inside it. -- Display warning when library version and GUI version mismatch -- Changelog file - -### Changed -- #1189 AS3 - sort imports to have same order always -- GUI: AS3 P-code header show actual trait type and method type -- GUI: Script editing buttons now named "Edit ActionScript" and "Edit P-code" -- Set advance values button has confirm dialog with information -- #1274 Linux package no longer requires Oracle Java only -- Library now packaged inside ZIP file - -### Fixed -- P-code docs formatting fix -- Export dialog - handling sprite and SWF frames correctly -- #1275 debugger - show local variables fixes -- AS3 p-code editing - popup docs correctly displayed when label on line start -- #1278 replacing DefineBits error -- #1281 DefineFont 2/3 getting character advance value when replacing fix -- Set advance values button - Do not set advance if the char cannot be displayed in source font -- AS3 Goto declaration for single character names -- Identifier renaming for top level classes -- AS3 direct editation not correctly saving local register names -- #1254 FLA export - placing AS3 classes to FLA directory instead of scripts dir -- Mac OS X installer fix (.pkg) -- #1289 AS1/2 direct editation - variables used in inner functions must not be stored in local registers -- #1283 AS3 Unbounded Vector - Vector<*> decompilation and direct editation fix -- #1294 Font editation (DefineFont2/3) - correct switching of wide character codes -- #1302 Callpropvoid instruction docblock not correct -- #1309 recent files not getting updates - -## [9.0.0] - 2016-08-12 -### Added -- Instance metadata (AMF3) editing in PlaceObject4 -- [#1156] Flash Viewer - DefineScalingGrid support (9-slice scaling) -- [#1171] Export stroke scale to FLA -- FLA export - check invalid unicode characters -- [#1170] Extract from memory in commandline -- Reload one vs Reload all buttons -- ABC: Float and Float4 support -- AS3 p-code instruction documentation in GUI -- [#1241] Settings to show original bytes in hex view -- Search in dump view -- Jump to resources view from hex view -- Show warning on 32bit JRE - -### Changed -- [#1162] improved opening loaded SWF files -- Flash Viewer - skip frames when not on time -- [#1199] Automatically import alpha channel to JPEG3/4 from PNG - -### Fixed -- [#1151] Filters on texts fixed -- [#1128] Adding characters to font fixed (FontAlignZones not removed) -- [#1163] Clicking open->file makes program buggy -- Refresh tree after raw edit -- [#1172] Text double escape fix -- [#1174] Change language fix -- some AS2 deobfuscation fixes -- [#1183] Index out of bounds fix -- Implicit coersion on binary/unary opfix -- debugger: corect display variable values through getters -- Multiple XLF export fix -- [#1193] FLA export - text tag advance fix, one layer shape fix -- [#1193] FLA export - smoothed image detection, export raw JPEG data -- [#1193] Export space character to TTF correctly -- [#1200] Previous search text selected when quick find -- Flash viewer: aspect ratio on startup fix -- [#1198] Saving trait slot const value -- Zoom parameter commandline fixed -- [#1205] clipping fixed -- [#1194] Wrong sound effects in FLA -- [#1210] Frame Export fix -- Improved/fixed go to declaration in AS3 -- [#1217] PCode window not in same position as AS -- Hide memor search on non windows platform -- [#1244] Incorrect showing of NOP instructions -- [#1244] Remove unknown actions when deobfuscation is enabled, compole unknown instructions back -- [#1241] File content is different from hex view -- [#1247] Incorrectly handling remainingbytes for DefineCompactedFont -- [#1236] won't open fixed -- [#1251] SWF not same after export XML and import back -- [#1265] Error during export -- [#1268] Font export - Using second glyph when two glyphs for one character found -- [#1268] GFX compacted font - fixed advance values on export - -## [8.0.1] - 2016-02-20 -### Changed -- FFDec debug tab in advanced settings moved to other tabs - -### Fixed -- [#1161] AS1/2 deobfuscation broken -- AS1/2 Simplify expressions fix - -## [8.0.0] - 2016-02-18 -### Added -- Debugger - AS1/2 Show registers -- Debugger - display variables in the tree structure -- Debugger - set value of variable -- Debugger - AS1/2 View constantpool -- Debugger - P-code level debugging for both AS1/2 and AS3 -- Basic SVG import for shapes -- Simplify expression setting -- [#1118] Loading characters through ImportAssets - show as readonly -- [#409], [#1132], [SkinPart] metadata support - decompilation and direct editation in AS3 -- [#1134] compiling §§ instructions back while direct editation (§§goto is still missing) -- [#1121] Ability to save binary data by its name -- [#1052] Add object to existing frame -- Allow adding tag to main timeline -- AS1/2: Ctrl+click to declaration of variables, registers -- Allow trait specification in pcode import -- Icons for tag types in Dump view -- Show error message when a text tag is invalid (glyph index problem) -- AS3 direct editation - store local register names in debug info = allow to rename them - -### Changed -- New application icon and splash screen -- [#1145] AS3 better declaration type detection, better convert_x instruction handling -- Binary export - use .swf extension for swf files -- Better tree labels in generic tag editor (Raw edit) -- [#758] Allow zooming more than preview area in internal viewer - -### Fixed -- [#1096] FLA export - pretty print -- [#1104] AS1/2/3 Script Importat not working -- [#1107] Text Offset Incorrect fixed -- [#1106] New Shapes replace function fix -- [#1113] It takes too long to switch between rendered sprite -- [#1075] Lenght of DefineText in some cases -- [#1127] autoRenameIdentifiers is not supported in CLI mode -- [#1128] Letterspacing bug (after font embed): ignore letterspacing when character changed -- [#1103] Foreach variables fixes -- AS3 Switch fixes -- Default clause position in switch -- [#1133] Incorrect frame order for nested sprite -- [#1135] Handle try "to" in p-code correctly -- Font wideOffsets,wideCodes fixed in DefineFont2/3 -- AS3: super method call -- [#1138] All exported videos are the same file which may be broken -- [#1139],[#930] Windows Installer: Correct ActiveX download link, Download latest java from webpages -- [#1137] running flashplayer(debugger) executable in Linux/MacOs -- [#1144] Command line argument renameInvalidIdentifiers -- [#1145] double not (!!) not removed -- [#1147] Sprite is exported incorrectly -- [#1148] handing end of stream exception in abc reader, loc exception -- [#1152] Font info tag modified tag was not set => saved swf was corrupt -- [#1154] Some 32bit JRE problems - program won't start -- [#1145] Correct precedence handling on binary operators -- [#116] not resolving unusual tags in DefineSprite - -## [7.1.2] - 2015-12-03 -### Fixed -- AS3 debugger start halt fix -- AS1/2 debugger fix on nondebug enabled SWFs -- AS1/2 debugger fix for functions -- Debug menu item enabled fix -- AS3 local reg index fix -- Advanced settings calendar -- AVM2 instructions in hex view -- [#1070] Incorrect switch decompilation -- [#1098] Import XML fix - -## [7.1.1] - 2015-11-23 -### Fixed -- Critical debugger fix - widelines - -## [7.1.0] - 2015-11-23 -### Added -- AS1/2 debugger -- Breakpoint/IP marker on line beginning - -### Changed -- Starting debugger on demand -- Installer message about playerglobal is only warning now - -### Fixed -- [#1033], [#1083] AS3 deobfuscation issues -- [#1091] AS 1/2 direct editation saving - -## [7.0.1] - 2015-11-18 -### Fixed -- Debugger: Adding breakpoint if script initializer not displayed - -## [7.0.0] - 2015-11-18 -### Added -- AS3 Debugger - breakpoints, stepping, show variables -- Faster AS3 direct editation - -### Changed -- Better Configuration of flashplayer paths - -### Removed -- Removed old "debugger" buttons -- Removed search from browsers cache - inactual code - -### Fixed -- Many AS3 direct editation related bugs -- [#1076] export fix - -## [6.1.1] - 2015-10-30 -### Fixed -- Deobfuscate AS3 metadata -- [#1068] MorphShape with focal gradient fix, FLA XML export formatting fix -- [#1063] AS3 direct edit - script initializer fix, generating method names -- XML export/import fixes -- [#1019] Namespace imports fix -- AVM2 code execution fix -- [#1016] AS3 direct editation fixes -- [#1010] AS2 direct editation - internal and override is not a reserved word -- [#1008] pushshort instruction diassembly -- [#1004] this/super can be AS1/2 variable -- [#933] AS3 allow numbers as object literal keys - -## [6.1.0] - 2015-10-26 -### Added -- Open other loaded SWFs during playback (useful for loaders) -- Export uncompressed data from dump tree -- Print performance statistics from commandline -- [#1062] Editing/displaying script initializers -- Enable debugging on SWF file (commandline) - -### Changed -- Faster syntax highlighting -- Better AS1/2 deobfuscator -- [#418] AS3 deobfuscator improved - -### Fixed -- AS call method fix (first parameter is "this") -- [#1047] open all scripts folders -- [#812] decompile fail -- [#1056] deltaY missing when adding a new StraightEdgeRecord -- [#1057] Editing as in editor results in package name moving -- [#991] GUI export -- [#689] Ignore Case not correctly toggled -- [#1060] reversed and/or detection in some cases -- [#1037] isXML call - -## [6.0.2] - 2015-09-12 -### Added -- AS3: Display and direct edit trait Metadata -- Allow to specify tag type on image or shape import -- Convert image tags from commandline -- [#489] Hex decode very large integers -- Add new tags without show empty folders -- Dependent characters in basic tag info -- [#1007] replace bytearray in raw editing -- Italian translation - -### Changed -- AS2 parser - add string to constant pool if there is not enough space -- [#1044] AS2 - order scripts by physical location, name by offset - -### Removed -- Deprecated commandline parameters removed - -### Fixed -- JNA problems on some JDKs -- [#947] Marklevels errors ignored 17a94b7 -- [#953] Mac application permission fix (maybe) -- [#954] IndexOutOfBounds fix -- [#950] AddTrait setting modified fix -- [#945] AS1/2 directeditation fix - member named as global function -- [#957] AS1/2 IndexOutOfBounds fix -- [#956] Invalid jump offsets warning -- [#968] Sprites export with wrong coloring -- [#978] case sensitivity of filenames -- [#955] AS2 decompilation problem -- Image alpha fixes -- [#966] Go to document class -- [#991] scripts exporting -- [#999] save as fla -- [#1000] image export for malformed JPEG3 tags -- [#1017] store alchemy opcodes with wrong order -- [#1030] stack overflow fix - -## [6.0.1] - 2015-07-06 -### Added -- Special §§ instructions marked as red -- [#949] Replace alpha channel from commandline -- AS3 deobfuscation from commandline -- Option to ignore FlashCC/Alchemy packages - -### Changed -- [#944],[#991],[#939],[#942] AS3 deobfuscation improvements - -### Fixed -- AS1/2 deobfuscation fixed -- [#952] Not loading SWF without extension - -## [6.0.0] - 2015-07-04 -### Added -- New AS3 deobfuscation method -- Internal "preprocessor" §§ actions introduced - §§pop,§§push,... -- Allow reload FFDec when no SWF is opened -- [#858] Allow to set compression type in header -- [#905] Show codec details for sound items -- Better alchemy/DomainMemory instruction handling -- Better obfuscated names handling -- [#920] Export instance name to SVG -- [#921] Export html DefineEditText to SVG -- Open multiple files with drag and drop -- Better "multi packs" handling (Alchemy) -- SWF version 29 to flash player 18 mapping -- ImportAssets2 sha1 field -- [#924] Sprites to image from commandline -- AS1/2 direct editation big numbers fix -- Allow to add FILTERs and SHAPERECORDs in generic tag editor -- Enable close all menu when no swf is selected -- Restore modified state even when something goes wrong -- Some old tags added - -### Changed -- AS decompilation highly improved -- Better &&, || handling -- DoABCDefine renamed to DoABC2 -- Separated Sprite export settings - -### Deprecated -- Old AS1/2/3 deobfuscation method marked as deprecated (can be enabled back deep in the configuration) - -### Fixed -- Many decompilation problems - EmptyStack exception, Maximum recursion level reached, etc. -- Few menu issues -- [#895] Correct handling CMYK JPEG -- [#884] AS direct edit assignment -- [#899] Show script after AS3 direct editation -- Some AS1/2 parser problems -- [#903] FLA export - fix for missing fontname, lastframe -- [#855] AS3 direct edit - for..in variable declaration fix -- [#850] Constant initialization for same multinames -- [#832] AS3 direct edit - other ABCs resolving fix -- [#904] Cannot export images -- [#910] Missing instructions -- Opening not existent files on restoring last session -- [#922] Edit text leading -- Put image before shape on shape replace -- [#916] Replacing Shape corrupts SWF -- JRE setup parameters fixed -- [#938] Parallel speedup limit fix - -## [5.3.0] - 2015-05-25 -### Added -- Generic tag editor: improved table editing (import/export assets tags, etc.) - -### Changed -- Classic (nonribbon) UI improved - has same items as Ribbon UI -- Icons improvements -- Disabling menu items when work in progress - -### Fixed -- [#897] Classic UI fix - -## [5.2.0] - 2015-05-22 -### Added -- UI8 editbox for swf version in header panel -- Basic tag information panel - -### Changed -- AS1/2: Shown only the constant pool(s) in pcode editor -- Do not allow to chage tag tree selection, when current tag is under editing -- Faster bitmap export -- Using less memory when playing sounds -- Error message changed when the opened file is not swf - -### Fixed -- [#470] panels size after resizing from/to full screen -- [#877] A small glitch after search in AS -- [#878] small glitch after saving P-code or swf file -- [#470] glitch -- [#845] If frame consist 2 DoAction then it imports only first one -- pdf export (when no frame exists) -- text rendering (alpha channel was ignored), -- bmp export (paddings when width%2==1) -- [#883] -dumpSWF option does not work anymore -- [#882] Canvas export border size -- [#760] Internal viewer line linear gradient fill is not working -- [#887] error on export a special swf's P-CODE -- Extensions of exported images fixed - -## [5.1.0] - 2015-05-04 -### Added -- Allow to copy/move multiple tags, and dependencies -- [#842] For reconstruction if debug line info present -- [#841] Loop control for sound preview -- [#845] Import exported AS1/2 (DefineButton2&DefineSprite) button -- Scrollbar added to fontpanel -- SWF header editor -- Configure what object types to export in exportdialog - -### Changed -- Better gif exporter -- [#772]: closing loading dialog now cancels the loading of the swf -- [#762] export pcode with different extension - -### Fixed -- CRITICAL: Update System Bug causing updates not working -- [#862] AS3 asm: do not read beyond return/throw instructions -- [#865], [#613]: ribbon prefered width fix -- [#868] export path fix, allow to export buttons -- [#865] TagTree font size problem on high resolution screens -- [#713],[#807] Installer for 4.0+ fails to access Adobe Website -- [#728] Large fonts, [#857] add scroll on DefineFont3 - -## [5.0.2] - 2015-04-18 -### Added -- Reopen last session - -### Fixed -- ffdec.sh file line endings fixed - -## [5.0.1] - 2015-04-18 -### Fixed -- [#860]: Opening bundle (zip, swc, any binary file) files fixed - -## [5.0.0] - 2015-04-18 -### Added -- Color skins -- [#824] Mac OS X package -- [#809] Move left,right buttons for DefineTexts using translatex parameter -- [#805] Editor mode for DefineTexts -- [#825] Hotkeys for next/previous DefineText -- Export/Import symbol classes/export asset tags -- Frame export progress -- [#737] Single file script export -- Displaying changed AS3 scripts in GUI as bold -- Additional character info tags placed under character node -- New icons for other tags (metadata,fileattributes,setbackground,place/remove) -- Metadata tag editor - -### Changed -- Default color skin altered -- [#350] Allow only one running instance (Windows only, can be turned off) -- SWFs in zip based bundles (SWC for example) can be modified & saved -- Performace improvements -- More compact SWF-XML format -- Marking changed parentnodes as bold too - -### Fixed -- [#814] Exporting with scale problem -- [#816] P-code not shown after class initializer trait selection -- [#835] Static initializer improvements -- AS3 direct editing - local register decrement fix -- AS3 direct editing - maintain register order/names -- [#836] AS1/2/3 Correct expression precedence handling -- AS3 preincrement -- [#848] Correct toggling text switches -- [#817] AS1/2 for..in variable declaration -- [#849] Attribute member -- [#852] Ignore case for russian characters -- [#837] AS3 try..finally without catch - -## [4.1.1] - 2015-02-21 -### Added -- Export/Import XML added to ribbon menu -- Few GUI enhancements -- Undo tag changes context menu - -### Changed -- Java 8 now required - -### Removed -- Removed support for Java below 8 - -### Fixed -- [#811] export ActionScript - -## [4.1.0] - 2015-02-18 -### Added -- XML export/import -- confirmation dialogs added -- Add support for non-standard ABC-compressed SWF file -- [#745] Copy tag to another SWF -- [#803] Align text in DefineText - -### Changed -- performance improvements -- [#758] Zoom to fit is dynamic - -### Fixed -- [#738] Frame export -- [#742] Can't edit frames -- [#747] Move tag to adds extra frame -- [#749] Internal viewer Sprite fill color -- [#752] Sound is not stopped -- [#753] Reload swf -- [#759] Decompilation § symbol -- [#766] Can't extract all resources -- [#768] Super calls not being correctly recognized -- [#773] Scripts associated with ClipActions are not shown -- [#776] Stop working after setting "number of threads" to 0 -- [#783] No OK box when edited script or text was saved -- [#785] Text search. Remember last choise, Unicode case insensitive search -- [#787] Search in AS bug (when navigating to searched results) -- [#788] Add DefineCompactedFont Tag to gfx file -- [#790] Impossible to change letters under a font -- [#794] Font extraction fails sometimes -- [#798] Close file streams after export, exporting progress -- [#800] Unexpected deleted carrier return in DefineEditText -- Build fix on Linux -- Fis Startup Script for OpenJDK -- Other minor fixes - -## [4.0.5] - 2014-12-01 -### Added -- Escape control characters in strings, identifier names -- [#676] import text error messages / logging enhancement -- [#734] \xAB escapes, \uABCD escapes -- [#687] AS3 - allowing p-code comments on separate line -- [#709] Text Export to Single File with custom filename - -### Fixed -- [#732] Random freezing - JavactiveX library updated. -- [#730] Not working without ActiveX fix on Windows -- [#735] Automatic deobfuscation not correctly switched (required restart) - -## [4.0.4] - 2014-11-23 -### Changed -- better file cache, removing unneccessary temp files - -### Fixed -- obfuscated identifiers - -## [4.0.3] - 2014-11-23 -### Added -- [#722] Go to next/previous frame -- BMP file format export (images,frames,shapes) and import(images) - -### Fixed -- [#725] various AS direct editation bug fixes - namespace compilation, AS 1/2 strict equals, submethod scope, unbounded type -- [#715] namespace resolving fix -- [#635],[#726] placing cursor inside Unicode characters - -## [4.0.2] - 2014-11-22 -### Added -- show frame number during play -- flashplayer - show controls for DefineSprite -- goto frame -- [#716],[#717],[#718] Proxy - save SWF, replace, copy URL, filesizes, table design - -### Changed -- [#720]: edited shape tag is not marked as modified after replacing -- reorganized about dialog - -### Fixed -- [#719] null swf name in Proxy after cancelling rename dialog -- flashplayer - font display -- [#723]: saving swf with invalid referenced characters -- DefineCompactedFont paging -- [#288] Less memory usage during FLA export -- Corrected syntax hilighting for AS3 P-code - -## [4.0.1] - 2014-11-12 -### Fixed -- [#713] Installer can continue when no file can be downloaded -- Fixed shapes -- Checking for updates moved to separate thread - -## [4.0.0] - 2014-11-11 -### Added -- [#677] Zoom level in export settings -- internal viewer: linear/srgb gradients -- zooming buttons for flashplayer/internal viewer -- stroke scaling modes for canvas export -- create snapshot button -- [#389] Selecting font face on import -- [#701] Importing font from TTF file -- Reorganized font panel -- [#707] Debugger for logging messages -- [#302] AS3: Better Ctrl+Click handling with underline, more declaration targets -- [#685] Getting local register names from debug info can be disabled -- Adding new tags -- [#698] Allowing unicode letters in identifiers -- [#710] Information about deobfuscation in error comments -- One EXE for 32/64 bit, uses percentage memory. -- EXE SplashScreen -- New Improved Windows Installer (NSIS) - can install Java and FlashPlayer, download playerglobal.swc -- Config setting to load inner SWFs automatically -- Replace shape with image - -### Changed -- better FlashPlayer integration using JavactiveX library -- Faster building tag tree -- Faster timeline construction -- [#711] Improved folder view - faster and with correct context menu - -### Fixed -- AS2 deobfuscation fixes -- AS2 loops fix -- [#681] Linux script fixes -- AS2 constructor name fix -- [#688] AS3 direct edit fixes -- [#691] AS3 p-code reading/saving fix -- AS3 direct edit -submethod name resolve fix -- frames to html canvas fix -- [#524] Mask layer not applied when nonempty script layer -- [#663] AS3 imports fixes -- Font export of dot character -- Font panel Yes button fixed -- [#702] GFX font reading fix -- Better obfuscated names handling -- [#539] for(each) in declaration fixes - -## [3.0.0] - 2014-09-20 -### Added -- Separated GUI (GPL) and library (now LGPL) -- Editing obfuscated identifiers via new paragraph(§) syntax -- Timeline View with preview and object hilighting -- Show GFX data in dump view -- [#650] New parameter to replace binarydata, images, sounds, scripts from commandline -- Dump view - selecting node -- [#680] Loading subSWFs from binaryTags now optional (button/context menu) to avoid unnecessary memory consumption - -### Removed -- Removed deprecated commandline export formats (see --help) - -### Fixed -- FileAttributes tag reading fix -- [#649] GFX reading fixed -- [#656] Search in memory - 64 bit processes fix -- [#661] scripts not showing -- [#664] expanding fillStyles in raw edit -- [#668] add missing character fix, text tags fix -- [#674] texts hilighting initialization fix -- [#675] AS1/2 and/or operator compilation -- [#632] Locking file after opening (cannot save, etc.) -- [#651] Unnecessary removing expression killed in unreachable part -- [#678] Windows batch file paths fixed -- [#672] Disabling transparency slider on RGB only selection -- [#684] Sound streams inside DefineSprites, soundstream handling - -## [2.1.4] - 2014-08-23 -### Added -- AS1/2: New method for deobfuscation (can be switched off in settings) -- AS1/2: Using eval/set on invalid identifiers, quotes in function names/parameters - -### Fixed -- [#647] Skipping FileAttributesTag with Parallel speedup on -- [#648] Export from embedded SWF - -## [2.1.3] - 2014-08-18 -### Added -- Show "save" and "saveas" in application menu -- Saving data range in dump view -- Show actions, abcdata in dumpview (context menu on the tree node) -- [#612] show color in hex format - -### Changed -- Faster dump info collecting (less memory) -- Allow selecting multiple files in open file dialog - -### Fixed -- [#623] ffdec.sh UNIX file endings, executable -- [#624] search in embedded swf files -- [#632] AS1/2 Unnecessary GetVariable before NewObject -- [#627] filter swf not working -- LZMA saving -- Export pcode&hex from commandline -- [#640] text import fixed, ignore BOM - -## [2.1.2] - 2014-07-20 -### Added -- Dump view -- Context menu: Jump to character, raw edit all tags -- Catalan translation -- SWF header display - -### Fixed -- [#595] AS3 direct edit - Getter/Setter generation - caused FlashPlayer crashes -- [#592] AS3 Multiname resolving in P-code causing different bytecode -- [#585] AS3 moving popped values to output -- [#578] Always on top fixed on search results -- [#501] GotoFrame2 fix -- [#616] Frames to PNG export -- Export context menu -- [#559] Bitmap export opacity -- [#401] Placeobject 3/4 fix -- [#593] Return object newline -- [#594] Setting for curly brace - -## [2.1.1] - 2014-06-05 -### Added -- [#302] Find declaration (Ctrl+click, Ctrl+B), Find usages (Ctrl+U) - Works only for exactly same multinames, not local registers -- AS1/2 direct edit - global functions improvements -- AS1/2 negate operator, unary minus operator -- Opening SWFs in BinaryData tags -- AS1: Old string operators support, and/or, <> operator (editation) -- Statusbar loading animation improved -- [#579] AS3 direct editation - removing old class/methods from ABC -- remove character without the dependencies (remove only the place/remove tags) -- Running on system with no home directory -- [#428] PDF export (as images only) -- Commandline FlashPaper to PDF export -- Select frames / Characters commandline options - -### Changed -- [#337] quickfind visibility improved -- [#584] commandline script export - select whole packages (use .+ at the end of -selectas3class) - -### Fixed -- [#576] AS1/2 direct editation: DefineFunction2 fix -- AS1/2 property fix -- AS1/2 typeof operator fix -- [#250] line spacing fix -- PlaceObject 3-4 className -- [#579] AS3 direct editation bugfixes - property resolving, integer values -- Morphshape canvas export fix -- Canvas export fix - closing path -- [#580] Rename invalid identifiers commandline fix -- [#510] JSyntaxPane find and replace dialog wrap around fix -- No more frame caching during export => memory saving (like [#583]) -- [#586] DropShadow filter fix -- Canvas export colortransform fix - -## [2.1.0 update 2] - 2014-05-08 -### Added -- AS3 decompilation/editation: Vector initializers -- AS3 direct editation: more classes in one file - -### Fixed -- [#574] DefineSprite editing fix -- Various AS3 direct editation fixes - -## [2.1.0 update 1] - 2014-05-05 -### Added -- Portugese-brasilian translation - -### Changed -- HTML Canvas export improvements - -### Fixed -- Various AS3 direct editation bugs, like [#570] - -## [2.1.0] - 2014-05-01 -### Added -- AS3 direct editation (Experimental!) -- Frames SVG Export -- Shape/MorphShape/Frames HTML 5 Canvas Export -- [#559] morphshapes as animated SVG -- [#563] Single file text export/import -- Font WOFF export -- Advanced settings dialog with tabs, config names, descriptions - -### Fixed -- [#561], [#509], [#433]: AS3 EmptyStackException fix - correct hasnext2 arguments -- Internal viewer: Filters fix - -## [2.0.1 update 2] - 2014-04-05 -### Fixed -- [#557] AS3 null namespace fix - p-code not working - -## [2.0.1 update 1] - 2014-04-04 -### Fixed -- [#556] Goto main class on startup fix -- [#557] Nullpointer fix (private namespaces) - -## [2.0.1] - 2014-04-03 -### Added -- Thumbnail view -- Font TTF export -- Exporting frames: PNGs, AVI, GIF (via Internal flash viewer) -- Expand all context menu -- Internal viewer: Button mouse move and click handling -- Playing sounds without flash player -- Internal viewer: Sounds on stage -- All sounds to WAV export -- Internal viewer: Showing texts, dynamic text border/fill -- [#504]: Unicode characters in JSyntaxPane -- Internal viewer: showing object under cursor -- Folder icons -- Sound/Image format on command line. -- Removing placeobject tags -- Removing frames -- AS: "elseif" statements -- Code formatting: space before parenthesis - -### Changed -- Single frames animated. - -### Fixed -- [#529] limit the number of displayed binaryData bytes -- [#538] Interface are sometimes dynamic -- [#537] super is sometime preceded by a dot -- [#540] Saving SWF changes very large static uint values -- [#387] Frames preview bugged -- AS:loop mismatch fix on parallel speedup -- [#552] Some timeout exceptions -- [#494] Fixed nightly builds updates - -## [2.0.0] - 2014-03-02 -### Added -- Generic tag tree editor -- Timeline view (stub only) -- FLA export to CS5, CS5.5, CC format (previously only CS6 was supported) -- [#513]: command line option to extract swf from binary file -- Configurable code formatting (Indentation + brace position) -- [#262] Export FLA: Font character ranges export -- Configurable checking for updates - -### Changed -- Improved Internal Flash viewer - better shapes, morphshapes, DefineEditText tag, clipping, blend modes -- Improved commandline usage -- Automatic deobfuscation default value set to False (See News on webpages) -- Check for updates can be configured to inform about Nightly builds aswell - -### Deprecated -- Some commandline options are now deprecated, see --help - -### Fixed -- [#499] Cannot save via Proxy fixed -- [#504] font name reading fixed -- [#508] Support for OS without GUI -- [#305] Export FLA: empty sound layers -- [#312] Export FLA: Improved Shape/MorphShape fix -- [#503] Export FLA: Smoothing invalid shapes -- [#401] Invalid GFX tags in non GFX files -- [#304],[#306],[#507],[#424],[#425],[#478],[#485],[#517],[#518] Many direct AS1/2 editing issues -- [#361] FFDec icon is not visible on application start -- [#392] Video stream data fix -- [#516] AS3 P-code editor - Null name namespace handling - -## [1.8.1 update 1] - 2014-02-02 -### Fixed -- [#495] font embedding fix -- [#496] date format in new version dialog -- cosmetic changes - -## [1.8.1] - 2014-01-30 -### Added -- [#299] replace DefineBits images -- [#303] open folder with exported FLA -- [#324],[#346] SWC/zip/other binary file support -- [#371] detailed logging -- [#426] command line switch to rename identifiers -- [#457] clear recent opened files list -- [#458] save selected system font for swf fonts -- [#460] text editor: do not scroll to the end automatically -- [#462] font embedding dialog: show more sample characters -- [#463] global search in texts -- [#465] make font properties editable -- [#466] font preview - -### Changed -- [#369] new SVG and preview image rendering -- [#390] refresh font list without reloading the application -- [#453] update texts aftert adding new character to a font tag -- [#459] remember text panel splitter position -- [#461] font panel gui redesigned - -### Fixed -- [#451] dialog windows are not on the center of the screen -- [#454] Text syntax highlighting -- [#455],[#465] classic interface issues -- [#474] changeing language only available one time -- [#477] log window localization -- [#481] SVG export fix -- [#484] Oversized advance value after editing DefineText with DefineFont2 font -- [#493] missing search results - -## [1.8.0 update 1] - 2013-12-27 -### Added -- [#453] refresh (edit+save action) all texts button - -### Fixed -- Flash panel and font panel fixed - -## [1.8.0] - 2013-12-27 -### Added -- [#350] Allow to open multiple SWFs -- [#365] Filter fake SWFs during memory search -- [#366] Allow to sort the result list in memory search window -- [#429] Auto rename invalid identifiers setting -- [#447] Non-ribbon interface - -### Fixed -- [#354] Infinite decompilation fixed -- [#438] Case sensitive Command line arguments fixed -- [#436] Saving actionscript fixed -- [#446] Precedence issue fixed -- [#451] Dialogue window positions on a multi-monitor configuration fixed - -## [1.7.4 update 1] - 2013-12-05 -### Added -- [#426] Command line parameter for renaming invalid identifiers - -### Fixed -- [#427] Exception on linux fixed -- [#405], [#420], [#421] Some decompilation issues fixed -- [#430] Configuration default value problem fixed -- [#397], [#431] Deobfuscation stucked sometimes problem fixed - -## [1.7.4] - 2013-11-10 -### Added -- [#169] hexedit method body bytes -- [#335] last opened files -- [#404] Exporting P-code and Hex + console parameters -- [#407] register name is configurable -- Advanced settings -- Cancellable decompiling, exporting and searching - -### Fixed -- [#399], [#400] performance optimizations - -## [1.7.3 update 2] - 2013-09-29 -### Fixed -- [#398] AS3 p-code values with index 0 (null) - -## [1.7.3 update 1] - 2013-09-28 -### Added -- [#382] AS3: Adding new method - -## [1.7.3] - 2013-09-27 -### Added -- AS3: Multiname and namespace editing. -- [#382],[#396] AS3: Adding new trait (method/slot/const) -- AS3: Highlighting pair parenthesis/bracket -- AS3: Editing various new P-code parameters -- AS3: Highligting of trait names/types/parameters -- AS3: Global rename identifier for traits -- [#357] Playback controls for DefineSound -- [#391] AS3: Native methods mark -- [#395] Support for GFx ScaleForm SWFs (with fonts editing) -- Displaying fonts in internal viewer -- [#334], [#395] New Embed font dialog - selecting character ranges with preview -- Replacing characters in font (Yes/No to all dialog) - -### Changed -- AS3: New p-code syntax inspired by RABCDasm -- AS3: Editing whole trait in one textarea -- AS3: Removed messages about adding new constants -- AS3: Modified colors in editor -- [#301] Clearing error log causes icon to reset - -## [1.7.2 update 2] - 2013-09-13 -### Changed -- Updated translations - -### Fixed -- [#383] Firefox browser cache handling -- [#386] SWF resizing - -## [1.7.2 update 1] - 2013-09-11 -### Changed -- updated translations - -### Fixed -- [#383] Fixed cache loading when Firefox not used - -## [1.7.2] - 2013-09-11 -### Added -- [#357] Sounds Preview (Windows only) -- Movies preview (Windows only) -- Whole SWF display -- Preview controls (Play,Pause,Stop) -- Search SWFs in browsers cache (Firefox, Chrome) -- [#367] Memory search: Save selected files to disk -- Portugese translation - -### Changed -- [#380] Faster displaying DefineBitsLossless(2) images - -### Fixed -- [#292] Background color for Fonts preview fixed -- [#375] Replacing DefineBitsLossless image tag -- [#378] Refreshing language of JSyntaxPane -- MORPHGRADIENT reading fix - -## [1.7.1] - 2013-08-25 -### Added -- Loading SWFs from other processes memory (Windows only, sorry) -- [#325] Spanish translation -- [#210] Ukrainian and Dutch translation -- [#355] Chinese translation -- [#292] Change background color in SWF preview -- [#301] Clear errors log button -- [#313] Command line parameter for ignore all errors -- [#330] Protection agains adding same characters -- [#332] AS1/2 Showing elapsed times during commandline export -- [#344] Reload opened SWF -- Decompilation timeouts - -### Fixed -- [#295] Export FLA: wrong font -- [#297] Too bright titlebar button colors -- [#307] Export FLA: fixed empty textfields -- [#309] Export FLA: static text letter spacing detection -- [#310] Export FLA: Strokes -- [#311] Export FLA: BitmapFill -- [#327] AS1/2 Disassembly error stop application -- [#328] Fixed replacing images in DefineBitsJPEGX -- [#333] AS1/2 action reading -- [#336] Graph window is too small -- [#337] Quick search panel barely visible in same cases -- [#338] Expand/collapse icon in errorlog - -## [1.7.0 update 1] - 2013-08-11 -### Added -- [#315] German translation (partial) - -### Fixed -- [#123] Better context menu integration -- [#243],[#326] Better deobfuscation -- [#287] Typo in parallelSpeedUp parameter -- [#290],[#291] improved select language dialog -- [#294] minor GUI fixes -- [#298] Progressbar positition issues -- [#296] better export directory remembering -- [#314] Better deobfuscating filenames -- [#316] Readonly editor panes accepted Ctrl+Z/Y -- [#318] Export FLA: Shapes export fix -- [#319] AS3: Improved try..catch..finally decompilation -- [#323] AS3: Fixed default switch part - -## [1.7.0] - 2013-08-03 -### Added -- Listing contributors on about page -- [#223] AS2: Detecting uninitialized class fields -- [#250] Export FLA: Detecting static fields margin and spacing -- [#261] Export FLA: AS1/2 Frame scripts on first layer -- [#269] Commandline parameters for switching configuration -- [#274] AS3 Displaying elapsed time during commandline export -- [#275] AS3 Removing returnvoid as last statement - -### Changed -- New GUI based on Substance look and feel -- Menu changed to ribbon panel -- New round icon -- [#258] AS1/2: Improved chained assignments -- [#267] Starting program without choosing a file -- [#286] Saving to temp file first - -### Fixed -- [#123] Improved context menu integration on Windows -- [#233] Globally rename identifier deselects item in the tree -- [#235] Export FLA: Dynamic text fields coordinates -- [#243],[#263],[#264],[#265],[#266],[#281] Improved deobfuscation -- [#251] Export FLA: Fixed filter strength rounding -- [#257] Export FLA: Text field color and size issues -- [#259] Fixed images alpha -- [#260] Export FLA: Labels position -- [#268] AS1/2 Function parameter shown as register instead loc -- [#272] AS3 Class initializer editation fix -- [#276] Fixed anonymous/inline functions handling -- [#220] Improved editing fonts / texts -- [#284],... other small fixes - -## [1.6.7] - 2013-07-20 -### Added -- [#220] Selection of font to import characters from -- [#232] Automatically add .swf extension in saveas dialog -- [#253] Abort/Retry/Ignore dialog on errors with file saving - -### Changed -- Improved translations - -### Fixed -- [#137],[#242], [#243], [#244] AS1/2/3 fixed deobfuscation -- [#203] AS1/2 improved direct editing -- [#220] Adding characters to font fix -- [#225] AS1/2 object literal without name quotes -- [#236] AS1/2 Rename invalid identifiers issues -- [#245] AS3 Double space around "as" keyword -- [#247] AS3 Scrolling to main class at startup -- [#248] Memory issues (slowdown) -- [#254] Expressions as commands -- [#255] Windows 7 loading issues -- [#256] AS3 Object literal in return clause -- SWF text parsing (new lines) -- Labels size by locales - -## [1.6.6 update 2] - 2013-07-16 -### Fixed -- [#241] Program could not be started - -## [1.6.6 update 1] - 2013-07-16 -### Changed -- Better localization support - -### Fixed -- [#238],[#239],[#240] Fixed deobfuscation related problems -- [#237] Parentheses in AS1/2 add,subtract - -## [1.6.6] - 2013-07-16 -### Added -- [#217] Russian translation (focus) -- [#219] Hungarian translation (honfika) -- [#224] Swedish translation (Capasha) -- [#220] Adding characters to Fonts, displaying font info -- [#121] Search progress indication -- Error log - -### Changed -- [#203] Improved direct editing of AS1/2 -- [#207] Update SWF preview after switching external/internal flash player - -### Fixed -- [#151] Memory caching -- [#171] Skipping invalid AS3 code - newobject, newarray -- [#206] AS3 switch problem -- [#208] Renaming anonymous functions -- [#209],[#229] FLA export texts positions -- [#213],[#221] other decompilation issues -- [#225] AS object literal broken with ternar operator -- [#226] onClip indentation in FLA export -- [#227] gotoAndStop wrong frame index -- [#230] FLA export missing strokes -- Shapes viewer - missing strokes - -## [1.6.5 update 1] - 2013-07-09 -### Fixed -- [#151] Fixed caching in memory -- [#172] AS1/2 constant detection fix -- [#174] Renaming SymbolClass fix -- [#175],[#212] Fixed create directory issues on export -- [#185],[#186] on-clip actions indentation -- [#197] AS1/2 Missing storeregister before switch -- [#216] AS2 Fixed field order -- [#213] AS2 Fixed var fields quotes, switch nullpointer - -## [1.6.5] - 2013-07-08 -### Added -- Multilanguage support (currently English and Czech) -- [#151] Option for caching in memory instead of files -- [#168] Export selection in tree context menu -- [#176] option to show main class on startup -- [#177] saving window maximized state -- [#202] Removing tags other than DefineSprite - -### Changed -- [#173],[#190] Better renaming -- [#129], [#153] Better deobfuscation -- [#180] better error handling -- [#185],[#186] better displaying and exporting onclip actions - -### Fixed -- [#123] Better context menu integration -- [#136] FLA export - text sizes -- [#137],[#179] foreach issues (hasNext) -- [#144] Plain text export - empty line fix -- [#144] Not displaying texts -- [#164] DefineMorphShape issues -- [#167] Sprite tag appearing twice in export filename -- [#170] AS3 Try in loop -- [#172] loop detection fix -- [#175] use empty namespace -- [#178] AS subtract with negate -- [#181] AS3 missing quotes in object field -- [#182] missing namespace imports -- [#183] wrong stage size -- [#184] wrong video link -- [#189] Fixed three dots in tree -- [#191] Focalgradient fill fix -- [#195] AS2 issues -- [#196],[#197] switch issues -- [#198] DefineFont2 empty check -- [#200] DefineBitsLossLess fix -- [#201] Nonworking main window in Linux/MacOS (due toAssociation) - -## [1.6.4 update 1] - 2013-06-30 -### Fixed -- [#166] For loops detection -- [#165] AS3:direct lookupswitch support - -## [1.6.4] - 2013-06-30 -### Added -- [#63] Globally rename identifier -- [#67] Deobfuscation - rename identifiers according to type -- [#117] Drag & Drop SWF file to main window opens it -- [#123] Context menu integration on Windows -- [#127] Drag & Drop items from tree outside of application -- [#134] AS3: Find document class -- [#144] New lines in plain text export -- [#155] Remembering window size + splitbar positions between runs - -### Changed -- [#142] Using exportassets tag for tag names -- [#146] Display AS2 classes as tree of packages -- Better loop detection - -### Fixed -- [#129] AS1/2: not refreshing decompiled after rename -- [#130] Renaming SymbolClass identifiers too -- [#132] Renaming identifiers renamed strings -- [#136] Invalid text positions in FLA export -- [#145] Unicode support -- [#147] Escape filenames during obfuscated AS3 export -- [#148] Better package vs classname handling -- [#152] Empty if branches not inverted -- [#156] Better search handling (not freezing) -- [#157] AS3: Try statements in loops -- [#158] Graph repaint problem -- [#159] AS3: Improper rest parameter handling -- [#160] Commandline binaryData export -- [#162] DefineBitsJPEG2 image replacing -- [#163] Closing SWF file after loading -- other minor fixes - -## [1.6.3 update 2] - 2013-06-21 -### Changed -- [#149] Ifs with empty onTrue branches now inverted - -### Fixed -- [#150] Long line restriction removed - -## [1.6.3 update 1] - 2013-06-21 -### Fixed -- Memory limit decreased - FFDec was not working on 32 bit JVM. - -## [1.6.3] - 2013-06-20 - -### Changed -- Parallel SpeedUp can be disabled in menu -- Better loop detection - -### Fixed -- [#119] Replacements file not found issue -- [#101] AS1/2 postincrement fix -- [#114],[#116],[#135],[#141] Fixed loop detection -- [#102] Fixed loop highlighting in export -- [#124] Flash player file path detection -- [#128] Improved imports -- [#135] CommentItem fix -- [#129],[#131] Better deobfuscation -- [#104] AS3 inc/dec local deobfuscation fix -- [#113],[#133],[#140] Memory limit increased - -## [1.6.2] - 2013-06-09 -### Added -- New loop detection algorithm - -### Changed -- [#108] - Faster loading and decompiling (Parallelism) -- Improved Internal flash viewer - shapes and morphshapes - -### Fixed -- Ternar operator fix -- [#102] Fixed Shapes to FLA export -- AS1/2 class detection fix -- [#105],[#104],[#101] fixed via new loop detection - -## [1.6.1] - 2013-06-03 -### Added -- Internal Flash Viewer - preview of flash parts (shapes,sprites,frames) without need of Flash Player. (Used on nonWindows platforms by default) -- [#109], [#106], [#107] some code improvements - -### Changed -- Application needs Java 1.7 to run - -### Removed -- Support for Java before 1.7 - -### Fixed -- [#102], [#110] AS3: Class highlight fix -- [#103] AS3:Fixed setslot handling -- [#104] AS3:Inc/Declocal nullpointer fix -- [#104] Multiple conditions in loop fix -- [#111] AS3:Object literal truncates line -- [#105] Better do while..break handling -- loop fixes - -## [1.6.0 update 1] - 2013-05-25 -### Added -- better FLA export - -### Fixed -- Many FLA export related bugs (like [#96]) -- [#98] Empty initializers do not cause empty lines now -- [#99] small logging issues -- [#100] large obfuscated code support - -## [1.6.0] - 2013-05-20 -### Added -- Export to FLA (Experimental BETA!) -- [#85] Search text in all ActionScripts -- SWF 11 support - -### Fixed -- [#79] ActionStartDrag constraint fix -- [#92] Inversed GreaterThan/LessThan -- [#93] AS1/2 fixed switch detection -- [#94] AS1/2 ActionTry - register cast fix -- [#95] Better script end handling - -## [1.5.2] - 2013-05-05 -### Added -- Improved automatic update system (changes log). -- Handling script traits as separate objects. -- [#86] open/save file dialog now accepts absolute paths in quotes - -### Fixed -- [#87] Not displaying image changes in DefineBitsLossLess1 & 2 -- [#88] Fixed graph building -- [#89] AS3: bracket in property name lead to missing dot -- [#82] printgraph issue - -## [1.5.1 update 1] - 2013-05-04 -### Added -- Exporting texts via commandline -- Exporting all via commandline - -### Fixed -- DefineText2 color parameter -- AS3 GetSlot,SetSlot -- [#78],[#81],[#82],[#84] Fixed deobfuscation, exceptions during printgraph,... -- [#83] Fixed images transparency (zlib fix) -- Fixed graphparts with only jump in it (obfuscators) -- MORPHGRADIENT FIX -- Trasparency in DefineBitsJPEG3 and 4 -- Displaying shapes,morphshapes and sprites with bitmaps - -## [1.5.1] - 2013-05-01 -### Added -- Support for larger switches (10+cases) -- Editing text tags -- [#65] Exporting text tags -- Removing sprites -- Replacing images - -### Fixed -- DefineMorphShape2 fix -- [#79] - AS1/2 class detection fix, wrong printgraph fix -- [#78] - script trait slots fix - -## [1.5.0 update 1] - 2013-04-21 -### Fixed -- Automatic deobfuscation config defaulted to Off for AS1/2. - -## [1.5.0] - 2013-04-20 -### Added -- Direct editing of ActionScript 1/2 code (Beta) -- AS1/2: ifFrameLoaded support -- Automatic deobfuscation can be disabled in the menu -- [#48] - Decompile only specified class (commandline option) -- [#53] - AS3: Displaying multiname indices in trait detail, displaying method indices -- [#66] - Decompressing LZMA via commandline -- [#68] - Exporting DefineBinaryData tags, assigning class names to characters (SymbolClass tag) -- [#69] - DoABC vs DoABCDefine tags decompilation -- [#75] - Comma separator in while/do..while conditions, better if..return handling -- AS1/2: parsing NaN,Infinity value (Fix for [#73]) - -### Changed -- New icons (edit/save/cancel and main menu) - -### Fixed -- [#62] - Errors on not defined character tags (import tag) -- [#72] - First ternary operator expression was always true -- Fixed many deobfuscation related bugs - -## [1.4.3 update 2] - 2013-04-10 -### Fixed -- [#64] - AS1/2 Resolving registers in ActionDefineFunction2 (super,this,...and parameters shown as registerxx) -- Try to fix lib/FlashPlayer.exe issue - -## [1.4.3 update 1] - 2013-04-06 -### Fixed -- [#38] - Indentation in const/var initializers, missing semicolon -- [#56] - Test output left in last release -- [#57] - Unknown instructions now do not log an exception (obfuscators do this) -- [#58] - Index out of bounds exception fix on methodinfo indices in imports detection. -- AS3 loops fix -- While true fix - -## [1.4.3] - 2013-04-04 -### Added -- AS1/2 Better deobfuscation - -### Fixed -- [#45] - Unicode characters fix -- [#50] - AS1/2 Function body deobfuscation fix -- [#51] - Displaying java class names instead of expressions -- [#52] - AS1/2 Better constantpool detection (deobfuscation) -- [#38] - AS3 indentation in initialized const/var value for newobject -- Fixed ImportAssets2 tag id - -## [1.4.2 update 1] - 2013-03-25 -### Fixed -- [#47] - wrong AS3 deobfuscation -- AS3 deobfuscation issues -- AS3 switch - -## [1.4.2] - 2013-03-24 -### Added -- [#42] - Displaying code as hex -- AS1/2: Renaming identifiers (deobfuscation) -- AS1/2,AS3: Better deobfuscation -- Storing configuration to user home -- Installer for Windows systems - -### Changed -- Graph button changed to icon. - -### Fixed -- [#39] - AS1/2 NewMethod..Pop fix -- [#40] - No logging + For..in..return decompilation -- [#44] - DefineFont2 fix -- [#36] - Multiname with invalid index -- [#43] - Ternary operator and more -- [#46] - Ifs with empty branches got ignored -- [#3] - Ignoring unknown opcodes -- Logging exceptions during export - -## [1.4.1] - 2013-03-10 -### Added -- Exporting sounds -- Better AS1/2 deobfuscation (disassembly & decompilation) - -### Fixed -- Exporting only first 1000 frames of the movie -- Decompiled code was not refreshed on AS1/2 changes -- Application no longer creates empty directories on export - -## [1.4.0 update 1] - 2013-03-04 -### Fixed -- [#37] - AS3: Reversed loop conditions - -## [1.4.0] - 2013-03-03 -### Added -- AS3: ignoring return void at the end of methods -- New icons - Silk icons -- AS3: Traits list sort button -- Better Graph display -- Frames view -- Exporting of movies (No audio) -- Some AS3 related Tests -- Homepage & Donate link in the menu - -### Changed -- Tree view instead of tabs -- AS1/2 and AS3 now share same decompiling method. - -### Fixed -- [#34] - Reversed loop conditions -- [#35] - Fixed unicode strings (Japanese) - -## [1.3.1] - 2013-02-23 -### Changed -- Flash player no longer uses SWT library - -### Fixed -- [#32] - AS2: Action255 bug -- [#31] - Erorrneous tags are now ignored -- DefineBitsLossLess 1&2 on 8bit colormapped images - -## [1.3.0] - 2013-02-17 -### Added -- Decompilation is more resistant to obfuscation -- Shapes SVG export -- AS2: Decompiling classes & interfaces -- Click&go feature - clicking actionscript source displays appropriate P-code instruction and vice-versa (both AS1/2 and AS3) -- AS3: Deobfuscation menu -- Graph button for displaying code flow Graph - -### Changed -- Complete new decompiling method in both AS1/2 and AS3 -- Application renamed from "JP ActionScript Decompiler" to "JPEXS Free Flash Decompiler". -- To edit source, Edit button must be pressed first (Due to click&go feature) - -### Fixed -- AS3: Method info editor fixed -- Edittext & Button displaying - -## [1.2.0 update 1] - 2013-01-19 -## [1.2.0] - 2013-01-19 -### Added -- Displaying various SWF objects (shapes, sprites,...) with flash player library (Windows only, sorry). -- Images display and export -- AS2: Exporting selection -- Progressbar during loading - -### Changed -- One merged window for AS1/2 and 3. -- Updated icons - -### Fixed -- AS3: xml attrib, switch in anonymous function (in AS2 too) - -## [1.1.0] - 2013-01-02 -### Added -- Checking for updates -- AS2: Exporting -- AS3: Decompiling whole scripts instead of just classes -- AS3: Exporting selected scripts -- AS3: Script search bar -- AS3: List of DoABCTags now has default "- all -" item -- AS3: Better imports, use namespaces -- AS3: XML related instructions -- AS3: Anonymous functions with names -- AS3: Better initialization of const values -- Logging exceptions to log.txt file - -### Fixed -- AS3: set_local..get_local, dup, chained assignments, highlighting, callsupervoid, typenames, with statement, loops - -## [1.0.1] - 2012-12-26 -### Added -- AS3: Runtime namespace resolving -- AS3: Arguments variable -- AS3: Better recognizing Pre/Post Increments/Decrements -- AS3: Better declarations - -### Fixed -- AS3: Fixed static variables - -## [1.0.0] - 2012-12-24 -### Added -- Support for LZMA compressed files -- AS3: Detecting local register types for declaration. -- AS3: Displaying inline functions -- AS3: Last save/open dir is remembered -- AS3: Better usage detection for multinames -- AS3: Commandline arguments for exporting -- AS3: Better chained assignments -- AS2: FSCommand2 instruction support -- Proxy: Mimetype application/octet-stream added -- Added executable for Windows users. - -### Changed -- AS3: GUI - Constants tab moved to the top -- AS3: Deobfuscation is now optional, can be accessed via menu - -### Fixed -- AS3: rest parameter, for..in, fail on large classes (due to sub limiter) -- Other minor fixes - -## [beta 1] - 2011-07-30 -### Added -- AS3: Automatic computing method body parameters (EXPERIMENTAL) -- AS3: Editing return type of methods -- AS3: Editing type and default value for variables/constants (Slot/Const traits) -- AS1/2: Few enhancements -- About dialog - -### Changed -- Gui: Updated Icons - -### Fixed -- AS 1/2: Fixed large bug causing Ifs to not decompile properly -- Proxy: Some minor fixes - -## [alpha 10] - 2011-07-13 -### Added -- AS3:Highlighting actual line -- AS3:Completing instruction names via Ctrl+Space -- AS3:Editing method parameters, method body parameters via tab panel -- AS3:ByteCode minor_version 17 supported - decimal datatypes -- AS3:Local variables and method parameters take name from debug information if present -- AS3:Automatic renaming of classes/methods when obfuscated names -- AS3:Better error messages (When cannot decompile obfuscated code) - -### Fixed -- AS3:Fixed Vector datatypes (TypeName multiname, applytype instruction) -- AS3:Hilighting fixes -- AS3:Fixed decrement/increment statements decompilation -- AS3:Decompiler now adds variable declarations on the beginning of decompiled method -- AS3:Try/catch statements fixed when debug information present -- AS3:Fixed for each statements -- AS3:Other minor fixes - -## [alpha 9] - 2011-07-02 -### Added -- AS3: Added disassembling of some new types of instructions -- AS3: Exporting source as PCode - -### Fixed -- AS3: Many other bugfixes... - -## [alpha 8] - 2010-09-19 -### Added -- AS3: Editing exceptions -- AS3: Finding usage of multinames from constant table - -### Changed -- AS1/2: Better GUI -- AS1/2: Better decompiling of Ifs, For..in - -## [alpha 7] - 2010-09-04 -### Added -- Initial public release - -[Unreleased]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version9.0.0...dev -[9.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version8.0.1...version9.0.0 -[8.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version8.0.0...version8.0.1 -[8.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.1.2...version8.0.0 -[7.1.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.1.1...version7.1.2 -[7.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.1.0...version7.1.1 -[7.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.0.1...version7.1.0 -[7.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.0.0...version7.0.1 -[7.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.1.1...version7.0.0 -[6.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.1.0...version6.1.1 -[6.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.0.2...version6.1.0 -[6.0.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.0.1...version6.0.2 -[6.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.0.0...version6.0.1 -[6.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.3.0...version6.0.0 -[5.3.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.2.0...version5.3.0 -[5.2.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.1.0...version5.2.0 -[5.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.0.2...version5.1.0 -[5.0.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.0.1...version5.0.2 -[5.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.0.0...version5.0.1 -[5.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.1.1...version5.0.0 -[4.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.1.0...version4.1.1 -[4.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.5...version4.1.0 -[4.0.5]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.4...version4.0.5 -[4.0.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.3...version4.0.4 -[4.0.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.2...version4.0.3 -[4.0.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.1...version4.0.2 -[4.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.0...version4.0.1 -[4.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version3.0.0...version4.0.0 -[3.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.4...version3.0.0 -[2.1.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.3...version2.1.4 -[2.1.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.2...version2.1.3 -[2.1.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.1...version2.1.2 -[2.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.0u2...version2.1.1 -[2.1.0 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.0u1...version2.1.0u2 -[2.1.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.0...version2.1.0u1 -[2.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.1u2...version2.1.0 -[2.0.1 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.1u1...version2.0.1u2 -[2.0.1 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.1...version2.0.1u1 -[2.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.0...version2.0.1 -[2.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.1u1...version2.0.0 -[1.8.1 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.1...version1.8.1u1 -[1.8.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.0u1...version1.8.1 -[1.8.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.0...version1.8.0u1 -[1.8.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.4u1...version1.8.0 -[1.7.4 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.4...version1.7.4u1 -[1.7.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.3u2...version1.7.4 -[1.7.3 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.3u1...version1.7.3u2 -[1.7.3 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.3...version1.7.3u1 -[1.7.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.2u2...version1.7.3 -[1.7.2 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.2u1...version1.7.2u2 -[1.7.2 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.2...version1.7.2u1 -[1.7.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.1...version1.7.2 -[1.7.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.0u1...version1.7.1 -[1.7.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.0...version1.7.0u1 -[1.7.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.7...version1.7.0 -[1.6.7]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.6u2...version1.6.7 -[1.6.6 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.6u1...version1.6.6u2 -[1.6.6 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.6...version1.6.6u1 -[1.6.6]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.5u1...version1.6.6 -[1.6.5 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.5...version1.6.5u1 -[1.6.5]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.4u1...version1.6.5 -[1.6.4 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.4...version1.6.4u1 -[1.6.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.3u2...version1.6.4 -[1.6.3 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.3u1...version1.6.3u2 -[1.6.3 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.3...version1.6.3u1 -[1.6.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.2...version1.6.3 -[1.6.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.1...version1.6.2 -[1.6.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.0u1...version1.6.1 -[1.6.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.0...version1.6.0u1 -[1.6.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.2...version1.6.0 -[1.5.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.1u1...version1.5.2 -[1.5.1 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.1...version1.5.1u1 -[1.5.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.0u1...version1.5.1 -[1.5.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.0...version1.5.0u1 -[1.5.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.3u2...version1.5.0 -[1.4.3 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.3u1...version1.4.3u2 -[1.4.3 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.3...version1.4.3u1 -[1.4.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.2u1...version1.4.3 -[1.4.2 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.2...version1.4.2u1 -[1.4.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.1...version1.4.2 -[1.4.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.0u1...version1.4.1 -[1.4.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.0...version1.4.0u1 -[1.4.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.3.1...version1.4.0 -[1.3.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.3.0...version1.3.1 -[1.3.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.2.0u1...version1.3.0 -[1.2.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.2.0...version1.2.0u1 -[1.2.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.1.0...version1.2.0 -[1.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.0.1...version1.1.0 -[1.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.0.0...version1.0.1 -[1.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/beta1...version1.0.0 -[beta 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha10...beta1 -[alpha 10]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha9...alpha10 -[alpha 9]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha8...alpha9 -[alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 -[alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 -[#1162]: https://www.free-decompiler.com/flash/issues/1162 -[#1156]: https://www.free-decompiler.com/flash/issues/1156 -[#1171]: https://www.free-decompiler.com/flash/issues/1171 -[#1199]: https://www.free-decompiler.com/flash/issues/1199 -[#1170]: https://www.free-decompiler.com/flash/issues/1170 -[#1241]: https://www.free-decompiler.com/flash/issues/1241 -[#1151]: https://www.free-decompiler.com/flash/issues/1151 -[#1128]: https://www.free-decompiler.com/flash/issues/1128 -[#1163]: https://www.free-decompiler.com/flash/issues/1163 -[#1172]: https://www.free-decompiler.com/flash/issues/1172 -[#1174]: https://www.free-decompiler.com/flash/issues/1174 -[#1183]: https://www.free-decompiler.com/flash/issues/1183 -[#1193]: https://www.free-decompiler.com/flash/issues/1193 -[#1200]: https://www.free-decompiler.com/flash/issues/1200 -[#1198]: https://www.free-decompiler.com/flash/issues/1198 -[#1205]: https://www.free-decompiler.com/flash/issues/1205 -[#1194]: https://www.free-decompiler.com/flash/issues/1194 -[#1210]: https://www.free-decompiler.com/flash/issues/1210 -[#1217]: https://www.free-decompiler.com/flash/issues/1217 -[#1244]: https://www.free-decompiler.com/flash/issues/1244 -[#1247]: https://www.free-decompiler.com/flash/issues/1247 -[#1236]: https://www.free-decompiler.com/flash/issues/1236 -[#1251]: https://www.free-decompiler.com/flash/issues/1251 -[#1265]: https://www.free-decompiler.com/flash/issues/1265 -[#1268]: https://www.free-decompiler.com/flash/issues/1268 -[#1161]: https://www.free-decompiler.com/flash/issues/1161 -[#1145]: https://www.free-decompiler.com/flash/issues/1145 -[#1118]: https://www.free-decompiler.com/flash/issues/1118 -[#409]: https://www.free-decompiler.com/flash/issues/409 -[#1132]: https://www.free-decompiler.com/flash/issues/1132 -[#1134]: https://www.free-decompiler.com/flash/issues/1134 -[#1121]: https://www.free-decompiler.com/flash/issues/1121 -[#1052]: https://www.free-decompiler.com/flash/issues/1052 -[#758]: https://www.free-decompiler.com/flash/issues/758 -[#1096]: https://www.free-decompiler.com/flash/issues/1096 -[#1104]: https://www.free-decompiler.com/flash/issues/1104 -[#1107]: https://www.free-decompiler.com/flash/issues/1107 -[#1106]: https://www.free-decompiler.com/flash/issues/1106 -[#1113]: https://www.free-decompiler.com/flash/issues/1113 -[#1075]: https://www.free-decompiler.com/flash/issues/1075 -[#1127]: https://www.free-decompiler.com/flash/issues/1127 -[#1103]: https://www.free-decompiler.com/flash/issues/1103 -[#1133]: https://www.free-decompiler.com/flash/issues/1133 -[#1135]: https://www.free-decompiler.com/flash/issues/1135 -[#1138]: https://www.free-decompiler.com/flash/issues/1138 -[#1139]: https://www.free-decompiler.com/flash/issues/1139 -[#930]: https://www.free-decompiler.com/flash/issues/930 -[#1137]: https://www.free-decompiler.com/flash/issues/1137 -[#1144]: https://www.free-decompiler.com/flash/issues/1144 -[#1147]: https://www.free-decompiler.com/flash/issues/1147 -[#1148]: https://www.free-decompiler.com/flash/issues/1148 -[#1152]: https://www.free-decompiler.com/flash/issues/1152 -[#1154]: https://www.free-decompiler.com/flash/issues/1154 -[#116]: https://www.free-decompiler.com/flash/issues/116 -[#1070]: https://www.free-decompiler.com/flash/issues/1070 -[#1098]: https://www.free-decompiler.com/flash/issues/1098 -[#1033]: https://www.free-decompiler.com/flash/issues/1033 -[#1083]: https://www.free-decompiler.com/flash/issues/1083 -[#1091]: https://www.free-decompiler.com/flash/issues/1091 -[#1076]: https://www.free-decompiler.com/flash/issues/1076 -[#1068]: https://www.free-decompiler.com/flash/issues/1068 -[#1063]: https://www.free-decompiler.com/flash/issues/1063 -[#1019]: https://www.free-decompiler.com/flash/issues/1019 -[#1016]: https://www.free-decompiler.com/flash/issues/1016 -[#1010]: https://www.free-decompiler.com/flash/issues/1010 -[#1008]: https://www.free-decompiler.com/flash/issues/1008 -[#1004]: https://www.free-decompiler.com/flash/issues/1004 -[#933]: https://www.free-decompiler.com/flash/issues/933 -[#418]: https://www.free-decompiler.com/flash/issues/418 -[#1062]: https://www.free-decompiler.com/flash/issues/1062 -[#1047]: https://www.free-decompiler.com/flash/issues/1047 -[#812]: https://www.free-decompiler.com/flash/issues/812 -[#1056]: https://www.free-decompiler.com/flash/issues/1056 -[#1057]: https://www.free-decompiler.com/flash/issues/1057 -[#991]: https://www.free-decompiler.com/flash/issues/991 -[#689]: https://www.free-decompiler.com/flash/issues/689 -[#1060]: https://www.free-decompiler.com/flash/issues/1060 -[#1037]: https://www.free-decompiler.com/flash/issues/1037 -[#489]: https://www.free-decompiler.com/flash/issues/489 -[#1007]: https://www.free-decompiler.com/flash/issues/1007 -[#1044]: https://www.free-decompiler.com/flash/issues/1044 -[#947]: https://www.free-decompiler.com/flash/issues/947 -[#953]: https://www.free-decompiler.com/flash/issues/953 -[#954]: https://www.free-decompiler.com/flash/issues/954 -[#950]: https://www.free-decompiler.com/flash/issues/950 -[#945]: https://www.free-decompiler.com/flash/issues/945 -[#957]: https://www.free-decompiler.com/flash/issues/957 -[#956]: https://www.free-decompiler.com/flash/issues/956 -[#968]: https://www.free-decompiler.com/flash/issues/968 -[#978]: https://www.free-decompiler.com/flash/issues/978 -[#955]: https://www.free-decompiler.com/flash/issues/955 -[#966]: https://www.free-decompiler.com/flash/issues/966 -[#999]: https://www.free-decompiler.com/flash/issues/999 -[#1000]: https://www.free-decompiler.com/flash/issues/1000 -[#1017]: https://www.free-decompiler.com/flash/issues/1017 -[#1030]: https://www.free-decompiler.com/flash/issues/1030 -[#944]: https://www.free-decompiler.com/flash/issues/944 -[#939]: https://www.free-decompiler.com/flash/issues/939 -[#942]: https://www.free-decompiler.com/flash/issues/942 -[#949]: https://www.free-decompiler.com/flash/issues/949 -[#952]: https://www.free-decompiler.com/flash/issues/952 -[#858]: https://www.free-decompiler.com/flash/issues/858 -[#905]: https://www.free-decompiler.com/flash/issues/905 -[#920]: https://www.free-decompiler.com/flash/issues/920 -[#921]: https://www.free-decompiler.com/flash/issues/921 -[#924]: https://www.free-decompiler.com/flash/issues/924 -[#895]: https://www.free-decompiler.com/flash/issues/895 -[#884]: https://www.free-decompiler.com/flash/issues/884 -[#899]: https://www.free-decompiler.com/flash/issues/899 -[#903]: https://www.free-decompiler.com/flash/issues/903 -[#855]: https://www.free-decompiler.com/flash/issues/855 -[#850]: https://www.free-decompiler.com/flash/issues/850 -[#832]: https://www.free-decompiler.com/flash/issues/832 -[#904]: https://www.free-decompiler.com/flash/issues/904 -[#910]: https://www.free-decompiler.com/flash/issues/910 -[#922]: https://www.free-decompiler.com/flash/issues/922 -[#916]: https://www.free-decompiler.com/flash/issues/916 -[#938]: https://www.free-decompiler.com/flash/issues/938 -[#897]: https://www.free-decompiler.com/flash/issues/897 -[#470]: https://www.free-decompiler.com/flash/issues/470 -[#877]: https://www.free-decompiler.com/flash/issues/877 -[#878]: https://www.free-decompiler.com/flash/issues/878 -[#845]: https://www.free-decompiler.com/flash/issues/845 -[#883]: https://www.free-decompiler.com/flash/issues/883 -[#882]: https://www.free-decompiler.com/flash/issues/882 -[#760]: https://www.free-decompiler.com/flash/issues/760 -[#887]: https://www.free-decompiler.com/flash/issues/887 -[#842]: https://www.free-decompiler.com/flash/issues/842 -[#772]: https://www.free-decompiler.com/flash/issues/772 -[#762]: https://www.free-decompiler.com/flash/issues/762 -[#841]: https://www.free-decompiler.com/flash/issues/841 -[#862]: https://www.free-decompiler.com/flash/issues/862 -[#865]: https://www.free-decompiler.com/flash/issues/865 -[#613]: https://www.free-decompiler.com/flash/issues/613 -[#868]: https://www.free-decompiler.com/flash/issues/868 -[#713]: https://www.free-decompiler.com/flash/issues/713 -[#807]: https://www.free-decompiler.com/flash/issues/807 -[#728]: https://www.free-decompiler.com/flash/issues/728 -[#857]: https://www.free-decompiler.com/flash/issues/857 -[#860]: https://www.free-decompiler.com/flash/issues/860 -[#350]: https://www.free-decompiler.com/flash/issues/350 -[#824]: https://www.free-decompiler.com/flash/issues/824 -[#809]: https://www.free-decompiler.com/flash/issues/809 -[#805]: https://www.free-decompiler.com/flash/issues/805 -[#825]: https://www.free-decompiler.com/flash/issues/825 -[#737]: https://www.free-decompiler.com/flash/issues/737 -[#814]: https://www.free-decompiler.com/flash/issues/814 -[#816]: https://www.free-decompiler.com/flash/issues/816 -[#835]: https://www.free-decompiler.com/flash/issues/835 -[#836]: https://www.free-decompiler.com/flash/issues/836 -[#848]: https://www.free-decompiler.com/flash/issues/848 -[#817]: https://www.free-decompiler.com/flash/issues/817 -[#849]: https://www.free-decompiler.com/flash/issues/849 -[#852]: https://www.free-decompiler.com/flash/issues/852 -[#837]: https://www.free-decompiler.com/flash/issues/837 -[#811]: https://www.free-decompiler.com/flash/issues/811 -[#745]: https://www.free-decompiler.com/flash/issues/745 -[#803]: https://www.free-decompiler.com/flash/issues/803 -[#738]: https://www.free-decompiler.com/flash/issues/738 -[#742]: https://www.free-decompiler.com/flash/issues/742 -[#747]: https://www.free-decompiler.com/flash/issues/747 -[#749]: https://www.free-decompiler.com/flash/issues/749 -[#752]: https://www.free-decompiler.com/flash/issues/752 -[#753]: https://www.free-decompiler.com/flash/issues/753 -[#759]: https://www.free-decompiler.com/flash/issues/759 -[#766]: https://www.free-decompiler.com/flash/issues/766 -[#768]: https://www.free-decompiler.com/flash/issues/768 -[#773]: https://www.free-decompiler.com/flash/issues/773 -[#776]: https://www.free-decompiler.com/flash/issues/776 -[#783]: https://www.free-decompiler.com/flash/issues/783 -[#785]: https://www.free-decompiler.com/flash/issues/785 -[#787]: https://www.free-decompiler.com/flash/issues/787 -[#788]: https://www.free-decompiler.com/flash/issues/788 -[#790]: https://www.free-decompiler.com/flash/issues/790 -[#794]: https://www.free-decompiler.com/flash/issues/794 -[#798]: https://www.free-decompiler.com/flash/issues/798 -[#800]: https://www.free-decompiler.com/flash/issues/800 -[#676]: https://www.free-decompiler.com/flash/issues/676 -[#734]: https://www.free-decompiler.com/flash/issues/734 -[#687]: https://www.free-decompiler.com/flash/issues/687 -[#709]: https://www.free-decompiler.com/flash/issues/709 -[#732]: https://www.free-decompiler.com/flash/issues/732 -[#730]: https://www.free-decompiler.com/flash/issues/730 -[#735]: https://www.free-decompiler.com/flash/issues/735 -[#722]: https://www.free-decompiler.com/flash/issues/722 -[#725]: https://www.free-decompiler.com/flash/issues/725 -[#715]: https://www.free-decompiler.com/flash/issues/715 -[#635]: https://www.free-decompiler.com/flash/issues/635 -[#726]: https://www.free-decompiler.com/flash/issues/726 -[#720]: https://www.free-decompiler.com/flash/issues/720 -[#716]: https://www.free-decompiler.com/flash/issues/716 -[#717]: https://www.free-decompiler.com/flash/issues/717 -[#718]: https://www.free-decompiler.com/flash/issues/718 -[#719]: https://www.free-decompiler.com/flash/issues/719 -[#723]: https://www.free-decompiler.com/flash/issues/723 -[#288]: https://www.free-decompiler.com/flash/issues/288 -[#677]: https://www.free-decompiler.com/flash/issues/677 -[#389]: https://www.free-decompiler.com/flash/issues/389 -[#701]: https://www.free-decompiler.com/flash/issues/701 -[#707]: https://www.free-decompiler.com/flash/issues/707 -[#302]: https://www.free-decompiler.com/flash/issues/302 -[#685]: https://www.free-decompiler.com/flash/issues/685 -[#698]: https://www.free-decompiler.com/flash/issues/698 -[#710]: https://www.free-decompiler.com/flash/issues/710 -[#711]: https://www.free-decompiler.com/flash/issues/711 -[#681]: https://www.free-decompiler.com/flash/issues/681 -[#688]: https://www.free-decompiler.com/flash/issues/688 -[#691]: https://www.free-decompiler.com/flash/issues/691 -[#524]: https://www.free-decompiler.com/flash/issues/524 -[#663]: https://www.free-decompiler.com/flash/issues/663 -[#702]: https://www.free-decompiler.com/flash/issues/702 -[#539]: https://www.free-decompiler.com/flash/issues/539 -[#650]: https://www.free-decompiler.com/flash/issues/650 -[#680]: https://www.free-decompiler.com/flash/issues/680 -[#649]: https://www.free-decompiler.com/flash/issues/649 -[#656]: https://www.free-decompiler.com/flash/issues/656 -[#661]: https://www.free-decompiler.com/flash/issues/661 -[#664]: https://www.free-decompiler.com/flash/issues/664 -[#668]: https://www.free-decompiler.com/flash/issues/668 -[#674]: https://www.free-decompiler.com/flash/issues/674 -[#675]: https://www.free-decompiler.com/flash/issues/675 -[#632]: https://www.free-decompiler.com/flash/issues/632 -[#651]: https://www.free-decompiler.com/flash/issues/651 -[#678]: https://www.free-decompiler.com/flash/issues/678 -[#672]: https://www.free-decompiler.com/flash/issues/672 -[#684]: https://www.free-decompiler.com/flash/issues/684 -[#647]: https://www.free-decompiler.com/flash/issues/647 -[#648]: https://www.free-decompiler.com/flash/issues/648 -[#612]: https://www.free-decompiler.com/flash/issues/612 -[#623]: https://www.free-decompiler.com/flash/issues/623 -[#624]: https://www.free-decompiler.com/flash/issues/624 -[#627]: https://www.free-decompiler.com/flash/issues/627 -[#640]: https://www.free-decompiler.com/flash/issues/640 -[#595]: https://www.free-decompiler.com/flash/issues/595 -[#592]: https://www.free-decompiler.com/flash/issues/592 -[#585]: https://www.free-decompiler.com/flash/issues/585 -[#578]: https://www.free-decompiler.com/flash/issues/578 -[#501]: https://www.free-decompiler.com/flash/issues/501 -[#616]: https://www.free-decompiler.com/flash/issues/616 -[#559]: https://www.free-decompiler.com/flash/issues/559 -[#401]: https://www.free-decompiler.com/flash/issues/401 -[#593]: https://www.free-decompiler.com/flash/issues/593 -[#594]: https://www.free-decompiler.com/flash/issues/594 -[#579]: https://www.free-decompiler.com/flash/issues/579 -[#337]: https://www.free-decompiler.com/flash/issues/337 -[#584]: https://www.free-decompiler.com/flash/issues/584 -[#428]: https://www.free-decompiler.com/flash/issues/428 -[#576]: https://www.free-decompiler.com/flash/issues/576 -[#250]: https://www.free-decompiler.com/flash/issues/250 -[#580]: https://www.free-decompiler.com/flash/issues/580 -[#510]: https://www.free-decompiler.com/flash/issues/510 -[#583]: https://www.free-decompiler.com/flash/issues/583 -[#586]: https://www.free-decompiler.com/flash/issues/586 -[#574]: https://www.free-decompiler.com/flash/issues/574 -[#570]: https://www.free-decompiler.com/flash/issues/570 -[#563]: https://www.free-decompiler.com/flash/issues/563 -[#561]: https://www.free-decompiler.com/flash/issues/561 -[#509]: https://www.free-decompiler.com/flash/issues/509 -[#433]: https://www.free-decompiler.com/flash/issues/433 -[#557]: https://www.free-decompiler.com/flash/issues/557 -[#556]: https://www.free-decompiler.com/flash/issues/556 -[#504]: https://www.free-decompiler.com/flash/issues/504 -[#529]: https://www.free-decompiler.com/flash/issues/529 -[#538]: https://www.free-decompiler.com/flash/issues/538 -[#537]: https://www.free-decompiler.com/flash/issues/537 -[#540]: https://www.free-decompiler.com/flash/issues/540 -[#387]: https://www.free-decompiler.com/flash/issues/387 -[#552]: https://www.free-decompiler.com/flash/issues/552 -[#494]: https://www.free-decompiler.com/flash/issues/494 -[#513]: https://www.free-decompiler.com/flash/issues/513 -[#262]: https://www.free-decompiler.com/flash/issues/262 -[#499]: https://www.free-decompiler.com/flash/issues/499 -[#508]: https://www.free-decompiler.com/flash/issues/508 -[#305]: https://www.free-decompiler.com/flash/issues/305 -[#312]: https://www.free-decompiler.com/flash/issues/312 -[#503]: https://www.free-decompiler.com/flash/issues/503 -[#304]: https://www.free-decompiler.com/flash/issues/304 -[#306]: https://www.free-decompiler.com/flash/issues/306 -[#507]: https://www.free-decompiler.com/flash/issues/507 -[#424]: https://www.free-decompiler.com/flash/issues/424 -[#425]: https://www.free-decompiler.com/flash/issues/425 -[#478]: https://www.free-decompiler.com/flash/issues/478 -[#485]: https://www.free-decompiler.com/flash/issues/485 -[#517]: https://www.free-decompiler.com/flash/issues/517 -[#518]: https://www.free-decompiler.com/flash/issues/518 -[#361]: https://www.free-decompiler.com/flash/issues/361 -[#392]: https://www.free-decompiler.com/flash/issues/392 -[#516]: https://www.free-decompiler.com/flash/issues/516 -[#495]: https://www.free-decompiler.com/flash/issues/495 -[#496]: https://www.free-decompiler.com/flash/issues/496 -[#299]: https://www.free-decompiler.com/flash/issues/299 -[#303]: https://www.free-decompiler.com/flash/issues/303 -[#324]: https://www.free-decompiler.com/flash/issues/324 -[#346]: https://www.free-decompiler.com/flash/issues/346 -[#369]: https://www.free-decompiler.com/flash/issues/369 -[#371]: https://www.free-decompiler.com/flash/issues/371 -[#390]: https://www.free-decompiler.com/flash/issues/390 -[#426]: https://www.free-decompiler.com/flash/issues/426 -[#453]: https://www.free-decompiler.com/flash/issues/453 -[#457]: https://www.free-decompiler.com/flash/issues/457 -[#458]: https://www.free-decompiler.com/flash/issues/458 -[#459]: https://www.free-decompiler.com/flash/issues/459 -[#460]: https://www.free-decompiler.com/flash/issues/460 -[#461]: https://www.free-decompiler.com/flash/issues/461 -[#462]: https://www.free-decompiler.com/flash/issues/462 -[#463]: https://www.free-decompiler.com/flash/issues/463 -[#465]: https://www.free-decompiler.com/flash/issues/465 -[#466]: https://www.free-decompiler.com/flash/issues/466 -[#451]: https://www.free-decompiler.com/flash/issues/451 -[#454]: https://www.free-decompiler.com/flash/issues/454 -[#455]: https://www.free-decompiler.com/flash/issues/455 -[#474]: https://www.free-decompiler.com/flash/issues/474 -[#477]: https://www.free-decompiler.com/flash/issues/477 -[#481]: https://www.free-decompiler.com/flash/issues/481 -[#484]: https://www.free-decompiler.com/flash/issues/484 -[#493]: https://www.free-decompiler.com/flash/issues/493 -[#365]: https://www.free-decompiler.com/flash/issues/365 -[#366]: https://www.free-decompiler.com/flash/issues/366 -[#429]: https://www.free-decompiler.com/flash/issues/429 -[#447]: https://www.free-decompiler.com/flash/issues/447 -[#354]: https://www.free-decompiler.com/flash/issues/354 -[#438]: https://www.free-decompiler.com/flash/issues/438 -[#436]: https://www.free-decompiler.com/flash/issues/436 -[#446]: https://www.free-decompiler.com/flash/issues/446 -[#427]: https://www.free-decompiler.com/flash/issues/427 -[#405]: https://www.free-decompiler.com/flash/issues/405 -[#420]: https://www.free-decompiler.com/flash/issues/420 -[#421]: https://www.free-decompiler.com/flash/issues/421 -[#430]: https://www.free-decompiler.com/flash/issues/430 -[#397]: https://www.free-decompiler.com/flash/issues/397 -[#431]: https://www.free-decompiler.com/flash/issues/431 -[#169]: https://www.free-decompiler.com/flash/issues/169 -[#335]: https://www.free-decompiler.com/flash/issues/335 -[#404]: https://www.free-decompiler.com/flash/issues/404 -[#407]: https://www.free-decompiler.com/flash/issues/407 -[#399]: https://www.free-decompiler.com/flash/issues/399 -[#400]: https://www.free-decompiler.com/flash/issues/400 -[#398]: https://www.free-decompiler.com/flash/issues/398 -[#382]: https://www.free-decompiler.com/flash/issues/382 -[#396]: https://www.free-decompiler.com/flash/issues/396 -[#357]: https://www.free-decompiler.com/flash/issues/357 -[#391]: https://www.free-decompiler.com/flash/issues/391 -[#395]: https://www.free-decompiler.com/flash/issues/395 -[#301]: https://www.free-decompiler.com/flash/issues/301 -[#334]: https://www.free-decompiler.com/flash/issues/334 -[#383]: https://www.free-decompiler.com/flash/issues/383 -[#386]: https://www.free-decompiler.com/flash/issues/386 -[#367]: https://www.free-decompiler.com/flash/issues/367 -[#380]: https://www.free-decompiler.com/flash/issues/380 -[#292]: https://www.free-decompiler.com/flash/issues/292 -[#375]: https://www.free-decompiler.com/flash/issues/375 -[#378]: https://www.free-decompiler.com/flash/issues/378 -[#325]: https://www.free-decompiler.com/flash/issues/325 -[#210]: https://www.free-decompiler.com/flash/issues/210 -[#355]: https://www.free-decompiler.com/flash/issues/355 -[#313]: https://www.free-decompiler.com/flash/issues/313 -[#330]: https://www.free-decompiler.com/flash/issues/330 -[#332]: https://www.free-decompiler.com/flash/issues/332 -[#344]: https://www.free-decompiler.com/flash/issues/344 -[#295]: https://www.free-decompiler.com/flash/issues/295 -[#297]: https://www.free-decompiler.com/flash/issues/297 -[#307]: https://www.free-decompiler.com/flash/issues/307 -[#309]: https://www.free-decompiler.com/flash/issues/309 -[#310]: https://www.free-decompiler.com/flash/issues/310 -[#311]: https://www.free-decompiler.com/flash/issues/311 -[#327]: https://www.free-decompiler.com/flash/issues/327 -[#328]: https://www.free-decompiler.com/flash/issues/328 -[#333]: https://www.free-decompiler.com/flash/issues/333 -[#336]: https://www.free-decompiler.com/flash/issues/336 -[#338]: https://www.free-decompiler.com/flash/issues/338 -[#315]: https://www.free-decompiler.com/flash/issues/315 -[#123]: https://www.free-decompiler.com/flash/issues/123 -[#243]: https://www.free-decompiler.com/flash/issues/243 -[#326]: https://www.free-decompiler.com/flash/issues/326 -[#287]: https://www.free-decompiler.com/flash/issues/287 -[#290]: https://www.free-decompiler.com/flash/issues/290 -[#291]: https://www.free-decompiler.com/flash/issues/291 -[#294]: https://www.free-decompiler.com/flash/issues/294 -[#298]: https://www.free-decompiler.com/flash/issues/298 -[#296]: https://www.free-decompiler.com/flash/issues/296 -[#314]: https://www.free-decompiler.com/flash/issues/314 -[#316]: https://www.free-decompiler.com/flash/issues/316 -[#318]: https://www.free-decompiler.com/flash/issues/318 -[#319]: https://www.free-decompiler.com/flash/issues/319 -[#323]: https://www.free-decompiler.com/flash/issues/323 -[#223]: https://www.free-decompiler.com/flash/issues/223 -[#258]: https://www.free-decompiler.com/flash/issues/258 -[#261]: https://www.free-decompiler.com/flash/issues/261 -[#267]: https://www.free-decompiler.com/flash/issues/267 -[#269]: https://www.free-decompiler.com/flash/issues/269 -[#274]: https://www.free-decompiler.com/flash/issues/274 -[#275]: https://www.free-decompiler.com/flash/issues/275 -[#286]: https://www.free-decompiler.com/flash/issues/286 -[#233]: https://www.free-decompiler.com/flash/issues/233 -[#235]: https://www.free-decompiler.com/flash/issues/235 -[#263]: https://www.free-decompiler.com/flash/issues/263 -[#264]: https://www.free-decompiler.com/flash/issues/264 -[#265]: https://www.free-decompiler.com/flash/issues/265 -[#266]: https://www.free-decompiler.com/flash/issues/266 -[#281]: https://www.free-decompiler.com/flash/issues/281 -[#251]: https://www.free-decompiler.com/flash/issues/251 -[#257]: https://www.free-decompiler.com/flash/issues/257 -[#259]: https://www.free-decompiler.com/flash/issues/259 -[#260]: https://www.free-decompiler.com/flash/issues/260 -[#268]: https://www.free-decompiler.com/flash/issues/268 -[#272]: https://www.free-decompiler.com/flash/issues/272 -[#276]: https://www.free-decompiler.com/flash/issues/276 -[#220]: https://www.free-decompiler.com/flash/issues/220 -[#284]: https://www.free-decompiler.com/flash/issues/284 -[#232]: https://www.free-decompiler.com/flash/issues/232 -[#253]: https://www.free-decompiler.com/flash/issues/253 -[#137]: https://www.free-decompiler.com/flash/issues/137 -[#242]: https://www.free-decompiler.com/flash/issues/242 -[#244]: https://www.free-decompiler.com/flash/issues/244 -[#203]: https://www.free-decompiler.com/flash/issues/203 -[#225]: https://www.free-decompiler.com/flash/issues/225 -[#236]: https://www.free-decompiler.com/flash/issues/236 -[#245]: https://www.free-decompiler.com/flash/issues/245 -[#247]: https://www.free-decompiler.com/flash/issues/247 -[#248]: https://www.free-decompiler.com/flash/issues/248 -[#254]: https://www.free-decompiler.com/flash/issues/254 -[#255]: https://www.free-decompiler.com/flash/issues/255 -[#256]: https://www.free-decompiler.com/flash/issues/256 -[#241]: https://www.free-decompiler.com/flash/issues/241 -[#238]: https://www.free-decompiler.com/flash/issues/238 -[#239]: https://www.free-decompiler.com/flash/issues/239 -[#240]: https://www.free-decompiler.com/flash/issues/240 -[#237]: https://www.free-decompiler.com/flash/issues/237 -[#207]: https://www.free-decompiler.com/flash/issues/207 -[#217]: https://www.free-decompiler.com/flash/issues/217 -[#219]: https://www.free-decompiler.com/flash/issues/219 -[#224]: https://www.free-decompiler.com/flash/issues/224 -[#121]: https://www.free-decompiler.com/flash/issues/121 -[#151]: https://www.free-decompiler.com/flash/issues/151 -[#171]: https://www.free-decompiler.com/flash/issues/171 -[#206]: https://www.free-decompiler.com/flash/issues/206 -[#208]: https://www.free-decompiler.com/flash/issues/208 -[#209]: https://www.free-decompiler.com/flash/issues/209 -[#229]: https://www.free-decompiler.com/flash/issues/229 -[#213]: https://www.free-decompiler.com/flash/issues/213 -[#221]: https://www.free-decompiler.com/flash/issues/221 -[#226]: https://www.free-decompiler.com/flash/issues/226 -[#227]: https://www.free-decompiler.com/flash/issues/227 -[#230]: https://www.free-decompiler.com/flash/issues/230 -[#172]: https://www.free-decompiler.com/flash/issues/172 -[#174]: https://www.free-decompiler.com/flash/issues/174 -[#175]: https://www.free-decompiler.com/flash/issues/175 -[#212]: https://www.free-decompiler.com/flash/issues/212 -[#185]: https://www.free-decompiler.com/flash/issues/185 -[#186]: https://www.free-decompiler.com/flash/issues/186 -[#197]: https://www.free-decompiler.com/flash/issues/197 -[#216]: https://www.free-decompiler.com/flash/issues/216 -[#168]: https://www.free-decompiler.com/flash/issues/168 -[#173]: https://www.free-decompiler.com/flash/issues/173 -[#190]: https://www.free-decompiler.com/flash/issues/190 -[#129]: https://www.free-decompiler.com/flash/issues/129 -[#153]: https://www.free-decompiler.com/flash/issues/153 -[#176]: https://www.free-decompiler.com/flash/issues/176 -[#177]: https://www.free-decompiler.com/flash/issues/177 -[#180]: https://www.free-decompiler.com/flash/issues/180 -[#202]: https://www.free-decompiler.com/flash/issues/202 -[#136]: https://www.free-decompiler.com/flash/issues/136 -[#179]: https://www.free-decompiler.com/flash/issues/179 -[#144]: https://www.free-decompiler.com/flash/issues/144 -[#164]: https://www.free-decompiler.com/flash/issues/164 -[#167]: https://www.free-decompiler.com/flash/issues/167 -[#170]: https://www.free-decompiler.com/flash/issues/170 -[#178]: https://www.free-decompiler.com/flash/issues/178 -[#181]: https://www.free-decompiler.com/flash/issues/181 -[#182]: https://www.free-decompiler.com/flash/issues/182 -[#183]: https://www.free-decompiler.com/flash/issues/183 -[#184]: https://www.free-decompiler.com/flash/issues/184 -[#189]: https://www.free-decompiler.com/flash/issues/189 -[#191]: https://www.free-decompiler.com/flash/issues/191 -[#195]: https://www.free-decompiler.com/flash/issues/195 -[#196]: https://www.free-decompiler.com/flash/issues/196 -[#198]: https://www.free-decompiler.com/flash/issues/198 -[#200]: https://www.free-decompiler.com/flash/issues/200 -[#201]: https://www.free-decompiler.com/flash/issues/201 -[#166]: https://www.free-decompiler.com/flash/issues/166 -[#165]: https://www.free-decompiler.com/flash/issues/165 -[#63]: https://www.free-decompiler.com/flash/issues/63 -[#67]: https://www.free-decompiler.com/flash/issues/67 -[#117]: https://www.free-decompiler.com/flash/issues/117 -[#127]: https://www.free-decompiler.com/flash/issues/127 -[#134]: https://www.free-decompiler.com/flash/issues/134 -[#142]: https://www.free-decompiler.com/flash/issues/142 -[#146]: https://www.free-decompiler.com/flash/issues/146 -[#155]: https://www.free-decompiler.com/flash/issues/155 -[#130]: https://www.free-decompiler.com/flash/issues/130 -[#132]: https://www.free-decompiler.com/flash/issues/132 -[#145]: https://www.free-decompiler.com/flash/issues/145 -[#147]: https://www.free-decompiler.com/flash/issues/147 -[#148]: https://www.free-decompiler.com/flash/issues/148 -[#152]: https://www.free-decompiler.com/flash/issues/152 -[#156]: https://www.free-decompiler.com/flash/issues/156 -[#157]: https://www.free-decompiler.com/flash/issues/157 -[#158]: https://www.free-decompiler.com/flash/issues/158 -[#159]: https://www.free-decompiler.com/flash/issues/159 -[#160]: https://www.free-decompiler.com/flash/issues/160 -[#162]: https://www.free-decompiler.com/flash/issues/162 -[#163]: https://www.free-decompiler.com/flash/issues/163 -[#149]: https://www.free-decompiler.com/flash/issues/149 -[#150]: https://www.free-decompiler.com/flash/issues/150 -[#119]: https://www.free-decompiler.com/flash/issues/119 -[#101]: https://www.free-decompiler.com/flash/issues/101 -[#114]: https://www.free-decompiler.com/flash/issues/114 -[#135]: https://www.free-decompiler.com/flash/issues/135 -[#141]: https://www.free-decompiler.com/flash/issues/141 -[#102]: https://www.free-decompiler.com/flash/issues/102 -[#124]: https://www.free-decompiler.com/flash/issues/124 -[#128]: https://www.free-decompiler.com/flash/issues/128 -[#131]: https://www.free-decompiler.com/flash/issues/131 -[#104]: https://www.free-decompiler.com/flash/issues/104 -[#113]: https://www.free-decompiler.com/flash/issues/113 -[#133]: https://www.free-decompiler.com/flash/issues/133 -[#140]: https://www.free-decompiler.com/flash/issues/140 -[#108]: https://www.free-decompiler.com/flash/issues/108 -[#105]: https://www.free-decompiler.com/flash/issues/105 -[#109]: https://www.free-decompiler.com/flash/issues/109 -[#106]: https://www.free-decompiler.com/flash/issues/106 -[#107]: https://www.free-decompiler.com/flash/issues/107 -[#110]: https://www.free-decompiler.com/flash/issues/110 -[#103]: https://www.free-decompiler.com/flash/issues/103 -[#111]: https://www.free-decompiler.com/flash/issues/111 -[#96]: https://www.free-decompiler.com/flash/issues/96 -[#98]: https://www.free-decompiler.com/flash/issues/98 -[#99]: https://www.free-decompiler.com/flash/issues/99 -[#100]: https://www.free-decompiler.com/flash/issues/100 -[#85]: https://www.free-decompiler.com/flash/issues/85 -[#79]: https://www.free-decompiler.com/flash/issues/79 -[#92]: https://www.free-decompiler.com/flash/issues/92 -[#93]: https://www.free-decompiler.com/flash/issues/93 -[#94]: https://www.free-decompiler.com/flash/issues/94 -[#95]: https://www.free-decompiler.com/flash/issues/95 -[#86]: https://www.free-decompiler.com/flash/issues/86 -[#87]: https://www.free-decompiler.com/flash/issues/87 -[#88]: https://www.free-decompiler.com/flash/issues/88 -[#89]: https://www.free-decompiler.com/flash/issues/89 -[#82]: https://www.free-decompiler.com/flash/issues/82 -[#78]: https://www.free-decompiler.com/flash/issues/78 -[#81]: https://www.free-decompiler.com/flash/issues/81 -[#84]: https://www.free-decompiler.com/flash/issues/84 -[#83]: https://www.free-decompiler.com/flash/issues/83 -[#65]: https://www.free-decompiler.com/flash/issues/65 -[#48]: https://www.free-decompiler.com/flash/issues/48 -[#53]: https://www.free-decompiler.com/flash/issues/53 -[#66]: https://www.free-decompiler.com/flash/issues/66 -[#68]: https://www.free-decompiler.com/flash/issues/68 -[#69]: https://www.free-decompiler.com/flash/issues/69 -[#75]: https://www.free-decompiler.com/flash/issues/75 -[#73]: https://www.free-decompiler.com/flash/issues/73 -[#62]: https://www.free-decompiler.com/flash/issues/62 -[#72]: https://www.free-decompiler.com/flash/issues/72 -[#64]: https://www.free-decompiler.com/flash/issues/64 -[#38]: https://www.free-decompiler.com/flash/issues/38 -[#56]: https://www.free-decompiler.com/flash/issues/56 -[#57]: https://www.free-decompiler.com/flash/issues/57 -[#58]: https://www.free-decompiler.com/flash/issues/58 -[#45]: https://www.free-decompiler.com/flash/issues/45 -[#50]: https://www.free-decompiler.com/flash/issues/50 -[#51]: https://www.free-decompiler.com/flash/issues/51 -[#52]: https://www.free-decompiler.com/flash/issues/52 -[#47]: https://www.free-decompiler.com/flash/issues/47 -[#42]: https://www.free-decompiler.com/flash/issues/42 -[#39]: https://www.free-decompiler.com/flash/issues/39 -[#40]: https://www.free-decompiler.com/flash/issues/40 -[#44]: https://www.free-decompiler.com/flash/issues/44 -[#36]: https://www.free-decompiler.com/flash/issues/36 -[#43]: https://www.free-decompiler.com/flash/issues/43 -[#46]: https://www.free-decompiler.com/flash/issues/46 -[#3]: https://www.free-decompiler.com/flash/issues/3 -[#37]: https://www.free-decompiler.com/flash/issues/37 -[#34]: https://www.free-decompiler.com/flash/issues/34 -[#35]: https://www.free-decompiler.com/flash/issues/35 -[#32]: https://www.free-decompiler.com/flash/issues/32 +# Change Log +All notable changes to this project will be documented in this file. + +## [Unreleased] +### Added +- Iggy Files support - reading fonts only +- optional AS3 direct editation with Flex SDK +- AS3 p-code editing - metadata read/write support +- AS3 p-code editing - end of the block command like in RABCDasm +- AS3 p-code editing - popup docs for more than instructions +- Debugger - New columns for variable details - scope, flags, trait +- Debugger - Add watch feature +- AS3 decompilation - colliding trait/class names handling - show hash suffix with namespace index on such cases +- Deobfuscation Tool - Fix colliding trait/classes via toolbar command +- Auto rename identifiers option now fixes colliding trait/classes aswell +- #1254 FLA export - detecting scripts on AS3 timeline +- #907 FFDec Library JAR file has version inside it. +- Display warning when library version and GUI version mismatch +- Changelog file + +### Changed +- #1189 AS3 - sort imports to have same order always +- GUI: AS3 P-code header show actual trait type and method type +- GUI: Script editing buttons now named "Edit ActionScript" and "Edit P-code" +- Set advance values button has confirm dialog with information +- #1274 Linux package no longer requires Oracle Java only +- Library now packaged inside ZIP file + +### Fixed +- P-code docs formatting fix +- Export dialog - handling sprite and SWF frames correctly +- #1275 debugger - show local variables fixes +- AS3 p-code editing - popup docs correctly displayed when label on line start +- #1278 replacing DefineBits error +- #1281 DefineFont 2/3 getting character advance value when replacing fix +- Set advance values button - Do not set advance if the char cannot be displayed in source font +- AS3 Goto declaration for single character names +- Identifier renaming for top level classes +- AS3 direct editation not correctly saving local register names +- #1254 FLA export - placing AS3 classes to FLA directory instead of scripts dir +- Mac OS X installer fix (.pkg) +- #1289 AS1/2 direct editation - variables used in inner functions must not be stored in local registers +- #1283 AS3 Unbounded Vector - Vector<*> decompilation and direct editation fix +- #1294 Font editation (DefineFont2/3) - correct switching of wide character codes +- #1302 Callpropvoid instruction docblock not correct +- #1309 recent files not getting updates + +## [9.0.0] - 2016-08-12 +### Added +- Instance metadata (AMF3) editing in PlaceObject4 +- [#1156] Flash Viewer - DefineScalingGrid support (9-slice scaling) +- [#1171] Export stroke scale to FLA +- FLA export - check invalid unicode characters +- [#1170] Extract from memory in commandline +- Reload one vs Reload all buttons +- ABC: Float and Float4 support +- AS3 p-code instruction documentation in GUI +- [#1241] Settings to show original bytes in hex view +- Search in dump view +- Jump to resources view from hex view +- Show warning on 32bit JRE + +### Changed +- [#1162] improved opening loaded SWF files +- Flash Viewer - skip frames when not on time +- [#1199] Automatically import alpha channel to JPEG3/4 from PNG + +### Fixed +- [#1151] Filters on texts fixed +- [#1128] Adding characters to font fixed (FontAlignZones not removed) +- [#1163] Clicking open->file makes program buggy +- Refresh tree after raw edit +- [#1172] Text double escape fix +- [#1174] Change language fix +- some AS2 deobfuscation fixes +- [#1183] Index out of bounds fix +- Implicit coersion on binary/unary opfix +- debugger: corect display variable values through getters +- Multiple XLF export fix +- [#1193] FLA export - text tag advance fix, one layer shape fix +- [#1193] FLA export - smoothed image detection, export raw JPEG data +- [#1193] Export space character to TTF correctly +- [#1200] Previous search text selected when quick find +- Flash viewer: aspect ratio on startup fix +- [#1198] Saving trait slot const value +- Zoom parameter commandline fixed +- [#1205] clipping fixed +- [#1194] Wrong sound effects in FLA +- [#1210] Frame Export fix +- Improved/fixed go to declaration in AS3 +- [#1217] PCode window not in same position as AS +- Hide memor search on non windows platform +- [#1244] Incorrect showing of NOP instructions +- [#1244] Remove unknown actions when deobfuscation is enabled, compole unknown instructions back +- [#1241] File content is different from hex view +- [#1247] Incorrectly handling remainingbytes for DefineCompactedFont +- [#1236] won't open fixed +- [#1251] SWF not same after export XML and import back +- [#1265] Error during export +- [#1268] Font export - Using second glyph when two glyphs for one character found +- [#1268] GFX compacted font - fixed advance values on export + +## [8.0.1] - 2016-02-20 +### Changed +- FFDec debug tab in advanced settings moved to other tabs + +### Fixed +- [#1161] AS1/2 deobfuscation broken +- AS1/2 Simplify expressions fix + +## [8.0.0] - 2016-02-18 +### Added +- Debugger - AS1/2 Show registers +- Debugger - display variables in the tree structure +- Debugger - set value of variable +- Debugger - AS1/2 View constantpool +- Debugger - P-code level debugging for both AS1/2 and AS3 +- Basic SVG import for shapes +- Simplify expression setting +- [#1118] Loading characters through ImportAssets - show as readonly +- [#409], [#1132], [SkinPart] metadata support - decompilation and direct editation in AS3 +- [#1134] compiling §§ instructions back while direct editation (§§goto is still missing) +- [#1121] Ability to save binary data by its name +- [#1052] Add object to existing frame +- Allow adding tag to main timeline +- AS1/2: Ctrl+click to declaration of variables, registers +- Allow trait specification in pcode import +- Icons for tag types in Dump view +- Show error message when a text tag is invalid (glyph index problem) +- AS3 direct editation - store local register names in debug info = allow to rename them + +### Changed +- New application icon and splash screen +- [#1145] AS3 better declaration type detection, better convert_x instruction handling +- Binary export - use .swf extension for swf files +- Better tree labels in generic tag editor (Raw edit) +- [#758] Allow zooming more than preview area in internal viewer + +### Fixed +- [#1096] FLA export - pretty print +- [#1104] AS1/2/3 Script Importat not working +- [#1107] Text Offset Incorrect fixed +- [#1106] New Shapes replace function fix +- [#1113] It takes too long to switch between rendered sprite +- [#1075] Lenght of DefineText in some cases +- [#1127] autoRenameIdentifiers is not supported in CLI mode +- [#1128] Letterspacing bug (after font embed): ignore letterspacing when character changed +- [#1103] Foreach variables fixes +- AS3 Switch fixes +- Default clause position in switch +- [#1133] Incorrect frame order for nested sprite +- [#1135] Handle try "to" in p-code correctly +- Font wideOffsets,wideCodes fixed in DefineFont2/3 +- AS3: super method call +- [#1138] All exported videos are the same file which may be broken +- [#1139],[#930] Windows Installer: Correct ActiveX download link, Download latest java from webpages +- [#1137] running flashplayer(debugger) executable in Linux/MacOs +- [#1144] Command line argument renameInvalidIdentifiers +- [#1145] double not (!!) not removed +- [#1147] Sprite is exported incorrectly +- [#1148] handing end of stream exception in abc reader, loc exception +- [#1152] Font info tag modified tag was not set => saved swf was corrupt +- [#1154] Some 32bit JRE problems - program won't start +- [#1145] Correct precedence handling on binary operators +- [#116] not resolving unusual tags in DefineSprite + +## [7.1.2] - 2015-12-03 +### Fixed +- AS3 debugger start halt fix +- AS1/2 debugger fix on nondebug enabled SWFs +- AS1/2 debugger fix for functions +- Debug menu item enabled fix +- AS3 local reg index fix +- Advanced settings calendar +- AVM2 instructions in hex view +- [#1070] Incorrect switch decompilation +- [#1098] Import XML fix + +## [7.1.1] - 2015-11-23 +### Fixed +- Critical debugger fix - widelines + +## [7.1.0] - 2015-11-23 +### Added +- AS1/2 debugger +- Breakpoint/IP marker on line beginning + +### Changed +- Starting debugger on demand +- Installer message about playerglobal is only warning now + +### Fixed +- [#1033], [#1083] AS3 deobfuscation issues +- [#1091] AS 1/2 direct editation saving + +## [7.0.1] - 2015-11-18 +### Fixed +- Debugger: Adding breakpoint if script initializer not displayed + +## [7.0.0] - 2015-11-18 +### Added +- AS3 Debugger - breakpoints, stepping, show variables +- Faster AS3 direct editation + +### Changed +- Better Configuration of flashplayer paths + +### Removed +- Removed old "debugger" buttons +- Removed search from browsers cache - inactual code + +### Fixed +- Many AS3 direct editation related bugs +- [#1076] export fix + +## [6.1.1] - 2015-10-30 +### Fixed +- Deobfuscate AS3 metadata +- [#1068] MorphShape with focal gradient fix, FLA XML export formatting fix +- [#1063] AS3 direct edit - script initializer fix, generating method names +- XML export/import fixes +- [#1019] Namespace imports fix +- AVM2 code execution fix +- [#1016] AS3 direct editation fixes +- [#1010] AS2 direct editation - internal and override is not a reserved word +- [#1008] pushshort instruction diassembly +- [#1004] this/super can be AS1/2 variable +- [#933] AS3 allow numbers as object literal keys + +## [6.1.0] - 2015-10-26 +### Added +- Open other loaded SWFs during playback (useful for loaders) +- Export uncompressed data from dump tree +- Print performance statistics from commandline +- [#1062] Editing/displaying script initializers +- Enable debugging on SWF file (commandline) + +### Changed +- Faster syntax highlighting +- Better AS1/2 deobfuscator +- [#418] AS3 deobfuscator improved + +### Fixed +- AS call method fix (first parameter is "this") +- [#1047] open all scripts folders +- [#812] decompile fail +- [#1056] deltaY missing when adding a new StraightEdgeRecord +- [#1057] Editing as in editor results in package name moving +- [#991] GUI export +- [#689] Ignore Case not correctly toggled +- [#1060] reversed and/or detection in some cases +- [#1037] isXML call + +## [6.0.2] - 2015-09-12 +### Added +- AS3: Display and direct edit trait Metadata +- Allow to specify tag type on image or shape import +- Convert image tags from commandline +- [#489] Hex decode very large integers +- Add new tags without show empty folders +- Dependent characters in basic tag info +- [#1007] replace bytearray in raw editing +- Italian translation + +### Changed +- AS2 parser - add string to constant pool if there is not enough space +- [#1044] AS2 - order scripts by physical location, name by offset + +### Removed +- Deprecated commandline parameters removed + +### Fixed +- JNA problems on some JDKs +- [#947] Marklevels errors ignored 17a94b7 +- [#953] Mac application permission fix (maybe) +- [#954] IndexOutOfBounds fix +- [#950] AddTrait setting modified fix +- [#945] AS1/2 directeditation fix - member named as global function +- [#957] AS1/2 IndexOutOfBounds fix +- [#956] Invalid jump offsets warning +- [#968] Sprites export with wrong coloring +- [#978] case sensitivity of filenames +- [#955] AS2 decompilation problem +- Image alpha fixes +- [#966] Go to document class +- [#991] scripts exporting +- [#999] save as fla +- [#1000] image export for malformed JPEG3 tags +- [#1017] store alchemy opcodes with wrong order +- [#1030] stack overflow fix + +## [6.0.1] - 2015-07-06 +### Added +- Special §§ instructions marked as red +- [#949] Replace alpha channel from commandline +- AS3 deobfuscation from commandline +- Option to ignore FlashCC/Alchemy packages + +### Changed +- [#944],[#991],[#939],[#942] AS3 deobfuscation improvements + +### Fixed +- AS1/2 deobfuscation fixed +- [#952] Not loading SWF without extension + +## [6.0.0] - 2015-07-04 +### Added +- New AS3 deobfuscation method +- Internal "preprocessor" §§ actions introduced - §§pop,§§push,... +- Allow reload FFDec when no SWF is opened +- [#858] Allow to set compression type in header +- [#905] Show codec details for sound items +- Better alchemy/DomainMemory instruction handling +- Better obfuscated names handling +- [#920] Export instance name to SVG +- [#921] Export html DefineEditText to SVG +- Open multiple files with drag and drop +- Better "multi packs" handling (Alchemy) +- SWF version 29 to flash player 18 mapping +- ImportAssets2 sha1 field +- [#924] Sprites to image from commandline +- AS1/2 direct editation big numbers fix +- Allow to add FILTERs and SHAPERECORDs in generic tag editor +- Enable close all menu when no swf is selected +- Restore modified state even when something goes wrong +- Some old tags added + +### Changed +- AS decompilation highly improved +- Better &&, || handling +- DoABCDefine renamed to DoABC2 +- Separated Sprite export settings + +### Deprecated +- Old AS1/2/3 deobfuscation method marked as deprecated (can be enabled back deep in the configuration) + +### Fixed +- Many decompilation problems - EmptyStack exception, Maximum recursion level reached, etc. +- Few menu issues +- [#895] Correct handling CMYK JPEG +- [#884] AS direct edit assignment +- [#899] Show script after AS3 direct editation +- Some AS1/2 parser problems +- [#903] FLA export - fix for missing fontname, lastframe +- [#855] AS3 direct edit - for..in variable declaration fix +- [#850] Constant initialization for same multinames +- [#832] AS3 direct edit - other ABCs resolving fix +- [#904] Cannot export images +- [#910] Missing instructions +- Opening not existent files on restoring last session +- [#922] Edit text leading +- Put image before shape on shape replace +- [#916] Replacing Shape corrupts SWF +- JRE setup parameters fixed +- [#938] Parallel speedup limit fix + +## [5.3.0] - 2015-05-25 +### Added +- Generic tag editor: improved table editing (import/export assets tags, etc.) + +### Changed +- Classic (nonribbon) UI improved - has same items as Ribbon UI +- Icons improvements +- Disabling menu items when work in progress + +### Fixed +- [#897] Classic UI fix + +## [5.2.0] - 2015-05-22 +### Added +- UI8 editbox for swf version in header panel +- Basic tag information panel + +### Changed +- AS1/2: Shown only the constant pool(s) in pcode editor +- Do not allow to chage tag tree selection, when current tag is under editing +- Faster bitmap export +- Using less memory when playing sounds +- Error message changed when the opened file is not swf + +### Fixed +- [#470] panels size after resizing from/to full screen +- [#877] A small glitch after search in AS +- [#878] small glitch after saving P-code or swf file +- [#470] glitch +- [#845] If frame consist 2 DoAction then it imports only first one +- pdf export (when no frame exists) +- text rendering (alpha channel was ignored), +- bmp export (paddings when width%2==1) +- [#883] -dumpSWF option does not work anymore +- [#882] Canvas export border size +- [#760] Internal viewer line linear gradient fill is not working +- [#887] error on export a special swf's P-CODE +- Extensions of exported images fixed + +## [5.1.0] - 2015-05-04 +### Added +- Allow to copy/move multiple tags, and dependencies +- [#842] For reconstruction if debug line info present +- [#841] Loop control for sound preview +- [#845] Import exported AS1/2 (DefineButton2&DefineSprite) button +- Scrollbar added to fontpanel +- SWF header editor +- Configure what object types to export in exportdialog + +### Changed +- Better gif exporter +- [#772]: closing loading dialog now cancels the loading of the swf +- [#762] export pcode with different extension + +### Fixed +- CRITICAL: Update System Bug causing updates not working +- [#862] AS3 asm: do not read beyond return/throw instructions +- [#865], [#613]: ribbon prefered width fix +- [#868] export path fix, allow to export buttons +- [#865] TagTree font size problem on high resolution screens +- [#713],[#807] Installer for 4.0+ fails to access Adobe Website +- [#728] Large fonts, [#857] add scroll on DefineFont3 + +## [5.0.2] - 2015-04-18 +### Added +- Reopen last session + +### Fixed +- ffdec.sh file line endings fixed + +## [5.0.1] - 2015-04-18 +### Fixed +- [#860]: Opening bundle (zip, swc, any binary file) files fixed + +## [5.0.0] - 2015-04-18 +### Added +- Color skins +- [#824] Mac OS X package +- [#809] Move left,right buttons for DefineTexts using translatex parameter +- [#805] Editor mode for DefineTexts +- [#825] Hotkeys for next/previous DefineText +- Export/Import symbol classes/export asset tags +- Frame export progress +- [#737] Single file script export +- Displaying changed AS3 scripts in GUI as bold +- Additional character info tags placed under character node +- New icons for other tags (metadata,fileattributes,setbackground,place/remove) +- Metadata tag editor + +### Changed +- Default color skin altered +- [#350] Allow only one running instance (Windows only, can be turned off) +- SWFs in zip based bundles (SWC for example) can be modified & saved +- Performace improvements +- More compact SWF-XML format +- Marking changed parentnodes as bold too + +### Fixed +- [#814] Exporting with scale problem +- [#816] P-code not shown after class initializer trait selection +- [#835] Static initializer improvements +- AS3 direct editing - local register decrement fix +- AS3 direct editing - maintain register order/names +- [#836] AS1/2/3 Correct expression precedence handling +- AS3 preincrement +- [#848] Correct toggling text switches +- [#817] AS1/2 for..in variable declaration +- [#849] Attribute member +- [#852] Ignore case for russian characters +- [#837] AS3 try..finally without catch + +## [4.1.1] - 2015-02-21 +### Added +- Export/Import XML added to ribbon menu +- Few GUI enhancements +- Undo tag changes context menu + +### Changed +- Java 8 now required + +### Removed +- Removed support for Java below 8 + +### Fixed +- [#811] export ActionScript + +## [4.1.0] - 2015-02-18 +### Added +- XML export/import +- confirmation dialogs added +- Add support for non-standard ABC-compressed SWF file +- [#745] Copy tag to another SWF +- [#803] Align text in DefineText + +### Changed +- performance improvements +- [#758] Zoom to fit is dynamic + +### Fixed +- [#738] Frame export +- [#742] Can't edit frames +- [#747] Move tag to adds extra frame +- [#749] Internal viewer Sprite fill color +- [#752] Sound is not stopped +- [#753] Reload swf +- [#759] Decompilation § symbol +- [#766] Can't extract all resources +- [#768] Super calls not being correctly recognized +- [#773] Scripts associated with ClipActions are not shown +- [#776] Stop working after setting "number of threads" to 0 +- [#783] No OK box when edited script or text was saved +- [#785] Text search. Remember last choise, Unicode case insensitive search +- [#787] Search in AS bug (when navigating to searched results) +- [#788] Add DefineCompactedFont Tag to gfx file +- [#790] Impossible to change letters under a font +- [#794] Font extraction fails sometimes +- [#798] Close file streams after export, exporting progress +- [#800] Unexpected deleted carrier return in DefineEditText +- Build fix on Linux +- Fis Startup Script for OpenJDK +- Other minor fixes + +## [4.0.5] - 2014-12-01 +### Added +- Escape control characters in strings, identifier names +- [#676] import text error messages / logging enhancement +- [#734] \xAB escapes, \uABCD escapes +- [#687] AS3 - allowing p-code comments on separate line +- [#709] Text Export to Single File with custom filename + +### Fixed +- [#732] Random freezing - JavactiveX library updated. +- [#730] Not working without ActiveX fix on Windows +- [#735] Automatic deobfuscation not correctly switched (required restart) + +## [4.0.4] - 2014-11-23 +### Changed +- better file cache, removing unneccessary temp files + +### Fixed +- obfuscated identifiers + +## [4.0.3] - 2014-11-23 +### Added +- [#722] Go to next/previous frame +- BMP file format export (images,frames,shapes) and import(images) + +### Fixed +- [#725] various AS direct editation bug fixes - namespace compilation, AS 1/2 strict equals, submethod scope, unbounded type +- [#715] namespace resolving fix +- [#635],[#726] placing cursor inside Unicode characters + +## [4.0.2] - 2014-11-22 +### Added +- show frame number during play +- flashplayer - show controls for DefineSprite +- goto frame +- [#716],[#717],[#718] Proxy - save SWF, replace, copy URL, filesizes, table design + +### Changed +- [#720]: edited shape tag is not marked as modified after replacing +- reorganized about dialog + +### Fixed +- [#719] null swf name in Proxy after cancelling rename dialog +- flashplayer - font display +- [#723]: saving swf with invalid referenced characters +- DefineCompactedFont paging +- [#288] Less memory usage during FLA export +- Corrected syntax hilighting for AS3 P-code + +## [4.0.1] - 2014-11-12 +### Fixed +- [#713] Installer can continue when no file can be downloaded +- Fixed shapes +- Checking for updates moved to separate thread + +## [4.0.0] - 2014-11-11 +### Added +- [#677] Zoom level in export settings +- internal viewer: linear/srgb gradients +- zooming buttons for flashplayer/internal viewer +- stroke scaling modes for canvas export +- create snapshot button +- [#389] Selecting font face on import +- [#701] Importing font from TTF file +- Reorganized font panel +- [#707] Debugger for logging messages +- [#302] AS3: Better Ctrl+Click handling with underline, more declaration targets +- [#685] Getting local register names from debug info can be disabled +- Adding new tags +- [#698] Allowing unicode letters in identifiers +- [#710] Information about deobfuscation in error comments +- One EXE for 32/64 bit, uses percentage memory. +- EXE SplashScreen +- New Improved Windows Installer (NSIS) - can install Java and FlashPlayer, download playerglobal.swc +- Config setting to load inner SWFs automatically +- Replace shape with image + +### Changed +- better FlashPlayer integration using JavactiveX library +- Faster building tag tree +- Faster timeline construction +- [#711] Improved folder view - faster and with correct context menu + +### Fixed +- AS2 deobfuscation fixes +- AS2 loops fix +- [#681] Linux script fixes +- AS2 constructor name fix +- [#688] AS3 direct edit fixes +- [#691] AS3 p-code reading/saving fix +- AS3 direct edit -submethod name resolve fix +- frames to html canvas fix +- [#524] Mask layer not applied when nonempty script layer +- [#663] AS3 imports fixes +- Font export of dot character +- Font panel Yes button fixed +- [#702] GFX font reading fix +- Better obfuscated names handling +- [#539] for(each) in declaration fixes + +## [3.0.0] - 2014-09-20 +### Added +- Separated GUI (GPL) and library (now LGPL) +- Editing obfuscated identifiers via new paragraph(§) syntax +- Timeline View with preview and object hilighting +- Show GFX data in dump view +- [#650] New parameter to replace binarydata, images, sounds, scripts from commandline +- Dump view - selecting node +- [#680] Loading subSWFs from binaryTags now optional (button/context menu) to avoid unnecessary memory consumption + +### Removed +- Removed deprecated commandline export formats (see --help) + +### Fixed +- FileAttributes tag reading fix +- [#649] GFX reading fixed +- [#656] Search in memory - 64 bit processes fix +- [#661] scripts not showing +- [#664] expanding fillStyles in raw edit +- [#668] add missing character fix, text tags fix +- [#674] texts hilighting initialization fix +- [#675] AS1/2 and/or operator compilation +- [#632] Locking file after opening (cannot save, etc.) +- [#651] Unnecessary removing expression killed in unreachable part +- [#678] Windows batch file paths fixed +- [#672] Disabling transparency slider on RGB only selection +- [#684] Sound streams inside DefineSprites, soundstream handling + +## [2.1.4] - 2014-08-23 +### Added +- AS1/2: New method for deobfuscation (can be switched off in settings) +- AS1/2: Using eval/set on invalid identifiers, quotes in function names/parameters + +### Fixed +- [#647] Skipping FileAttributesTag with Parallel speedup on +- [#648] Export from embedded SWF + +## [2.1.3] - 2014-08-18 +### Added +- Show "save" and "saveas" in application menu +- Saving data range in dump view +- Show actions, abcdata in dumpview (context menu on the tree node) +- [#612] show color in hex format + +### Changed +- Faster dump info collecting (less memory) +- Allow selecting multiple files in open file dialog + +### Fixed +- [#623] ffdec.sh UNIX file endings, executable +- [#624] search in embedded swf files +- [#632] AS1/2 Unnecessary GetVariable before NewObject +- [#627] filter swf not working +- LZMA saving +- Export pcode&hex from commandline +- [#640] text import fixed, ignore BOM + +## [2.1.2] - 2014-07-20 +### Added +- Dump view +- Context menu: Jump to character, raw edit all tags +- Catalan translation +- SWF header display + +### Fixed +- [#595] AS3 direct edit - Getter/Setter generation - caused FlashPlayer crashes +- [#592] AS3 Multiname resolving in P-code causing different bytecode +- [#585] AS3 moving popped values to output +- [#578] Always on top fixed on search results +- [#501] GotoFrame2 fix +- [#616] Frames to PNG export +- Export context menu +- [#559] Bitmap export opacity +- [#401] Placeobject 3/4 fix +- [#593] Return object newline +- [#594] Setting for curly brace + +## [2.1.1] - 2014-06-05 +### Added +- [#302] Find declaration (Ctrl+click, Ctrl+B), Find usages (Ctrl+U) - Works only for exactly same multinames, not local registers +- AS1/2 direct edit - global functions improvements +- AS1/2 negate operator, unary minus operator +- Opening SWFs in BinaryData tags +- AS1: Old string operators support, and/or, <> operator (editation) +- Statusbar loading animation improved +- [#579] AS3 direct editation - removing old class/methods from ABC +- remove character without the dependencies (remove only the place/remove tags) +- Running on system with no home directory +- [#428] PDF export (as images only) +- Commandline FlashPaper to PDF export +- Select frames / Characters commandline options + +### Changed +- [#337] quickfind visibility improved +- [#584] commandline script export - select whole packages (use .+ at the end of -selectas3class) + +### Fixed +- [#576] AS1/2 direct editation: DefineFunction2 fix +- AS1/2 property fix +- AS1/2 typeof operator fix +- [#250] line spacing fix +- PlaceObject 3-4 className +- [#579] AS3 direct editation bugfixes - property resolving, integer values +- Morphshape canvas export fix +- Canvas export fix - closing path +- [#580] Rename invalid identifiers commandline fix +- [#510] JSyntaxPane find and replace dialog wrap around fix +- No more frame caching during export => memory saving (like [#583]) +- [#586] DropShadow filter fix +- Canvas export colortransform fix + +## [2.1.0 update 2] - 2014-05-08 +### Added +- AS3 decompilation/editation: Vector initializers +- AS3 direct editation: more classes in one file + +### Fixed +- [#574] DefineSprite editing fix +- Various AS3 direct editation fixes + +## [2.1.0 update 1] - 2014-05-05 +### Added +- Portugese-brasilian translation + +### Changed +- HTML Canvas export improvements + +### Fixed +- Various AS3 direct editation bugs, like [#570] + +## [2.1.0] - 2014-05-01 +### Added +- AS3 direct editation (Experimental!) +- Frames SVG Export +- Shape/MorphShape/Frames HTML 5 Canvas Export +- [#559] morphshapes as animated SVG +- [#563] Single file text export/import +- Font WOFF export +- Advanced settings dialog with tabs, config names, descriptions + +### Fixed +- [#561], [#509], [#433]: AS3 EmptyStackException fix - correct hasnext2 arguments +- Internal viewer: Filters fix + +## [2.0.1 update 2] - 2014-04-05 +### Fixed +- [#557] AS3 null namespace fix - p-code not working + +## [2.0.1 update 1] - 2014-04-04 +### Fixed +- [#556] Goto main class on startup fix +- [#557] Nullpointer fix (private namespaces) + +## [2.0.1] - 2014-04-03 +### Added +- Thumbnail view +- Font TTF export +- Exporting frames: PNGs, AVI, GIF (via Internal flash viewer) +- Expand all context menu +- Internal viewer: Button mouse move and click handling +- Playing sounds without flash player +- Internal viewer: Sounds on stage +- All sounds to WAV export +- Internal viewer: Showing texts, dynamic text border/fill +- [#504]: Unicode characters in JSyntaxPane +- Internal viewer: showing object under cursor +- Folder icons +- Sound/Image format on command line. +- Removing placeobject tags +- Removing frames +- AS: "elseif" statements +- Code formatting: space before parenthesis + +### Changed +- Single frames animated. + +### Fixed +- [#529] limit the number of displayed binaryData bytes +- [#538] Interface are sometimes dynamic +- [#537] super is sometime preceded by a dot +- [#540] Saving SWF changes very large static uint values +- [#387] Frames preview bugged +- AS:loop mismatch fix on parallel speedup +- [#552] Some timeout exceptions +- [#494] Fixed nightly builds updates + +## [2.0.0] - 2014-03-02 +### Added +- Generic tag tree editor +- Timeline view (stub only) +- FLA export to CS5, CS5.5, CC format (previously only CS6 was supported) +- [#513]: command line option to extract swf from binary file +- Configurable code formatting (Indentation + brace position) +- [#262] Export FLA: Font character ranges export +- Configurable checking for updates + +### Changed +- Improved Internal Flash viewer - better shapes, morphshapes, DefineEditText tag, clipping, blend modes +- Improved commandline usage +- Automatic deobfuscation default value set to False (See News on webpages) +- Check for updates can be configured to inform about Nightly builds aswell + +### Deprecated +- Some commandline options are now deprecated, see --help + +### Fixed +- [#499] Cannot save via Proxy fixed +- [#504] font name reading fixed +- [#508] Support for OS without GUI +- [#305] Export FLA: empty sound layers +- [#312] Export FLA: Improved Shape/MorphShape fix +- [#503] Export FLA: Smoothing invalid shapes +- [#401] Invalid GFX tags in non GFX files +- [#304],[#306],[#507],[#424],[#425],[#478],[#485],[#517],[#518] Many direct AS1/2 editing issues +- [#361] FFDec icon is not visible on application start +- [#392] Video stream data fix +- [#516] AS3 P-code editor - Null name namespace handling + +## [1.8.1 update 1] - 2014-02-02 +### Fixed +- [#495] font embedding fix +- [#496] date format in new version dialog +- cosmetic changes + +## [1.8.1] - 2014-01-30 +### Added +- [#299] replace DefineBits images +- [#303] open folder with exported FLA +- [#324],[#346] SWC/zip/other binary file support +- [#371] detailed logging +- [#426] command line switch to rename identifiers +- [#457] clear recent opened files list +- [#458] save selected system font for swf fonts +- [#460] text editor: do not scroll to the end automatically +- [#462] font embedding dialog: show more sample characters +- [#463] global search in texts +- [#465] make font properties editable +- [#466] font preview + +### Changed +- [#369] new SVG and preview image rendering +- [#390] refresh font list without reloading the application +- [#453] update texts aftert adding new character to a font tag +- [#459] remember text panel splitter position +- [#461] font panel gui redesigned + +### Fixed +- [#451] dialog windows are not on the center of the screen +- [#454] Text syntax highlighting +- [#455],[#465] classic interface issues +- [#474] changeing language only available one time +- [#477] log window localization +- [#481] SVG export fix +- [#484] Oversized advance value after editing DefineText with DefineFont2 font +- [#493] missing search results + +## [1.8.0 update 1] - 2013-12-27 +### Added +- [#453] refresh (edit+save action) all texts button + +### Fixed +- Flash panel and font panel fixed + +## [1.8.0] - 2013-12-27 +### Added +- [#350] Allow to open multiple SWFs +- [#365] Filter fake SWFs during memory search +- [#366] Allow to sort the result list in memory search window +- [#429] Auto rename invalid identifiers setting +- [#447] Non-ribbon interface + +### Fixed +- [#354] Infinite decompilation fixed +- [#438] Case sensitive Command line arguments fixed +- [#436] Saving actionscript fixed +- [#446] Precedence issue fixed +- [#451] Dialogue window positions on a multi-monitor configuration fixed + +## [1.7.4 update 1] - 2013-12-05 +### Added +- [#426] Command line parameter for renaming invalid identifiers + +### Fixed +- [#427] Exception on linux fixed +- [#405], [#420], [#421] Some decompilation issues fixed +- [#430] Configuration default value problem fixed +- [#397], [#431] Deobfuscation stucked sometimes problem fixed + +## [1.7.4] - 2013-11-10 +### Added +- [#169] hexedit method body bytes +- [#335] last opened files +- [#404] Exporting P-code and Hex + console parameters +- [#407] register name is configurable +- Advanced settings +- Cancellable decompiling, exporting and searching + +### Fixed +- [#399], [#400] performance optimizations + +## [1.7.3 update 2] - 2013-09-29 +### Fixed +- [#398] AS3 p-code values with index 0 (null) + +## [1.7.3 update 1] - 2013-09-28 +### Added +- [#382] AS3: Adding new method + +## [1.7.3] - 2013-09-27 +### Added +- AS3: Multiname and namespace editing. +- [#382],[#396] AS3: Adding new trait (method/slot/const) +- AS3: Highlighting pair parenthesis/bracket +- AS3: Editing various new P-code parameters +- AS3: Highligting of trait names/types/parameters +- AS3: Global rename identifier for traits +- [#357] Playback controls for DefineSound +- [#391] AS3: Native methods mark +- [#395] Support for GFx ScaleForm SWFs (with fonts editing) +- Displaying fonts in internal viewer +- [#334], [#395] New Embed font dialog - selecting character ranges with preview +- Replacing characters in font (Yes/No to all dialog) + +### Changed +- AS3: New p-code syntax inspired by RABCDasm +- AS3: Editing whole trait in one textarea +- AS3: Removed messages about adding new constants +- AS3: Modified colors in editor +- [#301] Clearing error log causes icon to reset + +## [1.7.2 update 2] - 2013-09-13 +### Changed +- Updated translations + +### Fixed +- [#383] Firefox browser cache handling +- [#386] SWF resizing + +## [1.7.2 update 1] - 2013-09-11 +### Changed +- updated translations + +### Fixed +- [#383] Fixed cache loading when Firefox not used + +## [1.7.2] - 2013-09-11 +### Added +- [#357] Sounds Preview (Windows only) +- Movies preview (Windows only) +- Whole SWF display +- Preview controls (Play,Pause,Stop) +- Search SWFs in browsers cache (Firefox, Chrome) +- [#367] Memory search: Save selected files to disk +- Portugese translation + +### Changed +- [#380] Faster displaying DefineBitsLossless(2) images + +### Fixed +- [#292] Background color for Fonts preview fixed +- [#375] Replacing DefineBitsLossless image tag +- [#378] Refreshing language of JSyntaxPane +- MORPHGRADIENT reading fix + +## [1.7.1] - 2013-08-25 +### Added +- Loading SWFs from other processes memory (Windows only, sorry) +- [#325] Spanish translation +- [#210] Ukrainian and Dutch translation +- [#355] Chinese translation +- [#292] Change background color in SWF preview +- [#301] Clear errors log button +- [#313] Command line parameter for ignore all errors +- [#330] Protection agains adding same characters +- [#332] AS1/2 Showing elapsed times during commandline export +- [#344] Reload opened SWF +- Decompilation timeouts + +### Fixed +- [#295] Export FLA: wrong font +- [#297] Too bright titlebar button colors +- [#307] Export FLA: fixed empty textfields +- [#309] Export FLA: static text letter spacing detection +- [#310] Export FLA: Strokes +- [#311] Export FLA: BitmapFill +- [#327] AS1/2 Disassembly error stop application +- [#328] Fixed replacing images in DefineBitsJPEGX +- [#333] AS1/2 action reading +- [#336] Graph window is too small +- [#337] Quick search panel barely visible in same cases +- [#338] Expand/collapse icon in errorlog + +## [1.7.0 update 1] - 2013-08-11 +### Added +- [#315] German translation (partial) + +### Fixed +- [#123] Better context menu integration +- [#243],[#326] Better deobfuscation +- [#287] Typo in parallelSpeedUp parameter +- [#290],[#291] improved select language dialog +- [#294] minor GUI fixes +- [#298] Progressbar positition issues +- [#296] better export directory remembering +- [#314] Better deobfuscating filenames +- [#316] Readonly editor panes accepted Ctrl+Z/Y +- [#318] Export FLA: Shapes export fix +- [#319] AS3: Improved try..catch..finally decompilation +- [#323] AS3: Fixed default switch part + +## [1.7.0] - 2013-08-03 +### Added +- Listing contributors on about page +- [#223] AS2: Detecting uninitialized class fields +- [#250] Export FLA: Detecting static fields margin and spacing +- [#261] Export FLA: AS1/2 Frame scripts on first layer +- [#269] Commandline parameters for switching configuration +- [#274] AS3 Displaying elapsed time during commandline export +- [#275] AS3 Removing returnvoid as last statement + +### Changed +- New GUI based on Substance look and feel +- Menu changed to ribbon panel +- New round icon +- [#258] AS1/2: Improved chained assignments +- [#267] Starting program without choosing a file +- [#286] Saving to temp file first + +### Fixed +- [#123] Improved context menu integration on Windows +- [#233] Globally rename identifier deselects item in the tree +- [#235] Export FLA: Dynamic text fields coordinates +- [#243],[#263],[#264],[#265],[#266],[#281] Improved deobfuscation +- [#251] Export FLA: Fixed filter strength rounding +- [#257] Export FLA: Text field color and size issues +- [#259] Fixed images alpha +- [#260] Export FLA: Labels position +- [#268] AS1/2 Function parameter shown as register instead loc +- [#272] AS3 Class initializer editation fix +- [#276] Fixed anonymous/inline functions handling +- [#220] Improved editing fonts / texts +- [#284],... other small fixes + +## [1.6.7] - 2013-07-20 +### Added +- [#220] Selection of font to import characters from +- [#232] Automatically add .swf extension in saveas dialog +- [#253] Abort/Retry/Ignore dialog on errors with file saving + +### Changed +- Improved translations + +### Fixed +- [#137],[#242], [#243], [#244] AS1/2/3 fixed deobfuscation +- [#203] AS1/2 improved direct editing +- [#220] Adding characters to font fix +- [#225] AS1/2 object literal without name quotes +- [#236] AS1/2 Rename invalid identifiers issues +- [#245] AS3 Double space around "as" keyword +- [#247] AS3 Scrolling to main class at startup +- [#248] Memory issues (slowdown) +- [#254] Expressions as commands +- [#255] Windows 7 loading issues +- [#256] AS3 Object literal in return clause +- SWF text parsing (new lines) +- Labels size by locales + +## [1.6.6 update 2] - 2013-07-16 +### Fixed +- [#241] Program could not be started + +## [1.6.6 update 1] - 2013-07-16 +### Changed +- Better localization support + +### Fixed +- [#238],[#239],[#240] Fixed deobfuscation related problems +- [#237] Parentheses in AS1/2 add,subtract + +## [1.6.6] - 2013-07-16 +### Added +- [#217] Russian translation (focus) +- [#219] Hungarian translation (honfika) +- [#224] Swedish translation (Capasha) +- [#220] Adding characters to Fonts, displaying font info +- [#121] Search progress indication +- Error log + +### Changed +- [#203] Improved direct editing of AS1/2 +- [#207] Update SWF preview after switching external/internal flash player + +### Fixed +- [#151] Memory caching +- [#171] Skipping invalid AS3 code - newobject, newarray +- [#206] AS3 switch problem +- [#208] Renaming anonymous functions +- [#209],[#229] FLA export texts positions +- [#213],[#221] other decompilation issues +- [#225] AS object literal broken with ternar operator +- [#226] onClip indentation in FLA export +- [#227] gotoAndStop wrong frame index +- [#230] FLA export missing strokes +- Shapes viewer - missing strokes + +## [1.6.5 update 1] - 2013-07-09 +### Fixed +- [#151] Fixed caching in memory +- [#172] AS1/2 constant detection fix +- [#174] Renaming SymbolClass fix +- [#175],[#212] Fixed create directory issues on export +- [#185],[#186] on-clip actions indentation +- [#197] AS1/2 Missing storeregister before switch +- [#216] AS2 Fixed field order +- [#213] AS2 Fixed var fields quotes, switch nullpointer + +## [1.6.5] - 2013-07-08 +### Added +- Multilanguage support (currently English and Czech) +- [#151] Option for caching in memory instead of files +- [#168] Export selection in tree context menu +- [#176] option to show main class on startup +- [#177] saving window maximized state +- [#202] Removing tags other than DefineSprite + +### Changed +- [#173],[#190] Better renaming +- [#129], [#153] Better deobfuscation +- [#180] better error handling +- [#185],[#186] better displaying and exporting onclip actions + +### Fixed +- [#123] Better context menu integration +- [#136] FLA export - text sizes +- [#137],[#179] foreach issues (hasNext) +- [#144] Plain text export - empty line fix +- [#144] Not displaying texts +- [#164] DefineMorphShape issues +- [#167] Sprite tag appearing twice in export filename +- [#170] AS3 Try in loop +- [#172] loop detection fix +- [#175] use empty namespace +- [#178] AS subtract with negate +- [#181] AS3 missing quotes in object field +- [#182] missing namespace imports +- [#183] wrong stage size +- [#184] wrong video link +- [#189] Fixed three dots in tree +- [#191] Focalgradient fill fix +- [#195] AS2 issues +- [#196],[#197] switch issues +- [#198] DefineFont2 empty check +- [#200] DefineBitsLossLess fix +- [#201] Nonworking main window in Linux/MacOS (due toAssociation) + +## [1.6.4 update 1] - 2013-06-30 +### Fixed +- [#166] For loops detection +- [#165] AS3:direct lookupswitch support + +## [1.6.4] - 2013-06-30 +### Added +- [#63] Globally rename identifier +- [#67] Deobfuscation - rename identifiers according to type +- [#117] Drag & Drop SWF file to main window opens it +- [#123] Context menu integration on Windows +- [#127] Drag & Drop items from tree outside of application +- [#134] AS3: Find document class +- [#144] New lines in plain text export +- [#155] Remembering window size + splitbar positions between runs + +### Changed +- [#142] Using exportassets tag for tag names +- [#146] Display AS2 classes as tree of packages +- Better loop detection + +### Fixed +- [#129] AS1/2: not refreshing decompiled after rename +- [#130] Renaming SymbolClass identifiers too +- [#132] Renaming identifiers renamed strings +- [#136] Invalid text positions in FLA export +- [#145] Unicode support +- [#147] Escape filenames during obfuscated AS3 export +- [#148] Better package vs classname handling +- [#152] Empty if branches not inverted +- [#156] Better search handling (not freezing) +- [#157] AS3: Try statements in loops +- [#158] Graph repaint problem +- [#159] AS3: Improper rest parameter handling +- [#160] Commandline binaryData export +- [#162] DefineBitsJPEG2 image replacing +- [#163] Closing SWF file after loading +- other minor fixes + +## [1.6.3 update 2] - 2013-06-21 +### Changed +- [#149] Ifs with empty onTrue branches now inverted + +### Fixed +- [#150] Long line restriction removed + +## [1.6.3 update 1] - 2013-06-21 +### Fixed +- Memory limit decreased - FFDec was not working on 32 bit JVM. + +## [1.6.3] - 2013-06-20 + +### Changed +- Parallel SpeedUp can be disabled in menu +- Better loop detection + +### Fixed +- [#119] Replacements file not found issue +- [#101] AS1/2 postincrement fix +- [#114],[#116],[#135],[#141] Fixed loop detection +- [#102] Fixed loop highlighting in export +- [#124] Flash player file path detection +- [#128] Improved imports +- [#135] CommentItem fix +- [#129],[#131] Better deobfuscation +- [#104] AS3 inc/dec local deobfuscation fix +- [#113],[#133],[#140] Memory limit increased + +## [1.6.2] - 2013-06-09 +### Added +- New loop detection algorithm + +### Changed +- [#108] - Faster loading and decompiling (Parallelism) +- Improved Internal flash viewer - shapes and morphshapes + +### Fixed +- Ternar operator fix +- [#102] Fixed Shapes to FLA export +- AS1/2 class detection fix +- [#105],[#104],[#101] fixed via new loop detection + +## [1.6.1] - 2013-06-03 +### Added +- Internal Flash Viewer - preview of flash parts (shapes,sprites,frames) without need of Flash Player. (Used on nonWindows platforms by default) +- [#109], [#106], [#107] some code improvements + +### Changed +- Application needs Java 1.7 to run + +### Removed +- Support for Java before 1.7 + +### Fixed +- [#102], [#110] AS3: Class highlight fix +- [#103] AS3:Fixed setslot handling +- [#104] AS3:Inc/Declocal nullpointer fix +- [#104] Multiple conditions in loop fix +- [#111] AS3:Object literal truncates line +- [#105] Better do while..break handling +- loop fixes + +## [1.6.0 update 1] - 2013-05-25 +### Added +- better FLA export + +### Fixed +- Many FLA export related bugs (like [#96]) +- [#98] Empty initializers do not cause empty lines now +- [#99] small logging issues +- [#100] large obfuscated code support + +## [1.6.0] - 2013-05-20 +### Added +- Export to FLA (Experimental BETA!) +- [#85] Search text in all ActionScripts +- SWF 11 support + +### Fixed +- [#79] ActionStartDrag constraint fix +- [#92] Inversed GreaterThan/LessThan +- [#93] AS1/2 fixed switch detection +- [#94] AS1/2 ActionTry - register cast fix +- [#95] Better script end handling + +## [1.5.2] - 2013-05-05 +### Added +- Improved automatic update system (changes log). +- Handling script traits as separate objects. +- [#86] open/save file dialog now accepts absolute paths in quotes + +### Fixed +- [#87] Not displaying image changes in DefineBitsLossLess1 & 2 +- [#88] Fixed graph building +- [#89] AS3: bracket in property name lead to missing dot +- [#82] printgraph issue + +## [1.5.1 update 1] - 2013-05-04 +### Added +- Exporting texts via commandline +- Exporting all via commandline + +### Fixed +- DefineText2 color parameter +- AS3 GetSlot,SetSlot +- [#78],[#81],[#82],[#84] Fixed deobfuscation, exceptions during printgraph,... +- [#83] Fixed images transparency (zlib fix) +- Fixed graphparts with only jump in it (obfuscators) +- MORPHGRADIENT FIX +- Trasparency in DefineBitsJPEG3 and 4 +- Displaying shapes,morphshapes and sprites with bitmaps + +## [1.5.1] - 2013-05-01 +### Added +- Support for larger switches (10+cases) +- Editing text tags +- [#65] Exporting text tags +- Removing sprites +- Replacing images + +### Fixed +- DefineMorphShape2 fix +- [#79] - AS1/2 class detection fix, wrong printgraph fix +- [#78] - script trait slots fix + +## [1.5.0 update 1] - 2013-04-21 +### Fixed +- Automatic deobfuscation config defaulted to Off for AS1/2. + +## [1.5.0] - 2013-04-20 +### Added +- Direct editing of ActionScript 1/2 code (Beta) +- AS1/2: ifFrameLoaded support +- Automatic deobfuscation can be disabled in the menu +- [#48] - Decompile only specified class (commandline option) +- [#53] - AS3: Displaying multiname indices in trait detail, displaying method indices +- [#66] - Decompressing LZMA via commandline +- [#68] - Exporting DefineBinaryData tags, assigning class names to characters (SymbolClass tag) +- [#69] - DoABC vs DoABCDefine tags decompilation +- [#75] - Comma separator in while/do..while conditions, better if..return handling +- AS1/2: parsing NaN,Infinity value (Fix for [#73]) + +### Changed +- New icons (edit/save/cancel and main menu) + +### Fixed +- [#62] - Errors on not defined character tags (import tag) +- [#72] - First ternary operator expression was always true +- Fixed many deobfuscation related bugs + +## [1.4.3 update 2] - 2013-04-10 +### Fixed +- [#64] - AS1/2 Resolving registers in ActionDefineFunction2 (super,this,...and parameters shown as registerxx) +- Try to fix lib/FlashPlayer.exe issue + +## [1.4.3 update 1] - 2013-04-06 +### Fixed +- [#38] - Indentation in const/var initializers, missing semicolon +- [#56] - Test output left in last release +- [#57] - Unknown instructions now do not log an exception (obfuscators do this) +- [#58] - Index out of bounds exception fix on methodinfo indices in imports detection. +- AS3 loops fix +- While true fix + +## [1.4.3] - 2013-04-04 +### Added +- AS1/2 Better deobfuscation + +### Fixed +- [#45] - Unicode characters fix +- [#50] - AS1/2 Function body deobfuscation fix +- [#51] - Displaying java class names instead of expressions +- [#52] - AS1/2 Better constantpool detection (deobfuscation) +- [#38] - AS3 indentation in initialized const/var value for newobject +- Fixed ImportAssets2 tag id + +## [1.4.2 update 1] - 2013-03-25 +### Fixed +- [#47] - wrong AS3 deobfuscation +- AS3 deobfuscation issues +- AS3 switch + +## [1.4.2] - 2013-03-24 +### Added +- [#42] - Displaying code as hex +- AS1/2: Renaming identifiers (deobfuscation) +- AS1/2,AS3: Better deobfuscation +- Storing configuration to user home +- Installer for Windows systems + +### Changed +- Graph button changed to icon. + +### Fixed +- [#39] - AS1/2 NewMethod..Pop fix +- [#40] - No logging + For..in..return decompilation +- [#44] - DefineFont2 fix +- [#36] - Multiname with invalid index +- [#43] - Ternary operator and more +- [#46] - Ifs with empty branches got ignored +- [#3] - Ignoring unknown opcodes +- Logging exceptions during export + +## [1.4.1] - 2013-03-10 +### Added +- Exporting sounds +- Better AS1/2 deobfuscation (disassembly & decompilation) + +### Fixed +- Exporting only first 1000 frames of the movie +- Decompiled code was not refreshed on AS1/2 changes +- Application no longer creates empty directories on export + +## [1.4.0 update 1] - 2013-03-04 +### Fixed +- [#37] - AS3: Reversed loop conditions + +## [1.4.0] - 2013-03-03 +### Added +- AS3: ignoring return void at the end of methods +- New icons - Silk icons +- AS3: Traits list sort button +- Better Graph display +- Frames view +- Exporting of movies (No audio) +- Some AS3 related Tests +- Homepage & Donate link in the menu + +### Changed +- Tree view instead of tabs +- AS1/2 and AS3 now share same decompiling method. + +### Fixed +- [#34] - Reversed loop conditions +- [#35] - Fixed unicode strings (Japanese) + +## [1.3.1] - 2013-02-23 +### Changed +- Flash player no longer uses SWT library + +### Fixed +- [#32] - AS2: Action255 bug +- [#31] - Erorrneous tags are now ignored +- DefineBitsLossLess 1&2 on 8bit colormapped images + +## [1.3.0] - 2013-02-17 +### Added +- Decompilation is more resistant to obfuscation +- Shapes SVG export +- AS2: Decompiling classes & interfaces +- Click&go feature - clicking actionscript source displays appropriate P-code instruction and vice-versa (both AS1/2 and AS3) +- AS3: Deobfuscation menu +- Graph button for displaying code flow Graph + +### Changed +- Complete new decompiling method in both AS1/2 and AS3 +- Application renamed from "JP ActionScript Decompiler" to "JPEXS Free Flash Decompiler". +- To edit source, Edit button must be pressed first (Due to click&go feature) + +### Fixed +- AS3: Method info editor fixed +- Edittext & Button displaying + +## [1.2.0 update 1] - 2013-01-19 +## [1.2.0] - 2013-01-19 +### Added +- Displaying various SWF objects (shapes, sprites,...) with flash player library (Windows only, sorry). +- Images display and export +- AS2: Exporting selection +- Progressbar during loading + +### Changed +- One merged window for AS1/2 and 3. +- Updated icons + +### Fixed +- AS3: xml attrib, switch in anonymous function (in AS2 too) + +## [1.1.0] - 2013-01-02 +### Added +- Checking for updates +- AS2: Exporting +- AS3: Decompiling whole scripts instead of just classes +- AS3: Exporting selected scripts +- AS3: Script search bar +- AS3: List of DoABCTags now has default "- all -" item +- AS3: Better imports, use namespaces +- AS3: XML related instructions +- AS3: Anonymous functions with names +- AS3: Better initialization of const values +- Logging exceptions to log.txt file + +### Fixed +- AS3: set_local..get_local, dup, chained assignments, highlighting, callsupervoid, typenames, with statement, loops + +## [1.0.1] - 2012-12-26 +### Added +- AS3: Runtime namespace resolving +- AS3: Arguments variable +- AS3: Better recognizing Pre/Post Increments/Decrements +- AS3: Better declarations + +### Fixed +- AS3: Fixed static variables + +## [1.0.0] - 2012-12-24 +### Added +- Support for LZMA compressed files +- AS3: Detecting local register types for declaration. +- AS3: Displaying inline functions +- AS3: Last save/open dir is remembered +- AS3: Better usage detection for multinames +- AS3: Commandline arguments for exporting +- AS3: Better chained assignments +- AS2: FSCommand2 instruction support +- Proxy: Mimetype application/octet-stream added +- Added executable for Windows users. + +### Changed +- AS3: GUI - Constants tab moved to the top +- AS3: Deobfuscation is now optional, can be accessed via menu + +### Fixed +- AS3: rest parameter, for..in, fail on large classes (due to sub limiter) +- Other minor fixes + +## [beta 1] - 2011-07-30 +### Added +- AS3: Automatic computing method body parameters (EXPERIMENTAL) +- AS3: Editing return type of methods +- AS3: Editing type and default value for variables/constants (Slot/Const traits) +- AS1/2: Few enhancements +- About dialog + +### Changed +- Gui: Updated Icons + +### Fixed +- AS 1/2: Fixed large bug causing Ifs to not decompile properly +- Proxy: Some minor fixes + +## [alpha 10] - 2011-07-13 +### Added +- AS3:Highlighting actual line +- AS3:Completing instruction names via Ctrl+Space +- AS3:Editing method parameters, method body parameters via tab panel +- AS3:ByteCode minor_version 17 supported - decimal datatypes +- AS3:Local variables and method parameters take name from debug information if present +- AS3:Automatic renaming of classes/methods when obfuscated names +- AS3:Better error messages (When cannot decompile obfuscated code) + +### Fixed +- AS3:Fixed Vector datatypes (TypeName multiname, applytype instruction) +- AS3:Hilighting fixes +- AS3:Fixed decrement/increment statements decompilation +- AS3:Decompiler now adds variable declarations on the beginning of decompiled method +- AS3:Try/catch statements fixed when debug information present +- AS3:Fixed for each statements +- AS3:Other minor fixes + +## [alpha 9] - 2011-07-02 +### Added +- AS3: Added disassembling of some new types of instructions +- AS3: Exporting source as PCode + +### Fixed +- AS3: Many other bugfixes... + +## [alpha 8] - 2010-09-19 +### Added +- AS3: Editing exceptions +- AS3: Finding usage of multinames from constant table + +### Changed +- AS1/2: Better GUI +- AS1/2: Better decompiling of Ifs, For..in + +## [alpha 7] - 2010-09-04 +### Added +- Initial public release + +[Unreleased]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version9.0.0...dev +[9.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version8.0.1...version9.0.0 +[8.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version8.0.0...version8.0.1 +[8.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.1.2...version8.0.0 +[7.1.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.1.1...version7.1.2 +[7.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.1.0...version7.1.1 +[7.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.0.1...version7.1.0 +[7.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version7.0.0...version7.0.1 +[7.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.1.1...version7.0.0 +[6.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.1.0...version6.1.1 +[6.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.0.2...version6.1.0 +[6.0.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.0.1...version6.0.2 +[6.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version6.0.0...version6.0.1 +[6.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.3.0...version6.0.0 +[5.3.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.2.0...version5.3.0 +[5.2.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.1.0...version5.2.0 +[5.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.0.2...version5.1.0 +[5.0.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.0.1...version5.0.2 +[5.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version5.0.0...version5.0.1 +[5.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.1.1...version5.0.0 +[4.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.1.0...version4.1.1 +[4.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.5...version4.1.0 +[4.0.5]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.4...version4.0.5 +[4.0.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.3...version4.0.4 +[4.0.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.2...version4.0.3 +[4.0.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.1...version4.0.2 +[4.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version4.0.0...version4.0.1 +[4.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version3.0.0...version4.0.0 +[3.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.4...version3.0.0 +[2.1.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.3...version2.1.4 +[2.1.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.2...version2.1.3 +[2.1.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.1...version2.1.2 +[2.1.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.0u2...version2.1.1 +[2.1.0 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.0u1...version2.1.0u2 +[2.1.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.1.0...version2.1.0u1 +[2.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.1u2...version2.1.0 +[2.0.1 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.1u1...version2.0.1u2 +[2.0.1 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.1...version2.0.1u1 +[2.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version2.0.0...version2.0.1 +[2.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.1u1...version2.0.0 +[1.8.1 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.1...version1.8.1u1 +[1.8.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.0u1...version1.8.1 +[1.8.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.8.0...version1.8.0u1 +[1.8.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.4u1...version1.8.0 +[1.7.4 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.4...version1.7.4u1 +[1.7.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.3u2...version1.7.4 +[1.7.3 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.3u1...version1.7.3u2 +[1.7.3 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.3...version1.7.3u1 +[1.7.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.2u2...version1.7.3 +[1.7.2 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.2u1...version1.7.2u2 +[1.7.2 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.2...version1.7.2u1 +[1.7.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.1...version1.7.2 +[1.7.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.0u1...version1.7.1 +[1.7.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.7.0...version1.7.0u1 +[1.7.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.7...version1.7.0 +[1.6.7]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.6u2...version1.6.7 +[1.6.6 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.6u1...version1.6.6u2 +[1.6.6 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.6...version1.6.6u1 +[1.6.6]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.5u1...version1.6.6 +[1.6.5 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.5...version1.6.5u1 +[1.6.5]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.4u1...version1.6.5 +[1.6.4 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.4...version1.6.4u1 +[1.6.4]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.3u2...version1.6.4 +[1.6.3 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.3u1...version1.6.3u2 +[1.6.3 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.3...version1.6.3u1 +[1.6.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.2...version1.6.3 +[1.6.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.1...version1.6.2 +[1.6.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.0u1...version1.6.1 +[1.6.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.6.0...version1.6.0u1 +[1.6.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.2...version1.6.0 +[1.5.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.1u1...version1.5.2 +[1.5.1 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.1...version1.5.1u1 +[1.5.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.0u1...version1.5.1 +[1.5.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.5.0...version1.5.0u1 +[1.5.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.3u2...version1.5.0 +[1.4.3 update 2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.3u1...version1.4.3u2 +[1.4.3 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.3...version1.4.3u1 +[1.4.3]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.2u1...version1.4.3 +[1.4.2 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.2...version1.4.2u1 +[1.4.2]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.1...version1.4.2 +[1.4.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.0u1...version1.4.1 +[1.4.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.4.0...version1.4.0u1 +[1.4.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.3.1...version1.4.0 +[1.3.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.3.0...version1.3.1 +[1.3.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.2.0u1...version1.3.0 +[1.2.0 update 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.2.0...version1.2.0u1 +[1.2.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.1.0...version1.2.0 +[1.1.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.0.1...version1.1.0 +[1.0.1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/version1.0.0...version1.0.1 +[1.0.0]: https://github.com/jindrapetrik/jpexs-decompiler/compare/beta1...version1.0.0 +[beta 1]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha10...beta1 +[alpha 10]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha9...alpha10 +[alpha 9]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha8...alpha9 +[alpha 8]: https://github.com/jindrapetrik/jpexs-decompiler/compare/alpha7...alpha8 +[alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 +[#1162]: https://www.free-decompiler.com/flash/issues/1162 +[#1156]: https://www.free-decompiler.com/flash/issues/1156 +[#1171]: https://www.free-decompiler.com/flash/issues/1171 +[#1199]: https://www.free-decompiler.com/flash/issues/1199 +[#1170]: https://www.free-decompiler.com/flash/issues/1170 +[#1241]: https://www.free-decompiler.com/flash/issues/1241 +[#1151]: https://www.free-decompiler.com/flash/issues/1151 +[#1128]: https://www.free-decompiler.com/flash/issues/1128 +[#1163]: https://www.free-decompiler.com/flash/issues/1163 +[#1172]: https://www.free-decompiler.com/flash/issues/1172 +[#1174]: https://www.free-decompiler.com/flash/issues/1174 +[#1183]: https://www.free-decompiler.com/flash/issues/1183 +[#1193]: https://www.free-decompiler.com/flash/issues/1193 +[#1200]: https://www.free-decompiler.com/flash/issues/1200 +[#1198]: https://www.free-decompiler.com/flash/issues/1198 +[#1205]: https://www.free-decompiler.com/flash/issues/1205 +[#1194]: https://www.free-decompiler.com/flash/issues/1194 +[#1210]: https://www.free-decompiler.com/flash/issues/1210 +[#1217]: https://www.free-decompiler.com/flash/issues/1217 +[#1244]: https://www.free-decompiler.com/flash/issues/1244 +[#1247]: https://www.free-decompiler.com/flash/issues/1247 +[#1236]: https://www.free-decompiler.com/flash/issues/1236 +[#1251]: https://www.free-decompiler.com/flash/issues/1251 +[#1265]: https://www.free-decompiler.com/flash/issues/1265 +[#1268]: https://www.free-decompiler.com/flash/issues/1268 +[#1161]: https://www.free-decompiler.com/flash/issues/1161 +[#1145]: https://www.free-decompiler.com/flash/issues/1145 +[#1118]: https://www.free-decompiler.com/flash/issues/1118 +[#409]: https://www.free-decompiler.com/flash/issues/409 +[#1132]: https://www.free-decompiler.com/flash/issues/1132 +[#1134]: https://www.free-decompiler.com/flash/issues/1134 +[#1121]: https://www.free-decompiler.com/flash/issues/1121 +[#1052]: https://www.free-decompiler.com/flash/issues/1052 +[#758]: https://www.free-decompiler.com/flash/issues/758 +[#1096]: https://www.free-decompiler.com/flash/issues/1096 +[#1104]: https://www.free-decompiler.com/flash/issues/1104 +[#1107]: https://www.free-decompiler.com/flash/issues/1107 +[#1106]: https://www.free-decompiler.com/flash/issues/1106 +[#1113]: https://www.free-decompiler.com/flash/issues/1113 +[#1075]: https://www.free-decompiler.com/flash/issues/1075 +[#1127]: https://www.free-decompiler.com/flash/issues/1127 +[#1103]: https://www.free-decompiler.com/flash/issues/1103 +[#1133]: https://www.free-decompiler.com/flash/issues/1133 +[#1135]: https://www.free-decompiler.com/flash/issues/1135 +[#1138]: https://www.free-decompiler.com/flash/issues/1138 +[#1139]: https://www.free-decompiler.com/flash/issues/1139 +[#930]: https://www.free-decompiler.com/flash/issues/930 +[#1137]: https://www.free-decompiler.com/flash/issues/1137 +[#1144]: https://www.free-decompiler.com/flash/issues/1144 +[#1147]: https://www.free-decompiler.com/flash/issues/1147 +[#1148]: https://www.free-decompiler.com/flash/issues/1148 +[#1152]: https://www.free-decompiler.com/flash/issues/1152 +[#1154]: https://www.free-decompiler.com/flash/issues/1154 +[#116]: https://www.free-decompiler.com/flash/issues/116 +[#1070]: https://www.free-decompiler.com/flash/issues/1070 +[#1098]: https://www.free-decompiler.com/flash/issues/1098 +[#1033]: https://www.free-decompiler.com/flash/issues/1033 +[#1083]: https://www.free-decompiler.com/flash/issues/1083 +[#1091]: https://www.free-decompiler.com/flash/issues/1091 +[#1076]: https://www.free-decompiler.com/flash/issues/1076 +[#1068]: https://www.free-decompiler.com/flash/issues/1068 +[#1063]: https://www.free-decompiler.com/flash/issues/1063 +[#1019]: https://www.free-decompiler.com/flash/issues/1019 +[#1016]: https://www.free-decompiler.com/flash/issues/1016 +[#1010]: https://www.free-decompiler.com/flash/issues/1010 +[#1008]: https://www.free-decompiler.com/flash/issues/1008 +[#1004]: https://www.free-decompiler.com/flash/issues/1004 +[#933]: https://www.free-decompiler.com/flash/issues/933 +[#418]: https://www.free-decompiler.com/flash/issues/418 +[#1062]: https://www.free-decompiler.com/flash/issues/1062 +[#1047]: https://www.free-decompiler.com/flash/issues/1047 +[#812]: https://www.free-decompiler.com/flash/issues/812 +[#1056]: https://www.free-decompiler.com/flash/issues/1056 +[#1057]: https://www.free-decompiler.com/flash/issues/1057 +[#991]: https://www.free-decompiler.com/flash/issues/991 +[#689]: https://www.free-decompiler.com/flash/issues/689 +[#1060]: https://www.free-decompiler.com/flash/issues/1060 +[#1037]: https://www.free-decompiler.com/flash/issues/1037 +[#489]: https://www.free-decompiler.com/flash/issues/489 +[#1007]: https://www.free-decompiler.com/flash/issues/1007 +[#1044]: https://www.free-decompiler.com/flash/issues/1044 +[#947]: https://www.free-decompiler.com/flash/issues/947 +[#953]: https://www.free-decompiler.com/flash/issues/953 +[#954]: https://www.free-decompiler.com/flash/issues/954 +[#950]: https://www.free-decompiler.com/flash/issues/950 +[#945]: https://www.free-decompiler.com/flash/issues/945 +[#957]: https://www.free-decompiler.com/flash/issues/957 +[#956]: https://www.free-decompiler.com/flash/issues/956 +[#968]: https://www.free-decompiler.com/flash/issues/968 +[#978]: https://www.free-decompiler.com/flash/issues/978 +[#955]: https://www.free-decompiler.com/flash/issues/955 +[#966]: https://www.free-decompiler.com/flash/issues/966 +[#999]: https://www.free-decompiler.com/flash/issues/999 +[#1000]: https://www.free-decompiler.com/flash/issues/1000 +[#1017]: https://www.free-decompiler.com/flash/issues/1017 +[#1030]: https://www.free-decompiler.com/flash/issues/1030 +[#944]: https://www.free-decompiler.com/flash/issues/944 +[#939]: https://www.free-decompiler.com/flash/issues/939 +[#942]: https://www.free-decompiler.com/flash/issues/942 +[#949]: https://www.free-decompiler.com/flash/issues/949 +[#952]: https://www.free-decompiler.com/flash/issues/952 +[#858]: https://www.free-decompiler.com/flash/issues/858 +[#905]: https://www.free-decompiler.com/flash/issues/905 +[#920]: https://www.free-decompiler.com/flash/issues/920 +[#921]: https://www.free-decompiler.com/flash/issues/921 +[#924]: https://www.free-decompiler.com/flash/issues/924 +[#895]: https://www.free-decompiler.com/flash/issues/895 +[#884]: https://www.free-decompiler.com/flash/issues/884 +[#899]: https://www.free-decompiler.com/flash/issues/899 +[#903]: https://www.free-decompiler.com/flash/issues/903 +[#855]: https://www.free-decompiler.com/flash/issues/855 +[#850]: https://www.free-decompiler.com/flash/issues/850 +[#832]: https://www.free-decompiler.com/flash/issues/832 +[#904]: https://www.free-decompiler.com/flash/issues/904 +[#910]: https://www.free-decompiler.com/flash/issues/910 +[#922]: https://www.free-decompiler.com/flash/issues/922 +[#916]: https://www.free-decompiler.com/flash/issues/916 +[#938]: https://www.free-decompiler.com/flash/issues/938 +[#897]: https://www.free-decompiler.com/flash/issues/897 +[#470]: https://www.free-decompiler.com/flash/issues/470 +[#877]: https://www.free-decompiler.com/flash/issues/877 +[#878]: https://www.free-decompiler.com/flash/issues/878 +[#845]: https://www.free-decompiler.com/flash/issues/845 +[#883]: https://www.free-decompiler.com/flash/issues/883 +[#882]: https://www.free-decompiler.com/flash/issues/882 +[#760]: https://www.free-decompiler.com/flash/issues/760 +[#887]: https://www.free-decompiler.com/flash/issues/887 +[#842]: https://www.free-decompiler.com/flash/issues/842 +[#772]: https://www.free-decompiler.com/flash/issues/772 +[#762]: https://www.free-decompiler.com/flash/issues/762 +[#841]: https://www.free-decompiler.com/flash/issues/841 +[#862]: https://www.free-decompiler.com/flash/issues/862 +[#865]: https://www.free-decompiler.com/flash/issues/865 +[#613]: https://www.free-decompiler.com/flash/issues/613 +[#868]: https://www.free-decompiler.com/flash/issues/868 +[#713]: https://www.free-decompiler.com/flash/issues/713 +[#807]: https://www.free-decompiler.com/flash/issues/807 +[#728]: https://www.free-decompiler.com/flash/issues/728 +[#857]: https://www.free-decompiler.com/flash/issues/857 +[#860]: https://www.free-decompiler.com/flash/issues/860 +[#350]: https://www.free-decompiler.com/flash/issues/350 +[#824]: https://www.free-decompiler.com/flash/issues/824 +[#809]: https://www.free-decompiler.com/flash/issues/809 +[#805]: https://www.free-decompiler.com/flash/issues/805 +[#825]: https://www.free-decompiler.com/flash/issues/825 +[#737]: https://www.free-decompiler.com/flash/issues/737 +[#814]: https://www.free-decompiler.com/flash/issues/814 +[#816]: https://www.free-decompiler.com/flash/issues/816 +[#835]: https://www.free-decompiler.com/flash/issues/835 +[#836]: https://www.free-decompiler.com/flash/issues/836 +[#848]: https://www.free-decompiler.com/flash/issues/848 +[#817]: https://www.free-decompiler.com/flash/issues/817 +[#849]: https://www.free-decompiler.com/flash/issues/849 +[#852]: https://www.free-decompiler.com/flash/issues/852 +[#837]: https://www.free-decompiler.com/flash/issues/837 +[#811]: https://www.free-decompiler.com/flash/issues/811 +[#745]: https://www.free-decompiler.com/flash/issues/745 +[#803]: https://www.free-decompiler.com/flash/issues/803 +[#738]: https://www.free-decompiler.com/flash/issues/738 +[#742]: https://www.free-decompiler.com/flash/issues/742 +[#747]: https://www.free-decompiler.com/flash/issues/747 +[#749]: https://www.free-decompiler.com/flash/issues/749 +[#752]: https://www.free-decompiler.com/flash/issues/752 +[#753]: https://www.free-decompiler.com/flash/issues/753 +[#759]: https://www.free-decompiler.com/flash/issues/759 +[#766]: https://www.free-decompiler.com/flash/issues/766 +[#768]: https://www.free-decompiler.com/flash/issues/768 +[#773]: https://www.free-decompiler.com/flash/issues/773 +[#776]: https://www.free-decompiler.com/flash/issues/776 +[#783]: https://www.free-decompiler.com/flash/issues/783 +[#785]: https://www.free-decompiler.com/flash/issues/785 +[#787]: https://www.free-decompiler.com/flash/issues/787 +[#788]: https://www.free-decompiler.com/flash/issues/788 +[#790]: https://www.free-decompiler.com/flash/issues/790 +[#794]: https://www.free-decompiler.com/flash/issues/794 +[#798]: https://www.free-decompiler.com/flash/issues/798 +[#800]: https://www.free-decompiler.com/flash/issues/800 +[#676]: https://www.free-decompiler.com/flash/issues/676 +[#734]: https://www.free-decompiler.com/flash/issues/734 +[#687]: https://www.free-decompiler.com/flash/issues/687 +[#709]: https://www.free-decompiler.com/flash/issues/709 +[#732]: https://www.free-decompiler.com/flash/issues/732 +[#730]: https://www.free-decompiler.com/flash/issues/730 +[#735]: https://www.free-decompiler.com/flash/issues/735 +[#722]: https://www.free-decompiler.com/flash/issues/722 +[#725]: https://www.free-decompiler.com/flash/issues/725 +[#715]: https://www.free-decompiler.com/flash/issues/715 +[#635]: https://www.free-decompiler.com/flash/issues/635 +[#726]: https://www.free-decompiler.com/flash/issues/726 +[#720]: https://www.free-decompiler.com/flash/issues/720 +[#716]: https://www.free-decompiler.com/flash/issues/716 +[#717]: https://www.free-decompiler.com/flash/issues/717 +[#718]: https://www.free-decompiler.com/flash/issues/718 +[#719]: https://www.free-decompiler.com/flash/issues/719 +[#723]: https://www.free-decompiler.com/flash/issues/723 +[#288]: https://www.free-decompiler.com/flash/issues/288 +[#677]: https://www.free-decompiler.com/flash/issues/677 +[#389]: https://www.free-decompiler.com/flash/issues/389 +[#701]: https://www.free-decompiler.com/flash/issues/701 +[#707]: https://www.free-decompiler.com/flash/issues/707 +[#302]: https://www.free-decompiler.com/flash/issues/302 +[#685]: https://www.free-decompiler.com/flash/issues/685 +[#698]: https://www.free-decompiler.com/flash/issues/698 +[#710]: https://www.free-decompiler.com/flash/issues/710 +[#711]: https://www.free-decompiler.com/flash/issues/711 +[#681]: https://www.free-decompiler.com/flash/issues/681 +[#688]: https://www.free-decompiler.com/flash/issues/688 +[#691]: https://www.free-decompiler.com/flash/issues/691 +[#524]: https://www.free-decompiler.com/flash/issues/524 +[#663]: https://www.free-decompiler.com/flash/issues/663 +[#702]: https://www.free-decompiler.com/flash/issues/702 +[#539]: https://www.free-decompiler.com/flash/issues/539 +[#650]: https://www.free-decompiler.com/flash/issues/650 +[#680]: https://www.free-decompiler.com/flash/issues/680 +[#649]: https://www.free-decompiler.com/flash/issues/649 +[#656]: https://www.free-decompiler.com/flash/issues/656 +[#661]: https://www.free-decompiler.com/flash/issues/661 +[#664]: https://www.free-decompiler.com/flash/issues/664 +[#668]: https://www.free-decompiler.com/flash/issues/668 +[#674]: https://www.free-decompiler.com/flash/issues/674 +[#675]: https://www.free-decompiler.com/flash/issues/675 +[#632]: https://www.free-decompiler.com/flash/issues/632 +[#651]: https://www.free-decompiler.com/flash/issues/651 +[#678]: https://www.free-decompiler.com/flash/issues/678 +[#672]: https://www.free-decompiler.com/flash/issues/672 +[#684]: https://www.free-decompiler.com/flash/issues/684 +[#647]: https://www.free-decompiler.com/flash/issues/647 +[#648]: https://www.free-decompiler.com/flash/issues/648 +[#612]: https://www.free-decompiler.com/flash/issues/612 +[#623]: https://www.free-decompiler.com/flash/issues/623 +[#624]: https://www.free-decompiler.com/flash/issues/624 +[#627]: https://www.free-decompiler.com/flash/issues/627 +[#640]: https://www.free-decompiler.com/flash/issues/640 +[#595]: https://www.free-decompiler.com/flash/issues/595 +[#592]: https://www.free-decompiler.com/flash/issues/592 +[#585]: https://www.free-decompiler.com/flash/issues/585 +[#578]: https://www.free-decompiler.com/flash/issues/578 +[#501]: https://www.free-decompiler.com/flash/issues/501 +[#616]: https://www.free-decompiler.com/flash/issues/616 +[#559]: https://www.free-decompiler.com/flash/issues/559 +[#401]: https://www.free-decompiler.com/flash/issues/401 +[#593]: https://www.free-decompiler.com/flash/issues/593 +[#594]: https://www.free-decompiler.com/flash/issues/594 +[#579]: https://www.free-decompiler.com/flash/issues/579 +[#337]: https://www.free-decompiler.com/flash/issues/337 +[#584]: https://www.free-decompiler.com/flash/issues/584 +[#428]: https://www.free-decompiler.com/flash/issues/428 +[#576]: https://www.free-decompiler.com/flash/issues/576 +[#250]: https://www.free-decompiler.com/flash/issues/250 +[#580]: https://www.free-decompiler.com/flash/issues/580 +[#510]: https://www.free-decompiler.com/flash/issues/510 +[#583]: https://www.free-decompiler.com/flash/issues/583 +[#586]: https://www.free-decompiler.com/flash/issues/586 +[#574]: https://www.free-decompiler.com/flash/issues/574 +[#570]: https://www.free-decompiler.com/flash/issues/570 +[#563]: https://www.free-decompiler.com/flash/issues/563 +[#561]: https://www.free-decompiler.com/flash/issues/561 +[#509]: https://www.free-decompiler.com/flash/issues/509 +[#433]: https://www.free-decompiler.com/flash/issues/433 +[#557]: https://www.free-decompiler.com/flash/issues/557 +[#556]: https://www.free-decompiler.com/flash/issues/556 +[#504]: https://www.free-decompiler.com/flash/issues/504 +[#529]: https://www.free-decompiler.com/flash/issues/529 +[#538]: https://www.free-decompiler.com/flash/issues/538 +[#537]: https://www.free-decompiler.com/flash/issues/537 +[#540]: https://www.free-decompiler.com/flash/issues/540 +[#387]: https://www.free-decompiler.com/flash/issues/387 +[#552]: https://www.free-decompiler.com/flash/issues/552 +[#494]: https://www.free-decompiler.com/flash/issues/494 +[#513]: https://www.free-decompiler.com/flash/issues/513 +[#262]: https://www.free-decompiler.com/flash/issues/262 +[#499]: https://www.free-decompiler.com/flash/issues/499 +[#508]: https://www.free-decompiler.com/flash/issues/508 +[#305]: https://www.free-decompiler.com/flash/issues/305 +[#312]: https://www.free-decompiler.com/flash/issues/312 +[#503]: https://www.free-decompiler.com/flash/issues/503 +[#304]: https://www.free-decompiler.com/flash/issues/304 +[#306]: https://www.free-decompiler.com/flash/issues/306 +[#507]: https://www.free-decompiler.com/flash/issues/507 +[#424]: https://www.free-decompiler.com/flash/issues/424 +[#425]: https://www.free-decompiler.com/flash/issues/425 +[#478]: https://www.free-decompiler.com/flash/issues/478 +[#485]: https://www.free-decompiler.com/flash/issues/485 +[#517]: https://www.free-decompiler.com/flash/issues/517 +[#518]: https://www.free-decompiler.com/flash/issues/518 +[#361]: https://www.free-decompiler.com/flash/issues/361 +[#392]: https://www.free-decompiler.com/flash/issues/392 +[#516]: https://www.free-decompiler.com/flash/issues/516 +[#495]: https://www.free-decompiler.com/flash/issues/495 +[#496]: https://www.free-decompiler.com/flash/issues/496 +[#299]: https://www.free-decompiler.com/flash/issues/299 +[#303]: https://www.free-decompiler.com/flash/issues/303 +[#324]: https://www.free-decompiler.com/flash/issues/324 +[#346]: https://www.free-decompiler.com/flash/issues/346 +[#369]: https://www.free-decompiler.com/flash/issues/369 +[#371]: https://www.free-decompiler.com/flash/issues/371 +[#390]: https://www.free-decompiler.com/flash/issues/390 +[#426]: https://www.free-decompiler.com/flash/issues/426 +[#453]: https://www.free-decompiler.com/flash/issues/453 +[#457]: https://www.free-decompiler.com/flash/issues/457 +[#458]: https://www.free-decompiler.com/flash/issues/458 +[#459]: https://www.free-decompiler.com/flash/issues/459 +[#460]: https://www.free-decompiler.com/flash/issues/460 +[#461]: https://www.free-decompiler.com/flash/issues/461 +[#462]: https://www.free-decompiler.com/flash/issues/462 +[#463]: https://www.free-decompiler.com/flash/issues/463 +[#465]: https://www.free-decompiler.com/flash/issues/465 +[#466]: https://www.free-decompiler.com/flash/issues/466 +[#451]: https://www.free-decompiler.com/flash/issues/451 +[#454]: https://www.free-decompiler.com/flash/issues/454 +[#455]: https://www.free-decompiler.com/flash/issues/455 +[#474]: https://www.free-decompiler.com/flash/issues/474 +[#477]: https://www.free-decompiler.com/flash/issues/477 +[#481]: https://www.free-decompiler.com/flash/issues/481 +[#484]: https://www.free-decompiler.com/flash/issues/484 +[#493]: https://www.free-decompiler.com/flash/issues/493 +[#365]: https://www.free-decompiler.com/flash/issues/365 +[#366]: https://www.free-decompiler.com/flash/issues/366 +[#429]: https://www.free-decompiler.com/flash/issues/429 +[#447]: https://www.free-decompiler.com/flash/issues/447 +[#354]: https://www.free-decompiler.com/flash/issues/354 +[#438]: https://www.free-decompiler.com/flash/issues/438 +[#436]: https://www.free-decompiler.com/flash/issues/436 +[#446]: https://www.free-decompiler.com/flash/issues/446 +[#427]: https://www.free-decompiler.com/flash/issues/427 +[#405]: https://www.free-decompiler.com/flash/issues/405 +[#420]: https://www.free-decompiler.com/flash/issues/420 +[#421]: https://www.free-decompiler.com/flash/issues/421 +[#430]: https://www.free-decompiler.com/flash/issues/430 +[#397]: https://www.free-decompiler.com/flash/issues/397 +[#431]: https://www.free-decompiler.com/flash/issues/431 +[#169]: https://www.free-decompiler.com/flash/issues/169 +[#335]: https://www.free-decompiler.com/flash/issues/335 +[#404]: https://www.free-decompiler.com/flash/issues/404 +[#407]: https://www.free-decompiler.com/flash/issues/407 +[#399]: https://www.free-decompiler.com/flash/issues/399 +[#400]: https://www.free-decompiler.com/flash/issues/400 +[#398]: https://www.free-decompiler.com/flash/issues/398 +[#382]: https://www.free-decompiler.com/flash/issues/382 +[#396]: https://www.free-decompiler.com/flash/issues/396 +[#357]: https://www.free-decompiler.com/flash/issues/357 +[#391]: https://www.free-decompiler.com/flash/issues/391 +[#395]: https://www.free-decompiler.com/flash/issues/395 +[#301]: https://www.free-decompiler.com/flash/issues/301 +[#334]: https://www.free-decompiler.com/flash/issues/334 +[#383]: https://www.free-decompiler.com/flash/issues/383 +[#386]: https://www.free-decompiler.com/flash/issues/386 +[#367]: https://www.free-decompiler.com/flash/issues/367 +[#380]: https://www.free-decompiler.com/flash/issues/380 +[#292]: https://www.free-decompiler.com/flash/issues/292 +[#375]: https://www.free-decompiler.com/flash/issues/375 +[#378]: https://www.free-decompiler.com/flash/issues/378 +[#325]: https://www.free-decompiler.com/flash/issues/325 +[#210]: https://www.free-decompiler.com/flash/issues/210 +[#355]: https://www.free-decompiler.com/flash/issues/355 +[#313]: https://www.free-decompiler.com/flash/issues/313 +[#330]: https://www.free-decompiler.com/flash/issues/330 +[#332]: https://www.free-decompiler.com/flash/issues/332 +[#344]: https://www.free-decompiler.com/flash/issues/344 +[#295]: https://www.free-decompiler.com/flash/issues/295 +[#297]: https://www.free-decompiler.com/flash/issues/297 +[#307]: https://www.free-decompiler.com/flash/issues/307 +[#309]: https://www.free-decompiler.com/flash/issues/309 +[#310]: https://www.free-decompiler.com/flash/issues/310 +[#311]: https://www.free-decompiler.com/flash/issues/311 +[#327]: https://www.free-decompiler.com/flash/issues/327 +[#328]: https://www.free-decompiler.com/flash/issues/328 +[#333]: https://www.free-decompiler.com/flash/issues/333 +[#336]: https://www.free-decompiler.com/flash/issues/336 +[#338]: https://www.free-decompiler.com/flash/issues/338 +[#315]: https://www.free-decompiler.com/flash/issues/315 +[#123]: https://www.free-decompiler.com/flash/issues/123 +[#243]: https://www.free-decompiler.com/flash/issues/243 +[#326]: https://www.free-decompiler.com/flash/issues/326 +[#287]: https://www.free-decompiler.com/flash/issues/287 +[#290]: https://www.free-decompiler.com/flash/issues/290 +[#291]: https://www.free-decompiler.com/flash/issues/291 +[#294]: https://www.free-decompiler.com/flash/issues/294 +[#298]: https://www.free-decompiler.com/flash/issues/298 +[#296]: https://www.free-decompiler.com/flash/issues/296 +[#314]: https://www.free-decompiler.com/flash/issues/314 +[#316]: https://www.free-decompiler.com/flash/issues/316 +[#318]: https://www.free-decompiler.com/flash/issues/318 +[#319]: https://www.free-decompiler.com/flash/issues/319 +[#323]: https://www.free-decompiler.com/flash/issues/323 +[#223]: https://www.free-decompiler.com/flash/issues/223 +[#258]: https://www.free-decompiler.com/flash/issues/258 +[#261]: https://www.free-decompiler.com/flash/issues/261 +[#267]: https://www.free-decompiler.com/flash/issues/267 +[#269]: https://www.free-decompiler.com/flash/issues/269 +[#274]: https://www.free-decompiler.com/flash/issues/274 +[#275]: https://www.free-decompiler.com/flash/issues/275 +[#286]: https://www.free-decompiler.com/flash/issues/286 +[#233]: https://www.free-decompiler.com/flash/issues/233 +[#235]: https://www.free-decompiler.com/flash/issues/235 +[#263]: https://www.free-decompiler.com/flash/issues/263 +[#264]: https://www.free-decompiler.com/flash/issues/264 +[#265]: https://www.free-decompiler.com/flash/issues/265 +[#266]: https://www.free-decompiler.com/flash/issues/266 +[#281]: https://www.free-decompiler.com/flash/issues/281 +[#251]: https://www.free-decompiler.com/flash/issues/251 +[#257]: https://www.free-decompiler.com/flash/issues/257 +[#259]: https://www.free-decompiler.com/flash/issues/259 +[#260]: https://www.free-decompiler.com/flash/issues/260 +[#268]: https://www.free-decompiler.com/flash/issues/268 +[#272]: https://www.free-decompiler.com/flash/issues/272 +[#276]: https://www.free-decompiler.com/flash/issues/276 +[#220]: https://www.free-decompiler.com/flash/issues/220 +[#284]: https://www.free-decompiler.com/flash/issues/284 +[#232]: https://www.free-decompiler.com/flash/issues/232 +[#253]: https://www.free-decompiler.com/flash/issues/253 +[#137]: https://www.free-decompiler.com/flash/issues/137 +[#242]: https://www.free-decompiler.com/flash/issues/242 +[#244]: https://www.free-decompiler.com/flash/issues/244 +[#203]: https://www.free-decompiler.com/flash/issues/203 +[#225]: https://www.free-decompiler.com/flash/issues/225 +[#236]: https://www.free-decompiler.com/flash/issues/236 +[#245]: https://www.free-decompiler.com/flash/issues/245 +[#247]: https://www.free-decompiler.com/flash/issues/247 +[#248]: https://www.free-decompiler.com/flash/issues/248 +[#254]: https://www.free-decompiler.com/flash/issues/254 +[#255]: https://www.free-decompiler.com/flash/issues/255 +[#256]: https://www.free-decompiler.com/flash/issues/256 +[#241]: https://www.free-decompiler.com/flash/issues/241 +[#238]: https://www.free-decompiler.com/flash/issues/238 +[#239]: https://www.free-decompiler.com/flash/issues/239 +[#240]: https://www.free-decompiler.com/flash/issues/240 +[#237]: https://www.free-decompiler.com/flash/issues/237 +[#207]: https://www.free-decompiler.com/flash/issues/207 +[#217]: https://www.free-decompiler.com/flash/issues/217 +[#219]: https://www.free-decompiler.com/flash/issues/219 +[#224]: https://www.free-decompiler.com/flash/issues/224 +[#121]: https://www.free-decompiler.com/flash/issues/121 +[#151]: https://www.free-decompiler.com/flash/issues/151 +[#171]: https://www.free-decompiler.com/flash/issues/171 +[#206]: https://www.free-decompiler.com/flash/issues/206 +[#208]: https://www.free-decompiler.com/flash/issues/208 +[#209]: https://www.free-decompiler.com/flash/issues/209 +[#229]: https://www.free-decompiler.com/flash/issues/229 +[#213]: https://www.free-decompiler.com/flash/issues/213 +[#221]: https://www.free-decompiler.com/flash/issues/221 +[#226]: https://www.free-decompiler.com/flash/issues/226 +[#227]: https://www.free-decompiler.com/flash/issues/227 +[#230]: https://www.free-decompiler.com/flash/issues/230 +[#172]: https://www.free-decompiler.com/flash/issues/172 +[#174]: https://www.free-decompiler.com/flash/issues/174 +[#175]: https://www.free-decompiler.com/flash/issues/175 +[#212]: https://www.free-decompiler.com/flash/issues/212 +[#185]: https://www.free-decompiler.com/flash/issues/185 +[#186]: https://www.free-decompiler.com/flash/issues/186 +[#197]: https://www.free-decompiler.com/flash/issues/197 +[#216]: https://www.free-decompiler.com/flash/issues/216 +[#168]: https://www.free-decompiler.com/flash/issues/168 +[#173]: https://www.free-decompiler.com/flash/issues/173 +[#190]: https://www.free-decompiler.com/flash/issues/190 +[#129]: https://www.free-decompiler.com/flash/issues/129 +[#153]: https://www.free-decompiler.com/flash/issues/153 +[#176]: https://www.free-decompiler.com/flash/issues/176 +[#177]: https://www.free-decompiler.com/flash/issues/177 +[#180]: https://www.free-decompiler.com/flash/issues/180 +[#202]: https://www.free-decompiler.com/flash/issues/202 +[#136]: https://www.free-decompiler.com/flash/issues/136 +[#179]: https://www.free-decompiler.com/flash/issues/179 +[#144]: https://www.free-decompiler.com/flash/issues/144 +[#164]: https://www.free-decompiler.com/flash/issues/164 +[#167]: https://www.free-decompiler.com/flash/issues/167 +[#170]: https://www.free-decompiler.com/flash/issues/170 +[#178]: https://www.free-decompiler.com/flash/issues/178 +[#181]: https://www.free-decompiler.com/flash/issues/181 +[#182]: https://www.free-decompiler.com/flash/issues/182 +[#183]: https://www.free-decompiler.com/flash/issues/183 +[#184]: https://www.free-decompiler.com/flash/issues/184 +[#189]: https://www.free-decompiler.com/flash/issues/189 +[#191]: https://www.free-decompiler.com/flash/issues/191 +[#195]: https://www.free-decompiler.com/flash/issues/195 +[#196]: https://www.free-decompiler.com/flash/issues/196 +[#198]: https://www.free-decompiler.com/flash/issues/198 +[#200]: https://www.free-decompiler.com/flash/issues/200 +[#201]: https://www.free-decompiler.com/flash/issues/201 +[#166]: https://www.free-decompiler.com/flash/issues/166 +[#165]: https://www.free-decompiler.com/flash/issues/165 +[#63]: https://www.free-decompiler.com/flash/issues/63 +[#67]: https://www.free-decompiler.com/flash/issues/67 +[#117]: https://www.free-decompiler.com/flash/issues/117 +[#127]: https://www.free-decompiler.com/flash/issues/127 +[#134]: https://www.free-decompiler.com/flash/issues/134 +[#142]: https://www.free-decompiler.com/flash/issues/142 +[#146]: https://www.free-decompiler.com/flash/issues/146 +[#155]: https://www.free-decompiler.com/flash/issues/155 +[#130]: https://www.free-decompiler.com/flash/issues/130 +[#132]: https://www.free-decompiler.com/flash/issues/132 +[#145]: https://www.free-decompiler.com/flash/issues/145 +[#147]: https://www.free-decompiler.com/flash/issues/147 +[#148]: https://www.free-decompiler.com/flash/issues/148 +[#152]: https://www.free-decompiler.com/flash/issues/152 +[#156]: https://www.free-decompiler.com/flash/issues/156 +[#157]: https://www.free-decompiler.com/flash/issues/157 +[#158]: https://www.free-decompiler.com/flash/issues/158 +[#159]: https://www.free-decompiler.com/flash/issues/159 +[#160]: https://www.free-decompiler.com/flash/issues/160 +[#162]: https://www.free-decompiler.com/flash/issues/162 +[#163]: https://www.free-decompiler.com/flash/issues/163 +[#149]: https://www.free-decompiler.com/flash/issues/149 +[#150]: https://www.free-decompiler.com/flash/issues/150 +[#119]: https://www.free-decompiler.com/flash/issues/119 +[#101]: https://www.free-decompiler.com/flash/issues/101 +[#114]: https://www.free-decompiler.com/flash/issues/114 +[#135]: https://www.free-decompiler.com/flash/issues/135 +[#141]: https://www.free-decompiler.com/flash/issues/141 +[#102]: https://www.free-decompiler.com/flash/issues/102 +[#124]: https://www.free-decompiler.com/flash/issues/124 +[#128]: https://www.free-decompiler.com/flash/issues/128 +[#131]: https://www.free-decompiler.com/flash/issues/131 +[#104]: https://www.free-decompiler.com/flash/issues/104 +[#113]: https://www.free-decompiler.com/flash/issues/113 +[#133]: https://www.free-decompiler.com/flash/issues/133 +[#140]: https://www.free-decompiler.com/flash/issues/140 +[#108]: https://www.free-decompiler.com/flash/issues/108 +[#105]: https://www.free-decompiler.com/flash/issues/105 +[#109]: https://www.free-decompiler.com/flash/issues/109 +[#106]: https://www.free-decompiler.com/flash/issues/106 +[#107]: https://www.free-decompiler.com/flash/issues/107 +[#110]: https://www.free-decompiler.com/flash/issues/110 +[#103]: https://www.free-decompiler.com/flash/issues/103 +[#111]: https://www.free-decompiler.com/flash/issues/111 +[#96]: https://www.free-decompiler.com/flash/issues/96 +[#98]: https://www.free-decompiler.com/flash/issues/98 +[#99]: https://www.free-decompiler.com/flash/issues/99 +[#100]: https://www.free-decompiler.com/flash/issues/100 +[#85]: https://www.free-decompiler.com/flash/issues/85 +[#79]: https://www.free-decompiler.com/flash/issues/79 +[#92]: https://www.free-decompiler.com/flash/issues/92 +[#93]: https://www.free-decompiler.com/flash/issues/93 +[#94]: https://www.free-decompiler.com/flash/issues/94 +[#95]: https://www.free-decompiler.com/flash/issues/95 +[#86]: https://www.free-decompiler.com/flash/issues/86 +[#87]: https://www.free-decompiler.com/flash/issues/87 +[#88]: https://www.free-decompiler.com/flash/issues/88 +[#89]: https://www.free-decompiler.com/flash/issues/89 +[#82]: https://www.free-decompiler.com/flash/issues/82 +[#78]: https://www.free-decompiler.com/flash/issues/78 +[#81]: https://www.free-decompiler.com/flash/issues/81 +[#84]: https://www.free-decompiler.com/flash/issues/84 +[#83]: https://www.free-decompiler.com/flash/issues/83 +[#65]: https://www.free-decompiler.com/flash/issues/65 +[#48]: https://www.free-decompiler.com/flash/issues/48 +[#53]: https://www.free-decompiler.com/flash/issues/53 +[#66]: https://www.free-decompiler.com/flash/issues/66 +[#68]: https://www.free-decompiler.com/flash/issues/68 +[#69]: https://www.free-decompiler.com/flash/issues/69 +[#75]: https://www.free-decompiler.com/flash/issues/75 +[#73]: https://www.free-decompiler.com/flash/issues/73 +[#62]: https://www.free-decompiler.com/flash/issues/62 +[#72]: https://www.free-decompiler.com/flash/issues/72 +[#64]: https://www.free-decompiler.com/flash/issues/64 +[#38]: https://www.free-decompiler.com/flash/issues/38 +[#56]: https://www.free-decompiler.com/flash/issues/56 +[#57]: https://www.free-decompiler.com/flash/issues/57 +[#58]: https://www.free-decompiler.com/flash/issues/58 +[#45]: https://www.free-decompiler.com/flash/issues/45 +[#50]: https://www.free-decompiler.com/flash/issues/50 +[#51]: https://www.free-decompiler.com/flash/issues/51 +[#52]: https://www.free-decompiler.com/flash/issues/52 +[#47]: https://www.free-decompiler.com/flash/issues/47 +[#42]: https://www.free-decompiler.com/flash/issues/42 +[#39]: https://www.free-decompiler.com/flash/issues/39 +[#40]: https://www.free-decompiler.com/flash/issues/40 +[#44]: https://www.free-decompiler.com/flash/issues/44 +[#36]: https://www.free-decompiler.com/flash/issues/36 +[#43]: https://www.free-decompiler.com/flash/issues/43 +[#46]: https://www.free-decompiler.com/flash/issues/46 +[#3]: https://www.free-decompiler.com/flash/issues/3 +[#37]: https://www.free-decompiler.com/flash/issues/37 +[#34]: https://www.free-decompiler.com/flash/issues/34 +[#35]: https://www.free-decompiler.com/flash/issues/35 +[#32]: https://www.free-decompiler.com/flash/issues/32 [#31]: https://www.free-decompiler.com/flash/issues/31 \ No newline at end of file diff --git a/build.xml b/build.xml index 9e13bd7c8..a24290fe1 100644 --- a/build.xml +++ b/build.xml @@ -1,1025 +1,1025 @@ - - - Builds project FFDec - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${app.description} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - - - - - - - - - ${product.name} ${version}${version.suffix} - - - - - - - - - - - #base.pkg -]]> - - - - - - - - - - - #!/bin/bash cd "$(dirname "$${BASH_SOURCE[0]}")" cd ../Resources ./${app.script} - - - - - - CFBundleDisplayName - ${product.name} - CFBundleExecutable - FFDec - CFBundleIconFile - Icon.icns - CFBundleIdentifier - ${mac.bundle} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - FFDec - CFBundlePackageType - APPL - CFBundleShortVersionString - ${version}${version.suffix} - CFBundleVersion - ${version}${version.suffix} - LSApplicationCategoryType - ${mac.category.type} - -]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - ${exe.type} - ${jar.filename}.jar - ${basedir}/${dist.dir}/${exe.filename}.exe - - - - normal - http://java.com/download - - false - false - false - - ${basedir}/${exe.icon} - - - ${min.java.version} - - preferJre - 64/32 - ${max.heap.size.percent} - -Djava.net.preferIPv4Stack=true - -Djna.nosys=true - - - graphics/splash2.bmp - true - 60 - true - - - ${version.number} - ${version}${version.suffix} - ${product.name} - ${vendor} - ${version.number} - ${version}${version.suffix} - ${product.name} - - ${internal.name} - ${exe.filename}.exe - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tools.properties NOT found. - Please create file named "tools.properties" and add two lines: - nsis.path = Path to Nullsoft Install System - launch4j.path = Path to launch4j - - WARNING: You must use double backslash in paths on Windows System or forward slash - - - - - - - - - - - - - - - - - - - - - - - - - - - - Checking version online: ${newversion.type}... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Uploading... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${project.name} ${version}${version.suffix} localization pack - ------------------------------------------------------------------- - Please follow instructions on https://www.free-decompiler.com/flash/translations/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Builds project FFDec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${app.description} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + + ${product.name} ${version}${version.suffix} + + + + + + + + + + + #base.pkg +]]> + + + + + + + + + + + #!/bin/bash cd "$(dirname "$${BASH_SOURCE[0]}")" cd ../Resources ./${app.script} + + + + + + CFBundleDisplayName + ${product.name} + CFBundleExecutable + FFDec + CFBundleIconFile + Icon.icns + CFBundleIdentifier + ${mac.bundle} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + FFDec + CFBundlePackageType + APPL + CFBundleShortVersionString + ${version}${version.suffix} + CFBundleVersion + ${version}${version.suffix} + LSApplicationCategoryType + ${mac.category.type} + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + ${exe.type} + ${jar.filename}.jar + ${basedir}/${dist.dir}/${exe.filename}.exe + + + + normal + http://java.com/download + + false + false + false + + ${basedir}/${exe.icon} + + + ${min.java.version} + + preferJre + 64/32 + ${max.heap.size.percent} + -Djava.net.preferIPv4Stack=true + -Djna.nosys=true + + + graphics/splash2.bmp + true + 60 + true + + + ${version.number} + ${version}${version.suffix} + ${product.name} + ${vendor} + ${version.number} + ${version}${version.suffix} + ${product.name} + + ${internal.name} + ${exe.filename}.exe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tools.properties NOT found. + Please create file named "tools.properties" and add two lines: + nsis.path = Path to Nullsoft Install System + launch4j.path = Path to launch4j + + WARNING: You must use double backslash in paths on Windows System or forward slash + + + + + + + + + + + + + + + + + + + + + + + + + + + + Checking version online: ${newversion.type}... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Uploading... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${project.name} ${version}${version.suffix} localization pack + ------------------------------------------------------------------- + Please follow instructions on https://www.free-decompiler.com/flash/translations/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iggy-extract.bat b/iggy-extract.bat index 0c0103516..c9d91f01a 100644 --- a/iggy-extract.bat +++ b/iggy-extract.bat @@ -1,2 +1,2 @@ -@java -cp lib/ffdec_lib.jar com.jpexs.decompiler.flash.iggy.conversion.IggyToSwfConvertor "%1" "%2" +@java -cp lib/ffdec_lib.jar com.jpexs.decompiler.flash.iggy.conversion.IggyToSwfConvertor "%1" "%2" @pause \ No newline at end of file diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index 6e2e2fd38..4172e6543 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -1,3684 +1,3684 @@ -/* - * Copyright (C) 2010-2016 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash; - -import SevenZip.Compression.LZMA.Decoder; -import SevenZip.Compression.LZMA.Encoder; -import com.jpexs.debugger.flash.SWD; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.CachedDecompilation; -import com.jpexs.decompiler.flash.abc.ClassPath; -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AbcMultiNameCollisionFixer; -import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; -import com.jpexs.decompiler.flash.abc.avm2.model.GetLexAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.InitPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; -import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; -import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; -import com.jpexs.decompiler.flash.abc.types.ConvertData; -import com.jpexs.decompiler.flash.abc.types.MethodBody; -import com.jpexs.decompiler.flash.abc.types.Multiname; -import com.jpexs.decompiler.flash.abc.types.ScriptInfo; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; -import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; -import com.jpexs.decompiler.flash.action.Action; -import com.jpexs.decompiler.flash.action.ActionGraphSource; -import com.jpexs.decompiler.flash.action.ActionList; -import com.jpexs.decompiler.flash.action.ActionListReader; -import com.jpexs.decompiler.flash.action.ActionLocalData; -import com.jpexs.decompiler.flash.action.CachedScript; -import com.jpexs.decompiler.flash.action.model.ConstantPool; -import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; -import com.jpexs.decompiler.flash.action.model.FunctionActionItem; -import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; -import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; -import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; -import com.jpexs.decompiler.flash.action.swf4.ActionEquals; -import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; -import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ActionPush; -import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; -import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; -import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; -import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; -import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; -import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; -import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; -import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; -import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; -import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; -import com.jpexs.decompiler.flash.ecma.Null; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; -import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; -import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; -import com.jpexs.decompiler.flash.exporters.shape.ShapeExportData; -import com.jpexs.decompiler.flash.helpers.HighlightedText; -import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; -import com.jpexs.decompiler.flash.helpers.NulWriter; -import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; -import com.jpexs.decompiler.flash.helpers.collections.MyEntry; -import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.DebugIDTag; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag; -import com.jpexs.decompiler.flash.tags.EnableDebuggerTag; -import com.jpexs.decompiler.flash.tags.EnableTelemetryTag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.ProtectTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.TagStub; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -import com.jpexs.decompiler.flash.tags.base.Exportable; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.ImportTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.RenderContext; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.enums.ImageFormat; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.AS2Package; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.FrameScript; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.annotations.Internal; -import com.jpexs.decompiler.flash.types.annotations.SWFField; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import com.jpexs.decompiler.flash.xfl.XFLConverter; -import com.jpexs.decompiler.flash.xfl.XFLExportSettings; -import com.jpexs.decompiler.graph.DottedChain; -import com.jpexs.decompiler.graph.Graph; -import com.jpexs.decompiler.graph.GraphSourceItem; -import com.jpexs.decompiler.graph.GraphSourceItemContainer; -import com.jpexs.decompiler.graph.GraphTargetItem; -import com.jpexs.decompiler.graph.ScopeStack; -import com.jpexs.decompiler.graph.TranslateStack; -import com.jpexs.decompiler.graph.model.IfItem; -import com.jpexs.decompiler.graph.model.LocalData; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.Cache; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.NulStream; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.AffineTransform; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.EmptyStackException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.TreeMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.InflaterInputStream; - -/** - * Class representing SWF file - * - * @author JPEXS - */ -public final class SWF implements SWFContainerItem, Timelined { - - // big object for testing cleanup - //BigObject bigObj = new BigObject(); - /** - * Default version of SWF file format - */ - public static final int DEFAULT_VERSION = 10; - - /** - * Maximum SWF file format version Needs to be fixed when SWF versions - * reaches this value - */ - public static final int MAX_VERSION = 32; - - /** - * Tags inside of file - */ - @SWFField - private List tags = new ArrayList<>(); - - @Internal - public ReadOnlyTagList readOnlyTags; - - public boolean hasEndTag = true; - - /** - * ExportRectangle for the display - */ - public RECT displayRect; - - /** - * Movie frame rate - */ - public float frameRate; - - /** - * Number of frames in movie - */ - public int frameCount; - - /** - * Version of SWF - */ - public int version; - - /** - * Uncompressed size of the file - */ - @Internal - public long fileSize; - - /** - * Used compression mode - */ - public SWFCompression compression = SWFCompression.NONE; - - /** - * Compressed size of the file (LZMA) - */ - @Internal - public long compressedSize; - - /** - * LZMA Properties - */ - public byte[] lzmaProperties; - - @Internal - public byte[] uncompressedData; - - @Internal - public byte[] originalUncompressedData; - - /** - * ScaleForm GFx - */ - public boolean gfx = false; - - @Internal - public SWFList swfList; - - @Internal - private String file; - - @Internal - private String fileTitle; - - @Internal - private volatile Map characters; - - @Internal - private volatile Map> characterIdTags; - - @Internal - private volatile Map> dependentCharacters; - - @Internal - private volatile List abcList; - - @Internal - private volatile JPEGTablesTag jtt; - - @Internal - public Map sourceFontNamesMap = new HashMap<>(); - - public static final double unitDivisor = 20; - - private static final Logger logger = Logger.getLogger(SWF.class.getName()); - - @Internal - private boolean isModified; - - @Internal - private Timeline timeline; - - @Internal - public DumpInfoSwfNode dumpInfo; - - @Internal - public DefineBinaryDataTag binaryData; - - @Internal - private final HashMap deobfuscated = new HashMap<>(); - - @Internal - private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation(); - - @Internal - private final Cache frameCache = Cache.getInstance(false, false, "frame"); - - @Internal - private final Cache rectCache = Cache.getInstance(true, true, "rect"); - - @Internal - private final Cache shapeExportDataCache = Cache.getInstance(true, true, "shapeExportData"); - - @Internal - private final Cache soundCache = Cache.getInstance(false, false, "sound"); - - @Internal - private final Cache as2PcodeCache = Cache.getInstance(true, true, "as2pcode"); - - @Internal - private final Cache as2Cache = Cache.getInstance(true, false, "as2"); - - @Internal - private final Cache as3Cache = Cache.getInstance(true, false, "as3"); - - public static List swfSignatures = Arrays.asList( - "FWS", // Uncompressed Flash - "CWS", // ZLib compressed Flash - "ZWS", // LZMA compressed Flash - "GFX", // Uncompressed ScaleForm GFx - "CFX", // Compressed ScaleForm GFx - "ABC" // Non-standard LZMA compressed Flash - ); - - public void updateCharacters() { - characters = null; - characterIdTags = null; - } - - public void clearTagSwfs() { - resetTimelines(this); - updateCharacters(); - - for (Tag tag : getTags()) { - if (tag instanceof DefineSpriteTag) { - DefineSpriteTag spriteTag = (DefineSpriteTag) tag; - for (Tag tag1 : spriteTag.getTags()) { - tag1.setSwf(null); - } - - for (int i = spriteTag.getTags().size() - 1; i >= 0; i--) { - spriteTag.removeTag(i); - } - } - - if (tag instanceof DefineBinaryDataTag) { - DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) tag; - if (binaryTag.innerSwf != null) { - binaryTag.innerSwf.clearTagSwfs(); - } - } - - tag.setSwf(null); - } - - tags.clear(); - if (abcList != null) { - abcList.clear(); - } - - if (swfList != null) { - swfList.swfs.clear(); - } - - as2PcodeCache.clear(); - as2Cache.clear(); - as3Cache.clear(); - frameCache.clear(); - soundCache.clear(); - - timeline = null; - clearDumpInfo(dumpInfo); - dumpInfo = null; - jtt = null; - binaryData = null; - } - - private void clearDumpInfo(DumpInfo di) { - for (DumpInfo childInfo : di.getChildInfos()) { - clearDumpInfo(childInfo); - } - - di.getChildInfos().clear(); - } - - public Map getCharacters() { - if (characters == null) { - synchronized (this) { - if (characters == null) { - Map chars = new HashMap<>(); - Map> charIdtags = new HashMap<>(); - parseCharacters(getTags(), chars, charIdtags); - characters = Collections.unmodifiableMap(chars); - characterIdTags = Collections.unmodifiableMap(charIdtags); - } - } - } - - return characters; - } - - public List getCharacterIdTags(int characterId) { - if (characterIdTags == null) { - getCharacters(); - } - - return characterIdTags.get(characterId); - } - - public CharacterIdTag getCharacterIdTag(int characterId, int tagId) { - List characterIdTags = getCharacterIdTags(characterId); - if (characterIdTags != null) { - for (CharacterIdTag t : characterIdTags) { - if (((Tag) t).getId() == tagId) { - if (t.getCharacterId() == characterId) { - return t; - } - } - } - } - - return null; - } - - public Map> getDependentCharacters() { - if (dependentCharacters == null) { - synchronized (this) { - if (dependentCharacters == null) { - Map> dep = new HashMap<>(); - for (Tag tag : getTags()) { - if (tag instanceof CharacterTag) { - int characterId = ((CharacterTag) tag).getCharacterId(); - Set needed = new HashSet<>(); - tag.getNeededCharacters(needed); - for (Integer needed1 : needed) { - Set s = dep.get(needed1); - if (s == null) { - s = new HashSet<>(); - dep.put(needed1, s); - } - - s.add(characterId); - } - } - } - - dependentCharacters = dep; - } - } - } - - return dependentCharacters; - } - - public Set getDependentCharacters(int characterId) { - Set visited = new HashSet<>(); - - Set dependents2 = new LinkedHashSet<>(); - Set deps = getDependentCharacters().get(characterId); - if (deps != null) { - dependents2.addAll(deps); - } - - while (visited.size() != dependents2.size()) { - for (int chId : dependents2) { - if (!visited.contains(chId)) { - visited.add(chId); - if (getCharacters().containsKey(chId)) { - deps = getDependentCharacters().get(chId); - if (deps != null) { - dependents2.addAll(deps); - } - - break; - } - } - } - } - - Set dependents = new LinkedHashSet<>(); - for (Integer chId : dependents2) { - if (getCharacters().containsKey(chId)) { - dependents.add(chId); - } - } - - return dependents; - } - - public CharacterTag getCharacter(int characterId) { - return getCharacters().get(characterId); - } - - public String getExportName(int characterId) { - CharacterTag characterTag = getCharacters().get(characterId); - String exportName = characterTag != null ? characterTag.getExportName() : null; - return exportName; - } - - public FontTag getFontByClass(String fontClass) { - if (fontClass == null) { - return null; - } - for (Tag t : getTags()) { - if (t instanceof FontTag) { - if (fontClass.equals(((FontTag) t).getClassName())) { - return (FontTag) t; - } - } - } - return null; - } - - public FontTag getFontByName(String fontName) { - if (fontName == null) { - return null; - } - for (Tag t : getTags()) { - if (t instanceof FontTag) { - if (fontName.equals(((FontTag) t).getFontName())) { - return (FontTag) t; - } - } - } - return null; - } - - public FontTag getFont(int fontId) { - CharacterTag characterTag = getCharacters().get(fontId); - if (characterTag instanceof FontTag) { - return (FontTag) characterTag; - } - - if (characterTag != null) { - logger.log(Level.SEVERE, "CharacterTag should be a FontTag. characterId: {0}", fontId); - } - - return null; - } - - public ImageTag getImage(int imageId) { - CharacterTag characterTag = getCharacters().get(imageId); - if (characterTag instanceof ImageTag) { - return (ImageTag) characterTag; - } - - if (characterTag != null) { - logger.log(Level.SEVERE, "CharacterTag should be an ImageTag. characterId: {0}", imageId); - } - - return null; - } - - public DefineSoundTag getSound(int soundId) { - CharacterTag characterTag = getCharacters().get(soundId); - if (characterTag instanceof DefineSoundTag) { - return (DefineSoundTag) characterTag; - } - - if (characterTag != null) { - logger.log(Level.SEVERE, "CharacterTag should be a DefineSoundTag. characterId: {0}", soundId); - } - - return null; - } - - public TextTag getText(int textId) { - CharacterTag characterTag = getCharacters().get(textId); - if (characterTag instanceof TextTag) { - return (TextTag) characterTag; - } - - if (characterTag != null) { - logger.log(Level.SEVERE, "CharacterTag should be a TextTag. characterId: {0}", textId); - } - - return null; - } - - public List getAbcList() { - if (abcList == null) { - synchronized (this) { - if (abcList == null) { - ArrayList newAbcList = new ArrayList<>(); - getAbcTags(getTags(), newAbcList); - abcList = newAbcList; - } - } - } - - return abcList; - } - - public boolean isAS3() { - FileAttributesTag fileAttributes = getFileAttributes(); - return (fileAttributes != null && fileAttributes.actionScript3) || (fileAttributes == null && !getAbcList().isEmpty()); - } - - public MetadataTag getMetadata() { - for (Tag t : getTags()) { - if (t instanceof MetadataTag) { - return (MetadataTag) t; - } - } - - return null; - } - - public FileAttributesTag getFileAttributes() { - for (Tag t : getTags()) { - if (t instanceof FileAttributesTag) { - return (FileAttributesTag) t; - } - } - - return null; - } - - public SetBackgroundColorTag getBackgroundColor() { - for (Tag t : getTags()) { - if (t instanceof SetBackgroundColorTag) { - return (SetBackgroundColorTag) t; - } - } - - return null; - } - - public EnableTelemetryTag getEnableTelemetry() { - for (Tag t : getTags()) { - if (t instanceof EnableTelemetryTag) { - return (EnableTelemetryTag) t; - } - } - return null; - } - - public int getNextCharacterId() { - int max = 0; - Set ids = new HashSet<>(getCharacters().keySet()); - for (Tag t : tags) { - if (t instanceof ImportTag) { - ids.addAll(((ImportTag) t).getAssets().keySet()); - } - } - for (int characterId : ids) { - if (characterId > max) { - max = characterId; - } - } - - return max + 1; - } - - public synchronized JPEGTablesTag getJtt() { - if (jtt == null) { - synchronized (this) { - if (jtt == null) { - for (Tag t : getTags()) { - if (t instanceof JPEGTablesTag) { - jtt = (JPEGTablesTag) t; - break; - } - } - } - } - } - - return jtt; - } - - public String getDocumentClass() { - for (Tag t : getTags()) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) t; - for (int i = 0; i < sc.tags.size(); i++) { - if (sc.tags.get(i) == 0) { - return sc.names.get(i); - } - } - } - } - - return null; - } - - public void fixCharactersOrder(boolean checkAll) { - Set addedCharacterIds = new HashSet<>(); - Set movedTags = new HashSet<>(); - for (int i = 0; i < tags.size(); i++) { - Tag tag = tags.get(i); - if (checkAll || tag.isModified()) { - Set needed = new HashSet<>(); - tag.getNeededCharacters(needed); - if (tag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) tag; - needed.remove(characterTag.getCharacterId()); - } - boolean moved = false; - for (Integer id : needed) { - if (!addedCharacterIds.contains(id)) { - CharacterTag neededCharacter = getCharacter(id); - if (neededCharacter == null) { - continue; - } - - if (movedTags.contains(neededCharacter)) { - logger.log(Level.SEVERE, "Fixing characters order failed, recursion detected."); - return; - } - - // move the needed character to the current position - tags.remove(neededCharacter); - tags.add(i, neededCharacter); - movedTags.add(neededCharacter); - moved = true; - } - } - - if (moved) { - i--; - continue; - } - } - if (tag instanceof CharacterTag) { - addedCharacterIds.add(((CharacterTag) tag).getCharacterId()); - } - } - } - - public void resetTimelines(Timelined timelined) { - timelined.resetTimeline(); - if (timelined instanceof SWF) { - for (Tag t : ((SWF) timelined).getTags()) { - if (t instanceof Timelined) { - resetTimelines((Timelined) t); - } - } - } - } - - private void parseCharacters(Iterable list, Map characters, Map> characterIdTags) { - for (Tag t : list) { - if (t instanceof CharacterIdTag) { - int characterId = ((CharacterIdTag) t).getCharacterId(); - if (t instanceof CharacterTag) { - if (characters.containsKey(characterId)) { - logger.log(Level.SEVERE, "SWF already contains characterId={0}", characterId); - } - - if (characterId != 0) { - characters.put(characterId, (CharacterTag) t); - characterIdTags.put(characterId, new ArrayList<>()); - } - } else if (characterIdTags.containsKey(characterId)) { - characterIdTags.get(characterId).add((CharacterIdTag) t); - } - } - - if (t instanceof DefineSpriteTag) { - parseCharacters(((DefineSpriteTag) t).getTags(), characters, characterIdTags); - } - } - } - - /** - * Unresolve recursive sprites - */ - private void checkInvalidSprites() { - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof DefineSpriteTag) { - if (!isSpriteValid((DefineSpriteTag) t, new ArrayList<>())) { - tags.set(i, new TagStub(this, t.getId(), "InvalidSprite", t.getOriginalRange(), null)); - } - } - } - } - - private boolean isSpriteValid(DefineSpriteTag sprite, List path) { - if (path.contains(sprite.spriteId)) { - return false; - } - path.add(sprite.spriteId); - for (Tag t : sprite.getTags()) { - if (t instanceof DefineSpriteTag) { - if (!isSpriteValid((DefineSpriteTag) t, path)) { - return false; - } - } - } - path.remove((Integer) sprite.spriteId); - return true; - } - - @Override - public Timeline getTimeline() { - if (timeline == null) { - timeline = new Timeline(this); - } - return timeline; - } - - @Override - public void resetTimeline() { - if (timeline != null) { - timeline.reset(this); - } - } - - /** - * Gets all tags with specified id - * - * @param tagId Identificator of tag type - * @return List of tags - */ - public List getTagData(int tagId) { - List ret = new ArrayList<>(); - for (Tag tag : getTags()) { - if (tag.getId() == tagId) { - ret.add(tag); - } - } - return ret; - } - - /** - * Saves this SWF into new file - * - * @param os OutputStream to save SWF in - * @throws IOException - */ - public void saveTo(OutputStream os) throws IOException { - byte[] uncompressedData = saveToByteArray(); - compress(new ByteArrayInputStream(uncompressedData), os, compression, lzmaProperties); - } - - public byte[] getHeaderBytes() { - return getHeaderBytes(compression, gfx); - } - - private static byte[] getHeaderBytes(SWFCompression compression, boolean gfx) { - if (compression == SWFCompression.LZMA_ABC) { - return new byte[]{'A', 'B', 'C'}; - } - - byte[] ret = new byte[3]; - - if (compression == SWFCompression.LZMA) { - ret[0] = 'Z'; - } else if (compression == SWFCompression.ZLIB) { - ret[0] = 'C'; - } else if (gfx) { - ret[0] = 'G'; - } else { - ret[0] = 'F'; - } - - if (gfx) { - ret[1] = 'F'; - ret[2] = 'X'; - } else { - ret[1] = 'W'; - ret[2] = 'S'; - } - - return ret; - } - - private byte[] saveToByteArray() throws IOException { - fixCharactersOrder(false); - - byte[] data; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, version)) { - sos.write(getHeaderBytes(SWFCompression.NONE, gfx)); - sos.writeUI8(version); - sos.writeUI32(0); // placeholder for file length - sos.writeRECT(displayRect); - sos.writeFIXED8(frameRate); - sos.writeUI16(frameCount); - - sos.writeTags(getLocalTags()); - if (hasEndTag) { - sos.writeUI16(0); - } - - data = baos.toByteArray(); - } - - // update file size - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFOutputStream sos = new SWFOutputStream(baos, version)) { - sos.writeUI32(data.length); - byte[] lengthData = baos.toByteArray(); - System.arraycopy(lengthData, 0, data, 4, lengthData.length); - } - - return data; - } - - /** - * Compress SWF file - * - * @param is InputStream - * @param os OutputStream to save SWF in - * @param compression - * @param lzmaProperties - * @throws IOException - */ - private static void compress(InputStream is, OutputStream os, SWFCompression compression, byte[] lzmaProperties) throws IOException { - byte[] hdr = new byte[8]; - - is.mark(8); - - // SWFheader: signature, version and fileSize - if (is.read(hdr) != 8) { - throw new SwfOpenException("SWF header is too short"); - } - - boolean uncompressed = hdr[0] == 'F' || hdr[0] == 'G'; // FWS or GFX - if (!uncompressed) { - // fisrt decompress, then compress to the given format - is.reset(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - decompress(is, baos, false); - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - compress(bais, os, compression, lzmaProperties); - return; - } - - boolean gfx = hdr[1] == 'F' && hdr[2] == 'X'; - int version = hdr[3]; - long fileSize; - try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) { - fileSize = sis.readUI32("fileSize"); - } - - SWFOutputStream sos = new SWFOutputStream(os, version); - sos.write(getHeaderBytes(compression, gfx)); - sos.writeUI8(version); - sos.writeUI32(fileSize); - - if (compression == SWFCompression.LZMA || compression == SWFCompression.LZMA_ABC) { - long uncompressedLength = fileSize - 8; - Encoder enc = new Encoder(); - if (lzmaProperties == null) { - // todo: the bytes are from a sample swf - lzmaProperties = new byte[]{93, 0, 0, 32, 0}; - } - - int val = lzmaProperties[0] & 0xFF; - int lc = val % 9; - int remainder = val / 9; - int lp = remainder % 5; - int pb = remainder / 5; - int dictionarySize = 0; - for (int i = 0; i < 4; i++) { - dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); - } - if (Configuration.lzmaFastBytes.get() > 0) { - enc.SetNumFastBytes(Configuration.lzmaFastBytes.get()); - } - enc.SetDictionarySize(dictionarySize); - enc.SetLcLpPb(lc, lp, pb); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - enc.SetEndMarkerMode(true); - enc.Code(is, baos, -1, -1, null); - byte[] data = baos.toByteArray(); - if (compression == SWFCompression.LZMA) { - byte[] udata = new byte[4]; - udata[0] = (byte) (data.length & 0xFF); - udata[1] = (byte) ((data.length >> 8) & 0xFF); - udata[2] = (byte) ((data.length >> 16) & 0xFF); - udata[3] = (byte) ((data.length >> 24) & 0xFF); - os.write(udata); - } - enc.WriteCoderProperties(os); - if (compression == SWFCompression.LZMA_ABC) { - byte[] udata = new byte[8]; - udata[0] = (byte) (uncompressedLength & 0xFF); - udata[1] = (byte) ((uncompressedLength >> 8) & 0xFF); - udata[2] = (byte) ((uncompressedLength >> 16) & 0xFF); - udata[3] = (byte) ((uncompressedLength >> 24) & 0xFF); - udata[4] = (byte) ((uncompressedLength >> 32) & 0xFF); - udata[5] = (byte) ((uncompressedLength >> 40) & 0xFF); - udata[6] = (byte) ((uncompressedLength >> 48) & 0xFF); - udata[7] = (byte) ((uncompressedLength >> 56) & 0xFF); - os.write(udata); - } - os.write(data); - } else if (compression == SWFCompression.ZLIB) { - DeflaterOutputStream dos = new DeflaterOutputStream(os); - try { - Helper.copyStream(is, dos); - } finally { - dos.finish(); - } - } else { - Helper.copyStream(is, os); - } - } - - @Override - public boolean isModified() { - if (isModified) { - return true; - } - - for (Tag tag : getTags()) { - if (tag.isModified()) { - return true; - } - } - return false; - } - - @Override - public void setModified(boolean value) { - isModified = value; - } - - public void clearModified() { - for (Tag tag : getTags()) { - if (tag.isModified()) { - tag.createOriginalData(); - tag.setModified(false); - } - } - - isModified = false; - - try { - uncompressedData = saveToByteArray(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Cannot save SWF", ex); - } - } - - /** - * Constructs an empty SWF - */ - public SWF() { - version = SWF.DEFAULT_VERSION; - displayRect = new RECT(0, 1, 0, 1); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { - this(is, null, null, null, parallelRead, false, true); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param parallelRead Use parallel threads? - * @param lazy - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, boolean parallelRead, boolean lazy) throws IOException, InterruptedException { - this(is, null, null, null, parallelRead, false, lazy); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param file Path to the file - * @param fileTitle Title of the SWF - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, String file, String fileTitle, boolean parallelRead) throws IOException, InterruptedException { - this(is, file, fileTitle, null, parallelRead, false, true); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param listener - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { - this(is, null, null, listener, parallelRead, false, true); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param file Path to the file - * @param fileTitle Title of the SWF - * @param listener - * @param parallelRead Use parallel threads? - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { - this(is, file, fileTitle, listener, parallelRead, false, true); - } - - /** - * Faster constructor to check SWF only - * - * @param is - * @throws java.io.IOException - */ - public SWF(InputStream is) throws IOException { - decompress(is, new NulStream(), true); - } - - public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy) throws IOException, InterruptedException { - this(is, file, fileTitle, listener, parallelRead, checkOnly, lazy, null); - } - - /** - * Construct SWF from stream - * - * @param is Stream to read SWF from - * @param file Path to the file - * @param fileTitle Title of the SWF - * @param listener - * @param parallelRead Use parallel threads? - * @param checkOnly Check only file validity - * @param lazy - * @param resolver Resolver for imported tags - * @throws IOException - * @throws java.lang.InterruptedException - */ - public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy, UrlResolver resolver) throws IOException, InterruptedException { - this.file = file; - this.fileTitle = fileTitle; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SWFHeader header = decompress(is, baos, true); - gfx = header.gfx; - compression = header.compression; - lzmaProperties = header.lzmaProperties; - uncompressedData = baos.toByteArray(); - originalUncompressedData = uncompressedData; - - SWFInputStream sis = new SWFInputStream(this, uncompressedData); - dumpInfo = new DumpInfoSwfNode(this, "rootswf", "", null, 0, 0); - sis.dumpInfo = dumpInfo; - sis.skipBytesEx(3, "signature"); // skip siganture - version = sis.readUI8("version"); - fileSize = sis.readUI32("fileSize"); - dumpInfo.lengthBytes = fileSize; - if (listener != null) { - sis.addPercentListener(listener); - } - sis.setPercentMax(fileSize); - displayRect = sis.readRECT("displayRect"); - frameRate = sis.readFIXED8("frameRate"); - frameCount = sis.readUI16("frameCount"); - List tags = sis.readTagList(this, 0, parallelRead, true, !checkOnly, lazy); - if (tags.size() > 0 && tags.get(tags.size() - 1).getId() == EndTag.ID) { - tags.remove(tags.size() - 1); - } else { - hasEndTag = false; - } - this.tags = tags; - readOnlyTags = null; - if (!checkOnly) { - checkInvalidSprites(); - updateCharacters(); - assignExportNamesToSymbols(); - assignClassesToSymbols(); - if (resolver != null) { - resolveImported(resolver); - } - SWFDecompilerPlugin.fireSwfParsed(this); - } else { - boolean hasNonUnknownTag = false; - for (Tag tag : tags) { - if (tag.getOriginalDataLength() > 0 && Tag.getRequiredTags().contains(tag.getId())) { - hasNonUnknownTag = true; - } - } - if (!hasNonUnknownTag) { - throw new IOException("Invalid SWF file. No known tag found."); - } - } - - if (Configuration.autoRenameIdentifiers.get()) { - deobfuscateIdentifiers(RenameType.TYPENUMBER); - AbcMultiNameCollisionFixer collisionFixer = new AbcMultiNameCollisionFixer(); - collisionFixer.fixCollisions(this); - assignClassesToSymbols(); - clearScriptCache(); - } - - getASMs(true); // Add scriptNames to ASMs - } - - private void resolveImported(UrlResolver resolver) { - for (int p = 0; p < tags.size(); p++) { - Tag t = tags.get(p); - if (t instanceof ImportTag) { - ImportTag importTag = (ImportTag) t; - - SWF iSwf = resolver.resolveUrl(importTag.getUrl()); - if (iSwf != null) { - Map exportedMap1 = new HashMap<>(); - Map classesMap1 = new HashMap<>(); - - for (Tag t2 : iSwf.tags) { - if (t2 instanceof ExportAssetsTag) { - ExportAssetsTag sc = (ExportAssetsTag) t2; - Map m2 = sc.getTagToNameMap(); - for (int key : m2.keySet()) { - if (!exportedMap1.containsKey(key)) { - exportedMap1.put(key, m2.get(key)); - } - } - } - if (t2 instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) t2; - Map m2 = sc.getTagToNameMap(); - for (int key : m2.keySet()) { - if (!classesMap1.containsKey(key)) { - classesMap1.put(key, m2.get(key)); - } - } - } - } - Map exportedMap2 = new HashMap<>(); - for (int k : exportedMap1.keySet()) { - exportedMap2.put(exportedMap1.get(k), k); - } - - Map classesMap2 = new HashMap<>(); - for (int k : classesMap1.keySet()) { - classesMap2.put(classesMap1.get(k), k); - } - - Map importedMap1 = importTag.getAssets(); - Map importedMap2 = new HashMap<>(); - for (int k : importedMap1.keySet()) { - importedMap2.put(importedMap1.get(k), k); - } - - int pos = 0; - for (String key : importedMap2.keySet()) { - if (!exportedMap2.containsKey(key)) { - continue; //? - } - int exportedId = exportedMap2.get(key); - int importedId = importedMap2.get(key); - for (Tag cht : iSwf.tags) { - if ((cht instanceof CharacterIdTag) && (((CharacterIdTag) cht).getCharacterId() == exportedId) && !(cht instanceof PlaceObjectTypeTag) && !(cht instanceof RemoveTag)) { - CharacterIdTag ch = (CharacterIdTag) cht; - cht.setSwf(this); - ch.setCharacterId(importedId); - cht.setImported(true); - tags.add(p + 1 + pos, cht); - pos++; - } - } - } - - int newId = getNextCharacterId(); - pos = 0; - for (String key : classesMap2.keySet()) { - int exportedId = classesMap2.get(key); - int importedId = newId++; - for (Tag cht : iSwf.tags) { - if ((cht instanceof CharacterIdTag) && (((CharacterIdTag) cht).getCharacterId() == exportedId) && !(cht instanceof PlaceObjectTypeTag) && !(cht instanceof RemoveTag)) { - CharacterIdTag ch = (CharacterIdTag) cht; - cht.setSwf(this); - ch.setCharacterId(importedId); - cht.setImported(true); - tags.add(p + 1 + pos, cht); - pos++; - } - } - } - updateCharacters(); - } - } - } - } - - @Override - public SWF getSwf() { - return this; - } - - public SWF getRootSwf() { - SWF result = this; - while (result.binaryData != null) { - result = result.binaryData.getSwf(); - } - - return result; - } - - public String getFile() { - return file; - } - - /** - * Get title of the file - * - * @return file title - */ - public String getFileTitle() { - if (fileTitle != null) { - return fileTitle; - } - return file; - } - - public String getShortFileName() { - String title = getFileTitle(); - if (title == null) { - return ""; - } - return new File(title).getName(); - } - - public void setFile(String file) { - this.file = file; - fileTitle = null; - } - - public Date getFileModificationDate() { - try { - if (swfList != null && swfList.sourceInfo != null) { - String fileName = swfList.sourceInfo.getFile(); - if (fileName != null) { - long lastModified = new File(fileName).lastModified(); - if (lastModified > 0) { - return new Date(lastModified); - } - } - } - } catch (SecurityException sex) { - } - - return new Date(); - } - - private static void getAbcTags(Iterable list, List actionScripts) { - for (Tag t : list) { - if (t instanceof DefineSpriteTag) { - getAbcTags(((DefineSpriteTag) t).getTags(), actionScripts); - } - if (t instanceof ABCContainerTag) { - actionScripts.add((ABCContainerTag) t); - } - } - } - - public void assignExportNamesToSymbols() { - HashMap exportNames = new HashMap<>(); - for (Tag t : getTags()) { - if (t instanceof ExportAssetsTag) { - ExportAssetsTag eat = (ExportAssetsTag) t; - for (int i = 0; i < eat.tags.size(); i++) { - Integer tagId = eat.tags.get(i); - String name = eat.names.get(i); - if ((!exportNames.containsKey(tagId)) && (!exportNames.containsValue(name))) { - exportNames.put(tagId, name); - } - } - } - } - for (Tag t : getTags()) { - if (t instanceof CharacterTag) { - CharacterTag ct = (CharacterTag) t; - if (exportNames.containsKey(ct.getCharacterId())) { - ct.setExportName(exportNames.get(ct.getCharacterId())); - } - } - } - } - - public void assignClassesToSymbols() { - HashMap classes = new HashMap<>(); - for (Tag t : getTags()) { - if (t instanceof SymbolClassTag) { - SymbolClassTag sct = (SymbolClassTag) t; - for (int i = 0; i < sct.tags.size(); i++) { - if ((!classes.containsKey(sct.tags.get(i))) && (!classes.containsValue(sct.names.get(i)))) { - classes.put(sct.tags.get(i), sct.names.get(i)); - } - } - } - } - for (Tag t : getTags()) { - if (t instanceof CharacterTag) { - CharacterTag ct = (CharacterTag) t; - if (classes.containsKey(ct.getCharacterId())) { - ct.setClassName(classes.get(ct.getCharacterId())); - } - } - } - } - - /** - * Compress SWF file - * - * @param fis Input stream - * @param fos Output stream - * @param compression - * @return True on success - */ - public static boolean compress(InputStream fis, OutputStream fos, SWFCompression compression) { - try { - compress(fis, fos, compression, null); - } catch (IOException ex) { - return false; - } - return true; - } - - public static boolean decompress(InputStream fis, OutputStream fos) { - try { - decompress(fis, fos, false); - return true; - } catch (IOException ex) { - return false; - } - } - - private static void decodeLZMAStream(InputStream is, OutputStream os, byte[] lzmaProperties, long fileSize) throws IOException { - Decoder decoder = new Decoder(); - if (!decoder.SetDecoderProperties(lzmaProperties)) { - throw new IOException("LZMA:Incorrect stream properties"); - } - if (!decoder.Code(is, os, fileSize - 8)) { - throw new IOException("LZMA:Error in data stream"); - } - } - - private static SWFHeader decompress(InputStream is, OutputStream os, boolean allowUncompressed) throws IOException { - byte[] hdr = new byte[8]; - - // SWFheader: signature, version and fileSize - if (is.read(hdr) != 8) { - throw new SwfOpenException("SWF header is too short"); - } - - String signature = new String(hdr, 0, 3, Utf8Helper.charset); - if (!swfSignatures.contains(signature)) { - throw new SwfOpenException("Invalid SWF file, wrong signature."); - } - - int version = hdr[3]; - long fileSize; - try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) { - fileSize = sis.readUI32("fileSize"); - } - - SWFHeader header = new SWFHeader(); - header.version = version; - header.fileSize = fileSize; - header.gfx = hdr[1] == 'F' && hdr[2] == 'X'; - - try (SWFOutputStream sos = new SWFOutputStream(os, version)) { - sos.write(getHeaderBytes(SWFCompression.NONE, header.gfx)); - sos.writeUI8(version); - sos.writeUI32(fileSize); - - switch (hdr[0]) { - case 'C': { // CWS, CFX - Helper.copyStream(new InflaterInputStream(is), os, fileSize - 8); - header.compression = SWFCompression.ZLIB; - break; - } - case 'Z': { // ZWS - byte[] lzmaprop = new byte[9]; - is.read(lzmaprop); - try (SWFInputStream sis = new SWFInputStream(null, lzmaprop)) { - sis.readUI32("LZMAsize"); // compressed LZMA data size = compressed SWF - 17 byte, - // where 17 = 8 byte header + this 4 byte + 5 bytes decoder properties - - int propertiesSize = 5; - byte[] lzmaProperties = sis.readBytes(propertiesSize, "lzmaproperties"); - if (lzmaProperties.length != propertiesSize) { - throw new IOException("LZMA:input .lzma file is too short"); - } - - decodeLZMAStream(is, os, lzmaProperties, fileSize); - - header.compression = SWFCompression.LZMA; - header.lzmaProperties = lzmaProperties; - } - break; - } - case 'A': { // ABC - byte[] lzmaProperties = new byte[5]; - is.read(lzmaProperties); - byte[] uncompressedLength = new byte[8]; - is.read(uncompressedLength); - - decodeLZMAStream(is, os, lzmaProperties, fileSize); - - header.compression = SWFCompression.LZMA_ABC; - header.lzmaProperties = lzmaProperties; - break; - } - default: { // FWS, GFX - if (allowUncompressed) { - Helper.copyStream(is, os, fileSize - 8); - } else { - throw new IOException("SWF is not compressed"); - } - } - } - - return header; - } - } - - public static boolean renameInvalidIdentifiers(RenameType renameType, InputStream fis, OutputStream fos) { - try { - SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get()); - int cnt = swf.deobfuscateIdentifiers(renameType); - swf.assignClassesToSymbols(); - System.out.println(cnt + " identifiers renamed."); - swf.saveTo(fos); - } catch (InterruptedException | IOException ex) { - return false; - } - return true; - } - - public List getScriptPacksByClassNames(List classNames) throws Exception { - Set resultSet = new HashSet<>(); - - List abcList = getAbcList(); - List allAbcList = new ArrayList<>(); - for (int i = 0; i < abcList.size(); i++) { - allAbcList.add(abcList.get(i).getABC()); - } - - for (String className : classNames) { - for (int i = 0; i < abcList.size(); i++) { - ABC abc = abcList.get(i).getABC(); - List scrs = abc.findScriptPacksByPath(className, allAbcList); - for (int j = 0; j < scrs.size(); j++) { - ScriptPack scr = scrs.get(j); - resultSet.add(scr); - } - } - } - - return new ArrayList<>(resultSet); - } - - private List uniqueAS3Packs(List packs) { - List ret = new ArrayList<>(); - Set classPaths = new HashSet<>(); - for (ScriptPack item : packs) { - ClassPath key = item.getClassPath(); - if (classPaths.contains(key)) { - logger.log(Level.SEVERE, "Duplicate pack path found (" + key + ")!"); - } else { - classPaths.add(key); - ret.add(item); - } - } - return ret; - } - - public List getAS3Packs() { - List packs = new ArrayList<>(); - - List abcList = getAbcList(); - List allAbcList = new ArrayList<>(); - for (int i = 0; i < abcList.size(); i++) { - allAbcList.add(abcList.get(i).getABC()); - } - - for (ABCContainerTag abcTag : abcList) { - packs.addAll(abcTag.getABC().getScriptPacks(null, allAbcList)); - } - return uniqueAS3Packs(packs); - } - - @Override - public RECT getRect() { - return displayRect; - } - - @Override - public RECT getRect(Set added) { - return displayRect; - } - - public EventListener getExportEventListener() { - EventListener evl = new EventListener() { - @Override - public void handleExportingEvent(String type, int index, int count, Object data) { - for (EventListener listener : listeners) { - listener.handleExportingEvent(type, index, count, data); - } - } - - @Override - public void handleExportedEvent(String type, int index, int count, Object data) { - for (EventListener listener : listeners) { - listener.handleExportedEvent(type, index, count, data); - } - } - - @Override - public void handleEvent(String event, Object data) { - informListeners(event, data); - } - }; - - return evl; - } - - public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) throws IOException { - return exportActionScript(handler, outdir, null, exportSettings, parallel, evl, true, true); - } - - public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, List as3scripts, ScriptExportSettings exportSettings, boolean parallel, EventListener evl, boolean as2, boolean as3) throws IOException { - List ret = new ArrayList<>(); - - if (isAS3()) { - if (as3) { - ret.addAll(new AS3ScriptExporter().exportActionScript3(this, handler, outdir, as3scripts, exportSettings, parallel, evl)); - } - } else if (as2) { - ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, outdir, getASMs(true), exportSettings, parallel, evl)); - } - return ret; - } - - public Map getASMs(boolean exportFileNames) { - return getASMs(exportFileNames, new ArrayList<>(), true); - } - - public Map getASMs(boolean exportFileNames, List nodesToExport, boolean exportAll) { - Map asmsToExport = new HashMap<>(); - for (TreeItem treeItem : getFirstLevelASMNodes(null)) { - getASMs(exportFileNames, treeItem, nodesToExport, exportAll, asmsToExport, File.separator + getASMPath(exportFileNames, treeItem)); - } - - return asmsToExport; - } - - private void getASMs(boolean exportFileNames, TreeItem treeItem, List nodesToExport, boolean exportAll, Map asmsToExport, String path) { - boolean exportNode = nodesToExport.contains(treeItem); - TreeItem realItem = treeItem instanceof TagScript ? ((TagScript) treeItem).getTag() : treeItem; - if (realItem instanceof ASMSource && (exportAll || exportNode)) { - String npath = path; - String exPath = path; - int ppos = 1; - while (asmsToExport.containsKey(npath)) { - ppos++; - npath = path + (exportFileNames ? "[" + ppos + "]" : "_" + ppos); - exPath = path + "[" + ppos + "]"; - } - ((ASMSource) realItem).setScriptName(exPath); - asmsToExport.put(npath, (ASMSource) realItem); - } - - if (treeItem instanceof TagScript) { - TagScript tagScript = (TagScript) treeItem; - for (TreeItem subItem : tagScript.getFrames()) { - getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - } else if (treeItem instanceof FrameScript) { - FrameScript frameScript = (FrameScript) treeItem; - Frame parentFrame = frameScript.getFrame(); - for (TreeItem subItem : parentFrame.actionContainers) { - getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - for (TreeItem subItem : parentFrame.actions) { - getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - } else if (treeItem instanceof AS2Package) { - AS2Package as2Package = (AS2Package) treeItem; - for (TreeItem subItem : as2Package.subPackages.values()) { - getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - for (TreeItem subItem : as2Package.scripts.values()) { - getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); - } - } - } - - private String getASMPath(boolean exportFileName, TreeItem treeItem) { - if (!exportFileName) { - return treeItem.toString(); - } - - String result; - if (treeItem instanceof Exportable) { - result = ((Exportable) treeItem).getExportFileName(); - } else { - result = treeItem.toString(); - } - - return Helper.makeFileName(result); - } - - private TreeItem getASMWrapToTagScript(TreeItem treeItem) { - if (treeItem instanceof Tag) { - Tag resultTag = (Tag) treeItem; - List subNodes = new ArrayList<>(); - if (treeItem instanceof ASMSourceContainer) { - for (ASMSource item : ((ASMSourceContainer) treeItem).getSubItems()) { - subNodes.add(item); - } - } - - TagScript tagScript = new TagScript(treeItem.getSwf(), resultTag, subNodes); - return tagScript; - } - - return treeItem; - } - - public List getFirstLevelASMNodes(Map tagScriptCache) { - Timeline timeline = getTimeline(); - List subNodes = new ArrayList<>(); - List subFrames = new ArrayList<>(); - subNodes.addAll(timeline.getAS2RootPackage().subPackages.values()); - subNodes.addAll(timeline.getAS2RootPackage().scripts.values()); - - for (Tag tag : timeline.otherTags) { - boolean hasInnerFrames = false; - List tagSubNodes = new ArrayList<>(); - if (tag instanceof Timelined) { - Timeline timeline2 = ((Timelined) tag).getTimeline(); - for (Frame frame : timeline2.getFrames()) { - if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { - FrameScript frameScript = new FrameScript(this, frame); - tagSubNodes.add(frameScript); - hasInnerFrames = true; - } - } - } - - if (tag instanceof ASMSourceContainer) { - for (ASMSource asm : ((ASMSourceContainer) tag).getSubItems()) { - tagSubNodes.add(asm); - } - } - - if (!tagSubNodes.isEmpty()) { - TagScript ts = new TagScript(this, tag, tagSubNodes); - if (tagScriptCache != null) { - tagScriptCache.put(tag, ts); - } - if (hasInnerFrames) { - subFrames.add(ts); - } else { - subNodes.add(ts); - } - } - } - - subNodes.addAll(subFrames); - for (Frame frame : timeline.getFrames()) { - if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { - FrameScript frameScript = new FrameScript(this, frame); - subNodes.add(frameScript); - } - } - - return subNodes; - } - - private final HashSet listeners = new HashSet<>(); - - public final void addEventListener(EventListener listener) { - listeners.add(listener); - for (Tag t : getTags()) { - if (t instanceof ABCContainerTag) { - (((ABCContainerTag) t).getABC()).addEventListener(listener); - } - } - } - - public final void removeEventListener(EventListener listener) { - listeners.remove(listener); - for (Tag t : getTags()) { - if (t instanceof ABCContainerTag) { - (((ABCContainerTag) t).getABC()).removeEventListener(listener); - } - } - } - - protected void informListeners(String event, Object data) { - for (EventListener listener : listeners) { - listener.handleEvent(event, data); - } - } - - public static void populateVideoFrames(int streamId, Iterable tags, HashMap output) { - for (Tag t : tags) { - if (t instanceof VideoFrameTag) { - VideoFrameTag videoFrameTag = (VideoFrameTag) t; - if (videoFrameTag.streamID == streamId) { - output.put(videoFrameTag.frameNum, (VideoFrameTag) t); - } - } - if (t instanceof DefineSpriteTag) { - populateVideoFrames(streamId, ((DefineSpriteTag) t).getTags(), output); - } - } - } - - private static void writeLE(OutputStream os, long val, int size) throws IOException { - for (int i = 0; i < size; i++) { - os.write((int) (val & 0xff)); - val >>= 8; - } - } - - public static void createWavFromPcmData(OutputStream fos, int soundRateHz, boolean soundSize, boolean soundType, byte[] data) throws IOException { - ByteArrayOutputStream subChunk1Data = new ByteArrayOutputStream(); - int audioFormat = 1; // PCM - writeLE(subChunk1Data, audioFormat, 2); - int numChannels = soundType ? 2 : 1; - writeLE(subChunk1Data, numChannels, 2); - int[] rateMap = {5512, 11025, 22050, 44100}; - int sampleRate = soundRateHz; // rateMap[soundRate]; - writeLE(subChunk1Data, sampleRate, 4); - int bitsPerSample = soundSize ? 16 : 8; - int byteRate = sampleRate * numChannels * bitsPerSample / 8; - writeLE(subChunk1Data, byteRate, 4); - int blockAlign = numChannels * bitsPerSample / 8; - writeLE(subChunk1Data, blockAlign, 2); - writeLE(subChunk1Data, bitsPerSample, 2); - - ByteArrayOutputStream chunks = new ByteArrayOutputStream(); - chunks.write(Utf8Helper.getBytes("fmt ")); - byte[] subChunk1DataBytes = subChunk1Data.toByteArray(); - writeLE(chunks, subChunk1DataBytes.length, 4); - chunks.write(subChunk1DataBytes); - - chunks.write(Utf8Helper.getBytes("data")); - writeLE(chunks, data.length, 4); - chunks.write(data); - - fos.write(Utf8Helper.getBytes("RIFF")); - byte[] chunkBytes = chunks.toByteArray(); - writeLE(fos, 4 + chunkBytes.length, 4); - fos.write(Utf8Helper.getBytes("WAVE")); - fos.write(chunkBytes); - } - - public static String getTypePrefix(CharacterTag c) { - if (c instanceof ShapeTag) { - return "shape"; - } - if (c instanceof MorphShapeTag) { - return "morphshape"; - } - if (c instanceof DefineSpriteTag) { - return "sprite"; - } - if (c instanceof TextTag) { - return "text"; - } - if (c instanceof ButtonTag) { - return "button"; - } - if (c instanceof FontTag) { - return "font"; - } - if (c instanceof ImageTag) { - return "image"; - } - return "character"; - } - - public static void writeLibrary(SWF fswf, Set library, OutputStream fos) throws IOException { - for (int c : library) { - CharacterTag ch = fswf.getCharacter(c); - if (ch instanceof FontTag) { - StringBuilder sb = new StringBuilder(); - sb.append("function ").append(getTypePrefix(ch)).append(c).append("(ctx,ch,textColor){\r\n"); - ((FontTag) ch).toHtmlCanvas(sb, 1); - sb.append("}\r\n\r\n"); - fos.write(Utf8Helper.getBytes(sb.toString())); - } else { - if (ch instanceof ImageTag) { - ImageTag image = (ImageTag) ch; - ImageFormat format = image.getImageFormat(); - byte[] imageData = Helper.readStream(image.getImageData()); - String base64ImgData = Helper.byteArrayToBase64String(imageData); - fos.write(Utf8Helper.getBytes("var imageObj" + c + " = document.createElement(\"img\");\r\nimageObj" + c + ".src=\"data:image/" + format + ";base64," + base64ImgData + "\";\r\n")); - } - fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ctrans,frame,ratio,time){\r\n")); - if (ch instanceof DrawableTag) { - StringBuilder sb = new StringBuilder(); - ((DrawableTag) ch).toHtmlCanvas(sb, 1); - fos.write(Utf8Helper.getBytes(sb.toString())); - } - fos.write(Utf8Helper.getBytes("}\r\n\r\n")); - } - } - } - - private static void getVariables(ConstantPool constantPool, BaseLocalData localData, TranslateStack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { - boolean debugMode = false; - while ((ip > -1) && ip < code.size()) { - if (visited.contains(ip)) { - break; - } - GraphSourceItem ins = code.get(ip); - - if (debugMode) { - System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ActionList(), new HashSet<>(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); - } - if (ins.isExit()) { - break; - } - if (ins.isIgnored()) { - ip++; - continue; - } - - String usageType = "name"; - GraphTargetItem name = null; - if ((ins instanceof ActionGetVariable) - || (ins instanceof ActionGetMember) - || (ins instanceof ActionDefineLocal2) - || (ins instanceof ActionNewMethod) - || (ins instanceof ActionNewObject) - || (ins instanceof ActionCallMethod) - || (ins instanceof ActionCallFunction)) { - if (stack.isEmpty()) { - break; - } - name = stack.peek(); - } - - if ((ins instanceof ActionGetVariable) || (ins instanceof ActionDefineLocal2)) { - usageType = "variable"; - } - if (ins instanceof ActionGetMember) { - usageType = "member"; - } - if ((ins instanceof ActionNewMethod) || (ins instanceof ActionNewObject)) { - usageType = "class"; - } - if (ins instanceof ActionCallMethod) { - usageType = "function"; // can there be method? - } - if (ins instanceof ActionCallFunction) { - usageType = "function"; - } - - if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) { - functions.add(ins); - } - - if (ins instanceof GraphSourceItemContainer) { - GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; - List cntSizes = cnt.getContainerSizes(); - long addr = code.pos2adr(ip + 1); - ip = code.adr2pos(addr); - String cntName = cnt.getName(); - for (Long size : cntSizes) { - if (size == 0) { - continue; - } - ip = code.adr2pos(addr); - addr += size; - int nextip = code.adr2pos(addr); - getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(code.getActions().subList(ip, nextip), code.version, new HashMap<>(), new HashMap<>(), new HashMap<>()), 0, path + (cntName == null ? "" : "/" + cntName)); - ip = nextip; - } - List> r = new ArrayList<>(); - r.add(new ArrayList<>()); - r.add(new ArrayList<>()); - r.add(new ArrayList<>()); - ((GraphSourceItemContainer) ins).translateContainer(r, ins, stack, output, new HashMap<>(), new HashMap<>(), new HashMap<>()); - continue; - } - - if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) { - if (stack.size() < 2) { - break; - } - name = stack.get(stack.size() - 2); - } - - if ((ins instanceof ActionSetVariable) || (ins instanceof ActionDefineLocal)) { - usageType = "variable"; - } - - if (ins instanceof ActionSetMember) { - usageType = "member"; - } - - if (name instanceof DirectValueActionItem) { - variables.add(new MyEntry<>((DirectValueActionItem) name, constantPool)); - usageTypes.put((DirectValueActionItem) name, usageType); - } - - // for..in return - if (((ins instanceof ActionEquals) || (ins instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { - stack.push(new DirectValueActionItem(null, null, 0, Null.INSTANCE, new ArrayList<>())); - } - - if (ins instanceof ActionConstantPool) { - constantPool = new ConstantPool(((ActionConstantPool) ins).constantPool); - } - int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - - int requiredStackSize = ins.getStackPopCount(localData, stack); - if (stack.size() < requiredStackSize) { - // probably obfucated code, never executed branch - break; - } - - ins.translate(localData, stack, output, staticOperation, path); - if (ins.isExit()) { - break; - } - - if (ins instanceof ActionPush) { - if (!stack.isEmpty()) { - GraphTargetItem top = stack.peek(); - if (top instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) top; - if ((dvt.value instanceof String) || (dvt.value instanceof ConstantIndex)) { - if (constantPool == null) { - constantPool = new ConstantPool(dvt.constants); - } - strings.put(dvt, constantPool); - } - } - } - } - - if (ins.isBranch() || ins.isJump()) { - if (ins instanceof ActionIf) { - if (stack.isEmpty()) { - break; - } - stack.pop(); - } - visited.add(ip); - List branches = ins.getBranches(code); - for (int b : branches) { - TranslateStack brStack = (TranslateStack) stack.clone(); - if (b >= 0) { - getVariables(constantPool, localData, brStack, output, code, b, variables, functions, strings, visited, usageTypes, path); - } else if (debugMode) { - System.out.println("Negative branch:" + b); - } - } - // } - break; - } - ip++; - } - } - - private static void getVariables(List> variables, List functions, HashMap strings, HashMap usageTypes, ActionGraphSource code, int addr, String path) throws InterruptedException { - ActionLocalData localData = new ActionLocalData(); - getVariables(null, localData, new TranslateStack(path), new ArrayList<>(), code, code.adr2pos(addr), variables, functions, strings, new ArrayList<>(), usageTypes, path); - } - - private List> getVariables(List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes, ASMSource src, String path) throws InterruptedException { - List> ret = new ArrayList<>(); - ActionList actions = src.getActions(); - actionsMap.put(src, actions); - getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(actions, version, new HashMap<>(), new HashMap<>(), new HashMap<>()), 0, path); - return ret; - } - - private void getVariables(Iterable tags, String path, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { - List processed = new ArrayList<>(); - for (Tag t : tags) { - String subPath = path + "/" + t.toString(); - if (t instanceof ASMSource) { - addVariable((ASMSource) t, subPath, processed, variables, actionsMap, functions, strings, usageTypes); - } - if (t instanceof ASMSourceContainer) { - List processed2 = new ArrayList<>(); - for (ASMSource asm : ((ASMSourceContainer) t).getSubItems()) { - addVariable(asm, subPath + "/" + asm.toString(), processed2, variables, actionsMap, functions, strings, usageTypes); - } - } - if (t instanceof DefineSpriteTag) { - getVariables(((DefineSpriteTag) t).getTags(), path + "/" + t.toString(), variables, actionsMap, functions, strings, usageTypes); - } - } - } - - private void addVariable(ASMSource asm, String path, List processed, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { - int pos = 1; - String infPath2 = path; - while (processed.contains(infPath2)) { - pos++; - infPath2 = path + "[" + pos + "]"; - } - processed.add(infPath2); - informListeners("getVariables", infPath2); - getVariables(variables, actionsMap, functions, strings, usageTypes, asm, path); - } - - public boolean as3StringConstantExists(String str) { - for (ABCContainerTag abcTag : getAbcList()) { - ABC abc = abcTag.getABC(); - for (int i = 1; i < abc.constants.getStringCount(); i++) { - if (abc.constants.getString(i).equals(str)) { - return true; - } - } - } - - return false; - } - - public void fixAS3Code() { - for (ABCContainerTag abcTag : getAbcList()) { - ABC abc = abcTag.getABC(); - for (MethodBody body : abc.bodies) { - AVM2Code code = body.getCode(); - body.setCodeBytes(code.getBytes()); - } - - ((Tag) abcTag).setModified(true); - } - } - - public int deobfuscateAS3Identifiers(RenameType renameType) { - for (Tag tag : getTags()) { - if (tag instanceof ABCContainerTag) { - ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, true); - tag.setModified(true); - } - } - for (Tag tag : getTags()) { - if (tag instanceof ABCContainerTag) { - ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, false); - tag.setModified(true); - } - } - for (Tag tag : getTags()) { - if (tag instanceof SymbolClassTag) { - SymbolClassTag sc = (SymbolClassTag) tag; - for (int i = 0; i < sc.names.size(); i++) { - String newname = deobfuscation.deobfuscateNameWithPackage(true, sc.names.get(i), deobfuscated, renameType, deobfuscated); - if (newname != null) { - sc.names.set(i, newname); - } - } - sc.setModified(true); - } - } - deobfuscation.deobfuscateInstanceNames(true, deobfuscated, renameType, getTags(), new HashMap<>()); - return deobfuscated.size(); - } - - public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException { - FileAttributesTag fileAttributes = getFileAttributes(); - if (fileAttributes == null) { - int cnt = 0; - cnt += deobfuscateAS2Identifiers(renameType); - cnt += deobfuscateAS3Identifiers(renameType); - return cnt; - } else if (fileAttributes.actionScript3) { - return deobfuscateAS3Identifiers(renameType); - } else { - return deobfuscateAS2Identifiers(renameType); - } - } - - public void renameAS2Identifier(String identifier, String newname) throws InterruptedException { - Map selected = new HashMap<>(); - selected.put(DottedChain.parseWithSuffix(identifier), DottedChain.parseWithSuffix(newname)); - renameAS2Identifiers(null, selected); - } - - private int deobfuscateAS2Identifiers(RenameType renameType) throws InterruptedException { - return renameAS2Identifiers(renameType, null); - } - - private int renameAS2Identifiers(RenameType renameType, Map selected) throws InterruptedException { - HashMap actionsMap = new HashMap<>(); - List allFunctions = new ArrayList<>(); - List> allVariableNames = new ArrayList<>(); - HashMap allStrings = new HashMap<>(); - HashMap usageTypes = new HashMap<>(); - - int ret = 0; - getVariables(getTags(), "", allVariableNames, actionsMap, allFunctions, allStrings, usageTypes); - informListeners("rename", ""); - int fc = 0; - for (MyEntry it : allVariableNames) { - String name = it.getKey().toStringNoH(it.getValue()); - deobfuscation.allVariableNamesStr.add(name); - } - - informListeners("rename", "classes"); - int classCount = 0; - for (Tag t : getTags()) { - if (t instanceof DoInitActionTag) { - classCount++; - } - } - int cnt = 0; - for (Tag t : getTags()) { - if (t instanceof DoInitActionTag) { - cnt++; - informListeners("rename", "class " + cnt + "/" + classCount); - DoInitActionTag dia = (DoInitActionTag) t; - String exportName = getExportName(dia.spriteId); - exportName = exportName != null ? exportName : "_unk_"; - final String pkgPrefix = "__Packages."; - String[] classNameParts = null; - if (exportName.startsWith(pkgPrefix)) { - String className = exportName.substring(pkgPrefix.length()); - if (className.contains(".")) { - classNameParts = className.split("\\."); - } else { - classNameParts = new String[]{className}; - } - } - int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; - List dec; - try { - dec = Action.actionsToTree(dia.getActions(), version, staticOperation, ""/*FIXME*/); - } catch (EmptyStackException ex) { - continue; - } - GraphTargetItem name = null; - for (GraphTargetItem it : dec) { - if (it instanceof ClassActionItem) { - ClassActionItem cti = (ClassActionItem) it; - List methods = new ArrayList<>(); - methods.addAll(cti.functions); - methods.addAll(cti.staticFunctions); - - for (GraphTargetItem gti : methods) { - if (gti instanceof FunctionActionItem) { - FunctionActionItem fun = (FunctionActionItem) gti; - if (fun.calculatedFunctionName instanceof DirectValueActionItem) { - DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName; - String fname = dvf.toStringNoH(null); - String changed = deobfuscation.deobfuscateName(false, fname, false, "method", deobfuscated, renameType, selected); - if (changed != null) { - deobfuscated.put(DottedChain.parseWithSuffix(fname), DottedChain.parseWithSuffix(changed)); - } - } - } - } - - List vars = new ArrayList<>(); - for (MyEntry item : cti.vars) { - vars.add(item.getKey()); - } - for (MyEntry item : cti.staticVars) { - vars.add(item.getKey()); - } - for (GraphTargetItem gti : vars) { - if (gti instanceof DirectValueActionItem) { - DirectValueActionItem dvf = (DirectValueActionItem) gti; - String vname = dvf.toStringNoH(null); - String changed = deobfuscation.deobfuscateName(false, vname, false, "attribute", deobfuscated, renameType, selected); - if (changed != null) { - deobfuscated.put(DottedChain.parseWithSuffix(vname), DottedChain.parseWithSuffix(changed)); - } - } - } - - name = cti.className; - break; - } - if (it instanceof InterfaceActionItem) { - InterfaceActionItem ift = (InterfaceActionItem) it; - name = ift.name; - } - } - - if (name != null) { - int pos = 0; - while (name instanceof GetMemberActionItem) { - GetMemberActionItem mem = (GetMemberActionItem) name; - GraphTargetItem memberName = mem.memberName; - if (memberName instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) memberName; - String nameStr = dvt.toStringNoH(null); - if (classNameParts != null) { - if (classNameParts.length - 1 - pos < 0) { - break; - } - } - String changedNameStr = nameStr; - if (classNameParts != null) { - changedNameStr = classNameParts[classNameParts.length - 1 - pos]; - } - String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); - if (changedNameStr2 != null) { - changedNameStr = changedNameStr2; - } - ret++; - deobfuscated.put(DottedChain.parseWithSuffix(nameStr), DottedChain.parseWithSuffix(changedNameStr)); - pos++; - } - name = mem.object; - } - if (name instanceof GetVariableActionItem) { - GetVariableActionItem var = (GetVariableActionItem) name; - if (var.name instanceof DirectValueActionItem) { - DirectValueActionItem dvt = (DirectValueActionItem) var.name; - String nameStr = dvt.toStringNoH(null); - if (classNameParts != null) { - if (classNameParts.length - 1 - pos < 0) { - break; - } - } - String changedNameStr = nameStr; - if (classNameParts != null) { - changedNameStr = classNameParts[classNameParts.length - 1 - pos]; - } - String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); - if (changedNameStr2 != null) { - changedNameStr = changedNameStr2; - } - ret++; - deobfuscated.put(DottedChain.parseWithSuffix(nameStr), DottedChain.parseWithSuffix(changedNameStr)); - pos++; - } - } - } - t.setModified(true); - } - } - - for (GraphSourceItem fun : allFunctions) { - fc++; - informListeners("rename", "function " + fc + "/" + allFunctions.size()); - if (fun instanceof ActionDefineFunction) { - ActionDefineFunction f = (ActionDefineFunction) fun; - if (f.functionName.isEmpty()) { // anonymous function, leave as is - continue; - } - String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); - if (changed != null) { - f.replacedFunctionName = changed; - ret++; - } - } - if (fun instanceof ActionDefineFunction2) { - ActionDefineFunction2 f = (ActionDefineFunction2) fun; - if (f.functionName.isEmpty()) { // anonymous function, leave as is - continue; - } - String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); - if (changed != null) { - f.replacedFunctionName = changed; - ret++; - } - } - } - - HashSet stringsNoVarH = new HashSet<>(); - List allVariableNamesDv = new ArrayList<>(); - for (MyEntry it : allVariableNames) { - allVariableNamesDv.add(it.getKey()); - } - for (DirectValueActionItem ti : allStrings.keySet()) { - if (!allVariableNamesDv.contains(ti)) { - stringsNoVarH.add(System.identityHashCode(allStrings.get(ti)) + "_" + ti.toStringNoH(allStrings.get(ti))); - } - } - - int vc = 0; - for (MyEntry it : allVariableNames) { - vc++; - String name = it.getKey().toStringNoH(it.getValue()); - String changed = deobfuscation.deobfuscateName(false, name, false, usageTypes.get(it.getKey()), deobfuscated, renameType, selected); - if (changed != null) { - boolean addNew = false; - String h = System.identityHashCode(it.getKey()) + "_" + name; - if (stringsNoVarH.contains(h)) { - addNew = true; - } - ActionPush pu = (ActionPush) it.getKey().getSrc(); - if (pu.replacement == null) { - pu.replacement = new ArrayList<>(); - pu.replacement.addAll(pu.values); - } - if (pu.replacement.get(it.getKey().pos) instanceof ConstantIndex) { - ConstantIndex ci = (ConstantIndex) pu.replacement.get(it.getKey().pos); - ConstantPool pool = it.getValue(); - if (pool == null) { - continue; - } - if (pool.constants == null) { - continue; - } - if (addNew) { - pool.constants.add(changed); - ci.index = pool.constants.size() - 1; - } else { - pool.constants.set(ci.index, changed); - } - } else { - pu.replacement.set(it.getKey().pos, changed); - } - ret++; - } - } - - for (ASMSource src : actionsMap.keySet()) { - actionsMap.get(src).removeNops(); - src.setActions(actionsMap.get(src)); - src.setModified(); - } - - deobfuscation.deobfuscateInstanceNames(false, deobfuscated, renameType, getTags(), selected); - return ret; - } - - public IdentifiersDeobfuscation getDeobfuscation() { - return deobfuscation; - } - - public void exportFla(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException, InterruptedException { - XFLExportSettings settings = new XFLExportSettings(); - settings.compressed = true; - exportXfl(handler, outfile, swfName, generator, generatorVerName, generatorVersion, parallel, version, settings); - } - - public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException, InterruptedException { - XFLExportSettings settings = new XFLExportSettings(); - settings.compressed = false; - exportXfl(handler, outfile, swfName, generator, generatorVerName, generatorVersion, parallel, version, settings); - } - - public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version, XFLExportSettings settings) throws IOException, InterruptedException { - new XFLConverter().convertSWF(handler, this, swfName, outfile, settings, generator, generatorVerName, generatorVersion, parallel, version); - clearAllCache(); - } - - public static AffineTransform matrixToTransform(MATRIX mat) { - return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(), - mat.getRotateSkew1Float(), mat.getScaleYFloat(), - mat.translateX, mat.translateY); - } - - public SerializableImage getFromCache(String key) { - if (frameCache.contains(key)) { - return frameCache.get(key); - } - return null; - } - - public byte[] getFromCache(SoundTag soundTag) { - if (soundCache.contains(soundTag)) { - return soundCache.get(soundTag); - } - return null; - } - - public void putToCache(String key, SerializableImage img) { - if (Configuration.useFrameCache.get()) { - frameCache.put(key, img); - } - } - - public void putToCache(SoundTag soundTag, byte[] data) { - soundCache.put(soundTag, data); - } - - public void clearImageCache() { - jtt = null; - frameCache.clear(); - rectCache.clear(); - for (Tag tag : getTags()) { - if (tag instanceof ImageTag) { - ((ImageTag) tag).clearCache(); - } else if (tag instanceof DefineCompactedFont) { - ((DefineCompactedFont) tag).rebuildShapeCache(); - } - } - } - - public void clearSoundCache() { - soundCache.clear(); - } - - public void clearScriptCache() { - as2PcodeCache.clear(); - as2Cache.clear(); - as3Cache.clear(); - IdentifiersDeobfuscation.clearCache(); - } - - public void clearReadOnlyListCache() { - readOnlyTags = null; - for (Tag tag : tags) { - if (tag instanceof DefineSpriteTag) { - ((DefineSpriteTag) tag).clearReadOnlyListCache(); - } - } - } - - public static void clearAllStaticCache() { - Cache.clearAll(); - Helper.clearShapeCache(); - System.gc(); - } - - public void clearAbcListCache() { - abcList = null; - } - - public void clearAllCache() { - characters = null; - characterIdTags = null; - clearAbcListCache(); - timeline = null; - clearReadOnlyListCache(); - clearImageCache(); - clearScriptCache(); - clearAllStaticCache(); - } - - public static void uncache(ASMSource src) { - if (src != null) { - SWF swf = src.getSwf(); - swf.as2Cache.remove(src); - swf.as2PcodeCache.remove(src); - } - } - - public static void uncache(ScriptPack pack) { - if (pack != null) { - pack.getSwf().as3Cache.remove(pack); - } - } - - public static boolean isCached(ASMSource src) { - return src.getSwf().as2Cache.contains(src); - } - - public static boolean isCached(ScriptPack pack) { - return pack.getSwf().as3Cache.contains(pack); - } - - public static ActionList getCachedActionList(ASMSource src, final List listeners) throws InterruptedException { - synchronized (src) { - SWF swf = src.getSwf(); - int deobfuscationMode = Configuration.autoDeobfuscate.get() ? 1 : 0; - if (swf != null && swf.as2PcodeCache.contains(src)) { - ActionList result = swf.as2PcodeCache.get(src); - if (result.deobfuscationMode == deobfuscationMode) { - return result; - } - } - - try { - ByteArrayRange actionBytes = src.getActionBytes(); - int prevLength = actionBytes.getPos(); - SWFInputStream rri = new SWFInputStream(swf, actionBytes.getArray()); - if (prevLength != 0) { - rri.seek(prevLength); - } - - int version = swf == null ? SWF.DEFAULT_VERSION : swf.version; - ActionList list = ActionListReader.readActionListTimeout(listeners, rri, version, prevLength, prevLength + actionBytes.getLength(), src.toString()/*FIXME?*/, deobfuscationMode); - list.fileData = actionBytes.getArray(); - list.deobfuscationMode = deobfuscationMode; - if (swf != null) { - swf.as2PcodeCache.put(src, list); - } - - return list; - } catch (InterruptedException ex) { - throw ex; - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - return new ActionList(); - } - } - } - - public static CachedScript getFromCache(ASMSource src) { - SWF swf = src.getSwf(); - if (swf.as2Cache.contains(src)) { - return swf.as2Cache.get(src); - } - - return null; - } - - public static CachedScript getCached(ASMSource src, ActionList actions) throws InterruptedException { - SWF swf = src.getSwf(); - if (swf.as2Cache.contains(src)) { - return swf.as2Cache.get(src); - } - - if (actions == null) { - actions = src.getActions(); - } - - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - writer.startFunction("!script"); - Action.actionsToSource(src, actions, src.toString()/*FIXME?*/, writer); - writer.endFunction(); - List hilights = writer.instructionHilights; - List methodHilights = writer.methodHilights; - List classHilights = writer.classHilights; - List specialHilights = writer.specialHilights; - - String srcNoHex = writer.toString(); - CachedScript res = new CachedScript(srcNoHex, hilights, methodHilights, classHilights, specialHilights); - swf.as2Cache.put(src, res); - return res; - } - - public static CachedDecompilation getCached(ScriptPack pack) throws InterruptedException { - SWF swf = pack.getSwf(); - if (swf.as3Cache.contains(pack)) { - return swf.as3Cache.get(pack); - } - - int scriptIndex = pack.scriptIndex; - ScriptInfo script = null; - if (scriptIndex > -1) { - script = pack.abc.script_info.get(scriptIndex); - } - boolean parallel = Configuration.parallelSpeedUp.get(); - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - pack.toSource(writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel); - HighlightedText hilightedCode = new HighlightedText(writer); - CachedDecompilation res = new CachedDecompilation(hilightedCode); - swf.as3Cache.put(pack, res); - - return res; - } - - public Cache getRectCache() { - return rectCache; - } - - public Cache getShapeExportDataCache() { - return shapeExportDataCache; - } - - public static RECT fixRect(RECT rect) { - RECT ret = new RECT(); - ret.Xmin = rect.Xmin; - ret.Xmax = rect.Xmax; - ret.Ymin = rect.Ymin; - ret.Ymax = rect.Ymax; - - if (ret.Xmax <= 0) { - ret.Xmax = ret.getWidth(); - ret.Xmin = 0; - } - if (ret.Ymax <= 0) { - ret.Ymax = ret.getHeight(); - ret.Ymin = 0; - } - if (ret.Xmin < 0) { - ret.Xmax += (-ret.Xmin); - ret.Xmin = 0; - } - if (ret.Ymin < 0) { - ret.Ymax += (-ret.Ymin); - ret.Ymin = 0; - } - - if (ret.getWidth() < 1 || ret.getHeight() < 1) { - ret.Xmin = 0; - ret.Ymin = 0; - ret.Xmax = 20; - ret.Ymax = 20; - } - return ret; - } - - public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, Point cursorPosition, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor, double zoom) { - if (timeline.getFrameCount() == 0) { - return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB_PRE); - } - - RECT rect = displayRect; - SerializableImage image = new SerializableImage((int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1, - (int) (rect.getHeight() * zoom / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB_PRE); - if (backGroundColor == null) { - image.fillTransparent(); - } else { - Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics(); - g.setComposite(AlphaComposite.Src); - g.setColor(backGroundColor); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - } - - Matrix m = transformation.clone(); - m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); - m.scale(zoom); - RenderContext renderContext = new RenderContext(); - renderContext.cursorPosition = cursorPosition; - renderContext.mouseButton = mouseButton; - timeline.toImage(frame, time, renderContext, image, false, m, transformation, m, colorTransform); - - return image; - } - - private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) { - Map stage = new HashMap<>(); - Set dependingChars = new HashSet<>(); - Timelined timelined = timeline.timelined; - ReadOnlyTagList tags = timelined.getTags(); - if (toRemove instanceof CharacterTag) { - int characterId = ((CharacterTag) toRemove).getCharacterId(); - - if (characterId != 0) { - dependingChars.add(characterId); - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof CharacterIdTag) { - CharacterIdTag c = (CharacterIdTag) t; - Set needed = new HashSet<>(); - t.getNeededCharacters(needed); - if (needed.contains(characterId)) { - dependingChars.add(c.getCharacterId()); - } - } - } - } - } - - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof RemoveTag) { - RemoveTag rt = (RemoveTag) t; - int depth = rt.getDepth(); - if (stage.containsKey(depth)) { - int currentCharId = stage.get(depth); - stage.remove(depth); - if (dependingChars.contains(currentCharId)) { - timelined.removeTag(i); - i--; - continue; - } - } - } - if (t instanceof PlaceObjectTypeTag) { - PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; - int placeCharId = po.getCharacterId(); - int depth = po.getDepth(); - if (placeCharId != 0) { - stage.put(depth, placeCharId); - if (dependingChars.contains(placeCharId)) { - timelined.removeTag(i); - i--; - continue; - } - } - } - if (t instanceof CharacterIdTag) { - CharacterIdTag c = (CharacterIdTag) t; - if (dependingChars.contains(c.getCharacterId())) { - timelined.removeTag(i); - i--; - continue; - } - } - Set needed = new HashSet<>(); - t.getNeededCharacters(needed); - for (int dep : dependingChars) { - if (needed.contains(dep)) { - timelined.removeTag(i); - i--; - //continue; - } - } - if (t == toRemove) { - timelined.removeTag(i); - i--; - continue; - } - if (t instanceof Timelined) { - removeTagWithDependenciesFromTimeline(toRemove, ((Timelined) t).getTimeline()); - } - } - } - - private boolean removeTagFromTimeline(Tag toRemove, Timeline timeline) { - boolean modified = false; - int characterId = -1; - if (toRemove instanceof CharacterTag) { - characterId = ((CharacterTag) toRemove).getCharacterId(); - modified = timeline.removeCharacter(characterId); - } - Timelined timelined = timeline.timelined; - ReadOnlyTagList tags = timelined.getTags(); - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t == toRemove) { - timelined.removeTag(t); - i--; - continue; - } - - if (toRemove instanceof CharacterTag) { - if (t.removeCharacter(characterId)) { - modified = true; - i = -1; - continue; - } - } - - if (t instanceof DefineSpriteTag) { - DefineSpriteTag spr = (DefineSpriteTag) t; - boolean sprModified = removeTagFromTimeline(toRemove, spr.getTimeline()); - if (sprModified) { - spr.setModified(true); - } - modified |= sprModified; - } - } - return modified; - } - - public void removeTags(Collection tags, boolean removeDependencies) { - Set timelineds = new HashSet<>(); - for (Tag tag : tags) { - Timelined timelined = tag.getTimelined(); - timelineds.add(timelined); - removeTagInternal(timelined, tag, removeDependencies); - } - - for (Timelined timelined : timelineds) { - resetTimelines(timelined); - } - - updateCharacters(); - clearImageCache(); - } - - @Override - public void removeTag(int index) { - setModified(true); - tags.remove(index); - updateCharacters(); - } - - @Override - public void removeTag(Tag tag) { - setModified(true); - tags.remove(tag); - updateCharacters(); - } - - public void removeTag(Tag tag, boolean removeDependencies) { - Timelined timelined = tag.getTimelined(); - removeTagInternal(timelined, tag, removeDependencies); - resetTimelines(timelined); - updateCharacters(); - clearImageCache(); - } - - private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies) { - if (tag instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(tag.getId())) { - timelined.removeTag(tag); - timelined.setModified(true); - timelined.resetTimeline(); - } else // timeline should be always the swf here - { - if (removeDependencies) { - removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); - timelined.setModified(true); - } else { - boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); - if (modified) { - timelined.setModified(true); - } - } - } - } - - @Override - public ReadOnlyTagList getTags() { - if (readOnlyTags == null) { - readOnlyTags = new ReadOnlyTagList(tags); - } - - return readOnlyTags; - } - - public ReadOnlyTagList getLocalTags() { - List localTags = new ArrayList<>(); - for (Tag t : tags) { - if (!t.isImported()) { - localTags.add(t); - } - } - return new ReadOnlyTagList(localTags); - } - - /** - * Adds a tag to the SWF - * - * @param tag - */ - @Override - public void addTag(Tag tag) { - setModified(true); - tags.add(tag); - updateCharacters(); - } - - /** - * Adds a tag to the SWF - * - * @param index - * @param tag - */ - @Override - public void addTag(int index, Tag tag) { - setModified(true); - tags.add(index, tag); - updateCharacters(); - } - - /** - * Replaces a tag in the SWF - * - * @param oldTag - * @param newTag - */ - public void replaceTag(Tag oldTag, Tag newTag) { - setModified(true); - int index = tags.indexOf(oldTag); - if (index != -1) { - tags.set(index, newTag); - updateCharacters(); - } - } - - /** - * Adds a tag to the SWF If targetTreeItem is: - Frame: adds the tag to the - * Frame. Frame can be a frame of the main timeline or a DefineSprite frame - * - DefineSprite: adds the tag to the end of the DefineSprite's tag list - - * Any other tag in the SWF: adds the new tag exactly before the specified - * tag - Other: adds the tag to the end of the SWF's tag list - * - * @param tag - * @param targetTreeItem - */ - public void addTag(Tag tag, TreeItem targetTreeItem) { - SWF swf = tag.getSwf(); - Frame frame = targetTreeItem instanceof Frame ? (Frame) targetTreeItem : null; - Timelined timelined; - if (frame != null) { - timelined = frame.timeline.timelined; - } else { - timelined = swf.getTimelined(targetTreeItem); - } - - tag.setTimelined(timelined); - - ReadOnlyTagList tags = timelined.getTags(); - - int index; - if (frame != null) { - if (frame.showFrameTag != null) { - index = tags.indexOf(frame.showFrameTag); - } else { - index = -1; - } - } else if (timelined instanceof DefineSpriteTag) { - index = -1; - } else if (targetTreeItem instanceof Tag) { - if (tag instanceof CharacterIdTag && !(tag instanceof CharacterTag) && targetTreeItem instanceof CharacterTag) { - ((CharacterIdTag) tag).setCharacterId(((CharacterTag) targetTreeItem).getCharacterId()); - } - - index = tags.indexOf((Tag) targetTreeItem); // todo: honfika: why not index + 1? - } else { - index = -1; - if (tag instanceof CharacterTag) { - // add before the last ShowFrame tag - for (int i = tags.size() - 1; i >= 0; i--) { - if (tags.get(i) instanceof ShowFrameTag) { - index = i; - break; - } - } - } - } - - if (index > -1) { - timelined.addTag(index, tag); - } else { - timelined.addTag(tag); - } - - timelined.resetTimeline(); - - if (timelined instanceof DefineSpriteTag) { - DefineSpriteTag sprite = (DefineSpriteTag) timelined; - sprite.frameCount = timelined.getTimeline().getFrameCount(); - } - } - - public Timelined getTimelined(TreeItem treeItem) { - if (treeItem instanceof Frame) { - return ((Frame) treeItem).timeline.timelined; - } - - if (treeItem instanceof DefineSpriteTag) { - return (DefineSpriteTag) treeItem; - } - - return treeItem.getSwf(); - } - - public void packCharacterIds() { - int maxId = getNextCharacterId(); - int id = 1; - for (int i = 1; i < maxId; i++) { - CharacterTag charactertag = getCharacter(i); - if (charactertag != null) { - if (i != id) { - replaceCharacter(i, id); - } - id++; - } else { - // make sure that the id is not referenced in the tags - replaceCharacter(i, 0); - } - } - } - - public void sortCharacterIds() { - int maxId = Math.max(tags.size(), getNextCharacterId()); - int id = maxId; - // first set the chatacter ids to surely not used ids - for (Tag tag : getTags()) { - if (tag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) tag; - replaceCharacter(characterTag.getCharacterId(), id++); - } - } - // then set them to 1,2,3... - id = 1; - for (Tag tag : getTags()) { - if (tag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) tag; - replaceCharacter(characterTag.getCharacterId(), id++); - } - } - } - - public boolean replaceCharacter(int oldCharacterId, int newCharacterId) { - boolean modified = false; - for (Tag tag : getTags()) { - boolean modified2 = false; - if (tag instanceof CharacterIdTag) { - CharacterIdTag characterIdTag = (CharacterIdTag) tag; - if (characterIdTag.getCharacterId() == oldCharacterId) { - characterIdTag.setCharacterId(newCharacterId); - modified2 = true; - } - } - modified2 |= tag.replaceCharacter(oldCharacterId, newCharacterId); - if (modified2) { - tag.setModified(true); - } - modified |= modified2; - } - return modified; - } - - public void replaceCharacterTags(CharacterTag characterTag, int newCharacterId) { - int characterId = characterTag.getCharacterId(); - CharacterTag newCharacter = getCharacter(newCharacterId); - newCharacter.setCharacterId(characterId); - characterTag.setCharacterId(newCharacterId); - newCharacter.setModified(true); - characterTag.setModified(true); - - assignExportNamesToSymbols(); - assignClassesToSymbols(); - clearImageCache(); - updateCharacters(); - } - - @Override - public String toString() { - return getShortFileName(); - } - - public void deobfuscate(DeobfuscationLevel level) throws InterruptedException { - List atags = getAbcList(); - - for (ABCContainerTag tag : atags) { - if (level == DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE) { - tag.getABC().removeDeadCode(); - } else if (level == DeobfuscationLevel.LEVEL_REMOVE_TRAPS) { - tag.getABC().removeTraps(); - } else if (level == DeobfuscationLevel.LEVEL_RESTORE_CONTROL_FLOW) { - tag.getABC().removeTraps(); - } - - ((Tag) tag).setModified(true); - } - } - - /** - * Enables debugging. Adds tags to enable debugging and optinally injects - * debugline and debugfile instructions to AS3 code by decompiling it first - * - * @param injectAS3Code Modify AS3 code with debugfile / debugline ? - * @param decompileDir Directory to virtual decompile (will affect - * debugfile) - */ - public void enableDebugging(boolean injectAS3Code, File decompileDir) { - enableDebugging(injectAS3Code, decompileDir, false); - } - - /** - * Enables debugging. Adds tags to enable debugging. - */ - public void enableDebugging() { - enableDebugging(false, null, false); - } - - /** - * Enables debugging. Adds tags to enable debugging and injects debugline - * and debugfile instructions to AS3 code. Optionally enables Telemetry - * - * @param injectAS3Code Modify AS3 code with debugfile / debugline ? - * @param decompileDir Directory to virtual decompile (will affect - * debugfile) - * @param telemetry Enable telemetry info? - */ - public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry) { - enableDebugging(injectAS3Code, decompileDir, telemetry, false); - } - - /** - * Injects debugline and debugfile instructions to AS3 P-code (lines of - * P-code) - */ - public void injectAS3PcodeDebugInfo() { - List packs = getAS3Packs(); - for (ScriptPack s : packs) { - int abcIndex = s.allABCs.indexOf(s.abc); - if (s.isSimple) { - s.injectPCodeDebugInfo(abcIndex); - } - } - } - - /** - * Injects debugline and debugfile instructions to AS3 code - * - * @param decompileDir Directory to set file information paths - */ - public void injectAS3DebugInfo(File decompileDir) { - List packs = getAS3Packs(); - for (ScriptPack s : packs) { - if (s.isSimple) { - s.injectDebugInfo(decompileDir); - } - } - } - - /** - * Enables debugging. Adds tags to enable debugging and injects debugline - * and debugfile instructions to AS3 code. Optionally enables Telemetry - * - * @param injectAS3Code Modify AS3 code with debugfile / debugline ? - * @param decompileDir Directory to virtual decompile (will affect - * debugfile) - * @param telemetry Enable telemetry info? - * @param pcodeLevel inject Pcode lines instead of decompiled lines - */ - public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry, boolean pcodeLevel) { - - if (injectAS3Code) { - if (pcodeLevel) { - injectAS3PcodeDebugInfo(); - } else { - injectAS3DebugInfo(decompileDir); - } - } - - int pos = 0; - - boolean hasEnabled = false; - - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if (t instanceof MetadataTag) { - pos = i + 1; - } - if (t instanceof FileAttributesTag) { - pos = i + 1; - } - if (version >= 6 && (t instanceof EnableDebugger2Tag)) { - hasEnabled = true; - break; - } - if (version == 5 && (t instanceof EnableDebuggerTag)) { - hasEnabled = true; - break; - } - if (version < 5 && (t instanceof ProtectTag)) { - hasEnabled = true; - break; - } - } - - if (!hasEnabled) { - if (version >= 6) { - tags.add(pos, new EnableDebugger2Tag(this)); - } else if (version == 5) { - tags.add(pos, new EnableDebuggerTag(this)); - } else { - tags.add(pos, new ProtectTag(this)); - } - } - - getOrAddDebugId(); - } - - /** - * Finds DebugID tag - * - * @return the tag or null if not found - */ - public DebugIDTag getDebugId() { - for (Tag t : getTags()) { - if (t instanceof DebugIDTag) { - return (DebugIDTag) t; - } - } - return null; - } - - /** - * Finds DebugID tag and generates new one if none exists - * - * @return the tag or null if there is not debugging enabled in the swf file - */ - public DebugIDTag getOrAddDebugId() { - DebugIDTag r = getDebugId(); - if (r == null) { - for (int i = 0; i < tags.size(); i++) { - Tag t = tags.get(i); - if ((t instanceof EnableDebuggerTag) || (t instanceof EnableDebugger2Tag)) { - r = new DebugIDTag(this); - tags.add(i + 1, r); - new Random().nextBytes(r.debugId); - break; - } - } - } - return r; - } - - public boolean generatePCodeSwdFile(File file, Map> breakpoints) throws IOException { - DebugIDTag dit = getDebugId(); - if (dit == null) { - return false; - } - List items = new ArrayList<>(); - Map asms = getASMs(true); - - try { - items.add(new SWD.DebugId(dit.debugId)); - - } catch (Throwable t) { - logger.log(Level.SEVERE, "message", t); - return false; - } - - int moduleId = 0; - List names = new ArrayList<>(asms.keySet()); - Collections.sort(names); - for (String name : names) { - moduleId++; - String sname = "#PCODE " + name; - int bitmap = SWD.bitmapAction; - items.add(new SWD.DebugScript(moduleId, bitmap, sname, "")); - - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - try { - asms.get(name).getASMSource(ScriptExportMode.PCODE, writer, asms.get(name).getActions()); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - List hls = writer.instructionHilights; - - Map offsetToLine = new TreeMap<>(); - String txt = writer.toString(); - txt = txt.replace("\r", ""); - int line = 1; - for (int i = 0; i < txt.length(); i++) { - Highlighting h = Highlighting.searchPos(hls, i); - if (h != null) { - int of = (int) h.getProperties().fileOffset; - if (of > -1 && !offsetToLine.containsKey(of) && !offsetToLine.containsValue(line)) { - offsetToLine.put(of, line); - } - } - if (txt.charAt(i) == '\n') { - line++; - } - } - - for (int ofs : offsetToLine.keySet()) { - items.add(new SWD.DebugOffset(moduleId, offsetToLine.get(ofs), ofs)); - } - - if (breakpoints.containsKey(sname)) { - Set bplines = breakpoints.get(sname); - for (int bpline : bplines) { - if (offsetToLine.containsValue(bpline)) { - try { - SWD.DebugBreakpoint dbp = new SWD.DebugBreakpoint(moduleId, bpline); - items.add(dbp); - } catch (IllegalArgumentException iex) { - logger.log(Level.WARNING, "Cannot generate breakpoint to SWD: {0}", iex.getMessage()); - } - } - } - } - } - - SWD swd = new SWD(7, items); - try (FileOutputStream fis = new FileOutputStream(file)) { - swd.saveTo(fis); - } - return true; - } - - public boolean generateSwdFile(File file, Map> breakpoints) throws IOException { - DebugIDTag dit = getDebugId(); - if (dit == null) { - return false; - } - List items = new ArrayList<>(); - Map asms = getASMs(true); - - try { - items.add(new SWD.DebugId(dit.debugId)); - - int moduleId = 0; - List names = new ArrayList<>(asms.keySet()); - Collections.sort(names); - for (String name : names) { - List regitems = new ArrayList<>(); - moduleId++; - CachedScript cs; - try { - cs = SWF.getCached(asms.get(name), asms.get(name).getActions()); - } catch (InterruptedException ex) { - return false; - } - String txt = cs.text.replace("\r", ""); - int line = 1; - Map lineToOffset = new HashMap<>(); - Map regNames = new HashMap<>(); - - for (int pos = 0; pos < txt.length(); pos++) { - Highlighting h = Highlighting.searchPos(cs.hilights, pos); - if (h != null) { - - int firstLineOffset = (int) h.getProperties().firstLineOffset; - if (firstLineOffset > -1 && h.getProperties().declaration && h.getProperties().regIndex > -1 && (!regNames.containsKey(h.getProperties().regIndex) || !regNames.get(h.getProperties().regIndex).equals(h.getProperties().localName))) { - regNames.put(h.getProperties().regIndex, h.getProperties().localName); - - List curRegIndexes = new ArrayList<>(regNames.keySet()); - List curRegNames = new ArrayList<>(); - for (int i = 0; i < curRegIndexes.size(); i++) { - curRegNames.add(regNames.get(curRegIndexes.get(i))); - } - regitems.add(new SWD.DebugRegisters((int) h.getProperties().firstLineOffset, curRegIndexes, curRegNames)); - } - - if (firstLineOffset != -1 && !lineToOffset.containsKey(line)) { - lineToOffset.put(line, firstLineOffset); - } - } - if (txt.charAt(pos) == '\n') { - line++; - } - } - - Map offSetToLine = new TreeMap<>(); - for (Map.Entry en : lineToOffset.entrySet()) { - offSetToLine.put(en.getValue(), en.getKey()); - } - - //final String NONAME = "[No instance name assigned]"; - String sname = name; - int bitmap = SWD.bitmapAction; - /* Matcher m; - int bitmap = SWD.bitmapAction; - m = Pattern.compile("^\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname); - if (m.matches()) { - //TODO: scenes?, layers? - sname = "Actions for Scene 1: Frame " + m.group(1) + " of Layer Name Layer 1"; - } else if ((m = Pattern.compile("^\\\\__Packages\\\\(.*)$").matcher(sname)).matches()) { - sname = m.group(1).replace("\\", ".") + ": .\\" + m.group(1) + ".as"; - } else { - continue; //FIXME! - } - m = Pattern.compile("^\\\\DefineSprite_([0-9])+\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname); - if (m.matches()) { - //TODO: layers? - //sname = "Actions for Symbol " + m.group(1) + ": Frame " + m.group(2) + " of Layer Name Layer 1"; - continue; //FIXME! - } - - //TODO: handle onxxx together ? - m = Pattern.compile("^\\\\DefineButton2?_([0-9]+)\\\\on\\(.*$").matcher(sname); - if (m.matches()) { - //bitmap = SWD.bitmapOnAction; - //sname = "Actions for " + NONAME + " (Symbol " + m.group(1) + ")"; - continue; //FIXME! - } - - //TODO: handle onClipEvent together ? - m = Pattern.compile("^\\\\frame_([0-9]+)\\\\PlaceObject[2-3]?_([0-9]+)_[^\\\\]*\\\\onClipEvent\\(.*$").matcher(sname); - if (m.matches()) { - //bitmap = SWD.bitmapOnClipAction; - //sname = "Actions for " + NONAME + " (Symbol " + m.group(2) + ")"; - continue; //FIXME! - }//*/ - - items.add(new SWD.DebugScript(moduleId, bitmap, sname, txt)); - for (int ofs : offSetToLine.keySet()) { - items.add(new SWD.DebugOffset(moduleId, offSetToLine.get(ofs), ofs)); - } - if (breakpoints.containsKey(name)) { - Set bplines = breakpoints.get(name); - for (int bpline : bplines) { - if (lineToOffset.containsKey(bpline)) { - try { - SWD.DebugBreakpoint dbp = new SWD.DebugBreakpoint(moduleId, bpline); - items.add(dbp); - } catch (IllegalArgumentException iex) { - logger.log(Level.WARNING, "Cannot generate breakpoint to SWD: {0}", iex.getMessage()); - } - } - } - } - items.addAll(regitems); - //moduleId++; - } - //items.addAll(swdOffsets); - //items.addAll(swfBps); - - } catch (Throwable t) { - logger.log(Level.SEVERE, "message", t); - return false; - } - SWD swd = new SWD(7, items); - try (FileOutputStream fis = new FileOutputStream(file)) { - swd.saveTo(fis); - } - return true; - } - - public boolean enableTelemetry(String password) { - - EnableTelemetryTag et = getEnableTelemetry(); - - if (et == null) { - FileAttributesTag fat = getFileAttributes(); - if (fat == null) { - return false; - } - int insertTo = tags.indexOf(fat) + 1; - MetadataTag mt = getMetadata(); - if (mt != null) { - insertTo = tags.indexOf(mt) + 1; - } - - et = new EnableTelemetryTag(this); - tags.add(insertTo, et); - } - et.setPassword(password); - //TODO: SWFs with tag 92 (signed) are unsupported - return true; - } - - public String getFlexMainClass(List ignoredClasses, List ignoredNs) { - String documentClass = getDocumentClass(); - - ScriptPack documentPack = null; - for (ScriptPack item : getAS3Packs()) { - if (item.getClassPath().toString().equals(documentClass)) { - documentPack = item; - break; - } - } - - if (documentPack != null) { - if (!documentPack.traitIndices.isEmpty()) { - Trait firstTrait = documentPack.abc.script_info.get(documentPack.scriptIndex).traits.traits.get(documentPack.traitIndices.get(0)); - if (firstTrait instanceof TraitClass) { - int cindex = ((TraitClass) firstTrait).class_info; - Multiname superName = documentPack.abc.constants.getMultiname(documentPack.abc.instance_info.get(cindex).super_index); - String parentClass = superName.getNameWithNamespace(documentPack.abc.constants, true).toRawString(); - if ("mx.managers.SystemManager".equals(parentClass)) { - for (Trait t : documentPack.abc.instance_info.get(cindex).instance_traits.traits) { - if ((t instanceof TraitMethodGetterSetter) && "info".equals(t.getName(documentPack.abc).getName(documentPack.abc.constants, new ArrayList<>(), true, true))) { - - int mi = ((TraitMethodGetterSetter) t).method_info; - try { - documentPack.abc.findBody(mi).convert(new ConvertData(), "??", ScriptExportMode.AS, true, mi, documentPack.scriptIndex, cindex, documentPack.abc, t, new ScopeStack(), 0, new NulWriter(), new ArrayList<>(), new ArrayList<>(), true); - List infos = documentPack.abc.findBody(mi).convertedItems; - if (!infos.isEmpty()) { - if (infos.get(0) instanceof IfItem) { - IfItem ift = ((IfItem) infos.get(0)); - if (!ift.onTrue.isEmpty()) { - if (ift.onTrue.get(0) instanceof InitPropertyAVM2Item) { - if (ift.onTrue.get(0).value instanceof NewObjectAVM2Item) { - NewObjectAVM2Item no = (NewObjectAVM2Item) ift.onTrue.get(0).value; - List compiledLocales = new ArrayList<>(); - List compiledResourceBundleNames = new ArrayList<>(); - List mixins = new ArrayList<>(); - String mainClassName = null; - //currentDomain,preloader - /*double width = 0; - double height = 0; - */ - for (NameValuePair nvp : no.pairs) { - if (nvp.name instanceof StringAVM2Item) { - String n = ((StringAVM2Item) nvp.name).getValue(); - switch (n) { - case "compiledLocales": - if (nvp.value instanceof NewArrayAVM2Item) { - NewArrayAVM2Item na = (NewArrayAVM2Item) nvp.value; - for (GraphTargetItem tv : na.values) { - compiledLocales.add("" + tv.getResult()); - } - } - break; - case "compiledResourceBundleNames": - if (nvp.value instanceof NewArrayAVM2Item) { - NewArrayAVM2Item na = (NewArrayAVM2Item) nvp.value; - for (GraphTargetItem tv : na.values) { - compiledResourceBundleNames.add("" + tv.getResult()); - } - } - break; - case "mixins": - if (nvp.value instanceof NewArrayAVM2Item) { - NewArrayAVM2Item na = (NewArrayAVM2Item) nvp.value; - for (GraphTargetItem tv : na.values) { - mixins.add("" + tv.getResult()); - } - } - break; - /*case "width": - width = Double.parseDouble("" + nvp.value.getResult()); - break; - case "height": - height = Double.parseDouble("" + nvp.value.getResult()); - break;*/ - case "mainClassName": - mainClassName = "" + nvp.value.getResult(); - break; - } - } - } - - ignoredClasses.add(documentClass); - for (String loc : compiledLocales) { - ignoredClasses.add(loc + "$" + "controls" + "_properties"); - for (String res : compiledResourceBundleNames) { - ignoredClasses.add(loc + "$" + res + "_properties"); - } - } - ignoredClasses.addAll(mixins); - - //find internal classes used in mixins - for (ScriptPack p : getAS3Packs()) { - for (String m : mixins) { - if (m.equals(p.getClassPath().toRawString())) { - for (int ti : p.traitIndices) { - Trait tr = p.abc.script_info.get(p.scriptIndex).traits.traits.get(ti); - if (tr instanceof TraitClass) { - int ci = ((TraitClass) tr).class_info; - int cinit = p.abc.class_info.get(ci).cinit_index; - p.abc.findBody(cinit).convert(new ConvertData(), "??", ScriptExportMode.AS, true, cinit, p.scriptIndex, cindex, p.abc, t, new ScopeStack(), 0, new NulWriter(), new ArrayList<>(), new ArrayList<>(), true); - List cinitBody = p.abc.findBody(cinit).convertedItems; - for (GraphTargetItem cit : cinitBody) { - if (cit instanceof SetPropertyAVM2Item) { - if (cit.value instanceof GetLexAVM2Item) { - GetLexAVM2Item gl = (GetLexAVM2Item) cit.value; - ignoredClasses.add(gl.propertyName.getNameWithNamespace(p.abc.constants, true).toRawString()); - } - } - } - } - } - } - } - } - ignoredNs.add("mx"); - ignoredNs.add("spark"); - ignoredNs.add("flashx"); - return mainClassName; - } - } - } - } - } - } catch (InterruptedException e) { - //ignore - } - } - } - } - } - } - } - return null; - } - - @Override - public void replaceTag(int index, Tag newTag) { - removeTag(index); - addTag(index, newTag); - } -} +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash; + +import SevenZip.Compression.LZMA.Decoder; +import SevenZip.Compression.LZMA.Encoder; +import com.jpexs.debugger.flash.SWD; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.CachedDecompilation; +import com.jpexs.decompiler.flash.abc.ClassPath; +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AbcMultiNameCollisionFixer; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; +import com.jpexs.decompiler.flash.abc.avm2.model.GetLexAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.InitPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NameValuePair; +import com.jpexs.decompiler.flash.abc.avm2.model.NewArrayAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.NewObjectAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.SetPropertyAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.StringAVM2Item; +import com.jpexs.decompiler.flash.abc.types.ConvertData; +import com.jpexs.decompiler.flash.abc.types.MethodBody; +import com.jpexs.decompiler.flash.abc.types.Multiname; +import com.jpexs.decompiler.flash.abc.types.ScriptInfo; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; +import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; +import com.jpexs.decompiler.flash.action.Action; +import com.jpexs.decompiler.flash.action.ActionGraphSource; +import com.jpexs.decompiler.flash.action.ActionList; +import com.jpexs.decompiler.flash.action.ActionListReader; +import com.jpexs.decompiler.flash.action.ActionLocalData; +import com.jpexs.decompiler.flash.action.CachedScript; +import com.jpexs.decompiler.flash.action.model.ConstantPool; +import com.jpexs.decompiler.flash.action.model.DirectValueActionItem; +import com.jpexs.decompiler.flash.action.model.FunctionActionItem; +import com.jpexs.decompiler.flash.action.model.GetMemberActionItem; +import com.jpexs.decompiler.flash.action.model.GetVariableActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem; +import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem; +import com.jpexs.decompiler.flash.action.swf4.ActionEquals; +import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; +import com.jpexs.decompiler.flash.action.swf4.ActionIf; +import com.jpexs.decompiler.flash.action.swf4.ActionPush; +import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; +import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; +import com.jpexs.decompiler.flash.action.swf5.ActionCallFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionCallMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionConstantPool; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineFunction; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal; +import com.jpexs.decompiler.flash.action.swf5.ActionDefineLocal2; +import com.jpexs.decompiler.flash.action.swf5.ActionEquals2; +import com.jpexs.decompiler.flash.action.swf5.ActionGetMember; +import com.jpexs.decompiler.flash.action.swf5.ActionNewMethod; +import com.jpexs.decompiler.flash.action.swf5.ActionNewObject; +import com.jpexs.decompiler.flash.action.swf5.ActionSetMember; +import com.jpexs.decompiler.flash.action.swf7.ActionDefineFunction2; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; +import com.jpexs.decompiler.flash.ecma.Null; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; +import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; +import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +import com.jpexs.decompiler.flash.exporters.shape.ShapeExportData; +import com.jpexs.decompiler.flash.helpers.HighlightedText; +import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; +import com.jpexs.decompiler.flash.helpers.NulWriter; +import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; +import com.jpexs.decompiler.flash.helpers.collections.MyEntry; +import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DebugIDTag; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag; +import com.jpexs.decompiler.flash.tags.EnableDebuggerTag; +import com.jpexs.decompiler.flash.tags.EnableTelemetryTag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.ProtectTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.TagStub; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.Exportable; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.ImportTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.RenderContext; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.enums.ImageFormat; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.AS2Package; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.FrameScript; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.annotations.Internal; +import com.jpexs.decompiler.flash.types.annotations.SWFField; +import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.decompiler.flash.xfl.XFLConverter; +import com.jpexs.decompiler.flash.xfl.XFLExportSettings; +import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphSourceItem; +import com.jpexs.decompiler.graph.GraphSourceItemContainer; +import com.jpexs.decompiler.graph.GraphTargetItem; +import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.TranslateStack; +import com.jpexs.decompiler.graph.model.IfItem; +import com.jpexs.decompiler.graph.model.LocalData; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Cache; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.NulStream; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EmptyStackException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Class representing SWF file + * + * @author JPEXS + */ +public final class SWF implements SWFContainerItem, Timelined { + + // big object for testing cleanup + //BigObject bigObj = new BigObject(); + /** + * Default version of SWF file format + */ + public static final int DEFAULT_VERSION = 10; + + /** + * Maximum SWF file format version Needs to be fixed when SWF versions + * reaches this value + */ + public static final int MAX_VERSION = 32; + + /** + * Tags inside of file + */ + @SWFField + private List tags = new ArrayList<>(); + + @Internal + public ReadOnlyTagList readOnlyTags; + + public boolean hasEndTag = true; + + /** + * ExportRectangle for the display + */ + public RECT displayRect; + + /** + * Movie frame rate + */ + public float frameRate; + + /** + * Number of frames in movie + */ + public int frameCount; + + /** + * Version of SWF + */ + public int version; + + /** + * Uncompressed size of the file + */ + @Internal + public long fileSize; + + /** + * Used compression mode + */ + public SWFCompression compression = SWFCompression.NONE; + + /** + * Compressed size of the file (LZMA) + */ + @Internal + public long compressedSize; + + /** + * LZMA Properties + */ + public byte[] lzmaProperties; + + @Internal + public byte[] uncompressedData; + + @Internal + public byte[] originalUncompressedData; + + /** + * ScaleForm GFx + */ + public boolean gfx = false; + + @Internal + public SWFList swfList; + + @Internal + private String file; + + @Internal + private String fileTitle; + + @Internal + private volatile Map characters; + + @Internal + private volatile Map> characterIdTags; + + @Internal + private volatile Map> dependentCharacters; + + @Internal + private volatile List abcList; + + @Internal + private volatile JPEGTablesTag jtt; + + @Internal + public Map sourceFontNamesMap = new HashMap<>(); + + public static final double unitDivisor = 20; + + private static final Logger logger = Logger.getLogger(SWF.class.getName()); + + @Internal + private boolean isModified; + + @Internal + private Timeline timeline; + + @Internal + public DumpInfoSwfNode dumpInfo; + + @Internal + public DefineBinaryDataTag binaryData; + + @Internal + private final HashMap deobfuscated = new HashMap<>(); + + @Internal + private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation(); + + @Internal + private final Cache frameCache = Cache.getInstance(false, false, "frame"); + + @Internal + private final Cache rectCache = Cache.getInstance(true, true, "rect"); + + @Internal + private final Cache shapeExportDataCache = Cache.getInstance(true, true, "shapeExportData"); + + @Internal + private final Cache soundCache = Cache.getInstance(false, false, "sound"); + + @Internal + private final Cache as2PcodeCache = Cache.getInstance(true, true, "as2pcode"); + + @Internal + private final Cache as2Cache = Cache.getInstance(true, false, "as2"); + + @Internal + private final Cache as3Cache = Cache.getInstance(true, false, "as3"); + + public static List swfSignatures = Arrays.asList( + "FWS", // Uncompressed Flash + "CWS", // ZLib compressed Flash + "ZWS", // LZMA compressed Flash + "GFX", // Uncompressed ScaleForm GFx + "CFX", // Compressed ScaleForm GFx + "ABC" // Non-standard LZMA compressed Flash + ); + + public void updateCharacters() { + characters = null; + characterIdTags = null; + } + + public void clearTagSwfs() { + resetTimelines(this); + updateCharacters(); + + for (Tag tag : getTags()) { + if (tag instanceof DefineSpriteTag) { + DefineSpriteTag spriteTag = (DefineSpriteTag) tag; + for (Tag tag1 : spriteTag.getTags()) { + tag1.setSwf(null); + } + + for (int i = spriteTag.getTags().size() - 1; i >= 0; i--) { + spriteTag.removeTag(i); + } + } + + if (tag instanceof DefineBinaryDataTag) { + DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) tag; + if (binaryTag.innerSwf != null) { + binaryTag.innerSwf.clearTagSwfs(); + } + } + + tag.setSwf(null); + } + + tags.clear(); + if (abcList != null) { + abcList.clear(); + } + + if (swfList != null) { + swfList.swfs.clear(); + } + + as2PcodeCache.clear(); + as2Cache.clear(); + as3Cache.clear(); + frameCache.clear(); + soundCache.clear(); + + timeline = null; + clearDumpInfo(dumpInfo); + dumpInfo = null; + jtt = null; + binaryData = null; + } + + private void clearDumpInfo(DumpInfo di) { + for (DumpInfo childInfo : di.getChildInfos()) { + clearDumpInfo(childInfo); + } + + di.getChildInfos().clear(); + } + + public Map getCharacters() { + if (characters == null) { + synchronized (this) { + if (characters == null) { + Map chars = new HashMap<>(); + Map> charIdtags = new HashMap<>(); + parseCharacters(getTags(), chars, charIdtags); + characters = Collections.unmodifiableMap(chars); + characterIdTags = Collections.unmodifiableMap(charIdtags); + } + } + } + + return characters; + } + + public List getCharacterIdTags(int characterId) { + if (characterIdTags == null) { + getCharacters(); + } + + return characterIdTags.get(characterId); + } + + public CharacterIdTag getCharacterIdTag(int characterId, int tagId) { + List characterIdTags = getCharacterIdTags(characterId); + if (characterIdTags != null) { + for (CharacterIdTag t : characterIdTags) { + if (((Tag) t).getId() == tagId) { + if (t.getCharacterId() == characterId) { + return t; + } + } + } + } + + return null; + } + + public Map> getDependentCharacters() { + if (dependentCharacters == null) { + synchronized (this) { + if (dependentCharacters == null) { + Map> dep = new HashMap<>(); + for (Tag tag : getTags()) { + if (tag instanceof CharacterTag) { + int characterId = ((CharacterTag) tag).getCharacterId(); + Set needed = new HashSet<>(); + tag.getNeededCharacters(needed); + for (Integer needed1 : needed) { + Set s = dep.get(needed1); + if (s == null) { + s = new HashSet<>(); + dep.put(needed1, s); + } + + s.add(characterId); + } + } + } + + dependentCharacters = dep; + } + } + } + + return dependentCharacters; + } + + public Set getDependentCharacters(int characterId) { + Set visited = new HashSet<>(); + + Set dependents2 = new LinkedHashSet<>(); + Set deps = getDependentCharacters().get(characterId); + if (deps != null) { + dependents2.addAll(deps); + } + + while (visited.size() != dependents2.size()) { + for (int chId : dependents2) { + if (!visited.contains(chId)) { + visited.add(chId); + if (getCharacters().containsKey(chId)) { + deps = getDependentCharacters().get(chId); + if (deps != null) { + dependents2.addAll(deps); + } + + break; + } + } + } + } + + Set dependents = new LinkedHashSet<>(); + for (Integer chId : dependents2) { + if (getCharacters().containsKey(chId)) { + dependents.add(chId); + } + } + + return dependents; + } + + public CharacterTag getCharacter(int characterId) { + return getCharacters().get(characterId); + } + + public String getExportName(int characterId) { + CharacterTag characterTag = getCharacters().get(characterId); + String exportName = characterTag != null ? characterTag.getExportName() : null; + return exportName; + } + + public FontTag getFontByClass(String fontClass) { + if (fontClass == null) { + return null; + } + for (Tag t : getTags()) { + if (t instanceof FontTag) { + if (fontClass.equals(((FontTag) t).getClassName())) { + return (FontTag) t; + } + } + } + return null; + } + + public FontTag getFontByName(String fontName) { + if (fontName == null) { + return null; + } + for (Tag t : getTags()) { + if (t instanceof FontTag) { + if (fontName.equals(((FontTag) t).getFontName())) { + return (FontTag) t; + } + } + } + return null; + } + + public FontTag getFont(int fontId) { + CharacterTag characterTag = getCharacters().get(fontId); + if (characterTag instanceof FontTag) { + return (FontTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be a FontTag. characterId: {0}", fontId); + } + + return null; + } + + public ImageTag getImage(int imageId) { + CharacterTag characterTag = getCharacters().get(imageId); + if (characterTag instanceof ImageTag) { + return (ImageTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be an ImageTag. characterId: {0}", imageId); + } + + return null; + } + + public DefineSoundTag getSound(int soundId) { + CharacterTag characterTag = getCharacters().get(soundId); + if (characterTag instanceof DefineSoundTag) { + return (DefineSoundTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be a DefineSoundTag. characterId: {0}", soundId); + } + + return null; + } + + public TextTag getText(int textId) { + CharacterTag characterTag = getCharacters().get(textId); + if (characterTag instanceof TextTag) { + return (TextTag) characterTag; + } + + if (characterTag != null) { + logger.log(Level.SEVERE, "CharacterTag should be a TextTag. characterId: {0}", textId); + } + + return null; + } + + public List getAbcList() { + if (abcList == null) { + synchronized (this) { + if (abcList == null) { + ArrayList newAbcList = new ArrayList<>(); + getAbcTags(getTags(), newAbcList); + abcList = newAbcList; + } + } + } + + return abcList; + } + + public boolean isAS3() { + FileAttributesTag fileAttributes = getFileAttributes(); + return (fileAttributes != null && fileAttributes.actionScript3) || (fileAttributes == null && !getAbcList().isEmpty()); + } + + public MetadataTag getMetadata() { + for (Tag t : getTags()) { + if (t instanceof MetadataTag) { + return (MetadataTag) t; + } + } + + return null; + } + + public FileAttributesTag getFileAttributes() { + for (Tag t : getTags()) { + if (t instanceof FileAttributesTag) { + return (FileAttributesTag) t; + } + } + + return null; + } + + public SetBackgroundColorTag getBackgroundColor() { + for (Tag t : getTags()) { + if (t instanceof SetBackgroundColorTag) { + return (SetBackgroundColorTag) t; + } + } + + return null; + } + + public EnableTelemetryTag getEnableTelemetry() { + for (Tag t : getTags()) { + if (t instanceof EnableTelemetryTag) { + return (EnableTelemetryTag) t; + } + } + return null; + } + + public int getNextCharacterId() { + int max = 0; + Set ids = new HashSet<>(getCharacters().keySet()); + for (Tag t : tags) { + if (t instanceof ImportTag) { + ids.addAll(((ImportTag) t).getAssets().keySet()); + } + } + for (int characterId : ids) { + if (characterId > max) { + max = characterId; + } + } + + return max + 1; + } + + public synchronized JPEGTablesTag getJtt() { + if (jtt == null) { + synchronized (this) { + if (jtt == null) { + for (Tag t : getTags()) { + if (t instanceof JPEGTablesTag) { + jtt = (JPEGTablesTag) t; + break; + } + } + } + } + } + + return jtt; + } + + public String getDocumentClass() { + for (Tag t : getTags()) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) t; + for (int i = 0; i < sc.tags.size(); i++) { + if (sc.tags.get(i) == 0) { + return sc.names.get(i); + } + } + } + } + + return null; + } + + public void fixCharactersOrder(boolean checkAll) { + Set addedCharacterIds = new HashSet<>(); + Set movedTags = new HashSet<>(); + for (int i = 0; i < tags.size(); i++) { + Tag tag = tags.get(i); + if (checkAll || tag.isModified()) { + Set needed = new HashSet<>(); + tag.getNeededCharacters(needed); + if (tag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) tag; + needed.remove(characterTag.getCharacterId()); + } + boolean moved = false; + for (Integer id : needed) { + if (!addedCharacterIds.contains(id)) { + CharacterTag neededCharacter = getCharacter(id); + if (neededCharacter == null) { + continue; + } + + if (movedTags.contains(neededCharacter)) { + logger.log(Level.SEVERE, "Fixing characters order failed, recursion detected."); + return; + } + + // move the needed character to the current position + tags.remove(neededCharacter); + tags.add(i, neededCharacter); + movedTags.add(neededCharacter); + moved = true; + } + } + + if (moved) { + i--; + continue; + } + } + if (tag instanceof CharacterTag) { + addedCharacterIds.add(((CharacterTag) tag).getCharacterId()); + } + } + } + + public void resetTimelines(Timelined timelined) { + timelined.resetTimeline(); + if (timelined instanceof SWF) { + for (Tag t : ((SWF) timelined).getTags()) { + if (t instanceof Timelined) { + resetTimelines((Timelined) t); + } + } + } + } + + private void parseCharacters(Iterable list, Map characters, Map> characterIdTags) { + for (Tag t : list) { + if (t instanceof CharacterIdTag) { + int characterId = ((CharacterIdTag) t).getCharacterId(); + if (t instanceof CharacterTag) { + if (characters.containsKey(characterId)) { + logger.log(Level.SEVERE, "SWF already contains characterId={0}", characterId); + } + + if (characterId != 0) { + characters.put(characterId, (CharacterTag) t); + characterIdTags.put(characterId, new ArrayList<>()); + } + } else if (characterIdTags.containsKey(characterId)) { + characterIdTags.get(characterId).add((CharacterIdTag) t); + } + } + + if (t instanceof DefineSpriteTag) { + parseCharacters(((DefineSpriteTag) t).getTags(), characters, characterIdTags); + } + } + } + + /** + * Unresolve recursive sprites + */ + private void checkInvalidSprites() { + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof DefineSpriteTag) { + if (!isSpriteValid((DefineSpriteTag) t, new ArrayList<>())) { + tags.set(i, new TagStub(this, t.getId(), "InvalidSprite", t.getOriginalRange(), null)); + } + } + } + } + + private boolean isSpriteValid(DefineSpriteTag sprite, List path) { + if (path.contains(sprite.spriteId)) { + return false; + } + path.add(sprite.spriteId); + for (Tag t : sprite.getTags()) { + if (t instanceof DefineSpriteTag) { + if (!isSpriteValid((DefineSpriteTag) t, path)) { + return false; + } + } + } + path.remove((Integer) sprite.spriteId); + return true; + } + + @Override + public Timeline getTimeline() { + if (timeline == null) { + timeline = new Timeline(this); + } + return timeline; + } + + @Override + public void resetTimeline() { + if (timeline != null) { + timeline.reset(this); + } + } + + /** + * Gets all tags with specified id + * + * @param tagId Identificator of tag type + * @return List of tags + */ + public List getTagData(int tagId) { + List ret = new ArrayList<>(); + for (Tag tag : getTags()) { + if (tag.getId() == tagId) { + ret.add(tag); + } + } + return ret; + } + + /** + * Saves this SWF into new file + * + * @param os OutputStream to save SWF in + * @throws IOException + */ + public void saveTo(OutputStream os) throws IOException { + byte[] uncompressedData = saveToByteArray(); + compress(new ByteArrayInputStream(uncompressedData), os, compression, lzmaProperties); + } + + public byte[] getHeaderBytes() { + return getHeaderBytes(compression, gfx); + } + + private static byte[] getHeaderBytes(SWFCompression compression, boolean gfx) { + if (compression == SWFCompression.LZMA_ABC) { + return new byte[]{'A', 'B', 'C'}; + } + + byte[] ret = new byte[3]; + + if (compression == SWFCompression.LZMA) { + ret[0] = 'Z'; + } else if (compression == SWFCompression.ZLIB) { + ret[0] = 'C'; + } else if (gfx) { + ret[0] = 'G'; + } else { + ret[0] = 'F'; + } + + if (gfx) { + ret[1] = 'F'; + ret[2] = 'X'; + } else { + ret[1] = 'W'; + ret[2] = 'S'; + } + + return ret; + } + + private byte[] saveToByteArray() throws IOException { + fixCharactersOrder(false); + + byte[] data; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(baos, version)) { + sos.write(getHeaderBytes(SWFCompression.NONE, gfx)); + sos.writeUI8(version); + sos.writeUI32(0); // placeholder for file length + sos.writeRECT(displayRect); + sos.writeFIXED8(frameRate); + sos.writeUI16(frameCount); + + sos.writeTags(getLocalTags()); + if (hasEndTag) { + sos.writeUI16(0); + } + + data = baos.toByteArray(); + } + + // update file size + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFOutputStream sos = new SWFOutputStream(baos, version)) { + sos.writeUI32(data.length); + byte[] lengthData = baos.toByteArray(); + System.arraycopy(lengthData, 0, data, 4, lengthData.length); + } + + return data; + } + + /** + * Compress SWF file + * + * @param is InputStream + * @param os OutputStream to save SWF in + * @param compression + * @param lzmaProperties + * @throws IOException + */ + private static void compress(InputStream is, OutputStream os, SWFCompression compression, byte[] lzmaProperties) throws IOException { + byte[] hdr = new byte[8]; + + is.mark(8); + + // SWFheader: signature, version and fileSize + if (is.read(hdr) != 8) { + throw new SwfOpenException("SWF header is too short"); + } + + boolean uncompressed = hdr[0] == 'F' || hdr[0] == 'G'; // FWS or GFX + if (!uncompressed) { + // fisrt decompress, then compress to the given format + is.reset(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + decompress(is, baos, false); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + compress(bais, os, compression, lzmaProperties); + return; + } + + boolean gfx = hdr[1] == 'F' && hdr[2] == 'X'; + int version = hdr[3]; + long fileSize; + try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) { + fileSize = sis.readUI32("fileSize"); + } + + SWFOutputStream sos = new SWFOutputStream(os, version); + sos.write(getHeaderBytes(compression, gfx)); + sos.writeUI8(version); + sos.writeUI32(fileSize); + + if (compression == SWFCompression.LZMA || compression == SWFCompression.LZMA_ABC) { + long uncompressedLength = fileSize - 8; + Encoder enc = new Encoder(); + if (lzmaProperties == null) { + // todo: the bytes are from a sample swf + lzmaProperties = new byte[]{93, 0, 0, 32, 0}; + } + + int val = lzmaProperties[0] & 0xFF; + int lc = val % 9; + int remainder = val / 9; + int lp = remainder % 5; + int pb = remainder / 5; + int dictionarySize = 0; + for (int i = 0; i < 4; i++) { + dictionarySize += ((int) (lzmaProperties[1 + i]) & 0xFF) << (i * 8); + } + if (Configuration.lzmaFastBytes.get() > 0) { + enc.SetNumFastBytes(Configuration.lzmaFastBytes.get()); + } + enc.SetDictionarySize(dictionarySize); + enc.SetLcLpPb(lc, lp, pb); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + enc.SetEndMarkerMode(true); + enc.Code(is, baos, -1, -1, null); + byte[] data = baos.toByteArray(); + if (compression == SWFCompression.LZMA) { + byte[] udata = new byte[4]; + udata[0] = (byte) (data.length & 0xFF); + udata[1] = (byte) ((data.length >> 8) & 0xFF); + udata[2] = (byte) ((data.length >> 16) & 0xFF); + udata[3] = (byte) ((data.length >> 24) & 0xFF); + os.write(udata); + } + enc.WriteCoderProperties(os); + if (compression == SWFCompression.LZMA_ABC) { + byte[] udata = new byte[8]; + udata[0] = (byte) (uncompressedLength & 0xFF); + udata[1] = (byte) ((uncompressedLength >> 8) & 0xFF); + udata[2] = (byte) ((uncompressedLength >> 16) & 0xFF); + udata[3] = (byte) ((uncompressedLength >> 24) & 0xFF); + udata[4] = (byte) ((uncompressedLength >> 32) & 0xFF); + udata[5] = (byte) ((uncompressedLength >> 40) & 0xFF); + udata[6] = (byte) ((uncompressedLength >> 48) & 0xFF); + udata[7] = (byte) ((uncompressedLength >> 56) & 0xFF); + os.write(udata); + } + os.write(data); + } else if (compression == SWFCompression.ZLIB) { + DeflaterOutputStream dos = new DeflaterOutputStream(os); + try { + Helper.copyStream(is, dos); + } finally { + dos.finish(); + } + } else { + Helper.copyStream(is, os); + } + } + + @Override + public boolean isModified() { + if (isModified) { + return true; + } + + for (Tag tag : getTags()) { + if (tag.isModified()) { + return true; + } + } + return false; + } + + @Override + public void setModified(boolean value) { + isModified = value; + } + + public void clearModified() { + for (Tag tag : getTags()) { + if (tag.isModified()) { + tag.createOriginalData(); + tag.setModified(false); + } + } + + isModified = false; + + try { + uncompressedData = saveToByteArray(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Cannot save SWF", ex); + } + } + + /** + * Constructs an empty SWF + */ + public SWF() { + version = SWF.DEFAULT_VERSION; + displayRect = new RECT(0, 1, 0, 1); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, boolean parallelRead) throws IOException, InterruptedException { + this(is, null, null, null, parallelRead, false, true); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param parallelRead Use parallel threads? + * @param lazy + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, boolean parallelRead, boolean lazy) throws IOException, InterruptedException { + this(is, null, null, null, parallelRead, false, lazy); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param file Path to the file + * @param fileTitle Title of the SWF + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, String file, String fileTitle, boolean parallelRead) throws IOException, InterruptedException { + this(is, file, fileTitle, null, parallelRead, false, true); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param listener + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { + this(is, null, null, listener, parallelRead, false, true); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param file Path to the file + * @param fileTitle Title of the SWF + * @param listener + * @param parallelRead Use parallel threads? + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead) throws IOException, InterruptedException { + this(is, file, fileTitle, listener, parallelRead, false, true); + } + + /** + * Faster constructor to check SWF only + * + * @param is + * @throws java.io.IOException + */ + public SWF(InputStream is) throws IOException { + decompress(is, new NulStream(), true); + } + + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy) throws IOException, InterruptedException { + this(is, file, fileTitle, listener, parallelRead, checkOnly, lazy, null); + } + + /** + * Construct SWF from stream + * + * @param is Stream to read SWF from + * @param file Path to the file + * @param fileTitle Title of the SWF + * @param listener + * @param parallelRead Use parallel threads? + * @param checkOnly Check only file validity + * @param lazy + * @param resolver Resolver for imported tags + * @throws IOException + * @throws java.lang.InterruptedException + */ + public SWF(InputStream is, String file, String fileTitle, ProgressListener listener, boolean parallelRead, boolean checkOnly, boolean lazy, UrlResolver resolver) throws IOException, InterruptedException { + this.file = file; + this.fileTitle = fileTitle; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SWFHeader header = decompress(is, baos, true); + gfx = header.gfx; + compression = header.compression; + lzmaProperties = header.lzmaProperties; + uncompressedData = baos.toByteArray(); + originalUncompressedData = uncompressedData; + + SWFInputStream sis = new SWFInputStream(this, uncompressedData); + dumpInfo = new DumpInfoSwfNode(this, "rootswf", "", null, 0, 0); + sis.dumpInfo = dumpInfo; + sis.skipBytesEx(3, "signature"); // skip siganture + version = sis.readUI8("version"); + fileSize = sis.readUI32("fileSize"); + dumpInfo.lengthBytes = fileSize; + if (listener != null) { + sis.addPercentListener(listener); + } + sis.setPercentMax(fileSize); + displayRect = sis.readRECT("displayRect"); + frameRate = sis.readFIXED8("frameRate"); + frameCount = sis.readUI16("frameCount"); + List tags = sis.readTagList(this, 0, parallelRead, true, !checkOnly, lazy); + if (tags.size() > 0 && tags.get(tags.size() - 1).getId() == EndTag.ID) { + tags.remove(tags.size() - 1); + } else { + hasEndTag = false; + } + this.tags = tags; + readOnlyTags = null; + if (!checkOnly) { + checkInvalidSprites(); + updateCharacters(); + assignExportNamesToSymbols(); + assignClassesToSymbols(); + if (resolver != null) { + resolveImported(resolver); + } + SWFDecompilerPlugin.fireSwfParsed(this); + } else { + boolean hasNonUnknownTag = false; + for (Tag tag : tags) { + if (tag.getOriginalDataLength() > 0 && Tag.getRequiredTags().contains(tag.getId())) { + hasNonUnknownTag = true; + } + } + if (!hasNonUnknownTag) { + throw new IOException("Invalid SWF file. No known tag found."); + } + } + + if (Configuration.autoRenameIdentifiers.get()) { + deobfuscateIdentifiers(RenameType.TYPENUMBER); + AbcMultiNameCollisionFixer collisionFixer = new AbcMultiNameCollisionFixer(); + collisionFixer.fixCollisions(this); + assignClassesToSymbols(); + clearScriptCache(); + } + + getASMs(true); // Add scriptNames to ASMs + } + + private void resolveImported(UrlResolver resolver) { + for (int p = 0; p < tags.size(); p++) { + Tag t = tags.get(p); + if (t instanceof ImportTag) { + ImportTag importTag = (ImportTag) t; + + SWF iSwf = resolver.resolveUrl(importTag.getUrl()); + if (iSwf != null) { + Map exportedMap1 = new HashMap<>(); + Map classesMap1 = new HashMap<>(); + + for (Tag t2 : iSwf.tags) { + if (t2 instanceof ExportAssetsTag) { + ExportAssetsTag sc = (ExportAssetsTag) t2; + Map m2 = sc.getTagToNameMap(); + for (int key : m2.keySet()) { + if (!exportedMap1.containsKey(key)) { + exportedMap1.put(key, m2.get(key)); + } + } + } + if (t2 instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) t2; + Map m2 = sc.getTagToNameMap(); + for (int key : m2.keySet()) { + if (!classesMap1.containsKey(key)) { + classesMap1.put(key, m2.get(key)); + } + } + } + } + Map exportedMap2 = new HashMap<>(); + for (int k : exportedMap1.keySet()) { + exportedMap2.put(exportedMap1.get(k), k); + } + + Map classesMap2 = new HashMap<>(); + for (int k : classesMap1.keySet()) { + classesMap2.put(classesMap1.get(k), k); + } + + Map importedMap1 = importTag.getAssets(); + Map importedMap2 = new HashMap<>(); + for (int k : importedMap1.keySet()) { + importedMap2.put(importedMap1.get(k), k); + } + + int pos = 0; + for (String key : importedMap2.keySet()) { + if (!exportedMap2.containsKey(key)) { + continue; //? + } + int exportedId = exportedMap2.get(key); + int importedId = importedMap2.get(key); + for (Tag cht : iSwf.tags) { + if ((cht instanceof CharacterIdTag) && (((CharacterIdTag) cht).getCharacterId() == exportedId) && !(cht instanceof PlaceObjectTypeTag) && !(cht instanceof RemoveTag)) { + CharacterIdTag ch = (CharacterIdTag) cht; + cht.setSwf(this); + ch.setCharacterId(importedId); + cht.setImported(true); + tags.add(p + 1 + pos, cht); + pos++; + } + } + } + + int newId = getNextCharacterId(); + pos = 0; + for (String key : classesMap2.keySet()) { + int exportedId = classesMap2.get(key); + int importedId = newId++; + for (Tag cht : iSwf.tags) { + if ((cht instanceof CharacterIdTag) && (((CharacterIdTag) cht).getCharacterId() == exportedId) && !(cht instanceof PlaceObjectTypeTag) && !(cht instanceof RemoveTag)) { + CharacterIdTag ch = (CharacterIdTag) cht; + cht.setSwf(this); + ch.setCharacterId(importedId); + cht.setImported(true); + tags.add(p + 1 + pos, cht); + pos++; + } + } + } + updateCharacters(); + } + } + } + } + + @Override + public SWF getSwf() { + return this; + } + + public SWF getRootSwf() { + SWF result = this; + while (result.binaryData != null) { + result = result.binaryData.getSwf(); + } + + return result; + } + + public String getFile() { + return file; + } + + /** + * Get title of the file + * + * @return file title + */ + public String getFileTitle() { + if (fileTitle != null) { + return fileTitle; + } + return file; + } + + public String getShortFileName() { + String title = getFileTitle(); + if (title == null) { + return ""; + } + return new File(title).getName(); + } + + public void setFile(String file) { + this.file = file; + fileTitle = null; + } + + public Date getFileModificationDate() { + try { + if (swfList != null && swfList.sourceInfo != null) { + String fileName = swfList.sourceInfo.getFile(); + if (fileName != null) { + long lastModified = new File(fileName).lastModified(); + if (lastModified > 0) { + return new Date(lastModified); + } + } + } + } catch (SecurityException sex) { + } + + return new Date(); + } + + private static void getAbcTags(Iterable list, List actionScripts) { + for (Tag t : list) { + if (t instanceof DefineSpriteTag) { + getAbcTags(((DefineSpriteTag) t).getTags(), actionScripts); + } + if (t instanceof ABCContainerTag) { + actionScripts.add((ABCContainerTag) t); + } + } + } + + public void assignExportNamesToSymbols() { + HashMap exportNames = new HashMap<>(); + for (Tag t : getTags()) { + if (t instanceof ExportAssetsTag) { + ExportAssetsTag eat = (ExportAssetsTag) t; + for (int i = 0; i < eat.tags.size(); i++) { + Integer tagId = eat.tags.get(i); + String name = eat.names.get(i); + if ((!exportNames.containsKey(tagId)) && (!exportNames.containsValue(name))) { + exportNames.put(tagId, name); + } + } + } + } + for (Tag t : getTags()) { + if (t instanceof CharacterTag) { + CharacterTag ct = (CharacterTag) t; + if (exportNames.containsKey(ct.getCharacterId())) { + ct.setExportName(exportNames.get(ct.getCharacterId())); + } + } + } + } + + public void assignClassesToSymbols() { + HashMap classes = new HashMap<>(); + for (Tag t : getTags()) { + if (t instanceof SymbolClassTag) { + SymbolClassTag sct = (SymbolClassTag) t; + for (int i = 0; i < sct.tags.size(); i++) { + if ((!classes.containsKey(sct.tags.get(i))) && (!classes.containsValue(sct.names.get(i)))) { + classes.put(sct.tags.get(i), sct.names.get(i)); + } + } + } + } + for (Tag t : getTags()) { + if (t instanceof CharacterTag) { + CharacterTag ct = (CharacterTag) t; + if (classes.containsKey(ct.getCharacterId())) { + ct.setClassName(classes.get(ct.getCharacterId())); + } + } + } + } + + /** + * Compress SWF file + * + * @param fis Input stream + * @param fos Output stream + * @param compression + * @return True on success + */ + public static boolean compress(InputStream fis, OutputStream fos, SWFCompression compression) { + try { + compress(fis, fos, compression, null); + } catch (IOException ex) { + return false; + } + return true; + } + + public static boolean decompress(InputStream fis, OutputStream fos) { + try { + decompress(fis, fos, false); + return true; + } catch (IOException ex) { + return false; + } + } + + private static void decodeLZMAStream(InputStream is, OutputStream os, byte[] lzmaProperties, long fileSize) throws IOException { + Decoder decoder = new Decoder(); + if (!decoder.SetDecoderProperties(lzmaProperties)) { + throw new IOException("LZMA:Incorrect stream properties"); + } + if (!decoder.Code(is, os, fileSize - 8)) { + throw new IOException("LZMA:Error in data stream"); + } + } + + private static SWFHeader decompress(InputStream is, OutputStream os, boolean allowUncompressed) throws IOException { + byte[] hdr = new byte[8]; + + // SWFheader: signature, version and fileSize + if (is.read(hdr) != 8) { + throw new SwfOpenException("SWF header is too short"); + } + + String signature = new String(hdr, 0, 3, Utf8Helper.charset); + if (!swfSignatures.contains(signature)) { + throw new SwfOpenException("Invalid SWF file, wrong signature."); + } + + int version = hdr[3]; + long fileSize; + try (SWFInputStream sis = new SWFInputStream(null, Arrays.copyOfRange(hdr, 4, 8), 4, 4)) { + fileSize = sis.readUI32("fileSize"); + } + + SWFHeader header = new SWFHeader(); + header.version = version; + header.fileSize = fileSize; + header.gfx = hdr[1] == 'F' && hdr[2] == 'X'; + + try (SWFOutputStream sos = new SWFOutputStream(os, version)) { + sos.write(getHeaderBytes(SWFCompression.NONE, header.gfx)); + sos.writeUI8(version); + sos.writeUI32(fileSize); + + switch (hdr[0]) { + case 'C': { // CWS, CFX + Helper.copyStream(new InflaterInputStream(is), os, fileSize - 8); + header.compression = SWFCompression.ZLIB; + break; + } + case 'Z': { // ZWS + byte[] lzmaprop = new byte[9]; + is.read(lzmaprop); + try (SWFInputStream sis = new SWFInputStream(null, lzmaprop)) { + sis.readUI32("LZMAsize"); // compressed LZMA data size = compressed SWF - 17 byte, + // where 17 = 8 byte header + this 4 byte + 5 bytes decoder properties + + int propertiesSize = 5; + byte[] lzmaProperties = sis.readBytes(propertiesSize, "lzmaproperties"); + if (lzmaProperties.length != propertiesSize) { + throw new IOException("LZMA:input .lzma file is too short"); + } + + decodeLZMAStream(is, os, lzmaProperties, fileSize); + + header.compression = SWFCompression.LZMA; + header.lzmaProperties = lzmaProperties; + } + break; + } + case 'A': { // ABC + byte[] lzmaProperties = new byte[5]; + is.read(lzmaProperties); + byte[] uncompressedLength = new byte[8]; + is.read(uncompressedLength); + + decodeLZMAStream(is, os, lzmaProperties, fileSize); + + header.compression = SWFCompression.LZMA_ABC; + header.lzmaProperties = lzmaProperties; + break; + } + default: { // FWS, GFX + if (allowUncompressed) { + Helper.copyStream(is, os, fileSize - 8); + } else { + throw new IOException("SWF is not compressed"); + } + } + } + + return header; + } + } + + public static boolean renameInvalidIdentifiers(RenameType renameType, InputStream fis, OutputStream fos) { + try { + SWF swf = new SWF(fis, Configuration.parallelSpeedUp.get()); + int cnt = swf.deobfuscateIdentifiers(renameType); + swf.assignClassesToSymbols(); + System.out.println(cnt + " identifiers renamed."); + swf.saveTo(fos); + } catch (InterruptedException | IOException ex) { + return false; + } + return true; + } + + public List getScriptPacksByClassNames(List classNames) throws Exception { + Set resultSet = new HashSet<>(); + + List abcList = getAbcList(); + List allAbcList = new ArrayList<>(); + for (int i = 0; i < abcList.size(); i++) { + allAbcList.add(abcList.get(i).getABC()); + } + + for (String className : classNames) { + for (int i = 0; i < abcList.size(); i++) { + ABC abc = abcList.get(i).getABC(); + List scrs = abc.findScriptPacksByPath(className, allAbcList); + for (int j = 0; j < scrs.size(); j++) { + ScriptPack scr = scrs.get(j); + resultSet.add(scr); + } + } + } + + return new ArrayList<>(resultSet); + } + + private List uniqueAS3Packs(List packs) { + List ret = new ArrayList<>(); + Set classPaths = new HashSet<>(); + for (ScriptPack item : packs) { + ClassPath key = item.getClassPath(); + if (classPaths.contains(key)) { + logger.log(Level.SEVERE, "Duplicate pack path found (" + key + ")!"); + } else { + classPaths.add(key); + ret.add(item); + } + } + return ret; + } + + public List getAS3Packs() { + List packs = new ArrayList<>(); + + List abcList = getAbcList(); + List allAbcList = new ArrayList<>(); + for (int i = 0; i < abcList.size(); i++) { + allAbcList.add(abcList.get(i).getABC()); + } + + for (ABCContainerTag abcTag : abcList) { + packs.addAll(abcTag.getABC().getScriptPacks(null, allAbcList)); + } + return uniqueAS3Packs(packs); + } + + @Override + public RECT getRect() { + return displayRect; + } + + @Override + public RECT getRect(Set added) { + return displayRect; + } + + public EventListener getExportEventListener() { + EventListener evl = new EventListener() { + @Override + public void handleExportingEvent(String type, int index, int count, Object data) { + for (EventListener listener : listeners) { + listener.handleExportingEvent(type, index, count, data); + } + } + + @Override + public void handleExportedEvent(String type, int index, int count, Object data) { + for (EventListener listener : listeners) { + listener.handleExportedEvent(type, index, count, data); + } + } + + @Override + public void handleEvent(String event, Object data) { + informListeners(event, data); + } + }; + + return evl; + } + + public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, ScriptExportSettings exportSettings, boolean parallel, EventListener evl) throws IOException { + return exportActionScript(handler, outdir, null, exportSettings, parallel, evl, true, true); + } + + public List exportActionScript(AbortRetryIgnoreHandler handler, String outdir, List as3scripts, ScriptExportSettings exportSettings, boolean parallel, EventListener evl, boolean as2, boolean as3) throws IOException { + List ret = new ArrayList<>(); + + if (isAS3()) { + if (as3) { + ret.addAll(new AS3ScriptExporter().exportActionScript3(this, handler, outdir, as3scripts, exportSettings, parallel, evl)); + } + } else if (as2) { + ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, outdir, getASMs(true), exportSettings, parallel, evl)); + } + return ret; + } + + public Map getASMs(boolean exportFileNames) { + return getASMs(exportFileNames, new ArrayList<>(), true); + } + + public Map getASMs(boolean exportFileNames, List nodesToExport, boolean exportAll) { + Map asmsToExport = new HashMap<>(); + for (TreeItem treeItem : getFirstLevelASMNodes(null)) { + getASMs(exportFileNames, treeItem, nodesToExport, exportAll, asmsToExport, File.separator + getASMPath(exportFileNames, treeItem)); + } + + return asmsToExport; + } + + private void getASMs(boolean exportFileNames, TreeItem treeItem, List nodesToExport, boolean exportAll, Map asmsToExport, String path) { + boolean exportNode = nodesToExport.contains(treeItem); + TreeItem realItem = treeItem instanceof TagScript ? ((TagScript) treeItem).getTag() : treeItem; + if (realItem instanceof ASMSource && (exportAll || exportNode)) { + String npath = path; + String exPath = path; + int ppos = 1; + while (asmsToExport.containsKey(npath)) { + ppos++; + npath = path + (exportFileNames ? "[" + ppos + "]" : "_" + ppos); + exPath = path + "[" + ppos + "]"; + } + ((ASMSource) realItem).setScriptName(exPath); + asmsToExport.put(npath, (ASMSource) realItem); + } + + if (treeItem instanceof TagScript) { + TagScript tagScript = (TagScript) treeItem; + for (TreeItem subItem : tagScript.getFrames()) { + getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + } else if (treeItem instanceof FrameScript) { + FrameScript frameScript = (FrameScript) treeItem; + Frame parentFrame = frameScript.getFrame(); + for (TreeItem subItem : parentFrame.actionContainers) { + getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + for (TreeItem subItem : parentFrame.actions) { + getASMs(exportFileNames, getASMWrapToTagScript(subItem), nodesToExport, exportAll || exportNode, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + } else if (treeItem instanceof AS2Package) { + AS2Package as2Package = (AS2Package) treeItem; + for (TreeItem subItem : as2Package.subPackages.values()) { + getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + for (TreeItem subItem : as2Package.scripts.values()) { + getASMs(exportFileNames, subItem, nodesToExport, exportAll, asmsToExport, path + File.separator + getASMPath(exportFileNames, subItem)); + } + } + } + + private String getASMPath(boolean exportFileName, TreeItem treeItem) { + if (!exportFileName) { + return treeItem.toString(); + } + + String result; + if (treeItem instanceof Exportable) { + result = ((Exportable) treeItem).getExportFileName(); + } else { + result = treeItem.toString(); + } + + return Helper.makeFileName(result); + } + + private TreeItem getASMWrapToTagScript(TreeItem treeItem) { + if (treeItem instanceof Tag) { + Tag resultTag = (Tag) treeItem; + List subNodes = new ArrayList<>(); + if (treeItem instanceof ASMSourceContainer) { + for (ASMSource item : ((ASMSourceContainer) treeItem).getSubItems()) { + subNodes.add(item); + } + } + + TagScript tagScript = new TagScript(treeItem.getSwf(), resultTag, subNodes); + return tagScript; + } + + return treeItem; + } + + public List getFirstLevelASMNodes(Map tagScriptCache) { + Timeline timeline = getTimeline(); + List subNodes = new ArrayList<>(); + List subFrames = new ArrayList<>(); + subNodes.addAll(timeline.getAS2RootPackage().subPackages.values()); + subNodes.addAll(timeline.getAS2RootPackage().scripts.values()); + + for (Tag tag : timeline.otherTags) { + boolean hasInnerFrames = false; + List tagSubNodes = new ArrayList<>(); + if (tag instanceof Timelined) { + Timeline timeline2 = ((Timelined) tag).getTimeline(); + for (Frame frame : timeline2.getFrames()) { + if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { + FrameScript frameScript = new FrameScript(this, frame); + tagSubNodes.add(frameScript); + hasInnerFrames = true; + } + } + } + + if (tag instanceof ASMSourceContainer) { + for (ASMSource asm : ((ASMSourceContainer) tag).getSubItems()) { + tagSubNodes.add(asm); + } + } + + if (!tagSubNodes.isEmpty()) { + TagScript ts = new TagScript(this, tag, tagSubNodes); + if (tagScriptCache != null) { + tagScriptCache.put(tag, ts); + } + if (hasInnerFrames) { + subFrames.add(ts); + } else { + subNodes.add(ts); + } + } + } + + subNodes.addAll(subFrames); + for (Frame frame : timeline.getFrames()) { + if (!frame.actions.isEmpty() || !frame.actionContainers.isEmpty()) { + FrameScript frameScript = new FrameScript(this, frame); + subNodes.add(frameScript); + } + } + + return subNodes; + } + + private final HashSet listeners = new HashSet<>(); + + public final void addEventListener(EventListener listener) { + listeners.add(listener); + for (Tag t : getTags()) { + if (t instanceof ABCContainerTag) { + (((ABCContainerTag) t).getABC()).addEventListener(listener); + } + } + } + + public final void removeEventListener(EventListener listener) { + listeners.remove(listener); + for (Tag t : getTags()) { + if (t instanceof ABCContainerTag) { + (((ABCContainerTag) t).getABC()).removeEventListener(listener); + } + } + } + + protected void informListeners(String event, Object data) { + for (EventListener listener : listeners) { + listener.handleEvent(event, data); + } + } + + public static void populateVideoFrames(int streamId, Iterable tags, HashMap output) { + for (Tag t : tags) { + if (t instanceof VideoFrameTag) { + VideoFrameTag videoFrameTag = (VideoFrameTag) t; + if (videoFrameTag.streamID == streamId) { + output.put(videoFrameTag.frameNum, (VideoFrameTag) t); + } + } + if (t instanceof DefineSpriteTag) { + populateVideoFrames(streamId, ((DefineSpriteTag) t).getTags(), output); + } + } + } + + private static void writeLE(OutputStream os, long val, int size) throws IOException { + for (int i = 0; i < size; i++) { + os.write((int) (val & 0xff)); + val >>= 8; + } + } + + public static void createWavFromPcmData(OutputStream fos, int soundRateHz, boolean soundSize, boolean soundType, byte[] data) throws IOException { + ByteArrayOutputStream subChunk1Data = new ByteArrayOutputStream(); + int audioFormat = 1; // PCM + writeLE(subChunk1Data, audioFormat, 2); + int numChannels = soundType ? 2 : 1; + writeLE(subChunk1Data, numChannels, 2); + int[] rateMap = {5512, 11025, 22050, 44100}; + int sampleRate = soundRateHz; // rateMap[soundRate]; + writeLE(subChunk1Data, sampleRate, 4); + int bitsPerSample = soundSize ? 16 : 8; + int byteRate = sampleRate * numChannels * bitsPerSample / 8; + writeLE(subChunk1Data, byteRate, 4); + int blockAlign = numChannels * bitsPerSample / 8; + writeLE(subChunk1Data, blockAlign, 2); + writeLE(subChunk1Data, bitsPerSample, 2); + + ByteArrayOutputStream chunks = new ByteArrayOutputStream(); + chunks.write(Utf8Helper.getBytes("fmt ")); + byte[] subChunk1DataBytes = subChunk1Data.toByteArray(); + writeLE(chunks, subChunk1DataBytes.length, 4); + chunks.write(subChunk1DataBytes); + + chunks.write(Utf8Helper.getBytes("data")); + writeLE(chunks, data.length, 4); + chunks.write(data); + + fos.write(Utf8Helper.getBytes("RIFF")); + byte[] chunkBytes = chunks.toByteArray(); + writeLE(fos, 4 + chunkBytes.length, 4); + fos.write(Utf8Helper.getBytes("WAVE")); + fos.write(chunkBytes); + } + + public static String getTypePrefix(CharacterTag c) { + if (c instanceof ShapeTag) { + return "shape"; + } + if (c instanceof MorphShapeTag) { + return "morphshape"; + } + if (c instanceof DefineSpriteTag) { + return "sprite"; + } + if (c instanceof TextTag) { + return "text"; + } + if (c instanceof ButtonTag) { + return "button"; + } + if (c instanceof FontTag) { + return "font"; + } + if (c instanceof ImageTag) { + return "image"; + } + return "character"; + } + + public static void writeLibrary(SWF fswf, Set library, OutputStream fos) throws IOException { + for (int c : library) { + CharacterTag ch = fswf.getCharacter(c); + if (ch instanceof FontTag) { + StringBuilder sb = new StringBuilder(); + sb.append("function ").append(getTypePrefix(ch)).append(c).append("(ctx,ch,textColor){\r\n"); + ((FontTag) ch).toHtmlCanvas(sb, 1); + sb.append("}\r\n\r\n"); + fos.write(Utf8Helper.getBytes(sb.toString())); + } else { + if (ch instanceof ImageTag) { + ImageTag image = (ImageTag) ch; + ImageFormat format = image.getImageFormat(); + byte[] imageData = Helper.readStream(image.getImageData()); + String base64ImgData = Helper.byteArrayToBase64String(imageData); + fos.write(Utf8Helper.getBytes("var imageObj" + c + " = document.createElement(\"img\");\r\nimageObj" + c + ".src=\"data:image/" + format + ";base64," + base64ImgData + "\";\r\n")); + } + fos.write(Utf8Helper.getBytes("function " + getTypePrefix(ch) + c + "(ctx,ctrans,frame,ratio,time){\r\n")); + if (ch instanceof DrawableTag) { + StringBuilder sb = new StringBuilder(); + ((DrawableTag) ch).toHtmlCanvas(sb, 1); + fos.write(Utf8Helper.getBytes(sb.toString())); + } + fos.write(Utf8Helper.getBytes("}\r\n\r\n")); + } + } + } + + private static void getVariables(ConstantPool constantPool, BaseLocalData localData, TranslateStack stack, List output, ActionGraphSource code, int ip, List> variables, List functions, HashMap strings, List visited, HashMap usageTypes, String path) throws InterruptedException { + boolean debugMode = false; + while ((ip > -1) && ip < code.size()) { + if (visited.contains(ip)) { + break; + } + GraphSourceItem ins = code.get(ip); + + if (debugMode) { + System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ActionList(), new HashSet<>(), ScriptExportMode.PCODE) + " stack:" + Helper.stackToString(stack, LocalData.create(new ConstantPool()))); + } + if (ins.isExit()) { + break; + } + if (ins.isIgnored()) { + ip++; + continue; + } + + String usageType = "name"; + GraphTargetItem name = null; + if ((ins instanceof ActionGetVariable) + || (ins instanceof ActionGetMember) + || (ins instanceof ActionDefineLocal2) + || (ins instanceof ActionNewMethod) + || (ins instanceof ActionNewObject) + || (ins instanceof ActionCallMethod) + || (ins instanceof ActionCallFunction)) { + if (stack.isEmpty()) { + break; + } + name = stack.peek(); + } + + if ((ins instanceof ActionGetVariable) || (ins instanceof ActionDefineLocal2)) { + usageType = "variable"; + } + if (ins instanceof ActionGetMember) { + usageType = "member"; + } + if ((ins instanceof ActionNewMethod) || (ins instanceof ActionNewObject)) { + usageType = "class"; + } + if (ins instanceof ActionCallMethod) { + usageType = "function"; // can there be method? + } + if (ins instanceof ActionCallFunction) { + usageType = "function"; + } + + if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) { + functions.add(ins); + } + + if (ins instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; + List cntSizes = cnt.getContainerSizes(); + long addr = code.pos2adr(ip + 1); + ip = code.adr2pos(addr); + String cntName = cnt.getName(); + for (Long size : cntSizes) { + if (size == 0) { + continue; + } + ip = code.adr2pos(addr); + addr += size; + int nextip = code.adr2pos(addr); + getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(code.getActions().subList(ip, nextip), code.version, new HashMap<>(), new HashMap<>(), new HashMap<>()), 0, path + (cntName == null ? "" : "/" + cntName)); + ip = nextip; + } + List> r = new ArrayList<>(); + r.add(new ArrayList<>()); + r.add(new ArrayList<>()); + r.add(new ArrayList<>()); + ((GraphSourceItemContainer) ins).translateContainer(r, ins, stack, output, new HashMap<>(), new HashMap<>(), new HashMap<>()); + continue; + } + + if ((ins instanceof ActionSetVariable) || (ins instanceof ActionSetMember) || (ins instanceof ActionDefineLocal)) { + if (stack.size() < 2) { + break; + } + name = stack.get(stack.size() - 2); + } + + if ((ins instanceof ActionSetVariable) || (ins instanceof ActionDefineLocal)) { + usageType = "variable"; + } + + if (ins instanceof ActionSetMember) { + usageType = "member"; + } + + if (name instanceof DirectValueActionItem) { + variables.add(new MyEntry<>((DirectValueActionItem) name, constantPool)); + usageTypes.put((DirectValueActionItem) name, usageType); + } + + // for..in return + if (((ins instanceof ActionEquals) || (ins instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueActionItem)) { + stack.push(new DirectValueActionItem(null, null, 0, Null.INSTANCE, new ArrayList<>())); + } + + if (ins instanceof ActionConstantPool) { + constantPool = new ConstantPool(((ActionConstantPool) ins).constantPool); + } + int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; + + int requiredStackSize = ins.getStackPopCount(localData, stack); + if (stack.size() < requiredStackSize) { + // probably obfucated code, never executed branch + break; + } + + ins.translate(localData, stack, output, staticOperation, path); + if (ins.isExit()) { + break; + } + + if (ins instanceof ActionPush) { + if (!stack.isEmpty()) { + GraphTargetItem top = stack.peek(); + if (top instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) top; + if ((dvt.value instanceof String) || (dvt.value instanceof ConstantIndex)) { + if (constantPool == null) { + constantPool = new ConstantPool(dvt.constants); + } + strings.put(dvt, constantPool); + } + } + } + } + + if (ins.isBranch() || ins.isJump()) { + if (ins instanceof ActionIf) { + if (stack.isEmpty()) { + break; + } + stack.pop(); + } + visited.add(ip); + List branches = ins.getBranches(code); + for (int b : branches) { + TranslateStack brStack = (TranslateStack) stack.clone(); + if (b >= 0) { + getVariables(constantPool, localData, brStack, output, code, b, variables, functions, strings, visited, usageTypes, path); + } else if (debugMode) { + System.out.println("Negative branch:" + b); + } + } + // } + break; + } + ip++; + } + } + + private static void getVariables(List> variables, List functions, HashMap strings, HashMap usageTypes, ActionGraphSource code, int addr, String path) throws InterruptedException { + ActionLocalData localData = new ActionLocalData(); + getVariables(null, localData, new TranslateStack(path), new ArrayList<>(), code, code.adr2pos(addr), variables, functions, strings, new ArrayList<>(), usageTypes, path); + } + + private List> getVariables(List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes, ASMSource src, String path) throws InterruptedException { + List> ret = new ArrayList<>(); + ActionList actions = src.getActions(); + actionsMap.put(src, actions); + getVariables(variables, functions, strings, usageTypes, new ActionGraphSource(actions, version, new HashMap<>(), new HashMap<>(), new HashMap<>()), 0, path); + return ret; + } + + private void getVariables(Iterable tags, String path, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { + List processed = new ArrayList<>(); + for (Tag t : tags) { + String subPath = path + "/" + t.toString(); + if (t instanceof ASMSource) { + addVariable((ASMSource) t, subPath, processed, variables, actionsMap, functions, strings, usageTypes); + } + if (t instanceof ASMSourceContainer) { + List processed2 = new ArrayList<>(); + for (ASMSource asm : ((ASMSourceContainer) t).getSubItems()) { + addVariable(asm, subPath + "/" + asm.toString(), processed2, variables, actionsMap, functions, strings, usageTypes); + } + } + if (t instanceof DefineSpriteTag) { + getVariables(((DefineSpriteTag) t).getTags(), path + "/" + t.toString(), variables, actionsMap, functions, strings, usageTypes); + } + } + } + + private void addVariable(ASMSource asm, String path, List processed, List> variables, HashMap actionsMap, List functions, HashMap strings, HashMap usageTypes) throws InterruptedException { + int pos = 1; + String infPath2 = path; + while (processed.contains(infPath2)) { + pos++; + infPath2 = path + "[" + pos + "]"; + } + processed.add(infPath2); + informListeners("getVariables", infPath2); + getVariables(variables, actionsMap, functions, strings, usageTypes, asm, path); + } + + public boolean as3StringConstantExists(String str) { + for (ABCContainerTag abcTag : getAbcList()) { + ABC abc = abcTag.getABC(); + for (int i = 1; i < abc.constants.getStringCount(); i++) { + if (abc.constants.getString(i).equals(str)) { + return true; + } + } + } + + return false; + } + + public void fixAS3Code() { + for (ABCContainerTag abcTag : getAbcList()) { + ABC abc = abcTag.getABC(); + for (MethodBody body : abc.bodies) { + AVM2Code code = body.getCode(); + body.setCodeBytes(code.getBytes()); + } + + ((Tag) abcTag).setModified(true); + } + } + + public int deobfuscateAS3Identifiers(RenameType renameType) { + for (Tag tag : getTags()) { + if (tag instanceof ABCContainerTag) { + ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, true); + tag.setModified(true); + } + } + for (Tag tag : getTags()) { + if (tag instanceof ABCContainerTag) { + ((ABCContainerTag) tag).getABC().deobfuscateIdentifiers(deobfuscated, renameType, false); + tag.setModified(true); + } + } + for (Tag tag : getTags()) { + if (tag instanceof SymbolClassTag) { + SymbolClassTag sc = (SymbolClassTag) tag; + for (int i = 0; i < sc.names.size(); i++) { + String newname = deobfuscation.deobfuscateNameWithPackage(true, sc.names.get(i), deobfuscated, renameType, deobfuscated); + if (newname != null) { + sc.names.set(i, newname); + } + } + sc.setModified(true); + } + } + deobfuscation.deobfuscateInstanceNames(true, deobfuscated, renameType, getTags(), new HashMap<>()); + return deobfuscated.size(); + } + + public int deobfuscateIdentifiers(RenameType renameType) throws InterruptedException { + FileAttributesTag fileAttributes = getFileAttributes(); + if (fileAttributes == null) { + int cnt = 0; + cnt += deobfuscateAS2Identifiers(renameType); + cnt += deobfuscateAS3Identifiers(renameType); + return cnt; + } else if (fileAttributes.actionScript3) { + return deobfuscateAS3Identifiers(renameType); + } else { + return deobfuscateAS2Identifiers(renameType); + } + } + + public void renameAS2Identifier(String identifier, String newname) throws InterruptedException { + Map selected = new HashMap<>(); + selected.put(DottedChain.parseWithSuffix(identifier), DottedChain.parseWithSuffix(newname)); + renameAS2Identifiers(null, selected); + } + + private int deobfuscateAS2Identifiers(RenameType renameType) throws InterruptedException { + return renameAS2Identifiers(renameType, null); + } + + private int renameAS2Identifiers(RenameType renameType, Map selected) throws InterruptedException { + HashMap actionsMap = new HashMap<>(); + List allFunctions = new ArrayList<>(); + List> allVariableNames = new ArrayList<>(); + HashMap allStrings = new HashMap<>(); + HashMap usageTypes = new HashMap<>(); + + int ret = 0; + getVariables(getTags(), "", allVariableNames, actionsMap, allFunctions, allStrings, usageTypes); + informListeners("rename", ""); + int fc = 0; + for (MyEntry it : allVariableNames) { + String name = it.getKey().toStringNoH(it.getValue()); + deobfuscation.allVariableNamesStr.add(name); + } + + informListeners("rename", "classes"); + int classCount = 0; + for (Tag t : getTags()) { + if (t instanceof DoInitActionTag) { + classCount++; + } + } + int cnt = 0; + for (Tag t : getTags()) { + if (t instanceof DoInitActionTag) { + cnt++; + informListeners("rename", "class " + cnt + "/" + classCount); + DoInitActionTag dia = (DoInitActionTag) t; + String exportName = getExportName(dia.spriteId); + exportName = exportName != null ? exportName : "_unk_"; + final String pkgPrefix = "__Packages."; + String[] classNameParts = null; + if (exportName.startsWith(pkgPrefix)) { + String className = exportName.substring(pkgPrefix.length()); + if (className.contains(".")) { + classNameParts = className.split("\\."); + } else { + classNameParts = new String[]{className}; + } + } + int staticOperation = Graph.SOP_USE_STATIC; //(Boolean) Configuration.getConfig("autoDeobfuscate", true) ? Graph.SOP_SKIP_STATIC : Graph.SOP_USE_STATIC; + List dec; + try { + dec = Action.actionsToTree(dia.getActions(), version, staticOperation, ""/*FIXME*/); + } catch (EmptyStackException ex) { + continue; + } + GraphTargetItem name = null; + for (GraphTargetItem it : dec) { + if (it instanceof ClassActionItem) { + ClassActionItem cti = (ClassActionItem) it; + List methods = new ArrayList<>(); + methods.addAll(cti.functions); + methods.addAll(cti.staticFunctions); + + for (GraphTargetItem gti : methods) { + if (gti instanceof FunctionActionItem) { + FunctionActionItem fun = (FunctionActionItem) gti; + if (fun.calculatedFunctionName instanceof DirectValueActionItem) { + DirectValueActionItem dvf = (DirectValueActionItem) fun.calculatedFunctionName; + String fname = dvf.toStringNoH(null); + String changed = deobfuscation.deobfuscateName(false, fname, false, "method", deobfuscated, renameType, selected); + if (changed != null) { + deobfuscated.put(DottedChain.parseWithSuffix(fname), DottedChain.parseWithSuffix(changed)); + } + } + } + } + + List vars = new ArrayList<>(); + for (MyEntry item : cti.vars) { + vars.add(item.getKey()); + } + for (MyEntry item : cti.staticVars) { + vars.add(item.getKey()); + } + for (GraphTargetItem gti : vars) { + if (gti instanceof DirectValueActionItem) { + DirectValueActionItem dvf = (DirectValueActionItem) gti; + String vname = dvf.toStringNoH(null); + String changed = deobfuscation.deobfuscateName(false, vname, false, "attribute", deobfuscated, renameType, selected); + if (changed != null) { + deobfuscated.put(DottedChain.parseWithSuffix(vname), DottedChain.parseWithSuffix(changed)); + } + } + } + + name = cti.className; + break; + } + if (it instanceof InterfaceActionItem) { + InterfaceActionItem ift = (InterfaceActionItem) it; + name = ift.name; + } + } + + if (name != null) { + int pos = 0; + while (name instanceof GetMemberActionItem) { + GetMemberActionItem mem = (GetMemberActionItem) name; + GraphTargetItem memberName = mem.memberName; + if (memberName instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) memberName; + String nameStr = dvt.toStringNoH(null); + if (classNameParts != null) { + if (classNameParts.length - 1 - pos < 0) { + break; + } + } + String changedNameStr = nameStr; + if (classNameParts != null) { + changedNameStr = classNameParts[classNameParts.length - 1 - pos]; + } + String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); + if (changedNameStr2 != null) { + changedNameStr = changedNameStr2; + } + ret++; + deobfuscated.put(DottedChain.parseWithSuffix(nameStr), DottedChain.parseWithSuffix(changedNameStr)); + pos++; + } + name = mem.object; + } + if (name instanceof GetVariableActionItem) { + GetVariableActionItem var = (GetVariableActionItem) name; + if (var.name instanceof DirectValueActionItem) { + DirectValueActionItem dvt = (DirectValueActionItem) var.name; + String nameStr = dvt.toStringNoH(null); + if (classNameParts != null) { + if (classNameParts.length - 1 - pos < 0) { + break; + } + } + String changedNameStr = nameStr; + if (classNameParts != null) { + changedNameStr = classNameParts[classNameParts.length - 1 - pos]; + } + String changedNameStr2 = deobfuscation.deobfuscateName(false, changedNameStr, pos == 0, pos == 0 ? "class" : "package", deobfuscated, renameType, selected); + if (changedNameStr2 != null) { + changedNameStr = changedNameStr2; + } + ret++; + deobfuscated.put(DottedChain.parseWithSuffix(nameStr), DottedChain.parseWithSuffix(changedNameStr)); + pos++; + } + } + } + t.setModified(true); + } + } + + for (GraphSourceItem fun : allFunctions) { + fc++; + informListeners("rename", "function " + fc + "/" + allFunctions.size()); + if (fun instanceof ActionDefineFunction) { + ActionDefineFunction f = (ActionDefineFunction) fun; + if (f.functionName.isEmpty()) { // anonymous function, leave as is + continue; + } + String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); + if (changed != null) { + f.replacedFunctionName = changed; + ret++; + } + } + if (fun instanceof ActionDefineFunction2) { + ActionDefineFunction2 f = (ActionDefineFunction2) fun; + if (f.functionName.isEmpty()) { // anonymous function, leave as is + continue; + } + String changed = deobfuscation.deobfuscateName(false, f.functionName, false, "function", deobfuscated, renameType, selected); + if (changed != null) { + f.replacedFunctionName = changed; + ret++; + } + } + } + + HashSet stringsNoVarH = new HashSet<>(); + List allVariableNamesDv = new ArrayList<>(); + for (MyEntry it : allVariableNames) { + allVariableNamesDv.add(it.getKey()); + } + for (DirectValueActionItem ti : allStrings.keySet()) { + if (!allVariableNamesDv.contains(ti)) { + stringsNoVarH.add(System.identityHashCode(allStrings.get(ti)) + "_" + ti.toStringNoH(allStrings.get(ti))); + } + } + + int vc = 0; + for (MyEntry it : allVariableNames) { + vc++; + String name = it.getKey().toStringNoH(it.getValue()); + String changed = deobfuscation.deobfuscateName(false, name, false, usageTypes.get(it.getKey()), deobfuscated, renameType, selected); + if (changed != null) { + boolean addNew = false; + String h = System.identityHashCode(it.getKey()) + "_" + name; + if (stringsNoVarH.contains(h)) { + addNew = true; + } + ActionPush pu = (ActionPush) it.getKey().getSrc(); + if (pu.replacement == null) { + pu.replacement = new ArrayList<>(); + pu.replacement.addAll(pu.values); + } + if (pu.replacement.get(it.getKey().pos) instanceof ConstantIndex) { + ConstantIndex ci = (ConstantIndex) pu.replacement.get(it.getKey().pos); + ConstantPool pool = it.getValue(); + if (pool == null) { + continue; + } + if (pool.constants == null) { + continue; + } + if (addNew) { + pool.constants.add(changed); + ci.index = pool.constants.size() - 1; + } else { + pool.constants.set(ci.index, changed); + } + } else { + pu.replacement.set(it.getKey().pos, changed); + } + ret++; + } + } + + for (ASMSource src : actionsMap.keySet()) { + actionsMap.get(src).removeNops(); + src.setActions(actionsMap.get(src)); + src.setModified(); + } + + deobfuscation.deobfuscateInstanceNames(false, deobfuscated, renameType, getTags(), selected); + return ret; + } + + public IdentifiersDeobfuscation getDeobfuscation() { + return deobfuscation; + } + + public void exportFla(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException, InterruptedException { + XFLExportSettings settings = new XFLExportSettings(); + settings.compressed = true; + exportXfl(handler, outfile, swfName, generator, generatorVerName, generatorVersion, parallel, version, settings); + } + + public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version) throws IOException, InterruptedException { + XFLExportSettings settings = new XFLExportSettings(); + settings.compressed = false; + exportXfl(handler, outfile, swfName, generator, generatorVerName, generatorVersion, parallel, version, settings); + } + + public void exportXfl(AbortRetryIgnoreHandler handler, String outfile, String swfName, String generator, String generatorVerName, String generatorVersion, boolean parallel, FLAVersion version, XFLExportSettings settings) throws IOException, InterruptedException { + new XFLConverter().convertSWF(handler, this, swfName, outfile, settings, generator, generatorVerName, generatorVersion, parallel, version); + clearAllCache(); + } + + public static AffineTransform matrixToTransform(MATRIX mat) { + return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(), + mat.getRotateSkew1Float(), mat.getScaleYFloat(), + mat.translateX, mat.translateY); + } + + public SerializableImage getFromCache(String key) { + if (frameCache.contains(key)) { + return frameCache.get(key); + } + return null; + } + + public byte[] getFromCache(SoundTag soundTag) { + if (soundCache.contains(soundTag)) { + return soundCache.get(soundTag); + } + return null; + } + + public void putToCache(String key, SerializableImage img) { + if (Configuration.useFrameCache.get()) { + frameCache.put(key, img); + } + } + + public void putToCache(SoundTag soundTag, byte[] data) { + soundCache.put(soundTag, data); + } + + public void clearImageCache() { + jtt = null; + frameCache.clear(); + rectCache.clear(); + for (Tag tag : getTags()) { + if (tag instanceof ImageTag) { + ((ImageTag) tag).clearCache(); + } else if (tag instanceof DefineCompactedFont) { + ((DefineCompactedFont) tag).rebuildShapeCache(); + } + } + } + + public void clearSoundCache() { + soundCache.clear(); + } + + public void clearScriptCache() { + as2PcodeCache.clear(); + as2Cache.clear(); + as3Cache.clear(); + IdentifiersDeobfuscation.clearCache(); + } + + public void clearReadOnlyListCache() { + readOnlyTags = null; + for (Tag tag : tags) { + if (tag instanceof DefineSpriteTag) { + ((DefineSpriteTag) tag).clearReadOnlyListCache(); + } + } + } + + public static void clearAllStaticCache() { + Cache.clearAll(); + Helper.clearShapeCache(); + System.gc(); + } + + public void clearAbcListCache() { + abcList = null; + } + + public void clearAllCache() { + characters = null; + characterIdTags = null; + clearAbcListCache(); + timeline = null; + clearReadOnlyListCache(); + clearImageCache(); + clearScriptCache(); + clearAllStaticCache(); + } + + public static void uncache(ASMSource src) { + if (src != null) { + SWF swf = src.getSwf(); + swf.as2Cache.remove(src); + swf.as2PcodeCache.remove(src); + } + } + + public static void uncache(ScriptPack pack) { + if (pack != null) { + pack.getSwf().as3Cache.remove(pack); + } + } + + public static boolean isCached(ASMSource src) { + return src.getSwf().as2Cache.contains(src); + } + + public static boolean isCached(ScriptPack pack) { + return pack.getSwf().as3Cache.contains(pack); + } + + public static ActionList getCachedActionList(ASMSource src, final List listeners) throws InterruptedException { + synchronized (src) { + SWF swf = src.getSwf(); + int deobfuscationMode = Configuration.autoDeobfuscate.get() ? 1 : 0; + if (swf != null && swf.as2PcodeCache.contains(src)) { + ActionList result = swf.as2PcodeCache.get(src); + if (result.deobfuscationMode == deobfuscationMode) { + return result; + } + } + + try { + ByteArrayRange actionBytes = src.getActionBytes(); + int prevLength = actionBytes.getPos(); + SWFInputStream rri = new SWFInputStream(swf, actionBytes.getArray()); + if (prevLength != 0) { + rri.seek(prevLength); + } + + int version = swf == null ? SWF.DEFAULT_VERSION : swf.version; + ActionList list = ActionListReader.readActionListTimeout(listeners, rri, version, prevLength, prevLength + actionBytes.getLength(), src.toString()/*FIXME?*/, deobfuscationMode); + list.fileData = actionBytes.getArray(); + list.deobfuscationMode = deobfuscationMode; + if (swf != null) { + swf.as2PcodeCache.put(src, list); + } + + return list; + } catch (InterruptedException ex) { + throw ex; + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + return new ActionList(); + } + } + } + + public static CachedScript getFromCache(ASMSource src) { + SWF swf = src.getSwf(); + if (swf.as2Cache.contains(src)) { + return swf.as2Cache.get(src); + } + + return null; + } + + public static CachedScript getCached(ASMSource src, ActionList actions) throws InterruptedException { + SWF swf = src.getSwf(); + if (swf.as2Cache.contains(src)) { + return swf.as2Cache.get(src); + } + + if (actions == null) { + actions = src.getActions(); + } + + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + writer.startFunction("!script"); + Action.actionsToSource(src, actions, src.toString()/*FIXME?*/, writer); + writer.endFunction(); + List hilights = writer.instructionHilights; + List methodHilights = writer.methodHilights; + List classHilights = writer.classHilights; + List specialHilights = writer.specialHilights; + + String srcNoHex = writer.toString(); + CachedScript res = new CachedScript(srcNoHex, hilights, methodHilights, classHilights, specialHilights); + swf.as2Cache.put(src, res); + return res; + } + + public static CachedDecompilation getCached(ScriptPack pack) throws InterruptedException { + SWF swf = pack.getSwf(); + if (swf.as3Cache.contains(pack)) { + return swf.as3Cache.get(pack); + } + + int scriptIndex = pack.scriptIndex; + ScriptInfo script = null; + if (scriptIndex > -1) { + script = pack.abc.script_info.get(scriptIndex); + } + boolean parallel = Configuration.parallelSpeedUp.get(); + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + pack.toSource(writer, script == null ? null : script.traits.traits, new ConvertData(), ScriptExportMode.AS, parallel); + HighlightedText hilightedCode = new HighlightedText(writer); + CachedDecompilation res = new CachedDecompilation(hilightedCode); + swf.as3Cache.put(pack, res); + + return res; + } + + public Cache getRectCache() { + return rectCache; + } + + public Cache getShapeExportDataCache() { + return shapeExportDataCache; + } + + public static RECT fixRect(RECT rect) { + RECT ret = new RECT(); + ret.Xmin = rect.Xmin; + ret.Xmax = rect.Xmax; + ret.Ymin = rect.Ymin; + ret.Ymax = rect.Ymax; + + if (ret.Xmax <= 0) { + ret.Xmax = ret.getWidth(); + ret.Xmin = 0; + } + if (ret.Ymax <= 0) { + ret.Ymax = ret.getHeight(); + ret.Ymin = 0; + } + if (ret.Xmin < 0) { + ret.Xmax += (-ret.Xmin); + ret.Xmin = 0; + } + if (ret.Ymin < 0) { + ret.Ymax += (-ret.Ymin); + ret.Ymin = 0; + } + + if (ret.getWidth() < 1 || ret.getHeight() < 1) { + ret.Xmin = 0; + ret.Ymin = 0; + ret.Xmax = 20; + ret.Ymax = 20; + } + return ret; + } + + public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, Point cursorPosition, int mouseButton, RECT displayRect, Matrix transformation, ColorTransform colorTransform, Color backGroundColor, double zoom) { + if (timeline.getFrameCount() == 0) { + return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB_PRE); + } + + RECT rect = displayRect; + SerializableImage image = new SerializableImage((int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1, + (int) (rect.getHeight() * zoom / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB_PRE); + if (backGroundColor == null) { + image.fillTransparent(); + } else { + Graphics2D g = (Graphics2D) image.getBufferedImage().getGraphics(); + g.setComposite(AlphaComposite.Src); + g.setColor(backGroundColor); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + } + + Matrix m = transformation.clone(); + m.translate(-rect.Xmin * zoom, -rect.Ymin * zoom); + m.scale(zoom); + RenderContext renderContext = new RenderContext(); + renderContext.cursorPosition = cursorPosition; + renderContext.mouseButton = mouseButton; + timeline.toImage(frame, time, renderContext, image, false, m, transformation, m, colorTransform); + + return image; + } + + private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) { + Map stage = new HashMap<>(); + Set dependingChars = new HashSet<>(); + Timelined timelined = timeline.timelined; + ReadOnlyTagList tags = timelined.getTags(); + if (toRemove instanceof CharacterTag) { + int characterId = ((CharacterTag) toRemove).getCharacterId(); + + if (characterId != 0) { + dependingChars.add(characterId); + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof CharacterIdTag) { + CharacterIdTag c = (CharacterIdTag) t; + Set needed = new HashSet<>(); + t.getNeededCharacters(needed); + if (needed.contains(characterId)) { + dependingChars.add(c.getCharacterId()); + } + } + } + } + } + + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof RemoveTag) { + RemoveTag rt = (RemoveTag) t; + int depth = rt.getDepth(); + if (stage.containsKey(depth)) { + int currentCharId = stage.get(depth); + stage.remove(depth); + if (dependingChars.contains(currentCharId)) { + timelined.removeTag(i); + i--; + continue; + } + } + } + if (t instanceof PlaceObjectTypeTag) { + PlaceObjectTypeTag po = (PlaceObjectTypeTag) t; + int placeCharId = po.getCharacterId(); + int depth = po.getDepth(); + if (placeCharId != 0) { + stage.put(depth, placeCharId); + if (dependingChars.contains(placeCharId)) { + timelined.removeTag(i); + i--; + continue; + } + } + } + if (t instanceof CharacterIdTag) { + CharacterIdTag c = (CharacterIdTag) t; + if (dependingChars.contains(c.getCharacterId())) { + timelined.removeTag(i); + i--; + continue; + } + } + Set needed = new HashSet<>(); + t.getNeededCharacters(needed); + for (int dep : dependingChars) { + if (needed.contains(dep)) { + timelined.removeTag(i); + i--; + //continue; + } + } + if (t == toRemove) { + timelined.removeTag(i); + i--; + continue; + } + if (t instanceof Timelined) { + removeTagWithDependenciesFromTimeline(toRemove, ((Timelined) t).getTimeline()); + } + } + } + + private boolean removeTagFromTimeline(Tag toRemove, Timeline timeline) { + boolean modified = false; + int characterId = -1; + if (toRemove instanceof CharacterTag) { + characterId = ((CharacterTag) toRemove).getCharacterId(); + modified = timeline.removeCharacter(characterId); + } + Timelined timelined = timeline.timelined; + ReadOnlyTagList tags = timelined.getTags(); + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t == toRemove) { + timelined.removeTag(t); + i--; + continue; + } + + if (toRemove instanceof CharacterTag) { + if (t.removeCharacter(characterId)) { + modified = true; + i = -1; + continue; + } + } + + if (t instanceof DefineSpriteTag) { + DefineSpriteTag spr = (DefineSpriteTag) t; + boolean sprModified = removeTagFromTimeline(toRemove, spr.getTimeline()); + if (sprModified) { + spr.setModified(true); + } + modified |= sprModified; + } + } + return modified; + } + + public void removeTags(Collection tags, boolean removeDependencies) { + Set timelineds = new HashSet<>(); + for (Tag tag : tags) { + Timelined timelined = tag.getTimelined(); + timelineds.add(timelined); + removeTagInternal(timelined, tag, removeDependencies); + } + + for (Timelined timelined : timelineds) { + resetTimelines(timelined); + } + + updateCharacters(); + clearImageCache(); + } + + @Override + public void removeTag(int index) { + setModified(true); + tags.remove(index); + updateCharacters(); + } + + @Override + public void removeTag(Tag tag) { + setModified(true); + tags.remove(tag); + updateCharacters(); + } + + public void removeTag(Tag tag, boolean removeDependencies) { + Timelined timelined = tag.getTimelined(); + removeTagInternal(timelined, tag, removeDependencies); + resetTimelines(timelined); + updateCharacters(); + clearImageCache(); + } + + private void removeTagInternal(Timelined timelined, Tag tag, boolean removeDependencies) { + if (tag instanceof ShowFrameTag || ShowFrameTag.isNestedTagType(tag.getId())) { + timelined.removeTag(tag); + timelined.setModified(true); + timelined.resetTimeline(); + } else // timeline should be always the swf here + { + if (removeDependencies) { + removeTagWithDependenciesFromTimeline(tag, timelined.getTimeline()); + timelined.setModified(true); + } else { + boolean modified = removeTagFromTimeline(tag, timelined.getTimeline()); + if (modified) { + timelined.setModified(true); + } + } + } + } + + @Override + public ReadOnlyTagList getTags() { + if (readOnlyTags == null) { + readOnlyTags = new ReadOnlyTagList(tags); + } + + return readOnlyTags; + } + + public ReadOnlyTagList getLocalTags() { + List localTags = new ArrayList<>(); + for (Tag t : tags) { + if (!t.isImported()) { + localTags.add(t); + } + } + return new ReadOnlyTagList(localTags); + } + + /** + * Adds a tag to the SWF + * + * @param tag + */ + @Override + public void addTag(Tag tag) { + setModified(true); + tags.add(tag); + updateCharacters(); + } + + /** + * Adds a tag to the SWF + * + * @param index + * @param tag + */ + @Override + public void addTag(int index, Tag tag) { + setModified(true); + tags.add(index, tag); + updateCharacters(); + } + + /** + * Replaces a tag in the SWF + * + * @param oldTag + * @param newTag + */ + public void replaceTag(Tag oldTag, Tag newTag) { + setModified(true); + int index = tags.indexOf(oldTag); + if (index != -1) { + tags.set(index, newTag); + updateCharacters(); + } + } + + /** + * Adds a tag to the SWF If targetTreeItem is: - Frame: adds the tag to the + * Frame. Frame can be a frame of the main timeline or a DefineSprite frame + * - DefineSprite: adds the tag to the end of the DefineSprite's tag list - + * Any other tag in the SWF: adds the new tag exactly before the specified + * tag - Other: adds the tag to the end of the SWF's tag list + * + * @param tag + * @param targetTreeItem + */ + public void addTag(Tag tag, TreeItem targetTreeItem) { + SWF swf = tag.getSwf(); + Frame frame = targetTreeItem instanceof Frame ? (Frame) targetTreeItem : null; + Timelined timelined; + if (frame != null) { + timelined = frame.timeline.timelined; + } else { + timelined = swf.getTimelined(targetTreeItem); + } + + tag.setTimelined(timelined); + + ReadOnlyTagList tags = timelined.getTags(); + + int index; + if (frame != null) { + if (frame.showFrameTag != null) { + index = tags.indexOf(frame.showFrameTag); + } else { + index = -1; + } + } else if (timelined instanceof DefineSpriteTag) { + index = -1; + } else if (targetTreeItem instanceof Tag) { + if (tag instanceof CharacterIdTag && !(tag instanceof CharacterTag) && targetTreeItem instanceof CharacterTag) { + ((CharacterIdTag) tag).setCharacterId(((CharacterTag) targetTreeItem).getCharacterId()); + } + + index = tags.indexOf((Tag) targetTreeItem); // todo: honfika: why not index + 1? + } else { + index = -1; + if (tag instanceof CharacterTag) { + // add before the last ShowFrame tag + for (int i = tags.size() - 1; i >= 0; i--) { + if (tags.get(i) instanceof ShowFrameTag) { + index = i; + break; + } + } + } + } + + if (index > -1) { + timelined.addTag(index, tag); + } else { + timelined.addTag(tag); + } + + timelined.resetTimeline(); + + if (timelined instanceof DefineSpriteTag) { + DefineSpriteTag sprite = (DefineSpriteTag) timelined; + sprite.frameCount = timelined.getTimeline().getFrameCount(); + } + } + + public Timelined getTimelined(TreeItem treeItem) { + if (treeItem instanceof Frame) { + return ((Frame) treeItem).timeline.timelined; + } + + if (treeItem instanceof DefineSpriteTag) { + return (DefineSpriteTag) treeItem; + } + + return treeItem.getSwf(); + } + + public void packCharacterIds() { + int maxId = getNextCharacterId(); + int id = 1; + for (int i = 1; i < maxId; i++) { + CharacterTag charactertag = getCharacter(i); + if (charactertag != null) { + if (i != id) { + replaceCharacter(i, id); + } + id++; + } else { + // make sure that the id is not referenced in the tags + replaceCharacter(i, 0); + } + } + } + + public void sortCharacterIds() { + int maxId = Math.max(tags.size(), getNextCharacterId()); + int id = maxId; + // first set the chatacter ids to surely not used ids + for (Tag tag : getTags()) { + if (tag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) tag; + replaceCharacter(characterTag.getCharacterId(), id++); + } + } + // then set them to 1,2,3... + id = 1; + for (Tag tag : getTags()) { + if (tag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) tag; + replaceCharacter(characterTag.getCharacterId(), id++); + } + } + } + + public boolean replaceCharacter(int oldCharacterId, int newCharacterId) { + boolean modified = false; + for (Tag tag : getTags()) { + boolean modified2 = false; + if (tag instanceof CharacterIdTag) { + CharacterIdTag characterIdTag = (CharacterIdTag) tag; + if (characterIdTag.getCharacterId() == oldCharacterId) { + characterIdTag.setCharacterId(newCharacterId); + modified2 = true; + } + } + modified2 |= tag.replaceCharacter(oldCharacterId, newCharacterId); + if (modified2) { + tag.setModified(true); + } + modified |= modified2; + } + return modified; + } + + public void replaceCharacterTags(CharacterTag characterTag, int newCharacterId) { + int characterId = characterTag.getCharacterId(); + CharacterTag newCharacter = getCharacter(newCharacterId); + newCharacter.setCharacterId(characterId); + characterTag.setCharacterId(newCharacterId); + newCharacter.setModified(true); + characterTag.setModified(true); + + assignExportNamesToSymbols(); + assignClassesToSymbols(); + clearImageCache(); + updateCharacters(); + } + + @Override + public String toString() { + return getShortFileName(); + } + + public void deobfuscate(DeobfuscationLevel level) throws InterruptedException { + List atags = getAbcList(); + + for (ABCContainerTag tag : atags) { + if (level == DeobfuscationLevel.LEVEL_REMOVE_DEAD_CODE) { + tag.getABC().removeDeadCode(); + } else if (level == DeobfuscationLevel.LEVEL_REMOVE_TRAPS) { + tag.getABC().removeTraps(); + } else if (level == DeobfuscationLevel.LEVEL_RESTORE_CONTROL_FLOW) { + tag.getABC().removeTraps(); + } + + ((Tag) tag).setModified(true); + } + } + + /** + * Enables debugging. Adds tags to enable debugging and optinally injects + * debugline and debugfile instructions to AS3 code by decompiling it first + * + * @param injectAS3Code Modify AS3 code with debugfile / debugline ? + * @param decompileDir Directory to virtual decompile (will affect + * debugfile) + */ + public void enableDebugging(boolean injectAS3Code, File decompileDir) { + enableDebugging(injectAS3Code, decompileDir, false); + } + + /** + * Enables debugging. Adds tags to enable debugging. + */ + public void enableDebugging() { + enableDebugging(false, null, false); + } + + /** + * Enables debugging. Adds tags to enable debugging and injects debugline + * and debugfile instructions to AS3 code. Optionally enables Telemetry + * + * @param injectAS3Code Modify AS3 code with debugfile / debugline ? + * @param decompileDir Directory to virtual decompile (will affect + * debugfile) + * @param telemetry Enable telemetry info? + */ + public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry) { + enableDebugging(injectAS3Code, decompileDir, telemetry, false); + } + + /** + * Injects debugline and debugfile instructions to AS3 P-code (lines of + * P-code) + */ + public void injectAS3PcodeDebugInfo() { + List packs = getAS3Packs(); + for (ScriptPack s : packs) { + int abcIndex = s.allABCs.indexOf(s.abc); + if (s.isSimple) { + s.injectPCodeDebugInfo(abcIndex); + } + } + } + + /** + * Injects debugline and debugfile instructions to AS3 code + * + * @param decompileDir Directory to set file information paths + */ + public void injectAS3DebugInfo(File decompileDir) { + List packs = getAS3Packs(); + for (ScriptPack s : packs) { + if (s.isSimple) { + s.injectDebugInfo(decompileDir); + } + } + } + + /** + * Enables debugging. Adds tags to enable debugging and injects debugline + * and debugfile instructions to AS3 code. Optionally enables Telemetry + * + * @param injectAS3Code Modify AS3 code with debugfile / debugline ? + * @param decompileDir Directory to virtual decompile (will affect + * debugfile) + * @param telemetry Enable telemetry info? + * @param pcodeLevel inject Pcode lines instead of decompiled lines + */ + public void enableDebugging(boolean injectAS3Code, File decompileDir, boolean telemetry, boolean pcodeLevel) { + + if (injectAS3Code) { + if (pcodeLevel) { + injectAS3PcodeDebugInfo(); + } else { + injectAS3DebugInfo(decompileDir); + } + } + + int pos = 0; + + boolean hasEnabled = false; + + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if (t instanceof MetadataTag) { + pos = i + 1; + } + if (t instanceof FileAttributesTag) { + pos = i + 1; + } + if (version >= 6 && (t instanceof EnableDebugger2Tag)) { + hasEnabled = true; + break; + } + if (version == 5 && (t instanceof EnableDebuggerTag)) { + hasEnabled = true; + break; + } + if (version < 5 && (t instanceof ProtectTag)) { + hasEnabled = true; + break; + } + } + + if (!hasEnabled) { + if (version >= 6) { + tags.add(pos, new EnableDebugger2Tag(this)); + } else if (version == 5) { + tags.add(pos, new EnableDebuggerTag(this)); + } else { + tags.add(pos, new ProtectTag(this)); + } + } + + getOrAddDebugId(); + } + + /** + * Finds DebugID tag + * + * @return the tag or null if not found + */ + public DebugIDTag getDebugId() { + for (Tag t : getTags()) { + if (t instanceof DebugIDTag) { + return (DebugIDTag) t; + } + } + return null; + } + + /** + * Finds DebugID tag and generates new one if none exists + * + * @return the tag or null if there is not debugging enabled in the swf file + */ + public DebugIDTag getOrAddDebugId() { + DebugIDTag r = getDebugId(); + if (r == null) { + for (int i = 0; i < tags.size(); i++) { + Tag t = tags.get(i); + if ((t instanceof EnableDebuggerTag) || (t instanceof EnableDebugger2Tag)) { + r = new DebugIDTag(this); + tags.add(i + 1, r); + new Random().nextBytes(r.debugId); + break; + } + } + } + return r; + } + + public boolean generatePCodeSwdFile(File file, Map> breakpoints) throws IOException { + DebugIDTag dit = getDebugId(); + if (dit == null) { + return false; + } + List items = new ArrayList<>(); + Map asms = getASMs(true); + + try { + items.add(new SWD.DebugId(dit.debugId)); + + } catch (Throwable t) { + logger.log(Level.SEVERE, "message", t); + return false; + } + + int moduleId = 0; + List names = new ArrayList<>(asms.keySet()); + Collections.sort(names); + for (String name : names) { + moduleId++; + String sname = "#PCODE " + name; + int bitmap = SWD.bitmapAction; + items.add(new SWD.DebugScript(moduleId, bitmap, sname, "")); + + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + try { + asms.get(name).getASMSource(ScriptExportMode.PCODE, writer, asms.get(name).getActions()); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + List hls = writer.instructionHilights; + + Map offsetToLine = new TreeMap<>(); + String txt = writer.toString(); + txt = txt.replace("\r", ""); + int line = 1; + for (int i = 0; i < txt.length(); i++) { + Highlighting h = Highlighting.searchPos(hls, i); + if (h != null) { + int of = (int) h.getProperties().fileOffset; + if (of > -1 && !offsetToLine.containsKey(of) && !offsetToLine.containsValue(line)) { + offsetToLine.put(of, line); + } + } + if (txt.charAt(i) == '\n') { + line++; + } + } + + for (int ofs : offsetToLine.keySet()) { + items.add(new SWD.DebugOffset(moduleId, offsetToLine.get(ofs), ofs)); + } + + if (breakpoints.containsKey(sname)) { + Set bplines = breakpoints.get(sname); + for (int bpline : bplines) { + if (offsetToLine.containsValue(bpline)) { + try { + SWD.DebugBreakpoint dbp = new SWD.DebugBreakpoint(moduleId, bpline); + items.add(dbp); + } catch (IllegalArgumentException iex) { + logger.log(Level.WARNING, "Cannot generate breakpoint to SWD: {0}", iex.getMessage()); + } + } + } + } + } + + SWD swd = new SWD(7, items); + try (FileOutputStream fis = new FileOutputStream(file)) { + swd.saveTo(fis); + } + return true; + } + + public boolean generateSwdFile(File file, Map> breakpoints) throws IOException { + DebugIDTag dit = getDebugId(); + if (dit == null) { + return false; + } + List items = new ArrayList<>(); + Map asms = getASMs(true); + + try { + items.add(new SWD.DebugId(dit.debugId)); + + int moduleId = 0; + List names = new ArrayList<>(asms.keySet()); + Collections.sort(names); + for (String name : names) { + List regitems = new ArrayList<>(); + moduleId++; + CachedScript cs; + try { + cs = SWF.getCached(asms.get(name), asms.get(name).getActions()); + } catch (InterruptedException ex) { + return false; + } + String txt = cs.text.replace("\r", ""); + int line = 1; + Map lineToOffset = new HashMap<>(); + Map regNames = new HashMap<>(); + + for (int pos = 0; pos < txt.length(); pos++) { + Highlighting h = Highlighting.searchPos(cs.hilights, pos); + if (h != null) { + + int firstLineOffset = (int) h.getProperties().firstLineOffset; + if (firstLineOffset > -1 && h.getProperties().declaration && h.getProperties().regIndex > -1 && (!regNames.containsKey(h.getProperties().regIndex) || !regNames.get(h.getProperties().regIndex).equals(h.getProperties().localName))) { + regNames.put(h.getProperties().regIndex, h.getProperties().localName); + + List curRegIndexes = new ArrayList<>(regNames.keySet()); + List curRegNames = new ArrayList<>(); + for (int i = 0; i < curRegIndexes.size(); i++) { + curRegNames.add(regNames.get(curRegIndexes.get(i))); + } + regitems.add(new SWD.DebugRegisters((int) h.getProperties().firstLineOffset, curRegIndexes, curRegNames)); + } + + if (firstLineOffset != -1 && !lineToOffset.containsKey(line)) { + lineToOffset.put(line, firstLineOffset); + } + } + if (txt.charAt(pos) == '\n') { + line++; + } + } + + Map offSetToLine = new TreeMap<>(); + for (Map.Entry en : lineToOffset.entrySet()) { + offSetToLine.put(en.getValue(), en.getKey()); + } + + //final String NONAME = "[No instance name assigned]"; + String sname = name; + int bitmap = SWD.bitmapAction; + /* Matcher m; + int bitmap = SWD.bitmapAction; + m = Pattern.compile("^\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname); + if (m.matches()) { + //TODO: scenes?, layers? + sname = "Actions for Scene 1: Frame " + m.group(1) + " of Layer Name Layer 1"; + } else if ((m = Pattern.compile("^\\\\__Packages\\\\(.*)$").matcher(sname)).matches()) { + sname = m.group(1).replace("\\", ".") + ": .\\" + m.group(1) + ".as"; + } else { + continue; //FIXME! + } + m = Pattern.compile("^\\\\DefineSprite_([0-9])+\\\\frame_([0-9]+)\\\\DoAction$").matcher(sname); + if (m.matches()) { + //TODO: layers? + //sname = "Actions for Symbol " + m.group(1) + ": Frame " + m.group(2) + " of Layer Name Layer 1"; + continue; //FIXME! + } + + //TODO: handle onxxx together ? + m = Pattern.compile("^\\\\DefineButton2?_([0-9]+)\\\\on\\(.*$").matcher(sname); + if (m.matches()) { + //bitmap = SWD.bitmapOnAction; + //sname = "Actions for " + NONAME + " (Symbol " + m.group(1) + ")"; + continue; //FIXME! + } + + //TODO: handle onClipEvent together ? + m = Pattern.compile("^\\\\frame_([0-9]+)\\\\PlaceObject[2-3]?_([0-9]+)_[^\\\\]*\\\\onClipEvent\\(.*$").matcher(sname); + if (m.matches()) { + //bitmap = SWD.bitmapOnClipAction; + //sname = "Actions for " + NONAME + " (Symbol " + m.group(2) + ")"; + continue; //FIXME! + }//*/ + + items.add(new SWD.DebugScript(moduleId, bitmap, sname, txt)); + for (int ofs : offSetToLine.keySet()) { + items.add(new SWD.DebugOffset(moduleId, offSetToLine.get(ofs), ofs)); + } + if (breakpoints.containsKey(name)) { + Set bplines = breakpoints.get(name); + for (int bpline : bplines) { + if (lineToOffset.containsKey(bpline)) { + try { + SWD.DebugBreakpoint dbp = new SWD.DebugBreakpoint(moduleId, bpline); + items.add(dbp); + } catch (IllegalArgumentException iex) { + logger.log(Level.WARNING, "Cannot generate breakpoint to SWD: {0}", iex.getMessage()); + } + } + } + } + items.addAll(regitems); + //moduleId++; + } + //items.addAll(swdOffsets); + //items.addAll(swfBps); + + } catch (Throwable t) { + logger.log(Level.SEVERE, "message", t); + return false; + } + SWD swd = new SWD(7, items); + try (FileOutputStream fis = new FileOutputStream(file)) { + swd.saveTo(fis); + } + return true; + } + + public boolean enableTelemetry(String password) { + + EnableTelemetryTag et = getEnableTelemetry(); + + if (et == null) { + FileAttributesTag fat = getFileAttributes(); + if (fat == null) { + return false; + } + int insertTo = tags.indexOf(fat) + 1; + MetadataTag mt = getMetadata(); + if (mt != null) { + insertTo = tags.indexOf(mt) + 1; + } + + et = new EnableTelemetryTag(this); + tags.add(insertTo, et); + } + et.setPassword(password); + //TODO: SWFs with tag 92 (signed) are unsupported + return true; + } + + public String getFlexMainClass(List ignoredClasses, List ignoredNs) { + String documentClass = getDocumentClass(); + + ScriptPack documentPack = null; + for (ScriptPack item : getAS3Packs()) { + if (item.getClassPath().toString().equals(documentClass)) { + documentPack = item; + break; + } + } + + if (documentPack != null) { + if (!documentPack.traitIndices.isEmpty()) { + Trait firstTrait = documentPack.abc.script_info.get(documentPack.scriptIndex).traits.traits.get(documentPack.traitIndices.get(0)); + if (firstTrait instanceof TraitClass) { + int cindex = ((TraitClass) firstTrait).class_info; + Multiname superName = documentPack.abc.constants.getMultiname(documentPack.abc.instance_info.get(cindex).super_index); + String parentClass = superName.getNameWithNamespace(documentPack.abc.constants, true).toRawString(); + if ("mx.managers.SystemManager".equals(parentClass)) { + for (Trait t : documentPack.abc.instance_info.get(cindex).instance_traits.traits) { + if ((t instanceof TraitMethodGetterSetter) && "info".equals(t.getName(documentPack.abc).getName(documentPack.abc.constants, new ArrayList<>(), true, true))) { + + int mi = ((TraitMethodGetterSetter) t).method_info; + try { + documentPack.abc.findBody(mi).convert(new ConvertData(), "??", ScriptExportMode.AS, true, mi, documentPack.scriptIndex, cindex, documentPack.abc, t, new ScopeStack(), 0, new NulWriter(), new ArrayList<>(), new ArrayList<>(), true); + List infos = documentPack.abc.findBody(mi).convertedItems; + if (!infos.isEmpty()) { + if (infos.get(0) instanceof IfItem) { + IfItem ift = ((IfItem) infos.get(0)); + if (!ift.onTrue.isEmpty()) { + if (ift.onTrue.get(0) instanceof InitPropertyAVM2Item) { + if (ift.onTrue.get(0).value instanceof NewObjectAVM2Item) { + NewObjectAVM2Item no = (NewObjectAVM2Item) ift.onTrue.get(0).value; + List compiledLocales = new ArrayList<>(); + List compiledResourceBundleNames = new ArrayList<>(); + List mixins = new ArrayList<>(); + String mainClassName = null; + //currentDomain,preloader + /*double width = 0; + double height = 0; + */ + for (NameValuePair nvp : no.pairs) { + if (nvp.name instanceof StringAVM2Item) { + String n = ((StringAVM2Item) nvp.name).getValue(); + switch (n) { + case "compiledLocales": + if (nvp.value instanceof NewArrayAVM2Item) { + NewArrayAVM2Item na = (NewArrayAVM2Item) nvp.value; + for (GraphTargetItem tv : na.values) { + compiledLocales.add("" + tv.getResult()); + } + } + break; + case "compiledResourceBundleNames": + if (nvp.value instanceof NewArrayAVM2Item) { + NewArrayAVM2Item na = (NewArrayAVM2Item) nvp.value; + for (GraphTargetItem tv : na.values) { + compiledResourceBundleNames.add("" + tv.getResult()); + } + } + break; + case "mixins": + if (nvp.value instanceof NewArrayAVM2Item) { + NewArrayAVM2Item na = (NewArrayAVM2Item) nvp.value; + for (GraphTargetItem tv : na.values) { + mixins.add("" + tv.getResult()); + } + } + break; + /*case "width": + width = Double.parseDouble("" + nvp.value.getResult()); + break; + case "height": + height = Double.parseDouble("" + nvp.value.getResult()); + break;*/ + case "mainClassName": + mainClassName = "" + nvp.value.getResult(); + break; + } + } + } + + ignoredClasses.add(documentClass); + for (String loc : compiledLocales) { + ignoredClasses.add(loc + "$" + "controls" + "_properties"); + for (String res : compiledResourceBundleNames) { + ignoredClasses.add(loc + "$" + res + "_properties"); + } + } + ignoredClasses.addAll(mixins); + + //find internal classes used in mixins + for (ScriptPack p : getAS3Packs()) { + for (String m : mixins) { + if (m.equals(p.getClassPath().toRawString())) { + for (int ti : p.traitIndices) { + Trait tr = p.abc.script_info.get(p.scriptIndex).traits.traits.get(ti); + if (tr instanceof TraitClass) { + int ci = ((TraitClass) tr).class_info; + int cinit = p.abc.class_info.get(ci).cinit_index; + p.abc.findBody(cinit).convert(new ConvertData(), "??", ScriptExportMode.AS, true, cinit, p.scriptIndex, cindex, p.abc, t, new ScopeStack(), 0, new NulWriter(), new ArrayList<>(), new ArrayList<>(), true); + List cinitBody = p.abc.findBody(cinit).convertedItems; + for (GraphTargetItem cit : cinitBody) { + if (cit instanceof SetPropertyAVM2Item) { + if (cit.value instanceof GetLexAVM2Item) { + GetLexAVM2Item gl = (GetLexAVM2Item) cit.value; + ignoredClasses.add(gl.propertyName.getNameWithNamespace(p.abc.constants, true).toRawString()); + } + } + } + } + } + } + } + } + ignoredNs.add("mx"); + ignoredNs.add("spark"); + ignoredNs.add("flashx"); + return mainClassName; + } + } + } + } + } + } catch (InterruptedException e) { + //ignore + } + } + } + } + } + } + } + return null; + } + + @Override + public void replaceTag(int index, Tag newTag) { + removeTag(index); + addTag(index, newTag); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSourceInfo.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSourceInfo.java index 7b46e778f..6e2558e32 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSourceInfo.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWFSourceInfo.java @@ -1,105 +1,105 @@ -/* - * Copyright (C) 2010-2016 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash; - -import com.jpexs.decompiler.flash.iggy.conversion.IggySwfBundle; -import com.jpexs.helpers.Path; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * - * @author JPEXS - */ -public class SWFSourceInfo { - - private final InputStream inputStream; - - private String file; - - private final String fileTitle; - - public SWFSourceInfo(InputStream inputStream, String file, String fileTitle) { - this.inputStream = inputStream; - this.file = file; - this.fileTitle = fileTitle; - } - - public InputStream getInputStream() { - return inputStream; - } - - public String getFile() { - return file; - } - - public void setFile(String file) { - this.file = file; - } - - public String getFileTitle() { - return fileTitle; - } - - /** - * Get title of the file - * - * @return file title - */ - public String getFileTitleOrName() { - if (fileTitle != null) { - return fileTitle; - } - return file; - } - - public boolean isBundle() { - if (inputStream == null) { - File fileObj = new File(file); - String fileName = fileObj.getName(); - if (fileName.startsWith("asdec_") && fileName.endsWith(".tmp")) { - return false; - } - String extension = Path.getExtension(fileObj); - return extension == null || !(extension.equals(".swf") || extension.equals(".gfx")); - } - return false; - } - - public SWFBundle getBundle(boolean noCheck, SearchMode searchMode) throws IOException { - if (!isBundle()) { - return null; - } - - String extension = Path.getExtension(new File(file)); - if (extension != null) { - switch (extension) { - case ".swc": - return new SWC(new File(file)); - case ".zip": - return new ZippedSWFBundle(new File(file)); - case ".iggy": - return new IggySwfBundle(new File(file)); - } - } - - return new BinarySWFBundle(new BufferedInputStream(new FileInputStream(file)), noCheck, searchMode); - } -} +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash; + +import com.jpexs.decompiler.flash.iggy.conversion.IggySwfBundle; +import com.jpexs.helpers.Path; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author JPEXS + */ +public class SWFSourceInfo { + + private final InputStream inputStream; + + private String file; + + private final String fileTitle; + + public SWFSourceInfo(InputStream inputStream, String file, String fileTitle) { + this.inputStream = inputStream; + this.file = file; + this.fileTitle = fileTitle; + } + + public InputStream getInputStream() { + return inputStream; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getFileTitle() { + return fileTitle; + } + + /** + * Get title of the file + * + * @return file title + */ + public String getFileTitleOrName() { + if (fileTitle != null) { + return fileTitle; + } + return file; + } + + public boolean isBundle() { + if (inputStream == null) { + File fileObj = new File(file); + String fileName = fileObj.getName(); + if (fileName.startsWith("asdec_") && fileName.endsWith(".tmp")) { + return false; + } + String extension = Path.getExtension(fileObj); + return extension == null || !(extension.equals(".swf") || extension.equals(".gfx")); + } + return false; + } + + public SWFBundle getBundle(boolean noCheck, SearchMode searchMode) throws IOException { + if (!isBundle()) { + return null; + } + + String extension = Path.getExtension(new File(file)); + if (extension != null) { + switch (extension) { + case ".swc": + return new SWC(new File(file)); + case ".zip": + return new ZippedSWFBundle(new File(file)); + case ".iggy": + return new IggySwfBundle(new File(file)); + } + } + + return new BinarySWFBundle(new BufferedInputStream(new FileInputStream(file)), noCheck, searchMode); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/AbstractDataStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/AbstractDataStream.java index 4a15a3135..66b21399a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/AbstractDataStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/AbstractDataStream.java @@ -1,110 +1,110 @@ -package com.jpexs.decompiler.flash.iggy; - -import java.io.EOFException; -import java.io.IOException; - -/** - * - * @author JPEXS - */ -public abstract class AbstractDataStream { - - /** - * Available bytes - * - * @return null if unknown, long value otherwise - */ - public abstract Long available(); - - public abstract long position(); - - public abstract boolean is64(); - - protected long readUI64() throws IOException { - try { - return (readUI32() + (readUI32() << 32)) & 0xffffffffffffffffL; - } catch (EOFException ex) { - return -1; - } - } - - protected boolean writeUI64(long val) throws IOException { - write((int) (val & 0xff)); - write((int) ((val >> 8) & 0xff)); - write((int) ((val >> 16) & 0xff)); - write((int) ((val >> 24) & 0xff)); - - write((int) ((val >> 32) & 0xff)); - write((int) ((val >> 40) & 0xff)); - write((int) ((val >> 48) & 0xff)); - write((int) ((val >> 56) & 0xff)); - return true; - } - - protected long readUI32() throws IOException { - try { - return (readUI8() + (readUI8() << 8) + (readUI8() << 16) + (readUI8() << 24)); - } catch (EOFException ex) { - return -1; - } - } - - protected boolean writeUI32(long val) throws IOException { - write((int) (val & 0xff)); - write((int) ((val >> 8) & 0xff)); - write((int) ((val >> 16) & 0xff)); - write((int) ((val >> 24) & 0xff)); - return true; - } - - protected int readUI16() throws IOException { - try { - return (readUI8() + (readUI8() << 8)) & 0xffff; - } catch (EOFException ex) { - return -1; - } - } - - protected boolean writeUI16(int val) throws IOException { - write(val & 0xff); - write((val >> 8) & 0xff); - return true; - } - - protected int readUI8() throws IOException { - try { - return read() & 0xff; - } catch (EOFException ex) { - return -1; - } - } - - protected boolean writeUI8(int val) throws IOException { - write(val); - return true; - } - - protected float readFloat() throws IOException { - return Float.intBitsToFloat((int) readUI32()); - } - - protected boolean writeFloat(float val) throws IOException { - return writeUI32(Float.floatToIntBits(val)); - } - - protected byte[] readBytes(int numBytes) throws IOException { - byte[] ret = new byte[numBytes]; - for (int i = 0; i < numBytes; i++) { - ret[i] = (byte) read(); - } - return ret; - } - - protected abstract int read() throws IOException; - - protected abstract void seek(long pos, SeekMode mode) throws IOException; - - protected void write(int val) throws IOException { - //nothing - } -} +package com.jpexs.decompiler.flash.iggy; + +import java.io.EOFException; +import java.io.IOException; + +/** + * + * @author JPEXS + */ +public abstract class AbstractDataStream { + + /** + * Available bytes + * + * @return null if unknown, long value otherwise + */ + public abstract Long available(); + + public abstract long position(); + + public abstract boolean is64(); + + protected long readUI64() throws IOException { + try { + return (readUI32() + (readUI32() << 32)) & 0xffffffffffffffffL; + } catch (EOFException ex) { + return -1; + } + } + + protected boolean writeUI64(long val) throws IOException { + write((int) (val & 0xff)); + write((int) ((val >> 8) & 0xff)); + write((int) ((val >> 16) & 0xff)); + write((int) ((val >> 24) & 0xff)); + + write((int) ((val >> 32) & 0xff)); + write((int) ((val >> 40) & 0xff)); + write((int) ((val >> 48) & 0xff)); + write((int) ((val >> 56) & 0xff)); + return true; + } + + protected long readUI32() throws IOException { + try { + return (readUI8() + (readUI8() << 8) + (readUI8() << 16) + (readUI8() << 24)); + } catch (EOFException ex) { + return -1; + } + } + + protected boolean writeUI32(long val) throws IOException { + write((int) (val & 0xff)); + write((int) ((val >> 8) & 0xff)); + write((int) ((val >> 16) & 0xff)); + write((int) ((val >> 24) & 0xff)); + return true; + } + + protected int readUI16() throws IOException { + try { + return (readUI8() + (readUI8() << 8)) & 0xffff; + } catch (EOFException ex) { + return -1; + } + } + + protected boolean writeUI16(int val) throws IOException { + write(val & 0xff); + write((val >> 8) & 0xff); + return true; + } + + protected int readUI8() throws IOException { + try { + return read() & 0xff; + } catch (EOFException ex) { + return -1; + } + } + + protected boolean writeUI8(int val) throws IOException { + write(val); + return true; + } + + protected float readFloat() throws IOException { + return Float.intBitsToFloat((int) readUI32()); + } + + protected boolean writeFloat(float val) throws IOException { + return writeUI32(Float.floatToIntBits(val)); + } + + protected byte[] readBytes(int numBytes) throws IOException { + byte[] ret = new byte[numBytes]; + for (int i = 0; i < numBytes; i++) { + ret[i] = (byte) read(); + } + return ret; + } + + protected abstract int read() throws IOException; + + protected abstract void seek(long pos, SeekMode mode) throws IOException; + + protected void write(int val) throws IOException { + //nothing + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/ByteArrayDataStream.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/ByteArrayDataStream.java index 97e9992a9..b8327fb52 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/ByteArrayDataStream.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/ByteArrayDataStream.java @@ -1,85 +1,85 @@ -package com.jpexs.decompiler.flash.iggy; - -import java.io.EOFException; -import java.io.IOException; -import java.util.Arrays; - -/** - * - * @author JPEXS - */ -public class ByteArrayDataStream extends AbstractDataStream { - - private byte[] data; - private long pos; - private boolean use64bit; - - public ByteArrayDataStream(int initialSize, boolean use64bit) { - this(new byte[initialSize], use64bit); - } - - @Override - public long position() { - return pos; - } - - public ByteArrayDataStream(byte data[], boolean use64bit) { - this.data = data; - pos = 0; - this.use64bit = use64bit; - } - - @Override - public boolean is64() { - return use64bit; - } - - @Override - protected int read() throws IOException { - if (pos >= data.length) { - throw new EOFException("End of stream reached"); - } - int ret = data[(int) pos] & 0xff; - pos++; - return ret; - } - - public void resize(int newsize) { - data = Arrays.copyOf(data, newsize); - if (pos > data.length) { - pos = data.length; - } - } - - @Override - protected void write(int val) throws IOException { - if (pos >= data.length) { - throw new EOFException("End of stream reached"); - } - data[(int) pos] = (byte) val; - pos++; - } - - @Override - protected void seek(long pos, SeekMode mode) throws IOException { - long newpos = pos; - if (mode == SeekMode.CUR) { - newpos = this.pos + pos; - } else if (mode == SeekMode.END) { - newpos = data.length - pos; - } - if (newpos > data.length) { - throw new ArrayIndexOutOfBoundsException("Position outside bounds accessed: " + pos + ". Size: " + data.length); - } else if (newpos < 0) { - throw new ArrayIndexOutOfBoundsException("Negative position accessed: " + pos); - } else { - this.pos = (int) newpos; - } - } - - @Override - public Long available() { - return (long) (data.length - pos); - } - -} +package com.jpexs.decompiler.flash.iggy; + +import java.io.EOFException; +import java.io.IOException; +import java.util.Arrays; + +/** + * + * @author JPEXS + */ +public class ByteArrayDataStream extends AbstractDataStream { + + private byte[] data; + private long pos; + private boolean use64bit; + + public ByteArrayDataStream(int initialSize, boolean use64bit) { + this(new byte[initialSize], use64bit); + } + + @Override + public long position() { + return pos; + } + + public ByteArrayDataStream(byte data[], boolean use64bit) { + this.data = data; + pos = 0; + this.use64bit = use64bit; + } + + @Override + public boolean is64() { + return use64bit; + } + + @Override + protected int read() throws IOException { + if (pos >= data.length) { + throw new EOFException("End of stream reached"); + } + int ret = data[(int) pos] & 0xff; + pos++; + return ret; + } + + public void resize(int newsize) { + data = Arrays.copyOf(data, newsize); + if (pos > data.length) { + pos = data.length; + } + } + + @Override + protected void write(int val) throws IOException { + if (pos >= data.length) { + throw new EOFException("End of stream reached"); + } + data[(int) pos] = (byte) val; + pos++; + } + + @Override + protected void seek(long pos, SeekMode mode) throws IOException { + long newpos = pos; + if (mode == SeekMode.CUR) { + newpos = this.pos + pos; + } else if (mode == SeekMode.END) { + newpos = data.length - pos; + } + if (newpos > data.length) { + throw new ArrayIndexOutOfBoundsException("Position outside bounds accessed: " + pos + ". Size: " + data.length); + } else if (newpos < 0) { + throw new ArrayIndexOutOfBoundsException("Negative position accessed: " + pos); + } else { + this.pos = (int) newpos; + } + } + + @Override + public Long available() { + return (long) (data.length - pos); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/DataType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/DataType.java index 1471d4bf7..e3fd968cd 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/DataType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/DataType.java @@ -1,9 +1,9 @@ -package com.jpexs.decompiler.flash.iggy; - -/** - * @author JPEXS - */ -public enum DataType { - ubits, uint8_t, uint16_t, uint32_t, uint64_t, float_t, unknown, - widechar_t //or maybe just "string"? It has two bytes per character and is null terminated -} +package com.jpexs.decompiler.flash.iggy; + +/** + * @author JPEXS + */ +public enum DataType { + ubits, uint8_t, uint16_t, uint32_t, uint64_t, float_t, unknown, + widechar_t //or maybe just "string"? It has two bytes per character and is null terminated +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharAdvances.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharAdvances.java index 6e81126ca..7e5a4b864 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharAdvances.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharAdvances.java @@ -1,37 +1,37 @@ -package com.jpexs.decompiler.flash.iggy; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class IggyCharAdvances implements StructureInterface { - - List advances; - private long charCount; - - public List getScales() { - return advances; - } - - public IggyCharAdvances(AbstractDataStream stream, long charCount) throws IOException { - this.charCount = charCount; - readFromDataStream(stream); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - advances = new ArrayList<>(); - for (int i = 0; i < charCount; i++) { - advances.add(stream.readFloat()); - } - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } -} +package com.jpexs.decompiler.flash.iggy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class IggyCharAdvances implements StructureInterface { + + List advances; + private long charCount; + + public List getScales() { + return advances; + } + + public IggyCharAdvances(AbstractDataStream stream, long charCount) throws IOException { + this.charCount = charCount; + readFromDataStream(stream); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + advances = new ArrayList<>(); + for (int i = 0; i < charCount; i++) { + advances.add(stream.readFloat()); + } + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharIndices.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharIndices.java index 59bebd63b..6c57824df 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharIndices.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharIndices.java @@ -1,44 +1,44 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class IggyCharIndices implements StructureInterface { - - @IggyFieldType(value = DataType.widechar_t) - List chars; - @IggyFieldType(DataType.uint32_t) - long padd; - - public List getChars() { - return chars; - } - - private long charCount; - - public IggyCharIndices(AbstractDataStream stream, long charCount) throws IOException { - this.charCount = charCount; - readFromDataStream(stream); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - chars = new ArrayList<>(); - for (int i = 0; i < charCount; i++) { - chars.add((char) stream.readUI16()); - } - padd = stream.readUI32(); - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class IggyCharIndices implements StructureInterface { + + @IggyFieldType(value = DataType.widechar_t) + List chars; + @IggyFieldType(DataType.uint32_t) + long padd; + + public List getChars() { + return chars; + } + + private long charCount; + + public IggyCharIndices(AbstractDataStream stream, long charCount) throws IOException { + this.charCount = charCount; + readFromDataStream(stream); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + chars = new ArrayList<>(); + for (int i = 0; i < charCount; i++) { + chars.add((char) stream.readUI16()); + } + padd = stream.readUI32(); + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharKerning.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharKerning.java index 386adfc70..ee00e2c84 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharKerning.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharKerning.java @@ -1,58 +1,58 @@ -package com.jpexs.decompiler.flash.iggy; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class IggyCharKerning implements StructureInterface { - - private long kernCount; - List charsA; - List charsB; - List kerningOffsets; - long pad; - - public long getKernCount() { - return kernCount; - } - - public List getCharsA() { - return charsA; - } - - public List getCharsB() { - return charsB; - } - - public List getKerningOffsets() { - return kerningOffsets; - } - - public IggyCharKerning(AbstractDataStream stream, long kernCount) throws IOException { - this.kernCount = kernCount; - readFromDataStream(stream); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - charsA = new ArrayList<>(); - charsB = new ArrayList<>(); - kerningOffsets = new ArrayList<>(); - for (int i = 0; i < kernCount; i++) { - charsA.add((char) stream.readUI16()); - charsB.add((char) stream.readUI16()); - kerningOffsets.add((short) stream.readUI16()); - } - pad = stream.readUI32(); - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - -} +package com.jpexs.decompiler.flash.iggy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class IggyCharKerning implements StructureInterface { + + private long kernCount; + List charsA; + List charsB; + List kerningOffsets; + long pad; + + public long getKernCount() { + return kernCount; + } + + public List getCharsA() { + return charsA; + } + + public List getCharsB() { + return charsB; + } + + public List getKerningOffsets() { + return kerningOffsets; + } + + public IggyCharKerning(AbstractDataStream stream, long kernCount) throws IOException { + this.kernCount = kernCount; + readFromDataStream(stream); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + charsA = new ArrayList<>(); + charsB = new ArrayList<>(); + kerningOffsets = new ArrayList<>(); + for (int i = 0; i < kernCount; i++) { + charsA.add((char) stream.readUI16()); + charsB.add((char) stream.readUI16()); + kerningOffsets.add((short) stream.readUI16()); + } + pad = stream.readUI32(); + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharOffset.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharOffset.java index 5272599d6..0899925ac 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharOffset.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyCharOffset.java @@ -1,99 +1,99 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class IggyCharOffset implements StructureInterface { - - private static Logger LOGGER = Logger.getLogger(IggyCharOffset.class.getName()); - - @IggyFieldType(DataType.uint64_t) - long zero; - @IggyFieldType(DataType.uint16_t) - int ischar1; - @IggyFieldType(DataType.uint16_t) - int ischar2; - @IggyFieldType(DataType.uint32_t) - long zero2; - @IggyFieldType(DataType.uint16_t) - int xscale; - @IggyFieldType(DataType.uint16_t) - int yscale; - @IggyFieldType(DataType.uint32_t) - long zero3; - @IggyFieldType(DataType.uint64_t) - long offset; - - public IggyCharOffset(AbstractDataStream stream) throws IOException { - readFromDataStream(stream); - } - - public IggyCharOffset(long zero, int ischar1, int ischar2, long zero2, int xscale, int yscale, long zero3, long offset) { - this.zero = zero; - this.ischar1 = ischar1; - this.ischar2 = ischar2; - this.zero2 = zero2; - this.xscale = xscale; - this.yscale = yscale; - this.zero3 = zero3; - this.offset = offset; - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - zero = stream.readUI64(); - ischar1 = stream.readUI16(); - ischar2 = stream.readUI16(); - zero2 = stream.readUI32(); - xscale = stream.readUI16(); - yscale = stream.readUI16(); - zero3 = stream.readUI32(); - long cur_position = stream.position(); - long relative_offset = stream.readUI64(); - if (ischar1 > 0) { - offset = cur_position + relative_offset; - } else { - offset = 0; - LOGGER.finer(String.format("Empty char")); - } - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - public long getZero() { - return zero; - } - - public boolean isChar1() { - return ischar1 > 0; - } - - public boolean isChar2() { - return ischar2 > 0; - } - - public long getZero2() { - return zero2; - } - - public int getXscale() { - return xscale; - } - - public int getYscale() { - return yscale; - } - - public long getZero3() { - return zero3; - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class IggyCharOffset implements StructureInterface { + + private static Logger LOGGER = Logger.getLogger(IggyCharOffset.class.getName()); + + @IggyFieldType(DataType.uint64_t) + long zero; + @IggyFieldType(DataType.uint16_t) + int ischar1; + @IggyFieldType(DataType.uint16_t) + int ischar2; + @IggyFieldType(DataType.uint32_t) + long zero2; + @IggyFieldType(DataType.uint16_t) + int xscale; + @IggyFieldType(DataType.uint16_t) + int yscale; + @IggyFieldType(DataType.uint32_t) + long zero3; + @IggyFieldType(DataType.uint64_t) + long offset; + + public IggyCharOffset(AbstractDataStream stream) throws IOException { + readFromDataStream(stream); + } + + public IggyCharOffset(long zero, int ischar1, int ischar2, long zero2, int xscale, int yscale, long zero3, long offset) { + this.zero = zero; + this.ischar1 = ischar1; + this.ischar2 = ischar2; + this.zero2 = zero2; + this.xscale = xscale; + this.yscale = yscale; + this.zero3 = zero3; + this.offset = offset; + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + zero = stream.readUI64(); + ischar1 = stream.readUI16(); + ischar2 = stream.readUI16(); + zero2 = stream.readUI32(); + xscale = stream.readUI16(); + yscale = stream.readUI16(); + zero3 = stream.readUI32(); + long cur_position = stream.position(); + long relative_offset = stream.readUI64(); + if (ischar1 > 0) { + offset = cur_position + relative_offset; + } else { + offset = 0; + LOGGER.finer(String.format("Empty char")); + } + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public long getZero() { + return zero; + } + + public boolean isChar1() { + return ischar1 > 0; + } + + public boolean isChar2() { + return ischar2 > 0; + } + + public long getZero2() { + return zero2; + } + + public int getXscale() { + return xscale; + } + + public int getYscale() { + return yscale; + } + + public long getZero3() { + return zero3; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyDataReader.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyDataReader.java index 205800a79..179062ea9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyDataReader.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyDataReader.java @@ -1,89 +1,89 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * - * @author JPEXS - */ -public class IggyDataReader implements StructureInterface { - - final static int NO_OFFSET = 1; - - @IggyFieldType(value = DataType.widechar_t, count = 48) - String name; - - Map fonts; - Map texts; - Map text2Font; - - private IggyFlashHeader64 header; - private Map sizesOfOffsets; - private List allOffsets; - - public IggyDataReader(IggyFlashHeader64 header, AbstractDataStream stream, List offsets) throws IOException { - this.header = header; - sizesOfOffsets = new HashMap<>(); - for (int i = 0; i < offsets.size() - 1; i++) { - sizesOfOffsets.put(offsets.get(i), offsets.get(i + 1) - offsets.get(i)); - } - sizesOfOffsets.put(offsets.get(offsets.size() - 1), 0L); //Last offset has 0L length? - this.allOffsets = offsets; - readFromDataStream(stream); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - //here is offset[0] - StringBuilder nameBuilder = new StringBuilder(); - do { - char c = (char) stream.readUI16(); - if (c == '\0') { - break; - } - nameBuilder.append(c); - } while (true); - name = nameBuilder.toString(); - //here is offset[1] - int pad8 = 8 - (int) (stream.position() % 8); - stream.seek(pad8, SeekMode.CUR); - //here is offset [2] - fonts = new HashMap<>(); - int fontIndex = 0; - for (int i = 2; i < allOffsets.size(); i++) { - long offset = allOffsets.get(i); - stream.seek(offset, SeekMode.SET); - int type = stream.readUI16(); - stream.seek(-2, SeekMode.CUR); - if (type == IggyFont.ID) { - IggyFont font = new IggyFont(stream); - fonts.put(fontIndex++, font); - } - if (type == IggyText.ID) { - //TODO: Texts - incomplete - } - } - } - - public String getName() { - return name; - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[\r\n"); - sb.append("name ").append(name).append("\r\n"); - return sb.toString(); - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author JPEXS + */ +public class IggyDataReader implements StructureInterface { + + final static int NO_OFFSET = 1; + + @IggyFieldType(value = DataType.widechar_t, count = 48) + String name; + + Map fonts; + Map texts; + Map text2Font; + + private IggyFlashHeader64 header; + private Map sizesOfOffsets; + private List allOffsets; + + public IggyDataReader(IggyFlashHeader64 header, AbstractDataStream stream, List offsets) throws IOException { + this.header = header; + sizesOfOffsets = new HashMap<>(); + for (int i = 0; i < offsets.size() - 1; i++) { + sizesOfOffsets.put(offsets.get(i), offsets.get(i + 1) - offsets.get(i)); + } + sizesOfOffsets.put(offsets.get(offsets.size() - 1), 0L); //Last offset has 0L length? + this.allOffsets = offsets; + readFromDataStream(stream); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + //here is offset[0] + StringBuilder nameBuilder = new StringBuilder(); + do { + char c = (char) stream.readUI16(); + if (c == '\0') { + break; + } + nameBuilder.append(c); + } while (true); + name = nameBuilder.toString(); + //here is offset[1] + int pad8 = 8 - (int) (stream.position() % 8); + stream.seek(pad8, SeekMode.CUR); + //here is offset [2] + fonts = new HashMap<>(); + int fontIndex = 0; + for (int i = 2; i < allOffsets.size(); i++) { + long offset = allOffsets.get(i); + stream.seek(offset, SeekMode.SET); + int type = stream.readUI16(); + stream.seek(-2, SeekMode.CUR); + if (type == IggyFont.ID) { + IggyFont font = new IggyFont(stream); + fonts.put(fontIndex++, font); + } + if (type == IggyText.ID) { + //TODO: Texts - incomplete + } + } + } + + public String getName() { + return name; + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[\r\n"); + sb.append("name ").append(name).append("\r\n"); + return sb.toString(); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFile.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFile.java index 8a05befac..fb1a9f239 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFile.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFile.java @@ -1,715 +1,715 @@ -package com.jpexs.decompiler.flash.iggy; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - * - * Based of works of somebody called eternity. - * - */ -public class IggyFile extends AbstractDataStream implements AutoCloseable { - - final static Logger LOGGER = Logger.getLogger(IggyFile.class.getName()); - - private RandomAccessFile raf; - private IggyHeader header; - private List subFileEntries = new ArrayList<>(); - - private List headers = new ArrayList<>(); - private List flashDataReaders = new ArrayList<>(); - - public Set getFontIds(int swfIndex) { - return flashDataReaders.get(swfIndex).fonts.keySet(); - } - - public IggyFont getFont(int swfIndex, int fontId) { - return flashDataReaders.get(swfIndex).fonts.get(fontId); - } - - public IggyText getText(int swfIndex, int textId) { - return flashDataReaders.get(swfIndex).texts.get(textId); - } - - public Set getTextIds(int swfIndex) { - return flashDataReaders.get(swfIndex).texts.keySet(); - } - - @Override - public long position() { - try { - return raf.getFilePointer(); - } catch (IOException ex) { - return -1; - } - } - - public IggyFile(File file) throws IOException { - raf = new RandomAccessFile(file, "r"); - header = new IggyHeader(this); - for (int i = 0; i < header.getNumSubfiles(); i++) { - subFileEntries.add(new IggySubFileEntry(this)); - } - - List> indexTables = new ArrayList<>(); //TODO: use this two for something ?? - List> offsetTables = new ArrayList<>(); - List flashDataStreams = new ArrayList<>(); - - for (int i = 0; i < subFileEntries.size(); i++) { - IggySubFileEntry entry = subFileEntries.get(i); - ByteArrayDataStream dataStream = getEntryDataStream(i); - if (entry.type == IggySubFileEntry.TYPE_INDEX) { - List indexTable = new ArrayList<>(); - List offsets = new ArrayList<>(); - IggyIndexParser.parseIndex(dataStream, indexTable, offsets); - indexTables.add(indexTable); - offsetTables.add(offsets); - } else if (entry.type == IggySubFileEntry.TYPE_FLASH) { - IggyFlashHeaderInterface hdr; - if (is64()) { - hdr = new IggyFlashHeader64(dataStream); - } else { - hdr = new IggyFlashHeader32(dataStream); - } - headers.add(hdr); - flashDataStreams.add(dataStream); - } - } - - for (int swfIndex = 0; swfIndex < headers.size(); swfIndex++) { - IggyFlashHeaderInterface hdr = headers.get(swfIndex); - IggyDataReader dataReader = new IggyDataReader((IggyFlashHeader64) hdr /*FIXME for 32*/, flashDataStreams.get(swfIndex), offsetTables.get(swfIndex)); - flashDataReaders.add(dataReader); - } - } - - @Override - public boolean is64() { - return header.is64(); - } - - public IggyHeader getHeader() { - return header; - } - - public IggySubFileEntry getSubFileEntry(int entryIndex) { - if (entryIndex < 0 || entryIndex >= subFileEntries.size()) { - throw new ArrayIndexOutOfBoundsException("No entry with index " + entryIndex + " exists"); - } - return subFileEntries.get(entryIndex); - } - - public int getNumEntries() { - return subFileEntries.size(); - } - - public byte[] getEntryData(int entryIndex) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - InputStream is = getEntryInputStream(entryIndex); - byte buf[] = new byte[1024]; - int cnt; - while ((cnt = is.read(buf)) > 0) { - baos.write(buf, 0, cnt); - } - return baos.toByteArray(); - } - - public ByteArrayDataStream getEntryDataStream(int entryIndex) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - InputStream is = getEntryInputStream(entryIndex); - byte buf[] = new byte[1024]; - int cnt; - while ((cnt = is.read(buf)) > 0) { - baos.write(buf, 0, cnt); - } - byte data[] = baos.toByteArray(); - return new ByteArrayDataStream(data, is64()); - } - - public InputStream getEntryInputStream(int entryIndex) { - IggySubFileEntry entry = getSubFileEntry(entryIndex); - - return new InputStream() { - long offset = entry.offset; - long maxOffset = entry.offset + entry.size; - - @Override - public synchronized int read() throws IOException { - if (offset < maxOffset) { - raf.seek(offset); - offset++; - return raf.read(); - } - return -1; - } - }; - } - - @Override - protected int read() throws IOException { - int val = raf.read(); - if (val == -1) { - throw new EOFException(); - } - return val; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[IggyFile:").append("\r\n"); - sb.append(header).append("\r\n"); - sb.append("Entries:").append("\r\n"); - for (IggySubFileEntry entry : subFileEntries) { - sb.append(entry).append("\r\n"); - } - sb.append("]"); - return sb.toString(); - } - - public static void extractIggyFile(File iggyFile, File extractDir) throws IOException { - final String FILENAME_FORMAT = "index%d_type%d.bin"; - try (IggyFile ir = new IggyFile(iggyFile)) { - for (int i = 0; i < ir.getNumEntries(); i++) { - IggySubFileEntry entry = ir.getSubFileEntry(i); - try (FileOutputStream fos = new FileOutputStream(new File(extractDir, String.format(FILENAME_FORMAT, i, entry.type)))) { - fos.write(ir.getEntryData(i)); - } - } - } - } - - private static void processFile(File f) { - if (f.isDirectory()) { - System.out.println("Processing directory " + f + ":"); - File iggyFiles[] = f.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".iggy"); - } - }); - for (File sf : iggyFiles) { - processFile(sf); - } - return; - } - System.out.print("Processing file " + f + "..."); - try { - File dir = f.getParentFile(); - File extractDir = new File(dir, "extracted_" + (f.getName()).replace(".iggy", "")); - extractDir.mkdir(); - extractIggyFile(f, extractDir); - System.out.println("OK"); - } catch (Exception ex) { - System.err.println("FAIL"); - System.exit(1); - } - } - - public static void main(String[] args) throws IOException { - System.out.println("Iggy file splitter"); - if (args.length == 0) { - System.err.println("No file specified"); - System.exit(1); - } - - for (String s : args) { - File f = new File(s); - if (!f.exists()) { - System.err.println("File " + f + " does not exists"); - System.exit(1); - } - processFile(f); - - } - System.exit(0); - } - - private static void copyStream(InputStream is, OutputStream os) { - try { - final int bufSize = 4096; - byte[] buf = new byte[bufSize]; - int cnt; - while ((cnt = is.read(buf)) > 0) { - os.write(buf, 0, cnt); - } - } catch (IOException ex) { - // ignore - } - } - - @Override - public void close() { - try { - raf.close(); - } catch (IOException ex) { - //ignore - } - } - - @Override - protected void seek(long pos, SeekMode mode) throws IOException { - long newpos = pos; - if (mode == SeekMode.CUR) { - newpos = raf.getFilePointer() + pos; - } else if (mode == SeekMode.END) { - newpos = raf.length() - pos; - } - if (newpos > raf.length()) { - throw new ArrayIndexOutOfBoundsException("Position outside bounds accessed: " + pos + ". Size: " + raf.length()); - } else if (newpos < 0) { - throw new ArrayIndexOutOfBoundsException("Negative position accessed: " + pos); - } else { - raf.seek(newpos); - } - } - - private static boolean updateIndex(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], long item_size_change /*int32_t*/) throws IOException { - ByteArrayDataStream stream = new ByteArrayDataStream(index_bytes, is_64); - - /* - index_table: - n = UI8 - for i=0..n-1 - table[i] = UI8 - cnt = UI8 - cnt * UI16 - - */ - int index_table_size = stream.readUI8(); - int index_table[] = new int[index_table_size]; - - for (int i = 0; i < index_table_size; i++) { - index_table[i] = stream.readUI8(); - int num = stream.readUI8(); - stream.seek(num * 2, SeekMode.CUR); - //num * UI16 - } - - int state = 0; - long offset = 0;//uint32_t - int code; //uint8_t - - while ((code = stream.readUI8()) >= 0) { - if (state == 1) { - if (code != 0xFD) { - LOGGER.log(Level.WARNING, "We were expecting code 0xFD in state 1."); - return false; - } - } else if (state == 2) { - if (code != 0xFF) { - LOGGER.log(Level.WARNING, "We were expecting code 0xFF in state 2."); - return false; - } - } - - if (code < 0x80) // 0-0x7F - { - // code is directly an index to the index_table - if (code >= index_table_size) { - LOGGER.log(Level.WARNING, String.format("< 0x80: index is greater than index_table_size. %x > %x", code, index_table_size)); - return false; - } - - offset += index_table[code]; - } else if (code < 0xC0) // 0x80-BF - { - int index; //uint8_t - - if ((index = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "< 0xC0: Cannot read index."); - return false; - } - - if (index >= index_table_size) { - LOGGER.log(Level.WARNING, String.format("< 0xC0: index is greater than index_table_size. %x > %x", index, index_table_size)); - return false; - } - - int n = code - 0x7F; - offset += index_table[index] * n; - } else if (code < 0xD0) // 0xC0-0xCF - { - offset += ((code * 2) - 0x17E); - } else if (code < 0xE0) // 0xD0-0xDF - { - // Code here depends on plattform[0], we are assuming it is 1, as we checked in load function - int i = code & 0xF; //uint8_t - int n8; //uint8_t - int n; - - if ((n8 = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "< 0xE0: Cannot read n."); - return false; - } - - n = n8 + 1; - - if (is_64) { - if (i <= 2) { - offset += 8 * n; // Ptr type - } else if (i <= 4) { - offset += 2 * n; - } else if (i == 5) { - offset += 4 * n; - } else if (i == 6) { - offset += 8 * n; // 64 bits type - } else { - LOGGER.log(Level.WARNING, String.format("< 0xE0: Invalid value for i (%x %x)", i, code)); - } - } else { - switch (i) { - case 2: - offset += 4 * n; // Ptr type - break; - case 4: - offset += 2 * n; - break; - case 5: - offset += 4 * n; // 32 bits type - break; - case 6: - offset += 8 * n; - break; - default: - LOGGER.log(Level.WARNING, String.format("< 0xE0: invalid value for i (%x %x)", i, code)); - } - } - } else if (code == 0xFC) { - stream.seek(1, SeekMode.CUR); - } else if (code == 0xFD) { - int n, m; //uint8_t - - if ((n = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "0xFD: Cannot read n."); - return false; - } - - if (state == 1) { - if (is_64) { - if (n != 0xF) { - LOGGER.log(Level.WARNING, String.format("We were expecting an offset of 0xF in state 1.")); - return false; - } - } else if (n != 0xB) { - LOGGER.log(Level.WARNING, "We were expecting an offset of 0xB in state 1."); - return false; - } - - state = 2; - } - - if ((m = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "0xFD: Cannot read m."); - return false; - } - - offset += n; - stream.seek(m * 2, SeekMode.CUR); - } else if (code == 0xFE) { - int n8; //uint8_t - int n; - - if ((n8 = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "0xFE: Cannot read n."); - return false; - } - - n = n8 + 1; - offset += n; - } else if (code == 0xFF) { - long n; //uint32_t - - if ((n = stream.readUI32()) < 0) { - LOGGER.log(Level.WARNING, "0xFF: Cannot read n."); - return false; - } - - if (state == 2) { - n += item_size_change; - stream.seek(-4, SeekMode.CUR); - return stream.writeUI32(n); - } - - offset += n; - } else { - LOGGER.log(Level.WARNING, String.format("Unrecognized code: %x", code)); - } - - if (state == 0 && offset == item_offset) { - state = 1; - } - } - return false; - } - - /** - * Gets length of an item. - * - * @param item_offset - * @param is_64 - * @param index_bytes - * @return null when item not exists, item length otherwise - * @throws IOException - */ - private static Long getItemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[]) throws IOException { - return itemLength(item_offset, is_64, index_bytes, null); - } - - /** - * Sets new length of an item. - * - * @param item_offset - * @param is_64 - * @param index_bytes - * @param newLength - * @return null when item not exists, old item length otherwise - * @throws IOException - */ - private static Long setItemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], long newLength) throws IOException { - return itemLength(item_offset, is_64, index_bytes, newLength); - } - - /** - * Sets/Gets length of an item - * - * @param item_offset - * @param is_64 - * @param index_bytes - * @param newValue New value to set. If null then no change. - * @return null when item does not exists, old item length otherwise - * @throws IOException - */ - private static Long itemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], Long newValue) throws IOException { - ByteArrayDataStream stream = new ByteArrayDataStream(index_bytes, is_64); - - /* - index_table: - n = UI8 - for i=0..n-1 - table[i] = UI8 - cnt = UI8 - cnt * UI16 - - */ - int index_table_size = stream.readUI8(); - int index_table[] = new int[index_table_size]; - - for (int i = 0; i < index_table_size; i++) { - index_table[i] = stream.readUI8(); - int num = stream.readUI8(); - stream.seek(num * 2, SeekMode.CUR); - //num * UI16 - } - - int state = 0; - long offset = 0;//uint32_t - int code; //uint8_t - - while ((code = stream.readUI8()) >= 0) { - if (state == 1) { - if (code != 0xFD) { - LOGGER.log(Level.WARNING, "We were expecting code 0xFD in state 1."); - return null; - } - } else if (state == 2) { - if (code != 0xFF) { - LOGGER.log(Level.WARNING, "We were expecting code 0xFF in state 2."); - return null; - } - } - - if (code < 0x80) // 0-0x7F - { - // code is directly an index to the index_table - if (code >= index_table_size) { - LOGGER.log(Level.WARNING, String.format("< 0x80: index is greater than index_table_size. %x > %x", code, index_table_size)); - return null; - } - - offset += index_table[code]; - } else if (code < 0xC0) // 0x80-BF - { - int index; //uint8_t - - if ((index = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "< 0xC0: Cannot read index."); - return null; - } - - if (index >= index_table_size) { - LOGGER.log(Level.WARNING, String.format("< 0xC0: index is greater than index_table_size. %x > %x", index, index_table_size)); - return null; - } - - int n = code - 0x7F; - offset += index_table[index] * n; - } else if (code < 0xD0) // 0xC0-0xCF - { - offset += ((code * 2) - 0x17E); - } else if (code < 0xE0) // 0xD0-0xDF - { - // Code here depends on plattform[0], we are assuming it is 1, as we checked in load function - int i = code & 0xF; //uint8_t - int n8; //uint8_t - int n; - - if ((n8 = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "< 0xE0: Cannot read n."); - return null; - } - - n = n8 + 1; - - if (is_64) { - if (i <= 2) { - offset += 8 * n; // Ptr type - } else if (i <= 4) { - offset += 2 * n; - } else if (i == 5) { - offset += 4 * n; - } else if (i == 6) { - offset += 8 * n; // 64 bits type - } else { - LOGGER.log(Level.WARNING, String.format("< 0xE0: Invalid value for i (%x %x)", i, code)); - } - } else { - switch (i) { - case 2: - offset += 4 * n; // Ptr type - break; - case 4: - offset += 2 * n; - break; - case 5: - offset += 4 * n; // 32 bits type - break; - case 6: - offset += 8 * n; - break; - default: - LOGGER.log(Level.WARNING, String.format("< 0xE0: invalid value for i (%x %x)", i, code)); - } - } - } else if (code == 0xFC) { - stream.seek(1, SeekMode.CUR); - } else if (code == 0xFD) { - int n, m; //uint8_t - - if ((n = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "0xFD: Cannot read n."); - return null; - } - - if (state == 1) { - if (is_64) { - if (n != 0xF) { - LOGGER.log(Level.WARNING, String.format("We were expecting an offset of 0xF in state 1.")); - return null; - } - } else if (n != 0xB) { - LOGGER.log(Level.WARNING, "We were expecting an offset of 0xB in state 1."); - return null; - } - - state = 2; - } - - if ((m = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "0xFD: Cannot read m."); - return null; - } - - offset += n; - stream.seek(m * 2, SeekMode.CUR); - } else if (code == 0xFE) { - int n8; //uint8_t - int n; - - if ((n8 = stream.readUI8()) < 0) { - LOGGER.log(Level.WARNING, "0xFE: Cannot read n."); - return null; - } - - n = n8 + 1; - offset += n; - } else if (code == 0xFF) { - long n; //uint32_t - - if ((n = stream.readUI32()) < 0) { - LOGGER.log(Level.WARNING, "0xFF: Cannot read n."); - return null; - } - - if (state == 2) { - if (newValue != null) { - stream.seek(-4, SeekMode.CUR); - stream.writeUI32(newValue); - } - return n; - } - - offset += n; - } else { - LOGGER.log(Level.WARNING, String.format("Unrecognized code: %x", code)); - } - - if (state == 0 && offset == item_offset) { - state = 1; - } - } - return null; - } - - @Override - public Long available() { - try { - return raf.length() - raf.getFilePointer(); - } catch (IOException ex) { - return null; - } - } - - public int getSwfCount() { - return flashDataReaders.size(); - } - - public String getSwfName(int swfIndex) { - return flashDataReaders.get(swfIndex).getName(); - } - - public long getSwfXMin(int swfIndex) { - return headers.get(swfIndex).getXMin(); - } - - public long getSwfYMin(int swfIndex) { - return headers.get(swfIndex).getYMin(); - } - - public long getSwfXMax(int swfIndex) { - return headers.get(swfIndex).getXMax(); - } - - public long getSwfYMax(int swfIndex) { - return headers.get(swfIndex).getYMax(); - } - - public float getSwfFrameRate(int swfIndex) { - return headers.get(swfIndex).getFrameRate(); - } -} +package com.jpexs.decompiler.flash.iggy; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + * + * Based of works of somebody called eternity. + * + */ +public class IggyFile extends AbstractDataStream implements AutoCloseable { + + final static Logger LOGGER = Logger.getLogger(IggyFile.class.getName()); + + private RandomAccessFile raf; + private IggyHeader header; + private List subFileEntries = new ArrayList<>(); + + private List headers = new ArrayList<>(); + private List flashDataReaders = new ArrayList<>(); + + public Set getFontIds(int swfIndex) { + return flashDataReaders.get(swfIndex).fonts.keySet(); + } + + public IggyFont getFont(int swfIndex, int fontId) { + return flashDataReaders.get(swfIndex).fonts.get(fontId); + } + + public IggyText getText(int swfIndex, int textId) { + return flashDataReaders.get(swfIndex).texts.get(textId); + } + + public Set getTextIds(int swfIndex) { + return flashDataReaders.get(swfIndex).texts.keySet(); + } + + @Override + public long position() { + try { + return raf.getFilePointer(); + } catch (IOException ex) { + return -1; + } + } + + public IggyFile(File file) throws IOException { + raf = new RandomAccessFile(file, "r"); + header = new IggyHeader(this); + for (int i = 0; i < header.getNumSubfiles(); i++) { + subFileEntries.add(new IggySubFileEntry(this)); + } + + List> indexTables = new ArrayList<>(); //TODO: use this two for something ?? + List> offsetTables = new ArrayList<>(); + List flashDataStreams = new ArrayList<>(); + + for (int i = 0; i < subFileEntries.size(); i++) { + IggySubFileEntry entry = subFileEntries.get(i); + ByteArrayDataStream dataStream = getEntryDataStream(i); + if (entry.type == IggySubFileEntry.TYPE_INDEX) { + List indexTable = new ArrayList<>(); + List offsets = new ArrayList<>(); + IggyIndexParser.parseIndex(dataStream, indexTable, offsets); + indexTables.add(indexTable); + offsetTables.add(offsets); + } else if (entry.type == IggySubFileEntry.TYPE_FLASH) { + IggyFlashHeaderInterface hdr; + if (is64()) { + hdr = new IggyFlashHeader64(dataStream); + } else { + hdr = new IggyFlashHeader32(dataStream); + } + headers.add(hdr); + flashDataStreams.add(dataStream); + } + } + + for (int swfIndex = 0; swfIndex < headers.size(); swfIndex++) { + IggyFlashHeaderInterface hdr = headers.get(swfIndex); + IggyDataReader dataReader = new IggyDataReader((IggyFlashHeader64) hdr /*FIXME for 32*/, flashDataStreams.get(swfIndex), offsetTables.get(swfIndex)); + flashDataReaders.add(dataReader); + } + } + + @Override + public boolean is64() { + return header.is64(); + } + + public IggyHeader getHeader() { + return header; + } + + public IggySubFileEntry getSubFileEntry(int entryIndex) { + if (entryIndex < 0 || entryIndex >= subFileEntries.size()) { + throw new ArrayIndexOutOfBoundsException("No entry with index " + entryIndex + " exists"); + } + return subFileEntries.get(entryIndex); + } + + public int getNumEntries() { + return subFileEntries.size(); + } + + public byte[] getEntryData(int entryIndex) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream is = getEntryInputStream(entryIndex); + byte buf[] = new byte[1024]; + int cnt; + while ((cnt = is.read(buf)) > 0) { + baos.write(buf, 0, cnt); + } + return baos.toByteArray(); + } + + public ByteArrayDataStream getEntryDataStream(int entryIndex) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream is = getEntryInputStream(entryIndex); + byte buf[] = new byte[1024]; + int cnt; + while ((cnt = is.read(buf)) > 0) { + baos.write(buf, 0, cnt); + } + byte data[] = baos.toByteArray(); + return new ByteArrayDataStream(data, is64()); + } + + public InputStream getEntryInputStream(int entryIndex) { + IggySubFileEntry entry = getSubFileEntry(entryIndex); + + return new InputStream() { + long offset = entry.offset; + long maxOffset = entry.offset + entry.size; + + @Override + public synchronized int read() throws IOException { + if (offset < maxOffset) { + raf.seek(offset); + offset++; + return raf.read(); + } + return -1; + } + }; + } + + @Override + protected int read() throws IOException { + int val = raf.read(); + if (val == -1) { + throw new EOFException(); + } + return val; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[IggyFile:").append("\r\n"); + sb.append(header).append("\r\n"); + sb.append("Entries:").append("\r\n"); + for (IggySubFileEntry entry : subFileEntries) { + sb.append(entry).append("\r\n"); + } + sb.append("]"); + return sb.toString(); + } + + public static void extractIggyFile(File iggyFile, File extractDir) throws IOException { + final String FILENAME_FORMAT = "index%d_type%d.bin"; + try (IggyFile ir = new IggyFile(iggyFile)) { + for (int i = 0; i < ir.getNumEntries(); i++) { + IggySubFileEntry entry = ir.getSubFileEntry(i); + try (FileOutputStream fos = new FileOutputStream(new File(extractDir, String.format(FILENAME_FORMAT, i, entry.type)))) { + fos.write(ir.getEntryData(i)); + } + } + } + } + + private static void processFile(File f) { + if (f.isDirectory()) { + System.out.println("Processing directory " + f + ":"); + File iggyFiles[] = f.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".iggy"); + } + }); + for (File sf : iggyFiles) { + processFile(sf); + } + return; + } + System.out.print("Processing file " + f + "..."); + try { + File dir = f.getParentFile(); + File extractDir = new File(dir, "extracted_" + (f.getName()).replace(".iggy", "")); + extractDir.mkdir(); + extractIggyFile(f, extractDir); + System.out.println("OK"); + } catch (Exception ex) { + System.err.println("FAIL"); + System.exit(1); + } + } + + public static void main(String[] args) throws IOException { + System.out.println("Iggy file splitter"); + if (args.length == 0) { + System.err.println("No file specified"); + System.exit(1); + } + + for (String s : args) { + File f = new File(s); + if (!f.exists()) { + System.err.println("File " + f + " does not exists"); + System.exit(1); + } + processFile(f); + + } + System.exit(0); + } + + private static void copyStream(InputStream is, OutputStream os) { + try { + final int bufSize = 4096; + byte[] buf = new byte[bufSize]; + int cnt; + while ((cnt = is.read(buf)) > 0) { + os.write(buf, 0, cnt); + } + } catch (IOException ex) { + // ignore + } + } + + @Override + public void close() { + try { + raf.close(); + } catch (IOException ex) { + //ignore + } + } + + @Override + protected void seek(long pos, SeekMode mode) throws IOException { + long newpos = pos; + if (mode == SeekMode.CUR) { + newpos = raf.getFilePointer() + pos; + } else if (mode == SeekMode.END) { + newpos = raf.length() - pos; + } + if (newpos > raf.length()) { + throw new ArrayIndexOutOfBoundsException("Position outside bounds accessed: " + pos + ". Size: " + raf.length()); + } else if (newpos < 0) { + throw new ArrayIndexOutOfBoundsException("Negative position accessed: " + pos); + } else { + raf.seek(newpos); + } + } + + private static boolean updateIndex(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], long item_size_change /*int32_t*/) throws IOException { + ByteArrayDataStream stream = new ByteArrayDataStream(index_bytes, is_64); + + /* + index_table: + n = UI8 + for i=0..n-1 + table[i] = UI8 + cnt = UI8 + cnt * UI16 + + */ + int index_table_size = stream.readUI8(); + int index_table[] = new int[index_table_size]; + + for (int i = 0; i < index_table_size; i++) { + index_table[i] = stream.readUI8(); + int num = stream.readUI8(); + stream.seek(num * 2, SeekMode.CUR); + //num * UI16 + } + + int state = 0; + long offset = 0;//uint32_t + int code; //uint8_t + + while ((code = stream.readUI8()) >= 0) { + if (state == 1) { + if (code != 0xFD) { + LOGGER.log(Level.WARNING, "We were expecting code 0xFD in state 1."); + return false; + } + } else if (state == 2) { + if (code != 0xFF) { + LOGGER.log(Level.WARNING, "We were expecting code 0xFF in state 2."); + return false; + } + } + + if (code < 0x80) // 0-0x7F + { + // code is directly an index to the index_table + if (code >= index_table_size) { + LOGGER.log(Level.WARNING, String.format("< 0x80: index is greater than index_table_size. %x > %x", code, index_table_size)); + return false; + } + + offset += index_table[code]; + } else if (code < 0xC0) // 0x80-BF + { + int index; //uint8_t + + if ((index = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "< 0xC0: Cannot read index."); + return false; + } + + if (index >= index_table_size) { + LOGGER.log(Level.WARNING, String.format("< 0xC0: index is greater than index_table_size. %x > %x", index, index_table_size)); + return false; + } + + int n = code - 0x7F; + offset += index_table[index] * n; + } else if (code < 0xD0) // 0xC0-0xCF + { + offset += ((code * 2) - 0x17E); + } else if (code < 0xE0) // 0xD0-0xDF + { + // Code here depends on plattform[0], we are assuming it is 1, as we checked in load function + int i = code & 0xF; //uint8_t + int n8; //uint8_t + int n; + + if ((n8 = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "< 0xE0: Cannot read n."); + return false; + } + + n = n8 + 1; + + if (is_64) { + if (i <= 2) { + offset += 8 * n; // Ptr type + } else if (i <= 4) { + offset += 2 * n; + } else if (i == 5) { + offset += 4 * n; + } else if (i == 6) { + offset += 8 * n; // 64 bits type + } else { + LOGGER.log(Level.WARNING, String.format("< 0xE0: Invalid value for i (%x %x)", i, code)); + } + } else { + switch (i) { + case 2: + offset += 4 * n; // Ptr type + break; + case 4: + offset += 2 * n; + break; + case 5: + offset += 4 * n; // 32 bits type + break; + case 6: + offset += 8 * n; + break; + default: + LOGGER.log(Level.WARNING, String.format("< 0xE0: invalid value for i (%x %x)", i, code)); + } + } + } else if (code == 0xFC) { + stream.seek(1, SeekMode.CUR); + } else if (code == 0xFD) { + int n, m; //uint8_t + + if ((n = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "0xFD: Cannot read n."); + return false; + } + + if (state == 1) { + if (is_64) { + if (n != 0xF) { + LOGGER.log(Level.WARNING, String.format("We were expecting an offset of 0xF in state 1.")); + return false; + } + } else if (n != 0xB) { + LOGGER.log(Level.WARNING, "We were expecting an offset of 0xB in state 1."); + return false; + } + + state = 2; + } + + if ((m = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "0xFD: Cannot read m."); + return false; + } + + offset += n; + stream.seek(m * 2, SeekMode.CUR); + } else if (code == 0xFE) { + int n8; //uint8_t + int n; + + if ((n8 = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "0xFE: Cannot read n."); + return false; + } + + n = n8 + 1; + offset += n; + } else if (code == 0xFF) { + long n; //uint32_t + + if ((n = stream.readUI32()) < 0) { + LOGGER.log(Level.WARNING, "0xFF: Cannot read n."); + return false; + } + + if (state == 2) { + n += item_size_change; + stream.seek(-4, SeekMode.CUR); + return stream.writeUI32(n); + } + + offset += n; + } else { + LOGGER.log(Level.WARNING, String.format("Unrecognized code: %x", code)); + } + + if (state == 0 && offset == item_offset) { + state = 1; + } + } + return false; + } + + /** + * Gets length of an item. + * + * @param item_offset + * @param is_64 + * @param index_bytes + * @return null when item not exists, item length otherwise + * @throws IOException + */ + private static Long getItemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[]) throws IOException { + return itemLength(item_offset, is_64, index_bytes, null); + } + + /** + * Sets new length of an item. + * + * @param item_offset + * @param is_64 + * @param index_bytes + * @param newLength + * @return null when item not exists, old item length otherwise + * @throws IOException + */ + private static Long setItemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], long newLength) throws IOException { + return itemLength(item_offset, is_64, index_bytes, newLength); + } + + /** + * Sets/Gets length of an item + * + * @param item_offset + * @param is_64 + * @param index_bytes + * @param newValue New value to set. If null then no change. + * @return null when item does not exists, old item length otherwise + * @throws IOException + */ + private static Long itemLength(long item_offset /*uint32_t*/, boolean is_64, byte index_bytes[], Long newValue) throws IOException { + ByteArrayDataStream stream = new ByteArrayDataStream(index_bytes, is_64); + + /* + index_table: + n = UI8 + for i=0..n-1 + table[i] = UI8 + cnt = UI8 + cnt * UI16 + + */ + int index_table_size = stream.readUI8(); + int index_table[] = new int[index_table_size]; + + for (int i = 0; i < index_table_size; i++) { + index_table[i] = stream.readUI8(); + int num = stream.readUI8(); + stream.seek(num * 2, SeekMode.CUR); + //num * UI16 + } + + int state = 0; + long offset = 0;//uint32_t + int code; //uint8_t + + while ((code = stream.readUI8()) >= 0) { + if (state == 1) { + if (code != 0xFD) { + LOGGER.log(Level.WARNING, "We were expecting code 0xFD in state 1."); + return null; + } + } else if (state == 2) { + if (code != 0xFF) { + LOGGER.log(Level.WARNING, "We were expecting code 0xFF in state 2."); + return null; + } + } + + if (code < 0x80) // 0-0x7F + { + // code is directly an index to the index_table + if (code >= index_table_size) { + LOGGER.log(Level.WARNING, String.format("< 0x80: index is greater than index_table_size. %x > %x", code, index_table_size)); + return null; + } + + offset += index_table[code]; + } else if (code < 0xC0) // 0x80-BF + { + int index; //uint8_t + + if ((index = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "< 0xC0: Cannot read index."); + return null; + } + + if (index >= index_table_size) { + LOGGER.log(Level.WARNING, String.format("< 0xC0: index is greater than index_table_size. %x > %x", index, index_table_size)); + return null; + } + + int n = code - 0x7F; + offset += index_table[index] * n; + } else if (code < 0xD0) // 0xC0-0xCF + { + offset += ((code * 2) - 0x17E); + } else if (code < 0xE0) // 0xD0-0xDF + { + // Code here depends on plattform[0], we are assuming it is 1, as we checked in load function + int i = code & 0xF; //uint8_t + int n8; //uint8_t + int n; + + if ((n8 = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "< 0xE0: Cannot read n."); + return null; + } + + n = n8 + 1; + + if (is_64) { + if (i <= 2) { + offset += 8 * n; // Ptr type + } else if (i <= 4) { + offset += 2 * n; + } else if (i == 5) { + offset += 4 * n; + } else if (i == 6) { + offset += 8 * n; // 64 bits type + } else { + LOGGER.log(Level.WARNING, String.format("< 0xE0: Invalid value for i (%x %x)", i, code)); + } + } else { + switch (i) { + case 2: + offset += 4 * n; // Ptr type + break; + case 4: + offset += 2 * n; + break; + case 5: + offset += 4 * n; // 32 bits type + break; + case 6: + offset += 8 * n; + break; + default: + LOGGER.log(Level.WARNING, String.format("< 0xE0: invalid value for i (%x %x)", i, code)); + } + } + } else if (code == 0xFC) { + stream.seek(1, SeekMode.CUR); + } else if (code == 0xFD) { + int n, m; //uint8_t + + if ((n = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "0xFD: Cannot read n."); + return null; + } + + if (state == 1) { + if (is_64) { + if (n != 0xF) { + LOGGER.log(Level.WARNING, String.format("We were expecting an offset of 0xF in state 1.")); + return null; + } + } else if (n != 0xB) { + LOGGER.log(Level.WARNING, "We were expecting an offset of 0xB in state 1."); + return null; + } + + state = 2; + } + + if ((m = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "0xFD: Cannot read m."); + return null; + } + + offset += n; + stream.seek(m * 2, SeekMode.CUR); + } else if (code == 0xFE) { + int n8; //uint8_t + int n; + + if ((n8 = stream.readUI8()) < 0) { + LOGGER.log(Level.WARNING, "0xFE: Cannot read n."); + return null; + } + + n = n8 + 1; + offset += n; + } else if (code == 0xFF) { + long n; //uint32_t + + if ((n = stream.readUI32()) < 0) { + LOGGER.log(Level.WARNING, "0xFF: Cannot read n."); + return null; + } + + if (state == 2) { + if (newValue != null) { + stream.seek(-4, SeekMode.CUR); + stream.writeUI32(newValue); + } + return n; + } + + offset += n; + } else { + LOGGER.log(Level.WARNING, String.format("Unrecognized code: %x", code)); + } + + if (state == 0 && offset == item_offset) { + state = 1; + } + } + return null; + } + + @Override + public Long available() { + try { + return raf.length() - raf.getFilePointer(); + } catch (IOException ex) { + return null; + } + } + + public int getSwfCount() { + return flashDataReaders.size(); + } + + public String getSwfName(int swfIndex) { + return flashDataReaders.get(swfIndex).getName(); + } + + public long getSwfXMin(int swfIndex) { + return headers.get(swfIndex).getXMin(); + } + + public long getSwfYMin(int swfIndex) { + return headers.get(swfIndex).getYMin(); + } + + public long getSwfXMax(int swfIndex) { + return headers.get(swfIndex).getXMax(); + } + + public long getSwfYMax(int swfIndex) { + return headers.get(swfIndex).getYMax(); + } + + public float getSwfFrameRate(int swfIndex) { + return headers.get(swfIndex).getFrameRate(); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader32.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader32.java index 894371e66..433900e90 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader32.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader32.java @@ -1,195 +1,195 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; - -/** - * - * @author JPEXS - * - * Based of works of somebody called eternity. - * - * All relative offsets are relative from that specific field position All - * relative offsets can get value "1" to indicate "nothing" - */ -public class IggyFlashHeader32 implements IggyFlashHeaderInterface { - - @IggyFieldType(DataType.uint32_t) - long main_offset; // 0 Relative offset to first section (matches sizeof header) - @IggyFieldType(DataType.uint32_t) - long as3_section_offset; // 4 Relative offset to as3 file names table... - @IggyFieldType(DataType.uint32_t) - long unk_offset; // 8 relative offset to something - @IggyFieldType(DataType.uint32_t) - long unk_offset2; // 0xC relative offset to something - @IggyFieldType(DataType.uint32_t) - long unk_offset3; // 0x10 relative offset to something - @IggyFieldType(DataType.uint32_t) - long unk_offset4; // 0x14 relative offset to something - @IggyFieldType(DataType.uint32_t) - long xmin; //0x18 in pixels - @IggyFieldType(DataType.uint32_t) - long ymin; //0x0C in pixels - @IggyFieldType(DataType.uint32_t) - long xmax; // 0x20 in pixels - @IggyFieldType(DataType.uint32_t) - long ymax; // 0x24 in pixels - @IggyFieldType(DataType.uint32_t) - long unk_28; // probably number of blocks/objects after header - @IggyFieldType(DataType.uint32_t) - long unk_2C; - @IggyFieldType(DataType.uint32_t) - long unk_30; - @IggyFieldType(DataType.uint32_t) - long unk_34; - @IggyFieldType(DataType.uint32_t) - long unk_38; - @IggyFieldType(DataType.uint32_t) - long unk_3C; - @IggyFieldType(DataType.float_t) - float frameRate; - @IggyFieldType(DataType.uint32_t) - long unk_44; - @IggyFieldType(DataType.uint32_t) - long unk_48; - @IggyFieldType(DataType.uint32_t) - long unk_4C; - @IggyFieldType(DataType.uint32_t) - long names_offset; // 0x50 relative offset to the names/import section of the file - @IggyFieldType(DataType.uint32_t) - long unk_offset5; // 0x54 relative offset to something - @IggyFieldType(DataType.uint64_t) - long unk_58; // Maybe number of imports/names pointed by names_offset - @IggyFieldType(DataType.uint32_t) - long last_section_offset; // 0x60 relative offset, points to the small last section of the file - @IggyFieldType(DataType.uint32_t) - long unk_offset6; // 0x64 relative offset to something - @IggyFieldType(DataType.uint32_t) - long as3_code_offset; // 0x68 relative offset to as3 code (8 bytes header + abc blob) - @IggyFieldType(DataType.uint32_t) - long as3_names_offset; // 0x6C relative offset to as3 file names table (or classes names or whatever) - @IggyFieldType(DataType.uint32_t) - long unk_70; - @IggyFieldType(DataType.uint32_t) - long unk_74; - @IggyFieldType(DataType.uint32_t) - long unk_78; // Maybe number of classes / as3 names - @IggyFieldType(DataType.uint32_t) - long unk_7C; - - // Offset 0x80 (outside header): there are *unk_28* relative offsets that point to flash objects. - // The flash objects are in a format different to swf but there is probably a way to convert between them. - // After the offsets, the bodies of objects pointed above, which apparently have a code like 0xFFXX to identify the type of object, followed by a (unique?) identifier - // for the object. - // A DefineEditText-like object can be easily spotted and apparently uses type code 0x06 (or 0xFF06) but as stated above, - // it is written in a different way. - public IggyFlashHeader32(AbstractDataStream stream) throws IOException { - readFromDataStream(stream); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - main_offset = stream.readUI32(); - as3_section_offset = stream.readUI32(); - unk_offset = stream.readUI32(); - unk_offset2 = stream.readUI32(); - unk_offset3 = stream.readUI32(); - unk_offset4 = stream.readUI32(); - xmin = stream.readUI32(); - ymin = stream.readUI32(); - xmax = stream.readUI32(); - ymax = stream.readUI32(); - unk_28 = stream.readUI32(); - unk_2C = stream.readUI32(); - unk_30 = stream.readUI32(); - unk_34 = stream.readUI32(); - unk_38 = stream.readUI32(); - unk_3C = stream.readUI32(); - frameRate = stream.readFloat(); - unk_44 = stream.readUI32(); - unk_48 = stream.readUI32(); - unk_4C = stream.readUI32(); - unk_3C = stream.readUI32(); - names_offset = stream.readUI32(); - unk_offset5 = stream.readUI32(); - unk_58 = stream.readUI64(); - last_section_offset = stream.readUI32(); - unk_offset6 = stream.readUI32(); - as3_code_offset = stream.readUI32(); - as3_names_offset = stream.readUI32(); - unk_70 = stream.readUI32(); - unk_74 = stream.readUI32(); - unk_78 = stream.readUI32(); - unk_7C = stream.readUI32(); - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append("[\r\n"); - sb.append("main_offset ").append(main_offset).append("\r\n"); - sb.append("as3_section_offset ").append(as3_section_offset).append("\r\n"); - sb.append("unk_offset ").append(unk_offset).append("\r\n"); - sb.append("unk_offset2 ").append(unk_offset2).append("\r\n"); - sb.append("unk_offset3 ").append(unk_offset3).append("\r\n"); - sb.append("unk_offset4 ").append(unk_offset4).append("\r\n"); - sb.append("xmin ").append(xmin).append("\r\n"); - sb.append("ymin ").append(ymin).append("\r\n"); - sb.append("xmax ").append(xmax).append("\r\n"); - sb.append("ymax ").append(ymax).append("\r\n"); - sb.append("unk_28 ").append(unk_28).append("\r\n"); - sb.append("unk_2C ").append(unk_2C).append("\r\n"); - sb.append("unk_30 ").append(unk_30).append("\r\n"); - sb.append("unk_34 ").append(unk_34).append("\r\n"); - sb.append("unk_38 ").append(unk_38).append("\r\n"); - sb.append("unk_3C ").append(unk_3C).append("\r\n"); - sb.append("frameRate ").append(frameRate).append("\r\n"); - sb.append("unk_44 ").append(unk_44).append("\r\n"); - sb.append("unk_48 ").append(unk_48).append("\r\n"); - sb.append("unk_4C ").append(unk_4C).append("\r\n"); - sb.append("names_offset ").append(names_offset).append("\r\n"); - sb.append("unk_offset5 ").append(unk_offset5).append("\r\n"); - sb.append("unk_58 ").append(unk_58).append("\r\n"); - sb.append("last_section_offset ").append(last_section_offset).append("\r\n"); - sb.append("unk_offset6 ").append(unk_offset6).append("\r\n"); - sb.append("as3_code_offset ").append(as3_code_offset).append("\r\n"); - sb.append("as3_names_offset ").append(as3_names_offset).append("\r\n"); - sb.append("unk_70 ").append(unk_70).append("\r\n"); - sb.append("unk_74 ").append(unk_74).append("\r\n"); - sb.append("unk_78 ").append(unk_78).append("\r\n"); - sb.append("unk_7C ").append(unk_7C).append("\r\n"); - sb.append("]"); - return sb.toString(); - } - - @Override - public long getXMin() { - return xmin; - } - - @Override - public long getYMin() { - return ymin; - } - - @Override - public long getXMax() { - return xmax; - } - - @Override - public long getYMax() { - return ymax; - } - - @Override - public float getFrameRate() { - return frameRate; - } -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; + +/** + * + * @author JPEXS + * + * Based of works of somebody called eternity. + * + * All relative offsets are relative from that specific field position All + * relative offsets can get value "1" to indicate "nothing" + */ +public class IggyFlashHeader32 implements IggyFlashHeaderInterface { + + @IggyFieldType(DataType.uint32_t) + long main_offset; // 0 Relative offset to first section (matches sizeof header) + @IggyFieldType(DataType.uint32_t) + long as3_section_offset; // 4 Relative offset to as3 file names table... + @IggyFieldType(DataType.uint32_t) + long unk_offset; // 8 relative offset to something + @IggyFieldType(DataType.uint32_t) + long unk_offset2; // 0xC relative offset to something + @IggyFieldType(DataType.uint32_t) + long unk_offset3; // 0x10 relative offset to something + @IggyFieldType(DataType.uint32_t) + long unk_offset4; // 0x14 relative offset to something + @IggyFieldType(DataType.uint32_t) + long xmin; //0x18 in pixels + @IggyFieldType(DataType.uint32_t) + long ymin; //0x0C in pixels + @IggyFieldType(DataType.uint32_t) + long xmax; // 0x20 in pixels + @IggyFieldType(DataType.uint32_t) + long ymax; // 0x24 in pixels + @IggyFieldType(DataType.uint32_t) + long unk_28; // probably number of blocks/objects after header + @IggyFieldType(DataType.uint32_t) + long unk_2C; + @IggyFieldType(DataType.uint32_t) + long unk_30; + @IggyFieldType(DataType.uint32_t) + long unk_34; + @IggyFieldType(DataType.uint32_t) + long unk_38; + @IggyFieldType(DataType.uint32_t) + long unk_3C; + @IggyFieldType(DataType.float_t) + float frameRate; + @IggyFieldType(DataType.uint32_t) + long unk_44; + @IggyFieldType(DataType.uint32_t) + long unk_48; + @IggyFieldType(DataType.uint32_t) + long unk_4C; + @IggyFieldType(DataType.uint32_t) + long names_offset; // 0x50 relative offset to the names/import section of the file + @IggyFieldType(DataType.uint32_t) + long unk_offset5; // 0x54 relative offset to something + @IggyFieldType(DataType.uint64_t) + long unk_58; // Maybe number of imports/names pointed by names_offset + @IggyFieldType(DataType.uint32_t) + long last_section_offset; // 0x60 relative offset, points to the small last section of the file + @IggyFieldType(DataType.uint32_t) + long unk_offset6; // 0x64 relative offset to something + @IggyFieldType(DataType.uint32_t) + long as3_code_offset; // 0x68 relative offset to as3 code (8 bytes header + abc blob) + @IggyFieldType(DataType.uint32_t) + long as3_names_offset; // 0x6C relative offset to as3 file names table (or classes names or whatever) + @IggyFieldType(DataType.uint32_t) + long unk_70; + @IggyFieldType(DataType.uint32_t) + long unk_74; + @IggyFieldType(DataType.uint32_t) + long unk_78; // Maybe number of classes / as3 names + @IggyFieldType(DataType.uint32_t) + long unk_7C; + + // Offset 0x80 (outside header): there are *unk_28* relative offsets that point to flash objects. + // The flash objects are in a format different to swf but there is probably a way to convert between them. + // After the offsets, the bodies of objects pointed above, which apparently have a code like 0xFFXX to identify the type of object, followed by a (unique?) identifier + // for the object. + // A DefineEditText-like object can be easily spotted and apparently uses type code 0x06 (or 0xFF06) but as stated above, + // it is written in a different way. + public IggyFlashHeader32(AbstractDataStream stream) throws IOException { + readFromDataStream(stream); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + main_offset = stream.readUI32(); + as3_section_offset = stream.readUI32(); + unk_offset = stream.readUI32(); + unk_offset2 = stream.readUI32(); + unk_offset3 = stream.readUI32(); + unk_offset4 = stream.readUI32(); + xmin = stream.readUI32(); + ymin = stream.readUI32(); + xmax = stream.readUI32(); + ymax = stream.readUI32(); + unk_28 = stream.readUI32(); + unk_2C = stream.readUI32(); + unk_30 = stream.readUI32(); + unk_34 = stream.readUI32(); + unk_38 = stream.readUI32(); + unk_3C = stream.readUI32(); + frameRate = stream.readFloat(); + unk_44 = stream.readUI32(); + unk_48 = stream.readUI32(); + unk_4C = stream.readUI32(); + unk_3C = stream.readUI32(); + names_offset = stream.readUI32(); + unk_offset5 = stream.readUI32(); + unk_58 = stream.readUI64(); + last_section_offset = stream.readUI32(); + unk_offset6 = stream.readUI32(); + as3_code_offset = stream.readUI32(); + as3_names_offset = stream.readUI32(); + unk_70 = stream.readUI32(); + unk_74 = stream.readUI32(); + unk_78 = stream.readUI32(); + unk_7C = stream.readUI32(); + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("[\r\n"); + sb.append("main_offset ").append(main_offset).append("\r\n"); + sb.append("as3_section_offset ").append(as3_section_offset).append("\r\n"); + sb.append("unk_offset ").append(unk_offset).append("\r\n"); + sb.append("unk_offset2 ").append(unk_offset2).append("\r\n"); + sb.append("unk_offset3 ").append(unk_offset3).append("\r\n"); + sb.append("unk_offset4 ").append(unk_offset4).append("\r\n"); + sb.append("xmin ").append(xmin).append("\r\n"); + sb.append("ymin ").append(ymin).append("\r\n"); + sb.append("xmax ").append(xmax).append("\r\n"); + sb.append("ymax ").append(ymax).append("\r\n"); + sb.append("unk_28 ").append(unk_28).append("\r\n"); + sb.append("unk_2C ").append(unk_2C).append("\r\n"); + sb.append("unk_30 ").append(unk_30).append("\r\n"); + sb.append("unk_34 ").append(unk_34).append("\r\n"); + sb.append("unk_38 ").append(unk_38).append("\r\n"); + sb.append("unk_3C ").append(unk_3C).append("\r\n"); + sb.append("frameRate ").append(frameRate).append("\r\n"); + sb.append("unk_44 ").append(unk_44).append("\r\n"); + sb.append("unk_48 ").append(unk_48).append("\r\n"); + sb.append("unk_4C ").append(unk_4C).append("\r\n"); + sb.append("names_offset ").append(names_offset).append("\r\n"); + sb.append("unk_offset5 ").append(unk_offset5).append("\r\n"); + sb.append("unk_58 ").append(unk_58).append("\r\n"); + sb.append("last_section_offset ").append(last_section_offset).append("\r\n"); + sb.append("unk_offset6 ").append(unk_offset6).append("\r\n"); + sb.append("as3_code_offset ").append(as3_code_offset).append("\r\n"); + sb.append("as3_names_offset ").append(as3_names_offset).append("\r\n"); + sb.append("unk_70 ").append(unk_70).append("\r\n"); + sb.append("unk_74 ").append(unk_74).append("\r\n"); + sb.append("unk_78 ").append(unk_78).append("\r\n"); + sb.append("unk_7C ").append(unk_7C).append("\r\n"); + sb.append("]"); + return sb.toString(); + } + + @Override + public long getXMin() { + return xmin; + } + + @Override + public long getYMin() { + return ymin; + } + + @Override + public long getXMax() { + return xmax; + } + + @Override + public long getYMax() { + return ymax; + } + + @Override + public float getFrameRate() { + return frameRate; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader64.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader64.java index 51e091c5e..bebb70cd5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader64.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeader64.java @@ -1,211 +1,211 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; - -/** - * - * @author Jindra - * - * Based of works of somebody called eternity. - */ -public class IggyFlashHeader64 implements IggyFlashHeaderInterface { - - @IggyFieldType(DataType.uint64_t) - long main_offset; // 0 Relative offset to first section (matches sizeof header); - @IggyFieldType(DataType.uint64_t) - long as3_section_offset; // 8 Relative offset to as3 file names table... - @IggyFieldType(DataType.uint64_t) - long unk_offset; // 0x10 relative offset to something - @IggyFieldType(DataType.uint64_t) - long unk_offset2; // 0x18 relative offset to something - @IggyFieldType(DataType.uint64_t) - long unk_offset3; // 0x20 relative offset to something - @IggyFieldType(DataType.uint64_t) - long unk_offset4; // 0x28 names_offset; 0x50 relative pointer to the names/import section of the file - @IggyFieldType(DataType.uint32_t) - long xmin; // 0x30 in pixels - @IggyFieldType(DataType.uint32_t) - long ymin; // 0x34 in pixels - @IggyFieldType(DataType.uint32_t) - long xmax; // 0x38 in pixels - @IggyFieldType(DataType.uint32_t) - long ymax; // 0x3C in pixels - @IggyFieldType(DataType.uint32_t) - long unk_40; // probably numer of blocks/objects after header - @IggyFieldType(DataType.uint32_t) - long unk_44; - @IggyFieldType(DataType.uint32_t) - long unk_48; - @IggyFieldType(DataType.uint32_t) - long unk_4C; - @IggyFieldType(DataType.uint32_t) - long unk_50; - @IggyFieldType(DataType.uint32_t) - long unk_54; - @IggyFieldType(DataType.float_t) - float frame_rate; - @IggyFieldType(DataType.uint32_t) - long unk_5C; - @IggyFieldType(DataType.uint64_t) - long fonts_offset; - @IggyFieldType(DataType.uint64_t) - long unk_68; - @IggyFieldType(DataType.uint64_t) - long names_offset; // 0x70 relative offset to the names/import section of the file - end of fonts - @IggyFieldType(DataType.uint64_t) - long unk_offset5; // 0x78 relative offset to something - @IggyFieldType(DataType.uint64_t) - long unk_80; // Maybe number of imports/names pointed by names_offset - @IggyFieldType(DataType.uint64_t) - long last_section_offset; // 0x88 relative offset, points to the small last section of the file - @IggyFieldType(DataType.uint64_t) - long unk_offset6; // 0x90 relative offset to something - @IggyFieldType(DataType.uint64_t) - long as3_code_offset; // 0x98 relative offset to as3 code (16 bytes header + abc blob) - @IggyFieldType(DataType.uint64_t) - long as3_names_offset; // 0xA0 relative offset to as3 file names table (or classes names or whatever) - @IggyFieldType(DataType.uint32_t) - long unk_A8; - @IggyFieldType(DataType.uint32_t) //font_main_off - long unk_AC; //font_main_size - @IggyFieldType(DataType.uint32_t) //font_info_off - long font_count; // Maybe number of classes / as3 names //font_info_size - @IggyFieldType(DataType.uint32_t) - long unk_B4; //zero (?) - - // Offset 0xB8 (outside header): there are *unk_40* relative offsets that point to flash objects. - // The flash objects are in a format different to swf but there is probably a way to convert between them. - // After the offsets, the bodies of objects pointed above, which apparently have a code like 0xFFXX to identify the type of object, followed by a (unique?) identifier - // for the object. - // A DefineEditText-like object can be easily spotted and apparently uses type code 0x06 (or 0xFF06) but as stated above, - // it is written in a different way. - public IggyFlashHeader64(AbstractDataStream stream) throws IOException { - readFromDataStream(stream); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - main_offset = stream.readUI64(); - as3_section_offset = stream.readUI64(); - unk_offset = stream.readUI64(); - unk_offset2 = stream.readUI64(); - unk_offset3 = stream.readUI64(); - unk_offset4 = stream.readUI64(); - xmin = stream.readUI32(); - ymin = stream.readUI32(); - xmax = stream.readUI32(); - ymax = stream.readUI32(); - unk_40 = stream.readUI32(); - unk_44 = stream.readUI32(); - unk_48 = stream.readUI32(); - unk_4C = stream.readUI32(); - unk_50 = stream.readUI32(); - unk_54 = stream.readUI32(); - frame_rate = stream.readFloat(); - unk_5C = stream.readUI32(); - fonts_offset = stream.readUI64(); - unk_68 = stream.readUI64(); - names_offset = stream.readUI64(); - unk_offset5 = stream.readUI64(); - unk_80 = stream.readUI64(); - last_section_offset = stream.readUI64(); - unk_offset6 = stream.readUI64(); - as3_code_offset = stream.readUI64(); - as3_names_offset = stream.readUI64(); - unk_A8 = stream.readUI32(); - unk_AC = stream.readUI32(); - font_count = stream.readUI32(); - unk_B4 = stream.readUI32(); - - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[\r\n"); - sb.append("main_offset ").append(main_offset).append("\r\n"); - sb.append("as3_section_offset ").append(as3_section_offset); - sb.append(" global: ").append(main_offset + as3_section_offset); - sb.append("\r\n"); - sb.append("unk_offset ").append(unk_offset); - if (unk_offset != 1) { - sb.append(" global: ").append(main_offset + unk_offset); - } - sb.append("\r\n"); - sb.append("unk_offset2 ").append(unk_offset2); - if (unk_offset2 != 1) { - sb.append(" global: ").append(main_offset + unk_offset2); - } - sb.append("\r\n"); - sb.append("unk_offset3 ").append(unk_offset3); - if (unk_offset3 != 1) { - sb.append(" global: ").append(main_offset + unk_offset3); - } - sb.append("\r\n"); - sb.append("unk_offset4 ").append(unk_offset4); - if (unk_offset4 != 1) { - sb.append(" global: ").append(main_offset + unk_offset4); - } - sb.append("\r\n"); - sb.append("xmin ").append(xmin).append("\r\n"); - sb.append("ymin ").append(ymin).append("\r\n"); - sb.append("xmax ").append(ymax).append("\r\n"); - sb.append("ymax ").append(ymax).append("\r\n"); - sb.append("unk_40 ").append(unk_40).append("\r\n"); - sb.append("unk_44 ").append(unk_44).append("\r\n"); - sb.append("unk_48 ").append(unk_48).append("\r\n"); - sb.append("unk_4C ").append(unk_4C).append("\r\n"); - sb.append("unk_50 ").append(unk_50).append("\r\n"); - sb.append("unk_54 ").append(unk_54).append("\r\n"); - sb.append("frame_rate ").append(frame_rate).append("\r\n"); - sb.append("unk_5C ").append(unk_5C).append("\r\n"); - sb.append("fonts_offset ").append(fonts_offset).append("\r\n"); - sb.append("unk_68 ").append(unk_68).append("\r\n"); - sb.append("names_offset ").append(names_offset).append("\r\n"); - sb.append("unk_offset5 ").append(unk_offset5).append("\r\n"); - sb.append("unk_80 ").append(unk_80).append("\r\n"); - sb.append("last_section_offset ").append(last_section_offset).append("\r\n"); - sb.append("unk_offset6 ").append(unk_offset6).append("\r\n"); - sb.append("as3_code_offset ").append(as3_code_offset).append("\r\n"); - sb.append("as3_names_offset ").append(as3_names_offset).append("\r\n"); - sb.append("unk_A8 ").append(unk_A8).append("\r\n"); - sb.append("unk_AC ").append(unk_AC).append("\r\n"); - sb.append("font_count ").append(font_count).append("\r\n"); - sb.append("unk_B4 ").append(unk_B4).append("\r\n"); - sb.append("]"); - return sb.toString(); - - } - - @Override - public long getXMin() { - return xmin; - } - - @Override - public long getYMin() { - return ymin; - } - - @Override - public long getXMax() { - return xmax; - } - - @Override - public long getYMax() { - return ymax; - } - - @Override - public float getFrameRate() { - return frame_rate; - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; + +/** + * + * @author Jindra + * + * Based of works of somebody called eternity. + */ +public class IggyFlashHeader64 implements IggyFlashHeaderInterface { + + @IggyFieldType(DataType.uint64_t) + long main_offset; // 0 Relative offset to first section (matches sizeof header); + @IggyFieldType(DataType.uint64_t) + long as3_section_offset; // 8 Relative offset to as3 file names table... + @IggyFieldType(DataType.uint64_t) + long unk_offset; // 0x10 relative offset to something + @IggyFieldType(DataType.uint64_t) + long unk_offset2; // 0x18 relative offset to something + @IggyFieldType(DataType.uint64_t) + long unk_offset3; // 0x20 relative offset to something + @IggyFieldType(DataType.uint64_t) + long unk_offset4; // 0x28 names_offset; 0x50 relative pointer to the names/import section of the file + @IggyFieldType(DataType.uint32_t) + long xmin; // 0x30 in pixels + @IggyFieldType(DataType.uint32_t) + long ymin; // 0x34 in pixels + @IggyFieldType(DataType.uint32_t) + long xmax; // 0x38 in pixels + @IggyFieldType(DataType.uint32_t) + long ymax; // 0x3C in pixels + @IggyFieldType(DataType.uint32_t) + long unk_40; // probably numer of blocks/objects after header + @IggyFieldType(DataType.uint32_t) + long unk_44; + @IggyFieldType(DataType.uint32_t) + long unk_48; + @IggyFieldType(DataType.uint32_t) + long unk_4C; + @IggyFieldType(DataType.uint32_t) + long unk_50; + @IggyFieldType(DataType.uint32_t) + long unk_54; + @IggyFieldType(DataType.float_t) + float frame_rate; + @IggyFieldType(DataType.uint32_t) + long unk_5C; + @IggyFieldType(DataType.uint64_t) + long fonts_offset; + @IggyFieldType(DataType.uint64_t) + long unk_68; + @IggyFieldType(DataType.uint64_t) + long names_offset; // 0x70 relative offset to the names/import section of the file - end of fonts + @IggyFieldType(DataType.uint64_t) + long unk_offset5; // 0x78 relative offset to something + @IggyFieldType(DataType.uint64_t) + long unk_80; // Maybe number of imports/names pointed by names_offset + @IggyFieldType(DataType.uint64_t) + long last_section_offset; // 0x88 relative offset, points to the small last section of the file + @IggyFieldType(DataType.uint64_t) + long unk_offset6; // 0x90 relative offset to something + @IggyFieldType(DataType.uint64_t) + long as3_code_offset; // 0x98 relative offset to as3 code (16 bytes header + abc blob) + @IggyFieldType(DataType.uint64_t) + long as3_names_offset; // 0xA0 relative offset to as3 file names table (or classes names or whatever) + @IggyFieldType(DataType.uint32_t) + long unk_A8; + @IggyFieldType(DataType.uint32_t) //font_main_off + long unk_AC; //font_main_size + @IggyFieldType(DataType.uint32_t) //font_info_off + long font_count; // Maybe number of classes / as3 names //font_info_size + @IggyFieldType(DataType.uint32_t) + long unk_B4; //zero (?) + + // Offset 0xB8 (outside header): there are *unk_40* relative offsets that point to flash objects. + // The flash objects are in a format different to swf but there is probably a way to convert between them. + // After the offsets, the bodies of objects pointed above, which apparently have a code like 0xFFXX to identify the type of object, followed by a (unique?) identifier + // for the object. + // A DefineEditText-like object can be easily spotted and apparently uses type code 0x06 (or 0xFF06) but as stated above, + // it is written in a different way. + public IggyFlashHeader64(AbstractDataStream stream) throws IOException { + readFromDataStream(stream); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + main_offset = stream.readUI64(); + as3_section_offset = stream.readUI64(); + unk_offset = stream.readUI64(); + unk_offset2 = stream.readUI64(); + unk_offset3 = stream.readUI64(); + unk_offset4 = stream.readUI64(); + xmin = stream.readUI32(); + ymin = stream.readUI32(); + xmax = stream.readUI32(); + ymax = stream.readUI32(); + unk_40 = stream.readUI32(); + unk_44 = stream.readUI32(); + unk_48 = stream.readUI32(); + unk_4C = stream.readUI32(); + unk_50 = stream.readUI32(); + unk_54 = stream.readUI32(); + frame_rate = stream.readFloat(); + unk_5C = stream.readUI32(); + fonts_offset = stream.readUI64(); + unk_68 = stream.readUI64(); + names_offset = stream.readUI64(); + unk_offset5 = stream.readUI64(); + unk_80 = stream.readUI64(); + last_section_offset = stream.readUI64(); + unk_offset6 = stream.readUI64(); + as3_code_offset = stream.readUI64(); + as3_names_offset = stream.readUI64(); + unk_A8 = stream.readUI32(); + unk_AC = stream.readUI32(); + font_count = stream.readUI32(); + unk_B4 = stream.readUI32(); + + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[\r\n"); + sb.append("main_offset ").append(main_offset).append("\r\n"); + sb.append("as3_section_offset ").append(as3_section_offset); + sb.append(" global: ").append(main_offset + as3_section_offset); + sb.append("\r\n"); + sb.append("unk_offset ").append(unk_offset); + if (unk_offset != 1) { + sb.append(" global: ").append(main_offset + unk_offset); + } + sb.append("\r\n"); + sb.append("unk_offset2 ").append(unk_offset2); + if (unk_offset2 != 1) { + sb.append(" global: ").append(main_offset + unk_offset2); + } + sb.append("\r\n"); + sb.append("unk_offset3 ").append(unk_offset3); + if (unk_offset3 != 1) { + sb.append(" global: ").append(main_offset + unk_offset3); + } + sb.append("\r\n"); + sb.append("unk_offset4 ").append(unk_offset4); + if (unk_offset4 != 1) { + sb.append(" global: ").append(main_offset + unk_offset4); + } + sb.append("\r\n"); + sb.append("xmin ").append(xmin).append("\r\n"); + sb.append("ymin ").append(ymin).append("\r\n"); + sb.append("xmax ").append(ymax).append("\r\n"); + sb.append("ymax ").append(ymax).append("\r\n"); + sb.append("unk_40 ").append(unk_40).append("\r\n"); + sb.append("unk_44 ").append(unk_44).append("\r\n"); + sb.append("unk_48 ").append(unk_48).append("\r\n"); + sb.append("unk_4C ").append(unk_4C).append("\r\n"); + sb.append("unk_50 ").append(unk_50).append("\r\n"); + sb.append("unk_54 ").append(unk_54).append("\r\n"); + sb.append("frame_rate ").append(frame_rate).append("\r\n"); + sb.append("unk_5C ").append(unk_5C).append("\r\n"); + sb.append("fonts_offset ").append(fonts_offset).append("\r\n"); + sb.append("unk_68 ").append(unk_68).append("\r\n"); + sb.append("names_offset ").append(names_offset).append("\r\n"); + sb.append("unk_offset5 ").append(unk_offset5).append("\r\n"); + sb.append("unk_80 ").append(unk_80).append("\r\n"); + sb.append("last_section_offset ").append(last_section_offset).append("\r\n"); + sb.append("unk_offset6 ").append(unk_offset6).append("\r\n"); + sb.append("as3_code_offset ").append(as3_code_offset).append("\r\n"); + sb.append("as3_names_offset ").append(as3_names_offset).append("\r\n"); + sb.append("unk_A8 ").append(unk_A8).append("\r\n"); + sb.append("unk_AC ").append(unk_AC).append("\r\n"); + sb.append("font_count ").append(font_count).append("\r\n"); + sb.append("unk_B4 ").append(unk_B4).append("\r\n"); + sb.append("]"); + return sb.toString(); + + } + + @Override + public long getXMin() { + return xmin; + } + + @Override + public long getYMin() { + return ymin; + } + + @Override + public long getXMax() { + return xmax; + } + + @Override + public long getYMax() { + return ymax; + } + + @Override + public float getFrameRate() { + return frame_rate; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeaderInterface.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeaderInterface.java index fc07b1ba6..0cde6ac8a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeaderInterface.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFlashHeaderInterface.java @@ -1,18 +1,18 @@ -package com.jpexs.decompiler.flash.iggy; - -/** - * - * @author JPEXS - */ -public interface IggyFlashHeaderInterface extends StructureInterface { - - public long getXMin(); - - public long getYMin(); - - public long getXMax(); - - public long getYMax(); - - public float getFrameRate(); -} +package com.jpexs.decompiler.flash.iggy; + +/** + * + * @author JPEXS + */ +public interface IggyFlashHeaderInterface extends StructureInterface { + + public long getXMin(); + + public long getYMin(); + + public long getXMax(); + + public long getYMax(); + + public float getFrameRate(); +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFont.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFont.java index 41a0baa58..bd9def55c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFont.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyFont.java @@ -1,316 +1,316 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType; -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class IggyFont implements StructureInterface { - - public static final int ID = 0xFF16; - - @IggyFieldType(DataType.uint16_t) - int type; //stejny pro rozdilne fonty - @IggyFieldType(DataType.uint16_t) - int fontId; - @IggyArrayFieldType(value = DataType.uint8_t, count = 28) - byte[] zeroone; // stejny pro rozdilne fonty - @IggyFieldType(DataType.uint16_t) - int char_count2; - @IggyFieldType(value = DataType.uint16_t) - int ascent; - @IggyFieldType(value = DataType.uint16_t) - int descent; - @IggyFieldType(value = DataType.uint16_t) - int leading; - @IggyFieldType(DataType.uint64_t) - long flags; - @IggyFieldType(DataType.uint64_t) - long start_of_char_struct; - @IggyFieldType(DataType.uint64_t) - long start_of_char_index; - @IggyFieldType(DataType.uint64_t) - long start_of_scale; - @IggyFieldType(DataType.uint32_t) - long kern_count; - @IggyArrayFieldType(value = DataType.float_t, count = 5) - float[] unk_float; - @IggyFieldType(DataType.uint64_t) - long start_of_kern; - @IggyFieldType(DataType.uint64_t) - long zero_padd; - @IggyFieldType(DataType.uint64_t) - long what_2; - @IggyFieldType(DataType.uint64_t) - long zero_padd_2; - @IggyFieldType(DataType.uint64_t) - long start_of_name; - @IggyFieldType(DataType.uint64_t) - long one_padd; - @IggyFieldType(DataType.uint16_t) - int xscale; - @IggyFieldType(DataType.uint16_t) - int yscale; - @IggyFieldType(DataType.uint64_t) - long zero_padd_3; - @IggyFieldType(DataType.float_t) - float ssr1; - @IggyFieldType(DataType.float_t) - float ssr2; - @IggyFieldType(DataType.uint32_t) - long char_count; - @IggyFieldType(DataType.uint64_t) - long zero_padd_4; - @IggyFieldType(DataType.uint64_t) - long what_3; - @IggyFieldType(value = DataType.uint8_t, count = 272) - byte[] zeroes; - @IggyFieldType(DataType.float_t) - float sss1; - @IggyFieldType(DataType.uint32_t) - long one_padd2; - @IggyFieldType(DataType.float_t) - float sss2; - @IggyFieldType(DataType.uint32_t) - long one_padd3; - @IggyFieldType(DataType.float_t) - float sss3; - @IggyFieldType(DataType.uint32_t) - long one_padd4; - @IggyFieldType(DataType.float_t) - float sss4; - @IggyFieldType(DataType.uint32_t) - long one_padd5; - - @IggyFieldType(value = DataType.widechar_t, count = 16) - String name; - - List charOffsets; - List glyphs; - IggyCharIndices codePoints; - IggyCharAdvances charScales; - IggyCharKerning charKernings; - - byte[] padTo4byteBoundary; - - public IggyFont(int type, int order_in_iggy_file, byte[] zeroone, int char_count2, int ascent, int descent, int leading, long flags, long start_of_char_struct, long start_of_char_index, long start_of_scale, long kern_count, float[] unk_float, long start_of_kern, long zero_padd, long what_2, long zero_padd_2, long start_of_name, long one_padd, int xscale, int yscale, long zero_padd_3, float ssr1, float ssr2, long char_count, long zero_padd_4, long what_3, byte[] zeroes, float sss1, long one_padd2, float sss2, long one_padd3, float sss3, long one_padd4, float sss4, long one_padd5, String name, List charOffsets, List chars, IggyCharIndices charIndices, IggyCharAdvances charScales, IggyCharKerning charKernings, byte[] padTo4byteBoundary) { - this.type = type; - this.fontId = order_in_iggy_file; - this.zeroone = zeroone; - this.char_count2 = char_count2; - this.ascent = ascent; - this.descent = descent; - this.leading = leading; - this.flags = flags; - this.start_of_char_struct = start_of_char_struct; - this.start_of_char_index = start_of_char_index; - this.start_of_scale = start_of_scale; - this.kern_count = kern_count; - this.unk_float = unk_float; - this.start_of_kern = start_of_kern; - this.zero_padd = zero_padd; - this.what_2 = what_2; - this.zero_padd_2 = zero_padd_2; - this.start_of_name = start_of_name; - this.one_padd = one_padd; - this.xscale = xscale; - this.yscale = yscale; - this.zero_padd_3 = zero_padd_3; - this.ssr1 = ssr1; - this.ssr2 = ssr2; - this.char_count = char_count; - this.zero_padd_4 = zero_padd_4; - this.what_3 = what_3; - this.zeroes = zeroes; - this.sss1 = sss1; - this.one_padd2 = one_padd2; - this.sss2 = sss2; - this.one_padd3 = one_padd3; - this.sss3 = sss3; - this.one_padd4 = one_padd4; - this.sss4 = sss4; - this.one_padd5 = one_padd5; - this.name = name; - this.charOffsets = charOffsets; - this.glyphs = chars; - this.codePoints = charIndices; - this.charScales = charScales; - this.charKernings = charKernings; - this.padTo4byteBoundary = padTo4byteBoundary; - } - - public IggyFont(AbstractDataStream stream) throws IOException { - readFromDataStream(stream); - } - - private long readAbsoluteOffset(AbstractDataStream stream) throws IOException { - long offset = stream.readUI64(); - if (offset == 1) { - return 0; - } - return stream.position() - 8 + offset; - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - ByteArrayDataStream s = new ByteArrayDataStream(stream.readBytes((int) (long) stream.available()), stream.is64()); - type = s.readUI16(); - fontId = s.readUI16(); - zeroone = s.readBytes(28); - char_count2 = s.readUI16(); - ascent = s.readUI16(); - descent = s.readUI16(); - leading = s.readUI16(); - flags = s.readUI64(); - start_of_char_struct = readAbsoluteOffset(s); - start_of_char_index = readAbsoluteOffset(s); - start_of_scale = readAbsoluteOffset(s); - kern_count = s.readUI32(); - unk_float = new float[5]; - for (int i = 0; i < unk_float.length; i++) { - unk_float[i] = s.readFloat(); - } - start_of_kern = readAbsoluteOffset(s); - zero_padd = s.readUI64(); - what_2 = s.readUI64(); - zero_padd_2 = s.readUI64(); - start_of_name = readAbsoluteOffset(s); - one_padd = s.readUI64(); - xscale = s.readUI16(); - yscale = s.readUI16(); - zero_padd_3 = s.readUI64(); - ssr1 = s.readFloat(); - ssr2 = s.readFloat(); - char_count = s.readUI32(); - zero_padd_4 = s.readUI64(); - what_3 = s.readUI64(); - s.seek(272, SeekMode.CUR); - sss1 = s.readFloat(); - one_padd2 = s.readUI32(); - sss2 = s.readFloat(); - one_padd3 = s.readUI32(); - sss3 = s.readFloat(); - one_padd4 = s.readUI32(); - sss4 = s.readFloat(); - one_padd5 = s.readUI32(); - if (start_of_name != 0) { - s.seek(start_of_name, SeekMode.SET); - StringBuilder nameBuilder = new StringBuilder(); - int nameCharCnt = 0; - do { - char c = (char) s.readUI16(); - nameCharCnt++; - if (c == '\0') { - break; - } - nameBuilder.append(c); - } while (true); - s.seek(32 - nameCharCnt * 2, SeekMode.CUR); - name = nameBuilder.toString(); - } - s.readUI64(); //pad zero - if (start_of_char_struct != 0) { - s.seek(start_of_char_struct, SeekMode.SET); - charOffsets = new ArrayList<>(); - for (int i = 0; i < char_count; i++) { - charOffsets.add(new IggyCharOffset(s)); - } - glyphs = new ArrayList<>(); - for (int i = 0; i < char_count; i++) { - long offset = charOffsets.get(i).offset; - glyphs.add(new IggyShape(s, offset)); - } - } - if (start_of_char_index != 0) { - s.seek(start_of_char_index, SeekMode.SET); - codePoints = new IggyCharIndices(s, char_count); - } - if (start_of_scale != 0) { - s.seek(start_of_scale, SeekMode.SET); - charScales = new IggyCharAdvances(s, char_count); - } - if (start_of_kern != 0) { - s.seek(start_of_kern, SeekMode.SET); - charKernings = new IggyCharKerning(s, kern_count); - } - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - public int getType() { - return type; - } - - public long getFlags() { - return flags; - } - - public int getXscale() { - return xscale; - } - - public int getYscale() { - return yscale; - } - - public long getCharacterCount() { - return char_count; - } - - public String getName() { - return name; - } - - public List getChars() { - return glyphs; - } - - public IggyCharIndices getCharIndices() { - return codePoints; - } - - public IggyCharAdvances getCharAdvances() { - return charScales; - } - - public IggyCharKerning getCharKernings() { - return charKernings; - } - - public float[] getUnk_float() { - return unk_float; - } - - public int getAscent() { - return ascent; - } - - public int getDescent() { - return descent; - } - - public int getLeading() { - return leading; - } - - public long getWhat_2() { - return what_2; - } - - public long getWhat_3() { - return what_3; - } - - public List getCharOffsets() { - return charOffsets; - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType; +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class IggyFont implements StructureInterface { + + public static final int ID = 0xFF16; + + @IggyFieldType(DataType.uint16_t) + int type; //stejny pro rozdilne fonty + @IggyFieldType(DataType.uint16_t) + int fontId; + @IggyArrayFieldType(value = DataType.uint8_t, count = 28) + byte[] zeroone; // stejny pro rozdilne fonty + @IggyFieldType(DataType.uint16_t) + int char_count2; + @IggyFieldType(value = DataType.uint16_t) + int ascent; + @IggyFieldType(value = DataType.uint16_t) + int descent; + @IggyFieldType(value = DataType.uint16_t) + int leading; + @IggyFieldType(DataType.uint64_t) + long flags; + @IggyFieldType(DataType.uint64_t) + long start_of_char_struct; + @IggyFieldType(DataType.uint64_t) + long start_of_char_index; + @IggyFieldType(DataType.uint64_t) + long start_of_scale; + @IggyFieldType(DataType.uint32_t) + long kern_count; + @IggyArrayFieldType(value = DataType.float_t, count = 5) + float[] unk_float; + @IggyFieldType(DataType.uint64_t) + long start_of_kern; + @IggyFieldType(DataType.uint64_t) + long zero_padd; + @IggyFieldType(DataType.uint64_t) + long what_2; + @IggyFieldType(DataType.uint64_t) + long zero_padd_2; + @IggyFieldType(DataType.uint64_t) + long start_of_name; + @IggyFieldType(DataType.uint64_t) + long one_padd; + @IggyFieldType(DataType.uint16_t) + int xscale; + @IggyFieldType(DataType.uint16_t) + int yscale; + @IggyFieldType(DataType.uint64_t) + long zero_padd_3; + @IggyFieldType(DataType.float_t) + float ssr1; + @IggyFieldType(DataType.float_t) + float ssr2; + @IggyFieldType(DataType.uint32_t) + long char_count; + @IggyFieldType(DataType.uint64_t) + long zero_padd_4; + @IggyFieldType(DataType.uint64_t) + long what_3; + @IggyFieldType(value = DataType.uint8_t, count = 272) + byte[] zeroes; + @IggyFieldType(DataType.float_t) + float sss1; + @IggyFieldType(DataType.uint32_t) + long one_padd2; + @IggyFieldType(DataType.float_t) + float sss2; + @IggyFieldType(DataType.uint32_t) + long one_padd3; + @IggyFieldType(DataType.float_t) + float sss3; + @IggyFieldType(DataType.uint32_t) + long one_padd4; + @IggyFieldType(DataType.float_t) + float sss4; + @IggyFieldType(DataType.uint32_t) + long one_padd5; + + @IggyFieldType(value = DataType.widechar_t, count = 16) + String name; + + List charOffsets; + List glyphs; + IggyCharIndices codePoints; + IggyCharAdvances charScales; + IggyCharKerning charKernings; + + byte[] padTo4byteBoundary; + + public IggyFont(int type, int order_in_iggy_file, byte[] zeroone, int char_count2, int ascent, int descent, int leading, long flags, long start_of_char_struct, long start_of_char_index, long start_of_scale, long kern_count, float[] unk_float, long start_of_kern, long zero_padd, long what_2, long zero_padd_2, long start_of_name, long one_padd, int xscale, int yscale, long zero_padd_3, float ssr1, float ssr2, long char_count, long zero_padd_4, long what_3, byte[] zeroes, float sss1, long one_padd2, float sss2, long one_padd3, float sss3, long one_padd4, float sss4, long one_padd5, String name, List charOffsets, List chars, IggyCharIndices charIndices, IggyCharAdvances charScales, IggyCharKerning charKernings, byte[] padTo4byteBoundary) { + this.type = type; + this.fontId = order_in_iggy_file; + this.zeroone = zeroone; + this.char_count2 = char_count2; + this.ascent = ascent; + this.descent = descent; + this.leading = leading; + this.flags = flags; + this.start_of_char_struct = start_of_char_struct; + this.start_of_char_index = start_of_char_index; + this.start_of_scale = start_of_scale; + this.kern_count = kern_count; + this.unk_float = unk_float; + this.start_of_kern = start_of_kern; + this.zero_padd = zero_padd; + this.what_2 = what_2; + this.zero_padd_2 = zero_padd_2; + this.start_of_name = start_of_name; + this.one_padd = one_padd; + this.xscale = xscale; + this.yscale = yscale; + this.zero_padd_3 = zero_padd_3; + this.ssr1 = ssr1; + this.ssr2 = ssr2; + this.char_count = char_count; + this.zero_padd_4 = zero_padd_4; + this.what_3 = what_3; + this.zeroes = zeroes; + this.sss1 = sss1; + this.one_padd2 = one_padd2; + this.sss2 = sss2; + this.one_padd3 = one_padd3; + this.sss3 = sss3; + this.one_padd4 = one_padd4; + this.sss4 = sss4; + this.one_padd5 = one_padd5; + this.name = name; + this.charOffsets = charOffsets; + this.glyphs = chars; + this.codePoints = charIndices; + this.charScales = charScales; + this.charKernings = charKernings; + this.padTo4byteBoundary = padTo4byteBoundary; + } + + public IggyFont(AbstractDataStream stream) throws IOException { + readFromDataStream(stream); + } + + private long readAbsoluteOffset(AbstractDataStream stream) throws IOException { + long offset = stream.readUI64(); + if (offset == 1) { + return 0; + } + return stream.position() - 8 + offset; + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + ByteArrayDataStream s = new ByteArrayDataStream(stream.readBytes((int) (long) stream.available()), stream.is64()); + type = s.readUI16(); + fontId = s.readUI16(); + zeroone = s.readBytes(28); + char_count2 = s.readUI16(); + ascent = s.readUI16(); + descent = s.readUI16(); + leading = s.readUI16(); + flags = s.readUI64(); + start_of_char_struct = readAbsoluteOffset(s); + start_of_char_index = readAbsoluteOffset(s); + start_of_scale = readAbsoluteOffset(s); + kern_count = s.readUI32(); + unk_float = new float[5]; + for (int i = 0; i < unk_float.length; i++) { + unk_float[i] = s.readFloat(); + } + start_of_kern = readAbsoluteOffset(s); + zero_padd = s.readUI64(); + what_2 = s.readUI64(); + zero_padd_2 = s.readUI64(); + start_of_name = readAbsoluteOffset(s); + one_padd = s.readUI64(); + xscale = s.readUI16(); + yscale = s.readUI16(); + zero_padd_3 = s.readUI64(); + ssr1 = s.readFloat(); + ssr2 = s.readFloat(); + char_count = s.readUI32(); + zero_padd_4 = s.readUI64(); + what_3 = s.readUI64(); + s.seek(272, SeekMode.CUR); + sss1 = s.readFloat(); + one_padd2 = s.readUI32(); + sss2 = s.readFloat(); + one_padd3 = s.readUI32(); + sss3 = s.readFloat(); + one_padd4 = s.readUI32(); + sss4 = s.readFloat(); + one_padd5 = s.readUI32(); + if (start_of_name != 0) { + s.seek(start_of_name, SeekMode.SET); + StringBuilder nameBuilder = new StringBuilder(); + int nameCharCnt = 0; + do { + char c = (char) s.readUI16(); + nameCharCnt++; + if (c == '\0') { + break; + } + nameBuilder.append(c); + } while (true); + s.seek(32 - nameCharCnt * 2, SeekMode.CUR); + name = nameBuilder.toString(); + } + s.readUI64(); //pad zero + if (start_of_char_struct != 0) { + s.seek(start_of_char_struct, SeekMode.SET); + charOffsets = new ArrayList<>(); + for (int i = 0; i < char_count; i++) { + charOffsets.add(new IggyCharOffset(s)); + } + glyphs = new ArrayList<>(); + for (int i = 0; i < char_count; i++) { + long offset = charOffsets.get(i).offset; + glyphs.add(new IggyShape(s, offset)); + } + } + if (start_of_char_index != 0) { + s.seek(start_of_char_index, SeekMode.SET); + codePoints = new IggyCharIndices(s, char_count); + } + if (start_of_scale != 0) { + s.seek(start_of_scale, SeekMode.SET); + charScales = new IggyCharAdvances(s, char_count); + } + if (start_of_kern != 0) { + s.seek(start_of_kern, SeekMode.SET); + charKernings = new IggyCharKerning(s, kern_count); + } + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getType() { + return type; + } + + public long getFlags() { + return flags; + } + + public int getXscale() { + return xscale; + } + + public int getYscale() { + return yscale; + } + + public long getCharacterCount() { + return char_count; + } + + public String getName() { + return name; + } + + public List getChars() { + return glyphs; + } + + public IggyCharIndices getCharIndices() { + return codePoints; + } + + public IggyCharAdvances getCharAdvances() { + return charScales; + } + + public IggyCharKerning getCharKernings() { + return charKernings; + } + + public float[] getUnk_float() { + return unk_float; + } + + public int getAscent() { + return ascent; + } + + public int getDescent() { + return descent; + } + + public int getLeading() { + return leading; + } + + public long getWhat_2() { + return what_2; + } + + public long getWhat_3() { + return what_3; + } + + public List getCharOffsets() { + return charOffsets; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyHeader.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyHeader.java index 067b7cd5c..ca9b8f9a2 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyHeader.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyHeader.java @@ -1,153 +1,153 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType; -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; - -/** - * - * @author JPEXS - * - * little endian all - * - * Based of works of somebody called eternity. - */ -public class IggyHeader implements StructureInterface { - - public static long MAGIC = 0xED0A6749; - - //Must be 0xED0A6749 - @IggyFieldType(DataType.uint32_t) - private long magic = MAGIC; - - //Assume 0x900 - @IggyFieldType(DataType.uint32_t) - private long version; - - //Assuming: 1 - @IggyFieldType(value = DataType.uint8_t) - private int platform1; - - //32/64 - @IggyFieldType(value = DataType.uint8_t) - private int platform2; - - //Assuming: 1 - @IggyFieldType(value = DataType.uint8_t) - private int platform3; - - //Usually: 3 - @IggyFieldType(value = DataType.uint8_t) - private int platform4; - - //flags for platform 64? - @IggyFieldType(DataType.uint32_t) - private long unk_0C; - - @IggyArrayFieldType(value = DataType.uint8_t, count = 12) - private byte[] reserved; - - @IggyFieldType(value = DataType.uint32_t) - private long numSubfiles; - - public IggyHeader(AbstractDataStream stream) throws IOException { - readFromDataStream(stream); - } - - /** - * - * @param version - * @param platform1 - * @param platform2 32/64 - * @param platform3 - * @param platform4 - * @param unk_0C - * @param reserved - * @param num_subfiles - */ - public IggyHeader(long version, int platform1, int platform2, int platform3, int platform4, long unk_0C, byte[] reserved, long num_subfiles) { - this.version = version; - this.platform1 = platform1; - this.platform2 = platform2; - this.platform3 = platform3; - this.platform4 = platform4; - this.unk_0C = unk_0C; - this.reserved = reserved; - this.numSubfiles = num_subfiles; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("["); - sb.append("version: ").append(version).append(", "); - sb.append("platform: ").append(platform1).append(" ").append(platform2).append(" ").append(platform3).append(" ").append(platform4).append(", "); - sb.append("unk_0C: ").append(String.format("%08X", unk_0C)).append(", "); - sb.append("reserved: 12 bytes").append(", "); - sb.append("num_subfiles: ").append(numSubfiles); - sb.append("]"); - return sb.toString(); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - magic = stream.readUI32(); - if (magic != IggyHeader.MAGIC) { - throw new IOException("Invalid Iggy file"); - } - version = stream.readUI32(); - platform1 = stream.readUI8(); - platform2 = stream.readUI8(); //32/64 - platform3 = stream.readUI8(); - platform4 = stream.readUI8(); - unk_0C = stream.readUI32(); - reserved = stream.readBytes(12); - numSubfiles = stream.readUI32(); - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - public boolean is64() { - return platform2 == 64; - } - - public long getMagic() { - return magic; - } - - public long getVersion() { - return version; - } - - public int getPlatform1() { - return platform1; - } - - public int getPlatform2() { - return platform2; - } - - public int getPlatform3() { - return platform3; - } - - public int getPlatform4() { - return platform4; - } - - public long getUnk_0C() { - return unk_0C; - } - - public byte[] getReserved() { - return reserved; - } - - public long getNumSubfiles() { - return numSubfiles; - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType; +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; + +/** + * + * @author JPEXS + * + * little endian all + * + * Based of works of somebody called eternity. + */ +public class IggyHeader implements StructureInterface { + + public static long MAGIC = 0xED0A6749; + + //Must be 0xED0A6749 + @IggyFieldType(DataType.uint32_t) + private long magic = MAGIC; + + //Assume 0x900 + @IggyFieldType(DataType.uint32_t) + private long version; + + //Assuming: 1 + @IggyFieldType(value = DataType.uint8_t) + private int platform1; + + //32/64 + @IggyFieldType(value = DataType.uint8_t) + private int platform2; + + //Assuming: 1 + @IggyFieldType(value = DataType.uint8_t) + private int platform3; + + //Usually: 3 + @IggyFieldType(value = DataType.uint8_t) + private int platform4; + + //flags for platform 64? + @IggyFieldType(DataType.uint32_t) + private long unk_0C; + + @IggyArrayFieldType(value = DataType.uint8_t, count = 12) + private byte[] reserved; + + @IggyFieldType(value = DataType.uint32_t) + private long numSubfiles; + + public IggyHeader(AbstractDataStream stream) throws IOException { + readFromDataStream(stream); + } + + /** + * + * @param version + * @param platform1 + * @param platform2 32/64 + * @param platform3 + * @param platform4 + * @param unk_0C + * @param reserved + * @param num_subfiles + */ + public IggyHeader(long version, int platform1, int platform2, int platform3, int platform4, long unk_0C, byte[] reserved, long num_subfiles) { + this.version = version; + this.platform1 = platform1; + this.platform2 = platform2; + this.platform3 = platform3; + this.platform4 = platform4; + this.unk_0C = unk_0C; + this.reserved = reserved; + this.numSubfiles = num_subfiles; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + sb.append("version: ").append(version).append(", "); + sb.append("platform: ").append(platform1).append(" ").append(platform2).append(" ").append(platform3).append(" ").append(platform4).append(", "); + sb.append("unk_0C: ").append(String.format("%08X", unk_0C)).append(", "); + sb.append("reserved: 12 bytes").append(", "); + sb.append("num_subfiles: ").append(numSubfiles); + sb.append("]"); + return sb.toString(); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + magic = stream.readUI32(); + if (magic != IggyHeader.MAGIC) { + throw new IOException("Invalid Iggy file"); + } + version = stream.readUI32(); + platform1 = stream.readUI8(); + platform2 = stream.readUI8(); //32/64 + platform3 = stream.readUI8(); + platform4 = stream.readUI8(); + unk_0C = stream.readUI32(); + reserved = stream.readBytes(12); + numSubfiles = stream.readUI32(); + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean is64() { + return platform2 == 64; + } + + public long getMagic() { + return magic; + } + + public long getVersion() { + return version; + } + + public int getPlatform1() { + return platform1; + } + + public int getPlatform2() { + return platform2; + } + + public int getPlatform3() { + return platform3; + } + + public int getPlatform4() { + return platform4; + } + + public long getUnk_0C() { + return unk_0C; + } + + public byte[] getReserved() { + return reserved; + } + + public long getNumSubfiles() { + return numSubfiles; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyIndexParser.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyIndexParser.java index b3bbb9868..f5c963b7f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyIndexParser.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyIndexParser.java @@ -1,165 +1,165 @@ -package com.jpexs.decompiler.flash.iggy; - -import java.io.IOException; -import java.util.List; -import java.util.logging.Logger; - -/** - * - * @author Jindra - */ -public class IggyIndexParser { - - private static Logger LOGGER = Logger.getLogger(IggyIndexParser.class.getName()); - - /* - Offsets: - 1) name - 2) UI16 - zero - 3) tag list - 4) ... tag data offsets - - */ - /** - * Parser for index data. It creates table of indices and table of offsets - * - * @param indexStream Stream of index - * @param indexTableEntry Output index tabke - * @param offsets Output list of offsets - * @throws IOException on error - */ - public static void parseIndex(ByteArrayDataStream indexStream, List indexTableEntry, List offsets) throws IOException { - int indexTableSize = indexStream.readUI8(); - int[] indexTable = new int[indexTableSize]; - for (int i = 0; i < indexTableSize; i++) { - int offset = indexStream.readUI8(); - LOGGER.fine(String.format("index_table_entry: %02x\n", offset)); - indexTable[i] = offset; - indexTableEntry.add(offset); - int num = indexStream.readUI8(); - indexStream.seek(num * 2, SeekMode.CUR); - } - - long offset = 0; - int code; - - while ((code = indexStream.readUI8()) > -1) { - LOGGER.finer(String.format("Code = %x\n", code)); - - if (code < 0x80) // 0-0x7F - { - // code is directly an index to the index_table - if (code >= indexTableSize) { - LOGGER.severe(String.format("< 0x80: index is greater than index_table_size. %x > %x\n", code, indexTableSize)); - return; - } - - offset += indexTable[code]; - } else if (code < 0xC0) // 0x80-BF - { - int index; - - if ((index = indexStream.readUI8()) < 0) { - LOGGER.severe(String.format("< 0xC0: Cannot read index.\n")); - return; - } - - if (index >= indexTableSize) { - LOGGER.severe(String.format("< 0xC0: index is greater than index_table_size. %x > %x\n", index, indexTableSize)); - return; - } - - int n = code - 0x7F; - offset += indexTable[index] * n; - } else if (code < 0xD0) // 0xC0-0xCF - { - offset += ((code * 2) - 0x17E); - } else if (code < 0xE0) // 0xD0-0xDF - { - // Code here depends on plattform[0], we are assuming it is 1, as we checked in load function - int i = code & 0xF; - int n8; - int n; - - if ((n8 = indexStream.readUI8()) < 0) { - LOGGER.severe(String.format("< 0xE0: Cannot read n.\n")); - return; - } - - n = n8 + 1; - - if (indexStream.is64()) { - if (i <= 2) { - offset += 8 * n; // Ptr type - } else if (i <= 4) { - offset += 2 * n; - } else if (i == 5) { - offset += 4 * n; - } else if (i == 6) { - offset += 8 * n; // 64 bits type - } else { - LOGGER.severe(String.format("< 0xE0: Invalid value for i (%x %x)\n", i, code)); - } - } else { - switch (i) { - case 2: - offset += 4 * n; // Ptr type - break; - case 4: - offset += 2 * n; - break; - case 5: - offset += 4 * n; // 32 bits type - break; - case 6: - offset += 8 * n; - break; - default: - LOGGER.severe(String.format("< 0xE0: invalid value for i (%x %x)\n", i, code)); - } - } - } else if (code == 0xFC) { - indexStream.seek(1, SeekMode.CUR); - } else if (code == 0xFD) { - int n, m; - - if ((n = indexStream.readUI8()) < 0) { - LOGGER.severe(String.format("0xFD: Cannot read n.\n")); - return; - } - - if ((m = indexStream.readUI8()) < 0) { - LOGGER.severe(String.format("0xFD: Cannot read m.\n")); - return; - } - - offset += n; - indexStream.seek(m * 2, SeekMode.CUR); - } else if (code == 0xFE) { - int n8; - int n; - - if ((n8 = indexStream.readUI8()) < 0) { - LOGGER.severe(String.format("0xFE: Cannot read n.\n")); - return; - } - - n = n8 + 1; - offset += n; - } else if (code == 0xFF) { - long n; - - if ((n = indexStream.readUI32()) < 0) { - LOGGER.severe(String.format("0xFF: Cannot read n.\n")); - return; - } - - offset += n; - } else { - LOGGER.warning(String.format("Unrecognized code: %x\n", code)); - } - - offsets.add(offset); - } - } -} +package com.jpexs.decompiler.flash.iggy; + +import java.io.IOException; +import java.util.List; +import java.util.logging.Logger; + +/** + * + * @author Jindra + */ +public class IggyIndexParser { + + private static Logger LOGGER = Logger.getLogger(IggyIndexParser.class.getName()); + + /* + Offsets: + 1) name + 2) UI16 - zero + 3) tag list + 4) ... tag data offsets + + */ + /** + * Parser for index data. It creates table of indices and table of offsets + * + * @param indexStream Stream of index + * @param indexTableEntry Output index tabke + * @param offsets Output list of offsets + * @throws IOException on error + */ + public static void parseIndex(ByteArrayDataStream indexStream, List indexTableEntry, List offsets) throws IOException { + int indexTableSize = indexStream.readUI8(); + int[] indexTable = new int[indexTableSize]; + for (int i = 0; i < indexTableSize; i++) { + int offset = indexStream.readUI8(); + LOGGER.fine(String.format("index_table_entry: %02x\n", offset)); + indexTable[i] = offset; + indexTableEntry.add(offset); + int num = indexStream.readUI8(); + indexStream.seek(num * 2, SeekMode.CUR); + } + + long offset = 0; + int code; + + while ((code = indexStream.readUI8()) > -1) { + LOGGER.finer(String.format("Code = %x\n", code)); + + if (code < 0x80) // 0-0x7F + { + // code is directly an index to the index_table + if (code >= indexTableSize) { + LOGGER.severe(String.format("< 0x80: index is greater than index_table_size. %x > %x\n", code, indexTableSize)); + return; + } + + offset += indexTable[code]; + } else if (code < 0xC0) // 0x80-BF + { + int index; + + if ((index = indexStream.readUI8()) < 0) { + LOGGER.severe(String.format("< 0xC0: Cannot read index.\n")); + return; + } + + if (index >= indexTableSize) { + LOGGER.severe(String.format("< 0xC0: index is greater than index_table_size. %x > %x\n", index, indexTableSize)); + return; + } + + int n = code - 0x7F; + offset += indexTable[index] * n; + } else if (code < 0xD0) // 0xC0-0xCF + { + offset += ((code * 2) - 0x17E); + } else if (code < 0xE0) // 0xD0-0xDF + { + // Code here depends on plattform[0], we are assuming it is 1, as we checked in load function + int i = code & 0xF; + int n8; + int n; + + if ((n8 = indexStream.readUI8()) < 0) { + LOGGER.severe(String.format("< 0xE0: Cannot read n.\n")); + return; + } + + n = n8 + 1; + + if (indexStream.is64()) { + if (i <= 2) { + offset += 8 * n; // Ptr type + } else if (i <= 4) { + offset += 2 * n; + } else if (i == 5) { + offset += 4 * n; + } else if (i == 6) { + offset += 8 * n; // 64 bits type + } else { + LOGGER.severe(String.format("< 0xE0: Invalid value for i (%x %x)\n", i, code)); + } + } else { + switch (i) { + case 2: + offset += 4 * n; // Ptr type + break; + case 4: + offset += 2 * n; + break; + case 5: + offset += 4 * n; // 32 bits type + break; + case 6: + offset += 8 * n; + break; + default: + LOGGER.severe(String.format("< 0xE0: invalid value for i (%x %x)\n", i, code)); + } + } + } else if (code == 0xFC) { + indexStream.seek(1, SeekMode.CUR); + } else if (code == 0xFD) { + int n, m; + + if ((n = indexStream.readUI8()) < 0) { + LOGGER.severe(String.format("0xFD: Cannot read n.\n")); + return; + } + + if ((m = indexStream.readUI8()) < 0) { + LOGGER.severe(String.format("0xFD: Cannot read m.\n")); + return; + } + + offset += n; + indexStream.seek(m * 2, SeekMode.CUR); + } else if (code == 0xFE) { + int n8; + int n; + + if ((n8 = indexStream.readUI8()) < 0) { + LOGGER.severe(String.format("0xFE: Cannot read n.\n")); + return; + } + + n = n8 + 1; + offset += n; + } else if (code == 0xFF) { + long n; + + if ((n = indexStream.readUI32()) < 0) { + LOGGER.severe(String.format("0xFF: Cannot read n.\n")); + return; + } + + offset += n; + } else { + LOGGER.warning(String.format("Unrecognized code: %x\n", code)); + } + + offsets.add(offset); + } + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShape.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShape.java index 863366226..e5272877e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShape.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShape.java @@ -1,155 +1,155 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class IggyShape implements StructureInterface { - - private static Logger LOGGER = Logger.getLogger(IggyShape.class.getName()); - - @IggyFieldType(DataType.float_t) - float minx; //bearing X - this is the horizontal distance from the current pen position to the glyph's left bbox edge. - @IggyFieldType(DataType.float_t) - float miny; //bearing Y - this is the vertical distance from the baseline to the top of the glyph's bbox. - @IggyFieldType(DataType.float_t) - float maxx; //advanceX - bearingX - @IggyFieldType(DataType.float_t) - float maxy; //advanceY - bearingY - @IggyFieldType(DataType.uint64_t) - long unk; // stejny vetsinou - napr. 48 - JP: to by mohlo byt advance - @IggyFieldType(DataType.uint64_t) - long count; - @IggyFieldType(DataType.uint64_t) - long one; // 1 - @IggyFieldType(DataType.uint64_t) - long one2; // 1 - @IggyFieldType(DataType.uint64_t) - long one3; // 1 - @IggyFieldType(DataType.uint32_t) - long one4; // 1 - @IggyFieldType(DataType.uint32_t) - long two1; // 2 - - public float getBearingX() { - return minx; - } - - public float getBearingY() { - return miny; - } - - public float getWidth() { - return maxx - minx; - } - - public float getHeight() { - return maxy - miny; - } - - List nodes; - - private long offset; - - public IggyShape(AbstractDataStream stream, long offset) throws IOException { - this.offset = offset; - readFromDataStream(stream); - } - - public IggyShape(float minx, float miny, float maxx, float maxy, long advance, long count, long one, long one2, long one3, long one4, long two1, List nodes) { - this.minx = minx; - this.miny = miny; - this.maxx = maxx; - this.maxy = maxy; - this.unk = advance; - this.count = count; - this.one = one; - this.one2 = one2; - this.one3 = one3; - this.one4 = one4; - this.two1 = two1; - this.nodes = nodes; - } - - @Override - public void readFromDataStream(AbstractDataStream s) throws IOException { - s.seek(offset, SeekMode.SET); - minx = s.readFloat(); - miny = s.readFloat(); - maxx = s.readFloat(); - maxy = s.readFloat(); - unk = s.readUI64(); - count = s.readUI64(); - one = s.readUI64(); - one2 = s.readUI64(); - one3 = s.readUI64(); - one4 = s.readUI32(); - two1 = s.readUI32(); - - if ((one != 1) || (one2 != 1) || (one3 != 1) || (one4 != 1) || (two1 != 2)) { - LOGGER.fine(String.format("Unique header at pos %d, one: %d, one2: %d, one3: %d, one4: %d, two1: %d\n", offset, one, one2, one3, one4, two1)); - } - - nodes = new ArrayList<>(); - for (int i = 0; i < count; i++) { - nodes.add(new IggyShapeNode(s, i == 0)); - } - - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - public float getMinx() { - return minx; - } - - public float getMiny() { - return miny; - } - - public float getMaxx() { - return maxx; - } - - public float getMaxy() { - return maxy; - } - - public long getUnk() { - return unk; - } - - public long getOne() { - return one; - } - - public long getOne2() { - return one2; - } - - public long getOne3() { - return one3; - } - - public long getOne4() { - return one4; - } - - public long getTwo1() { - return two1; - } - - public List getNodes() { - return nodes; - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class IggyShape implements StructureInterface { + + private static Logger LOGGER = Logger.getLogger(IggyShape.class.getName()); + + @IggyFieldType(DataType.float_t) + float minx; //bearing X - this is the horizontal distance from the current pen position to the glyph's left bbox edge. + @IggyFieldType(DataType.float_t) + float miny; //bearing Y - this is the vertical distance from the baseline to the top of the glyph's bbox. + @IggyFieldType(DataType.float_t) + float maxx; //advanceX - bearingX + @IggyFieldType(DataType.float_t) + float maxy; //advanceY - bearingY + @IggyFieldType(DataType.uint64_t) + long unk; // stejny vetsinou - napr. 48 - JP: to by mohlo byt advance + @IggyFieldType(DataType.uint64_t) + long count; + @IggyFieldType(DataType.uint64_t) + long one; // 1 + @IggyFieldType(DataType.uint64_t) + long one2; // 1 + @IggyFieldType(DataType.uint64_t) + long one3; // 1 + @IggyFieldType(DataType.uint32_t) + long one4; // 1 + @IggyFieldType(DataType.uint32_t) + long two1; // 2 + + public float getBearingX() { + return minx; + } + + public float getBearingY() { + return miny; + } + + public float getWidth() { + return maxx - minx; + } + + public float getHeight() { + return maxy - miny; + } + + List nodes; + + private long offset; + + public IggyShape(AbstractDataStream stream, long offset) throws IOException { + this.offset = offset; + readFromDataStream(stream); + } + + public IggyShape(float minx, float miny, float maxx, float maxy, long advance, long count, long one, long one2, long one3, long one4, long two1, List nodes) { + this.minx = minx; + this.miny = miny; + this.maxx = maxx; + this.maxy = maxy; + this.unk = advance; + this.count = count; + this.one = one; + this.one2 = one2; + this.one3 = one3; + this.one4 = one4; + this.two1 = two1; + this.nodes = nodes; + } + + @Override + public void readFromDataStream(AbstractDataStream s) throws IOException { + s.seek(offset, SeekMode.SET); + minx = s.readFloat(); + miny = s.readFloat(); + maxx = s.readFloat(); + maxy = s.readFloat(); + unk = s.readUI64(); + count = s.readUI64(); + one = s.readUI64(); + one2 = s.readUI64(); + one3 = s.readUI64(); + one4 = s.readUI32(); + two1 = s.readUI32(); + + if ((one != 1) || (one2 != 1) || (one3 != 1) || (one4 != 1) || (two1 != 2)) { + LOGGER.fine(String.format("Unique header at pos %d, one: %d, one2: %d, one3: %d, one4: %d, two1: %d\n", offset, one, one2, one3, one4, two1)); + } + + nodes = new ArrayList<>(); + for (int i = 0; i < count; i++) { + nodes.add(new IggyShapeNode(s, i == 0)); + } + + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public float getMinx() { + return minx; + } + + public float getMiny() { + return miny; + } + + public float getMaxx() { + return maxx; + } + + public float getMaxy() { + return maxy; + } + + public long getUnk() { + return unk; + } + + public long getOne() { + return one; + } + + public long getOne2() { + return one2; + } + + public long getOne3() { + return one3; + } + + public long getOne4() { + return one4; + } + + public long getTwo1() { + return two1; + } + + public List getNodes() { + return nodes; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShapeNode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShapeNode.java index 4668a00a6..d235c9562 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShapeNode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyShapeNode.java @@ -1,122 +1,122 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author JPEXS - */ -public class IggyShapeNode implements StructureInterface { - - private static Logger LOGGER = Logger.getLogger(IggyShapeNode.class.getName()); - - public static int NODE_TYPE_MOVE = 1; - public static int NODE_TYPE_LINE_TO = 2; - public static int NODE_TYPE_CURVE_POINT = 3; - - @IggyFieldType(DataType.float_t) - float targetX; - @IggyFieldType(DataType.float_t) - float targetY; // negative - @IggyFieldType(DataType.float_t) - float controlX; // for curves - @IggyFieldType(DataType.float_t) - float controlY; // for curves, negative - @IggyFieldType(DataType.uint8_t) //1-moveto, 2-lineto , 3 - curve to - int node_type; - @IggyFieldType(DataType.uint8_t) // 208 start smooth (for j=1 only), 61 smooth interupt (muze a nemusi byt pro novy oddeleny kus charu - kdyz je subtype predchoziho vetsi nez 0 (kupr 5) bude pro oddeleny usek 61, jinak pokud je subtype predchoziho 0 bude pro oddeleny usek 0) - int node_subtype; - @IggyFieldType(DataType.uint8_t) - int zer1; - @IggyFieldType(DataType.uint8_t) - int zer2; - @IggyFieldType(DataType.uint32_t) - long isstart; // 1 v prubehu nebo 0 pouze pro prvni (i kdyz jsou delene jako dvojtecka!!!) - - private boolean first; - - public IggyShapeNode(float x1, float y1, float x2, float y2, int node_type, int node_subtype, int zer1, int zer2, long isstart) { - this.targetX = x1; - this.targetY = y1; - this.controlX = x2; - this.controlY = y2; - this.node_type = node_type; - this.node_subtype = node_subtype; - this.zer1 = zer1; - this.zer2 = zer2; - this.isstart = isstart; - } - - public IggyShapeNode(AbstractDataStream s, boolean first) throws IOException { - this.first = first; - readFromDataStream(s); - } - - @Override - public void readFromDataStream(AbstractDataStream s) throws IOException { - targetX = s.readFloat(); - targetY = s.readFloat(); - controlX = s.readFloat(); - controlY = s.readFloat(); - node_type = s.readUI8(); - node_subtype = s.readUI8(); - zer1 = s.readUI8(); - zer2 = s.readUI8(); - isstart = s.readUI32(); - - if ((zer1 != 0) | (zer2 != 0)) { - LOGGER.fine(String.format("Unknown zeroes at pos %08X\n", s.position() - 6)); - } - if ((!first) & (isstart != 1)) { - LOGGER.fine(String.format("Unknown format at pos %08X\n", s.position() - 4)); - } - if ((first) & (isstart != 0)) { - LOGGER.fine(String.format("Unknown format at pos %08X\n", s.position() - 4)); - } - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - public float getX1() { - return targetX; - } - - public float getY1() { - return targetY; - } - - public float getX2() { - return controlX; - } - - public float getY2() { - return controlY; - } - - public int getNodeType() { - return node_type; - } - - public int getNodeSubType() { - return node_subtype; - } - - public int getZer1() { - return zer1; - } - - public int getZer2() { - return zer2; - } - - public boolean isStart() { - return isstart == 1; - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author JPEXS + */ +public class IggyShapeNode implements StructureInterface { + + private static Logger LOGGER = Logger.getLogger(IggyShapeNode.class.getName()); + + public static int NODE_TYPE_MOVE = 1; + public static int NODE_TYPE_LINE_TO = 2; + public static int NODE_TYPE_CURVE_POINT = 3; + + @IggyFieldType(DataType.float_t) + float targetX; + @IggyFieldType(DataType.float_t) + float targetY; // negative + @IggyFieldType(DataType.float_t) + float controlX; // for curves + @IggyFieldType(DataType.float_t) + float controlY; // for curves, negative + @IggyFieldType(DataType.uint8_t) //1-moveto, 2-lineto , 3 - curve to + int node_type; + @IggyFieldType(DataType.uint8_t) // 208 start smooth (for j=1 only), 61 smooth interupt (muze a nemusi byt pro novy oddeleny kus charu - kdyz je subtype predchoziho vetsi nez 0 (kupr 5) bude pro oddeleny usek 61, jinak pokud je subtype predchoziho 0 bude pro oddeleny usek 0) + int node_subtype; + @IggyFieldType(DataType.uint8_t) + int zer1; + @IggyFieldType(DataType.uint8_t) + int zer2; + @IggyFieldType(DataType.uint32_t) + long isstart; // 1 v prubehu nebo 0 pouze pro prvni (i kdyz jsou delene jako dvojtecka!!!) + + private boolean first; + + public IggyShapeNode(float x1, float y1, float x2, float y2, int node_type, int node_subtype, int zer1, int zer2, long isstart) { + this.targetX = x1; + this.targetY = y1; + this.controlX = x2; + this.controlY = y2; + this.node_type = node_type; + this.node_subtype = node_subtype; + this.zer1 = zer1; + this.zer2 = zer2; + this.isstart = isstart; + } + + public IggyShapeNode(AbstractDataStream s, boolean first) throws IOException { + this.first = first; + readFromDataStream(s); + } + + @Override + public void readFromDataStream(AbstractDataStream s) throws IOException { + targetX = s.readFloat(); + targetY = s.readFloat(); + controlX = s.readFloat(); + controlY = s.readFloat(); + node_type = s.readUI8(); + node_subtype = s.readUI8(); + zer1 = s.readUI8(); + zer2 = s.readUI8(); + isstart = s.readUI32(); + + if ((zer1 != 0) | (zer2 != 0)) { + LOGGER.fine(String.format("Unknown zeroes at pos %08X\n", s.position() - 6)); + } + if ((!first) & (isstart != 1)) { + LOGGER.fine(String.format("Unknown format at pos %08X\n", s.position() - 4)); + } + if ((first) & (isstart != 0)) { + LOGGER.fine(String.format("Unknown format at pos %08X\n", s.position() - 4)); + } + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public float getX1() { + return targetX; + } + + public float getY1() { + return targetY; + } + + public float getX2() { + return controlX; + } + + public float getY2() { + return controlY; + } + + public int getNodeType() { + return node_type; + } + + public int getNodeSubType() { + return node_subtype; + } + + public int getZer1() { + return zer1; + } + + public int getZer2() { + return zer2; + } + + public boolean isStart() { + return isstart == 1; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggySubFileEntry.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggySubFileEntry.java index c1a2cd141..e08a16cf9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggySubFileEntry.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggySubFileEntry.java @@ -1,70 +1,70 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - * - * Based of works of somebody called eternity. - */ -public class IggySubFileEntry implements StructureInterface { - - public static final int TYPE_INDEX = 0; - public static final int TYPE_FLASH = 1; - - @SWFType(BasicType.UI32) - long type; - - @SWFType(BasicType.UI32) - long size; - - //apparently same as size, maybe (un)compressed (?) - @SWFType(BasicType.UI32) - long size2; - - //absolute offset - @SWFType(BasicType.UI32) - long offset; - - public IggySubFileEntry(AbstractDataStream stream) throws IOException { - readFromDataStream(stream); - } - - public IggySubFileEntry(long type, long size, long size2, long offset) { - this.type = type; - this.size = size; - this.size2 = size2; - this.offset = offset; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("["); - sb.append("id: ").append(type).append(", "); - sb.append("size: ").append(size).append(", "); - sb.append("size2: ").append(size2).append(", "); - sb.append("offset: ").append(offset); - sb.append("]"); - return sb.toString(); - } - - @Override - public void readFromDataStream(AbstractDataStream stream) throws IOException { - type = stream.readUI32(); - size = stream.readUI32(); - size2 = stream.readUI32(); - offset = stream.readUI32(); - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + * + * Based of works of somebody called eternity. + */ +public class IggySubFileEntry implements StructureInterface { + + public static final int TYPE_INDEX = 0; + public static final int TYPE_FLASH = 1; + + @SWFType(BasicType.UI32) + long type; + + @SWFType(BasicType.UI32) + long size; + + //apparently same as size, maybe (un)compressed (?) + @SWFType(BasicType.UI32) + long size2; + + //absolute offset + @SWFType(BasicType.UI32) + long offset; + + public IggySubFileEntry(AbstractDataStream stream) throws IOException { + readFromDataStream(stream); + } + + public IggySubFileEntry(long type, long size, long size2, long offset) { + this.type = type; + this.size = size; + this.size2 = size2; + this.offset = offset; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + sb.append("id: ").append(type).append(", "); + sb.append("size: ").append(size).append(", "); + sb.append("size2: ").append(size2).append(", "); + sb.append("offset: ").append(offset); + sb.append("]"); + return sb.toString(); + } + + @Override + public void readFromDataStream(AbstractDataStream stream) throws IOException { + type = stream.readUI32(); + size = stream.readUI32(); + size2 = stream.readUI32(); + offset = stream.readUI32(); + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyText.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyText.java index fd8ec62c6..9931df6ea 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyText.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/IggyText.java @@ -1,194 +1,194 @@ -package com.jpexs.decompiler.flash.iggy; - -import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType; -import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; -import java.io.IOException; - -/** - * - * @author JPEXS - */ -public class IggyText implements StructureInterface { - - public static final int ID = 0xFF06; - - @IggyFieldType(DataType.uint16_t) - int type; // Tag type - @IggyFieldType(DataType.uint16_t) - int textIndex; - @IggyArrayFieldType(value = DataType.uint8_t, count = 28) - byte zeroone[]; - @IggyFieldType(DataType.float_t) - float par1; - @IggyFieldType(DataType.float_t) - float par2; - @IggyFieldType(DataType.float_t) - float par3; - @IggyFieldType(DataType.float_t) - float par4; - @IggyFieldType(DataType.uint16_t) - int enum_hex; - - //Guessed - boolean hasText; - boolean wordWrap; - boolean multiline; - boolean password; - boolean readOnly; - boolean hasTextColor; - boolean hasMaxLength; - boolean hasFont; - boolean hasFontClass; - boolean autosize; - boolean hasLayout; - boolean noSelect; - boolean border; - boolean wasStatic; - boolean html; - boolean useOutlines; - - @IggyFieldType(DataType.uint16_t) - int fontIndex; - @IggyFieldType(DataType.uint32_t) - long zero; - @IggyFieldType(DataType.uint64_t) - long one; - @IggyArrayFieldType(value = DataType.uint8_t, count = 32) - byte[] some; // same for different fonts - @IggyArrayFieldType(value = DataType.widechar_t) - String initialText; //till end of info file? - - public IggyText(int type, int order_in_iggy_file, byte[] zeroone, float par1, float par2, float par3, float par4, int enum_hex, int for_which_font_order_in_iggyfile, long zero, long one, byte[] some, long offset_of_name, String name) { - this.type = type; - this.textIndex = order_in_iggy_file; - this.zeroone = zeroone; - this.par1 = par1; - this.par2 = par2; - this.par3 = par3; - this.par4 = par4; - this.enum_hex = enum_hex; - this.fontIndex = for_which_font_order_in_iggyfile; - this.zero = zero; - this.one = one; - this.some = some; - this.initialText = name; - } - - public IggyText(AbstractDataStream stream) throws IOException { - this.readFromDataStream(stream); - } - - @Override - public void readFromDataStream(AbstractDataStream s) throws IOException { - - type = s.readUI16(); - //characterId - iggy Id - textIndex = s.readUI16(); - zeroone = s.readBytes(28); - - //bounds?: - par1 = s.readFloat(); - par2 = s.readFloat(); - par3 = s.readFloat(); - par4 = s.readFloat(); - - //flags - enum_hex = s.readUI16(); - - int en = enum_hex; - - //guessing - it could be like DefineEditText?... - hasText = ((en >> 0) & 1) == 1; - wordWrap = ((en >> 1) & 1) == 1; - multiline = ((en >> 2) & 1) == 1; - password = ((en >> 3) & 1) == 1; - readOnly = ((en >> 4) & 1) == 1; - hasTextColor = ((en >> 5) & 1) == 1; - hasMaxLength = ((en >> 6) & 1) == 1; - hasFont = ((en >> 7) & 1) == 1; - hasFontClass = ((en >> 8) & 1) == 1; - autosize = ((en >> 9) & 1) == 1; - hasLayout = ((en >> 10) & 1) == 1; - noSelect = ((en >> 11) & 1) == 1; - border = ((en >> 12) & 1) == 1; - wasStatic = ((en >> 13) & 1) == 1; - html = ((en >> 14) & 1) == 1; - useOutlines = ((en >> 15) & 1) == 1; - - //if hasFont? - fontIndex = s.readUI16(); //fontId - //if hasFontClass - readString? - //if hasFont || hasFontClass - readFontHeight? - //if hasTextColor....? - zero = s.readUI32(); - one = s.readUI64(); //01CB FF33 3333 - some = s.readBytes(32); // [6] => 40, [24] => 8 - StringBuilder textBuilder = new StringBuilder(); - do { - char c = (char) s.readUI16(); - if (c == '\0') { - break; - } - textBuilder.append(c); - } while (true); - initialText = textBuilder.toString(); - } - - @Override - public void writeToDataStream(AbstractDataStream stream) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - public int getType() { - return type; - } - - public int getTextIndex() { - return textIndex; - } - - public byte[] getZeroone() { - return zeroone; - } - - public float getPar1() { - return par1; - } - - public float getPar2() { - return par2; - } - - public float getPar3() { - return par3; - } - - public float getPar4() { - return par4; - } - - public int getEnum_hex() { - return enum_hex; - } - - public int getFontIndex() { - return fontIndex; - } - - public long getZero() { - return zero; - } - - public long getOne() { - return one; - } - - public byte[] getSome() { - return some; - } - - public String getInitialText() { - return initialText; - } - -} +package com.jpexs.decompiler.flash.iggy; + +import com.jpexs.decompiler.flash.iggy.annotations.IggyArrayFieldType; +import com.jpexs.decompiler.flash.iggy.annotations.IggyFieldType; +import java.io.IOException; + +/** + * + * @author JPEXS + */ +public class IggyText implements StructureInterface { + + public static final int ID = 0xFF06; + + @IggyFieldType(DataType.uint16_t) + int type; // Tag type + @IggyFieldType(DataType.uint16_t) + int textIndex; + @IggyArrayFieldType(value = DataType.uint8_t, count = 28) + byte zeroone[]; + @IggyFieldType(DataType.float_t) + float par1; + @IggyFieldType(DataType.float_t) + float par2; + @IggyFieldType(DataType.float_t) + float par3; + @IggyFieldType(DataType.float_t) + float par4; + @IggyFieldType(DataType.uint16_t) + int enum_hex; + + //Guessed + boolean hasText; + boolean wordWrap; + boolean multiline; + boolean password; + boolean readOnly; + boolean hasTextColor; + boolean hasMaxLength; + boolean hasFont; + boolean hasFontClass; + boolean autosize; + boolean hasLayout; + boolean noSelect; + boolean border; + boolean wasStatic; + boolean html; + boolean useOutlines; + + @IggyFieldType(DataType.uint16_t) + int fontIndex; + @IggyFieldType(DataType.uint32_t) + long zero; + @IggyFieldType(DataType.uint64_t) + long one; + @IggyArrayFieldType(value = DataType.uint8_t, count = 32) + byte[] some; // same for different fonts + @IggyArrayFieldType(value = DataType.widechar_t) + String initialText; //till end of info file? + + public IggyText(int type, int order_in_iggy_file, byte[] zeroone, float par1, float par2, float par3, float par4, int enum_hex, int for_which_font_order_in_iggyfile, long zero, long one, byte[] some, long offset_of_name, String name) { + this.type = type; + this.textIndex = order_in_iggy_file; + this.zeroone = zeroone; + this.par1 = par1; + this.par2 = par2; + this.par3 = par3; + this.par4 = par4; + this.enum_hex = enum_hex; + this.fontIndex = for_which_font_order_in_iggyfile; + this.zero = zero; + this.one = one; + this.some = some; + this.initialText = name; + } + + public IggyText(AbstractDataStream stream) throws IOException { + this.readFromDataStream(stream); + } + + @Override + public void readFromDataStream(AbstractDataStream s) throws IOException { + + type = s.readUI16(); + //characterId - iggy Id + textIndex = s.readUI16(); + zeroone = s.readBytes(28); + + //bounds?: + par1 = s.readFloat(); + par2 = s.readFloat(); + par3 = s.readFloat(); + par4 = s.readFloat(); + + //flags + enum_hex = s.readUI16(); + + int en = enum_hex; + + //guessing - it could be like DefineEditText?... + hasText = ((en >> 0) & 1) == 1; + wordWrap = ((en >> 1) & 1) == 1; + multiline = ((en >> 2) & 1) == 1; + password = ((en >> 3) & 1) == 1; + readOnly = ((en >> 4) & 1) == 1; + hasTextColor = ((en >> 5) & 1) == 1; + hasMaxLength = ((en >> 6) & 1) == 1; + hasFont = ((en >> 7) & 1) == 1; + hasFontClass = ((en >> 8) & 1) == 1; + autosize = ((en >> 9) & 1) == 1; + hasLayout = ((en >> 10) & 1) == 1; + noSelect = ((en >> 11) & 1) == 1; + border = ((en >> 12) & 1) == 1; + wasStatic = ((en >> 13) & 1) == 1; + html = ((en >> 14) & 1) == 1; + useOutlines = ((en >> 15) & 1) == 1; + + //if hasFont? + fontIndex = s.readUI16(); //fontId + //if hasFontClass - readString? + //if hasFont || hasFontClass - readFontHeight? + //if hasTextColor....? + zero = s.readUI32(); + one = s.readUI64(); //01CB FF33 3333 + some = s.readBytes(32); // [6] => 40, [24] => 8 + StringBuilder textBuilder = new StringBuilder(); + do { + char c = (char) s.readUI16(); + if (c == '\0') { + break; + } + textBuilder.append(c); + } while (true); + initialText = textBuilder.toString(); + } + + @Override + public void writeToDataStream(AbstractDataStream stream) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getType() { + return type; + } + + public int getTextIndex() { + return textIndex; + } + + public byte[] getZeroone() { + return zeroone; + } + + public float getPar1() { + return par1; + } + + public float getPar2() { + return par2; + } + + public float getPar3() { + return par3; + } + + public float getPar4() { + return par4; + } + + public int getEnum_hex() { + return enum_hex; + } + + public int getFontIndex() { + return fontIndex; + } + + public long getZero() { + return zero; + } + + public long getOne() { + return one; + } + + public byte[] getSome() { + return some; + } + + public String getInitialText() { + return initialText; + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/SeekMode.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/SeekMode.java index 9ae2ffbfe..449abf4ea 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/SeekMode.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/SeekMode.java @@ -1,9 +1,9 @@ -package com.jpexs.decompiler.flash.iggy; - -/** - * - * @author Jindra - */ -public enum SeekMode { - SET, CUR, END -} +package com.jpexs.decompiler.flash.iggy; + +/** + * + * @author Jindra + */ +public enum SeekMode { + SET, CUR, END +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/StructureInterface.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/StructureInterface.java index c48ad2a1b..8638c42e0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/StructureInterface.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/StructureInterface.java @@ -1,15 +1,15 @@ -package com.jpexs.decompiler.flash.iggy; - -import java.io.IOException; -import java.util.List; - -/** - * - * @author JPEXS - */ -public interface StructureInterface { - - public void readFromDataStream(AbstractDataStream stream) throws IOException; - - public void writeToDataStream(AbstractDataStream stream) throws IOException; -} +package com.jpexs.decompiler.flash.iggy; + +import java.io.IOException; +import java.util.List; + +/** + * + * @author JPEXS + */ +public interface StructureInterface { + + public void readFromDataStream(AbstractDataStream stream) throws IOException; + + public void writeToDataStream(AbstractDataStream stream) throws IOException; +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyArrayFieldType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyArrayFieldType.java index 19e39b675..b3e3dd86a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyArrayFieldType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyArrayFieldType.java @@ -1,26 +1,26 @@ -package com.jpexs.decompiler.flash.iggy.annotations; - -import com.jpexs.decompiler.flash.iggy.DataType; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author JPEXS - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface IggyArrayFieldType { - - /// Type of value - DataType value() default DataType.unknown; - - int count() default -1; - - /// Field name on which Count depends - String countField() default ""; - - //Count to add to countField - int countAdd() default 0; -} +package com.jpexs.decompiler.flash.iggy.annotations; + +import com.jpexs.decompiler.flash.iggy.DataType; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author JPEXS + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface IggyArrayFieldType { + + /// Type of value + DataType value() default DataType.unknown; + + int count() default -1; + + /// Field name on which Count depends + String countField() default ""; + + //Count to add to countField + int countAdd() default 0; +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyFieldType.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyFieldType.java index d8c2463a1..088d252fc 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyFieldType.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/annotations/IggyFieldType.java @@ -1,34 +1,34 @@ -package com.jpexs.decompiler.flash.iggy.annotations; - -import com.jpexs.decompiler.flash.iggy.DataType; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * - * @author JPEXS - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface IggyFieldType { - - /// Type of value - DataType value() default DataType.unknown; - - /// Alternate type when condition is met - DataType alternateValue() default DataType.unknown; - - /// Condition for alternate type - String alternateCondition() default ""; - - /// Count - used primarily for bit fields UB,SB,FB to specify number of bits - int count() default -1; - - /// Field name on which Count depends - String countField() default ""; - - //Count to add to countField - int countAdd() default 0; -} +package com.jpexs.decompiler.flash.iggy.annotations; + +import com.jpexs.decompiler.flash.iggy.DataType; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author JPEXS + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface IggyFieldType { + + /// Type of value + DataType value() default DataType.unknown; + + /// Alternate type when condition is met + DataType alternateValue() default DataType.unknown; + + /// Condition for alternate type + String alternateCondition() default ""; + + /// Count - used primarily for bit fields UB,SB,FB to specify number of bits + int count() default -1; + + /// Field name on which Count depends + String countField() default ""; + + //Count to add to countField + int countAdd() default 0; +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyShapeToSwfConvertor.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyShapeToSwfConvertor.java index b130dccbe..8644c6882 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyShapeToSwfConvertor.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyShapeToSwfConvertor.java @@ -1,105 +1,105 @@ -package com.jpexs.decompiler.flash.iggy.conversion; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.iggy.IggyShape; -import com.jpexs.decompiler.flash.iggy.IggyShapeNode; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.LINESTYLE; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import java.awt.Color; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author JPEXS - */ -public class IggyShapeToSwfConvertor { - - private static int makeLengthsEmX(double val) { - return (int) (val * 1024.0); - } - - private static int makeLengthsEmY(double val) { - return (int) (val * 1024.0); - } - - public static SHAPE convertCharToShape(IggyShape igchar) { - SHAPE shape = new SHAPE(); - List retList = new ArrayList<>(); - List ignodes = igchar.getNodes(); - - int prevX = 0; - int prevY = 0; - - for (IggyShapeNode ign : ignodes) { - if (ign.getNodeType() == IggyShapeNode.NODE_TYPE_MOVE) { - StyleChangeRecord scr = new StyleChangeRecord(); - scr.stateMoveTo = true; - prevX = scr.moveDeltaX = makeLengthsEmX(ign.getX1()); - prevY = scr.moveDeltaY = makeLengthsEmY(ign.getY1()); - scr.fillStyles = new FILLSTYLEARRAY(); - scr.lineStyles = new LINESTYLEARRAY(); - scr.calculateBits(); - retList.add(scr); - } else { - - int curX1 = makeLengthsEmX(ign.getX1()); - int curY1 = makeLengthsEmY(ign.getY1()); - - int curX2 = makeLengthsEmX(ign.getX2()); - int curY2 = makeLengthsEmY(ign.getY2()); - - if (ign.getNodeType() == IggyShapeNode.NODE_TYPE_LINE_TO) { - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.deltaX = curX1 - prevX; - ser.deltaY = curY1 - prevY; - ser.generalLineFlag = true; - ser.simplify(); - ser.calculateBits(); - prevX = curX1; - prevY = curY1; - retList.add(ser); - } else if (ign.getNodeType() == IggyShapeNode.NODE_TYPE_CURVE_POINT) { - CurvedEdgeRecord cer = new CurvedEdgeRecord(); - cer.controlDeltaX = curX2 - prevX; - cer.controlDeltaY = curY2 - prevY; - cer.anchorDeltaX = curX1 - curX2; - cer.anchorDeltaY = curY1 - curY2; - prevX = curX1; - prevY = curY1; - cer.calculateBits(); - retList.add(cer); - } - } - } - if (!retList.isEmpty()) { - StyleChangeRecord init; - if (retList.get(0) instanceof StyleChangeRecord) { - init = (StyleChangeRecord) retList.get(0); - } else { - init = new StyleChangeRecord(); - retList.add(0, init); - } - init.stateFillStyle0 = true; - init.fillStyle0 = 1; - shape.numFillBits = 1; - - } else { - shape.numFillBits = 0; - - } - retList.add(new EndShapeRecord()); - shape.shapeRecords = retList; - shape.numLineBits = 0; - - return shape; - } -} +package com.jpexs.decompiler.flash.iggy.conversion; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.iggy.IggyShape; +import com.jpexs.decompiler.flash.iggy.IggyShapeNode; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author JPEXS + */ +public class IggyShapeToSwfConvertor { + + private static int makeLengthsEmX(double val) { + return (int) (val * 1024.0); + } + + private static int makeLengthsEmY(double val) { + return (int) (val * 1024.0); + } + + public static SHAPE convertCharToShape(IggyShape igchar) { + SHAPE shape = new SHAPE(); + List retList = new ArrayList<>(); + List ignodes = igchar.getNodes(); + + int prevX = 0; + int prevY = 0; + + for (IggyShapeNode ign : ignodes) { + if (ign.getNodeType() == IggyShapeNode.NODE_TYPE_MOVE) { + StyleChangeRecord scr = new StyleChangeRecord(); + scr.stateMoveTo = true; + prevX = scr.moveDeltaX = makeLengthsEmX(ign.getX1()); + prevY = scr.moveDeltaY = makeLengthsEmY(ign.getY1()); + scr.fillStyles = new FILLSTYLEARRAY(); + scr.lineStyles = new LINESTYLEARRAY(); + scr.calculateBits(); + retList.add(scr); + } else { + + int curX1 = makeLengthsEmX(ign.getX1()); + int curY1 = makeLengthsEmY(ign.getY1()); + + int curX2 = makeLengthsEmX(ign.getX2()); + int curY2 = makeLengthsEmY(ign.getY2()); + + if (ign.getNodeType() == IggyShapeNode.NODE_TYPE_LINE_TO) { + StraightEdgeRecord ser = new StraightEdgeRecord(); + ser.deltaX = curX1 - prevX; + ser.deltaY = curY1 - prevY; + ser.generalLineFlag = true; + ser.simplify(); + ser.calculateBits(); + prevX = curX1; + prevY = curY1; + retList.add(ser); + } else if (ign.getNodeType() == IggyShapeNode.NODE_TYPE_CURVE_POINT) { + CurvedEdgeRecord cer = new CurvedEdgeRecord(); + cer.controlDeltaX = curX2 - prevX; + cer.controlDeltaY = curY2 - prevY; + cer.anchorDeltaX = curX1 - curX2; + cer.anchorDeltaY = curY1 - curY2; + prevX = curX1; + prevY = curY1; + cer.calculateBits(); + retList.add(cer); + } + } + } + if (!retList.isEmpty()) { + StyleChangeRecord init; + if (retList.get(0) instanceof StyleChangeRecord) { + init = (StyleChangeRecord) retList.get(0); + } else { + init = new StyleChangeRecord(); + retList.add(0, init); + } + init.stateFillStyle0 = true; + init.fillStyle0 = 1; + shape.numFillBits = 1; + + } else { + shape.numFillBits = 0; + + } + retList.add(new EndShapeRecord()); + shape.shapeRecords = retList; + shape.numLineBits = 0; + + return shape; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggySwfBundle.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggySwfBundle.java index d3f7199d1..330342129 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggySwfBundle.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggySwfBundle.java @@ -1,104 +1,104 @@ -package com.jpexs.decompiler.flash.iggy.conversion; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFBundle; -import com.jpexs.decompiler.flash.iggy.IggyFile; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.MemoryInputStream; -import com.jpexs.helpers.ReReadableInputStream; -import com.jpexs.helpers.streams.SeekableInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * - * @author JPEXS - */ -public class IggySwfBundle implements SWFBundle { - - private IggyFile iggyFile; - - public IggySwfBundle(InputStream is) throws IOException { - this(is, null); - } - - public IggySwfBundle(File filename) throws IOException { - this(null, filename); - } - - protected IggySwfBundle(InputStream is, File filename) throws IOException { - initBundle(is, filename); - } - - protected void initBundle(InputStream is, File filename) throws IOException { - if (filename == null) { - filename = File.createTempFile("bundle", ".iggy"); - Helper.saveStream(is, filename); - } - iggyFile = new IggyFile(filename); - } - - @Override - public int length() { - return iggyFile.getSwfCount(); - } - - @Override - public Set getKeys() { - Set ret = new TreeSet<>(); - for (int i = 0; i < length(); i++) { - ret.add(iggyFile.getSwfName(i)); - } - return ret; - } - - private int keyToSwfIndex(String key) { - for (int i = 0; i < length(); i++) { - if (key.equals(iggyFile.getSwfName(i))) { - return i; - } - } - throw new IllegalArgumentException("Key " + key + " does not exist!"); - } - - @Override - public SeekableInputStream getSWF(String key) throws IOException { - SWF swf = IggyToSwfConvertor.getSwf(iggyFile, keyToSwfIndex(key)); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - swf.saveTo(baos); - MemoryInputStream mis = new MemoryInputStream(baos.toByteArray()); - return mis; - } - - @Override - public Map getAll() throws IOException { - Map ret = new HashMap<>(); - for (String key : getKeys()) { - ret.put(key, getSWF(key)); - } - return ret; - } - - @Override - public String getExtension() { - return "iggy"; - } - - @Override - public boolean isReadOnly() { - return true; //TODO: make writable - } - - @Override - public boolean putSWF(String key, InputStream is) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - -} +package com.jpexs.decompiler.flash.iggy.conversion; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFBundle; +import com.jpexs.decompiler.flash.iggy.IggyFile; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.MemoryInputStream; +import com.jpexs.helpers.ReReadableInputStream; +import com.jpexs.helpers.streams.SeekableInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * + * @author JPEXS + */ +public class IggySwfBundle implements SWFBundle { + + private IggyFile iggyFile; + + public IggySwfBundle(InputStream is) throws IOException { + this(is, null); + } + + public IggySwfBundle(File filename) throws IOException { + this(null, filename); + } + + protected IggySwfBundle(InputStream is, File filename) throws IOException { + initBundle(is, filename); + } + + protected void initBundle(InputStream is, File filename) throws IOException { + if (filename == null) { + filename = File.createTempFile("bundle", ".iggy"); + Helper.saveStream(is, filename); + } + iggyFile = new IggyFile(filename); + } + + @Override + public int length() { + return iggyFile.getSwfCount(); + } + + @Override + public Set getKeys() { + Set ret = new TreeSet<>(); + for (int i = 0; i < length(); i++) { + ret.add(iggyFile.getSwfName(i)); + } + return ret; + } + + private int keyToSwfIndex(String key) { + for (int i = 0; i < length(); i++) { + if (key.equals(iggyFile.getSwfName(i))) { + return i; + } + } + throw new IllegalArgumentException("Key " + key + " does not exist!"); + } + + @Override + public SeekableInputStream getSWF(String key) throws IOException { + SWF swf = IggyToSwfConvertor.getSwf(iggyFile, keyToSwfIndex(key)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + swf.saveTo(baos); + MemoryInputStream mis = new MemoryInputStream(baos.toByteArray()); + return mis; + } + + @Override + public Map getAll() throws IOException { + Map ret = new HashMap<>(); + for (String key : getKeys()) { + ret.put(key, getSWF(key)); + } + return ret; + } + + @Override + public String getExtension() { + return "iggy"; + } + + @Override + public boolean isReadOnly() { + return true; //TODO: make writable + } + + @Override + public boolean putSWF(String key, InputStream is) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyToSwfConvertor.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyToSwfConvertor.java index dd0f3d7c8..ee66d6c06 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyToSwfConvertor.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/iggy/conversion/IggyToSwfConvertor.java @@ -1,320 +1,248 @@ -package com.jpexs.decompiler.flash.iggy.conversion; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFCompression; -import com.jpexs.decompiler.flash.iggy.IggyShape; -import com.jpexs.decompiler.flash.iggy.IggyCharKerning; -import com.jpexs.decompiler.flash.iggy.IggyShapeNode; -import com.jpexs.decompiler.flash.iggy.IggyCharOffset; -import com.jpexs.decompiler.flash.iggy.IggyCharAdvances; -import com.jpexs.decompiler.flash.iggy.IggyFile; -import com.jpexs.decompiler.flash.iggy.IggyFont; -import com.jpexs.decompiler.flash.iggy.IggyText; -import com.jpexs.decompiler.flash.tags.DefineEditTextTag; -import com.jpexs.decompiler.flash.tags.DefineFont2Tag; -import com.jpexs.decompiler.flash.tags.EndTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.KERNINGRECORD; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import java.awt.Color; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * - * WIP - * - * @author JPEXS - */ -public class IggyToSwfConvertor { - - public static SWF[] getAllSwfs(IggyFile file) { - SWF[] ret = new SWF[file.getSwfCount()]; - for (int i = 0; i < ret.length; i++) { - ret[i] = getSwf(file, i); - } - return ret; - } - - public static void exportAllSwfsToDir(IggyFile file, File outputDir) throws IOException { - for (int swfIndex = 0; swfIndex < file.getSwfCount(); swfIndex++) { - exportSwfToDir(file, swfIndex, outputDir); - } - } - - public static void exportSwfToDir(IggyFile file, int swfIndex, File outputDir) throws IOException { - try (FileOutputStream fos = new FileOutputStream(new File(outputDir, file.getSwfName(swfIndex)))) { - exportSwf(file, swfIndex, fos); - } - } - - public static void exportSwfToFile(IggyFile file, int swfIndex, File outputFile) throws IOException { - try (FileOutputStream fos = new FileOutputStream(outputFile)) { - exportSwf(file, swfIndex, fos); - } - } - - public static void exportSwf(IggyFile file, int swfIndex, OutputStream output) throws IOException { - SWF swf = getSwf(file, swfIndex); - swf.saveTo(output); - } - - private static int makeLengthsTwip(double val) { - return (int) (val * SWF.unitDivisor); - } - - private static int makeLengthsEm(double val) { - return (int) (val * 1024.0); - } - - private static SHAPE createEmptyChar() { - SHAPE shape = new SHAPE(); - List retList = new ArrayList<>(); - - { - StyleChangeRecord scr = new StyleChangeRecord(); - scr.stateMoveTo = true; - scr.moveDeltaX = 0; - scr.moveDeltaY = 0; - scr.fillStyles = new FILLSTYLEARRAY(); - scr.lineStyles = new LINESTYLEARRAY(); - scr.calculateBits(); - retList.add(scr); - } - - /*{ - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.deltaX = 1024; - ser.deltaY = 0; - ser.generalLineFlag = true; - ser.simplify(); - ser.calculateBits(); - retList.add(ser); - } - - { - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.deltaX = 0; - ser.deltaY = -1024; - ser.generalLineFlag = true; - ser.simplify(); - ser.calculateBits(); - retList.add(ser); - } - - { - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.deltaX = -1024; - ser.deltaY = 0; - ser.generalLineFlag = true; - ser.simplify(); - ser.calculateBits(); - retList.add(ser); - } - - { - StraightEdgeRecord ser = new StraightEdgeRecord(); - ser.deltaX = 0; - ser.deltaY = 1024; - ser.generalLineFlag = true; - ser.simplify(); - ser.calculateBits(); - retList.add(ser); - }*/ - StyleChangeRecord init; - if (!retList.isEmpty() && retList.get(0) instanceof StyleChangeRecord) { - init = (StyleChangeRecord) retList.get(0); - } else { - init = new StyleChangeRecord(); - retList.add(0, init); - } - - retList.add(new EndShapeRecord()); - init.stateFillStyle1 = true; - init.fillStyle1 = 1; - shape.shapeRecords = retList; - shape.numFillBits = 1; - shape.numLineBits = 0; - - return shape; - } - - public static SWF getSwf(IggyFile file, int swfIndex) { - SWF swf = new SWF(); - swf.compression = SWFCompression.NONE; - swf.frameCount = 1; //FIXME!! - swf.frameRate = file.getSwfFrameRate(swfIndex); - swf.gfx = false; - swf.displayRect = new RECT( - (int) (file.getSwfXMin(swfIndex) * SWF.unitDivisor), - (int) (file.getSwfXMax(swfIndex) * SWF.unitDivisor), - (int) (file.getSwfYMin(swfIndex) * SWF.unitDivisor), - (int) (file.getSwfYMax(swfIndex) * SWF.unitDivisor)); - swf.version = 10; //FIXME - - FileAttributesTag fat = new FileAttributesTag(swf); - fat.actionScript3 = false; - fat.hasMetadata = false; - fat.useNetwork = false; - swf.addTag(fat); - - Set fontIndices = file.getFontIds(swfIndex); - - int currentCharId = 0; - Map fontIndex2CharId = new HashMap<>(); - - for (int fontIndex : fontIndices) { - IggyFont iggyFont = file.getFont(swfIndex, fontIndex); - DefineFont2Tag fontTag = new DefineFont2Tag(swf); - currentCharId++; - fontIndex2CharId.put(fontIndex, currentCharId); - fontTag.fontID = currentCharId; - /*System.out.println("==================="); - System.out.println("xscale: " + iggyFont.getXscale()); //80 - System.out.println("yscale: " + iggyFont.getYscale()); //19 - - System.out.println("unk_float1: " + iggyFont.getUnk_float()[0]); - System.out.println("unk_float2: " + iggyFont.getUnk_float()[1]); - System.out.println("unk_float3: " + iggyFont.getUnk_float()[2]); - System.out.println("unk_float4: " + iggyFont.getUnk_float()[3]); - System.out.println("unk_float5: " + iggyFont.getUnk_float()[4]); - System.out.println("what_2: " + iggyFont.getWhat_2()); - System.out.println("what_3: " + iggyFont.getWhat_3());*/ - - fontTag.fontKerningTable = new ArrayList<>(); - IggyCharKerning ker = iggyFont.getCharKernings(); - if (ker != null) { - for (int i = 0; i < ker.getKernCount(); i++) { - int kerningCode1 = ker.getCharsA().get(i); - int kerningCode2 = ker.getCharsA().get(i); - int kerningOffset = ker.getKerningOffsets().get(i); - fontTag.fontKerningTable.add(new KERNINGRECORD(kerningCode1, kerningCode2, kerningOffset)); - } - } - - fontTag.fontFlagsWideCodes = true; - fontTag.fontFlagsWideOffsets = true; - fontTag.fontAscent = iggyFont.getAscent(); - fontTag.fontDescent = iggyFont.getDescent(); - fontTag.fontLeading = iggyFont.getLeading(); - fontTag.codeTable = new ArrayList<>(); - fontTag.fontName = iggyFont.getName(); - fontTag.glyphShapeTable = new ArrayList<>(); - fontTag.fontBoundsTable = new ArrayList<>(); - fontTag.fontAdvanceTable = new ArrayList<>(); - fontTag.fontFlagsHasLayout = true; - IggyCharAdvances advanceValues = iggyFont.getCharAdvances(); - for (int i = 0; i < iggyFont.getCharacterCount(); i++) { - int code = iggyFont.getCharIndices().getChars().get(i); - IggyShape glyph = iggyFont.getChars().get(i); - fontTag.codeTable.add(code); - SHAPE shp = IggyShapeToSwfConvertor.convertCharToShape(glyph); - fontTag.glyphShapeTable.add(shp); - fontTag.fontBoundsTable.add(shp.getBounds()); - fontTag.fontAdvanceTable.add(makeLengthsEm(advanceValues.getScales().get(i))); - - } - fontTag.setModified(true); - swf.addTag(fontTag); - } - - /* - //TODO: Texts, they are incomplete - - Map textIndex2CharId = new HashMap<>(); - - Set textIds = file.getTextIds(swfIndex); - for (int textId : textIds) { - IggyText iggyText = file.getText(swfIndex, textId); - DefineEditTextTag textTag = new DefineEditTextTag(swf); - currentCharId++; - textIndex2CharId.put(iggyText.getTextIndex(), currentCharId); - textTag.characterID = currentCharId; - textTag.hasText = true; - textTag.initialText = iggyText.getInitialText(); - textTag.html = true; - textTag.noSelect = true; - textTag.wasStatic = true; - textTag.hasFont = false; - textTag.hasFontClass = false; - textTag.hasMaxLength = false; - //textTag.multiline = true; - //textTag.wordWrap = true; - //textTag.hasTextColor = true; - //textTag.textColor = new RGBA(Color.black); - //textTag.fontHeight = 40; //?? - textTag.readOnly = true; - textTag.bounds = new RECT( - makeLengthsTwip(iggyText.getPar3()), - makeLengthsTwip(iggyText.getPar1()), - makeLengthsTwip(iggyText.getPar4()), - makeLengthsTwip(iggyText.getPar2()) - ); - - //textTag.hasFont = true; - //textTag.fontId = fontIndex2CharId.get(iggyText.getFontIndex()); - textTag.setModified(true); - swf.addTag(textTag); - } - */ - swf.addTag( - new EndTag(swf)); - swf.setModified( - true); - - return swf; - } - - public static void main(String[] args) { - - if (args.length < 2 || (args[0].isEmpty() || args[1].isEmpty())) { - System.err.println("Invalid arguments"); - System.err.println("Usage: iggy-extract.bat file.iggy d:/outdir/"); - System.exit(1); - } - - File file = new File(args[0]); - if (!file.exists()) { - System.err.println("FAIL: Input file: " + file.getAbsolutePath() + " does not exist."); - System.exit(1); - } - File outDir = new File(args[1]); - if (!outDir.exists()) { - if (!outDir.mkdirs()) { - System.err.println("FAIL: Cannot create output directory"); - System.exit(1); - } - } - - try { - System.out.print("(1/2) Loading file " + args[0] + "..."); - IggyFile iggyFile = new IggyFile(new File(args[0])); - System.out.println("OK"); - System.out.print("(2/2) Exporting SWF files to " + args[1] + "..."); - exportAllSwfsToDir(iggyFile, new File(args[1])); - System.out.println("OK"); - System.out.println("All finished sucessfully."); - System.exit(0); - } catch (IOException ex) { - System.out.println("FAIL"); - System.err.println("Error while converting: " + ex.getMessage()); - System.exit(1); - } - } -} +package com.jpexs.decompiler.flash.iggy.conversion; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFCompression; +import com.jpexs.decompiler.flash.iggy.IggyShape; +import com.jpexs.decompiler.flash.iggy.IggyCharKerning; +import com.jpexs.decompiler.flash.iggy.IggyShapeNode; +import com.jpexs.decompiler.flash.iggy.IggyCharOffset; +import com.jpexs.decompiler.flash.iggy.IggyCharAdvances; +import com.jpexs.decompiler.flash.iggy.IggyFile; +import com.jpexs.decompiler.flash.iggy.IggyFont; +import com.jpexs.decompiler.flash.iggy.IggyText; +import com.jpexs.decompiler.flash.tags.DefineEditTextTag; +import com.jpexs.decompiler.flash.tags.DefineFont2Tag; +import com.jpexs.decompiler.flash.tags.EndTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.KERNINGRECORD; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import java.awt.Color; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * + * WIP + * + * @author JPEXS + */ +public class IggyToSwfConvertor { + + public static SWF[] getAllSwfs(IggyFile file) { + SWF[] ret = new SWF[file.getSwfCount()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = getSwf(file, i); + } + return ret; + } + + public static void exportAllSwfsToDir(IggyFile file, File outputDir) throws IOException { + for (int swfIndex = 0; swfIndex < file.getSwfCount(); swfIndex++) { + exportSwfToDir(file, swfIndex, outputDir); + } + } + + public static void exportSwfToDir(IggyFile file, int swfIndex, File outputDir) throws IOException { + try (FileOutputStream fos = new FileOutputStream(new File(outputDir, file.getSwfName(swfIndex)))) { + exportSwf(file, swfIndex, fos); + } + } + + public static void exportSwfToFile(IggyFile file, int swfIndex, File outputFile) throws IOException { + try (FileOutputStream fos = new FileOutputStream(outputFile)) { + exportSwf(file, swfIndex, fos); + } + } + + public static void exportSwf(IggyFile file, int swfIndex, OutputStream output) throws IOException { + SWF swf = getSwf(file, swfIndex); + swf.saveTo(output); + } + + private static int makeLengthsTwip(double val) { + return (int) (val * SWF.unitDivisor); + } + + private static int makeLengthsEm(double val) { + return (int) (val * 1024.0); + } + + public static SWF getSwf(IggyFile file, int swfIndex) { + SWF swf = new SWF(); + swf.compression = SWFCompression.NONE; + swf.frameCount = 1; //FIXME!! + swf.frameRate = file.getSwfFrameRate(swfIndex); + swf.gfx = false; + swf.displayRect = new RECT( + (int) (file.getSwfXMin(swfIndex) * SWF.unitDivisor), + (int) (file.getSwfXMax(swfIndex) * SWF.unitDivisor), + (int) (file.getSwfYMin(swfIndex) * SWF.unitDivisor), + (int) (file.getSwfYMax(swfIndex) * SWF.unitDivisor)); + swf.version = 10; //FIXME + + FileAttributesTag fat = new FileAttributesTag(swf); + fat.actionScript3 = false; + fat.hasMetadata = false; + fat.useNetwork = false; + swf.addTag(fat); + + Set fontIndices = file.getFontIds(swfIndex); + + int currentCharId = 0; + Map fontIndex2CharId = new HashMap<>(); + + for (int fontIndex : fontIndices) { + IggyFont iggyFont = file.getFont(swfIndex, fontIndex); + DefineFont2Tag fontTag = new DefineFont2Tag(swf); + currentCharId++; + fontIndex2CharId.put(fontIndex, currentCharId); + fontTag.fontID = currentCharId; + /*System.out.println("==================="); + System.out.println("xscale: " + iggyFont.getXscale()); //80 + System.out.println("yscale: " + iggyFont.getYscale()); //19 + + System.out.println("unk_float1: " + iggyFont.getUnk_float()[0]); + System.out.println("unk_float2: " + iggyFont.getUnk_float()[1]); + System.out.println("unk_float3: " + iggyFont.getUnk_float()[2]); + System.out.println("unk_float4: " + iggyFont.getUnk_float()[3]); + System.out.println("unk_float5: " + iggyFont.getUnk_float()[4]); + System.out.println("what_2: " + iggyFont.getWhat_2()); + System.out.println("what_3: " + iggyFont.getWhat_3());*/ + + fontTag.fontKerningTable = new ArrayList<>(); + IggyCharKerning ker = iggyFont.getCharKernings(); + if (ker != null) { + for (int i = 0; i < ker.getKernCount(); i++) { + int kerningCode1 = ker.getCharsA().get(i); + int kerningCode2 = ker.getCharsA().get(i); + int kerningOffset = makeLengthsEm(ker.getKerningOffsets().get(i)); + fontTag.fontKerningTable.add(new KERNINGRECORD(kerningCode1, kerningCode2, kerningOffset)); + } + } + + fontTag.fontFlagsWideCodes = true; + fontTag.fontFlagsWideOffsets = true; + fontTag.fontAscent = iggyFont.getAscent(); + fontTag.fontDescent = iggyFont.getDescent(); + fontTag.fontLeading = iggyFont.getLeading(); + fontTag.codeTable = new ArrayList<>(); + fontTag.fontName = iggyFont.getName(); + fontTag.glyphShapeTable = new ArrayList<>(); + fontTag.fontBoundsTable = new ArrayList<>(); + fontTag.fontAdvanceTable = new ArrayList<>(); + fontTag.fontFlagsHasLayout = true; + IggyCharAdvances advanceValues = iggyFont.getCharAdvances(); + for (int i = 0; i < iggyFont.getCharacterCount(); i++) { + int code = iggyFont.getCharIndices().getChars().get(i); + IggyShape glyph = iggyFont.getChars().get(i); + fontTag.codeTable.add(code); + SHAPE shp = IggyShapeToSwfConvertor.convertCharToShape(glyph); + fontTag.glyphShapeTable.add(shp); + fontTag.fontBoundsTable.add(shp.getBounds()); + fontTag.fontAdvanceTable.add(makeLengthsEm(advanceValues.getScales().get(i))); + + } + fontTag.setModified(true); + swf.addTag(fontTag); + } + + /* + //TODO: Texts, they are incomplete + + Map textIndex2CharId = new HashMap<>(); + + Set textIds = file.getTextIds(swfIndex); + for (int textId : textIds) { + IggyText iggyText = file.getText(swfIndex, textId); + DefineEditTextTag textTag = new DefineEditTextTag(swf); + currentCharId++; + textIndex2CharId.put(iggyText.getTextIndex(), currentCharId); + textTag.characterID = currentCharId; + textTag.hasText = true; + textTag.initialText = iggyText.getInitialText(); + textTag.html = true; + textTag.noSelect = true; + textTag.wasStatic = true; + textTag.hasFont = false; + textTag.hasFontClass = false; + textTag.hasMaxLength = false; + //textTag.multiline = true; + //textTag.wordWrap = true; + //textTag.hasTextColor = true; + //textTag.textColor = new RGBA(Color.black); + //textTag.fontHeight = 40; //?? + textTag.readOnly = true; + textTag.bounds = new RECT( + makeLengthsTwip(iggyText.getPar3()), + makeLengthsTwip(iggyText.getPar1()), + makeLengthsTwip(iggyText.getPar4()), + makeLengthsTwip(iggyText.getPar2()) + ); + + //textTag.hasFont = true; + //textTag.fontId = fontIndex2CharId.get(iggyText.getFontIndex()); + textTag.setModified(true); + swf.addTag(textTag); + } + */ + swf.addTag( + new EndTag(swf)); + swf.setModified( + true); + + return swf; + } + + public static void main(String[] args) { + + if (args.length < 2 || (args[0].isEmpty() || args[1].isEmpty())) { + System.err.println("Invalid arguments"); + System.err.println("Usage: iggy-extract.bat file.iggy d:/outdir/"); + System.exit(1); + } + + File file = new File(args[0]); + if (!file.exists()) { + System.err.println("FAIL: Input file: " + file.getAbsolutePath() + " does not exist."); + System.exit(1); + } + File outDir = new File(args[1]); + if (!outDir.exists()) { + if (!outDir.mkdirs()) { + System.err.println("FAIL: Cannot create output directory"); + System.exit(1); + } + } + + try { + System.out.print("(1/2) Loading file " + args[0] + "..."); + IggyFile iggyFile = new IggyFile(new File(args[0])); + System.out.println("OK"); + System.out.print("(2/2) Exporting SWF files to " + args[1] + "..."); + exportAllSwfsToDir(iggyFile, new File(args[1])); + System.out.println("OK"); + System.out.println("All finished sucessfully."); + System.exit(0); + } catch (IOException ex) { + System.out.println("FAIL"); + System.err.println("Error while converting: " + ex.getMessage()); + System.exit(1); + } + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java index aca619699..1b6eb9b12 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/DefineEditTextTag.java @@ -1,1190 +1,1190 @@ -/* - * Copyright (C) 2010-2016 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.tags; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFInputStream; -import com.jpexs.decompiler.flash.SWFOutputStream; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.helpers.HighlightedText; -import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; -import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; -import com.jpexs.decompiler.flash.tags.base.RenderContext; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.dynamictext.CharacterWithStyle; -import com.jpexs.decompiler.flash.tags.dynamictext.DynamicTextModel; -import com.jpexs.decompiler.flash.tags.dynamictext.GlyphCharacter; -import com.jpexs.decompiler.flash.tags.dynamictext.Paragraph; -import com.jpexs.decompiler.flash.tags.dynamictext.SameStyleTextRecord; -import com.jpexs.decompiler.flash.tags.dynamictext.TextStyle; -import com.jpexs.decompiler.flash.tags.dynamictext.Word; -import com.jpexs.decompiler.flash.tags.enums.TextRenderMode; -import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; -import com.jpexs.decompiler.flash.tags.text.TextAlign; -import com.jpexs.decompiler.flash.tags.text.TextLexer; -import com.jpexs.decompiler.flash.tags.text.TextParseException; -import com.jpexs.decompiler.flash.types.BasicType; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.annotations.Conditional; -import com.jpexs.decompiler.flash.types.annotations.SWFType; -import com.jpexs.decompiler.flash.types.annotations.SWFVersion; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.SerializableImage; -import com.jpexs.helpers.utf8.Utf8Helper; -import java.awt.Color; -import java.awt.Font; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.Stack; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -/** - * - * @author JPEXS - */ -@SWFVersion(from = 4) -public class DefineEditTextTag extends TextTag { - - public static final int ID = 37; - - public static final String NAME = "DefineEditText"; - - @SWFType(BasicType.UI16) - public int characterID; - - public RECT bounds; - - public boolean hasText; - - public boolean wordWrap; - - public boolean multiline; - - public boolean password; - - public boolean readOnly; - - public boolean hasTextColor; - - public boolean hasMaxLength; - - public boolean hasFont; - - public boolean hasFontClass; - - public boolean autoSize; - - public boolean hasLayout; - - public boolean noSelect; - - public boolean border; - - public boolean wasStatic; - - public boolean html; - - public boolean useOutlines; - - @SWFType(BasicType.UI16) - @Conditional("hasFont") - public int fontId; - - @Conditional("hasFontClass") - public String fontClass; - - @SWFType(BasicType.UI16) - @Conditional("hasFont|hasFontClass") - public int fontHeight; - - @Conditional("hasTextColor") - public RGBA textColor; - - @SWFType(BasicType.UI16) - @Conditional("hasMaxLength") - public int maxLength; - - @SWFType(BasicType.UI8) - @Conditional("hasLayout") - public int align; - - @SWFType(BasicType.UI16) - @Conditional("hasLayout") - public int leftMargin; - - @SWFType(BasicType.UI16) - @Conditional("hasLayout") - public int rightMargin; - - @SWFType(BasicType.UI16) - @Conditional("hasLayout") - public int indent; - - @SWFType(BasicType.SI16) - @Conditional("hasLayout") - public int leading; - - public String variableName; - - @Conditional("hasText") - public String initialText; - - /** - * Constructor - * - * @param swf - */ - public DefineEditTextTag(SWF swf) { - super(swf, ID, NAME, null); - characterID = swf.getNextCharacterId(); - bounds = new RECT(); - variableName = ""; - } - - /** - * Constructor - * - * @param sis - * @param data - * @throws IOException - */ - public DefineEditTextTag(SWFInputStream sis, ByteArrayRange data) throws IOException { - super(sis.getSwf(), ID, NAME, data); - readData(sis, data, 0, false, false, false); - } - - @Override - public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { - characterID = sis.readUI16("characterID"); - bounds = sis.readRECT("bounds"); - hasText = sis.readUB(1, "hasText") == 1; - wordWrap = sis.readUB(1, "wordWrap") == 1; - multiline = sis.readUB(1, "multiline") == 1; - password = sis.readUB(1, "password") == 1; - readOnly = sis.readUB(1, "readOnly") == 1; - hasTextColor = sis.readUB(1, "hasTextColor") == 1; - hasMaxLength = sis.readUB(1, "hasMaxLength") == 1; - hasFont = sis.readUB(1, "hasFont") == 1; - hasFontClass = sis.readUB(1, "hasFontClass") == 1; - autoSize = sis.readUB(1, "autoSize") == 1; - hasLayout = sis.readUB(1, "hasLayout") == 1; - noSelect = sis.readUB(1, "noSelect") == 1; - border = sis.readUB(1, "border") == 1; - wasStatic = sis.readUB(1, "wasStatic") == 1; - html = sis.readUB(1, "html") == 1; - useOutlines = sis.readUB(1, "useOutlines") == 1; - if (hasFont) { - fontId = sis.readUI16("fontId"); - } - if (hasFontClass) { - fontClass = sis.readString("fontClass"); - } - // condition is wrong in the documentation - if (hasFont || hasFontClass) { - fontHeight = sis.readUI16("fontHeight"); - } - if (hasTextColor) { - textColor = sis.readRGBA("textColor"); - } - if (hasMaxLength) { - maxLength = sis.readUI16("maxLength"); - } - if (hasLayout) { - align = sis.readUI8("align"); //0 left, 1 right, 2 center, 3 justify - leftMargin = sis.readUI16("leftMargin"); - rightMargin = sis.readUI16("rightMargin"); - indent = sis.readUI16("indent"); - leading = sis.readSI16("leading"); - } - variableName = sis.readString("variableName"); - if (hasText) { - initialText = sis.readString("initialText"); - } - - } - - /** - * Gets data bytes - * - * @param sos SWF output stream - * @throws java.io.IOException - */ - @Override - public void getData(SWFOutputStream sos) throws IOException { - sos.writeUI16(characterID); - sos.writeRECT(bounds); - sos.writeUB(1, hasText ? 1 : 0); - sos.writeUB(1, wordWrap ? 1 : 0); - sos.writeUB(1, multiline ? 1 : 0); - sos.writeUB(1, password ? 1 : 0); - sos.writeUB(1, readOnly ? 1 : 0); - sos.writeUB(1, hasTextColor ? 1 : 0); - sos.writeUB(1, hasMaxLength ? 1 : 0); - sos.writeUB(1, hasFont ? 1 : 0); - sos.writeUB(1, hasFontClass ? 1 : 0); - sos.writeUB(1, autoSize ? 1 : 0); - sos.writeUB(1, hasLayout ? 1 : 0); - sos.writeUB(1, noSelect ? 1 : 0); - sos.writeUB(1, border ? 1 : 0); - sos.writeUB(1, wasStatic ? 1 : 0); - sos.writeUB(1, html ? 1 : 0); - sos.writeUB(1, useOutlines ? 1 : 0); - if (hasFont) { - sos.writeUI16(fontId); - } - if (hasFontClass) { - sos.writeString(fontClass); - } - if (hasFont || hasFontClass) { - sos.writeUI16(fontHeight); - } - if (hasTextColor) { - sos.writeRGBA(textColor); - } - if (hasMaxLength) { - sos.writeUI16(maxLength); - } - if (hasLayout) { - sos.writeUI8(align); - sos.writeUI16(leftMargin); - sos.writeUI16(rightMargin); - sos.writeUI16(indent); - sos.writeSI16(leading); - } - sos.writeString(variableName); - if (hasText) { - sos.writeString(initialText); - } - } - - @Override - public RECT getBounds() { - return bounds; - } - - @Override - public MATRIX getTextMatrix() { - MATRIX matrix = new MATRIX(); - matrix.translateX = bounds.Xmin; - matrix.translateY = bounds.Ymin; - return matrix; - } - - @Override - public void setBounds(RECT r) { - bounds = r; - } - - private String stripTags(String inp) { - boolean intag = false; - String outp = ""; - inp = inp.replaceAll("
", "\r\n"); - for (int i = 0; i < inp.length(); ++i) { - if (!intag && inp.charAt(i) == '<') { - intag = true; - continue; - } - if (intag && inp.charAt(i) == '>') { - intag = false; - continue; - } - if (!intag) { - outp += inp.charAt(i); - } - } - return outp; - } - - private String entitiesReplace(String s) { - s = s.replace("<", "<"); - s = s.replace(">", ">"); - s = s.replace("&", "&"); - s = s.replace(""", "\""); - return s; - } - - @Override - public List getTexts() { - String ret = ""; - if (hasText) { - ret = initialText; - } - if (html) { - ret = stripTags(ret); - ret = entitiesReplace(ret); - } - return Arrays.asList(ret); - } - - private List getTextWithStyle() { - String str = ""; - TextStyle style = new TextStyle(); - if (fontClass != null) { - style.font = swf.getFontByClass(fontClass); - } else { - style.font = swf.getFont(fontId); - } - style.fontHeight = fontHeight; - style.fontLeading = leading; - if (hasTextColor) { - style.textColor = textColor; - } - if (hasText) { - str = initialText; - } - final List ret = new ArrayList<>(); - if (html) { - SAXParserFactory factory = SAXParserFactory.newInstance(); - SAXParser saxParser; - final Stack styles = new Stack<>(); - styles.add(style); - try { - saxParser = factory.newSAXParser(); - DefaultHandler handler = new DefaultHandler() { - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - TextStyle style = styles.peek(); - switch (qName) { - case "p": - // todo: parse the following attribute: - // align - break; - case "b": - style = style.clone(); - style.bold = true; - styles.add(style); - break; - case "i": - style = style.clone(); - style.italic = true; - styles.add(style); - break; - case "u": - style = style.clone(); - style.underlined = true; - styles.add(style); - break; - case "font": - style = style.clone(); - String color = attributes.getValue("color"); - if (color != null) { - if (color.startsWith("#")) { - style.textColor = new RGBA(Color.decode(color)); - } - } - String size = attributes.getValue("size"); - if (size != null && size.length() > 0) { - char firstChar = size.charAt(0); - if (firstChar != '+' && firstChar != '-') { - int fontSize = Integer.parseInt(size); - style.fontHeight = (int) Math.round(fontSize * SWF.unitDivisor); - style.fontLeading = leading; - } else { - // todo: parse relative sizes - } - } - String face = attributes.getValue("face"); - { - if (face != null && face.length() > 0) { - style.fontFace = face; - FontTag insideFont = swf.getFontByName(face); - style.font = insideFont; - if (insideFont != null) { - style.fontFace = null; - } - } - } - // todo: parse the following attributes: letterSpacing, kerning - styles.add(style); - break; - case "br": - case "sbr": // what's this? - CharacterWithStyle cs = new CharacterWithStyle(); - cs.character = '\n'; - cs.style = style; - ret.add(cs); - break; - } - //ret = entitiesReplace(ret); - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - switch (qName) { - case "b": - case "i": - case "u": - case "font": - styles.pop(); - break; - case "p": - TextStyle style = styles.peek(); - CharacterWithStyle cs = new CharacterWithStyle(); - cs.character = '\n'; - cs.style = style; - ret.add(cs); - break; - } - } - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - String txt = new String(ch, start, length); - TextStyle style = styles.peek(); - addCharacters(ret, txt, style); - } - }; - str = " \n" - + "]>" + str + ""; - saxParser.parse(new ByteArrayInputStream(str.getBytes(Utf8Helper.charset)), handler); - } catch (ParserConfigurationException | SAXException | IOException ex) { - Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); - } - } else { - addCharacters(ret, str, style); - } - return ret; - } - - private void addCharacters(List list, String str, TextStyle style) { - for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); - CharacterWithStyle cs = new CharacterWithStyle(); - cs.character = ch; - cs.style = style; - list.add(cs); - } - } - - @Override - public List getFontIds() { - List ret = new ArrayList<>(); - ret.add(fontId); - return ret; - } - - @Override - public HighlightedText getFormattedText(boolean ignoreLetterSpacing) { - HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); - writer.append("["); - String[] alignNames = {"left", "right", "center", "justify"}; - String alignment; - if (align < alignNames.length) { - alignment = alignNames[align]; - } else { - alignment = "unknown"; - } - writer.newLine(); - writer.append("xmin ").append(bounds.Xmin).newLine(); - writer.append("ymin ").append(bounds.Ymin).newLine(); - writer.append("xmax ").append(bounds.Xmax).newLine(); - writer.append("ymax ").append(bounds.Ymax).newLine(); - if (wordWrap) { - writer.append("wordwrap 1").newLine(); - } - if (multiline) { - writer.append("multiline 1").newLine(); - } - if (password) { - writer.append("password 1").newLine(); - } - if (readOnly) { - writer.append("readonly 1").newLine(); - } - if (autoSize) { - writer.append("autosize 1").newLine(); - } - if (noSelect) { - writer.append("noselect 1").newLine(); - } - if (border) { - writer.append("border 1").newLine(); - } - if (wasStatic) { - writer.append("wasstatic 1").newLine(); - } - if (html) { - writer.append("html 1").newLine(); - } - if (useOutlines) { - writer.append("useoutlines 1").newLine(); - } - if (hasFont) { - writer.append("font ").append(fontId).newLine(); - writer.append("height ").append(fontHeight).newLine(); - } - if (hasTextColor) { - writer.append("color ").append(textColor.toHexARGB()).newLine(); - } - if (hasFontClass) { - writer.append("fontclass ").append(fontClass).newLine(); - } - if (hasMaxLength) { - writer.append("maxlength ").append(maxLength).newLine(); - } - writer.append("align ").append(alignment).newLine(); - if (hasLayout) { - writer.append("leftmargin ").append(leftMargin).newLine(); - writer.append("rightmargin ").append(rightMargin).newLine(); - writer.append("indent ").append(indent).newLine(); - writer.append("leading ").append(leading).newLine(); - } - if (!variableName.isEmpty()) { - writer.append("variablename ").append(variableName).newLine(); - } - writer.append("]"); - if (hasText) { - String text = initialText.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]"); - writer.hilightSpecial(text, HighlightSpecialType.TEXT); - } - return new HighlightedText(writer); - } - - @Override - public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws TextParseException { - try { - TextLexer lexer = new TextLexer(new StringReader(formattedText)); - ParsedSymbol s; - formattedText = ""; - RECT bounds = new RECT(this.bounds); - boolean wordWrap = false; - boolean multiline = false; - boolean password = false; - boolean readOnly = false; - boolean autoSize = false; - boolean noSelect = false; - boolean border = false; - boolean wasStatic = false; - boolean html = false; - boolean useOutlines = false; - int fontId = -1; - int fontHeight = -1; - String fontClass = null; - RGBA textColor = null; - int maxLength = -1; - int align = -1; - int leftMargin = -1; - int rightMargin = -1; - int indent = -1; - int leading = -1; - String variableName = null; - - int textIdx = 0; - while ((s = lexer.yylex()) != null) { - switch (s.type) { - case PARAMETER: - String paramName = (String) s.values[0]; - String paramValue = (String) s.values[1]; - switch (paramName) { - case "xmin": - try { - bounds.Xmin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new TextParseException("Invalid xmin value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "ymin": - try { - bounds.Ymin = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new TextParseException("Invalid ymin value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "xmax": - try { - bounds.Xmax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new TextParseException("Invalid xmax value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "ymax": - try { - bounds.Ymax = Integer.parseInt(paramValue); - } catch (NumberFormatException nfe) { - throw new TextParseException("Invalid ymax value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "wordwrap": - if (paramValue.equals("1")) { - wordWrap = true; - } - break; - case "multiline": - if (paramValue.equals("1")) { - multiline = true; - } - break; - case "password": - if (paramValue.equals("1")) { - password = true; - } - break; - case "readonly": - if (paramValue.equals("1")) { - readOnly = true; - } - break; - case "autosize": - if (paramValue.equals("1")) { - autoSize = true; - } - break; - case "noselect": - if (paramValue.equals("1")) { - noSelect = true; - } - break; - case "border": - if (paramValue.equals("1")) { - border = true; - } - break; - case "wasstatic": - if (paramValue.equals("1")) { - wasStatic = true; - } - break; - case "html": - if (paramValue.equals("1")) { - html = true; - } - break; - case "useoutlines": - if (paramValue.equals("1")) { - useOutlines = true; - } - break; - case "font"://note: height parameter must be also present - try { - fontId = Integer.parseInt(paramValue); - - FontTag ft = swf.getFont(fontId); - if (ft == null) { - throw new TextParseException("Font not found.", lexer.yyline()); - } - hasFont = true; - } catch (NumberFormatException ne) { - throw new TextParseException("Invalid font value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "fontclass": - fontClass = paramValue; - break; - case "height": - try {//TODO: font parameter must be also present - fontHeight = Integer.parseInt(paramValue); - hasFont = true; - } catch (NumberFormatException ne) { - throw new TextParseException("Invalid height value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "color": - Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); - if (m.matches()) { - textColor = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16)); - } else { - throw new TextParseException("Invalid color. Valid format is #aarrggbb. Found: " + paramValue, lexer.yyline()); - } - break; - case "maxlength": - try { - maxLength = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new TextParseException("Invalid maxLength value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "align": - switch (paramValue) { - case "left": - align = 0; - break; - case "right": - align = 1; - break; - case "center": - align = 2; - break; - case "justify": - align = 3; - break; - default: - throw new TextParseException("Invalid align value. Expected one of: left,right,center or justify. Found: " + paramValue, lexer.yyline()); - } - break; - case "leftmargin": - try { - leftMargin = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new TextParseException("Invalid leftmargin value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "rightmargin": - try { - rightMargin = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new TextParseException("Invalid rightmargin value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "indent": - try { - indent = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new TextParseException("Invalid indent value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "leading": - try { - leading = Integer.parseInt(paramValue); - } catch (NumberFormatException ne) { - throw new TextParseException("Invalid leading value. Number expected. Found: " + paramValue, lexer.yyline()); - } - break; - case "variablename": - variableName = paramValue; - break; - default: - throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline()); - } - break; - case TEXT: - String s2 = (String) s.values[0]; - if (s2 == null) { - s2 = ""; - } - - formattedText += (texts == null || textIdx >= texts.length) ? s2 : texts[textIdx++]; - formattedText = formattedText.replace("\r\n", "\r"); - break; - } - } - - setModified(true); - this.bounds = bounds; - if (formattedText.length() > 0) { - initialText = formattedText; - this.hasText = true; - } else { - this.hasText = false; - } - - this.wordWrap = wordWrap; - this.multiline = multiline; - this.password = password; - this.readOnly = readOnly; - this.noSelect = noSelect; - this.border = border; - this.wasStatic = wasStatic; - this.html = html; - this.useOutlines = useOutlines; - - if (textColor != null) { - hasTextColor = true; - this.textColor = textColor; - } else { - hasTextColor = false; - } - - if (maxLength > -1) { - this.maxLength = maxLength; - hasMaxLength = true; - } else { - hasMaxLength = false; - } - - if (fontId > -1) { - this.fontId = fontId; - } - - if (fontHeight > -1) { - this.fontHeight = fontHeight; - } - - if (fontClass != null) { - this.fontClass = fontClass; - hasFontClass = true; - } else { - hasFontClass = false; - } - - this.autoSize = autoSize; - this.align = align; - if (leftMargin > -1 || rightMargin > -1 || indent > -1 || leading > -1) { - this.leftMargin = leftMargin; - this.rightMargin = rightMargin; - this.indent = indent; - this.leading = leading; - hasLayout = true; - } else { - hasLayout = false; - } - - if (variableName == null) { - variableName = ""; - } - this.variableName = variableName; - - } catch (IOException ex) { - Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); - return false; - } - - return true; - } - - @Override - public void updateTextBounds() { - } - - @Override - public boolean alignText(TextAlign textAlign) { - return true; - } - - @Override - public boolean translateText(int diff) { - return true; - } - - @Override - public RECT getRect(Set added) { - return bounds; - } - - @Override - public int getCharacterId() { - return characterID; - } - - @Override - public void setCharacterId(int characterId) { - this.characterID = characterId; - } - - @Override - public void getNeededCharacters(Set needed) { - if (hasFont) { - needed.add(fontId); - } - if (html && hasText) { - List chs = getTextWithStyle(); - for (CharacterWithStyle ch : chs) { - if (ch.style.font != null) { - needed.add(ch.style.font.getFontId()); - } - } - } - } - - @Override - public boolean replaceCharacter(int oldCharacterId, int newCharacterId) { - if (fontId == oldCharacterId) { - fontId = newCharacterId; - setModified(true); - return true; - } - return false; - } - - @Override - public boolean removeCharacter(int characterId) { - if (fontId == characterId) { - hasFont = false; - fontId = 0; - setModified(true); - return true; - } - return false; - } - - @Override - public int getUsedParameters() { - return 0; - } - - @Override - public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) { - render(TextRenderMode.BITMAP, image, null, null, transformation, colorTransform, 1); - } - - @Override - public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { - render(TextRenderMode.SVG, null, exporter, null, new Matrix(), colorTransform, 1); - } - - @Override - public void toHtmlCanvas(StringBuilder result, double unitDivisor) { - render(TextRenderMode.HTML5_CANVAS, null, null, result, new Matrix(), null, unitDivisor); - } - - private void render(TextRenderMode renderMode, SerializableImage image, SVGExporter svgExporter, StringBuilder htmlCanvasBuilder, Matrix transformation, ColorTransform colorTransform, double zoom) { - if (border) { - // border is always black, fill color is always white? - RGB borderColor = new RGBA(Color.black); - RGB fillColor = new RGBA(Color.white); - switch (renderMode) { - case BITMAP: - drawBorder(swf, image, borderColor, fillColor, getRect(), getTextMatrix(), transformation, colorTransform); - break; - case HTML5_CANVAS: - drawBorderHtmlCanvas(swf, htmlCanvasBuilder, borderColor, fillColor, getRect(), getTextMatrix(), colorTransform, zoom); - break; - case SVG: - drawBorderSVG(swf, svgExporter, borderColor, fillColor, getRect(), getTextMatrix(), colorTransform, zoom); - break; - } - } - if (hasText) { - DynamicTextModel textModel = new DynamicTextModel(); - List txt = getTextWithStyle(); - TextStyle lastStyle = null; - char prevChar = 0; - boolean lastWasWhiteSpace = false; - for (int i = 0; i < txt.size(); i++) { - CharacterWithStyle cs = txt.get(i); - char c = cs.character; - if (c != '\r' && c != '\n') { - // create new SameStyleTextRecord for all words and all diffrent style text parts - if (lastWasWhiteSpace && !Character.isWhitespace(c)) { - textModel.newWord(); - lastWasWhiteSpace = false; - } - if (cs.style != lastStyle) { - lastStyle = cs.style; - textModel.style = lastStyle; - textModel.newRecord(); - } - Character nextChar = null; - if (i + 1 < txt.size()) { - nextChar = txt.get(i + 1).character; - } - int advance; - FontTag font = lastStyle.font; - DynamicTextGlyphEntry ge = new DynamicTextGlyphEntry(); - ge.fontFace = lastStyle.fontFace; - ge.fontStyle = (lastStyle.bold ? Font.BOLD : 0) | (lastStyle.italic ? Font.ITALIC : 0); - ge.character = c; - ge.glyphIndex = font == null ? -1 : font.charToGlyph(c); - if (font != null && font.hasLayout()) { - int kerningAdjustment = 0; - if (nextChar != null) { - kerningAdjustment = font.getCharKerningAdjustment(c, nextChar); - kerningAdjustment /= font.getDivider(); - } - advance = (int) Math.round(Math.round((double) lastStyle.fontHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); - } else { - String fontName = lastStyle.fontFace != null ? lastStyle.fontFace : FontTag.defaultFontName; - int fontStyle = font == null ? ge.fontStyle : font.getFontStyle(); - advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, fontStyle, (int) (lastStyle.fontHeight / SWF.unitDivisor), c, nextChar)); - } - ge.glyphAdvance = advance; - textModel.addGlyph(c, ge); - if (Character.isWhitespace(c)) { - lastWasWhiteSpace = true; - } - } else if (multiline) { - textModel.newParagraph(); - } - prevChar = c; - } - - textModel.calculateTextWidths(); - List> lines; - if (multiline && wordWrap) { - lines = new ArrayList<>(); - for (Paragraph paragraph : textModel.paragraphs) { - List line = new ArrayList<>(); - int lineLength = 0; - for (Word word : paragraph.words) { - if (lineLength + word.width <= bounds.getWidth()) { - line.addAll(word.records); - lineLength += word.width; - } else { - lines.add(line); - line = new ArrayList<>(); - line.addAll(word.records); - lineLength = 0; - } - } - if (!line.isEmpty()) { - lines.add(line); - } - } - } else { - lines = new ArrayList<>(); - for (Paragraph paragraph : textModel.paragraphs) { - List line = new ArrayList<>(); - for (Word word : paragraph.words) { - for (SameStyleTextRecord tr : word.records) { - line.add(tr); - } - } - lines.add(line); - } - } - - // remove spaces after last word - for (List line : lines) { - boolean removed = true; - while (removed) { - removed = false; - while (line.size() > 0 && line.get(line.size() - 1).glyphEntries.isEmpty()) { - line.remove(line.size() - 1); - removed = true; - } - if (line.size() > 0) { - SameStyleTextRecord lastRecord = line.get(line.size() - 1); - while (lastRecord.glyphEntries.size() > 0 - && Character.isWhitespace(lastRecord.glyphEntries.get(lastRecord.glyphEntries.size() - 1).character)) { - lastRecord.glyphEntries.remove(lastRecord.glyphEntries.size() - 1); - removed = true; - } - } - } - } - - textModel.calculateTextWidths(); - - List allTextRecords = new ArrayList<>(); - int lastHeight = 0; - int yOffset = -leading; - for (List line : lines) { - int width = 0; - int currentOffset = 0; - if (line.isEmpty()) { - currentOffset = lastHeight; - } else { - for (SameStyleTextRecord tr : line) { - width += tr.width; - int lineHeight = tr.style.fontHeight + tr.style.fontLeading; - lastHeight = lineHeight; - if (lineHeight > currentOffset) { - currentOffset = lineHeight; - } - } - } - yOffset += currentOffset; - int alignOffset = 0; - switch (align) { - case 0: // left - alignOffset = 0; - break; - case 1: // right - alignOffset = bounds.getWidth() - width; - break; - case 2: // center - alignOffset = (bounds.getWidth() - width) / 2; - break; - case 3: // justify - // todo; - break; - } - for (SameStyleTextRecord tr : line) { - tr.xOffset = alignOffset; - alignOffset += tr.width; - } - for (SameStyleTextRecord tr : line) { - TEXTRECORD tr2 = new TEXTRECORD(); - int fid = fontId; - if (fontClass != null) { - FontTag ft = swf.getFontByClass(fontClass); - if (ft != null) { - fid = ft.getFontId(); - } - } - if (tr.style.font != null) { - fid = tr.style.font.getFontId(); - } - - tr2.styleFlagsHasFont = fid != 0; - tr2.fontId = fid; - tr2.textHeight = tr.style.fontHeight; - if (tr.style.textColor != null) { - tr2.styleFlagsHasColor = true; - tr2.textColorA = tr.style.textColor; - } - // always add xOffset, because no xOffset and 0 xOffset is diffrent in text rendering - tr2.styleFlagsHasXOffset = true; - tr2.xOffset = tr.xOffset; - if (yOffset != 0) { - tr2.styleFlagsHasYOffset = true; - tr2.yOffset = yOffset; - } - tr2.glyphEntries = new ArrayList<>(tr.glyphEntries.size()); - for (GlyphCharacter ge : tr.glyphEntries) { - tr2.glyphEntries.add(ge.glyphEntry); - } - allTextRecords.add(tr2); - } - } - - switch (renderMode) { - case BITMAP: - staticTextToImage(swf, allTextRecords, 2, image, getTextMatrix(), transformation, colorTransform); - break; - case HTML5_CANVAS: - staticTextToHtmlCanvas(zoom, swf, allTextRecords, 2, htmlCanvasBuilder, getBounds(), getTextMatrix(), colorTransform); - break; - case SVG: - staticTextToSVG(swf, allTextRecords, 2, svgExporter, getBounds(), getTextMatrix(), colorTransform, zoom); - break; - } - } - } - - @Override - public ExportRectangle calculateTextBounds() { - return null; - } - - @Override - public int getNumFrames() { - return 1; - } - - @Override - public boolean isSingleFrame() { - return true; - } -} +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.tags; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFInputStream; +import com.jpexs.decompiler.flash.SWFOutputStream; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.helpers.HighlightedText; +import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; +import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; +import com.jpexs.decompiler.flash.tags.base.RenderContext; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.dynamictext.CharacterWithStyle; +import com.jpexs.decompiler.flash.tags.dynamictext.DynamicTextModel; +import com.jpexs.decompiler.flash.tags.dynamictext.GlyphCharacter; +import com.jpexs.decompiler.flash.tags.dynamictext.Paragraph; +import com.jpexs.decompiler.flash.tags.dynamictext.SameStyleTextRecord; +import com.jpexs.decompiler.flash.tags.dynamictext.TextStyle; +import com.jpexs.decompiler.flash.tags.dynamictext.Word; +import com.jpexs.decompiler.flash.tags.enums.TextRenderMode; +import com.jpexs.decompiler.flash.tags.text.ParsedSymbol; +import com.jpexs.decompiler.flash.tags.text.TextAlign; +import com.jpexs.decompiler.flash.tags.text.TextLexer; +import com.jpexs.decompiler.flash.tags.text.TextParseException; +import com.jpexs.decompiler.flash.types.BasicType; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.annotations.Conditional; +import com.jpexs.decompiler.flash.types.annotations.SWFType; +import com.jpexs.decompiler.flash.types.annotations.SWFVersion; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.SerializableImage; +import com.jpexs.helpers.utf8.Utf8Helper; +import java.awt.Color; +import java.awt.Font; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * + * @author JPEXS + */ +@SWFVersion(from = 4) +public class DefineEditTextTag extends TextTag { + + public static final int ID = 37; + + public static final String NAME = "DefineEditText"; + + @SWFType(BasicType.UI16) + public int characterID; + + public RECT bounds; + + public boolean hasText; + + public boolean wordWrap; + + public boolean multiline; + + public boolean password; + + public boolean readOnly; + + public boolean hasTextColor; + + public boolean hasMaxLength; + + public boolean hasFont; + + public boolean hasFontClass; + + public boolean autoSize; + + public boolean hasLayout; + + public boolean noSelect; + + public boolean border; + + public boolean wasStatic; + + public boolean html; + + public boolean useOutlines; + + @SWFType(BasicType.UI16) + @Conditional("hasFont") + public int fontId; + + @Conditional("hasFontClass") + public String fontClass; + + @SWFType(BasicType.UI16) + @Conditional("hasFont|hasFontClass") + public int fontHeight; + + @Conditional("hasTextColor") + public RGBA textColor; + + @SWFType(BasicType.UI16) + @Conditional("hasMaxLength") + public int maxLength; + + @SWFType(BasicType.UI8) + @Conditional("hasLayout") + public int align; + + @SWFType(BasicType.UI16) + @Conditional("hasLayout") + public int leftMargin; + + @SWFType(BasicType.UI16) + @Conditional("hasLayout") + public int rightMargin; + + @SWFType(BasicType.UI16) + @Conditional("hasLayout") + public int indent; + + @SWFType(BasicType.SI16) + @Conditional("hasLayout") + public int leading; + + public String variableName; + + @Conditional("hasText") + public String initialText; + + /** + * Constructor + * + * @param swf + */ + public DefineEditTextTag(SWF swf) { + super(swf, ID, NAME, null); + characterID = swf.getNextCharacterId(); + bounds = new RECT(); + variableName = ""; + } + + /** + * Constructor + * + * @param sis + * @param data + * @throws IOException + */ + public DefineEditTextTag(SWFInputStream sis, ByteArrayRange data) throws IOException { + super(sis.getSwf(), ID, NAME, data); + readData(sis, data, 0, false, false, false); + } + + @Override + public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException { + characterID = sis.readUI16("characterID"); + bounds = sis.readRECT("bounds"); + hasText = sis.readUB(1, "hasText") == 1; + wordWrap = sis.readUB(1, "wordWrap") == 1; + multiline = sis.readUB(1, "multiline") == 1; + password = sis.readUB(1, "password") == 1; + readOnly = sis.readUB(1, "readOnly") == 1; + hasTextColor = sis.readUB(1, "hasTextColor") == 1; + hasMaxLength = sis.readUB(1, "hasMaxLength") == 1; + hasFont = sis.readUB(1, "hasFont") == 1; + hasFontClass = sis.readUB(1, "hasFontClass") == 1; + autoSize = sis.readUB(1, "autoSize") == 1; + hasLayout = sis.readUB(1, "hasLayout") == 1; + noSelect = sis.readUB(1, "noSelect") == 1; + border = sis.readUB(1, "border") == 1; + wasStatic = sis.readUB(1, "wasStatic") == 1; + html = sis.readUB(1, "html") == 1; + useOutlines = sis.readUB(1, "useOutlines") == 1; + if (hasFont) { + fontId = sis.readUI16("fontId"); + } + if (hasFontClass) { + fontClass = sis.readString("fontClass"); + } + // condition is wrong in the documentation + if (hasFont || hasFontClass) { + fontHeight = sis.readUI16("fontHeight"); + } + if (hasTextColor) { + textColor = sis.readRGBA("textColor"); + } + if (hasMaxLength) { + maxLength = sis.readUI16("maxLength"); + } + if (hasLayout) { + align = sis.readUI8("align"); //0 left, 1 right, 2 center, 3 justify + leftMargin = sis.readUI16("leftMargin"); + rightMargin = sis.readUI16("rightMargin"); + indent = sis.readUI16("indent"); + leading = sis.readSI16("leading"); + } + variableName = sis.readString("variableName"); + if (hasText) { + initialText = sis.readString("initialText"); + } + + } + + /** + * Gets data bytes + * + * @param sos SWF output stream + * @throws java.io.IOException + */ + @Override + public void getData(SWFOutputStream sos) throws IOException { + sos.writeUI16(characterID); + sos.writeRECT(bounds); + sos.writeUB(1, hasText ? 1 : 0); + sos.writeUB(1, wordWrap ? 1 : 0); + sos.writeUB(1, multiline ? 1 : 0); + sos.writeUB(1, password ? 1 : 0); + sos.writeUB(1, readOnly ? 1 : 0); + sos.writeUB(1, hasTextColor ? 1 : 0); + sos.writeUB(1, hasMaxLength ? 1 : 0); + sos.writeUB(1, hasFont ? 1 : 0); + sos.writeUB(1, hasFontClass ? 1 : 0); + sos.writeUB(1, autoSize ? 1 : 0); + sos.writeUB(1, hasLayout ? 1 : 0); + sos.writeUB(1, noSelect ? 1 : 0); + sos.writeUB(1, border ? 1 : 0); + sos.writeUB(1, wasStatic ? 1 : 0); + sos.writeUB(1, html ? 1 : 0); + sos.writeUB(1, useOutlines ? 1 : 0); + if (hasFont) { + sos.writeUI16(fontId); + } + if (hasFontClass) { + sos.writeString(fontClass); + } + if (hasFont || hasFontClass) { + sos.writeUI16(fontHeight); + } + if (hasTextColor) { + sos.writeRGBA(textColor); + } + if (hasMaxLength) { + sos.writeUI16(maxLength); + } + if (hasLayout) { + sos.writeUI8(align); + sos.writeUI16(leftMargin); + sos.writeUI16(rightMargin); + sos.writeUI16(indent); + sos.writeSI16(leading); + } + sos.writeString(variableName); + if (hasText) { + sos.writeString(initialText); + } + } + + @Override + public RECT getBounds() { + return bounds; + } + + @Override + public MATRIX getTextMatrix() { + MATRIX matrix = new MATRIX(); + matrix.translateX = bounds.Xmin; + matrix.translateY = bounds.Ymin; + return matrix; + } + + @Override + public void setBounds(RECT r) { + bounds = r; + } + + private String stripTags(String inp) { + boolean intag = false; + String outp = ""; + inp = inp.replaceAll("
", "\r\n"); + for (int i = 0; i < inp.length(); ++i) { + if (!intag && inp.charAt(i) == '<') { + intag = true; + continue; + } + if (intag && inp.charAt(i) == '>') { + intag = false; + continue; + } + if (!intag) { + outp += inp.charAt(i); + } + } + return outp; + } + + private String entitiesReplace(String s) { + s = s.replace("<", "<"); + s = s.replace(">", ">"); + s = s.replace("&", "&"); + s = s.replace(""", "\""); + return s; + } + + @Override + public List getTexts() { + String ret = ""; + if (hasText) { + ret = initialText; + } + if (html) { + ret = stripTags(ret); + ret = entitiesReplace(ret); + } + return Arrays.asList(ret); + } + + private List getTextWithStyle() { + String str = ""; + TextStyle style = new TextStyle(); + if (fontClass != null) { + style.font = swf.getFontByClass(fontClass); + } else { + style.font = swf.getFont(fontId); + } + style.fontHeight = fontHeight; + style.fontLeading = leading; + if (hasTextColor) { + style.textColor = textColor; + } + if (hasText) { + str = initialText; + } + final List ret = new ArrayList<>(); + if (html) { + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser saxParser; + final Stack styles = new Stack<>(); + styles.add(style); + try { + saxParser = factory.newSAXParser(); + DefaultHandler handler = new DefaultHandler() { + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + TextStyle style = styles.peek(); + switch (qName) { + case "p": + // todo: parse the following attribute: + // align + break; + case "b": + style = style.clone(); + style.bold = true; + styles.add(style); + break; + case "i": + style = style.clone(); + style.italic = true; + styles.add(style); + break; + case "u": + style = style.clone(); + style.underlined = true; + styles.add(style); + break; + case "font": + style = style.clone(); + String color = attributes.getValue("color"); + if (color != null) { + if (color.startsWith("#")) { + style.textColor = new RGBA(Color.decode(color)); + } + } + String size = attributes.getValue("size"); + if (size != null && size.length() > 0) { + char firstChar = size.charAt(0); + if (firstChar != '+' && firstChar != '-') { + int fontSize = Integer.parseInt(size); + style.fontHeight = (int) Math.round(fontSize * SWF.unitDivisor); + style.fontLeading = leading; + } else { + // todo: parse relative sizes + } + } + String face = attributes.getValue("face"); + { + if (face != null && face.length() > 0) { + style.fontFace = face; + FontTag insideFont = swf.getFontByName(face); + style.font = insideFont; + if (insideFont != null) { + style.fontFace = null; + } + } + } + // todo: parse the following attributes: letterSpacing, kerning + styles.add(style); + break; + case "br": + case "sbr": // what's this? + CharacterWithStyle cs = new CharacterWithStyle(); + cs.character = '\n'; + cs.style = style; + ret.add(cs); + break; + } + //ret = entitiesReplace(ret); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + switch (qName) { + case "b": + case "i": + case "u": + case "font": + styles.pop(); + break; + case "p": + TextStyle style = styles.peek(); + CharacterWithStyle cs = new CharacterWithStyle(); + cs.character = '\n'; + cs.style = style; + ret.add(cs); + break; + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + String txt = new String(ch, start, length); + TextStyle style = styles.peek(); + addCharacters(ret, txt, style); + } + }; + str = " \n" + + "]>" + str + ""; + saxParser.parse(new ByteArrayInputStream(str.getBytes(Utf8Helper.charset)), handler); + } catch (ParserConfigurationException | SAXException | IOException ex) { + Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); + } + } else { + addCharacters(ret, str, style); + } + return ret; + } + + private void addCharacters(List list, String str, TextStyle style) { + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + CharacterWithStyle cs = new CharacterWithStyle(); + cs.character = ch; + cs.style = style; + list.add(cs); + } + } + + @Override + public List getFontIds() { + List ret = new ArrayList<>(); + ret.add(fontId); + return ret; + } + + @Override + public HighlightedText getFormattedText(boolean ignoreLetterSpacing) { + HighlightedTextWriter writer = new HighlightedTextWriter(Configuration.getCodeFormatting(), true); + writer.append("["); + String[] alignNames = {"left", "right", "center", "justify"}; + String alignment; + if (align < alignNames.length) { + alignment = alignNames[align]; + } else { + alignment = "unknown"; + } + writer.newLine(); + writer.append("xmin ").append(bounds.Xmin).newLine(); + writer.append("ymin ").append(bounds.Ymin).newLine(); + writer.append("xmax ").append(bounds.Xmax).newLine(); + writer.append("ymax ").append(bounds.Ymax).newLine(); + if (wordWrap) { + writer.append("wordwrap 1").newLine(); + } + if (multiline) { + writer.append("multiline 1").newLine(); + } + if (password) { + writer.append("password 1").newLine(); + } + if (readOnly) { + writer.append("readonly 1").newLine(); + } + if (autoSize) { + writer.append("autosize 1").newLine(); + } + if (noSelect) { + writer.append("noselect 1").newLine(); + } + if (border) { + writer.append("border 1").newLine(); + } + if (wasStatic) { + writer.append("wasstatic 1").newLine(); + } + if (html) { + writer.append("html 1").newLine(); + } + if (useOutlines) { + writer.append("useoutlines 1").newLine(); + } + if (hasFont) { + writer.append("font ").append(fontId).newLine(); + writer.append("height ").append(fontHeight).newLine(); + } + if (hasTextColor) { + writer.append("color ").append(textColor.toHexARGB()).newLine(); + } + if (hasFontClass) { + writer.append("fontclass ").append(fontClass).newLine(); + } + if (hasMaxLength) { + writer.append("maxlength ").append(maxLength).newLine(); + } + writer.append("align ").append(alignment).newLine(); + if (hasLayout) { + writer.append("leftmargin ").append(leftMargin).newLine(); + writer.append("rightmargin ").append(rightMargin).newLine(); + writer.append("indent ").append(indent).newLine(); + writer.append("leading ").append(leading).newLine(); + } + if (!variableName.isEmpty()) { + writer.append("variablename ").append(variableName).newLine(); + } + writer.append("]"); + if (hasText) { + String text = initialText.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]"); + writer.hilightSpecial(text, HighlightSpecialType.TEXT); + } + return new HighlightedText(writer); + } + + @Override + public boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws TextParseException { + try { + TextLexer lexer = new TextLexer(new StringReader(formattedText)); + ParsedSymbol s; + formattedText = ""; + RECT bounds = new RECT(this.bounds); + boolean wordWrap = false; + boolean multiline = false; + boolean password = false; + boolean readOnly = false; + boolean autoSize = false; + boolean noSelect = false; + boolean border = false; + boolean wasStatic = false; + boolean html = false; + boolean useOutlines = false; + int fontId = -1; + int fontHeight = -1; + String fontClass = null; + RGBA textColor = null; + int maxLength = -1; + int align = -1; + int leftMargin = -1; + int rightMargin = -1; + int indent = -1; + int leading = -1; + String variableName = null; + + int textIdx = 0; + while ((s = lexer.yylex()) != null) { + switch (s.type) { + case PARAMETER: + String paramName = (String) s.values[0]; + String paramValue = (String) s.values[1]; + switch (paramName) { + case "xmin": + try { + bounds.Xmin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new TextParseException("Invalid xmin value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "ymin": + try { + bounds.Ymin = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new TextParseException("Invalid ymin value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "xmax": + try { + bounds.Xmax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new TextParseException("Invalid xmax value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "ymax": + try { + bounds.Ymax = Integer.parseInt(paramValue); + } catch (NumberFormatException nfe) { + throw new TextParseException("Invalid ymax value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "wordwrap": + if (paramValue.equals("1")) { + wordWrap = true; + } + break; + case "multiline": + if (paramValue.equals("1")) { + multiline = true; + } + break; + case "password": + if (paramValue.equals("1")) { + password = true; + } + break; + case "readonly": + if (paramValue.equals("1")) { + readOnly = true; + } + break; + case "autosize": + if (paramValue.equals("1")) { + autoSize = true; + } + break; + case "noselect": + if (paramValue.equals("1")) { + noSelect = true; + } + break; + case "border": + if (paramValue.equals("1")) { + border = true; + } + break; + case "wasstatic": + if (paramValue.equals("1")) { + wasStatic = true; + } + break; + case "html": + if (paramValue.equals("1")) { + html = true; + } + break; + case "useoutlines": + if (paramValue.equals("1")) { + useOutlines = true; + } + break; + case "font"://note: height parameter must be also present + try { + fontId = Integer.parseInt(paramValue); + + FontTag ft = swf.getFont(fontId); + if (ft == null) { + throw new TextParseException("Font not found.", lexer.yyline()); + } + hasFont = true; + } catch (NumberFormatException ne) { + throw new TextParseException("Invalid font value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "fontclass": + fontClass = paramValue; + break; + case "height": + try {//TODO: font parameter must be also present + fontHeight = Integer.parseInt(paramValue); + hasFont = true; + } catch (NumberFormatException ne) { + throw new TextParseException("Invalid height value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "color": + Matcher m = Pattern.compile("#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])").matcher(paramValue); + if (m.matches()) { + textColor = new RGBA(Integer.parseInt(m.group(2), 16), Integer.parseInt(m.group(3), 16), Integer.parseInt(m.group(4), 16), Integer.parseInt(m.group(1), 16)); + } else { + throw new TextParseException("Invalid color. Valid format is #aarrggbb. Found: " + paramValue, lexer.yyline()); + } + break; + case "maxlength": + try { + maxLength = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new TextParseException("Invalid maxLength value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "align": + switch (paramValue) { + case "left": + align = 0; + break; + case "right": + align = 1; + break; + case "center": + align = 2; + break; + case "justify": + align = 3; + break; + default: + throw new TextParseException("Invalid align value. Expected one of: left,right,center or justify. Found: " + paramValue, lexer.yyline()); + } + break; + case "leftmargin": + try { + leftMargin = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new TextParseException("Invalid leftmargin value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "rightmargin": + try { + rightMargin = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new TextParseException("Invalid rightmargin value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "indent": + try { + indent = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new TextParseException("Invalid indent value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "leading": + try { + leading = Integer.parseInt(paramValue); + } catch (NumberFormatException ne) { + throw new TextParseException("Invalid leading value. Number expected. Found: " + paramValue, lexer.yyline()); + } + break; + case "variablename": + variableName = paramValue; + break; + default: + throw new TextParseException("Unrecognized parameter name: " + paramName, lexer.yyline()); + } + break; + case TEXT: + String s2 = (String) s.values[0]; + if (s2 == null) { + s2 = ""; + } + + formattedText += (texts == null || textIdx >= texts.length) ? s2 : texts[textIdx++]; + formattedText = formattedText.replace("\r\n", "\r"); + break; + } + } + + setModified(true); + this.bounds = bounds; + if (formattedText.length() > 0) { + initialText = formattedText; + this.hasText = true; + } else { + this.hasText = false; + } + + this.wordWrap = wordWrap; + this.multiline = multiline; + this.password = password; + this.readOnly = readOnly; + this.noSelect = noSelect; + this.border = border; + this.wasStatic = wasStatic; + this.html = html; + this.useOutlines = useOutlines; + + if (textColor != null) { + hasTextColor = true; + this.textColor = textColor; + } else { + hasTextColor = false; + } + + if (maxLength > -1) { + this.maxLength = maxLength; + hasMaxLength = true; + } else { + hasMaxLength = false; + } + + if (fontId > -1) { + this.fontId = fontId; + } + + if (fontHeight > -1) { + this.fontHeight = fontHeight; + } + + if (fontClass != null) { + this.fontClass = fontClass; + hasFontClass = true; + } else { + hasFontClass = false; + } + + this.autoSize = autoSize; + this.align = align; + if (leftMargin > -1 || rightMargin > -1 || indent > -1 || leading > -1) { + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + this.indent = indent; + this.leading = leading; + hasLayout = true; + } else { + hasLayout = false; + } + + if (variableName == null) { + variableName = ""; + } + this.variableName = variableName; + + } catch (IOException ex) { + Logger.getLogger(DefineEditTextTag.class.getName()).log(Level.SEVERE, null, ex); + return false; + } + + return true; + } + + @Override + public void updateTextBounds() { + } + + @Override + public boolean alignText(TextAlign textAlign) { + return true; + } + + @Override + public boolean translateText(int diff) { + return true; + } + + @Override + public RECT getRect(Set added) { + return bounds; + } + + @Override + public int getCharacterId() { + return characterID; + } + + @Override + public void setCharacterId(int characterId) { + this.characterID = characterId; + } + + @Override + public void getNeededCharacters(Set needed) { + if (hasFont) { + needed.add(fontId); + } + if (html && hasText) { + List chs = getTextWithStyle(); + for (CharacterWithStyle ch : chs) { + if (ch.style.font != null) { + needed.add(ch.style.font.getFontId()); + } + } + } + } + + @Override + public boolean replaceCharacter(int oldCharacterId, int newCharacterId) { + if (fontId == oldCharacterId) { + fontId = newCharacterId; + setModified(true); + return true; + } + return false; + } + + @Override + public boolean removeCharacter(int characterId) { + if (fontId == characterId) { + hasFont = false; + fontId = 0; + setModified(true); + return true; + } + return false; + } + + @Override + public int getUsedParameters() { + return 0; + } + + @Override + public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) { + render(TextRenderMode.BITMAP, image, null, null, transformation, colorTransform, 1); + } + + @Override + public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level) { + render(TextRenderMode.SVG, null, exporter, null, new Matrix(), colorTransform, 1); + } + + @Override + public void toHtmlCanvas(StringBuilder result, double unitDivisor) { + render(TextRenderMode.HTML5_CANVAS, null, null, result, new Matrix(), null, unitDivisor); + } + + private void render(TextRenderMode renderMode, SerializableImage image, SVGExporter svgExporter, StringBuilder htmlCanvasBuilder, Matrix transformation, ColorTransform colorTransform, double zoom) { + if (border) { + // border is always black, fill color is always white? + RGB borderColor = new RGBA(Color.black); + RGB fillColor = new RGBA(Color.white); + switch (renderMode) { + case BITMAP: + drawBorder(swf, image, borderColor, fillColor, getRect(), getTextMatrix(), transformation, colorTransform); + break; + case HTML5_CANVAS: + drawBorderHtmlCanvas(swf, htmlCanvasBuilder, borderColor, fillColor, getRect(), getTextMatrix(), colorTransform, zoom); + break; + case SVG: + drawBorderSVG(swf, svgExporter, borderColor, fillColor, getRect(), getTextMatrix(), colorTransform, zoom); + break; + } + } + if (hasText) { + DynamicTextModel textModel = new DynamicTextModel(); + List txt = getTextWithStyle(); + TextStyle lastStyle = null; + char prevChar = 0; + boolean lastWasWhiteSpace = false; + for (int i = 0; i < txt.size(); i++) { + CharacterWithStyle cs = txt.get(i); + char c = cs.character; + if (c != '\r' && c != '\n') { + // create new SameStyleTextRecord for all words and all diffrent style text parts + if (lastWasWhiteSpace && !Character.isWhitespace(c)) { + textModel.newWord(); + lastWasWhiteSpace = false; + } + if (cs.style != lastStyle) { + lastStyle = cs.style; + textModel.style = lastStyle; + textModel.newRecord(); + } + Character nextChar = null; + if (i + 1 < txt.size()) { + nextChar = txt.get(i + 1).character; + } + int advance; + FontTag font = lastStyle.font; + DynamicTextGlyphEntry ge = new DynamicTextGlyphEntry(); + ge.fontFace = lastStyle.fontFace; + ge.fontStyle = (lastStyle.bold ? Font.BOLD : 0) | (lastStyle.italic ? Font.ITALIC : 0); + ge.character = c; + ge.glyphIndex = font == null ? -1 : font.charToGlyph(c); + if (font != null && font.hasLayout()) { + int kerningAdjustment = 0; + if (nextChar != null) { + kerningAdjustment = font.getCharKerningAdjustment(c, nextChar); + kerningAdjustment /= font.getDivider(); + } + advance = (int) Math.round(Math.round((double) lastStyle.fontHeight * (font.getGlyphAdvance(ge.glyphIndex) + kerningAdjustment) / (font.getDivider() * 1024.0))); + } else { + String fontName = lastStyle.fontFace != null ? lastStyle.fontFace : FontTag.defaultFontName; + int fontStyle = font == null ? ge.fontStyle : font.getFontStyle(); + advance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(fontName, fontStyle, (int) (lastStyle.fontHeight / SWF.unitDivisor), c, nextChar)); + } + ge.glyphAdvance = advance; + textModel.addGlyph(c, ge); + if (Character.isWhitespace(c)) { + lastWasWhiteSpace = true; + } + } else if (multiline) { + textModel.newParagraph(); + } + prevChar = c; + } + + textModel.calculateTextWidths(); + List> lines; + if (multiline && wordWrap) { + lines = new ArrayList<>(); + for (Paragraph paragraph : textModel.paragraphs) { + List line = new ArrayList<>(); + int lineLength = 0; + for (Word word : paragraph.words) { + if (lineLength + word.width <= bounds.getWidth()) { + line.addAll(word.records); + lineLength += word.width; + } else { + lines.add(line); + line = new ArrayList<>(); + line.addAll(word.records); + lineLength = 0; + } + } + if (!line.isEmpty()) { + lines.add(line); + } + } + } else { + lines = new ArrayList<>(); + for (Paragraph paragraph : textModel.paragraphs) { + List line = new ArrayList<>(); + for (Word word : paragraph.words) { + for (SameStyleTextRecord tr : word.records) { + line.add(tr); + } + } + lines.add(line); + } + } + + // remove spaces after last word + for (List line : lines) { + boolean removed = true; + while (removed) { + removed = false; + while (line.size() > 0 && line.get(line.size() - 1).glyphEntries.isEmpty()) { + line.remove(line.size() - 1); + removed = true; + } + if (line.size() > 0) { + SameStyleTextRecord lastRecord = line.get(line.size() - 1); + while (lastRecord.glyphEntries.size() > 0 + && Character.isWhitespace(lastRecord.glyphEntries.get(lastRecord.glyphEntries.size() - 1).character)) { + lastRecord.glyphEntries.remove(lastRecord.glyphEntries.size() - 1); + removed = true; + } + } + } + } + + textModel.calculateTextWidths(); + + List allTextRecords = new ArrayList<>(); + int lastHeight = 0; + int yOffset = -leading; + for (List line : lines) { + int width = 0; + int currentOffset = 0; + if (line.isEmpty()) { + currentOffset = lastHeight; + } else { + for (SameStyleTextRecord tr : line) { + width += tr.width; + int lineHeight = tr.style.fontHeight + tr.style.fontLeading; + lastHeight = lineHeight; + if (lineHeight > currentOffset) { + currentOffset = lineHeight; + } + } + } + yOffset += currentOffset; + int alignOffset = 0; + switch (align) { + case 0: // left + alignOffset = 0; + break; + case 1: // right + alignOffset = bounds.getWidth() - width; + break; + case 2: // center + alignOffset = (bounds.getWidth() - width) / 2; + break; + case 3: // justify + // todo; + break; + } + for (SameStyleTextRecord tr : line) { + tr.xOffset = alignOffset; + alignOffset += tr.width; + } + for (SameStyleTextRecord tr : line) { + TEXTRECORD tr2 = new TEXTRECORD(); + int fid = fontId; + if (fontClass != null) { + FontTag ft = swf.getFontByClass(fontClass); + if (ft != null) { + fid = ft.getFontId(); + } + } + if (tr.style.font != null) { + fid = tr.style.font.getFontId(); + } + + tr2.styleFlagsHasFont = fid != 0; + tr2.fontId = fid; + tr2.textHeight = tr.style.fontHeight; + if (tr.style.textColor != null) { + tr2.styleFlagsHasColor = true; + tr2.textColorA = tr.style.textColor; + } + // always add xOffset, because no xOffset and 0 xOffset is diffrent in text rendering + tr2.styleFlagsHasXOffset = true; + tr2.xOffset = tr.xOffset; + if (yOffset != 0) { + tr2.styleFlagsHasYOffset = true; + tr2.yOffset = yOffset; + } + tr2.glyphEntries = new ArrayList<>(tr.glyphEntries.size()); + for (GlyphCharacter ge : tr.glyphEntries) { + tr2.glyphEntries.add(ge.glyphEntry); + } + allTextRecords.add(tr2); + } + } + + switch (renderMode) { + case BITMAP: + staticTextToImage(swf, allTextRecords, 2, image, getTextMatrix(), transformation, colorTransform); + break; + case HTML5_CANVAS: + staticTextToHtmlCanvas(zoom, swf, allTextRecords, 2, htmlCanvasBuilder, getBounds(), getTextMatrix(), colorTransform); + break; + case SVG: + staticTextToSVG(swf, allTextRecords, 2, svgExporter, getBounds(), getTextMatrix(), colorTransform, zoom); + break; + } + } + } + + @Override + public ExportRectangle calculateTextBounds() { + return null; + } + + @Override + public int getNumFrames() { + return 1; + } + + @Override + public boolean isSingleFrame() { + return true; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java index a273a186f..434e25c03 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/tags/base/TextTag.java @@ -1,780 +1,780 @@ -/* - * Copyright (C) 2010-2016 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -package com.jpexs.decompiler.flash.tags.base; - -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.exporters.FontExporter; -import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; -import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; -import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; -import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; -import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; -import com.jpexs.decompiler.flash.helpers.HighlightedText; -import com.jpexs.decompiler.flash.importers.TextImportResizeTextBoundsMode; -import com.jpexs.decompiler.flash.tags.text.JustifyAlignGlyphEntry; -import com.jpexs.decompiler.flash.tags.text.TextAlign; -import com.jpexs.decompiler.flash.tags.text.TextParseException; -import com.jpexs.decompiler.flash.types.ColorTransform; -import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry; -import com.jpexs.decompiler.flash.types.FILLSTYLE; -import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; -import com.jpexs.decompiler.flash.types.GLYPHENTRY; -import com.jpexs.decompiler.flash.types.LINESTYLE; -import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.RGB; -import com.jpexs.decompiler.flash.types.RGBA; -import com.jpexs.decompiler.flash.types.SHAPE; -import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; -import com.jpexs.decompiler.flash.types.TEXTRECORD; -import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; -import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; -import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; -import com.jpexs.helpers.ByteArrayRange; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.SerializableImage; -import java.awt.Color; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.font.LineMetrics; -import java.awt.font.TextAttribute; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.w3c.dom.Element; - -/** - * - * @author JPEXS - */ -public abstract class TextTag extends DrawableTag { - - public TextTag(SWF swf, int id, String name, ByteArrayRange data) { - super(swf, id, name, data); - } - - public abstract MATRIX getTextMatrix(); - - public abstract List getTexts(); - - public abstract List getFontIds(); - - public abstract HighlightedText getFormattedText(boolean ignoreLetterSpacing); - - // use the texts from the "texts" argument when it is not null - public abstract boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws TextParseException; - - public abstract void updateTextBounds(); - - public abstract boolean alignText(TextAlign textAlign); - - public abstract boolean translateText(int diff); - - public abstract RECT getBounds(); - - public abstract void setBounds(RECT r); - - public abstract ExportRectangle calculateTextBounds(); - - @Override - public RECT getRect() { - return getRect(null); // parameter not used - } - - private static void updateRect(RECT ret, int x, int y) { - if (x < ret.Xmin) { - ret.Xmin = x; - } - if (x > ret.Xmax) { - ret.Xmax = x; - } - if (y < ret.Ymin) { - ret.Ymin = y; - } - if (y > ret.Ymax) { - ret.Ymax = y; - } - } - - public static void alignText(SWF swf, List textRecords, TextAlign textAlign) { - // Remove Justify align entries - for (TEXTRECORD tr : textRecords) { - for (int i = 0; i < tr.glyphEntries.size(); i++) { - GLYPHENTRY ge = tr.glyphEntries.get(i); - if (ge instanceof JustifyAlignGlyphEntry) { - JustifyAlignGlyphEntry jge = (JustifyAlignGlyphEntry) ge; - ge = new GLYPHENTRY(); - ge.glyphAdvance = jge.originalAdvance; - ge.glyphIndex = jge.glyphIndex; - tr.glyphEntries.set(i, ge); - } - } - } - - int xMin = Integer.MAX_VALUE; - int maxWidth = 0; - for (TEXTRECORD tr : textRecords) { - int xOffset = tr.styleFlagsHasXOffset ? tr.xOffset : 0; - if (xOffset < xMin) { - xMin = xOffset; - } - - int width = tr.getTotalAdvance(); - - if (width > maxWidth) { - maxWidth = width; - } - } - - FontTag font = null; - - for (TEXTRECORD tr : textRecords) { - if (tr.styleFlagsHasFont) { - FontTag font2 = swf.getFont(tr.fontId); - if (font2 != null) { - font = font2; - } - } - - int width = tr.getTotalAdvance(); - switch (textAlign) { - case LEFT: - tr.xOffset = xMin; - tr.styleFlagsHasXOffset = true; - break; - case CENTER: - tr.xOffset = xMin + (maxWidth - width) / 2; - tr.styleFlagsHasXOffset = true; - break; - case RIGHT: - tr.xOffset = xMin + maxWidth - width; - tr.styleFlagsHasXOffset = true; - break; - case JUSTIFY: - tr.xOffset = xMin; - tr.styleFlagsHasXOffset = true; - - if (font != null) { - int diff = maxWidth - width; - if (diff > 0) { - int spaces = 0; - int spaces2 = 0; - int state = 0; - List glyphEntries = new ArrayList<>(); - List glyphEntries2 = new ArrayList<>(); - for (GLYPHENTRY ge : tr.glyphEntries) { - char ch = font.glyphToChar(ge.glyphIndex); - boolean whitespace = Character.isWhitespace(ch); - switch (state) { - case 0: - if (!whitespace) { - state = 1; - } - break; - case 1: - if (whitespace) { - spaces2++; - glyphEntries2.add(ge); - } else { - spaces += spaces2; - spaces2 = 0; - glyphEntries.addAll(glyphEntries2); - glyphEntries2.clear(); - } - break; - } - } - - if (spaces > 0 && glyphEntries.size() > 0) { - int fix = diff / spaces; - int remaining = diff - fix * spaces; - for (GLYPHENTRY ge : glyphEntries) { - int diff2 = fix; - if (remaining-- > 0) { - diff2++; - } - - JustifyAlignGlyphEntry jge = new JustifyAlignGlyphEntry(); - jge.originalAdvance = ge.glyphAdvance; - jge.glyphAdvance = ge.glyphAdvance + diff2; - jge.glyphIndex = ge.glyphIndex; - int idx = tr.glyphEntries.indexOf(ge); - tr.glyphEntries.set(idx, jge); - } - } - } - } - break; - } - } - } - - public static Map getTextRecordsAttributes(List list, SWF swf) { - Map att = new HashMap<>(); - RECT textBounds = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - FontTag font = null; - int x = 0; - int y = 0; - int textHeight = 12; - int lineSpacing = 0; - double leading = 0; - double ascent = 0; - double descent = 0; - double lineDistance = 0; - - List glyphs = null; - boolean firstLine = true; - double top = 0; - List allLeftMargins = new ArrayList<>(); - List allLetterSpacings = new ArrayList<>(); - FontMetrics fontMetrics; - BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); - Graphics graphics = bi.getGraphics(); - Font aFont = null; - int currentLeftMargin; - for (int r = 0; r < list.size(); r++) { - TEXTRECORD rec = list.get(r); - if (rec.styleFlagsHasFont) { - FontTag font2 = swf.getFont(rec.fontId); - if (font2 != null) { - font = font2; - } - textHeight = rec.textHeight; - if (font == null) { - Logger.getLogger(TextTag.class.getName()).log(Level.SEVERE, "Font with id=" + rec.fontId + " was not found."); - continue; - } - - glyphs = font.getGlyphShapeTable(); - - if (!font.hasLayout()) { - String fontName = FontTag.getFontNameWithFallback(font.getFontNameIntag()); - aFont = new Font(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor)); - - Map attr = new HashMap<>(); - - attr.put(TextAttribute.KERNING, TextAttribute.KERNING_ON); - attr.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON); - aFont = aFont.deriveFont(attr); - - fontMetrics = graphics.getFontMetrics(aFont); - LineMetrics lm = fontMetrics.getLineMetrics("A", graphics); - ascent = lm.getAscent(); - descent = lm.getDescent(); - leading = lm.getLeading(); - lineDistance = ascent + descent; - } else { - leading = ((double) font.getLeading() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); - ascent = ((double) font.getAscent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); - descent = ((double) font.getDescent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); - lineDistance = ascent + descent; - } - } - currentLeftMargin = 0; - if (rec.styleFlagsHasXOffset) { - x = rec.xOffset; - currentLeftMargin = x; - } - if (rec.styleFlagsHasYOffset) { - if (!firstLine) { - top += ascent + descent; - int topint = (int) (Math.round(top) * SWF.unitDivisor); - lineSpacing = rec.yOffset - topint; - top += lineSpacing / SWF.unitDivisor; - } else { - top = ascent; - } - y = rec.yOffset; - } - - firstLine = false; - allLeftMargins.add(currentLeftMargin); - - if (glyphs == null) { - Logger.getLogger(TextTag.class.getName()).log(Level.SEVERE, "Glyphs not found."); - continue; - } - - int letterSpacing = Integer.MAX_VALUE; - for (int e = 0; e < rec.glyphEntries.size(); e++) { - GLYPHENTRY entry = rec.glyphEntries.get(e); - GLYPHENTRY nextEntry = null; - if (e < rec.glyphEntries.size() - 1) { - nextEntry = rec.glyphEntries.get(e + 1); - } - RECT rect = SHAPERECORD.getBounds(glyphs.get(entry.glyphIndex).shapeRecords); - rect.Xmax = (int) Math.round(((double) rect.Xmax * textHeight) / (font.getDivider() * 1024)); - rect.Xmin = (int) Math.round(((double) rect.Xmin * textHeight) / (font.getDivider() * 1024)); - rect.Ymax = (int) Math.round(((double) rect.Ymax * textHeight) / (font.getDivider() * 1024)); - rect.Ymin = (int) Math.round(((double) rect.Ymin * textHeight) / (font.getDivider() * 1024)); - updateRect(textBounds, x + rect.Xmin, y + rect.Ymin); - updateRect(textBounds, x + rect.Xmax, y + rect.Ymax); - int adv = entry.glyphAdvance; - - int defaultAdvance; - if (font.hasLayout()) { - int kerningAdjustment = 0; - if (nextEntry != null) { - kerningAdjustment = font.getGlyphKerningAdjustment(entry.glyphIndex, nextEntry.glyphIndex); - } - defaultAdvance = (int) (Math.round(textHeight * (font.getGlyphAdvance(entry.glyphIndex) + kerningAdjustment) / (1024.0 * font.getDivider()))); - } else { - defaultAdvance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(aFont, font.glyphToChar(entry.glyphIndex), nextEntry == null ? null : font.glyphToChar(nextEntry.glyphIndex))); - } - int newLetterSpacing = adv - defaultAdvance; - if (e == 0 || e == rec.glyphEntries.size() - 1) { - if (rec.glyphEntries.size() == 1) { - letterSpacing = 0; - } - } else if (newLetterSpacing < letterSpacing) { - letterSpacing = newLetterSpacing; - } - x += adv; - } - allLetterSpacings.add(letterSpacing); - } - att.put("indent", 0); //? - att.put("rightMargin", 0); //? - att.put("lineSpacing", lineSpacing); - att.put("textBounds", textBounds); - att.put("allLeftMargins", allLeftMargins); - att.put("allLetterSpacings", allLetterSpacings); - return att; - } - - public static SHAPE getBorderShape(RGB borderColor, RGB fillColor, RECT rect) { - SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); - shape.fillStyles = new FILLSTYLEARRAY(); - if (fillColor != null) { - shape.fillStyles.fillStyles = new FILLSTYLE[1]; - FILLSTYLE fillStyle = new FILLSTYLE(); - fillStyle.fillStyleType = FILLSTYLE.SOLID; - fillStyle.color = fillColor; - shape.fillStyles.fillStyles[0] = fillStyle; - } else { - shape.fillStyles.fillStyles = new FILLSTYLE[0]; - } - shape.lineStyles = new LINESTYLEARRAY(); - shape.lineStyles.lineStyles = new LINESTYLE[1]; - LINESTYLE lineStyle = new LINESTYLE(); - lineStyle.color = borderColor; - lineStyle.width = 20; - shape.lineStyles.lineStyles[0] = lineStyle; - shape.shapeRecords = new ArrayList<>(); - StyleChangeRecord style = new StyleChangeRecord(); - style.lineStyle = 1; - style.stateLineStyle = true; - if (fillColor != null) { - style.stateFillStyle0 = true; - style.fillStyle0 = 1; - } - style.stateMoveTo = true; - shape.shapeRecords.add(style); - StraightEdgeRecord top = new StraightEdgeRecord(); - top.generalLineFlag = true; - top.deltaX = rect.getWidth(); - StraightEdgeRecord right = new StraightEdgeRecord(); - right.generalLineFlag = true; - right.deltaY = rect.getHeight(); - StraightEdgeRecord bottom = new StraightEdgeRecord(); - bottom.generalLineFlag = true; - bottom.deltaX = -rect.getWidth(); - StraightEdgeRecord left = new StraightEdgeRecord(); - left.generalLineFlag = true; - left.deltaY = -rect.getHeight(); - shape.shapeRecords.add(top); - shape.shapeRecords.add(right); - shape.shapeRecords.add(bottom); - shape.shapeRecords.add(left); - shape.shapeRecords.add(new EndShapeRecord()); - return shape; - } - - public static void drawBorder(SWF swf, SerializableImage image, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { - Graphics2D g = (Graphics2D) image.getGraphics(); - Matrix mat = transformation.clone(); - mat = mat.concatenate(new Matrix(textMatrix)); - BitmapExporter.export(swf, getBorderShape(borderColor, fillColor, rect), null, image, mat, mat, colorTransform); - } - - public static void drawBorderHtmlCanvas(SWF swf, StringBuilder result, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double unitDivisor) { - Matrix mat = new Matrix(textMatrix); - result.append("\tctx.save();\r\n"); - result.append("\tctx.transform(").append(mat.scaleX).append(",").append(mat.rotateSkew0).append(",").append(mat.rotateSkew1).append(",").append(mat.scaleY).append(",").append(mat.translateX).append(",").append(mat.translateY).append(");\r\n"); - SHAPE shape = getBorderShape(borderColor, fillColor, rect); - CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, shape, colorTransform, 0, 0); - cse.export(); - result.append(cse.getShapeData()); - result.append("\tctx.restore();\r\n"); - } - - public static void drawBorderSVG(SWF swf, SVGExporter exporter, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double zoom) { - exporter.createSubGroup(new Matrix(textMatrix), null); - SHAPE shape = getBorderShape(borderColor, fillColor, rect); - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform, zoom); - shapeExporter.export(); - exporter.endGroup(); - } - - public static void staticTextToImage(SWF swf, List textRecords, int numText, SerializableImage image, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { - int textColor = 0; - FontTag font = null; - int textHeight = 12; - int x = 0; - int y = 0; - List glyphs = null; - Matrix mat0 = transformation.clone(); - mat0 = mat0.concatenate(new Matrix(textMatrix)); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasColor) { - if (numText == 2) { - textColor = rec.textColorA.toInt(); - } else { - textColor = rec.textColor.toInt(); - } - - if (colorTransform != null) { - textColor = colorTransform.apply(textColor); - } - } - if (rec.styleFlagsHasFont) { - FontTag font2 = swf.getFont(rec.fontId); - if (font2 != null) { - font = font2; - } - glyphs = font == null ? null : font.getGlyphShapeTable(); - textHeight = rec.textHeight; - } - if (rec.styleFlagsHasXOffset) { - x = rec.xOffset; - } - if (rec.styleFlagsHasYOffset) { - y = rec.yOffset; - } - - double divider = font == null ? 1 : font.getDivider(); - double rat = textHeight / 1024.0 / divider; - - Matrix matScale = Matrix.getScaleInstance(rat); - Color textColor2 = new Color(textColor, true); - for (GLYPHENTRY entry : rec.glyphEntries) { - matScale.translateX = x; - matScale.translateY = y; - - Matrix mat = mat0.concatenate(matScale); - SHAPE shape = null; - if (entry.glyphIndex != -1 && glyphs != null) { - // shapeNum: 1 - shape = glyphs.get(entry.glyphIndex); - } else if (entry instanceof DynamicTextGlyphEntry) { - DynamicTextGlyphEntry dynamicEntry = (DynamicTextGlyphEntry) entry; - if (dynamicEntry.fontFace != null) { - FontTag fnt = swf.getFontByName(dynamicEntry.fontFace); - if (fnt != null) { - shape = fnt.getGlyphShapeTable().get(entry.glyphIndex); - } else { - shape = SHAPERECORD.fontCharacterToSHAPE(new Font(dynamicEntry.fontFace, dynamicEntry.fontStyle, 12), (int) Math.round(divider * 1024), dynamicEntry.character); - } - } - } - - if (shape != null) { - BitmapExporter.export(swf, shape, textColor2, image, mat, mat, colorTransform); - if (SHAPERECORD.DRAW_BOUNDING_BOX) { - RGB borderColor = new RGBA(Color.black); - RGB fillColor = new RGBA(new Color(255, 255, 255, 0)); - RECT bounds = shape.getBounds(); - mat = Matrix.getTranslateInstance(bounds.Xmin, bounds.Ymin).preConcatenate(mat); - TextTag.drawBorder(swf, image, borderColor, fillColor, bounds, new MATRIX(), mat, colorTransform); - } - } - - x += entry.glyphAdvance; - } - } - } - - public static ExportRectangle calculateTextBounds(SWF swf, List textRecords, MATRIX textMatrix) { - FontTag font = null; - int textHeight = 12; - int x = 0; - int y = 0; - List glyphs = null; - ExportRectangle result = null; - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasFont) { - font = swf.getFont(rec.fontId); - glyphs = font == null ? null : font.getGlyphShapeTable(); - textHeight = rec.textHeight; - } - if (rec.styleFlagsHasXOffset) { - x = rec.xOffset; - } - if (rec.styleFlagsHasYOffset) { - y = rec.yOffset; - } - - double rat = textHeight / 1024.0 / (font == null ? 1 : font.getDivider()); - - for (GLYPHENTRY entry : rec.glyphEntries) { - Matrix mat = new Matrix(); - mat = mat.concatenate(new Matrix(textMatrix)); - Matrix matTr = Matrix.getTranslateInstance(x, y); - mat = mat.concatenate(matTr); - mat = mat.concatenate(Matrix.getScaleInstance(rat)); - if (entry.glyphIndex != -1 && glyphs != null) { - // shapeNum: 1 - SHAPE shape = glyphs.get(entry.glyphIndex); - RECT glyphBounds = shape.getBounds(); - int glyphWidth = glyphBounds.getWidth(); - int glyphHeight = glyphBounds.getHeight(); - glyphBounds.Xmin -= glyphWidth / 2; - glyphBounds.Ymin -= glyphHeight / 2; - glyphBounds.Xmax += glyphWidth; - glyphBounds.Ymax += glyphHeight; - if (glyphBounds.Xmax > glyphBounds.Xmin && glyphBounds.Ymax > glyphBounds.Ymin) { - ExportRectangle rect = mat.transform(new ExportRectangle(glyphBounds)); - if (result == null) { - result = rect; - } else { - result.xMin = Math.min(result.xMin, rect.xMin); - result.yMin = Math.min(result.yMin, rect.yMin); - result.xMax = Math.max(result.xMax, rect.xMax); - result.yMax = Math.max(result.yMax, rect.yMax); - } - } - x += entry.glyphAdvance; - } - } - } - - return result; - } - - protected void updateTextBounds(RECT textBounds) { - TextImportResizeTextBoundsMode resizeMode = Configuration.textImportResizeTextBoundsMode.get(); - if (resizeMode != null && (resizeMode.equals(TextImportResizeTextBoundsMode.GROW_ONLY) || resizeMode.equals(TextImportResizeTextBoundsMode.GROW_AND_SHRINK))) { - ExportRectangle newBounds = calculateTextBounds(); - if (newBounds != null) { - int xMin = (int) Math.floor(newBounds.xMin); - int yMin = (int) Math.floor(newBounds.yMin); - int xMax = (int) Math.ceil(newBounds.xMax); - int yMax = (int) Math.ceil(newBounds.yMax); - if (resizeMode.equals(TextImportResizeTextBoundsMode.GROW_ONLY)) { - textBounds.Xmin = Math.min(xMin, textBounds.Xmin); - textBounds.Ymin = Math.min(yMin, textBounds.Ymin); - textBounds.Xmax = Math.max(xMax, textBounds.Xmax); - textBounds.Ymax = Math.max(yMax, textBounds.Ymax); - } else if (resizeMode.equals(TextImportResizeTextBoundsMode.GROW_AND_SHRINK)) { - textBounds.Xmin = xMin; - textBounds.Ymin = yMin; - textBounds.Xmax = xMax; - textBounds.Ymax = yMax; - } - } - } - } - - public static void staticTextToHtmlCanvas(double unitDivisor, SWF swf, List textRecords, int numText, StringBuilder result, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform) { - int textColor = 0; - FontTag font = null; - int fontId = -1; - int textHeight = 12; - int x = 0; - int y = 0; - - List glyphs = new ArrayList<>(); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasColor) { - if (numText == 2) { - textColor = rec.textColorA.toInt(); - } else { - textColor = rec.textColor.toInt(); - } - - if (colorTransform != null) { - textColor = colorTransform.apply(textColor); - } - } - if (rec.styleFlagsHasFont) { - font = swf.getFont(rec.fontId); - fontId = rec.fontId; - glyphs = font.getGlyphShapeTable(); - textHeight = rec.textHeight; - } - if (rec.styleFlagsHasXOffset) { - x = rec.xOffset; - } - if (rec.styleFlagsHasYOffset) { - y = rec.yOffset; - } - - double rat = textHeight / 1024.0 / font.getDivider(); - - result.append("\tvar textColor = ").append(CanvasShapeExporter.color(textColor)).append(";\r\n"); - for (GLYPHENTRY entry : rec.glyphEntries) { - Matrix mat = (new Matrix(textMatrix).concatenate(Matrix.getTranslateInstance(x, y))).concatenate(Matrix.getScaleInstance(rat)); - if (entry.glyphIndex != -1) { - // shapeNum: 1 - result.append("\tctx.save();\r\n"); - result.append("\tctx.transform(").append(mat.scaleX).append(",").append(mat.rotateSkew0).append(",").append(mat.rotateSkew1).append(",").append(mat.scaleY).append(",").append(mat.translateX).append(",").append(mat.translateY).append(");\r\n"); - result.append("\tfont").append(fontId).append("(ctx,\"").append(("" + font.glyphToChar(entry.glyphIndex)).replace("\\", "\\\\").replace("\"", "\\\"")).append("\",textColor);\r\n"); - result.append("\tctx.restore();\r\n"); - x += entry.glyphAdvance; - } - } - } - } - - public static void staticTextToSVG(SWF swf, List textRecords, int numText, SVGExporter exporter, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform, double zoom) { - int textColor = 0; - FontTag font = null; - int textHeight = 12; - int x = 0; - int y = 0; - List glyphs = new ArrayList<>(); - for (TEXTRECORD rec : textRecords) { - if (rec.styleFlagsHasColor) { - if (numText == 2) { - textColor = rec.textColorA.toInt(); - } else { - textColor = rec.textColor.toInt(); - } - - if (colorTransform != null) { - textColor = colorTransform.apply(textColor); - } - } - if (rec.styleFlagsHasFont) { - font = swf.getFont(rec.fontId); - glyphs = font.getGlyphShapeTable(); - textHeight = rec.textHeight; - } - int offsetX = 0; - int offsetY = 0; - if (rec.styleFlagsHasXOffset) { - offsetX = rec.xOffset; - x = offsetX; - } - if (rec.styleFlagsHasYOffset) { - offsetY = rec.yOffset; - y = offsetY; - } - - double rat = textHeight / 1024.0 / font.getDivider(); - - exporter.createSubGroup(new Matrix(textMatrix), null); - if (exporter.useTextTag) { - StringBuilder text = new StringBuilder(); - int totalAdvance = 0; - for (GLYPHENTRY entry : rec.glyphEntries) { - if (entry.glyphIndex != -1) { - char ch = font.glyphToChar(entry.glyphIndex); - text.append(ch); - totalAdvance += entry.glyphAdvance; - } - } - - boolean hasOffset = offsetX != 0 || offsetY != 0; - if (hasOffset) { - exporter.createSubGroup(Matrix.getTranslateInstance(offsetX, offsetY), null); - } - - Element textElement = exporter.createElement("text"); - textElement.setAttribute("font-size", Double.toString(rat * 1024)); - textElement.setAttribute("font-family", font.getFontNameIntag()); - textElement.setAttribute("textLength", Double.toString(totalAdvance / SWF.unitDivisor)); - textElement.setAttribute("lengthAdjust", "spacing"); - textElement.setTextContent(text.toString()); - - RGBA colorA = new RGBA(textColor); - textElement.setAttribute("fill", colorA.toHexRGB()); - if (colorA.alpha != 255) { - textElement.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); - } - - exporter.addToGroup(textElement); - FontExportMode fontExportMode = FontExportMode.WOFF; - exporter.addStyle(font.getFontNameIntag(), new FontExporter().exportFont(font, fontExportMode), fontExportMode); - - if (hasOffset) { - exporter.endGroup(); - } - } else { - for (GLYPHENTRY entry : rec.glyphEntries) { - Matrix mat = Matrix.getTranslateInstance(x, y).concatenate(Matrix.getScaleInstance(rat)); - if (entry.glyphIndex != -1) { - // shapeNum: 1 - SHAPE shape = glyphs.get(entry.glyphIndex); - char ch = font.glyphToChar(entry.glyphIndex); - - String charId = null; - Map chs; - if (exporter.exportedChars.containsKey(font)) { - chs = exporter.exportedChars.get(font); - if (chs.containsKey(ch)) { - charId = chs.get(ch); - } - } else { - chs = new HashMap<>(); - exporter.exportedChars.put(font, chs); - } - - if (charId == null) { - charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontNameIntag() + "_" + ch)); - exporter.createDefGroup(null, charId); - SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform, zoom); - shapeExporter.export(); - exporter.endGroup(); - chs.put(ch, charId); - } - - Element charImage = exporter.addUse(mat, bounds, charId, null); - RGBA colorA = new RGBA(textColor); - charImage.setAttribute("fill", colorA.toHexRGB()); - if (colorA.alpha != 255) { - charImage.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); - } - - x += entry.glyphAdvance; - } - } - } - exporter.endGroup(); - } - } - - @Override - public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) { - RECT r = getBounds(); - Shape shp = new Rectangle.Double(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()); - return transformation.toTransform().createTransformedShape(shp); //TODO: match character shapes (?) - } -} +/* + * Copyright (C) 2010-2016 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.tags.base; + +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.exporters.FontExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; +import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter; +import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter; +import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter; +import com.jpexs.decompiler.flash.helpers.HighlightedText; +import com.jpexs.decompiler.flash.importers.TextImportResizeTextBoundsMode; +import com.jpexs.decompiler.flash.tags.text.JustifyAlignGlyphEntry; +import com.jpexs.decompiler.flash.tags.text.TextAlign; +import com.jpexs.decompiler.flash.tags.text.TextParseException; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.DynamicTextGlyphEntry; +import com.jpexs.decompiler.flash.types.FILLSTYLE; +import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY; +import com.jpexs.decompiler.flash.types.GLYPHENTRY; +import com.jpexs.decompiler.flash.types.LINESTYLE; +import com.jpexs.decompiler.flash.types.LINESTYLEARRAY; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.RGB; +import com.jpexs.decompiler.flash.types.RGBA; +import com.jpexs.decompiler.flash.types.SHAPE; +import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE; +import com.jpexs.decompiler.flash.types.TEXTRECORD; +import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD; +import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord; +import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord; +import com.jpexs.helpers.ByteArrayRange; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.SerializableImage; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.w3c.dom.Element; + +/** + * + * @author JPEXS + */ +public abstract class TextTag extends DrawableTag { + + public TextTag(SWF swf, int id, String name, ByteArrayRange data) { + super(swf, id, name, data); + } + + public abstract MATRIX getTextMatrix(); + + public abstract List getTexts(); + + public abstract List getFontIds(); + + public abstract HighlightedText getFormattedText(boolean ignoreLetterSpacing); + + // use the texts from the "texts" argument when it is not null + public abstract boolean setFormattedText(MissingCharacterHandler missingCharHandler, String formattedText, String[] texts) throws TextParseException; + + public abstract void updateTextBounds(); + + public abstract boolean alignText(TextAlign textAlign); + + public abstract boolean translateText(int diff); + + public abstract RECT getBounds(); + + public abstract void setBounds(RECT r); + + public abstract ExportRectangle calculateTextBounds(); + + @Override + public RECT getRect() { + return getRect(null); // parameter not used + } + + private static void updateRect(RECT ret, int x, int y) { + if (x < ret.Xmin) { + ret.Xmin = x; + } + if (x > ret.Xmax) { + ret.Xmax = x; + } + if (y < ret.Ymin) { + ret.Ymin = y; + } + if (y > ret.Ymax) { + ret.Ymax = y; + } + } + + public static void alignText(SWF swf, List textRecords, TextAlign textAlign) { + // Remove Justify align entries + for (TEXTRECORD tr : textRecords) { + for (int i = 0; i < tr.glyphEntries.size(); i++) { + GLYPHENTRY ge = tr.glyphEntries.get(i); + if (ge instanceof JustifyAlignGlyphEntry) { + JustifyAlignGlyphEntry jge = (JustifyAlignGlyphEntry) ge; + ge = new GLYPHENTRY(); + ge.glyphAdvance = jge.originalAdvance; + ge.glyphIndex = jge.glyphIndex; + tr.glyphEntries.set(i, ge); + } + } + } + + int xMin = Integer.MAX_VALUE; + int maxWidth = 0; + for (TEXTRECORD tr : textRecords) { + int xOffset = tr.styleFlagsHasXOffset ? tr.xOffset : 0; + if (xOffset < xMin) { + xMin = xOffset; + } + + int width = tr.getTotalAdvance(); + + if (width > maxWidth) { + maxWidth = width; + } + } + + FontTag font = null; + + for (TEXTRECORD tr : textRecords) { + if (tr.styleFlagsHasFont) { + FontTag font2 = swf.getFont(tr.fontId); + if (font2 != null) { + font = font2; + } + } + + int width = tr.getTotalAdvance(); + switch (textAlign) { + case LEFT: + tr.xOffset = xMin; + tr.styleFlagsHasXOffset = true; + break; + case CENTER: + tr.xOffset = xMin + (maxWidth - width) / 2; + tr.styleFlagsHasXOffset = true; + break; + case RIGHT: + tr.xOffset = xMin + maxWidth - width; + tr.styleFlagsHasXOffset = true; + break; + case JUSTIFY: + tr.xOffset = xMin; + tr.styleFlagsHasXOffset = true; + + if (font != null) { + int diff = maxWidth - width; + if (diff > 0) { + int spaces = 0; + int spaces2 = 0; + int state = 0; + List glyphEntries = new ArrayList<>(); + List glyphEntries2 = new ArrayList<>(); + for (GLYPHENTRY ge : tr.glyphEntries) { + char ch = font.glyphToChar(ge.glyphIndex); + boolean whitespace = Character.isWhitespace(ch); + switch (state) { + case 0: + if (!whitespace) { + state = 1; + } + break; + case 1: + if (whitespace) { + spaces2++; + glyphEntries2.add(ge); + } else { + spaces += spaces2; + spaces2 = 0; + glyphEntries.addAll(glyphEntries2); + glyphEntries2.clear(); + } + break; + } + } + + if (spaces > 0 && glyphEntries.size() > 0) { + int fix = diff / spaces; + int remaining = diff - fix * spaces; + for (GLYPHENTRY ge : glyphEntries) { + int diff2 = fix; + if (remaining-- > 0) { + diff2++; + } + + JustifyAlignGlyphEntry jge = new JustifyAlignGlyphEntry(); + jge.originalAdvance = ge.glyphAdvance; + jge.glyphAdvance = ge.glyphAdvance + diff2; + jge.glyphIndex = ge.glyphIndex; + int idx = tr.glyphEntries.indexOf(ge); + tr.glyphEntries.set(idx, jge); + } + } + } + } + break; + } + } + } + + public static Map getTextRecordsAttributes(List list, SWF swf) { + Map att = new HashMap<>(); + RECT textBounds = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + FontTag font = null; + int x = 0; + int y = 0; + int textHeight = 12; + int lineSpacing = 0; + double leading = 0; + double ascent = 0; + double descent = 0; + double lineDistance = 0; + + List glyphs = null; + boolean firstLine = true; + double top = 0; + List allLeftMargins = new ArrayList<>(); + List allLetterSpacings = new ArrayList<>(); + FontMetrics fontMetrics; + BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + Graphics graphics = bi.getGraphics(); + Font aFont = null; + int currentLeftMargin; + for (int r = 0; r < list.size(); r++) { + TEXTRECORD rec = list.get(r); + if (rec.styleFlagsHasFont) { + FontTag font2 = swf.getFont(rec.fontId); + if (font2 != null) { + font = font2; + } + textHeight = rec.textHeight; + if (font == null) { + Logger.getLogger(TextTag.class.getName()).log(Level.SEVERE, "Font with id=" + rec.fontId + " was not found."); + continue; + } + + glyphs = font.getGlyphShapeTable(); + + if (!font.hasLayout()) { + String fontName = FontTag.getFontNameWithFallback(font.getFontNameIntag()); + aFont = new Font(fontName, font.getFontStyle(), (int) (textHeight / SWF.unitDivisor)); + + Map attr = new HashMap<>(); + + attr.put(TextAttribute.KERNING, TextAttribute.KERNING_ON); + attr.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON); + aFont = aFont.deriveFont(attr); + + fontMetrics = graphics.getFontMetrics(aFont); + LineMetrics lm = fontMetrics.getLineMetrics("A", graphics); + ascent = lm.getAscent(); + descent = lm.getDescent(); + leading = lm.getLeading(); + lineDistance = ascent + descent; + } else { + leading = ((double) font.getLeading() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); + ascent = ((double) font.getAscent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); + descent = ((double) font.getDescent() * textHeight / 1024.0 / font.getDivider() / SWF.unitDivisor); + lineDistance = ascent + descent; + } + } + currentLeftMargin = 0; + if (rec.styleFlagsHasXOffset) { + x = rec.xOffset; + currentLeftMargin = x; + } + if (rec.styleFlagsHasYOffset) { + if (!firstLine) { + top += ascent + descent; + int topint = (int) (Math.round(top) * SWF.unitDivisor); + lineSpacing = rec.yOffset - topint; + top += lineSpacing / SWF.unitDivisor; + } else { + top = ascent; + } + y = rec.yOffset; + } + + firstLine = false; + allLeftMargins.add(currentLeftMargin); + + if (glyphs == null) { + Logger.getLogger(TextTag.class.getName()).log(Level.SEVERE, "Glyphs not found."); + continue; + } + + int letterSpacing = Integer.MAX_VALUE; + for (int e = 0; e < rec.glyphEntries.size(); e++) { + GLYPHENTRY entry = rec.glyphEntries.get(e); + GLYPHENTRY nextEntry = null; + if (e < rec.glyphEntries.size() - 1) { + nextEntry = rec.glyphEntries.get(e + 1); + } + RECT rect = SHAPERECORD.getBounds(glyphs.get(entry.glyphIndex).shapeRecords); + rect.Xmax = (int) Math.round(((double) rect.Xmax * textHeight) / (font.getDivider() * 1024)); + rect.Xmin = (int) Math.round(((double) rect.Xmin * textHeight) / (font.getDivider() * 1024)); + rect.Ymax = (int) Math.round(((double) rect.Ymax * textHeight) / (font.getDivider() * 1024)); + rect.Ymin = (int) Math.round(((double) rect.Ymin * textHeight) / (font.getDivider() * 1024)); + updateRect(textBounds, x + rect.Xmin, y + rect.Ymin); + updateRect(textBounds, x + rect.Xmax, y + rect.Ymax); + int adv = entry.glyphAdvance; + + int defaultAdvance; + if (font.hasLayout()) { + int kerningAdjustment = 0; + if (nextEntry != null) { + kerningAdjustment = font.getGlyphKerningAdjustment(entry.glyphIndex, nextEntry.glyphIndex); + } + defaultAdvance = (int) (Math.round(textHeight * (font.getGlyphAdvance(entry.glyphIndex) + kerningAdjustment) / (1024.0 * font.getDivider()))); + } else { + defaultAdvance = (int) Math.round(SWF.unitDivisor * FontTag.getSystemFontAdvance(aFont, font.glyphToChar(entry.glyphIndex), nextEntry == null ? null : font.glyphToChar(nextEntry.glyphIndex))); + } + int newLetterSpacing = adv - defaultAdvance; + if (e == 0 || e == rec.glyphEntries.size() - 1) { + if (rec.glyphEntries.size() == 1) { + letterSpacing = 0; + } + } else if (newLetterSpacing < letterSpacing) { + letterSpacing = newLetterSpacing; + } + x += adv; + } + allLetterSpacings.add(letterSpacing); + } + att.put("indent", 0); //? + att.put("rightMargin", 0); //? + att.put("lineSpacing", lineSpacing); + att.put("textBounds", textBounds); + att.put("allLeftMargins", allLeftMargins); + att.put("allLetterSpacings", allLetterSpacings); + return att; + } + + public static SHAPE getBorderShape(RGB borderColor, RGB fillColor, RECT rect) { + SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE(); + shape.fillStyles = new FILLSTYLEARRAY(); + if (fillColor != null) { + shape.fillStyles.fillStyles = new FILLSTYLE[1]; + FILLSTYLE fillStyle = new FILLSTYLE(); + fillStyle.fillStyleType = FILLSTYLE.SOLID; + fillStyle.color = fillColor; + shape.fillStyles.fillStyles[0] = fillStyle; + } else { + shape.fillStyles.fillStyles = new FILLSTYLE[0]; + } + shape.lineStyles = new LINESTYLEARRAY(); + shape.lineStyles.lineStyles = new LINESTYLE[1]; + LINESTYLE lineStyle = new LINESTYLE(); + lineStyle.color = borderColor; + lineStyle.width = 20; + shape.lineStyles.lineStyles[0] = lineStyle; + shape.shapeRecords = new ArrayList<>(); + StyleChangeRecord style = new StyleChangeRecord(); + style.lineStyle = 1; + style.stateLineStyle = true; + if (fillColor != null) { + style.stateFillStyle0 = true; + style.fillStyle0 = 1; + } + style.stateMoveTo = true; + shape.shapeRecords.add(style); + StraightEdgeRecord top = new StraightEdgeRecord(); + top.generalLineFlag = true; + top.deltaX = rect.getWidth(); + StraightEdgeRecord right = new StraightEdgeRecord(); + right.generalLineFlag = true; + right.deltaY = rect.getHeight(); + StraightEdgeRecord bottom = new StraightEdgeRecord(); + bottom.generalLineFlag = true; + bottom.deltaX = -rect.getWidth(); + StraightEdgeRecord left = new StraightEdgeRecord(); + left.generalLineFlag = true; + left.deltaY = -rect.getHeight(); + shape.shapeRecords.add(top); + shape.shapeRecords.add(right); + shape.shapeRecords.add(bottom); + shape.shapeRecords.add(left); + shape.shapeRecords.add(new EndShapeRecord()); + return shape; + } + + public static void drawBorder(SWF swf, SerializableImage image, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { + Graphics2D g = (Graphics2D) image.getGraphics(); + Matrix mat = transformation.clone(); + mat = mat.concatenate(new Matrix(textMatrix)); + BitmapExporter.export(swf, getBorderShape(borderColor, fillColor, rect), null, image, mat, mat, colorTransform); + } + + public static void drawBorderHtmlCanvas(SWF swf, StringBuilder result, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double unitDivisor) { + Matrix mat = new Matrix(textMatrix); + result.append("\tctx.save();\r\n"); + result.append("\tctx.transform(").append(mat.scaleX).append(",").append(mat.rotateSkew0).append(",").append(mat.rotateSkew1).append(",").append(mat.scaleY).append(",").append(mat.translateX).append(",").append(mat.translateY).append(");\r\n"); + SHAPE shape = getBorderShape(borderColor, fillColor, rect); + CanvasShapeExporter cse = new CanvasShapeExporter(null, unitDivisor, swf, shape, colorTransform, 0, 0); + cse.export(); + result.append(cse.getShapeData()); + result.append("\tctx.restore();\r\n"); + } + + public static void drawBorderSVG(SWF swf, SVGExporter exporter, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double zoom) { + exporter.createSubGroup(new Matrix(textMatrix), null); + SHAPE shape = getBorderShape(borderColor, fillColor, rect); + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform, zoom); + shapeExporter.export(); + exporter.endGroup(); + } + + public static void staticTextToImage(SWF swf, List textRecords, int numText, SerializableImage image, MATRIX textMatrix, Matrix transformation, ColorTransform colorTransform) { + int textColor = 0; + FontTag font = null; + int textHeight = 12; + int x = 0; + int y = 0; + List glyphs = null; + Matrix mat0 = transformation.clone(); + mat0 = mat0.concatenate(new Matrix(textMatrix)); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasColor) { + if (numText == 2) { + textColor = rec.textColorA.toInt(); + } else { + textColor = rec.textColor.toInt(); + } + + if (colorTransform != null) { + textColor = colorTransform.apply(textColor); + } + } + if (rec.styleFlagsHasFont) { + FontTag font2 = swf.getFont(rec.fontId); + if (font2 != null) { + font = font2; + } + glyphs = font == null ? null : font.getGlyphShapeTable(); + textHeight = rec.textHeight; + } + if (rec.styleFlagsHasXOffset) { + x = rec.xOffset; + } + if (rec.styleFlagsHasYOffset) { + y = rec.yOffset; + } + + double divider = font == null ? 1 : font.getDivider(); + double rat = textHeight / 1024.0 / divider; + + Matrix matScale = Matrix.getScaleInstance(rat); + Color textColor2 = new Color(textColor, true); + for (GLYPHENTRY entry : rec.glyphEntries) { + matScale.translateX = x; + matScale.translateY = y; + + Matrix mat = mat0.concatenate(matScale); + SHAPE shape = null; + if (entry.glyphIndex != -1 && glyphs != null) { + // shapeNum: 1 + shape = glyphs.get(entry.glyphIndex); + } else if (entry instanceof DynamicTextGlyphEntry) { + DynamicTextGlyphEntry dynamicEntry = (DynamicTextGlyphEntry) entry; + if (dynamicEntry.fontFace != null) { + FontTag fnt = swf.getFontByName(dynamicEntry.fontFace); + if (fnt != null) { + shape = fnt.getGlyphShapeTable().get(entry.glyphIndex); + } else { + shape = SHAPERECORD.fontCharacterToSHAPE(new Font(dynamicEntry.fontFace, dynamicEntry.fontStyle, 12), (int) Math.round(divider * 1024), dynamicEntry.character); + } + } + } + + if (shape != null) { + BitmapExporter.export(swf, shape, textColor2, image, mat, mat, colorTransform); + if (SHAPERECORD.DRAW_BOUNDING_BOX) { + RGB borderColor = new RGBA(Color.black); + RGB fillColor = new RGBA(new Color(255, 255, 255, 0)); + RECT bounds = shape.getBounds(); + mat = Matrix.getTranslateInstance(bounds.Xmin, bounds.Ymin).preConcatenate(mat); + TextTag.drawBorder(swf, image, borderColor, fillColor, bounds, new MATRIX(), mat, colorTransform); + } + } + + x += entry.glyphAdvance; + } + } + } + + public static ExportRectangle calculateTextBounds(SWF swf, List textRecords, MATRIX textMatrix) { + FontTag font = null; + int textHeight = 12; + int x = 0; + int y = 0; + List glyphs = null; + ExportRectangle result = null; + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasFont) { + font = swf.getFont(rec.fontId); + glyphs = font == null ? null : font.getGlyphShapeTable(); + textHeight = rec.textHeight; + } + if (rec.styleFlagsHasXOffset) { + x = rec.xOffset; + } + if (rec.styleFlagsHasYOffset) { + y = rec.yOffset; + } + + double rat = textHeight / 1024.0 / (font == null ? 1 : font.getDivider()); + + for (GLYPHENTRY entry : rec.glyphEntries) { + Matrix mat = new Matrix(); + mat = mat.concatenate(new Matrix(textMatrix)); + Matrix matTr = Matrix.getTranslateInstance(x, y); + mat = mat.concatenate(matTr); + mat = mat.concatenate(Matrix.getScaleInstance(rat)); + if (entry.glyphIndex != -1 && glyphs != null) { + // shapeNum: 1 + SHAPE shape = glyphs.get(entry.glyphIndex); + RECT glyphBounds = shape.getBounds(); + int glyphWidth = glyphBounds.getWidth(); + int glyphHeight = glyphBounds.getHeight(); + glyphBounds.Xmin -= glyphWidth / 2; + glyphBounds.Ymin -= glyphHeight / 2; + glyphBounds.Xmax += glyphWidth; + glyphBounds.Ymax += glyphHeight; + if (glyphBounds.Xmax > glyphBounds.Xmin && glyphBounds.Ymax > glyphBounds.Ymin) { + ExportRectangle rect = mat.transform(new ExportRectangle(glyphBounds)); + if (result == null) { + result = rect; + } else { + result.xMin = Math.min(result.xMin, rect.xMin); + result.yMin = Math.min(result.yMin, rect.yMin); + result.xMax = Math.max(result.xMax, rect.xMax); + result.yMax = Math.max(result.yMax, rect.yMax); + } + } + x += entry.glyphAdvance; + } + } + } + + return result; + } + + protected void updateTextBounds(RECT textBounds) { + TextImportResizeTextBoundsMode resizeMode = Configuration.textImportResizeTextBoundsMode.get(); + if (resizeMode != null && (resizeMode.equals(TextImportResizeTextBoundsMode.GROW_ONLY) || resizeMode.equals(TextImportResizeTextBoundsMode.GROW_AND_SHRINK))) { + ExportRectangle newBounds = calculateTextBounds(); + if (newBounds != null) { + int xMin = (int) Math.floor(newBounds.xMin); + int yMin = (int) Math.floor(newBounds.yMin); + int xMax = (int) Math.ceil(newBounds.xMax); + int yMax = (int) Math.ceil(newBounds.yMax); + if (resizeMode.equals(TextImportResizeTextBoundsMode.GROW_ONLY)) { + textBounds.Xmin = Math.min(xMin, textBounds.Xmin); + textBounds.Ymin = Math.min(yMin, textBounds.Ymin); + textBounds.Xmax = Math.max(xMax, textBounds.Xmax); + textBounds.Ymax = Math.max(yMax, textBounds.Ymax); + } else if (resizeMode.equals(TextImportResizeTextBoundsMode.GROW_AND_SHRINK)) { + textBounds.Xmin = xMin; + textBounds.Ymin = yMin; + textBounds.Xmax = xMax; + textBounds.Ymax = yMax; + } + } + } + } + + public static void staticTextToHtmlCanvas(double unitDivisor, SWF swf, List textRecords, int numText, StringBuilder result, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform) { + int textColor = 0; + FontTag font = null; + int fontId = -1; + int textHeight = 12; + int x = 0; + int y = 0; + + List glyphs = new ArrayList<>(); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasColor) { + if (numText == 2) { + textColor = rec.textColorA.toInt(); + } else { + textColor = rec.textColor.toInt(); + } + + if (colorTransform != null) { + textColor = colorTransform.apply(textColor); + } + } + if (rec.styleFlagsHasFont) { + font = swf.getFont(rec.fontId); + fontId = rec.fontId; + glyphs = font.getGlyphShapeTable(); + textHeight = rec.textHeight; + } + if (rec.styleFlagsHasXOffset) { + x = rec.xOffset; + } + if (rec.styleFlagsHasYOffset) { + y = rec.yOffset; + } + + double rat = textHeight / 1024.0 / font.getDivider(); + + result.append("\tvar textColor = ").append(CanvasShapeExporter.color(textColor)).append(";\r\n"); + for (GLYPHENTRY entry : rec.glyphEntries) { + Matrix mat = (new Matrix(textMatrix).concatenate(Matrix.getTranslateInstance(x, y))).concatenate(Matrix.getScaleInstance(rat)); + if (entry.glyphIndex != -1) { + // shapeNum: 1 + result.append("\tctx.save();\r\n"); + result.append("\tctx.transform(").append(mat.scaleX).append(",").append(mat.rotateSkew0).append(",").append(mat.rotateSkew1).append(",").append(mat.scaleY).append(",").append(mat.translateX).append(",").append(mat.translateY).append(");\r\n"); + result.append("\tfont").append(fontId).append("(ctx,\"").append(("" + font.glyphToChar(entry.glyphIndex)).replace("\\", "\\\\").replace("\"", "\\\"")).append("\",textColor);\r\n"); + result.append("\tctx.restore();\r\n"); + x += entry.glyphAdvance; + } + } + } + } + + public static void staticTextToSVG(SWF swf, List textRecords, int numText, SVGExporter exporter, RECT bounds, MATRIX textMatrix, ColorTransform colorTransform, double zoom) { + int textColor = 0; + FontTag font = null; + int textHeight = 12; + int x = 0; + int y = 0; + List glyphs = new ArrayList<>(); + for (TEXTRECORD rec : textRecords) { + if (rec.styleFlagsHasColor) { + if (numText == 2) { + textColor = rec.textColorA.toInt(); + } else { + textColor = rec.textColor.toInt(); + } + + if (colorTransform != null) { + textColor = colorTransform.apply(textColor); + } + } + if (rec.styleFlagsHasFont) { + font = swf.getFont(rec.fontId); + glyphs = font.getGlyphShapeTable(); + textHeight = rec.textHeight; + } + int offsetX = 0; + int offsetY = 0; + if (rec.styleFlagsHasXOffset) { + offsetX = rec.xOffset; + x = offsetX; + } + if (rec.styleFlagsHasYOffset) { + offsetY = rec.yOffset; + y = offsetY; + } + + double rat = textHeight / 1024.0 / font.getDivider(); + + exporter.createSubGroup(new Matrix(textMatrix), null); + if (exporter.useTextTag) { + StringBuilder text = new StringBuilder(); + int totalAdvance = 0; + for (GLYPHENTRY entry : rec.glyphEntries) { + if (entry.glyphIndex != -1) { + char ch = font.glyphToChar(entry.glyphIndex); + text.append(ch); + totalAdvance += entry.glyphAdvance; + } + } + + boolean hasOffset = offsetX != 0 || offsetY != 0; + if (hasOffset) { + exporter.createSubGroup(Matrix.getTranslateInstance(offsetX, offsetY), null); + } + + Element textElement = exporter.createElement("text"); + textElement.setAttribute("font-size", Double.toString(rat * 1024)); + textElement.setAttribute("font-family", font.getFontNameIntag()); + textElement.setAttribute("textLength", Double.toString(totalAdvance / SWF.unitDivisor)); + textElement.setAttribute("lengthAdjust", "spacing"); + textElement.setTextContent(text.toString()); + + RGBA colorA = new RGBA(textColor); + textElement.setAttribute("fill", colorA.toHexRGB()); + if (colorA.alpha != 255) { + textElement.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); + } + + exporter.addToGroup(textElement); + FontExportMode fontExportMode = FontExportMode.WOFF; + exporter.addStyle(font.getFontNameIntag(), new FontExporter().exportFont(font, fontExportMode), fontExportMode); + + if (hasOffset) { + exporter.endGroup(); + } + } else { + for (GLYPHENTRY entry : rec.glyphEntries) { + Matrix mat = Matrix.getTranslateInstance(x, y).concatenate(Matrix.getScaleInstance(rat)); + if (entry.glyphIndex != -1) { + // shapeNum: 1 + SHAPE shape = glyphs.get(entry.glyphIndex); + char ch = font.glyphToChar(entry.glyphIndex); + + String charId = null; + Map chs; + if (exporter.exportedChars.containsKey(font)) { + chs = exporter.exportedChars.get(font); + if (chs.containsKey(ch)) { + charId = chs.get(ch); + } + } else { + chs = new HashMap<>(); + exporter.exportedChars.put(font, chs); + } + + if (charId == null) { + charId = exporter.getUniqueId(Helper.getValidHtmlId("font_" + font.getFontNameIntag() + "_" + ch)); + exporter.createDefGroup(null, charId); + SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shape, exporter, null, colorTransform, zoom); + shapeExporter.export(); + exporter.endGroup(); + chs.put(ch, charId); + } + + Element charImage = exporter.addUse(mat, bounds, charId, null); + RGBA colorA = new RGBA(textColor); + charImage.setAttribute("fill", colorA.toHexRGB()); + if (colorA.alpha != 255) { + charImage.setAttribute("fill-opacity", Float.toString(colorA.getAlphaFloat())); + } + + x += entry.glyphAdvance; + } + } + } + exporter.endGroup(); + } + } + + @Override + public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) { + RECT r = getBounds(); + Shape shp = new Rectangle.Double(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()); + return transformation.toTransform().createTransformedShape(shp); //TODO: match character shapes (?) + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java b/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java index f880818a6..720590e2f 100644 --- a/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java +++ b/src/com/jpexs/decompiler/flash/gui/AdvancedSettingsDialog.java @@ -1,626 +1,626 @@ -/* - * Copyright (C) 2010-2016 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui; - -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationCategory; -import com.jpexs.decompiler.flash.configuration.ConfigurationDirectory; -import com.jpexs.decompiler.flash.configuration.ConfigurationFile; -import com.jpexs.decompiler.flash.configuration.ConfigurationInternal; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import com.jpexs.decompiler.flash.gui.helpers.SpringUtilities; -import com.jpexs.helpers.Helper; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.RenderingHints; -import java.awt.event.ActionEvent; -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.ResourceBundle; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.Icon; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.SpringLayout; -import javax.swing.WindowConstants; -import javax.swing.filechooser.FileFilter; -import javax.swing.table.DefaultTableModel; -import org.pushingpixels.substance.api.ColorSchemeAssociationKind; -import org.pushingpixels.substance.api.ComponentState; -import org.pushingpixels.substance.api.DecorationAreaType; -import org.pushingpixels.substance.api.SubstanceLookAndFeel; -import org.pushingpixels.substance.api.SubstanceSkin; -import org.pushingpixels.substance.api.renderers.SubstanceDefaultListCellRenderer; -import org.pushingpixels.substance.api.skin.SkinInfo; - -/** - * - * @author JPEXS - */ -public class AdvancedSettingsDialog extends AppDialog { - - private final Map componentsMap = new HashMap<>(); - - private JButton cancelButton; - - private JButton okButton; - - private JButton resetButton; - - /** - * Creates new form AdvancedSettingsDialog - * - * @param selectedCategory - */ - public AdvancedSettingsDialog(String selectedCategory) { - initComponents(selectedCategory); - View.centerScreen(this); - View.setWindowIcon(this); - - //configurationTable.setCellEditor(configurationTable.getDefaultEditor(null)); - pack(); - } - - private DefaultTableModel getModel() { - return new DefaultTableModel( - new Object[][]{}, - new String[]{ - translate("advancedSettings.columns.name"), - translate("advancedSettings.columns.value"), - translate("advancedSettings.columns.description") - } - ) { - Class[] types = new Class[]{ - String.class, Object.class, String.class - }; - - boolean[] canEdit = new boolean[]{ - false, true, false - }; - - @Override - public Class getColumnClass(int columnIndex) { - return types[columnIndex]; - } - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return canEdit[columnIndex]; - } - }; - } - - private static class SkinSelect { - - private final String name; - - private final String className; - - public SkinSelect(String name, String className) { - this.name = name; - this.className = className; - } - - public String getClassName() { - return className; - } - - @Override - public String toString() { - return name; - } - } - - private void initComponents(String selectedCategory) { - okButton = new JButton(); - cancelButton = new JButton(); - resetButton = new JButton(); - - setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - setTitle(translate("advancedSettings.dialog.title")); - setModal(true); - setPreferredSize(new Dimension(800, 500)); - - okButton.setText(AppStrings.translate("button.ok")); - okButton.addActionListener(this::okButtonActionPerformed); - - cancelButton.setText(AppStrings.translate("button.cancel")); - cancelButton.addActionListener(this::cancelButtonActionPerformed); - - resetButton.setText(AppStrings.translate("button.reset")); - resetButton.addActionListener(this::resetButtonActionPerformed); - - Container cnt = getContentPane(); - cnt.setLayout(new BorderLayout()); - //cnt.add(new JScrollPane(configurationTable),BorderLayout.CENTER); - - JPanel buttonsPanel = new JPanel(new BorderLayout()); - - JPanel buttonsLeftPanel = new JPanel(new FlowLayout()); - buttonsLeftPanel.add(resetButton, BorderLayout.WEST); - - buttonsPanel.add(buttonsLeftPanel, BorderLayout.WEST); - - JPanel buttonsRightPanel = new JPanel(new FlowLayout()); - buttonsRightPanel.add(cancelButton); - buttonsRightPanel.add(okButton); - buttonsPanel.add(buttonsRightPanel, BorderLayout.EAST); - - cnt.add(buttonsPanel, BorderLayout.SOUTH); - - JTabbedPane tabPane = new JTabbedPane(); - - JComboBox skinComboBox = new JComboBox<>(); - skinComboBox.setRenderer(new SubstanceDefaultListCellRenderer() { - - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - SubstanceDefaultListCellRenderer cmp = (SubstanceDefaultListCellRenderer) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); //To change body of generated methods, choose Tools | Templates. - final SkinSelect ss = (SkinSelect) value; - cmp.setIcon(new Icon() { - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - Graphics2D g2 = (Graphics2D) g; - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - try { - Class act = Class.forName(ss.getClassName()); - SubstanceSkin skin = (SubstanceSkin) act.newInstance(); - Color fill = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); - Color hilight = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ROLLOVER_SELECTED).getBackgroundFillColor(); - Color border = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.BORDER, ComponentState.ENABLED).getDarkColor(); - g2.setColor(fill); - g2.fillOval(0, 0, 16, 16); - g2.setColor(hilight); - g2.fillArc(0, 0, 16, 16, -45, 90); - g2.setColor(border); - g2.drawOval(0, 0, 16, 16); - - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) { - //no icon - } - - } - - @Override - public int getIconWidth() { - return 16; - } - - @Override - public int getIconHeight() { - return 16; - } - }); - return cmp; - } - - }); - skinComboBox.addItem(new SkinSelect(OceanicSkin.NAME, OceanicSkin.class.getName())); - Map skins = SubstanceLookAndFeel.getAllSkins(); - for (String skinKey : skins.keySet()) { - SkinInfo skin = skins.get(skinKey); - skinComboBox.addItem(new SkinSelect(skin.getDisplayName(), skin.getClassName())); - if (skin.getClassName().equals(Configuration.guiSkin.get())) { - skinComboBox.setSelectedIndex(skinComboBox.getItemCount() - 1); - } - } - - Map tabs = new HashMap<>(); - getCategories(componentsMap, tabs, skinComboBox, getResourceBundle()); - - String[] catOrder = new String[]{"ui", "display", "decompilation", "script", "format", "export", "import", "paths", "limit", "update", "debug", "other"}; - - for (String cat : catOrder) { - if (!tabs.containsKey(cat)) { - continue; - } - - tabPane.add(translate("config.group.name." + cat), tabs.get(cat)); - tabPane.setToolTipTextAt(tabPane.getTabCount() - 1, translate("config.group.description." + cat)); - } - if (selectedCategory != null && tabs.containsKey(selectedCategory)) { - tabPane.setSelectedComponent(tabs.get(selectedCategory)); - } - - cnt.add(tabPane, BorderLayout.CENTER); - pack(); - } - - public static String selectConfigFile(ConfigurationItem config, String current, String pattern) { - JFileChooser fc = new JFileChooser(); - fc.setSelectedFile(new File(current)); - fc.setMultiSelectionEnabled(false); - fc.setCurrentDirectory(new File((String) config.get())); - FileFilter allSupportedFilter = new FileFilter() { - private final String[] supportedExtensions = new String[]{".swf", ".gfx", ".swc", ".zip", ".iggy"}; - - @Override - public boolean accept(File f) { - if (f.isDirectory()) { - return true; - } - return f.getName().matches(pattern); - } - - @Override - public String getDescription() { - return ""; - } - }; - fc.setFileFilter(allSupportedFilter); - - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - int returnVal = fc.showOpenDialog(f); - if (returnVal == JFileChooser.APPROVE_OPTION) { - return Helper.fixDialogFile(fc.getSelectedFile()).getAbsolutePath(); - } else { - return (String) config.get(); - } - } - - public static void getCategories(Map componentsMap, Map tabs, JComboBox skinComboBox, ResourceBundle resourceBundle) { - Map> categorized = new HashMap<>(); - - Map fields = Configuration.getConfigurationFields(); - String[] keys = new String[fields.size()]; - keys = fields.keySet().toArray(keys); - Arrays.sort(keys); - - for (String name : keys) { - Field field = fields.get(name); - ConfigurationCategory cat = field.getAnnotation(ConfigurationCategory.class); - String scat = (cat == null || cat.value().isEmpty()) ? "other" : cat.value(); - if (!categorized.containsKey(scat)) { - categorized.put(scat, new HashMap<>()); - } - - categorized.get(scat).put(name, field); - } - - for (String cat : categorized.keySet()) { - JPanel configPanel = new JPanel(new SpringLayout()); - int itemCount = 0; - List names = new ArrayList<>(categorized.get(cat).keySet()); - - final Map locNames = new HashMap<>(); - for (String name : names) { - String locName; - - if (resourceBundle.containsKey("config.name." + name)) { - locName = resourceBundle.getString("config.name." + name); - } else { //if it is undocumented, then it must have ConfigurationInternal annotation - Field f = fields.get(name); - ConfigurationInternal cint = f.getAnnotation(ConfigurationInternal.class); - if (cint == null) { - throw new RuntimeException("Missing configuration name: " + name); - } - - locName = "(Internal) " + name; - } - - locNames.put(name, locName); - } - - Collections.sort(names, new Comparator() { - @Override - public int compare(String name1, String name2) { - return locNames.get(name1).compareTo(locNames.get(name2)); - } - }); - for (String name : names) { - Field field = categorized.get(cat).get(name); - - String locName = locNames.get(name); - - try { - ConfigurationItem item = (ConfigurationItem) field.get(null); - - ParameterizedType listType = (ParameterizedType) field.getGenericType(); - java.lang.reflect.Type itemType2 = listType.getActualTypeArguments()[0]; - if (!(itemType2 instanceof Class)) { - continue; - } - - Class itemType = (Class) itemType2; - - String description = ""; - if (resourceBundle.containsKey("config.description." + name)) { - description = resourceBundle.getString("config.description." + name); - } - - Object defaultValue = Configuration.getDefaultValue(field); - if (name.equals("gui.skin")) { - Class c; - try { - c = Class.forName((String) defaultValue); - defaultValue = c.getField("NAME").get(c); - } catch (ClassNotFoundException | NoSuchFieldException | SecurityException ex) { - Logger.getLogger(AdvancedSettingsDialog.class.getName()).log(Level.SEVERE, null, ex); - } - } - - if (defaultValue != null) { - description += " (" + resourceBundle.getString("default") + ": " + defaultValue + ")"; - } - - JLabel l = new JLabel(locName, JLabel.TRAILING); - l.setToolTipText(description); - configPanel.add(l); - Component c = null; - Component addComponent = null; - if (name.equals("gui.skin")) { - skinComboBox.setToolTipText(description); - skinComboBox.setMaximumSize(new Dimension(Integer.MAX_VALUE, skinComboBox.getPreferredSize().height)); - c = skinComboBox; - } else if ((itemType == String.class) || (itemType == Integer.class) || (itemType == Long.class) || (itemType == Double.class) || (itemType == Float.class) || (itemType == Calendar.class)) { - ConfigurationFile confFile = field.getAnnotation(ConfigurationFile.class); - ConfigurationDirectory confDirectory = field.getAnnotation(ConfigurationDirectory.class); - - JTextField tf = new JTextField(); - Object val = item.get(); - if (val == null) { - val = ""; - } - if (itemType == Calendar.class) { - tf.setText(new SimpleDateFormat().format(((Calendar) val).getTime())); - } else { - tf.setText(val.toString()); - } - tf.setToolTipText(description); - tf.setMaximumSize(new Dimension(Integer.MAX_VALUE, tf.getPreferredSize().height)); - - c = tf; - if (confFile != null) { //|| confDirectory != null) { - JPanel p = new JPanel(new BorderLayout()); - p.setMaximumSize(new Dimension(Integer.MAX_VALUE, tf.getPreferredSize().height)); - p.add(tf, BorderLayout.CENTER); - JButton butSelect = new JButton(View.getIcon("folderopen16")); - butSelect.setToolTipText(ResourceBundle.getBundle(AppStrings.getResourcePath(MainFrame.class)).getString("FileChooser.openButtonText")); - butSelect.setMargin(new Insets(2, 2, 2, 2)); - butSelect.addActionListener((ActionEvent e) -> { - tf.setText(selectConfigFile(item, tf.getText(), confFile.value())); - }); - p.add(butSelect, BorderLayout.EAST); - addComponent = p; - } - } else if (itemType == Boolean.class) { - JCheckBox cb = new JCheckBox(); - cb.setSelected((Boolean) item.get()); - cb.setToolTipText(description); - c = cb; - } else if (itemType.isEnum()) { - JComboBox cb = new JComboBox<>(); - @SuppressWarnings("unchecked") - EnumSet enumValues = EnumSet.allOf(itemType); - String stringValue = null; - for (Object enumValue : enumValues) { - String enumValueStr = enumValue.toString(); - if (stringValue == null) { - stringValue = enumValueStr; - } - cb.addItem(enumValueStr); - } - if (item.get() != null) { - stringValue = item.get().toString(); - } - cb.setToolTipText(description); - cb.setSelectedItem(stringValue); - cb.setMaximumSize(new Dimension(Integer.MAX_VALUE, cb.getPreferredSize().height)); - c = cb; - } else { - throw new UnsupportedOperationException("Configuration ttem type '" + itemType.getName() + "' is not supported"); - } - - componentsMap.put(name, c); - if (addComponent == null) { - addComponent = c; - } - l.setLabelFor(c); - configPanel.add(addComponent); - } catch (IllegalArgumentException | IllegalAccessException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } - - itemCount++; - } - - SpringUtilities.makeCompactGrid(configPanel, - itemCount, 2, //rows, cols - 6, 6, //initX, initY - 6, 6); //xPad, yPad - if (resourceBundle.containsKey("config.group.tip." + cat)) { - String tip = resourceBundle.getString("config.group.tip." + cat); - String urls[] = new String[0]; - if (resourceBundle.containsKey("config.group.link." + cat)) { - urls = resourceBundle.getString("config.group.link." + cat).split(" "); - } - for (int i = 0; i < urls.length; i++) { - tip = tip.replace("%link" + (i + 1) + "%", urls[i]); - } - JPanel p = new JPanel(new BorderLayout()); - p.add(configPanel, BorderLayout.CENTER); - JPanel tipPanel = new JPanel(new FlowLayout()); - tipPanel.add(new HtmlLabel("" + resourceBundle.getString("tip") + "" + tip)); - p.add(tipPanel, BorderLayout.SOUTH); - configPanel = p; - } - tabs.put(cat, new JScrollPane(configPanel)); - } - } - - private void showRestartConfirmDialod() { - if (View.showConfirmDialog(this, translate("advancedSettings.restartConfirmation"), AppStrings.translate("message.warning"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - View.execInEventDispatchLater(() -> { - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - Logger.getLogger(AdvancedSettingsDialog.class.getName()).log(Level.SEVERE, null, ex); - } - SelectLanguageDialog.reloadUi(); - }); - - } - } - - @SuppressWarnings("unchecked") - private void okButtonActionPerformed(ActionEvent evt) { - boolean modified = false; - Map fields = Configuration.getConfigurationFields(); - Map values = new HashMap<>(); - for (String name : fields.keySet()) { - Component c = componentsMap.get(name); - Object value = null; - - ParameterizedType listType = (ParameterizedType) fields.get(name).getGenericType(); - java.lang.reflect.Type itemType2 = listType.getActualTypeArguments()[0]; - if (!(itemType2 instanceof Class)) { - continue; - } - - Class itemType = (Class) itemType2; - if (name.equals("gui.skin")) { - value = ((SkinSelect) ((JComboBox) c).getSelectedItem()).className; - } else if (itemType == String.class) { - value = ((JTextField) c).getText(); - } - if (itemType == Boolean.class) { - value = ((JCheckBox) c).isSelected(); - } - - if (itemType == Calendar.class) { - Calendar cal = Calendar.getInstance(); - try { - cal.setTime(new SimpleDateFormat().parse(((JTextField) c).getText())); - } catch (ParseException ex) { - c.requestFocusInWindow(); - return; - } - value = cal; - } - - if (itemType.isEnum()) { - String stringValue = (String) ((JComboBox) c).getSelectedItem(); - value = Enum.valueOf(itemType, stringValue); - } - - try { - if (itemType == Integer.class) { - value = Integer.parseInt(((JTextField) c).getText()); - } - if (itemType == Long.class) { - value = Long.parseLong(((JTextField) c).getText()); - } - if (itemType == Double.class) { - value = Double.parseDouble(((JTextField) c).getText()); - } - if (itemType == Float.class) { - value = Float.parseFloat(((JTextField) c).getText()); - } - } catch (NumberFormatException nfe) { - if (!((JTextField) c).getText().isEmpty()) { - c.requestFocusInWindow(); - return; - } // else null - } - values.put(name, value); - } - - for (String name : fields.keySet()) { - Component c = componentsMap.get(name); - Object value = values.get(name); - - Field field = fields.get(name); - ConfigurationItem item = null; - try { - item = (ConfigurationItem) field.get(null); - } catch (IllegalArgumentException | IllegalAccessException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } - if (item.get() == null || !item.get().equals(value)) { - if (item.hasValue() || value != null) { - item.set(value); - modified = true; - } - } - } - Configuration.saveConfig(); - setVisible(false); - if (modified) { - showRestartConfirmDialod(); - } - } - - private void cancelButtonActionPerformed(ActionEvent evt) { - setVisible(false); - } - - private void resetButtonActionPerformed(ActionEvent evt) { - Map rfields = Configuration.getConfigurationFields(); - for (Entry entry : rfields.entrySet()) { - String name = entry.getKey(); - Field field = entry.getValue(); - try { - ConfigurationItem item = (ConfigurationItem) field.get(null); - item.unset(); - } catch (IllegalArgumentException | IllegalAccessException ex) { - // Reflection exceptions. This should never happen - throw new Error(ex.getMessage()); - } - } - Configuration.saveConfig(); - setVisible(false); - showRestartConfirmDialod(); - } -} +/* + * Copyright (C) 2010-2016 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationCategory; +import com.jpexs.decompiler.flash.configuration.ConfigurationDirectory; +import com.jpexs.decompiler.flash.configuration.ConfigurationFile; +import com.jpexs.decompiler.flash.configuration.ConfigurationInternal; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import com.jpexs.decompiler.flash.gui.helpers.SpringUtilities; +import com.jpexs.helpers.Helper; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.RenderingHints; +import java.awt.event.ActionEvent; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ResourceBundle; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SpringLayout; +import javax.swing.WindowConstants; +import javax.swing.filechooser.FileFilter; +import javax.swing.table.DefaultTableModel; +import org.pushingpixels.substance.api.ColorSchemeAssociationKind; +import org.pushingpixels.substance.api.ComponentState; +import org.pushingpixels.substance.api.DecorationAreaType; +import org.pushingpixels.substance.api.SubstanceLookAndFeel; +import org.pushingpixels.substance.api.SubstanceSkin; +import org.pushingpixels.substance.api.renderers.SubstanceDefaultListCellRenderer; +import org.pushingpixels.substance.api.skin.SkinInfo; + +/** + * + * @author JPEXS + */ +public class AdvancedSettingsDialog extends AppDialog { + + private final Map componentsMap = new HashMap<>(); + + private JButton cancelButton; + + private JButton okButton; + + private JButton resetButton; + + /** + * Creates new form AdvancedSettingsDialog + * + * @param selectedCategory + */ + public AdvancedSettingsDialog(String selectedCategory) { + initComponents(selectedCategory); + View.centerScreen(this); + View.setWindowIcon(this); + + //configurationTable.setCellEditor(configurationTable.getDefaultEditor(null)); + pack(); + } + + private DefaultTableModel getModel() { + return new DefaultTableModel( + new Object[][]{}, + new String[]{ + translate("advancedSettings.columns.name"), + translate("advancedSettings.columns.value"), + translate("advancedSettings.columns.description") + } + ) { + Class[] types = new Class[]{ + String.class, Object.class, String.class + }; + + boolean[] canEdit = new boolean[]{ + false, true, false + }; + + @Override + public Class getColumnClass(int columnIndex) { + return types[columnIndex]; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit[columnIndex]; + } + }; + } + + private static class SkinSelect { + + private final String name; + + private final String className; + + public SkinSelect(String name, String className) { + this.name = name; + this.className = className; + } + + public String getClassName() { + return className; + } + + @Override + public String toString() { + return name; + } + } + + private void initComponents(String selectedCategory) { + okButton = new JButton(); + cancelButton = new JButton(); + resetButton = new JButton(); + + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setTitle(translate("advancedSettings.dialog.title")); + setModal(true); + setPreferredSize(new Dimension(800, 500)); + + okButton.setText(AppStrings.translate("button.ok")); + okButton.addActionListener(this::okButtonActionPerformed); + + cancelButton.setText(AppStrings.translate("button.cancel")); + cancelButton.addActionListener(this::cancelButtonActionPerformed); + + resetButton.setText(AppStrings.translate("button.reset")); + resetButton.addActionListener(this::resetButtonActionPerformed); + + Container cnt = getContentPane(); + cnt.setLayout(new BorderLayout()); + //cnt.add(new JScrollPane(configurationTable),BorderLayout.CENTER); + + JPanel buttonsPanel = new JPanel(new BorderLayout()); + + JPanel buttonsLeftPanel = new JPanel(new FlowLayout()); + buttonsLeftPanel.add(resetButton, BorderLayout.WEST); + + buttonsPanel.add(buttonsLeftPanel, BorderLayout.WEST); + + JPanel buttonsRightPanel = new JPanel(new FlowLayout()); + buttonsRightPanel.add(cancelButton); + buttonsRightPanel.add(okButton); + buttonsPanel.add(buttonsRightPanel, BorderLayout.EAST); + + cnt.add(buttonsPanel, BorderLayout.SOUTH); + + JTabbedPane tabPane = new JTabbedPane(); + + JComboBox skinComboBox = new JComboBox<>(); + skinComboBox.setRenderer(new SubstanceDefaultListCellRenderer() { + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + SubstanceDefaultListCellRenderer cmp = (SubstanceDefaultListCellRenderer) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); //To change body of generated methods, choose Tools | Templates. + final SkinSelect ss = (SkinSelect) value; + cmp.setIcon(new Icon() { + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + try { + Class act = Class.forName(ss.getClassName()); + SubstanceSkin skin = (SubstanceSkin) act.newInstance(); + Color fill = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ENABLED).getBackgroundFillColor(); + Color hilight = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.FILL, ComponentState.ROLLOVER_SELECTED).getBackgroundFillColor(); + Color border = skin.getColorScheme(DecorationAreaType.GENERAL, ColorSchemeAssociationKind.BORDER, ComponentState.ENABLED).getDarkColor(); + g2.setColor(fill); + g2.fillOval(0, 0, 16, 16); + g2.setColor(hilight); + g2.fillArc(0, 0, 16, 16, -45, 90); + g2.setColor(border); + g2.drawOval(0, 0, 16, 16); + + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) { + //no icon + } + + } + + @Override + public int getIconWidth() { + return 16; + } + + @Override + public int getIconHeight() { + return 16; + } + }); + return cmp; + } + + }); + skinComboBox.addItem(new SkinSelect(OceanicSkin.NAME, OceanicSkin.class.getName())); + Map skins = SubstanceLookAndFeel.getAllSkins(); + for (String skinKey : skins.keySet()) { + SkinInfo skin = skins.get(skinKey); + skinComboBox.addItem(new SkinSelect(skin.getDisplayName(), skin.getClassName())); + if (skin.getClassName().equals(Configuration.guiSkin.get())) { + skinComboBox.setSelectedIndex(skinComboBox.getItemCount() - 1); + } + } + + Map tabs = new HashMap<>(); + getCategories(componentsMap, tabs, skinComboBox, getResourceBundle()); + + String[] catOrder = new String[]{"ui", "display", "decompilation", "script", "format", "export", "import", "paths", "limit", "update", "debug", "other"}; + + for (String cat : catOrder) { + if (!tabs.containsKey(cat)) { + continue; + } + + tabPane.add(translate("config.group.name." + cat), tabs.get(cat)); + tabPane.setToolTipTextAt(tabPane.getTabCount() - 1, translate("config.group.description." + cat)); + } + if (selectedCategory != null && tabs.containsKey(selectedCategory)) { + tabPane.setSelectedComponent(tabs.get(selectedCategory)); + } + + cnt.add(tabPane, BorderLayout.CENTER); + pack(); + } + + public static String selectConfigFile(ConfigurationItem config, String current, String pattern) { + JFileChooser fc = new JFileChooser(); + fc.setSelectedFile(new File(current)); + fc.setMultiSelectionEnabled(false); + fc.setCurrentDirectory(new File((String) config.get())); + FileFilter allSupportedFilter = new FileFilter() { + private final String[] supportedExtensions = new String[]{".swf", ".gfx", ".swc", ".zip", ".iggy"}; + + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + return f.getName().matches(pattern); + } + + @Override + public String getDescription() { + return ""; + } + }; + fc.setFileFilter(allSupportedFilter); + + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + int returnVal = fc.showOpenDialog(f); + if (returnVal == JFileChooser.APPROVE_OPTION) { + return Helper.fixDialogFile(fc.getSelectedFile()).getAbsolutePath(); + } else { + return (String) config.get(); + } + } + + public static void getCategories(Map componentsMap, Map tabs, JComboBox skinComboBox, ResourceBundle resourceBundle) { + Map> categorized = new HashMap<>(); + + Map fields = Configuration.getConfigurationFields(); + String[] keys = new String[fields.size()]; + keys = fields.keySet().toArray(keys); + Arrays.sort(keys); + + for (String name : keys) { + Field field = fields.get(name); + ConfigurationCategory cat = field.getAnnotation(ConfigurationCategory.class); + String scat = (cat == null || cat.value().isEmpty()) ? "other" : cat.value(); + if (!categorized.containsKey(scat)) { + categorized.put(scat, new HashMap<>()); + } + + categorized.get(scat).put(name, field); + } + + for (String cat : categorized.keySet()) { + JPanel configPanel = new JPanel(new SpringLayout()); + int itemCount = 0; + List names = new ArrayList<>(categorized.get(cat).keySet()); + + final Map locNames = new HashMap<>(); + for (String name : names) { + String locName; + + if (resourceBundle.containsKey("config.name." + name)) { + locName = resourceBundle.getString("config.name." + name); + } else { //if it is undocumented, then it must have ConfigurationInternal annotation + Field f = fields.get(name); + ConfigurationInternal cint = f.getAnnotation(ConfigurationInternal.class); + if (cint == null) { + throw new RuntimeException("Missing configuration name: " + name); + } + + locName = "(Internal) " + name; + } + + locNames.put(name, locName); + } + + Collections.sort(names, new Comparator() { + @Override + public int compare(String name1, String name2) { + return locNames.get(name1).compareTo(locNames.get(name2)); + } + }); + for (String name : names) { + Field field = categorized.get(cat).get(name); + + String locName = locNames.get(name); + + try { + ConfigurationItem item = (ConfigurationItem) field.get(null); + + ParameterizedType listType = (ParameterizedType) field.getGenericType(); + java.lang.reflect.Type itemType2 = listType.getActualTypeArguments()[0]; + if (!(itemType2 instanceof Class)) { + continue; + } + + Class itemType = (Class) itemType2; + + String description = ""; + if (resourceBundle.containsKey("config.description." + name)) { + description = resourceBundle.getString("config.description." + name); + } + + Object defaultValue = Configuration.getDefaultValue(field); + if (name.equals("gui.skin")) { + Class c; + try { + c = Class.forName((String) defaultValue); + defaultValue = c.getField("NAME").get(c); + } catch (ClassNotFoundException | NoSuchFieldException | SecurityException ex) { + Logger.getLogger(AdvancedSettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + } + + if (defaultValue != null) { + description += " (" + resourceBundle.getString("default") + ": " + defaultValue + ")"; + } + + JLabel l = new JLabel(locName, JLabel.TRAILING); + l.setToolTipText(description); + configPanel.add(l); + Component c = null; + Component addComponent = null; + if (name.equals("gui.skin")) { + skinComboBox.setToolTipText(description); + skinComboBox.setMaximumSize(new Dimension(Integer.MAX_VALUE, skinComboBox.getPreferredSize().height)); + c = skinComboBox; + } else if ((itemType == String.class) || (itemType == Integer.class) || (itemType == Long.class) || (itemType == Double.class) || (itemType == Float.class) || (itemType == Calendar.class)) { + ConfigurationFile confFile = field.getAnnotation(ConfigurationFile.class); + ConfigurationDirectory confDirectory = field.getAnnotation(ConfigurationDirectory.class); + + JTextField tf = new JTextField(); + Object val = item.get(); + if (val == null) { + val = ""; + } + if (itemType == Calendar.class) { + tf.setText(new SimpleDateFormat().format(((Calendar) val).getTime())); + } else { + tf.setText(val.toString()); + } + tf.setToolTipText(description); + tf.setMaximumSize(new Dimension(Integer.MAX_VALUE, tf.getPreferredSize().height)); + + c = tf; + if (confFile != null) { //|| confDirectory != null) { + JPanel p = new JPanel(new BorderLayout()); + p.setMaximumSize(new Dimension(Integer.MAX_VALUE, tf.getPreferredSize().height)); + p.add(tf, BorderLayout.CENTER); + JButton butSelect = new JButton(View.getIcon("folderopen16")); + butSelect.setToolTipText(ResourceBundle.getBundle(AppStrings.getResourcePath(MainFrame.class)).getString("FileChooser.openButtonText")); + butSelect.setMargin(new Insets(2, 2, 2, 2)); + butSelect.addActionListener((ActionEvent e) -> { + tf.setText(selectConfigFile(item, tf.getText(), confFile.value())); + }); + p.add(butSelect, BorderLayout.EAST); + addComponent = p; + } + } else if (itemType == Boolean.class) { + JCheckBox cb = new JCheckBox(); + cb.setSelected((Boolean) item.get()); + cb.setToolTipText(description); + c = cb; + } else if (itemType.isEnum()) { + JComboBox cb = new JComboBox<>(); + @SuppressWarnings("unchecked") + EnumSet enumValues = EnumSet.allOf(itemType); + String stringValue = null; + for (Object enumValue : enumValues) { + String enumValueStr = enumValue.toString(); + if (stringValue == null) { + stringValue = enumValueStr; + } + cb.addItem(enumValueStr); + } + if (item.get() != null) { + stringValue = item.get().toString(); + } + cb.setToolTipText(description); + cb.setSelectedItem(stringValue); + cb.setMaximumSize(new Dimension(Integer.MAX_VALUE, cb.getPreferredSize().height)); + c = cb; + } else { + throw new UnsupportedOperationException("Configuration ttem type '" + itemType.getName() + "' is not supported"); + } + + componentsMap.put(name, c); + if (addComponent == null) { + addComponent = c; + } + l.setLabelFor(c); + configPanel.add(addComponent); + } catch (IllegalArgumentException | IllegalAccessException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } + + itemCount++; + } + + SpringUtilities.makeCompactGrid(configPanel, + itemCount, 2, //rows, cols + 6, 6, //initX, initY + 6, 6); //xPad, yPad + if (resourceBundle.containsKey("config.group.tip." + cat)) { + String tip = resourceBundle.getString("config.group.tip." + cat); + String urls[] = new String[0]; + if (resourceBundle.containsKey("config.group.link." + cat)) { + urls = resourceBundle.getString("config.group.link." + cat).split(" "); + } + for (int i = 0; i < urls.length; i++) { + tip = tip.replace("%link" + (i + 1) + "%", urls[i]); + } + JPanel p = new JPanel(new BorderLayout()); + p.add(configPanel, BorderLayout.CENTER); + JPanel tipPanel = new JPanel(new FlowLayout()); + tipPanel.add(new HtmlLabel("" + resourceBundle.getString("tip") + "" + tip)); + p.add(tipPanel, BorderLayout.SOUTH); + configPanel = p; + } + tabs.put(cat, new JScrollPane(configPanel)); + } + } + + private void showRestartConfirmDialod() { + if (View.showConfirmDialog(this, translate("advancedSettings.restartConfirmation"), AppStrings.translate("message.warning"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + View.execInEventDispatchLater(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + Logger.getLogger(AdvancedSettingsDialog.class.getName()).log(Level.SEVERE, null, ex); + } + SelectLanguageDialog.reloadUi(); + }); + + } + } + + @SuppressWarnings("unchecked") + private void okButtonActionPerformed(ActionEvent evt) { + boolean modified = false; + Map fields = Configuration.getConfigurationFields(); + Map values = new HashMap<>(); + for (String name : fields.keySet()) { + Component c = componentsMap.get(name); + Object value = null; + + ParameterizedType listType = (ParameterizedType) fields.get(name).getGenericType(); + java.lang.reflect.Type itemType2 = listType.getActualTypeArguments()[0]; + if (!(itemType2 instanceof Class)) { + continue; + } + + Class itemType = (Class) itemType2; + if (name.equals("gui.skin")) { + value = ((SkinSelect) ((JComboBox) c).getSelectedItem()).className; + } else if (itemType == String.class) { + value = ((JTextField) c).getText(); + } + if (itemType == Boolean.class) { + value = ((JCheckBox) c).isSelected(); + } + + if (itemType == Calendar.class) { + Calendar cal = Calendar.getInstance(); + try { + cal.setTime(new SimpleDateFormat().parse(((JTextField) c).getText())); + } catch (ParseException ex) { + c.requestFocusInWindow(); + return; + } + value = cal; + } + + if (itemType.isEnum()) { + String stringValue = (String) ((JComboBox) c).getSelectedItem(); + value = Enum.valueOf(itemType, stringValue); + } + + try { + if (itemType == Integer.class) { + value = Integer.parseInt(((JTextField) c).getText()); + } + if (itemType == Long.class) { + value = Long.parseLong(((JTextField) c).getText()); + } + if (itemType == Double.class) { + value = Double.parseDouble(((JTextField) c).getText()); + } + if (itemType == Float.class) { + value = Float.parseFloat(((JTextField) c).getText()); + } + } catch (NumberFormatException nfe) { + if (!((JTextField) c).getText().isEmpty()) { + c.requestFocusInWindow(); + return; + } // else null + } + values.put(name, value); + } + + for (String name : fields.keySet()) { + Component c = componentsMap.get(name); + Object value = values.get(name); + + Field field = fields.get(name); + ConfigurationItem item = null; + try { + item = (ConfigurationItem) field.get(null); + } catch (IllegalArgumentException | IllegalAccessException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } + if (item.get() == null || !item.get().equals(value)) { + if (item.hasValue() || value != null) { + item.set(value); + modified = true; + } + } + } + Configuration.saveConfig(); + setVisible(false); + if (modified) { + showRestartConfirmDialod(); + } + } + + private void cancelButtonActionPerformed(ActionEvent evt) { + setVisible(false); + } + + private void resetButtonActionPerformed(ActionEvent evt) { + Map rfields = Configuration.getConfigurationFields(); + for (Entry entry : rfields.entrySet()) { + String name = entry.getKey(); + Field field = entry.getValue(); + try { + ConfigurationItem item = (ConfigurationItem) field.get(null); + item.unset(); + } catch (IllegalArgumentException | IllegalAccessException ex) { + // Reflection exceptions. This should never happen + throw new Error(ex.getMessage()); + } + } + Configuration.saveConfig(); + setVisible(false); + showRestartConfirmDialod(); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/MainPanel.java b/src/com/jpexs/decompiler/flash/gui/MainPanel.java index e5a097b72..54b8fc920 100644 --- a/src/com/jpexs/decompiler/flash/gui/MainPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/MainPanel.java @@ -1,3681 +1,3681 @@ -/* - * Copyright (C) 2010-2016 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui; - -import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; -import com.jpexs.decompiler.flash.ApplicationInfo; -import com.jpexs.decompiler.flash.EventListener; -import com.jpexs.decompiler.flash.ReadOnlyTagList; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.SWFBundle; -import com.jpexs.decompiler.flash.SWFSourceInfo; -import com.jpexs.decompiler.flash.abc.ABC; -import com.jpexs.decompiler.flash.abc.RenameType; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; -import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AbcMultiNameCollisionFixer; -import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; -import com.jpexs.decompiler.flash.abc.types.traits.Trait; -import com.jpexs.decompiler.flash.configuration.Configuration; -import com.jpexs.decompiler.flash.configuration.ConfigurationItem; -import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; -import com.jpexs.decompiler.flash.dumpview.DumpInfo; -import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; -import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; -import com.jpexs.decompiler.flash.exporters.FontExporter; -import com.jpexs.decompiler.flash.exporters.FrameExporter; -import com.jpexs.decompiler.flash.exporters.ImageExporter; -import com.jpexs.decompiler.flash.exporters.MorphShapeExporter; -import com.jpexs.decompiler.flash.exporters.MovieExporter; -import com.jpexs.decompiler.flash.exporters.ShapeExporter; -import com.jpexs.decompiler.flash.exporters.SoundExporter; -import com.jpexs.decompiler.flash.exporters.SymbolClassExporter; -import com.jpexs.decompiler.flash.exporters.TextExporter; -import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; -import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; -import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode; -import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode; -import com.jpexs.decompiler.flash.exporters.modes.TextExportMode; -import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; -import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; -import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.FrameExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.MovieExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings; -import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; -import com.jpexs.decompiler.flash.exporters.swf.SwfJavaExporter; -import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; -import com.jpexs.decompiler.flash.flexsdk.MxmlcAs3ScriptReplacer; -import com.jpexs.decompiler.flash.gui.abc.ABCPanel; -import com.jpexs.decompiler.flash.gui.abc.ABCPanelSearchResult; -import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; -import com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane; -import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog; -import com.jpexs.decompiler.flash.gui.action.ActionPanel; -import com.jpexs.decompiler.flash.gui.action.ActionSearchResult; -import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; -import com.jpexs.decompiler.flash.gui.dumpview.DumpTree; -import com.jpexs.decompiler.flash.gui.dumpview.DumpTreeModel; -import com.jpexs.decompiler.flash.gui.dumpview.DumpViewPanel; -import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; -import com.jpexs.decompiler.flash.gui.helpers.ObservableList; -import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; -import com.jpexs.decompiler.flash.gui.tagtree.TagTree; -import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; -import com.jpexs.decompiler.flash.gui.timeline.TimelineViewPanel; -import com.jpexs.decompiler.flash.helpers.FileTextWriter; -import com.jpexs.decompiler.flash.helpers.Freed; -import com.jpexs.decompiler.flash.importers.AS2ScriptImporter; -import com.jpexs.decompiler.flash.importers.AS3ScriptImporter; -import com.jpexs.decompiler.flash.importers.As3ScriptReplacerFactory; -import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface; -import com.jpexs.decompiler.flash.importers.BinaryDataImporter; -import com.jpexs.decompiler.flash.importers.FFDecAs3ScriptReplacer; -import com.jpexs.decompiler.flash.importers.ImageImporter; -import com.jpexs.decompiler.flash.importers.ShapeImporter; -import com.jpexs.decompiler.flash.importers.SwfXmlImporter; -import com.jpexs.decompiler.flash.importers.SymbolClassImporter; -import com.jpexs.decompiler.flash.importers.TextImporter; -import com.jpexs.decompiler.flash.importers.svg.SvgImporter; -import com.jpexs.decompiler.flash.tags.ABCContainerTag; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.TagInfo; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.BoundedTag; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.CharacterTag; -import com.jpexs.decompiler.flash.tags.base.DrawableTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; -import com.jpexs.decompiler.flash.tags.base.SoundTag; -import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.text.TextAlign; -import com.jpexs.decompiler.flash.tags.text.TextParseException; -import com.jpexs.decompiler.flash.timeline.DepthState; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.timeline.Timeline; -import com.jpexs.decompiler.flash.timeline.Timelined; -import com.jpexs.decompiler.flash.treeitems.FolderItem; -import com.jpexs.decompiler.flash.treeitems.HeaderItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import com.jpexs.decompiler.flash.types.MATRIX; -import com.jpexs.decompiler.flash.types.RECT; -import com.jpexs.decompiler.flash.types.sound.SoundFormat; -import com.jpexs.decompiler.flash.xfl.FLAVersion; -import com.jpexs.helpers.CancellableWorker; -import com.jpexs.helpers.Helper; -import com.jpexs.helpers.Path; -import com.jpexs.helpers.ProgressListener; -import com.jpexs.helpers.SerializableImage; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Desktop; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DragGestureEvent; -import java.awt.dnd.DragGestureListener; -import java.awt.dnd.DragSource; -import java.awt.dnd.DragSourceDragEvent; -import java.awt.dnd.DragSourceDropEvent; -import java.awt.dnd.DragSourceEvent; -import java.awt.dnd.DragSourceListener; -import java.awt.dnd.DropTarget; -import java.awt.dnd.DropTargetDropEvent; -import java.awt.event.ActionEvent; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.UnsupportedAudioFileException; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.Icon; -import javax.swing.JColorChooser; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTabbedPane; -import javax.swing.JTextField; -import javax.swing.SwingConstants; -import javax.swing.UIManager; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.filechooser.FileFilter; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.tree.DefaultTreeSelectionModel; -import javax.swing.tree.TreePath; -import jsyntaxpane.DefaultSyntaxKit; - -/** - * - * @author JPEXS - */ -public final class MainPanel extends JPanel implements TreeSelectionListener, SearchListener, Freed { - - private final MainFrame mainFrame; - - private final ObservableList swfs; - - private final JPanel welcomePanel; - - private final TimelineViewPanel timelineViewPanel; - - private final MainFrameStatusPanel statusPanel; - - private final MainFrameMenu mainMenu; - - private final JProgressBar progressBar = new JProgressBar(0, 100); - - public TagTree tagTree; - - public DumpTree dumpTree; - - private final FlashPlayerPanel flashPanel; - - private final FlashPlayerPanel flashPanel2; - - private final JPanel contentPanel; - - private final JPanel displayPanel; - - public FolderPreviewPanel folderPreviewPanel; - - private boolean isWelcomeScreen = true; - - private static final String CARDPREVIEWPANEL = "Preview card"; - - private static final String CARDFOLDERPREVIEWPANEL = "Folder preview card"; - - private static final String CARDEMPTYPANEL = "Empty card"; - - private static final String CARDDUMPVIEW = "Dump view"; - - private static final String CARDACTIONSCRIPTPANEL = "ActionScript card"; - - private static final String CARDACTIONSCRIPT3PANEL = "ActionScript3 card"; - - private static final String CARDHEADER = "Header card"; - - private static final String DETAILCARDAS3NAVIGATOR = "Traits list"; - - private static final String DETAILCARDTAGINFO = "Tag information"; - - private static final String DETAILCARDEMPTYPANEL = "Empty card"; - - private static final String SPLIT_PANE1 = "SPLITPANE1"; - - private static final String WELCOME_PANEL = "WELCOMEPANEL"; - - private static final String TIMELINE_PANEL = "TIMELINEPANEL"; - - private static final String RESOURCES_VIEW = "RESOURCES"; - - private static final String DUMP_VIEW = "DUMP"; - - private static final String TIMELINE_VIEW = "TIMELINE"; - - private final JPersistentSplitPane splitPane1; - - private final JPersistentSplitPane splitPane2; - - private JPanel detailPanel; - - private JTextField filterField = new MyTextField(""); - - private JPanel searchPanel; - - private ABCPanel abcPanel; - - private ActionPanel actionPanel; - - private final PreviewPanel previewPanel; - - private final HeaderInfoPanel headerPanel; - - private DumpViewPanel dumpViewPanel; - - private final JPanel treePanel; - - private final PreviewPanel dumpPreviewPanel; - - private final TagInfoPanel tagInfoPanel; - - private TreePanelMode treePanelMode; - - private CancellableWorker setSourceWorker; - - public TreeItem oldItem; - - // play morph shape in 2 second(s) - public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; - - public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; - - private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); - - public void setPercent(int percent) { - progressBar.setValue(percent); - progressBar.setVisible(true); - } - - public void hidePercent() { - if (progressBar.isVisible()) { - progressBar.setVisible(false); - } - } - - public MainFrame getMainFrame() { - return mainFrame; - } - - static { - try { - File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - public void updateMenu() { - mainMenu.updateComponents(); - } - - private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) { - tabbedPane.add(tab); - - JLabel lbl = new JLabel(title); - lbl.setIcon(icon); - lbl.setIconTextGap(5); - lbl.setHorizontalTextPosition(SwingConstants.RIGHT); - - tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl); - } - - public void setStatus(String s) { - statusPanel.setStatus(s); - } - - public void setWorkStatus(String s, CancellableWorker worker) { - statusPanel.setWorkStatus(s, worker); - mainMenu.updateComponents(); - } - - private JPanel createWelcomePanel() { - JPanel welcomePanel = new JPanel(); - welcomePanel.setLayout(new BoxLayout(welcomePanel, BoxLayout.Y_AXIS)); - JLabel welcomeToLabel = new JLabel(translate("startup.welcometo")); - welcomeToLabel.setFont(welcomeToLabel.getFont().deriveFont(40)); - welcomeToLabel.setAlignmentX(0.5f); - JPanel appNamePanel = new JPanel(new FlowLayout()); - JLabel jpLabel = new JLabel("JPEXS "); - jpLabel.setAlignmentX(0.5f); - jpLabel.setForeground(new Color(0, 0, 160)); - jpLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - jpLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(jpLabel); - - JLabel ffLabel = new JLabel("Free Flash "); - ffLabel.setAlignmentX(0.5f); - ffLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - ffLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(ffLabel); - - JLabel decLabel = new JLabel("Decompiler"); - decLabel.setAlignmentX(0.5f); - decLabel.setForeground(Color.red); - decLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); - decLabel.setHorizontalAlignment(SwingConstants.CENTER); - appNamePanel.add(decLabel); - appNamePanel.setAlignmentX(0.5f); - welcomePanel.add(Box.createGlue()); - welcomePanel.add(welcomeToLabel); - welcomePanel.add(appNamePanel); - JLabel startLabel = new JLabel(translate("startup.selectopen")); - startLabel.setAlignmentX(0.5f); - startLabel.setFont(startLabel.getFont().deriveFont(30)); - welcomePanel.add(startLabel); - welcomePanel.add(Box.createGlue()); - return welcomePanel; - } - - private JPanel createFolderPreviewCard() { - JPanel folderPreviewCard = new JPanel(new BorderLayout()); - folderPreviewPanel = new FolderPreviewPanel(this, new ArrayList<>()); - folderPreviewCard.add(new JScrollPane(folderPreviewPanel), BorderLayout.CENTER); - - return folderPreviewCard; - } - - private JPanel createDumpPreviewCard() { - JPanel dumpViewCard = new JPanel(new BorderLayout()); - dumpViewPanel = new DumpViewPanel(dumpTree); - dumpViewCard.add(new JScrollPane(dumpViewPanel), BorderLayout.CENTER); - - return dumpViewCard; - } - - public String translate(String key) { - return mainFrame.translate(key); - } - - public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel, FlashPlayerPanel previewFlashPanel) { - super(); - - this.mainFrame = mainFrame; - this.mainMenu = mainMenu; - this.flashPanel = flashPanel; - this.flashPanel2 = previewFlashPanel; - - mainFrame.setTitle(ApplicationInfo.applicationVerName); - - setLayout(new BorderLayout()); - swfs = new ObservableList<>(); - - detailPanel = new JPanel(); - detailPanel.setLayout(new CardLayout()); - - JPanel whitePanel = new JPanel(); - whitePanel.setBackground(Color.white); - detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); - - tagInfoPanel = new TagInfoPanel(this); - detailPanel.add(tagInfoPanel, DETAILCARDTAGINFO); - - UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); - tagTree = new TagTree(null, this); - tagTree.addTreeSelectionListener(this); - tagTree.setSelectionModel(new DefaultTreeSelectionModel() { - private boolean isModified() { - if (abcPanel != null && abcPanel.isEditing()) { - abcPanel.tryAutoSave(); - } - - if (actionPanel != null && actionPanel.isEditing()) { - actionPanel.tryAutoSave(); - } - - if (previewPanel.isEditing()) { - previewPanel.tryAutoSave(); - } - - if (headerPanel.isEditing()) { - headerPanel.tryAutoSave(); - } - - return (abcPanel != null && abcPanel.isEditing()) - || (actionPanel != null && actionPanel.isEditing()) - || previewPanel.isEditing() || headerPanel.isEditing(); - } - - @Override - public void addSelectionPath(TreePath path) { - if (isModified()) { - return; - } - - super.addSelectionPath(path); - } - - @Override - public void addSelectionPaths(TreePath[] paths) { - if (isModified()) { - return; - } - - super.addSelectionPaths(paths); - } - - @Override - public void setSelectionPath(TreePath path) { - if (isModified()) { - return; - } - - super.setSelectionPath(path); - } - - @Override - public void setSelectionPaths(TreePath[] pPaths) { - if (isModified()) { - return; - } - - super.setSelectionPaths(pPaths); - } - - @Override - public void clearSelection() { - if (isModified()) { - return; - } - - super.clearSelection(); - } - - public void setSelection(TreePath[] selection) { - if (isModified()) { - return; - } - - this.selection = selection; - } - - @Override - public void removeSelectionPath(TreePath path) { - if (isModified()) { - return; - } - - super.removeSelectionPath(path); - } - - @Override - public void removeSelectionPaths(TreePath[] paths) { - if (isModified()) { - return; - } - - super.removeSelectionPaths(paths); - } - }); - - DragSource dragSource = DragSource.getDefaultDragSource(); - dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { - @Override - public void dragGestureRecognized(DragGestureEvent dge) { - dge.startDrag(DragSource.DefaultCopyDrop, new Transferable() { - @Override - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[]{DataFlavor.javaFileListFlavor}; - } - - @Override - public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(DataFlavor.javaFileListFlavor); - } - - @Override - public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { - if (flavor.equals(DataFlavor.javaFileListFlavor)) { - List files; - String tempDir = System.getProperty("java.io.tmpdir"); - if (!tempDir.endsWith(File.separator)) { - tempDir += File.separator; - } - Random rnd = new Random(); - tempDir += "ffdec" + File.separator + "export" + File.separator + System.currentTimeMillis() + "_" + rnd.nextInt(1000); - File fTempDir = new File(tempDir); - Path.createDirectorySafe(fTempDir); - - File ftemp = new File(tempDir); - ExportDialog exd = new ExportDialog(null); - try { - files = exportSelection(new GuiAbortRetryIgnoreHandler(), tempDir, exd); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - return null; - } - - files.clear(); - - File[] fs = ftemp.listFiles(); - files.addAll(Arrays.asList(fs)); - - Main.stopWork(); - - for (File f : files) { - f.deleteOnExit(); - } - new File(tempDir).deleteOnExit(); - return files; - - } - return null; - } - }, new DragSourceListener() { - @Override - public void dragEnter(DragSourceDragEvent dsde) { - enableDrop(false); - } - - @Override - public void dragOver(DragSourceDragEvent dsde) { - } - - @Override - public void dropActionChanged(DragSourceDragEvent dsde) { - } - - @Override - public void dragExit(DragSourceEvent dse) { - } - - @Override - public void dragDropEnd(DragSourceDropEvent dsde) { - enableDrop(true); - } - }); - } - }); - - tagTree.createContextMenu(); - - dumpTree = new DumpTree(null, this); - dumpTree.addTreeSelectionListener(this); - dumpTree.createContextMenu(); - - statusPanel = new MainFrameStatusPanel(this); - add(statusPanel, BorderLayout.SOUTH); - - displayPanel = new JPanel(new CardLayout()); - - DefaultSyntaxKit.initKit(); - previewPanel = new PreviewPanel(this, flashPanel); - - dumpPreviewPanel = new PreviewPanel(this, previewFlashPanel); - dumpPreviewPanel.setReadOnly(true); - - displayPanel.add(previewPanel, CARDPREVIEWPANEL); - displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL); - displayPanel.add(createDumpPreviewCard(), CARDDUMPVIEW); - - headerPanel = new HeaderInfoPanel(); - displayPanel.add(headerPanel, CARDHEADER); - - displayPanel.add(new JPanel(), CARDEMPTYPANEL); - showCard(CARDEMPTYPANEL); - - searchPanel = new JPanel(); - searchPanel.setLayout(new BorderLayout()); - searchPanel.add(filterField, BorderLayout.CENTER); - searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST); - JLabel closeSearchButton = new JLabel(View.getIcon("cancel16")); - closeSearchButton.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - closeTagTreeSearch(); - } - }); - searchPanel.add(closeSearchButton, BorderLayout.EAST); - searchPanel.setVisible(false); - treePanel = new JPanel(new CardLayout()); - treePanel.add(createResourcesViewCard(), RESOURCES_VIEW); - treePanel.add(createDumpViewCard(), DUMP_VIEW); - //treePanel.add(searchPanel, BorderLayout.SOUTH); - //searchPanel.setVisible(false); - filterField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void changedUpdate(DocumentEvent e) { - warn(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - warn(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - warn(); - } - - public void warn() { - doFilter(); - } - }); - - //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black)); - splitPane2 = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, treePanel, detailPanel, Configuration.guiSplitPane2DividerLocationPercent); - splitPane1 = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel, Configuration.guiSplitPane1DividerLocationPercent); - - welcomePanel = createWelcomePanel(); - add(welcomePanel, BorderLayout.CENTER); - - timelineViewPanel = new TimelineViewPanel(); - - CardLayout cl3 = new CardLayout(); - contentPanel = new JPanel(cl3); - contentPanel.add(welcomePanel, WELCOME_PANEL); - contentPanel.add(splitPane1, SPLIT_PANE1); - contentPanel.add(timelineViewPanel, TIMELINE_PANEL); - add(contentPanel); - cl3.show(contentPanel, WELCOME_PANEL); - - tagTree.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if ((e.getKeyCode() == 'F') && (e.isControlDown())) { - searchPanel.setVisible(true); - filterField.requestFocusInWindow(); - } - } - }); - detailPanel.setVisible(false); - - updateUi(); - - this.swfs.addCollectionChangedListener((e) -> { - TagTreeModel ttm = tagTree.getModel(); - if (ttm != null) { - if (getCurrentSwf() == null) { - tagTree.setSelectionPath(ttm.getTreePath(ttm.getRoot())); - } - ttm.updateSwfs(e); - tagTree.expandRoot(); - tagTree.expandFirstLevelNodes(); - } - - DumpTreeModel dtm = dumpTree.getModel(); - if (dtm != null) { - List> expandedNodes = View.getExpandedNodes(dumpTree); - dtm.updateSwfs(); - View.expandTreeNodes(dumpTree, expandedNodes); - dumpTree.expandRoot(); - dumpTree.expandFirstLevelNodes(); - } - - if (swfs.isEmpty()) { - tagTree.setUI(new BasicTreeUI() { - { - setHashColor(Color.gray); - } - }); - dumpTree.setUI(new BasicTreeUI() { - { - setHashColor(Color.gray); - } - }); - } - }); - - //Opening files with drag&drop to main window - enableDrop(true); - } - - public void closeTagTreeSearch() { - filterField.setText(""); - doFilter(); - searchPanel.setVisible(false); - } - - public void loadSwfAtPos(SWFList newSwfs, int index) { - previewPanel.clear(); - swfs.set(index, newSwfs); - SWF swf = newSwfs.size() > 0 ? newSwfs.get(0) : null; - if (swf != null) { - updateUi(swf); - } - - doFilter(); - reload(false); - } - - public void load(SWFList newSwfs, boolean first) { - - previewPanel.clear(); - - swfs.add(newSwfs); - SWF swf = newSwfs.size() > 0 ? newSwfs.get(0) : null; - if (swf != null) { - updateUi(swf); - } - - doFilter(); - reload(false); - } - - private ABCPanel getABCPanel() { - if (abcPanel == null) { - abcPanel = new ABCPanel(this); - displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); - detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR); - } - - return abcPanel; - } - - private ActionPanel getActionPanel() { - if (actionPanel == null) { - actionPanel = new ActionPanel(MainPanel.this); - displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL); - } - - return actionPanel; - } - - private void updateUi(final SWF swf) { - - mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); - - List abcList = swf.getAbcList(); - - boolean hasAbc = !abcList.isEmpty(); - - if (hasAbc) { - getABCPanel().setAbc(abcList.get(0).getABC()); - } - - if (isWelcomeScreen) { - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - cl.show(contentPanel, SPLIT_PANE1); - isWelcomeScreen = false; - } - - mainMenu.updateComponents(swf); - } - - private void updateUi() { - if (!isWelcomeScreen && swfs.isEmpty()) { - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - cl.show(contentPanel, WELCOME_PANEL); - isWelcomeScreen = true; - closeTagTreeSearch(); - } - - mainFrame.setTitle(ApplicationInfo.applicationVerName); - mainMenu.updateComponents(null); - - showView(getCurrentView()); - } - - private boolean closeConfirmation(SWFList swfList) { - String message = swfList == null - ? translate("message.confirm.closeAll") - : translate("message.confirm.close").replace("{swfName}", swfList.toString()); - - return View.showConfirmDialog(this, message, translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, Configuration.showCloseConfirmation, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION; - } - - public boolean isModified() { - for (SWFList swfList : swfs) { - for (SWF swf : swfList) { - if (swf.isModified()) { - return true; - } - } - } - - return false; - } - - public boolean closeAll(boolean showCloseConfirmation) { - if (showCloseConfirmation && isModified()) { - boolean closeConfirmResult = closeConfirmation(swfs.size() == 1 ? swfs.get(0) : null); - if (!closeConfirmResult) { - return false; - } - } - - List swfsLists = new ArrayList<>(swfs); - swfs.clear(); - oldItem = null; - clear(); - updateUi(); - - for (SWFList swfList : swfsLists) { - List swfs2 = new ArrayList<>(swfList); - for (SWF swf : swfs2) { - swf.clearTagSwfs(); - } - } - - return true; - } - - public boolean close(SWFList swfList) { - boolean modified = false; - for (SWF swf : swfList) { - if (swf.isModified()) { - modified = true; - } - } - - if (modified) { - boolean closeConfirmResult = closeConfirmation(swfList); - if (!closeConfirmResult) { - return false; - } - } - - swfs.remove(swfList); - oldItem = null; - clear(); - updateUi(); - - List swfs2 = new ArrayList<>(swfList); - for (SWF swf : swfs2) { - swf.clearTagSwfs(); - } - - return true; - } - - public void enableDrop(boolean value) { - if (value) { - setDropTarget(new DropTarget() { - @Override - public synchronized void drop(DropTargetDropEvent dtde) { - try { - dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); - @SuppressWarnings("unchecked") - List droppedFiles = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); - if (!droppedFiles.isEmpty()) { - SWFSourceInfo[] sourceInfos = new SWFSourceInfo[droppedFiles.size()]; - for (int i = 0; i < droppedFiles.size(); i++) { - sourceInfos[i] = new SWFSourceInfo(null, droppedFiles.get(i).getAbsolutePath(), null); - } - Main.openFile(sourceInfos, null); - } - } catch (UnsupportedFlavorException | IOException ex) { - } - } - }); - } else { - setDropTarget(null); - } - } - - public void updateClassesList() { - List nodes = getASTreeNodes(tagTree); - boolean updateNeeded = false; - for (TreeItem n : nodes) { - if (n instanceof ClassesListTreeModel) { - ((ClassesListTreeModel) n).update(); - updateNeeded = true; - } - } - - refreshTree(); - - if (updateNeeded) { - tagTree.updateUI(); - } - } - - public void doFilter() { - List nodes = getASTreeNodes(tagTree); - for (TreeItem n : nodes) { - if (n instanceof ClassesListTreeModel) { - String filterText = filterField.getText(); - ((ClassesListTreeModel) n).setFilter(filterText); - TagTreeModel tm = tagTree.getModel(); - TreePath path = tm.getTreePath(n); - tm.updateNode(path); - if (!filterText.isEmpty()) { - View.expandTreeNodes(tagTree, path, true); - } - } - } - } - - public void renameIdentifier(SWF swf, String identifier) throws InterruptedException { - String oldName = identifier; - String newName = View.showInputDialog(translate("rename.enternew"), oldName); - if (newName != null) { - if (!oldName.equals(newName)) { - swf.renameAS2Identifier(oldName, newName); - View.showMessageDialog(null, translate("rename.finished.identifier")); - updateClassesList(); - reload(true); - } - } - } - - public void renameMultiname(List abcList, int multiNameIndex) { - String oldName = ""; - AVM2ConstantPool constants = getABCPanel().abc.constants; - if (constants.getMultiname(multiNameIndex).name_index > 0) { - oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index); - } - - String newName = View.showInputDialog(translate("rename.enternew"), oldName); - if (newName != null) { - if (!oldName.equals(newName)) { - int mulCount = 0; - for (ABCContainerTag cnt : abcList) { - ABC abc = cnt.getABC(); - for (int m = 1; m < abc.constants.getMultinameCount(); m++) { - int ni = abc.constants.getMultiname(m).name_index; - String n = ""; - if (ni > 0) { - n = abc.constants.getString(ni); - } - if (n.equals(oldName)) { - abc.renameMultiname(m, newName); - mulCount++; - } - } - } - - View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", Integer.toString(mulCount))); - if (abcPanel != null) { - abcPanel.reload(); - } - - updateClassesList(); - reload(true); - ABCPanel abcPanel = getABCPanel(); - abcPanel.hilightScript(abcPanel.getSwf(), abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toRawString()); - } - } - } - - public List getASTreeNodes(TagTree tree) { - List result = new ArrayList<>(); - TagTreeModel tm = (TagTreeModel) tree.getModel(); - if (tm == null) { - return result; - } - TreeItem root = tm.getRoot(); - for (int i = 0; i < tm.getChildCount(root); i++) { - // first level node can be SWF and SWFBundle - TreeItem node = tm.getChild(root, i); - if (node instanceof SWFBundle) { - for (int j = 0; j < tm.getChildCount(node); j++) { - // child of SWFBundle should be SWF - SWF swfNode = (SWF) tm.getChild(node, j); - result.add(tm.getScriptsNode(swfNode)); - } - } else if (node instanceof SWF) { - SWF swfNode = (SWF) tm.getChild(root, i); - result.add(tm.getScriptsNode(swfNode)); - } - } - return result; - } - - public boolean confirmExperimental() { - return View.showConfirmDialog(null, translate("message.confirm.experimental"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; - } - - public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { - - List ret = new ArrayList<>(); - List sel = folderPreviewPanel.selectedItems.isEmpty() ? tagTree.getAllSelected() : new ArrayList<>(folderPreviewPanel.selectedItems.values()); - - Set usedSwfs = new HashSet<>(); - for (TreeItem d : sel) { - SWF selectedNodeSwf = d.getSwf(); - if (!usedSwfs.contains(selectedNodeSwf)) { - usedSwfs.add(selectedNodeSwf); - } - } - - Map usedSwfsIds = new HashMap<>(); - for (SWF swf : usedSwfs) { - List as3scripts = new ArrayList<>(); - List images = new ArrayList<>(); - List shapes = new ArrayList<>(); - List morphshapes = new ArrayList<>(); - List buttons = new ArrayList<>(); - List movies = new ArrayList<>(); - List sounds = new ArrayList<>(); - List texts = new ArrayList<>(); - List as12scripts = new ArrayList<>(); - List binaryData = new ArrayList<>(); - Map> frames = new HashMap<>(); - List fonts = new ArrayList<>(); - List symbolNames = new ArrayList<>(); - - for (TreeItem d : sel) { - SWF selectedNodeSwf = d.getSwf(); - - if (selectedNodeSwf != swf) { - continue; - } - - if (d instanceof TagScript) { - Tag tag = ((TagScript) d).getTag(); - if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { - as12scripts.add(d); - } - } - - if (d instanceof Tag || d instanceof ASMSource) { - TreeNodeType nodeType = TagTree.getTreeNodeType(d); - if (nodeType == TreeNodeType.IMAGE) { - images.add((Tag) d); - } - if (nodeType == TreeNodeType.SHAPE) { - shapes.add((Tag) d); - } - if (nodeType == TreeNodeType.BUTTON) { - buttons.add((Tag) d); - } - if (nodeType == TreeNodeType.MORPH_SHAPE) { - morphshapes.add((Tag) d); - } - if (nodeType == TreeNodeType.AS) { - as12scripts.add(d); - } - if (nodeType == TreeNodeType.MOVIE) { - movies.add((Tag) d); - } - if (nodeType == TreeNodeType.SOUND) { - sounds.add((Tag) d); - } - if (nodeType == TreeNodeType.BINARY_DATA) { - binaryData.add((Tag) d); - } - if (nodeType == TreeNodeType.TEXT) { - texts.add((Tag) d); - } - if (nodeType == TreeNodeType.FONT) { - fonts.add((Tag) d); - } - if (nodeType == TreeNodeType.OTHER_TAG) { - if (d instanceof SymbolClassTypeTag) { - symbolNames.add((Tag) d); - } - } - } - - if (d instanceof Frame) { - Frame fn = (Frame) d; - Timelined parent = fn.timeline.timelined; - int frame = fn.frame; - int parentId = 0; - if (parent instanceof CharacterTag) { - parentId = ((CharacterTag) parent).getCharacterId(); - } - if (!frames.containsKey(parentId)) { - frames.put(parentId, new ArrayList<>()); - } - frames.get(parentId).add(frame); - } - if (d instanceof ScriptPack) { - as3scripts.add((ScriptPack) d); - } - } - - String selFile2; - if (usedSwfs.size() > 1) { - selFile2 = selFile + File.separator + Helper.getNextId(swf.getShortFileName(), usedSwfsIds); - } else { - selFile2 = selFile; - } - - EventListener evl = swf.getExportEventListener(); - - if (export.isOptionEnabled(ImageExportMode.class)) { - ret.addAll(new ImageExporter().exportImages(handler, selFile2 + File.separator + ImageExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(images), - new ImageExportSettings(export.getValue(ImageExportMode.class)), evl)); - } - - if (export.isOptionEnabled(ShapeExportMode.class)) { - ret.addAll(new ShapeExporter().exportShapes(handler, selFile2 + File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(shapes), - new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl)); - } - - if (export.isOptionEnabled(MorphShapeExportMode.class)) { - ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile2 + File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(morphshapes), - new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl)); - } - - if (export.isOptionEnabled(TextExportMode.class)) { - ret.addAll(new TextExporter().exportTexts(handler, selFile2 + File.separator + TextExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(texts), - new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl)); - } - - if (export.isOptionEnabled(MovieExportMode.class)) { - ret.addAll(new MovieExporter().exportMovies(handler, selFile2 + File.separator + MovieExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(movies), - new MovieExportSettings(export.getValue(MovieExportMode.class)), evl)); - } - - if (export.isOptionEnabled(SoundExportMode.class)) { - ret.addAll(new SoundExporter().exportSounds(handler, selFile2 + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(sounds), - new SoundExportSettings(export.getValue(SoundExportMode.class)), evl)); - } - - if (export.isOptionEnabled(BinaryDataExportMode.class)) { - ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile2 + File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(binaryData), - new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl)); - } - - if (export.isOptionEnabled(FontExportMode.class)) { - ret.addAll(new FontExporter().exportFonts(handler, selFile2 + File.separator + FontExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(fonts), - new FontExportSettings(export.getValue(FontExportMode.class)), evl)); - } - - if (export.isOptionEnabled(SymbolClassExportMode.class)) { - ret.addAll(new SymbolClassExporter().exportNames(selFile2, new ReadOnlyTagList(symbolNames), evl)); - } - - FrameExporter frameExporter = new FrameExporter(); - - if (export.isOptionEnabled(FrameExportMode.class)) { - FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); - for (Entry> entry : frames.entrySet()) { - int containerId = entry.getKey(); - if (containerId == 0) { - String subFolder = FrameExportSettings.EXPORT_FOLDER_NAME; - ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, containerId, entry.getValue(), fes, evl)); - } - } - } - - if (export.isOptionEnabled(SpriteExportMode.class)) { - SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); - for (Entry> entry : frames.entrySet()) { - int containerId = entry.getKey(); - if (containerId != 0) { - String subFolder = SpriteExportSettings.EXPORT_FOLDER_NAME; - ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, containerId, entry.getValue(), ses, evl)); - } - } - } - - if (export.isOptionEnabled(ButtonExportMode.class)) { - ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); - for (Tag tag : buttons) { - ButtonTag button = (ButtonTag) tag; - String subFolder = ButtonExportSettings.EXPORT_FOLDER_NAME; - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, button.getCharacterId(), frameNums, bes, evl)); - } - } - - if (export.isOptionEnabled(ScriptExportMode.class)) { - if (as3scripts.size() > 0 || as12scripts.size() > 0) { - boolean parallel = Configuration.parallelSpeedUp.get(); - String scriptsFolder = Path.combine(selFile2, ScriptExportSettings.EXPORT_FOLDER_NAME); - Path.createDirectorySafe(new File(scriptsFolder)); - boolean singleScriptFile = Configuration.scriptExportSingleFile.get(); - if (parallel && singleScriptFile) { - logger.log(Level.WARNING, AppStrings.translate("export.script.singleFilePallelModeWarning")); - singleScriptFile = false; - } - - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile); - String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { - scriptExportSettings.singleFileWriter = writer; - if (swf.isAS3()) { - ret.addAll(new AS3ScriptExporter().exportActionScript3(swf, handler, scriptsFolder, as3scripts, scriptExportSettings, parallel, evl)); - } else { - Map asmsToExport = swf.getASMs(true, as12scripts, false); - ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, scriptsFolder, asmsToExport, scriptExportSettings, parallel, evl)); - } - } - } - } - } - - return ret; - } - - public void exportAll(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { - boolean exportAll = false; - if (exportAll) { - exportAllDebug(swf, handler, selFile, export); - return; - } - - EventListener evl = swf.getExportEventListener(); - - if (export.isOptionEnabled(ImageExportMode.class)) { - new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new ImageExportSettings(export.getValue(ImageExportMode.class)), evl); - } - - if (export.isOptionEnabled(ShapeExportMode.class)) { - new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl); - } - - if (export.isOptionEnabled(MorphShapeExportMode.class)) { - new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl); - } - - if (export.isOptionEnabled(TextExportMode.class)) { - new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl); - } - - if (export.isOptionEnabled(MovieExportMode.class)) { - new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new MovieExportSettings(export.getValue(MovieExportMode.class)), evl); - } - - if (export.isOptionEnabled(SoundExportMode.class)) { - new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new SoundExportSettings(export.getValue(SoundExportMode.class)), evl); - } - - if (export.isOptionEnabled(BinaryDataExportMode.class)) { - new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl); - } - - if (export.isOptionEnabled(FontExportMode.class)) { - new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), - new FontExportSettings(export.getValue(FontExportMode.class)), evl); - } - - if (export.isOptionEnabled(SymbolClassExportMode.class)) { - new SymbolClassExporter().exportNames(selFile, swf.getTags(), evl); - } - - FrameExporter frameExporter = new FrameExporter(); - - if (export.isOptionEnabled(FrameExportMode.class)) { - FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); - frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME), swf, 0, null, fes, evl); - } - - if (export.isOptionEnabled(SpriteExportMode.class)) { - SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), null, ses, evl); - } - } - } - - if (export.isOptionEnabled(ButtonExportMode.class)) { - ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof ButtonTag) { - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), frameNums, bes, evl); - } - } - } - - if (export.isOptionEnabled(ScriptExportMode.class)) { - boolean parallel = Configuration.parallelSpeedUp.get(); - String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); - Path.createDirectorySafe(new File(scriptsFolder)); - boolean singleScriptFile = Configuration.scriptExportSingleFile.get(); - if (parallel && singleScriptFile) { - logger.log(Level.WARNING, AppStrings.translate("export.script.singleFilePallelModeWarning")); - singleScriptFile = false; - } - - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile); - String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { - scriptExportSettings.singleFileWriter = writer; - swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); - } - } - } - - public void exportAllDebug(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { - EventListener evl = swf.getExportEventListener(); - - if (export.isOptionEnabled(ImageExportMode.class)) { - for (ImageExportMode exportMode : ImageExportMode.values()) { - new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new ImageExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(ShapeExportMode.class)) { - for (ShapeExportMode exportMode : ShapeExportMode.values()) { - new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new ShapeExportSettings(exportMode, export.getZoom()), evl); - } - } - - if (export.isOptionEnabled(MorphShapeExportMode.class)) { - for (MorphShapeExportMode exportMode : MorphShapeExportMode.values()) { - new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new MorphShapeExportSettings(exportMode, export.getZoom()), evl); - } - } - - if (export.isOptionEnabled(TextExportMode.class)) { - for (TextExportMode exportMode : TextExportMode.values()) { - new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new TextExportSettings(exportMode, Configuration.textExportSingleFile.get(), export.getZoom()), evl); - } - } - - if (export.isOptionEnabled(MovieExportMode.class)) { - for (MovieExportMode exportMode : MovieExportMode.values()) { - new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new MovieExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(SoundExportMode.class)) { - for (SoundExportMode exportMode : SoundExportMode.values()) { - new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new SoundExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(BinaryDataExportMode.class)) { - for (BinaryDataExportMode exportMode : BinaryDataExportMode.values()) { - new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new BinaryDataExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(FontExportMode.class)) { - for (FontExportMode exportMode : FontExportMode.values()) { - new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), - new FontExportSettings(exportMode), evl); - } - } - - if (export.isOptionEnabled(SymbolClassExportMode.class)) { - for (SymbolClassExportMode exportMode : SymbolClassExportMode.values()) { - new SymbolClassExporter().exportNames(selFile, swf.getTags(), evl); - } - } - - FrameExporter frameExporter = new FrameExporter(); - - if (export.isOptionEnabled(FrameExportMode.class)) { - for (FrameExportMode exportMode : FrameExportMode.values()) { - FrameExportSettings fes = new FrameExportSettings(exportMode, export.getZoom()); - frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, 0, null, fes, evl); - } - } - - if (export.isOptionEnabled(SpriteExportMode.class)) { - for (SpriteExportMode exportMode : SpriteExportMode.values()) { - SpriteExportSettings ses = new SpriteExportSettings(exportMode, export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof DefineSpriteTag) { - frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), null, ses, evl); - } - } - } - } - - if (export.isOptionEnabled(ButtonExportMode.class)) { - for (ButtonExportMode exportMode : ButtonExportMode.values()) { - ButtonExportSettings bes = new ButtonExportSettings(exportMode, export.getZoom()); - for (CharacterTag c : swf.getCharacters().values()) { - if (c instanceof ButtonTag) { - List frameNums = new ArrayList<>(); - frameNums.add(0); // todo: export all frames - frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), frameNums, bes, evl); - } - } - } - } - - if (export.isOptionEnabled(ScriptExportMode.class)) { - boolean parallel = Configuration.parallelSpeedUp.get(); - for (ScriptExportMode exportMode : ScriptExportMode.values()) { - String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME, exportMode.name()); - Path.createDirectorySafe(new File(scriptsFolder)); - boolean singleScriptFile = Configuration.scriptExportSingleFile.get(); - if (parallel && singleScriptFile) { - logger.log(Level.WARNING, AppStrings.translate("export.script.singleFilePallelModeWarning")); - singleScriptFile = false; - } - - ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, singleScriptFile); - String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); - try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { - scriptExportSettings.singleFileWriter = writer; - swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); - } - } - } - } - - public List getSwfs() { - return swfs; - } - - public SWFList getCurrentSwfList() { - SWF swf = getCurrentSwf(); - if (swf == null) { - return null; - } - - return swf.swfList; - } - - public SWF getCurrentSwf() { - if (swfs == null || swfs.isEmpty()) { - return null; - } - - if (treePanelMode == TreePanelMode.TAG_TREE) { - TreeItem treeNode = (TreeItem) tagTree.getLastSelectedPathComponent(); - if (treeNode == null || treeNode instanceof SWFList) { - return null; - } - - return treeNode.getSwf(); - } else if (treePanelMode == TreePanelMode.DUMP_TREE) { - DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); - - if (dumpInfo == null) { - return null; - } - - return DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); - } - - return null; - } - - public void gotoFrame(int frame) { - TreeItem treeItem = (TreeItem) tagTree.getLastSelectedPathComponent(); - if (treeItem == null) { - return; - } - if (treeItem instanceof Timelined) { - Timelined t = (Timelined) treeItem; - Frame f = tagTree.getModel().getFrame(treeItem.getSwf(), t, frame); - if (f != null) { - setTagTreeSelectedNode(f); - } - } - } - - public void gotoScriptLine(SWF swf, String scriptName, int line, int classIndex, int traitIndex, int methodIndex) { - gotoScriptName(swf, scriptName); - if (abcPanel != null) { - if (Main.isDebugPCode()) { - if (classIndex != -1) { - boolean classChanged = false; - if (abcPanel.decompiledTextArea.getClassIndex() != classIndex) { - abcPanel.decompiledTextArea.setClassIndex(classIndex); - classChanged = true; - } - if (traitIndex != -10 && (classChanged || abcPanel.decompiledTextArea.lastTraitIndex != traitIndex)) { - abcPanel.decompiledTextArea.gotoTrait(traitIndex); - } - } - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.gotoInstrLine(line); - } else { - abcPanel.decompiledTextArea.gotoLine(line); - } - } else if (actionPanel != null) { - if (Main.isDebugPCode()) { - actionPanel.editor.gotoLine(line); - } else { - actionPanel.decompiledEditor.gotoLine(line); - } - } - refreshBreakPoints(); - - } - - public void refreshBreakPoints() { - if (abcPanel != null) { - abcPanel.decompiledTextArea.refreshMarkers(); - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.refreshMarkers(); - } - if (actionPanel != null) { - actionPanel.decompiledEditor.refreshMarkers(); - actionPanel.editor.refreshMarkers(); - } - } - - /* - public void debuggerBreakAt(SWF swf, String cls, int line) { - View.execInEventDispatchLater(new Runnable() { - - @Override - public void run() { - gotoClassLine(swf, cls, line); - if (abcPanel != null) { - abcPanel.decompiledTextArea.addColorMarker(line, DecompiledEditorPane.FG_IP_COLOR, DecompiledEditorPane.BG_IP_COLOR, DecompiledEditorPane.PRIORITY_IP); - } - } - }); - - }*/ - public void gotoScriptName(SWF swf, String scriptName) { - if (swf == null) { - return; - } - if (swf.isAS3()) { - String rawScriptName = scriptName; - if (rawScriptName.startsWith("#PCODE ")) { - rawScriptName = rawScriptName.substring(rawScriptName.indexOf(';') + 1); - } - - List abcList = swf.getAbcList(); - if (!abcList.isEmpty()) { - ABCPanel abcPanel = getABCPanel(); - abcPanel.setAbc(abcList.get(0).getABC()); - abcPanel.hilightScript(swf, rawScriptName); - } - } else { - String rawScriptName = scriptName; - if (rawScriptName.startsWith("#PCODE ")) { - rawScriptName = rawScriptName.substring("#PCODE ".length()); - } - Map asms = swf.getASMs(true); - if (actionPanel != null && asms.containsKey(rawScriptName)) { - actionPanel.setSource(asms.get(rawScriptName), true); - } - } - } - - public void gotoDocumentClass(SWF swf) { - if (swf == null) { - return; - } - - String documentClass = swf.getDocumentClass(); - if (documentClass != null && !Configuration.dumpView.get()) { - List abcList = swf.getAbcList(); - if (!abcList.isEmpty()) { - ABCPanel abcPanel = getABCPanel(); - for (ABCContainerTag c : abcList) { - if (c.getABC().findClassByName(documentClass) > -1) { - abcPanel.setAbc(c.getABC()); - abcPanel.hilightScript(swf, documentClass); - break; - } - } - } - } - } - - public void disableDecompilationChanged() { - clearAllScriptCache(); - - if (abcPanel != null) { - abcPanel.reload(); - } - - updateClassesList(); - } - - private void clearAllScriptCache() { - for (SWFList swfList : swfs) { - for (SWF swf : swfList) { - swf.clearScriptCache(); - } - } - } - - public void searchInActionScriptOrText(Boolean searchInText, SWF swf) { - SearchDialog searchDialog = new SearchDialog(getMainFrame().getWindow(), false); - if (searchInText != null) { - if (searchInText) { - searchDialog.searchInTextsRadioButton.setSelected(true); - } else { - searchDialog.searchInASRadioButton.setSelected(true); - } - } - - if (searchDialog.showDialog() == AppDialog.OK_OPTION) { - final String txt = searchDialog.searchField.getText(); - if (!txt.isEmpty()) { - if (swf.isAS3()) { - getABCPanel(); - } else { - getActionPanel(); - } - - boolean ignoreCase = searchDialog.ignoreCaseCheckBox.isSelected(); - boolean regexp = searchDialog.regexpCheckBox.isSelected(); - - if (searchDialog.searchInASRadioButton.isSelected()) { - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - List abcResult = null; - List actionResult = null; - if (swf.isAS3()) { - abcResult = getABCPanel().search(swf, txt, ignoreCase, regexp, this); - } else { - actionResult = getActionPanel().search(swf, txt, ignoreCase, regexp, this); - } - - List fAbcResult = abcResult; - List fActionResult = actionResult; - View.execInEventDispatch(() -> { - boolean found = false; - if (fAbcResult != null) { - found = true; - getABCPanel().searchPanel.setSearchText(txt); - SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getABCPanel()); - sr.setResults(fAbcResult); - sr.setVisible(true); - } else if (fActionResult != null) { - found = true; - getActionPanel().searchPanel.setSearchText(txt); - - SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getActionPanel()); - sr.setResults(fActionResult); - sr.setVisible(true); - } - - if (!found) { - View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - - Main.stopWork(); - }); - - return null; - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.stopWork(); - }); - - } - }.execute(); - } else if (searchDialog.searchInTextsRadioButton.isSelected()) { - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - List textResult; - SearchPanel textSearchPanel = previewPanel.getTextPanel().getSearchPanel(); - textSearchPanel.setOptions(ignoreCase, regexp); - textResult = searchText(txt, ignoreCase, regexp, swf); - - List fTextResult = textResult; - View.execInEventDispatch(() -> { - textSearchPanel.setSearchText(txt); - boolean found = textSearchPanel.setResults(fTextResult); - if (!found) { - View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - - Main.stopWork(); - }); - - return null; - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.stopWork(); - }); - - } - }.execute(); - } - } - } - } - - public void replaceText() { - SearchDialog replaceDialog = new SearchDialog(getMainFrame().getWindow(), true); - if (replaceDialog.showDialog() == AppDialog.OK_OPTION) { - final String txt = replaceDialog.searchField.getText(); - if (!txt.isEmpty()) { - final SWF swf = getCurrentSwf(); - - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - int findCount = 0; - boolean ignoreCase = replaceDialog.ignoreCaseCheckBox.isSelected(); - boolean regexp = replaceDialog.regexpCheckBox.isSelected(); - String replacement = replaceDialog.replaceField.getText(); - if (!regexp) { - replacement = Matcher.quoteReplacement(replacement); - } - Pattern pat; - if (regexp) { - pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } else { - pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } - List textTags = new ArrayList<>(); - for (Tag tag : swf.getTags()) { - if (tag instanceof TextTag) { - textTags.add((TextTag) tag); - } - } - for (TextTag textTag : textTags) { - if (!replaceDialog.replaceInParametersCheckBox.isSelected()) { - List texts = textTag.getTexts(); - boolean found = false; - for (int i = 0; i < texts.size(); i++) { - String text = texts.get(i); - if (pat.matcher(text).find()) { - texts.set(i, text.replaceAll(txt, replacement)); - found = true; - findCount++; - } - } - if (found) { - String[] textArray = texts.toArray(new String[texts.size()]); - textTag.setFormattedText(getMissingCharacterHandler(), textTag.getFormattedText(false).text, textArray); - } - } else { - String text = textTag.getFormattedText(false).text; - if (pat.matcher(text).find()) { - textTag.setFormattedText(getMissingCharacterHandler(), text.replaceAll(txt, replacement), null); - findCount++; - } - } - } - - if (findCount > 0) { - swf.clearImageCache(); - repaintTree(); - } - - return null; - } - }.execute(); - } - } - } - - private List searchText(String txt, boolean ignoreCase, boolean regexp, SWF swf) { - if (txt != null && !txt.isEmpty()) { - List found = new ArrayList<>(); - Pattern pat; - if (regexp) { - pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } else { - pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); - } - for (Tag tag : swf.getTags()) { - if (tag instanceof TextTag) { - TextTag textTag = (TextTag) tag; - if (pat.matcher(textTag.getFormattedText(false).text).find()) { - found.add(textTag); - } - } - } - - return found; - } - - return null; - } - - @Override - public void updateSearchPos(TextTag item) { - setTagTreeSelectedNode(item); - previewPanel.getTextPanel().updateSearchPos(); - } - - private void setDumpTreeSelectedNode(DumpInfo dumpInfo) { - DumpTreeModel dtm = (DumpTreeModel) dumpTree.getModel(); - TreePath tp = dtm.getDumpInfoPath(dumpInfo); - if (tp != null) { - dumpTree.setSelectionPath(tp); - dumpTree.scrollPathToVisible(tp); - } else { - showCard(CARDEMPTYPANEL); - } - } - - public void setTagTreeSelectedNode(TreeItem treeItem) { - TagTreeModel ttm = tagTree.getModel(); - TreePath tp = ttm.getTreePath(treeItem); - if (tp != null) { - tagTree.setSelectionPath(tp); - tagTree.scrollPathToVisible(tp); - } else { - showCard(CARDEMPTYPANEL); - } - } - - public void autoDeobfuscateChanged() { - Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); - clearAllScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - reload(true); - updateClassesList(); - } - - public void renameColliding(final SWF swf) { - if (swf == null) { - return; - } - if (confirmExperimental()) { - new CancellableWorker() { - @Override - protected Integer doInBackground() throws Exception { - AbcMultiNameCollisionFixer fixer = new AbcMultiNameCollisionFixer(); - return fixer.fixCollisions(swf); - } - - @Override - protected void onStart() { - Main.startWork(translate("work.renaming.identifiers") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - try { - int cnt = get(); - Main.stopWork(); - View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt))); - swf.assignClassesToSymbols(); - swf.clearScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - updateClassesList(); - reload(true); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error during renaming identifiers", ex); - Main.stopWork(); - View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); - } - }); - } - }.execute(); - } - } - - public void renameOneIdentifier(final SWF swf) { - if (swf == null) { - return; - } - - FileAttributesTag fileAttributes = swf.getFileAttributes(); - if (fileAttributes != null && fileAttributes.actionScript3) { - final int multiName = getABCPanel().decompiledTextArea.getMultinameUnderCaret(); - final List abcList = swf.getAbcList(); - if (multiName > 0) { - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - renameMultiname(abcList, multiName); - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.renaming") + "...", this); - } - - @Override - protected void done() { - Main.stopWork(); - } - }.execute(); - - } else { - View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - } else { - final String identifier = getActionPanel().getStringUnderCursor(); - if (identifier != null) { - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - try { - renameIdentifier(swf, identifier); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.renaming") + "...", this); - } - - @Override - protected void done() { - Main.stopWork(); - } - }.execute(); - } else { - View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); - } - } - } - - public void exportFla(final SWF swf) { - if (swf == null) { - return; - } - JFileChooser fc = new JFileChooser(); - String selDir = Configuration.lastOpenDir.get(); - fc.setCurrentDirectory(new File(selDir)); - if (!selDir.endsWith(File.separator)) { - selDir += File.separator; - } - String fileName = new File(swf.getFile()).getName(); - fileName = fileName.substring(0, fileName.length() - 4) + ".fla"; - fc.setSelectedFile(new File(selDir + fileName)); - List flaFilters = new ArrayList<>(); - List xflFilters = new ArrayList<>(); - List versions = new ArrayList<>(); - boolean isAS3 = swf.isAS3(); - for (int i = FLAVersion.values().length - 1; i >= 0; i--) { - final FLAVersion v = FLAVersion.values()[i]; - if (!isAS3 && v.minASVersion() > 2) { - // This version does not support AS1/2 - } else { - versions.add(v); - FileFilter f = new FileFilter() { - @Override - public boolean accept(File f) { - return f.isDirectory() || (f.getName().toLowerCase().endsWith(".fla")); - } - - @Override - public String getDescription() { - return translate("filter.fla").replace("%version%", v.applicationName()); - } - }; - if (v == FLAVersion.CS6) { - fc.setFileFilter(f); - } else { - fc.addChoosableFileFilter(f); - } - flaFilters.add(f); - f = new FileFilter() { - @Override - public boolean accept(File f) { - return f.isDirectory() || (f.getName().toLowerCase().endsWith(".xfl")); - } - - @Override - public String getDescription() { - return translate("filter.xfl").replace("%version%", v.applicationName()); - } - }; - fc.addChoosableFileFilter(f); - xflFilters.add(f); - } - } - - fc.setAcceptAllFileFilterUsed(false); - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { - Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); - File sf = Helper.fixDialogFile(fc.getSelectedFile()); - - FileFilter selectedFilter = fc.getFileFilter(); - final boolean compressed = flaFilters.contains(selectedFilter); - String path = sf.getAbsolutePath(); - if (path.endsWith(".fla") || path.endsWith(".xfl")) { - path = path.substring(0, path.length() - 4); - } - path += compressed ? ".fla" : ".xfl"; - final FLAVersion selectedVersion = versions.get(compressed ? flaFilters.indexOf(selectedFilter) : xflFilters.indexOf(selectedFilter)); - final File selfile = new File(path); - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - Helper.freeMem(); - try { - AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); - if (compressed) { - swf.exportFla(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); - } else { - swf.exportXfl(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "FLA export error", ex); - View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); - } - Helper.freeMem(); - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.exporting.fla") + "...", this); - } - - @Override - protected void done() { - if (Configuration.openFolderAfterFlaExport.get()) { - try { - Desktop.getDesktop().open(selfile.getAbsoluteFile().getParentFile()); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - Main.stopWork(); - } - }.execute(); - } - } - - public void importText(final SWF swf) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("import.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - File textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); - TextImporter textImporter = new TextImporter(getMissingCharacterHandler(), new TextImportErrorHandler() { - // "configuration items" for the current replace only - private final ConfigurationItem showAgainImportError = new ConfigurationItem<>("showAgainImportError", true, true); - - private final ConfigurationItem showAgainInvalidText = new ConfigurationItem<>("showAgainInvalidText", true, true); - - private String getTextTagInfo(TextTag textTag) { - StringBuilder ret = new StringBuilder(); - if (textTag != null) { - ret.append(" TextId: ").append(textTag.getCharacterId()).append(" (").append(String.join(", ", textTag.getTexts())).append(")"); - } - - return ret.toString(); - } - - @Override - public boolean handle(TextTag textTag) { - String msg = translate("error.text.import"); - logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); - return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainImportError, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; - } - - @Override - public boolean handle(TextTag textTag, String message, long line) { - String msg = translate("error.text.invalid.continue").replace("%text%", message).replace("%line%", Long.toString(line)); - logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); - return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainInvalidText, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; - } - }); - - // try to import formatted texts - if (textsFile.exists()) { - textImporter.importTextsSingleFileFormatted(textsFile, swf); - } else { - textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); - // try to import plain texts - if (textsFile.exists()) { - textImporter.importTextsSingleFile(textsFile, swf); - } else { - textImporter.importTextsMultipleFiles(selFile, swf); - } - } - - swf.clearImageCache(); - reload(true); - } - } - - public As3ScriptReplacerInterface getAs3ScriptReplacer() { - As3ScriptReplacerInterface r = As3ScriptReplacerFactory.createByConfig(); - if (!r.isAvailable()) { - if (r instanceof MxmlcAs3ScriptReplacer) { - if (View.showConfirmDialog(null, AppStrings.translate("message.flexpath.notset"), AppStrings.translate("error"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { - Main.advancedSettings("paths"); - } - } else if (r instanceof FFDecAs3ScriptReplacer) { - if (View.showConfirmDialog(this, AppStrings.translate("message.playerpath.lib.notset"), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { - Main.advancedSettings("paths"); - } - } else { - //Not translated yet - just in case there are more Script replacers in the future. Unused now. - View.showConfirmDialog(this, "Current script replacer is not available", "Script replacer not available", JOptionPane.OK_OPTION, JOptionPane.ERROR_MESSAGE); - } - return null; - } - return r; - } - - public void importScript(final SWF swf) { - As3ScriptReplacerInterface as3ScriptReplacer = getAs3ScriptReplacer(); - if (as3ScriptReplacer == null) { - return; - } - String flexLocation = Configuration.flexSdkLocation.get(); - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("import.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); - - int countAs2 = new AS2ScriptImporter().importScripts(scriptsFolder, swf.getASMs(true)); - int countAs3 = new AS3ScriptImporter().importScripts(as3ScriptReplacer, scriptsFolder, swf.getAS3Packs()); - - if (countAs3 > 0) { - updateClassesList(); - } - - View.showMessageDialog(this, translate("import.script.result").replace("%count%", Integer.toString(countAs2 + countAs3))); - if (countAs2 != 0 || countAs3 != 0) { - reload(true); - } - } - } - - public void importSymbolClass(final SWF swf) { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("import.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - File importFile = new File(Path.combine(selFile, SymbolClassExporter.SYMBOL_CLASS_EXPORT_FILENAME)); - SymbolClassImporter importer = new SymbolClassImporter(); - - if (importFile.exists()) { - importer.importSymbolClasses(importFile, swf); - } - } - } - - private String selectExportDir() { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); - chooser.setDialogTitle(translate("export.select.directory")); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - final String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); - Configuration.lastExportDir.set(Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath()); - return selFile; - } - return null; - } - - public void export(final boolean onlySel) { - - final SWF swf = getCurrentSwf(); - List sel = tagTree.getAllSelected(); - if (!onlySel) { - sel = null; - } else if (sel.isEmpty()) { - return; - } - final ExportDialog export = new ExportDialog(sel); - if (export.showExportDialog() == AppDialog.OK_OPTION) { - final String selFile = selectExportDir(); - if (selFile != null) { - final long timeBefore = System.currentTimeMillis(); - - new CancellableWorker() { - @Override - public Void doInBackground() throws Exception { - try { - AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); - if (onlySel) { - exportSelection(errorHandler, selFile, export); - } else { - exportAll(swf, errorHandler, selFile, export); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error during export", ex); - View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage()); - } - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.exporting") + "...", this); - } - - @Override - protected void done() { - Main.stopWork(); - long timeAfter = System.currentTimeMillis(); - final long timeMs = timeAfter - timeBefore; - - View.execInEventDispatchLater(() -> { - setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); - }); - } - }.execute(); - - } - } - } - - public void exportJavaSource() { - List sel = tagTree.getSelected(); - for (TreeItem item : sel) { - if (item instanceof SWF) { - SWF swf = (SWF) item; - final String selFile = selectExportDir(); - if (selFile != null) { - Main.startWork(translate("work.exporting") + "...", null); - - try { - new SwfJavaExporter().exportJavaCode(swf, selFile); - Main.stopWork(); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - } - } - - public void exportSwfXml() { - List sel = tagTree.getSelected(); - Set swfs = new HashSet<>(); - - for (TreeItem item : sel) { - swfs.add(item.getSwf()); - } - - for (SWF swf : swfs) { - final String selFile = selectExportDir(); - if (selFile != null) { - Main.startWork(translate("work.exporting") + "...", null); - - try { - File outFile = new File(selFile + File.separator + Helper.makeFileName("swf.xml")); - new SwfXmlExporter().exportXml(swf, outFile); - Main.stopWork(); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - } - - public void importSwfXml() { - List sel = tagTree.getSelected(); - Set swfs = new HashSet<>(); - for (TreeItem item : sel) { - swfs.add(item.getSwf()); - } - if (swfs.size() > 1) { - return; - } - - for (SWF swf : swfs) { - File selectedFile = showImportFileChooser("filter.xml|*.xml"); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - String xml = Helper.readTextFile(selfile.getPath()); - try { - new SwfXmlImporter().importSwf(swf, xml); - swf.clearAllCache(); - swf.assignExportNamesToSymbols(); - swf.assignClassesToSymbols(); - refreshTree(swf); - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - } - } - - public void renameIdentifiers(final SWF swf) { - if (swf == null) { - return; - } - if (confirmExperimental()) { - RenameDialog renameDialog = new RenameDialog(); - if (renameDialog.showRenameDialog() == AppDialog.OK_OPTION) { - final RenameType renameType = renameDialog.getRenameType(); - new CancellableWorker() { - @Override - protected Integer doInBackground() throws Exception { - int cnt = swf.deobfuscateIdentifiers(renameType); - return cnt; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.renaming.identifiers") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - try { - int cnt = get(); - Main.stopWork(); - View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt))); - swf.assignClassesToSymbols(); - swf.clearScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - updateClassesList(); - reload(true); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error during renaming identifiers", ex); - Main.stopWork(); - View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); - } - }); - } - }.execute(); - } - } - } - - public void deobfuscate() { - DeobfuscationDialog deobfuscationDialog = new DeobfuscationDialog(); - if (deobfuscationDialog.showDialog() == AppDialog.OK_OPTION) { - DeobfuscationLevel level = DeobfuscationLevel.getByLevel(deobfuscationDialog.codeProcessingLevel.getValue()); - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - try { - ABCPanel abcPanel = getABCPanel(); - if (deobfuscationDialog.processAllCheckbox.isSelected()) { - SWF swf = abcPanel.getSwf(); - swf.deobfuscate(level); - } else { - int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); - DecompiledEditorPane decompiledTextArea = abcPanel.decompiledTextArea; - Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); - ABC abc = abcPanel.abc; - if (bi != -1) { - int scriptIndex = decompiledTextArea.getScriptLeaf().scriptIndex; - int classIndex = decompiledTextArea.getClassIndex(); - boolean isStatic = decompiledTextArea.getIsStatic(); - abc.bodies.get(bi).deobfuscate(level, t, scriptIndex, classIndex, isStatic, ""/*FIXME*/); - } - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(decompiledTextArea.getScriptLeaf().getPathScriptName(), bi, abc, t, abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Deobfuscation error", ex); - } - - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.deobfuscating") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.stopWork(); - View.showMessageDialog(null, translate("work.deobfuscating.complete")); - - clearAllScriptCache(); - getABCPanel().reload(); - updateClassesList(); - }); - } - }.execute(); - } - } - - public void removeNonScripts(SWF swf) { - if (swf == null) { - return; - } - - List tags = swf.getTags().toArrayList(); - List toRemove = new ArrayList<>(); - for (Tag tag : tags) { - System.out.println(tag.getClass()); - if (!(tag instanceof ABCContainerTag || tag instanceof ASMSource)) { - toRemove.add(tag); - } - } - - swf.removeTags(toRemove, true); - refreshTree(swf); - } - - public void removeExceptSelected(SWF swf) { - if (swf == null) { - return; - } - - List sel = tagTree.getAllSelected(); - Set needed = new HashSet<>(); - for (TreeItem item : sel) { - if (item instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) item; - characterTag.getNeededCharactersDeep(needed); - needed.add(characterTag.getCharacterId()); - } - } - - List tagsToRemove = new ArrayList<>(); - for (Tag tag : swf.getTags()) { - if (tag instanceof CharacterTag) { - CharacterTag characterTag = (CharacterTag) tag; - if (!needed.contains(characterTag.getCharacterId())) { - tagsToRemove.add(tag); - } - } - } - - swf.removeTags(tagsToRemove, true); - refreshTree(swf); - } - - private void clear() { - dumpViewPanel.clear(); - previewPanel.clear(); - headerPanel.clear(); - folderPreviewPanel.clear(); - if (abcPanel != null) { - abcPanel.clearSwf(); - } - if (actionPanel != null) { - actionPanel.clearSource(); - } - } - - public void refreshTree() { - refreshTree(new SWF[0]); - } - - public void refreshTree(SWF swf) { - refreshTree(new SWF[]{swf}); - } - - public void refreshTree(SWF[] swfs) { - clear(); - showCard(CARDEMPTYPANEL); - TreeItem treeItem = tagTree.getCurrentTreeItem(); - - tagTree.updateSwfs(swfs); - - if (treeItem != null) { - SWF treeItemSwf = treeItem.getSwf().getRootSwf(); - if (this.swfs.contains(treeItemSwf.swfList)) { - setTagTreeSelectedNode(treeItem); - } - } - - reload(true); - } - - public void refreshDecompiled() { - clearAllScriptCache(); - if (abcPanel != null) { - abcPanel.reload(); - } - - reload(true); - updateClassesList(); - } - - private MissingCharacterHandler getMissingCharacterHandler() { - return new MissingCharacterHandler() { - // "configuration items" for the current replace only - private final ConfigurationItem showAgainIgnoreMissingCharacters = new ConfigurationItem<>("showAgainIgnoreMissingCharacters", true, true); - - private boolean ignoreMissingCharacters = false; - - @Override - public boolean getIgnoreMissingCharacters() { - return ignoreMissingCharacters; - } - - @Override - public boolean handle(TextTag textTag, final FontTag font, final char character) { - String fontName = font.getSwf().sourceFontNamesMap.get(font.getFontId()); - if (fontName == null) { - fontName = font.getFontName(); - } - final Font f = FontTag.installedFontsByName.get(fontName); - if (f == null || !f.canDisplay(character)) { - String msg = translate("error.font.nocharacter").replace("%char%", "" + character); - logger.log(Level.SEVERE, "{0} FontId: {1} TextId: {2}", new Object[]{msg, font.getCharacterId(), textTag.getCharacterId()}); - ignoreMissingCharacters = View.showConfirmDialog(null, msg, translate("error"), - JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, - showAgainIgnoreMissingCharacters, - ignoreMissingCharacters ? JOptionPane.OK_OPTION : JOptionPane.CANCEL_OPTION) == JOptionPane.OK_OPTION; - return false; - } - - font.addCharacter(character, f); - - return true; - } - }; - } - - public boolean saveText(TextTag textTag, String formattedText, String[] texts, LineMarkedEditorPane editor) { - try { - if (textTag.setFormattedText(getMissingCharacterHandler(), formattedText, texts)) { - return true; - } - } catch (TextParseException ex) { - if (editor != null) { - editor.gotoLine((int) ex.line); - editor.markError(); - } - - View.showMessageDialog(null, translate("error.text.invalid").replace("%text%", ex.text).replace("%line%", Long.toString(ex.line)), translate("error"), JOptionPane.ERROR_MESSAGE); - } - - return false; - } - - public boolean alignText(TextTag textTag, TextAlign textAlign) { - return (textTag.alignText(textAlign)); - } - - public boolean translateText(TextTag textTag, int diff) { - return textTag.translateText(diff); - } - - public boolean previousTag() { - if (getCurrentView() == VIEW_RESOURCES) { - if (tagTree.getSelectionRows().length > 0) { - int row = tagTree.getSelectionRows()[0]; - if (row > 0) { - tagTree.setSelectionRow(row - 1); - tagTree.scrollRowToVisible(row - 1); - previewPanel.focusTextPanel(); - } - } - return true; - } - return false; - } - - public boolean nextTag() { - if (getCurrentView() == VIEW_RESOURCES) { - if (tagTree.getSelectionRows().length > 0) { - int row = tagTree.getSelectionRows()[0]; - if (row < tagTree.getRowCount() - 1) { - tagTree.setSelectionRow(row + 1); - tagTree.scrollRowToVisible(row + 1); - previewPanel.focusTextPanel(); - } - } - return true; - } - return false; - } - - public void selectBkColorButtonActionPerformed(ActionEvent evt) { - Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.getSwfBackgroundColor()); - if (newColor != null) { - View.setSwfBackgroundColor(newColor); - reload(true); - } - } - - public void replaceButtonActionPerformed(ActionEvent evt) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof DefineSoundTag) { - File selectedFile = showImportFileChooser("filter.sounds|*.mp3;*.wav|filter.sounds.mp3|*.mp3|filter.sounds.wav|*.wav"); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - DefineSoundTag ds = (DefineSoundTag) item; - int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; - if (selfile.getName().toLowerCase().endsWith(".mp3")) { - soundFormat = SoundFormat.FORMAT_MP3; - } - - boolean ok = false; - try { - ok = ds.setSound(new FileInputStream(selfile), soundFormat); - ds.getSwf().clearSoundCache(); - } catch (IOException ex) { - //ignore - } - - if (!ok) { - View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } else { - reload(true); - } - } - } - if (item instanceof ImageTag) { - ImageTag it = (ImageTag) item; - if (it.importSupported()) { - File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp"); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - try { - Tag newTag = new ImageImporter().importImage(it, data); - SWF swf = it.getSwf(); - if (newTag != null) { - refreshTree(swf); - setTagTreeSelectedNode(newTag); - } - swf.clearImageCache(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Invalid image", ex); - View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - - reload(true); - } - } - } - if (item instanceof ShapeTag) { - ShapeTag st = (ShapeTag) item; - String filter = "filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg"; - File selectedFile = showImportFileChooser(filter); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = null; - String svgText = null; - if (".svg".equals(Path.getExtension(selfile))) { - svgText = Helper.readTextFile(selfile.getAbsolutePath()); - showSvgImportWarning(); - } else { - data = Helper.readFile(selfile.getAbsolutePath()); - } - try { - Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText) : new ShapeImporter().importImage(st, data); - SWF swf = st.getSwf(); - if (newTag != null) { - refreshTree(swf); - setTagTreeSelectedNode(newTag); - } - - swf.clearImageCache(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Invalid image", ex); - View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - reload(true); - } - } - if (item instanceof DefineBinaryDataTag) { - DefineBinaryDataTag bt = (DefineBinaryDataTag) item; - File selectedFile = showImportFileChooser(""); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - new BinaryDataImporter().importData(bt, data); - refreshTree(bt.getSwf()); - reload(true); - } - } - } - - public void replaceNoFillButtonActionPerformed(ActionEvent evt) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof ShapeTag) { - ShapeTag st = (ShapeTag) item; - String filter = "filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg"; - File selectedFile = showImportFileChooser(filter); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = null; - String svgText = null; - if (".svg".equals(Path.getExtension(selfile))) { - svgText = Helper.readTextFile(selfile.getAbsolutePath()); - showSvgImportWarning(); - } else { - data = Helper.readFile(selfile.getAbsolutePath()); - } - try { - Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText, false) : new ShapeImporter().importImage(st, data, 0, false); - SWF swf = st.getSwf(); - if (newTag != null) { - refreshTree(swf); - setTagTreeSelectedNode(newTag); - } - - swf.clearImageCache(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Invalid image", ex); - View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - reload(true); - } - } - } - - private void showSvgImportWarning() { - View.showMessageDialog(null, AppStrings.translate("message.warning.svgImportExperimental"), AppStrings.translate("message.warning"), JOptionPane.WARNING_MESSAGE, Configuration.warningSvgImport); - } - - public void replaceAlphaButtonActionPerformed(ActionEvent evt) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item == null) { - return; - } - - if (item instanceof DefineBitsJPEG3Tag || item instanceof DefineBitsJPEG4Tag) { - ImageTag it = (ImageTag) item; - if (it.importSupported()) { - File selectedFile = showImportFileChooser(""); - if (selectedFile != null) { - File selfile = Helper.fixDialogFile(selectedFile); - byte[] data = Helper.readFile(selfile.getAbsolutePath()); - try { - new ImageImporter().importImageAlpha(it, data); - SWF swf = it.getSwf(); - swf.clearImageCache(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Invalid alpha channel data", ex); - View.showMessageDialog(null, translate("error.image.alpha.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); - } - - reload(true); - } - } - } - } - - public void exportJavaSourceActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - exportJavaSource(); - } - - public void exportSwfXmlActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - exportSwfXml(); - } - - public void importSwfXmlActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - importSwfXml(); - } - - public void exportSelectionActionPerformed(ActionEvent evt) { - if (Main.isWorking()) { - return; - } - - export(true); - } - - public File showImportFileChooser(String filter) { - String[] filterArray = filter.length() > 0 ? filter.split("\\|") : new String[0]; - - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); - boolean first = true; - for (int i = 0; i < filterArray.length; i += 2) { - final String filterName = filterArray[i]; - final String[] extensions = filterArray[i + 1].split(";"); - for (int j = 0; j < extensions.length; j++) { - if (extensions[j].startsWith("*.")) { - extensions[j] = extensions[j].substring(1); - } - } - FileFilter ff = new FileFilter() { - @Override - public boolean accept(File f) { - if (f.isDirectory()) { - return true; - } - String fileName = f.getName().toLowerCase(); - for (String ext : extensions) { - if (fileName.endsWith(ext)) { - return true; - } - } - return false; - } - - @Override - public String getDescription() { - StringBuilder extStr = new StringBuilder(); - boolean first = true; - for (String ext : extensions) { - if (first) { - first = false; - } else { - extStr.append(","); - } - - extStr.append("*").append(ext); - } - - return translate(filterName).replace("%extensions%", extStr); - } - }; - if (first) { - fc.setFileFilter(ff); - } else { - fc.addChoosableFileFilter(ff); - } - first = false; - } - - JFrame f = new JFrame(); - View.setWindowIcon(f); - if (fc.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) { - File result = fc.getSelectedFile(); - Configuration.lastOpenDir.set(Helper.fixDialogFile(result).getParentFile().getAbsolutePath()); - return result; - } - - return null; - } - - private void showDetail(String card) { - CardLayout cl = (CardLayout) (detailPanel.getLayout()); - cl.show(detailPanel, card); - if (card.equals(DETAILCARDEMPTYPANEL)) { - if (detailPanel.isVisible()) { - detailPanel.setVisible(false); - } - } else if (!detailPanel.isVisible()) { - detailPanel.setVisible(true); - } - } - - private void showCard(String card) { - CardLayout cl = (CardLayout) (displayPanel.getLayout()); - cl.show(displayPanel, card); - } - - @Override - public void valueChanged(TreeSelectionEvent e) { - Object source = e.getSource(); - TreeItem treeItem = (TreeItem) e.getPath().getLastPathComponent(); - - if (!(treeItem instanceof SWFList)) { - SWF swf = treeItem.getSwf(); - if (swfs.isEmpty()) { - // show welcome panel after closing swfs - updateUi(); - } else { - if (swf == null) { - swf = swfs.get(0).get(0); - } - - updateUi(swf); - } - } else { - updateUi(); - } - - reload(false); - - if (source == dumpTree) { - Tag t = null; - if (treeItem instanceof DumpInfo) { - DumpInfo di = (DumpInfo) treeItem; - t = di.getTag(); - } - - showPreview(t, dumpPreviewPanel); - } - } - - public void unloadFlashPlayer() { - if (flashPanel != null) { - try { - flashPanel.close(); - } catch (IOException ex) { - // ignore - } - } - if (flashPanel2 != null) { - try { - flashPanel2.close(); - } catch (IOException ex) { - // ignore - } - } - } - - public void clearDebuggerColors() { - if (abcPanel != null) { - abcPanel.decompiledTextArea.removeColorMarkerOnAllLines(DecompiledEditorPane.IP_MARKER); - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clearDebuggerColors(); - } - if (actionPanel != null) { - actionPanel.decompiledEditor.removeColorMarkerOnAllLines(DecompiledEditorPane.IP_MARKER); - actionPanel.editor.removeColorMarkerOnAllLines(DecompiledEditorPane.IP_MARKER); - } - } - - private void stopFlashPlayer() { - if (flashPanel != null) { - if (!flashPanel.isStopped()) { - flashPanel.stopSWF(); - } - } - if (flashPanel2 != null) { - if (!flashPanel2.isStopped()) { - flashPanel2.stopSWF(); - } - } - } - - public boolean isInternalFlashViewerSelected() { - return mainMenu.isInternalFlashViewerSelected(); - } - - public static final int VIEW_RESOURCES = 0; - - public static final int VIEW_DUMP = 1; - - public static final int VIEW_TIMELINE = 2; - - private int getCurrentView() { - return Configuration.dumpView.get() ? VIEW_DUMP : VIEW_RESOURCES; - } - - public void setTreeModel(int view) { - switch (view) { - case VIEW_DUMP: - if (dumpTree.getModel() == null) { - DumpTreeModel dtm = new DumpTreeModel(swfs); - dumpTree.setModel(dtm); - dumpTree.expandFirstLevelNodes(); - } - break; - case VIEW_RESOURCES: - if (tagTree.getModel() == null) { - TagTreeModel ttm = new TagTreeModel(swfs, Configuration.tagTreeShowEmptyFolders.get()); - tagTree.setModel(ttm); - tagTree.expandFirstLevelNodes(); - } - break; - } - } - - private JPanel createDumpViewCard() { - JPanel r = new JPanel(new BorderLayout()); - r.add(new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(dumpTree), dumpPreviewPanel, Configuration.guiDumpSplitPaneDividerLocationPercent), BorderLayout.CENTER); - return r; - } - - private JPanel createResourcesViewCard() { - JPanel r = new JPanel(new BorderLayout()); - r.add(new JScrollPane(tagTree), BorderLayout.CENTER); - r.add(searchPanel, BorderLayout.SOUTH); - return r; - } - - public boolean showView(int view) { - - CardLayout cl = (CardLayout) (contentPanel.getLayout()); - CardLayout cl2 = (CardLayout) (treePanel.getLayout()); - - setTreeModel(view); - switch (view) { - case VIEW_DUMP: - if (!isWelcomeScreen) { - cl.show(contentPanel, SPLIT_PANE1); - } - cl2.show(treePanel, DUMP_VIEW); - treePanelMode = TreePanelMode.DUMP_TREE; - showDetail(DETAILCARDEMPTYPANEL); - reload(true); - return true; - case VIEW_RESOURCES: - if (!isWelcomeScreen) { - cl.show(contentPanel, SPLIT_PANE1); - } - cl2.show(treePanel, RESOURCES_VIEW); - - treePanelMode = TreePanelMode.TAG_TREE; - - treePanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - tagTree.scrollPathToVisible(tagTree.getSelectionPath()); - } - }); - - reload(true); - return true; - case VIEW_TIMELINE: - final SWF swf = getCurrentSwf(); - if (swf != null) { - TreeItem item = tagTree.getCurrentTreeItem(); - if (item instanceof TagScript) { - item = ((TagScript) item).getTag(); - } - if (item instanceof Timelined) { - timelineViewPanel.setTimelined((Timelined) item); - } else if (item instanceof Frame) { - timelineViewPanel.setTimelined(((Frame) item).timeline.timelined); - } else { - timelineViewPanel.setTimelined(swf); - } - cl.show(contentPanel, TIMELINE_PANEL); - return true; - } - return false; - } - return false; - - } - - private void dumpViewReload(boolean forceReload) { - showDetail(DETAILCARDEMPTYPANEL); - - DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); - if (dumpInfo == null) { - showCard(CARDEMPTYPANEL); - return; - } - - dumpViewPanel.revalidate(); - dumpViewPanel.setSelectedNode(dumpInfo); - showCard(CARDDUMPVIEW); - } - - public void loadFromBinaryTag(final DefineBinaryDataTag binaryDataTag) { - loadFromBinaryTag(Arrays.asList(binaryDataTag)); - } - - public void loadFromBinaryTag(final List binaryDataTags) { - - Main.loadingDialog.setVisible(true); - new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - try { - for (DefineBinaryDataTag binaryDataTag : binaryDataTags) { - try { - InputStream is = new ByteArrayInputStream(binaryDataTag.binaryData.getRangeData()); - SWF bswf = new SWF(is, null, "(SWF Data)", new ProgressListener() { - @Override - public void progress(int p) { - Main.loadingDialog.setPercent(p); - } - }, Configuration.parallelSpeedUp.get()); - binaryDataTag.innerSwf = bswf; - bswf.binaryData = binaryDataTag; - } catch (IOException ex) { - //ignore - } - } - } catch (InterruptedException ex) { - //ignore - } - - return null; - } - - @Override - protected void onStart() { - Main.startWork(AppStrings.translate("work.reading.swf") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - Main.loadingDialog.setVisible(false); - Main.stopWork(); - }); - } - }.execute(); - } - - private void closeTag() { - previewPanel.closeTag(); - } - - public void showPreview(TreeItem treeItem, PreviewPanel previewPanel) { - previewPanel.clear(); - if (treeItem == null) { - previewPanel.showEmpty(); - return; - } - boolean internalViewer = isInternalFlashViewerSelected(); - if (treeItem instanceof SWF) { - SWF swf = (SWF) treeItem; - if (internalViewer) { - previewPanel.showImagePanel(swf, swf, -1); - } else { - previewPanel.setParametersPanelVisible(false); - if (flashPanel != null) { //same for flashPanel2 - previewPanel.showFlashViewerPanel(); - previewPanel.showSwf(swf); - } - } - } else if (treeItem instanceof MetadataTag) { - MetadataTag metadataTag = (MetadataTag) treeItem; - previewPanel.showMetaDataPanel(metadataTag); - } else if (treeItem instanceof DefineBinaryDataTag) { - DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) treeItem; - previewPanel.showBinaryPanel(binaryTag); - } else if (treeItem instanceof ImageTag) { - ImageTag imageTag = (ImageTag) treeItem; - previewPanel.setImageReplaceButtonVisible(!((Tag) imageTag).isReadOnly() && imageTag.importSupported(), imageTag instanceof DefineBitsJPEG3Tag || imageTag instanceof DefineBitsJPEG4Tag); - previewPanel.showImagePanel(imageTag.getImageCached()); - - } else if ((treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) { - final Tag tag = (Tag) treeItem; - DrawableTag d = (DrawableTag) tag; - Timelined timelined; - if (treeItem instanceof Timelined && !(treeItem instanceof ButtonTag)) { - timelined = (Timelined) tag; - } else { - timelined = makeTimelined(tag); - } - - previewPanel.setParametersPanelVisible(false); - previewPanel.showImagePanel(timelined, tag.getSwf(), -1); - } else if (treeItem instanceof Frame && internalViewer) { - Frame fn = (Frame) treeItem; - SWF swf = fn.getSwf(); - previewPanel.showImagePanel(fn.timeline.timelined, swf, fn.frame); - } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { - previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); - previewPanel.setImageReplaceButtonVisible(((Tag) treeItem).isReadOnly() && (treeItem instanceof DefineSoundTag), false); - try { - SoundTagPlayer soundThread = new SoundTagPlayer((SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true); - previewPanel.setMedia(soundThread); - } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { - logger.log(Level.SEVERE, null, ex); - } - - } else if ((treeItem instanceof FontTag) && internalViewer) { - previewPanel.showFontPanel((FontTag) treeItem); - } else if ((treeItem instanceof TextTag) && internalViewer) { - previewPanel.showTextPanel((TextTag) treeItem); - } else if ((treeItem instanceof Frame) || (treeItem instanceof CharacterTag) || (treeItem instanceof FontTag) || (treeItem instanceof SoundStreamHeadTypeTag)) { - previewPanel.createAndShowTempSwf(treeItem); - - if (treeItem instanceof TextTag) { - previewPanel.showTextPanel((TextTag) treeItem); - } else if (treeItem instanceof FontTag) { - previewPanel.showFontPanel((FontTag) treeItem); - } else { - previewPanel.setParametersPanelVisible(false); - } - } else { - previewPanel.showEmpty(); - } - } - - public void reload(boolean forceReload) { - tagTree.scrollPathToVisible(tagTree.getSelectionPath()); - if (Configuration.dumpView.get()) { - dumpViewReload(forceReload); - return; - } - - TreeItem treeItem = null; - TreePath treePath = tagTree.getSelectionPath(); - if (treePath != null && tagTree.getModel().treePathExists(treePath)) { - treeItem = (TreeItem) treePath.getLastPathComponent(); - } - - // save last selected node to config - if (treeItem != null && !(treeItem instanceof SWFList)) { - SWF swf = treeItem.getSwf(); - if (swf != null) { - swf = swf.getRootSwf(); - } - - if (swf != null) { - SwfSpecificConfiguration swfConf = Configuration.getOrCreateSwfSpecificConfiguration(swf.getShortFileName()); - swfConf.lastSelectedPath = tagTree.getSelectionPathString(); - } - } - - if (!forceReload && (treeItem == oldItem)) { - return; - } - - if (oldItem != treeItem) { - closeTag(); - } - - oldItem = treeItem; - - // show the preview of the tag when the user clicks to the tagname inside the scripts node, too - // this is a little bit inconsistent, beacuse the frames (FrameScript) are not shown - boolean preferScript = false; - if (treeItem instanceof TagScript) { - treeItem = ((TagScript) treeItem).getTag(); - preferScript = true; - } - - folderPreviewPanel.clear(); - previewPanel.clear(); - stopFlashPlayer(); - - previewPanel.setImageReplaceButtonVisible(false, false); - - boolean internalViewer = isInternalFlashViewerSelected(); - - if (treeItem instanceof ScriptPack) { - final ScriptPack scriptLeaf = (ScriptPack) treeItem; - if (setSourceWorker != null) { - setSourceWorker.cancel(true); - setSourceWorker = null; - } - if (!Main.isInited() || !Main.isWorking() || Main.isDebugging()) { - ABCPanel abcPanel = getABCPanel(); - CancellableWorker worker = new CancellableWorker() { - @Override - protected Void doInBackground() throws Exception { - abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); - abcPanel.setAbc(scriptLeaf.abc); - abcPanel.decompiledTextArea.setScript(scriptLeaf, true); - abcPanel.decompiledTextArea.setNoTrait(); - return null; - } - - @Override - protected void onStart() { - Main.startWork(translate("work.decompiling") + "...", this); - } - - @Override - protected void done() { - View.execInEventDispatch(() -> { - setSourceWorker = null; - try { - get(); - } catch (CancellationException ex) { - abcPanel.decompiledTextArea.setText("// " + AppStrings.translate("work.canceled")); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error", ex); - getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex); - } - - Main.stopWork(); - }); - } - }; - - worker.execute(); - setSourceWorker = worker; - } - - showDetail(DETAILCARDAS3NAVIGATOR); - showCard(CARDACTIONSCRIPT3PANEL); - return; - } - - if (treeItem instanceof Tag) { - Tag tag = (Tag) treeItem; - TagInfo tagInfo = new TagInfo(); - tag.getTagInfo(tagInfo); - if (!tagInfo.isEmpty()) { - tagInfoPanel.setTagInfos(tagInfo); - showDetail(DETAILCARDTAGINFO); - } else { - showDetail(DETAILCARDEMPTYPANEL); - } - } else { - showDetail(DETAILCARDEMPTYPANEL); - } - - if (treeItem instanceof HeaderItem) { - headerPanel.load(((HeaderItem) treeItem).getSwf()); - showCard(CARDHEADER); - } else if (treeItem instanceof FolderItem) { - showFolderPreview((FolderItem) treeItem); - } else if (treeItem instanceof SWF) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof MetadataTag) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof DefineBinaryDataTag) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof ASMSource && (!(treeItem instanceof DrawableTag) || preferScript)) { - getActionPanel().setSource((ASMSource) treeItem, !forceReload); - showCard(CARDACTIONSCRIPTPANEL); - } else if (treeItem instanceof ImageTag) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof FontTag) && internalViewer) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof TextTag) && internalViewer) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof Frame && internalViewer) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { - showPreview(treeItem, previewPanel); - showCard(CARDPREVIEWPANEL); - } else if ((treeItem instanceof Frame) || (treeItem instanceof CharacterTag) || (treeItem instanceof FontTag) || (treeItem instanceof SoundStreamHeadTypeTag)) { - showPreview(treeItem, previewPanel); - - showCard(CARDPREVIEWPANEL); - } else if (treeItem instanceof Tag) { - showGenericTag((Tag) treeItem); - } else { - showCard(CARDEMPTYPANEL); - } - } - - public void repaintTree() { - tagTree.repaint(); - reload(true); - } - - public void showGenericTag(Tag tag) { - previewPanel.showGenericTagPanel(tag); - showCard(CARDPREVIEWPANEL); - } - - public void showTextTagWithNewValue(TextTag textTag, TextTag newTextTag) { - - previewPanel.showTextComparePanel(textTag, newTextTag); - } - - private void showFolderPreview(FolderItem item) { - List folderPreviewItems = new ArrayList<>(); - String folderName = item.getName(); - SWF swf = item.swf; - switch (folderName) { - case TagTreeModel.FOLDER_SHAPES: - for (Tag tag : swf.getTags()) { - if (tag instanceof ShapeTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_MORPHSHAPES: - for (Tag tag : swf.getTags()) { - if (tag instanceof MorphShapeTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_SPRITES: - for (Tag tag : swf.getTags()) { - if (tag instanceof DefineSpriteTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_BUTTONS: - for (Tag tag : swf.getTags()) { - if (tag instanceof ButtonTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_FONTS: - for (Tag tag : swf.getTags()) { - if (tag instanceof FontTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_FRAMES: - for (Frame frame : swf.getTimeline().getFrames()) { - folderPreviewItems.add(frame); - } - break; - case TagTreeModel.FOLDER_IMAGES: - for (Tag tag : swf.getTags()) { - if (tag instanceof ImageTag) { - folderPreviewItems.add(tag); - } - } - break; - case TagTreeModel.FOLDER_TEXTS: - for (Tag tag : swf.getTags()) { - if (tag instanceof TextTag) { - folderPreviewItems.add(tag); - } - } - break; - } - - folderPreviewPanel.setItems(folderPreviewItems); - showCard(CARDFOLDERPREVIEWPANEL); - } - - private boolean isFreeing; - - @Override - public boolean isFreeing() { - return isFreeing; - } - - @Override - public void free() { - isFreeing = true; - } - - public void setErrorState(ErrorState errorState) { - statusPanel.setErrorState(errorState); - } - - public static Timelined makeTimelined(final Tag tag) { - return makeTimelined(tag, -1); - } - - public static Timelined makeTimelined(final Tag tag, final int fontFrameNum) { - - return new Timelined() { - private Timeline tim; - - @Override - public Timeline getTimeline() { - if (tim == null) { - Timeline timeline = new Timeline(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); - initTimeline(timeline); - tim = timeline; - } - - return tim; - } - - @Override - public void resetTimeline() { - if (tim != null) { - tim.reset(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); - initTimeline(tim); - } - } - - private void initTimeline(Timeline timeline) { - if (tag instanceof MorphShapeTag) { - timeline.frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; - int framesCnt = (int) (timeline.frameRate * MORPH_SHAPE_ANIMATION_LENGTH); - for (int i = 0; i < framesCnt; i++) { - Frame f = new Frame(timeline, i); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - ds.ratio = i * 65535 / framesCnt; - f.layers.put(1, ds); - f.layersChanged = true; - timeline.addFrame(f); - } - } else if (tag instanceof FontTag) { - int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); - int frame = fontFrameNum; - if (frame < 0 || frame >= pageCount) { - frame = 0; - } - - Frame f = new Frame(timeline, 0); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - f.layers.put(1, ds); - f.layersChanged = true; - timeline.addFrame(f); - timeline.fontFrameNum = frame; - } else { - Frame f = new Frame(timeline, 0); - DepthState ds = new DepthState(tag.getSwf(), f); - ds.characterId = ((CharacterTag) tag).getCharacterId(); - ds.matrix = new MATRIX(); - f.layers.put(1, ds); - timeline.addFrame(f); - } - timeline.displayRect = getRect(); - } - - @Override - public RECT getRect() { - return getRect(new HashSet<>()); - } - - @Override - public RECT getRect(Set added) { - BoundedTag bt = (BoundedTag) tag; - if (!added.contains(bt)) { - return bt.getRect(added); - } - return new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); - } - - @Override - public int hashCode() { - return tag.hashCode(); - } - - @Override - public void setModified(boolean value) { - } - - @Override - public ReadOnlyTagList getTags() { - return ReadOnlyTagList.EMPTY; - } - - @Override - public void removeTag(int index) { - } - - @Override - public void removeTag(Tag tag) { - } - - @Override - public void addTag(Tag tag) { - } - - @Override - public void addTag(int index, Tag tag) { - } - - @Override - public void replaceTag(int index, Tag newTag) { - } - }; - } - - private void disposeInner(Container container) { - for (Component c : container.getComponents()) { - if (c instanceof Container) { - Container c2 = (Container) c; - disposeInner(c2); - } - } - - container.removeAll(); - container.setLayout(null); - if (container instanceof TagEditorPanel) { - Helper.emptyObject(container); - } - } - - public void dispose() { - setDropTarget(null); - disposeInner(this); - Helper.emptyObject(this); - } -} +/* + * Copyright (C) 2010-2016 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; +import com.jpexs.decompiler.flash.ApplicationInfo; +import com.jpexs.decompiler.flash.EventListener; +import com.jpexs.decompiler.flash.ReadOnlyTagList; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.SWFBundle; +import com.jpexs.decompiler.flash.SWFSourceInfo; +import com.jpexs.decompiler.flash.abc.ABC; +import com.jpexs.decompiler.flash.abc.RenameType; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.AbcMultiNameCollisionFixer; +import com.jpexs.decompiler.flash.abc.avm2.deobfuscation.DeobfuscationLevel; +import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.configuration.Configuration; +import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import com.jpexs.decompiler.flash.configuration.SwfSpecificConfiguration; +import com.jpexs.decompiler.flash.dumpview.DumpInfo; +import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode; +import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; +import com.jpexs.decompiler.flash.exporters.FontExporter; +import com.jpexs.decompiler.flash.exporters.FrameExporter; +import com.jpexs.decompiler.flash.exporters.ImageExporter; +import com.jpexs.decompiler.flash.exporters.MorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.MovieExporter; +import com.jpexs.decompiler.flash.exporters.ShapeExporter; +import com.jpexs.decompiler.flash.exporters.SoundExporter; +import com.jpexs.decompiler.flash.exporters.SymbolClassExporter; +import com.jpexs.decompiler.flash.exporters.TextExporter; +import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ButtonExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; +import com.jpexs.decompiler.flash.exporters.modes.FrameExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ImageExportMode; +import com.jpexs.decompiler.flash.exporters.modes.MorphShapeExportMode; +import com.jpexs.decompiler.flash.exporters.modes.MovieExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; +import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SoundExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SpriteExportMode; +import com.jpexs.decompiler.flash.exporters.modes.SymbolClassExportMode; +import com.jpexs.decompiler.flash.exporters.modes.TextExportMode; +import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter; +import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter; +import com.jpexs.decompiler.flash.exporters.settings.BinaryDataExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ButtonExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FontExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.FrameExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ImageExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.MorphShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.MovieExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.SpriteExportSettings; +import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; +import com.jpexs.decompiler.flash.exporters.swf.SwfJavaExporter; +import com.jpexs.decompiler.flash.exporters.swf.SwfXmlExporter; +import com.jpexs.decompiler.flash.flexsdk.MxmlcAs3ScriptReplacer; +import com.jpexs.decompiler.flash.gui.abc.ABCPanel; +import com.jpexs.decompiler.flash.gui.abc.ABCPanelSearchResult; +import com.jpexs.decompiler.flash.gui.abc.ClassesListTreeModel; +import com.jpexs.decompiler.flash.gui.abc.DecompiledEditorPane; +import com.jpexs.decompiler.flash.gui.abc.DeobfuscationDialog; +import com.jpexs.decompiler.flash.gui.action.ActionPanel; +import com.jpexs.decompiler.flash.gui.action.ActionSearchResult; +import com.jpexs.decompiler.flash.gui.controls.JPersistentSplitPane; +import com.jpexs.decompiler.flash.gui.dumpview.DumpTree; +import com.jpexs.decompiler.flash.gui.dumpview.DumpTreeModel; +import com.jpexs.decompiler.flash.gui.dumpview.DumpViewPanel; +import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.gui.helpers.ObservableList; +import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel; +import com.jpexs.decompiler.flash.gui.tagtree.TagTree; +import com.jpexs.decompiler.flash.gui.tagtree.TagTreeModel; +import com.jpexs.decompiler.flash.gui.timeline.TimelineViewPanel; +import com.jpexs.decompiler.flash.helpers.FileTextWriter; +import com.jpexs.decompiler.flash.helpers.Freed; +import com.jpexs.decompiler.flash.importers.AS2ScriptImporter; +import com.jpexs.decompiler.flash.importers.AS3ScriptImporter; +import com.jpexs.decompiler.flash.importers.As3ScriptReplacerFactory; +import com.jpexs.decompiler.flash.importers.As3ScriptReplacerInterface; +import com.jpexs.decompiler.flash.importers.BinaryDataImporter; +import com.jpexs.decompiler.flash.importers.FFDecAs3ScriptReplacer; +import com.jpexs.decompiler.flash.importers.ImageImporter; +import com.jpexs.decompiler.flash.importers.ShapeImporter; +import com.jpexs.decompiler.flash.importers.SwfXmlImporter; +import com.jpexs.decompiler.flash.importers.SymbolClassImporter; +import com.jpexs.decompiler.flash.importers.TextImporter; +import com.jpexs.decompiler.flash.importers.svg.SvgImporter; +import com.jpexs.decompiler.flash.tags.ABCContainerTag; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.TagInfo; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.BoundedTag; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MissingCharacterHandler; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag; +import com.jpexs.decompiler.flash.tags.base.SoundTag; +import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextImportErrorHandler; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.text.TextAlign; +import com.jpexs.decompiler.flash.tags.text.TextParseException; +import com.jpexs.decompiler.flash.timeline.DepthState; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.Timelined; +import com.jpexs.decompiler.flash.treeitems.FolderItem; +import com.jpexs.decompiler.flash.treeitems.HeaderItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import com.jpexs.decompiler.flash.types.MATRIX; +import com.jpexs.decompiler.flash.types.RECT; +import com.jpexs.decompiler.flash.types.sound.SoundFormat; +import com.jpexs.decompiler.flash.xfl.FLAVersion; +import com.jpexs.helpers.CancellableWorker; +import com.jpexs.helpers.Helper; +import com.jpexs.helpers.Path; +import com.jpexs.helpers.ProgressListener; +import com.jpexs.helpers.SerializableImage; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Desktop; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JColorChooser; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.UIManager; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.filechooser.FileFilter; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeSelectionModel; +import javax.swing.tree.TreePath; +import jsyntaxpane.DefaultSyntaxKit; + +/** + * + * @author JPEXS + */ +public final class MainPanel extends JPanel implements TreeSelectionListener, SearchListener, Freed { + + private final MainFrame mainFrame; + + private final ObservableList swfs; + + private final JPanel welcomePanel; + + private final TimelineViewPanel timelineViewPanel; + + private final MainFrameStatusPanel statusPanel; + + private final MainFrameMenu mainMenu; + + private final JProgressBar progressBar = new JProgressBar(0, 100); + + public TagTree tagTree; + + public DumpTree dumpTree; + + private final FlashPlayerPanel flashPanel; + + private final FlashPlayerPanel flashPanel2; + + private final JPanel contentPanel; + + private final JPanel displayPanel; + + public FolderPreviewPanel folderPreviewPanel; + + private boolean isWelcomeScreen = true; + + private static final String CARDPREVIEWPANEL = "Preview card"; + + private static final String CARDFOLDERPREVIEWPANEL = "Folder preview card"; + + private static final String CARDEMPTYPANEL = "Empty card"; + + private static final String CARDDUMPVIEW = "Dump view"; + + private static final String CARDACTIONSCRIPTPANEL = "ActionScript card"; + + private static final String CARDACTIONSCRIPT3PANEL = "ActionScript3 card"; + + private static final String CARDHEADER = "Header card"; + + private static final String DETAILCARDAS3NAVIGATOR = "Traits list"; + + private static final String DETAILCARDTAGINFO = "Tag information"; + + private static final String DETAILCARDEMPTYPANEL = "Empty card"; + + private static final String SPLIT_PANE1 = "SPLITPANE1"; + + private static final String WELCOME_PANEL = "WELCOMEPANEL"; + + private static final String TIMELINE_PANEL = "TIMELINEPANEL"; + + private static final String RESOURCES_VIEW = "RESOURCES"; + + private static final String DUMP_VIEW = "DUMP"; + + private static final String TIMELINE_VIEW = "TIMELINE"; + + private final JPersistentSplitPane splitPane1; + + private final JPersistentSplitPane splitPane2; + + private JPanel detailPanel; + + private JTextField filterField = new MyTextField(""); + + private JPanel searchPanel; + + private ABCPanel abcPanel; + + private ActionPanel actionPanel; + + private final PreviewPanel previewPanel; + + private final HeaderInfoPanel headerPanel; + + private DumpViewPanel dumpViewPanel; + + private final JPanel treePanel; + + private final PreviewPanel dumpPreviewPanel; + + private final TagInfoPanel tagInfoPanel; + + private TreePanelMode treePanelMode; + + private CancellableWorker setSourceWorker; + + public TreeItem oldItem; + + // play morph shape in 2 second(s) + public static final int MORPH_SHAPE_ANIMATION_LENGTH = 2; + + public static final int MORPH_SHAPE_ANIMATION_FRAME_RATE = 30; + + private static final Logger logger = Logger.getLogger(MainPanel.class.getName()); + + public void setPercent(int percent) { + progressBar.setValue(percent); + progressBar.setVisible(true); + } + + public void hidePercent() { + if (progressBar.isVisible()) { + progressBar.setVisible(false); + } + } + + public MainFrame getMainFrame() { + return mainFrame; + } + + static { + try { + File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + public void updateMenu() { + mainMenu.updateComponents(); + } + + private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) { + tabbedPane.add(tab); + + JLabel lbl = new JLabel(title); + lbl.setIcon(icon); + lbl.setIconTextGap(5); + lbl.setHorizontalTextPosition(SwingConstants.RIGHT); + + tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl); + } + + public void setStatus(String s) { + statusPanel.setStatus(s); + } + + public void setWorkStatus(String s, CancellableWorker worker) { + statusPanel.setWorkStatus(s, worker); + mainMenu.updateComponents(); + } + + private JPanel createWelcomePanel() { + JPanel welcomePanel = new JPanel(); + welcomePanel.setLayout(new BoxLayout(welcomePanel, BoxLayout.Y_AXIS)); + JLabel welcomeToLabel = new JLabel(translate("startup.welcometo")); + welcomeToLabel.setFont(welcomeToLabel.getFont().deriveFont(40)); + welcomeToLabel.setAlignmentX(0.5f); + JPanel appNamePanel = new JPanel(new FlowLayout()); + JLabel jpLabel = new JLabel("JPEXS "); + jpLabel.setAlignmentX(0.5f); + jpLabel.setForeground(new Color(0, 0, 160)); + jpLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + jpLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(jpLabel); + + JLabel ffLabel = new JLabel("Free Flash "); + ffLabel.setAlignmentX(0.5f); + ffLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + ffLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(ffLabel); + + JLabel decLabel = new JLabel("Decompiler"); + decLabel.setAlignmentX(0.5f); + decLabel.setForeground(Color.red); + decLabel.setFont(new Font("Tahoma", Font.BOLD, 50)); + decLabel.setHorizontalAlignment(SwingConstants.CENTER); + appNamePanel.add(decLabel); + appNamePanel.setAlignmentX(0.5f); + welcomePanel.add(Box.createGlue()); + welcomePanel.add(welcomeToLabel); + welcomePanel.add(appNamePanel); + JLabel startLabel = new JLabel(translate("startup.selectopen")); + startLabel.setAlignmentX(0.5f); + startLabel.setFont(startLabel.getFont().deriveFont(30)); + welcomePanel.add(startLabel); + welcomePanel.add(Box.createGlue()); + return welcomePanel; + } + + private JPanel createFolderPreviewCard() { + JPanel folderPreviewCard = new JPanel(new BorderLayout()); + folderPreviewPanel = new FolderPreviewPanel(this, new ArrayList<>()); + folderPreviewCard.add(new JScrollPane(folderPreviewPanel), BorderLayout.CENTER); + + return folderPreviewCard; + } + + private JPanel createDumpPreviewCard() { + JPanel dumpViewCard = new JPanel(new BorderLayout()); + dumpViewPanel = new DumpViewPanel(dumpTree); + dumpViewCard.add(new JScrollPane(dumpViewPanel), BorderLayout.CENTER); + + return dumpViewCard; + } + + public String translate(String key) { + return mainFrame.translate(key); + } + + public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel, FlashPlayerPanel previewFlashPanel) { + super(); + + this.mainFrame = mainFrame; + this.mainMenu = mainMenu; + this.flashPanel = flashPanel; + this.flashPanel2 = previewFlashPanel; + + mainFrame.setTitle(ApplicationInfo.applicationVerName); + + setLayout(new BorderLayout()); + swfs = new ObservableList<>(); + + detailPanel = new JPanel(); + detailPanel.setLayout(new CardLayout()); + + JPanel whitePanel = new JPanel(); + whitePanel.setBackground(Color.white); + detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); + + tagInfoPanel = new TagInfoPanel(this); + detailPanel.add(tagInfoPanel, DETAILCARDTAGINFO); + + UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); + tagTree = new TagTree(null, this); + tagTree.addTreeSelectionListener(this); + tagTree.setSelectionModel(new DefaultTreeSelectionModel() { + private boolean isModified() { + if (abcPanel != null && abcPanel.isEditing()) { + abcPanel.tryAutoSave(); + } + + if (actionPanel != null && actionPanel.isEditing()) { + actionPanel.tryAutoSave(); + } + + if (previewPanel.isEditing()) { + previewPanel.tryAutoSave(); + } + + if (headerPanel.isEditing()) { + headerPanel.tryAutoSave(); + } + + return (abcPanel != null && abcPanel.isEditing()) + || (actionPanel != null && actionPanel.isEditing()) + || previewPanel.isEditing() || headerPanel.isEditing(); + } + + @Override + public void addSelectionPath(TreePath path) { + if (isModified()) { + return; + } + + super.addSelectionPath(path); + } + + @Override + public void addSelectionPaths(TreePath[] paths) { + if (isModified()) { + return; + } + + super.addSelectionPaths(paths); + } + + @Override + public void setSelectionPath(TreePath path) { + if (isModified()) { + return; + } + + super.setSelectionPath(path); + } + + @Override + public void setSelectionPaths(TreePath[] pPaths) { + if (isModified()) { + return; + } + + super.setSelectionPaths(pPaths); + } + + @Override + public void clearSelection() { + if (isModified()) { + return; + } + + super.clearSelection(); + } + + public void setSelection(TreePath[] selection) { + if (isModified()) { + return; + } + + this.selection = selection; + } + + @Override + public void removeSelectionPath(TreePath path) { + if (isModified()) { + return; + } + + super.removeSelectionPath(path); + } + + @Override + public void removeSelectionPaths(TreePath[] paths) { + if (isModified()) { + return; + } + + super.removeSelectionPaths(paths); + } + }); + + DragSource dragSource = DragSource.getDefaultDragSource(); + dragSource.createDefaultDragGestureRecognizer(tagTree, DnDConstants.ACTION_COPY_OR_MOVE, new DragGestureListener() { + @Override + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(DragSource.DefaultCopyDrop, new Transferable() { + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{DataFlavor.javaFileListFlavor}; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(DataFlavor.javaFileListFlavor); + } + + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { + if (flavor.equals(DataFlavor.javaFileListFlavor)) { + List files; + String tempDir = System.getProperty("java.io.tmpdir"); + if (!tempDir.endsWith(File.separator)) { + tempDir += File.separator; + } + Random rnd = new Random(); + tempDir += "ffdec" + File.separator + "export" + File.separator + System.currentTimeMillis() + "_" + rnd.nextInt(1000); + File fTempDir = new File(tempDir); + Path.createDirectorySafe(fTempDir); + + File ftemp = new File(tempDir); + ExportDialog exd = new ExportDialog(null); + try { + files = exportSelection(new GuiAbortRetryIgnoreHandler(), tempDir, exd); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + return null; + } + + files.clear(); + + File[] fs = ftemp.listFiles(); + files.addAll(Arrays.asList(fs)); + + Main.stopWork(); + + for (File f : files) { + f.deleteOnExit(); + } + new File(tempDir).deleteOnExit(); + return files; + + } + return null; + } + }, new DragSourceListener() { + @Override + public void dragEnter(DragSourceDragEvent dsde) { + enableDrop(false); + } + + @Override + public void dragOver(DragSourceDragEvent dsde) { + } + + @Override + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + @Override + public void dragExit(DragSourceEvent dse) { + } + + @Override + public void dragDropEnd(DragSourceDropEvent dsde) { + enableDrop(true); + } + }); + } + }); + + tagTree.createContextMenu(); + + dumpTree = new DumpTree(null, this); + dumpTree.addTreeSelectionListener(this); + dumpTree.createContextMenu(); + + statusPanel = new MainFrameStatusPanel(this); + add(statusPanel, BorderLayout.SOUTH); + + displayPanel = new JPanel(new CardLayout()); + + DefaultSyntaxKit.initKit(); + previewPanel = new PreviewPanel(this, flashPanel); + + dumpPreviewPanel = new PreviewPanel(this, previewFlashPanel); + dumpPreviewPanel.setReadOnly(true); + + displayPanel.add(previewPanel, CARDPREVIEWPANEL); + displayPanel.add(createFolderPreviewCard(), CARDFOLDERPREVIEWPANEL); + displayPanel.add(createDumpPreviewCard(), CARDDUMPVIEW); + + headerPanel = new HeaderInfoPanel(); + displayPanel.add(headerPanel, CARDHEADER); + + displayPanel.add(new JPanel(), CARDEMPTYPANEL); + showCard(CARDEMPTYPANEL); + + searchPanel = new JPanel(); + searchPanel.setLayout(new BorderLayout()); + searchPanel.add(filterField, BorderLayout.CENTER); + searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST); + JLabel closeSearchButton = new JLabel(View.getIcon("cancel16")); + closeSearchButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + closeTagTreeSearch(); + } + }); + searchPanel.add(closeSearchButton, BorderLayout.EAST); + searchPanel.setVisible(false); + treePanel = new JPanel(new CardLayout()); + treePanel.add(createResourcesViewCard(), RESOURCES_VIEW); + treePanel.add(createDumpViewCard(), DUMP_VIEW); + //treePanel.add(searchPanel, BorderLayout.SOUTH); + //searchPanel.setVisible(false); + filterField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + warn(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + warn(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + warn(); + } + + public void warn() { + doFilter(); + } + }); + + //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black)); + splitPane2 = new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, treePanel, detailPanel, Configuration.guiSplitPane2DividerLocationPercent); + splitPane1 = new JPersistentSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel, Configuration.guiSplitPane1DividerLocationPercent); + + welcomePanel = createWelcomePanel(); + add(welcomePanel, BorderLayout.CENTER); + + timelineViewPanel = new TimelineViewPanel(); + + CardLayout cl3 = new CardLayout(); + contentPanel = new JPanel(cl3); + contentPanel.add(welcomePanel, WELCOME_PANEL); + contentPanel.add(splitPane1, SPLIT_PANE1); + contentPanel.add(timelineViewPanel, TIMELINE_PANEL); + add(contentPanel); + cl3.show(contentPanel, WELCOME_PANEL); + + tagTree.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if ((e.getKeyCode() == 'F') && (e.isControlDown())) { + searchPanel.setVisible(true); + filterField.requestFocusInWindow(); + } + } + }); + detailPanel.setVisible(false); + + updateUi(); + + this.swfs.addCollectionChangedListener((e) -> { + TagTreeModel ttm = tagTree.getModel(); + if (ttm != null) { + if (getCurrentSwf() == null) { + tagTree.setSelectionPath(ttm.getTreePath(ttm.getRoot())); + } + ttm.updateSwfs(e); + tagTree.expandRoot(); + tagTree.expandFirstLevelNodes(); + } + + DumpTreeModel dtm = dumpTree.getModel(); + if (dtm != null) { + List> expandedNodes = View.getExpandedNodes(dumpTree); + dtm.updateSwfs(); + View.expandTreeNodes(dumpTree, expandedNodes); + dumpTree.expandRoot(); + dumpTree.expandFirstLevelNodes(); + } + + if (swfs.isEmpty()) { + tagTree.setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); + dumpTree.setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); + } + }); + + //Opening files with drag&drop to main window + enableDrop(true); + } + + public void closeTagTreeSearch() { + filterField.setText(""); + doFilter(); + searchPanel.setVisible(false); + } + + public void loadSwfAtPos(SWFList newSwfs, int index) { + previewPanel.clear(); + swfs.set(index, newSwfs); + SWF swf = newSwfs.size() > 0 ? newSwfs.get(0) : null; + if (swf != null) { + updateUi(swf); + } + + doFilter(); + reload(false); + } + + public void load(SWFList newSwfs, boolean first) { + + previewPanel.clear(); + + swfs.add(newSwfs); + SWF swf = newSwfs.size() > 0 ? newSwfs.get(0) : null; + if (swf != null) { + updateUi(swf); + } + + doFilter(); + reload(false); + } + + private ABCPanel getABCPanel() { + if (abcPanel == null) { + abcPanel = new ABCPanel(this); + displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); + detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR); + } + + return abcPanel; + } + + private ActionPanel getActionPanel() { + if (actionPanel == null) { + actionPanel = new ActionPanel(MainPanel.this); + displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL); + } + + return actionPanel; + } + + private void updateUi(final SWF swf) { + + mainFrame.setTitle(ApplicationInfo.applicationVerName + (Configuration.displayFileName.get() ? " - " + swf.getFileTitle() : "")); + + List abcList = swf.getAbcList(); + + boolean hasAbc = !abcList.isEmpty(); + + if (hasAbc) { + getABCPanel().setAbc(abcList.get(0).getABC()); + } + + if (isWelcomeScreen) { + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + cl.show(contentPanel, SPLIT_PANE1); + isWelcomeScreen = false; + } + + mainMenu.updateComponents(swf); + } + + private void updateUi() { + if (!isWelcomeScreen && swfs.isEmpty()) { + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + cl.show(contentPanel, WELCOME_PANEL); + isWelcomeScreen = true; + closeTagTreeSearch(); + } + + mainFrame.setTitle(ApplicationInfo.applicationVerName); + mainMenu.updateComponents(null); + + showView(getCurrentView()); + } + + private boolean closeConfirmation(SWFList swfList) { + String message = swfList == null + ? translate("message.confirm.closeAll") + : translate("message.confirm.close").replace("{swfName}", swfList.toString()); + + return View.showConfirmDialog(this, message, translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, Configuration.showCloseConfirmation, JOptionPane.OK_OPTION) == JOptionPane.OK_OPTION; + } + + public boolean isModified() { + for (SWFList swfList : swfs) { + for (SWF swf : swfList) { + if (swf.isModified()) { + return true; + } + } + } + + return false; + } + + public boolean closeAll(boolean showCloseConfirmation) { + if (showCloseConfirmation && isModified()) { + boolean closeConfirmResult = closeConfirmation(swfs.size() == 1 ? swfs.get(0) : null); + if (!closeConfirmResult) { + return false; + } + } + + List swfsLists = new ArrayList<>(swfs); + swfs.clear(); + oldItem = null; + clear(); + updateUi(); + + for (SWFList swfList : swfsLists) { + List swfs2 = new ArrayList<>(swfList); + for (SWF swf : swfs2) { + swf.clearTagSwfs(); + } + } + + return true; + } + + public boolean close(SWFList swfList) { + boolean modified = false; + for (SWF swf : swfList) { + if (swf.isModified()) { + modified = true; + } + } + + if (modified) { + boolean closeConfirmResult = closeConfirmation(swfList); + if (!closeConfirmResult) { + return false; + } + } + + swfs.remove(swfList); + oldItem = null; + clear(); + updateUi(); + + List swfs2 = new ArrayList<>(swfList); + for (SWF swf : swfs2) { + swf.clearTagSwfs(); + } + + return true; + } + + public void enableDrop(boolean value) { + if (value) { + setDropTarget(new DropTarget() { + @Override + public synchronized void drop(DropTargetDropEvent dtde) { + try { + dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); + @SuppressWarnings("unchecked") + List droppedFiles = (List) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); + if (!droppedFiles.isEmpty()) { + SWFSourceInfo[] sourceInfos = new SWFSourceInfo[droppedFiles.size()]; + for (int i = 0; i < droppedFiles.size(); i++) { + sourceInfos[i] = new SWFSourceInfo(null, droppedFiles.get(i).getAbsolutePath(), null); + } + Main.openFile(sourceInfos, null); + } + } catch (UnsupportedFlavorException | IOException ex) { + } + } + }); + } else { + setDropTarget(null); + } + } + + public void updateClassesList() { + List nodes = getASTreeNodes(tagTree); + boolean updateNeeded = false; + for (TreeItem n : nodes) { + if (n instanceof ClassesListTreeModel) { + ((ClassesListTreeModel) n).update(); + updateNeeded = true; + } + } + + refreshTree(); + + if (updateNeeded) { + tagTree.updateUI(); + } + } + + public void doFilter() { + List nodes = getASTreeNodes(tagTree); + for (TreeItem n : nodes) { + if (n instanceof ClassesListTreeModel) { + String filterText = filterField.getText(); + ((ClassesListTreeModel) n).setFilter(filterText); + TagTreeModel tm = tagTree.getModel(); + TreePath path = tm.getTreePath(n); + tm.updateNode(path); + if (!filterText.isEmpty()) { + View.expandTreeNodes(tagTree, path, true); + } + } + } + } + + public void renameIdentifier(SWF swf, String identifier) throws InterruptedException { + String oldName = identifier; + String newName = View.showInputDialog(translate("rename.enternew"), oldName); + if (newName != null) { + if (!oldName.equals(newName)) { + swf.renameAS2Identifier(oldName, newName); + View.showMessageDialog(null, translate("rename.finished.identifier")); + updateClassesList(); + reload(true); + } + } + } + + public void renameMultiname(List abcList, int multiNameIndex) { + String oldName = ""; + AVM2ConstantPool constants = getABCPanel().abc.constants; + if (constants.getMultiname(multiNameIndex).name_index > 0) { + oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index); + } + + String newName = View.showInputDialog(translate("rename.enternew"), oldName); + if (newName != null) { + if (!oldName.equals(newName)) { + int mulCount = 0; + for (ABCContainerTag cnt : abcList) { + ABC abc = cnt.getABC(); + for (int m = 1; m < abc.constants.getMultinameCount(); m++) { + int ni = abc.constants.getMultiname(m).name_index; + String n = ""; + if (ni > 0) { + n = abc.constants.getString(ni); + } + if (n.equals(oldName)) { + abc.renameMultiname(m, newName); + mulCount++; + } + } + } + + View.showMessageDialog(null, translate("rename.finished.multiname").replace("%count%", Integer.toString(mulCount))); + if (abcPanel != null) { + abcPanel.reload(); + } + + updateClassesList(); + reload(true); + ABCPanel abcPanel = getABCPanel(); + abcPanel.hilightScript(abcPanel.getSwf(), abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toRawString()); + } + } + } + + public List getASTreeNodes(TagTree tree) { + List result = new ArrayList<>(); + TagTreeModel tm = (TagTreeModel) tree.getModel(); + if (tm == null) { + return result; + } + TreeItem root = tm.getRoot(); + for (int i = 0; i < tm.getChildCount(root); i++) { + // first level node can be SWF and SWFBundle + TreeItem node = tm.getChild(root, i); + if (node instanceof SWFBundle) { + for (int j = 0; j < tm.getChildCount(node); j++) { + // child of SWFBundle should be SWF + SWF swfNode = (SWF) tm.getChild(node, j); + result.add(tm.getScriptsNode(swfNode)); + } + } else if (node instanceof SWF) { + SWF swfNode = (SWF) tm.getChild(root, i); + result.add(tm.getScriptsNode(swfNode)); + } + } + return result; + } + + public boolean confirmExperimental() { + return View.showConfirmDialog(null, translate("message.confirm.experimental"), translate("message.warning"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; + } + + public List exportSelection(AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { + + List ret = new ArrayList<>(); + List sel = folderPreviewPanel.selectedItems.isEmpty() ? tagTree.getAllSelected() : new ArrayList<>(folderPreviewPanel.selectedItems.values()); + + Set usedSwfs = new HashSet<>(); + for (TreeItem d : sel) { + SWF selectedNodeSwf = d.getSwf(); + if (!usedSwfs.contains(selectedNodeSwf)) { + usedSwfs.add(selectedNodeSwf); + } + } + + Map usedSwfsIds = new HashMap<>(); + for (SWF swf : usedSwfs) { + List as3scripts = new ArrayList<>(); + List images = new ArrayList<>(); + List shapes = new ArrayList<>(); + List morphshapes = new ArrayList<>(); + List buttons = new ArrayList<>(); + List movies = new ArrayList<>(); + List sounds = new ArrayList<>(); + List texts = new ArrayList<>(); + List as12scripts = new ArrayList<>(); + List binaryData = new ArrayList<>(); + Map> frames = new HashMap<>(); + List fonts = new ArrayList<>(); + List symbolNames = new ArrayList<>(); + + for (TreeItem d : sel) { + SWF selectedNodeSwf = d.getSwf(); + + if (selectedNodeSwf != swf) { + continue; + } + + if (d instanceof TagScript) { + Tag tag = ((TagScript) d).getTag(); + if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { + as12scripts.add(d); + } + } + + if (d instanceof Tag || d instanceof ASMSource) { + TreeNodeType nodeType = TagTree.getTreeNodeType(d); + if (nodeType == TreeNodeType.IMAGE) { + images.add((Tag) d); + } + if (nodeType == TreeNodeType.SHAPE) { + shapes.add((Tag) d); + } + if (nodeType == TreeNodeType.BUTTON) { + buttons.add((Tag) d); + } + if (nodeType == TreeNodeType.MORPH_SHAPE) { + morphshapes.add((Tag) d); + } + if (nodeType == TreeNodeType.AS) { + as12scripts.add(d); + } + if (nodeType == TreeNodeType.MOVIE) { + movies.add((Tag) d); + } + if (nodeType == TreeNodeType.SOUND) { + sounds.add((Tag) d); + } + if (nodeType == TreeNodeType.BINARY_DATA) { + binaryData.add((Tag) d); + } + if (nodeType == TreeNodeType.TEXT) { + texts.add((Tag) d); + } + if (nodeType == TreeNodeType.FONT) { + fonts.add((Tag) d); + } + if (nodeType == TreeNodeType.OTHER_TAG) { + if (d instanceof SymbolClassTypeTag) { + symbolNames.add((Tag) d); + } + } + } + + if (d instanceof Frame) { + Frame fn = (Frame) d; + Timelined parent = fn.timeline.timelined; + int frame = fn.frame; + int parentId = 0; + if (parent instanceof CharacterTag) { + parentId = ((CharacterTag) parent).getCharacterId(); + } + if (!frames.containsKey(parentId)) { + frames.put(parentId, new ArrayList<>()); + } + frames.get(parentId).add(frame); + } + if (d instanceof ScriptPack) { + as3scripts.add((ScriptPack) d); + } + } + + String selFile2; + if (usedSwfs.size() > 1) { + selFile2 = selFile + File.separator + Helper.getNextId(swf.getShortFileName(), usedSwfsIds); + } else { + selFile2 = selFile; + } + + EventListener evl = swf.getExportEventListener(); + + if (export.isOptionEnabled(ImageExportMode.class)) { + ret.addAll(new ImageExporter().exportImages(handler, selFile2 + File.separator + ImageExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(images), + new ImageExportSettings(export.getValue(ImageExportMode.class)), evl)); + } + + if (export.isOptionEnabled(ShapeExportMode.class)) { + ret.addAll(new ShapeExporter().exportShapes(handler, selFile2 + File.separator + ShapeExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(shapes), + new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl)); + } + + if (export.isOptionEnabled(MorphShapeExportMode.class)) { + ret.addAll(new MorphShapeExporter().exportMorphShapes(handler, selFile2 + File.separator + MorphShapeExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(morphshapes), + new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl)); + } + + if (export.isOptionEnabled(TextExportMode.class)) { + ret.addAll(new TextExporter().exportTexts(handler, selFile2 + File.separator + TextExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(texts), + new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl)); + } + + if (export.isOptionEnabled(MovieExportMode.class)) { + ret.addAll(new MovieExporter().exportMovies(handler, selFile2 + File.separator + MovieExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(movies), + new MovieExportSettings(export.getValue(MovieExportMode.class)), evl)); + } + + if (export.isOptionEnabled(SoundExportMode.class)) { + ret.addAll(new SoundExporter().exportSounds(handler, selFile2 + File.separator + SoundExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(sounds), + new SoundExportSettings(export.getValue(SoundExportMode.class)), evl)); + } + + if (export.isOptionEnabled(BinaryDataExportMode.class)) { + ret.addAll(new BinaryDataExporter().exportBinaryData(handler, selFile2 + File.separator + BinaryDataExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(binaryData), + new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl)); + } + + if (export.isOptionEnabled(FontExportMode.class)) { + ret.addAll(new FontExporter().exportFonts(handler, selFile2 + File.separator + FontExportSettings.EXPORT_FOLDER_NAME, new ReadOnlyTagList(fonts), + new FontExportSettings(export.getValue(FontExportMode.class)), evl)); + } + + if (export.isOptionEnabled(SymbolClassExportMode.class)) { + ret.addAll(new SymbolClassExporter().exportNames(selFile2, new ReadOnlyTagList(symbolNames), evl)); + } + + FrameExporter frameExporter = new FrameExporter(); + + if (export.isOptionEnabled(FrameExportMode.class)) { + FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); + for (Entry> entry : frames.entrySet()) { + int containerId = entry.getKey(); + if (containerId == 0) { + String subFolder = FrameExportSettings.EXPORT_FOLDER_NAME; + ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, containerId, entry.getValue(), fes, evl)); + } + } + } + + if (export.isOptionEnabled(SpriteExportMode.class)) { + SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); + for (Entry> entry : frames.entrySet()) { + int containerId = entry.getKey(); + if (containerId != 0) { + String subFolder = SpriteExportSettings.EXPORT_FOLDER_NAME; + ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, containerId, entry.getValue(), ses, evl)); + } + } + } + + if (export.isOptionEnabled(ButtonExportMode.class)) { + ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); + for (Tag tag : buttons) { + ButtonTag button = (ButtonTag) tag; + String subFolder = ButtonExportSettings.EXPORT_FOLDER_NAME; + List frameNums = new ArrayList<>(); + frameNums.add(0); // todo: export all frames + ret.addAll(frameExporter.exportFrames(handler, selFile2 + File.separator + subFolder, swf, button.getCharacterId(), frameNums, bes, evl)); + } + } + + if (export.isOptionEnabled(ScriptExportMode.class)) { + if (as3scripts.size() > 0 || as12scripts.size() > 0) { + boolean parallel = Configuration.parallelSpeedUp.get(); + String scriptsFolder = Path.combine(selFile2, ScriptExportSettings.EXPORT_FOLDER_NAME); + Path.createDirectorySafe(new File(scriptsFolder)); + boolean singleScriptFile = Configuration.scriptExportSingleFile.get(); + if (parallel && singleScriptFile) { + logger.log(Level.WARNING, AppStrings.translate("export.script.singleFilePallelModeWarning")); + singleScriptFile = false; + } + + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile); + String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); + try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + scriptExportSettings.singleFileWriter = writer; + if (swf.isAS3()) { + ret.addAll(new AS3ScriptExporter().exportActionScript3(swf, handler, scriptsFolder, as3scripts, scriptExportSettings, parallel, evl)); + } else { + Map asmsToExport = swf.getASMs(true, as12scripts, false); + ret.addAll(new AS2ScriptExporter().exportAS2Scripts(handler, scriptsFolder, asmsToExport, scriptExportSettings, parallel, evl)); + } + } + } + } + } + + return ret; + } + + public void exportAll(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { + boolean exportAll = false; + if (exportAll) { + exportAllDebug(swf, handler, selFile, export); + return; + } + + EventListener evl = swf.getExportEventListener(); + + if (export.isOptionEnabled(ImageExportMode.class)) { + new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new ImageExportSettings(export.getValue(ImageExportMode.class)), evl); + } + + if (export.isOptionEnabled(ShapeExportMode.class)) { + new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new ShapeExportSettings(export.getValue(ShapeExportMode.class), export.getZoom()), evl); + } + + if (export.isOptionEnabled(MorphShapeExportMode.class)) { + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new MorphShapeExportSettings(export.getValue(MorphShapeExportMode.class), export.getZoom()), evl); + } + + if (export.isOptionEnabled(TextExportMode.class)) { + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new TextExportSettings(export.getValue(TextExportMode.class), Configuration.textExportSingleFile.get(), export.getZoom()), evl); + } + + if (export.isOptionEnabled(MovieExportMode.class)) { + new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new MovieExportSettings(export.getValue(MovieExportMode.class)), evl); + } + + if (export.isOptionEnabled(SoundExportMode.class)) { + new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new SoundExportSettings(export.getValue(SoundExportMode.class)), evl); + } + + if (export.isOptionEnabled(BinaryDataExportMode.class)) { + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new BinaryDataExportSettings(export.getValue(BinaryDataExportMode.class)), evl); + } + + if (export.isOptionEnabled(FontExportMode.class)) { + new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME), swf.getTags(), + new FontExportSettings(export.getValue(FontExportMode.class)), evl); + } + + if (export.isOptionEnabled(SymbolClassExportMode.class)) { + new SymbolClassExporter().exportNames(selFile, swf.getTags(), evl); + } + + FrameExporter frameExporter = new FrameExporter(); + + if (export.isOptionEnabled(FrameExportMode.class)) { + FrameExportSettings fes = new FrameExportSettings(export.getValue(FrameExportMode.class), export.getZoom()); + frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME), swf, 0, null, fes, evl); + } + + if (export.isOptionEnabled(SpriteExportMode.class)) { + SpriteExportSettings ses = new SpriteExportSettings(export.getValue(SpriteExportMode.class), export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof DefineSpriteTag) { + frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), null, ses, evl); + } + } + } + + if (export.isOptionEnabled(ButtonExportMode.class)) { + ButtonExportSettings bes = new ButtonExportSettings(export.getValue(ButtonExportMode.class), export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof ButtonTag) { + List frameNums = new ArrayList<>(); + frameNums.add(0); // todo: export all frames + frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME), swf, c.getCharacterId(), frameNums, bes, evl); + } + } + } + + if (export.isOptionEnabled(ScriptExportMode.class)) { + boolean parallel = Configuration.parallelSpeedUp.get(); + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); + Path.createDirectorySafe(new File(scriptsFolder)); + boolean singleScriptFile = Configuration.scriptExportSingleFile.get(); + if (parallel && singleScriptFile) { + logger.log(Level.WARNING, AppStrings.translate("export.script.singleFilePallelModeWarning")); + singleScriptFile = false; + } + + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(export.getValue(ScriptExportMode.class), singleScriptFile); + String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); + try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + scriptExportSettings.singleFileWriter = writer; + swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); + } + } + } + + public void exportAllDebug(SWF swf, AbortRetryIgnoreHandler handler, String selFile, ExportDialog export) throws IOException, InterruptedException { + EventListener evl = swf.getExportEventListener(); + + if (export.isOptionEnabled(ImageExportMode.class)) { + for (ImageExportMode exportMode : ImageExportMode.values()) { + new ImageExporter().exportImages(handler, Path.combine(selFile, ImageExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new ImageExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(ShapeExportMode.class)) { + for (ShapeExportMode exportMode : ShapeExportMode.values()) { + new ShapeExporter().exportShapes(handler, Path.combine(selFile, ShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new ShapeExportSettings(exportMode, export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(MorphShapeExportMode.class)) { + for (MorphShapeExportMode exportMode : MorphShapeExportMode.values()) { + new MorphShapeExporter().exportMorphShapes(handler, Path.combine(selFile, MorphShapeExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new MorphShapeExportSettings(exportMode, export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(TextExportMode.class)) { + for (TextExportMode exportMode : TextExportMode.values()) { + new TextExporter().exportTexts(handler, Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new TextExportSettings(exportMode, Configuration.textExportSingleFile.get(), export.getZoom()), evl); + } + } + + if (export.isOptionEnabled(MovieExportMode.class)) { + for (MovieExportMode exportMode : MovieExportMode.values()) { + new MovieExporter().exportMovies(handler, Path.combine(selFile, MovieExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new MovieExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(SoundExportMode.class)) { + for (SoundExportMode exportMode : SoundExportMode.values()) { + new SoundExporter().exportSounds(handler, Path.combine(selFile, SoundExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new SoundExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(BinaryDataExportMode.class)) { + for (BinaryDataExportMode exportMode : BinaryDataExportMode.values()) { + new BinaryDataExporter().exportBinaryData(handler, Path.combine(selFile, BinaryDataExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new BinaryDataExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(FontExportMode.class)) { + for (FontExportMode exportMode : FontExportMode.values()) { + new FontExporter().exportFonts(handler, Path.combine(selFile, FontExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf.getTags(), + new FontExportSettings(exportMode), evl); + } + } + + if (export.isOptionEnabled(SymbolClassExportMode.class)) { + for (SymbolClassExportMode exportMode : SymbolClassExportMode.values()) { + new SymbolClassExporter().exportNames(selFile, swf.getTags(), evl); + } + } + + FrameExporter frameExporter = new FrameExporter(); + + if (export.isOptionEnabled(FrameExportMode.class)) { + for (FrameExportMode exportMode : FrameExportMode.values()) { + FrameExportSettings fes = new FrameExportSettings(exportMode, export.getZoom()); + frameExporter.exportFrames(handler, Path.combine(selFile, FrameExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, 0, null, fes, evl); + } + } + + if (export.isOptionEnabled(SpriteExportMode.class)) { + for (SpriteExportMode exportMode : SpriteExportMode.values()) { + SpriteExportSettings ses = new SpriteExportSettings(exportMode, export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof DefineSpriteTag) { + frameExporter.exportFrames(handler, Path.combine(selFile, SpriteExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), null, ses, evl); + } + } + } + } + + if (export.isOptionEnabled(ButtonExportMode.class)) { + for (ButtonExportMode exportMode : ButtonExportMode.values()) { + ButtonExportSettings bes = new ButtonExportSettings(exportMode, export.getZoom()); + for (CharacterTag c : swf.getCharacters().values()) { + if (c instanceof ButtonTag) { + List frameNums = new ArrayList<>(); + frameNums.add(0); // todo: export all frames + frameExporter.exportFrames(handler, Path.combine(selFile, ButtonExportSettings.EXPORT_FOLDER_NAME, exportMode.name()), swf, c.getCharacterId(), frameNums, bes, evl); + } + } + } + } + + if (export.isOptionEnabled(ScriptExportMode.class)) { + boolean parallel = Configuration.parallelSpeedUp.get(); + for (ScriptExportMode exportMode : ScriptExportMode.values()) { + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME, exportMode.name()); + Path.createDirectorySafe(new File(scriptsFolder)); + boolean singleScriptFile = Configuration.scriptExportSingleFile.get(); + if (parallel && singleScriptFile) { + logger.log(Level.WARNING, AppStrings.translate("export.script.singleFilePallelModeWarning")); + singleScriptFile = false; + } + + ScriptExportSettings scriptExportSettings = new ScriptExportSettings(exportMode, singleScriptFile); + String singleFileName = Path.combine(scriptsFolder, swf.getShortFileName() + scriptExportSettings.getFileExtension()); + try (FileTextWriter writer = scriptExportSettings.singleFile ? new FileTextWriter(Configuration.getCodeFormatting(), new FileOutputStream(singleFileName)) : null) { + scriptExportSettings.singleFileWriter = writer; + swf.exportActionScript(handler, scriptsFolder, scriptExportSettings, parallel, evl); + } + } + } + } + + public List getSwfs() { + return swfs; + } + + public SWFList getCurrentSwfList() { + SWF swf = getCurrentSwf(); + if (swf == null) { + return null; + } + + return swf.swfList; + } + + public SWF getCurrentSwf() { + if (swfs == null || swfs.isEmpty()) { + return null; + } + + if (treePanelMode == TreePanelMode.TAG_TREE) { + TreeItem treeNode = (TreeItem) tagTree.getLastSelectedPathComponent(); + if (treeNode == null || treeNode instanceof SWFList) { + return null; + } + + return treeNode.getSwf(); + } else if (treePanelMode == TreePanelMode.DUMP_TREE) { + DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); + + if (dumpInfo == null) { + return null; + } + + return DumpInfoSwfNode.getSwfNode(dumpInfo).getSwf(); + } + + return null; + } + + public void gotoFrame(int frame) { + TreeItem treeItem = (TreeItem) tagTree.getLastSelectedPathComponent(); + if (treeItem == null) { + return; + } + if (treeItem instanceof Timelined) { + Timelined t = (Timelined) treeItem; + Frame f = tagTree.getModel().getFrame(treeItem.getSwf(), t, frame); + if (f != null) { + setTagTreeSelectedNode(f); + } + } + } + + public void gotoScriptLine(SWF swf, String scriptName, int line, int classIndex, int traitIndex, int methodIndex) { + gotoScriptName(swf, scriptName); + if (abcPanel != null) { + if (Main.isDebugPCode()) { + if (classIndex != -1) { + boolean classChanged = false; + if (abcPanel.decompiledTextArea.getClassIndex() != classIndex) { + abcPanel.decompiledTextArea.setClassIndex(classIndex); + classChanged = true; + } + if (traitIndex != -10 && (classChanged || abcPanel.decompiledTextArea.lastTraitIndex != traitIndex)) { + abcPanel.decompiledTextArea.gotoTrait(traitIndex); + } + } + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.gotoInstrLine(line); + } else { + abcPanel.decompiledTextArea.gotoLine(line); + } + } else if (actionPanel != null) { + if (Main.isDebugPCode()) { + actionPanel.editor.gotoLine(line); + } else { + actionPanel.decompiledEditor.gotoLine(line); + } + } + refreshBreakPoints(); + + } + + public void refreshBreakPoints() { + if (abcPanel != null) { + abcPanel.decompiledTextArea.refreshMarkers(); + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.refreshMarkers(); + } + if (actionPanel != null) { + actionPanel.decompiledEditor.refreshMarkers(); + actionPanel.editor.refreshMarkers(); + } + } + + /* + public void debuggerBreakAt(SWF swf, String cls, int line) { + View.execInEventDispatchLater(new Runnable() { + + @Override + public void run() { + gotoClassLine(swf, cls, line); + if (abcPanel != null) { + abcPanel.decompiledTextArea.addColorMarker(line, DecompiledEditorPane.FG_IP_COLOR, DecompiledEditorPane.BG_IP_COLOR, DecompiledEditorPane.PRIORITY_IP); + } + } + }); + + }*/ + public void gotoScriptName(SWF swf, String scriptName) { + if (swf == null) { + return; + } + if (swf.isAS3()) { + String rawScriptName = scriptName; + if (rawScriptName.startsWith("#PCODE ")) { + rawScriptName = rawScriptName.substring(rawScriptName.indexOf(';') + 1); + } + + List abcList = swf.getAbcList(); + if (!abcList.isEmpty()) { + ABCPanel abcPanel = getABCPanel(); + abcPanel.setAbc(abcList.get(0).getABC()); + abcPanel.hilightScript(swf, rawScriptName); + } + } else { + String rawScriptName = scriptName; + if (rawScriptName.startsWith("#PCODE ")) { + rawScriptName = rawScriptName.substring("#PCODE ".length()); + } + Map asms = swf.getASMs(true); + if (actionPanel != null && asms.containsKey(rawScriptName)) { + actionPanel.setSource(asms.get(rawScriptName), true); + } + } + } + + public void gotoDocumentClass(SWF swf) { + if (swf == null) { + return; + } + + String documentClass = swf.getDocumentClass(); + if (documentClass != null && !Configuration.dumpView.get()) { + List abcList = swf.getAbcList(); + if (!abcList.isEmpty()) { + ABCPanel abcPanel = getABCPanel(); + for (ABCContainerTag c : abcList) { + if (c.getABC().findClassByName(documentClass) > -1) { + abcPanel.setAbc(c.getABC()); + abcPanel.hilightScript(swf, documentClass); + break; + } + } + } + } + } + + public void disableDecompilationChanged() { + clearAllScriptCache(); + + if (abcPanel != null) { + abcPanel.reload(); + } + + updateClassesList(); + } + + private void clearAllScriptCache() { + for (SWFList swfList : swfs) { + for (SWF swf : swfList) { + swf.clearScriptCache(); + } + } + } + + public void searchInActionScriptOrText(Boolean searchInText, SWF swf) { + SearchDialog searchDialog = new SearchDialog(getMainFrame().getWindow(), false); + if (searchInText != null) { + if (searchInText) { + searchDialog.searchInTextsRadioButton.setSelected(true); + } else { + searchDialog.searchInASRadioButton.setSelected(true); + } + } + + if (searchDialog.showDialog() == AppDialog.OK_OPTION) { + final String txt = searchDialog.searchField.getText(); + if (!txt.isEmpty()) { + if (swf.isAS3()) { + getABCPanel(); + } else { + getActionPanel(); + } + + boolean ignoreCase = searchDialog.ignoreCaseCheckBox.isSelected(); + boolean regexp = searchDialog.regexpCheckBox.isSelected(); + + if (searchDialog.searchInASRadioButton.isSelected()) { + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + List abcResult = null; + List actionResult = null; + if (swf.isAS3()) { + abcResult = getABCPanel().search(swf, txt, ignoreCase, regexp, this); + } else { + actionResult = getActionPanel().search(swf, txt, ignoreCase, regexp, this); + } + + List fAbcResult = abcResult; + List fActionResult = actionResult; + View.execInEventDispatch(() -> { + boolean found = false; + if (fAbcResult != null) { + found = true; + getABCPanel().searchPanel.setSearchText(txt); + SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getABCPanel()); + sr.setResults(fAbcResult); + sr.setVisible(true); + } else if (fActionResult != null) { + found = true; + getActionPanel().searchPanel.setSearchText(txt); + + SearchResultsDialog sr = new SearchResultsDialog<>(getMainFrame().getWindow(), txt, getActionPanel()); + sr.setResults(fActionResult); + sr.setVisible(true); + } + + if (!found) { + View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + + Main.stopWork(); + }); + + return null; + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.stopWork(); + }); + + } + }.execute(); + } else if (searchDialog.searchInTextsRadioButton.isSelected()) { + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + List textResult; + SearchPanel textSearchPanel = previewPanel.getTextPanel().getSearchPanel(); + textSearchPanel.setOptions(ignoreCase, regexp); + textResult = searchText(txt, ignoreCase, regexp, swf); + + List fTextResult = textResult; + View.execInEventDispatch(() -> { + textSearchPanel.setSearchText(txt); + boolean found = textSearchPanel.setResults(fTextResult); + if (!found) { + View.showMessageDialog(null, translate("message.search.notfound").replace("%searchtext%", txt), translate("message.search.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + + Main.stopWork(); + }); + + return null; + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.stopWork(); + }); + + } + }.execute(); + } + } + } + } + + public void replaceText() { + SearchDialog replaceDialog = new SearchDialog(getMainFrame().getWindow(), true); + if (replaceDialog.showDialog() == AppDialog.OK_OPTION) { + final String txt = replaceDialog.searchField.getText(); + if (!txt.isEmpty()) { + final SWF swf = getCurrentSwf(); + + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + int findCount = 0; + boolean ignoreCase = replaceDialog.ignoreCaseCheckBox.isSelected(); + boolean regexp = replaceDialog.regexpCheckBox.isSelected(); + String replacement = replaceDialog.replaceField.getText(); + if (!regexp) { + replacement = Matcher.quoteReplacement(replacement); + } + Pattern pat; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } + List textTags = new ArrayList<>(); + for (Tag tag : swf.getTags()) { + if (tag instanceof TextTag) { + textTags.add((TextTag) tag); + } + } + for (TextTag textTag : textTags) { + if (!replaceDialog.replaceInParametersCheckBox.isSelected()) { + List texts = textTag.getTexts(); + boolean found = false; + for (int i = 0; i < texts.size(); i++) { + String text = texts.get(i); + if (pat.matcher(text).find()) { + texts.set(i, text.replaceAll(txt, replacement)); + found = true; + findCount++; + } + } + if (found) { + String[] textArray = texts.toArray(new String[texts.size()]); + textTag.setFormattedText(getMissingCharacterHandler(), textTag.getFormattedText(false).text, textArray); + } + } else { + String text = textTag.getFormattedText(false).text; + if (pat.matcher(text).find()) { + textTag.setFormattedText(getMissingCharacterHandler(), text.replaceAll(txt, replacement), null); + findCount++; + } + } + } + + if (findCount > 0) { + swf.clearImageCache(); + repaintTree(); + } + + return null; + } + }.execute(); + } + } + } + + private List searchText(String txt, boolean ignoreCase, boolean regexp, SWF swf) { + if (txt != null && !txt.isEmpty()) { + List found = new ArrayList<>(); + Pattern pat; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0); + } + for (Tag tag : swf.getTags()) { + if (tag instanceof TextTag) { + TextTag textTag = (TextTag) tag; + if (pat.matcher(textTag.getFormattedText(false).text).find()) { + found.add(textTag); + } + } + } + + return found; + } + + return null; + } + + @Override + public void updateSearchPos(TextTag item) { + setTagTreeSelectedNode(item); + previewPanel.getTextPanel().updateSearchPos(); + } + + private void setDumpTreeSelectedNode(DumpInfo dumpInfo) { + DumpTreeModel dtm = (DumpTreeModel) dumpTree.getModel(); + TreePath tp = dtm.getDumpInfoPath(dumpInfo); + if (tp != null) { + dumpTree.setSelectionPath(tp); + dumpTree.scrollPathToVisible(tp); + } else { + showCard(CARDEMPTYPANEL); + } + } + + public void setTagTreeSelectedNode(TreeItem treeItem) { + TagTreeModel ttm = tagTree.getModel(); + TreePath tp = ttm.getTreePath(treeItem); + if (tp != null) { + tagTree.setSelectionPath(tp); + tagTree.scrollPathToVisible(tp); + } else { + showCard(CARDEMPTYPANEL); + } + } + + public void autoDeobfuscateChanged() { + Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); + clearAllScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + reload(true); + updateClassesList(); + } + + public void renameColliding(final SWF swf) { + if (swf == null) { + return; + } + if (confirmExperimental()) { + new CancellableWorker() { + @Override + protected Integer doInBackground() throws Exception { + AbcMultiNameCollisionFixer fixer = new AbcMultiNameCollisionFixer(); + return fixer.fixCollisions(swf); + } + + @Override + protected void onStart() { + Main.startWork(translate("work.renaming.identifiers") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + try { + int cnt = get(); + Main.stopWork(); + View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt))); + swf.assignClassesToSymbols(); + swf.clearScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + updateClassesList(); + reload(true); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error during renaming identifiers", ex); + Main.stopWork(); + View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); + } + }); + } + }.execute(); + } + } + + public void renameOneIdentifier(final SWF swf) { + if (swf == null) { + return; + } + + FileAttributesTag fileAttributes = swf.getFileAttributes(); + if (fileAttributes != null && fileAttributes.actionScript3) { + final int multiName = getABCPanel().decompiledTextArea.getMultinameUnderCaret(); + final List abcList = swf.getAbcList(); + if (multiName > 0) { + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + renameMultiname(abcList, multiName); + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.renaming") + "...", this); + } + + @Override + protected void done() { + Main.stopWork(); + } + }.execute(); + + } else { + View.showMessageDialog(null, translate("message.rename.notfound.multiname"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + } else { + final String identifier = getActionPanel().getStringUnderCursor(); + if (identifier != null) { + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + try { + renameIdentifier(swf, identifier); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.renaming") + "...", this); + } + + @Override + protected void done() { + Main.stopWork(); + } + }.execute(); + } else { + View.showMessageDialog(null, translate("message.rename.notfound.identifier"), translate("message.rename.notfound.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + } + + public void exportFla(final SWF swf) { + if (swf == null) { + return; + } + JFileChooser fc = new JFileChooser(); + String selDir = Configuration.lastOpenDir.get(); + fc.setCurrentDirectory(new File(selDir)); + if (!selDir.endsWith(File.separator)) { + selDir += File.separator; + } + String fileName = new File(swf.getFile()).getName(); + fileName = fileName.substring(0, fileName.length() - 4) + ".fla"; + fc.setSelectedFile(new File(selDir + fileName)); + List flaFilters = new ArrayList<>(); + List xflFilters = new ArrayList<>(); + List versions = new ArrayList<>(); + boolean isAS3 = swf.isAS3(); + for (int i = FLAVersion.values().length - 1; i >= 0; i--) { + final FLAVersion v = FLAVersion.values()[i]; + if (!isAS3 && v.minASVersion() > 2) { + // This version does not support AS1/2 + } else { + versions.add(v); + FileFilter f = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.getName().toLowerCase().endsWith(".fla")); + } + + @Override + public String getDescription() { + return translate("filter.fla").replace("%version%", v.applicationName()); + } + }; + if (v == FLAVersion.CS6) { + fc.setFileFilter(f); + } else { + fc.addChoosableFileFilter(f); + } + flaFilters.add(f); + f = new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() || (f.getName().toLowerCase().endsWith(".xfl")); + } + + @Override + public String getDescription() { + return translate("filter.xfl").replace("%version%", v.applicationName()); + } + }; + fc.addChoosableFileFilter(f); + xflFilters.add(f); + } + } + + fc.setAcceptAllFileFilterUsed(false); + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showSaveDialog(f) == JFileChooser.APPROVE_OPTION) { + Configuration.lastOpenDir.set(Helper.fixDialogFile(fc.getSelectedFile()).getParentFile().getAbsolutePath()); + File sf = Helper.fixDialogFile(fc.getSelectedFile()); + + FileFilter selectedFilter = fc.getFileFilter(); + final boolean compressed = flaFilters.contains(selectedFilter); + String path = sf.getAbsolutePath(); + if (path.endsWith(".fla") || path.endsWith(".xfl")) { + path = path.substring(0, path.length() - 4); + } + path += compressed ? ".fla" : ".xfl"; + final FLAVersion selectedVersion = versions.get(compressed ? flaFilters.indexOf(selectedFilter) : xflFilters.indexOf(selectedFilter)); + final File selfile = new File(path); + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + Helper.freeMem(); + try { + AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); + if (compressed) { + swf.exportFla(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); + } else { + swf.exportXfl(errorHandler, selfile.getAbsolutePath(), new File(swf.getFile()).getName(), ApplicationInfo.APPLICATION_NAME, ApplicationInfo.applicationVerName, ApplicationInfo.version, Configuration.parallelSpeedUp.get(), selectedVersion); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "FLA export error", ex); + View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage(), translate("error"), JOptionPane.ERROR_MESSAGE); + } + Helper.freeMem(); + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.exporting.fla") + "...", this); + } + + @Override + protected void done() { + if (Configuration.openFolderAfterFlaExport.get()) { + try { + Desktop.getDesktop().open(selfile.getAbsoluteFile().getParentFile()); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + Main.stopWork(); + } + }.execute(); + } + } + + public void importText(final SWF swf) { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("import.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + File textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_FORMATTED)); + TextImporter textImporter = new TextImporter(getMissingCharacterHandler(), new TextImportErrorHandler() { + // "configuration items" for the current replace only + private final ConfigurationItem showAgainImportError = new ConfigurationItem<>("showAgainImportError", true, true); + + private final ConfigurationItem showAgainInvalidText = new ConfigurationItem<>("showAgainInvalidText", true, true); + + private String getTextTagInfo(TextTag textTag) { + StringBuilder ret = new StringBuilder(); + if (textTag != null) { + ret.append(" TextId: ").append(textTag.getCharacterId()).append(" (").append(String.join(", ", textTag.getTexts())).append(")"); + } + + return ret.toString(); + } + + @Override + public boolean handle(TextTag textTag) { + String msg = translate("error.text.import"); + logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); + return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainImportError, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; + } + + @Override + public boolean handle(TextTag textTag, String message, long line) { + String msg = translate("error.text.invalid.continue").replace("%text%", message).replace("%line%", Long.toString(line)); + logger.log(Level.SEVERE, msg + getTextTagInfo(textTag)); + return View.showConfirmDialog(MainPanel.this, msg, translate("error"), JOptionPane.OK_CANCEL_OPTION, showAgainInvalidText, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION; + } + }); + + // try to import formatted texts + if (textsFile.exists()) { + textImporter.importTextsSingleFileFormatted(textsFile, swf); + } else { + textsFile = new File(Path.combine(selFile, TextExportSettings.EXPORT_FOLDER_NAME, TextExporter.TEXT_EXPORT_FILENAME_PLAIN)); + // try to import plain texts + if (textsFile.exists()) { + textImporter.importTextsSingleFile(textsFile, swf); + } else { + textImporter.importTextsMultipleFiles(selFile, swf); + } + } + + swf.clearImageCache(); + reload(true); + } + } + + public As3ScriptReplacerInterface getAs3ScriptReplacer() { + As3ScriptReplacerInterface r = As3ScriptReplacerFactory.createByConfig(); + if (!r.isAvailable()) { + if (r instanceof MxmlcAs3ScriptReplacer) { + if (View.showConfirmDialog(null, AppStrings.translate("message.flexpath.notset"), AppStrings.translate("error"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { + Main.advancedSettings("paths"); + } + } else if (r instanceof FFDecAs3ScriptReplacer) { + if (View.showConfirmDialog(this, AppStrings.translate("message.playerpath.lib.notset"), AppStrings.translate("message.action.playerglobal.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { + Main.advancedSettings("paths"); + } + } else { + //Not translated yet - just in case there are more Script replacers in the future. Unused now. + View.showConfirmDialog(this, "Current script replacer is not available", "Script replacer not available", JOptionPane.OK_OPTION, JOptionPane.ERROR_MESSAGE); + } + return null; + } + return r; + } + + public void importScript(final SWF swf) { + As3ScriptReplacerInterface as3ScriptReplacer = getAs3ScriptReplacer(); + if (as3ScriptReplacer == null) { + return; + } + String flexLocation = Configuration.flexSdkLocation.get(); + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("import.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + String scriptsFolder = Path.combine(selFile, ScriptExportSettings.EXPORT_FOLDER_NAME); + + int countAs2 = new AS2ScriptImporter().importScripts(scriptsFolder, swf.getASMs(true)); + int countAs3 = new AS3ScriptImporter().importScripts(as3ScriptReplacer, scriptsFolder, swf.getAS3Packs()); + + if (countAs3 > 0) { + updateClassesList(); + } + + View.showMessageDialog(this, translate("import.script.result").replace("%count%", Integer.toString(countAs2 + countAs3))); + if (countAs2 != 0 || countAs3 != 0) { + reload(true); + } + } + } + + public void importSymbolClass(final SWF swf) { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("import.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + File importFile = new File(Path.combine(selFile, SymbolClassExporter.SYMBOL_CLASS_EXPORT_FILENAME)); + SymbolClassImporter importer = new SymbolClassImporter(); + + if (importFile.exists()) { + importer.importSymbolClasses(importFile, swf); + } + } + } + + private String selectExportDir() { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(Configuration.lastExportDir.get())); + chooser.setDialogTitle(translate("export.select.directory")); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + final String selFile = Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath(); + Configuration.lastExportDir.set(Helper.fixDialogFile(chooser.getSelectedFile()).getAbsolutePath()); + return selFile; + } + return null; + } + + public void export(final boolean onlySel) { + + final SWF swf = getCurrentSwf(); + List sel = tagTree.getAllSelected(); + if (!onlySel) { + sel = null; + } else if (sel.isEmpty()) { + return; + } + final ExportDialog export = new ExportDialog(sel); + if (export.showExportDialog() == AppDialog.OK_OPTION) { + final String selFile = selectExportDir(); + if (selFile != null) { + final long timeBefore = System.currentTimeMillis(); + + new CancellableWorker() { + @Override + public Void doInBackground() throws Exception { + try { + AbortRetryIgnoreHandler errorHandler = new GuiAbortRetryIgnoreHandler(); + if (onlySel) { + exportSelection(errorHandler, selFile, export); + } else { + exportAll(swf, errorHandler, selFile, export); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error during export", ex); + View.showMessageDialog(null, translate("error.export") + ": " + ex.getClass().getName() + " " + ex.getLocalizedMessage()); + } + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.exporting") + "...", this); + } + + @Override + protected void done() { + Main.stopWork(); + long timeAfter = System.currentTimeMillis(); + final long timeMs = timeAfter - timeBefore; + + View.execInEventDispatchLater(() -> { + setStatus(translate("export.finishedin").replace("%time%", Helper.formatTimeSec(timeMs))); + }); + } + }.execute(); + + } + } + } + + public void exportJavaSource() { + List sel = tagTree.getSelected(); + for (TreeItem item : sel) { + if (item instanceof SWF) { + SWF swf = (SWF) item; + final String selFile = selectExportDir(); + if (selFile != null) { + Main.startWork(translate("work.exporting") + "...", null); + + try { + new SwfJavaExporter().exportJavaCode(swf, selFile); + Main.stopWork(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + } + } + + public void exportSwfXml() { + List sel = tagTree.getSelected(); + Set swfs = new HashSet<>(); + + for (TreeItem item : sel) { + swfs.add(item.getSwf()); + } + + for (SWF swf : swfs) { + final String selFile = selectExportDir(); + if (selFile != null) { + Main.startWork(translate("work.exporting") + "...", null); + + try { + File outFile = new File(selFile + File.separator + Helper.makeFileName("swf.xml")); + new SwfXmlExporter().exportXml(swf, outFile); + Main.stopWork(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + } + + public void importSwfXml() { + List sel = tagTree.getSelected(); + Set swfs = new HashSet<>(); + for (TreeItem item : sel) { + swfs.add(item.getSwf()); + } + if (swfs.size() > 1) { + return; + } + + for (SWF swf : swfs) { + File selectedFile = showImportFileChooser("filter.xml|*.xml"); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + String xml = Helper.readTextFile(selfile.getPath()); + try { + new SwfXmlImporter().importSwf(swf, xml); + swf.clearAllCache(); + swf.assignExportNamesToSymbols(); + swf.assignClassesToSymbols(); + refreshTree(swf); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + } + + public void renameIdentifiers(final SWF swf) { + if (swf == null) { + return; + } + if (confirmExperimental()) { + RenameDialog renameDialog = new RenameDialog(); + if (renameDialog.showRenameDialog() == AppDialog.OK_OPTION) { + final RenameType renameType = renameDialog.getRenameType(); + new CancellableWorker() { + @Override + protected Integer doInBackground() throws Exception { + int cnt = swf.deobfuscateIdentifiers(renameType); + return cnt; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.renaming.identifiers") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + try { + int cnt = get(); + Main.stopWork(); + View.showMessageDialog(null, translate("message.rename.renamed").replace("%count%", Integer.toString(cnt))); + swf.assignClassesToSymbols(); + swf.clearScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + updateClassesList(); + reload(true); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error during renaming identifiers", ex); + Main.stopWork(); + View.showMessageDialog(null, translate("error.occured").replace("%error%", ex.getClass().getSimpleName())); + } + }); + } + }.execute(); + } + } + } + + public void deobfuscate() { + DeobfuscationDialog deobfuscationDialog = new DeobfuscationDialog(); + if (deobfuscationDialog.showDialog() == AppDialog.OK_OPTION) { + DeobfuscationLevel level = DeobfuscationLevel.getByLevel(deobfuscationDialog.codeProcessingLevel.getValue()); + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + try { + ABCPanel abcPanel = getABCPanel(); + if (deobfuscationDialog.processAllCheckbox.isSelected()) { + SWF swf = abcPanel.getSwf(); + swf.deobfuscate(level); + } else { + int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex(); + DecompiledEditorPane decompiledTextArea = abcPanel.decompiledTextArea; + Trait t = abcPanel.decompiledTextArea.getCurrentTrait(); + ABC abc = abcPanel.abc; + if (bi != -1) { + int scriptIndex = decompiledTextArea.getScriptLeaf().scriptIndex; + int classIndex = decompiledTextArea.getClassIndex(); + boolean isStatic = decompiledTextArea.getIsStatic(); + abc.bodies.get(bi).deobfuscate(level, t, scriptIndex, classIndex, isStatic, ""/*FIXME*/); + } + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(decompiledTextArea.getScriptLeaf().getPathScriptName(), bi, abc, t, abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getScriptIndex()); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, "Deobfuscation error", ex); + } + + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.deobfuscating") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.stopWork(); + View.showMessageDialog(null, translate("work.deobfuscating.complete")); + + clearAllScriptCache(); + getABCPanel().reload(); + updateClassesList(); + }); + } + }.execute(); + } + } + + public void removeNonScripts(SWF swf) { + if (swf == null) { + return; + } + + List tags = swf.getTags().toArrayList(); + List toRemove = new ArrayList<>(); + for (Tag tag : tags) { + System.out.println(tag.getClass()); + if (!(tag instanceof ABCContainerTag || tag instanceof ASMSource)) { + toRemove.add(tag); + } + } + + swf.removeTags(toRemove, true); + refreshTree(swf); + } + + public void removeExceptSelected(SWF swf) { + if (swf == null) { + return; + } + + List sel = tagTree.getAllSelected(); + Set needed = new HashSet<>(); + for (TreeItem item : sel) { + if (item instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) item; + characterTag.getNeededCharactersDeep(needed); + needed.add(characterTag.getCharacterId()); + } + } + + List tagsToRemove = new ArrayList<>(); + for (Tag tag : swf.getTags()) { + if (tag instanceof CharacterTag) { + CharacterTag characterTag = (CharacterTag) tag; + if (!needed.contains(characterTag.getCharacterId())) { + tagsToRemove.add(tag); + } + } + } + + swf.removeTags(tagsToRemove, true); + refreshTree(swf); + } + + private void clear() { + dumpViewPanel.clear(); + previewPanel.clear(); + headerPanel.clear(); + folderPreviewPanel.clear(); + if (abcPanel != null) { + abcPanel.clearSwf(); + } + if (actionPanel != null) { + actionPanel.clearSource(); + } + } + + public void refreshTree() { + refreshTree(new SWF[0]); + } + + public void refreshTree(SWF swf) { + refreshTree(new SWF[]{swf}); + } + + public void refreshTree(SWF[] swfs) { + clear(); + showCard(CARDEMPTYPANEL); + TreeItem treeItem = tagTree.getCurrentTreeItem(); + + tagTree.updateSwfs(swfs); + + if (treeItem != null) { + SWF treeItemSwf = treeItem.getSwf().getRootSwf(); + if (this.swfs.contains(treeItemSwf.swfList)) { + setTagTreeSelectedNode(treeItem); + } + } + + reload(true); + } + + public void refreshDecompiled() { + clearAllScriptCache(); + if (abcPanel != null) { + abcPanel.reload(); + } + + reload(true); + updateClassesList(); + } + + private MissingCharacterHandler getMissingCharacterHandler() { + return new MissingCharacterHandler() { + // "configuration items" for the current replace only + private final ConfigurationItem showAgainIgnoreMissingCharacters = new ConfigurationItem<>("showAgainIgnoreMissingCharacters", true, true); + + private boolean ignoreMissingCharacters = false; + + @Override + public boolean getIgnoreMissingCharacters() { + return ignoreMissingCharacters; + } + + @Override + public boolean handle(TextTag textTag, final FontTag font, final char character) { + String fontName = font.getSwf().sourceFontNamesMap.get(font.getFontId()); + if (fontName == null) { + fontName = font.getFontName(); + } + final Font f = FontTag.installedFontsByName.get(fontName); + if (f == null || !f.canDisplay(character)) { + String msg = translate("error.font.nocharacter").replace("%char%", "" + character); + logger.log(Level.SEVERE, "{0} FontId: {1} TextId: {2}", new Object[]{msg, font.getCharacterId(), textTag.getCharacterId()}); + ignoreMissingCharacters = View.showConfirmDialog(null, msg, translate("error"), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, + showAgainIgnoreMissingCharacters, + ignoreMissingCharacters ? JOptionPane.OK_OPTION : JOptionPane.CANCEL_OPTION) == JOptionPane.OK_OPTION; + return false; + } + + font.addCharacter(character, f); + + return true; + } + }; + } + + public boolean saveText(TextTag textTag, String formattedText, String[] texts, LineMarkedEditorPane editor) { + try { + if (textTag.setFormattedText(getMissingCharacterHandler(), formattedText, texts)) { + return true; + } + } catch (TextParseException ex) { + if (editor != null) { + editor.gotoLine((int) ex.line); + editor.markError(); + } + + View.showMessageDialog(null, translate("error.text.invalid").replace("%text%", ex.text).replace("%line%", Long.toString(ex.line)), translate("error"), JOptionPane.ERROR_MESSAGE); + } + + return false; + } + + public boolean alignText(TextTag textTag, TextAlign textAlign) { + return (textTag.alignText(textAlign)); + } + + public boolean translateText(TextTag textTag, int diff) { + return textTag.translateText(diff); + } + + public boolean previousTag() { + if (getCurrentView() == VIEW_RESOURCES) { + if (tagTree.getSelectionRows().length > 0) { + int row = tagTree.getSelectionRows()[0]; + if (row > 0) { + tagTree.setSelectionRow(row - 1); + tagTree.scrollRowToVisible(row - 1); + previewPanel.focusTextPanel(); + } + } + return true; + } + return false; + } + + public boolean nextTag() { + if (getCurrentView() == VIEW_RESOURCES) { + if (tagTree.getSelectionRows().length > 0) { + int row = tagTree.getSelectionRows()[0]; + if (row < tagTree.getRowCount() - 1) { + tagTree.setSelectionRow(row + 1); + tagTree.scrollRowToVisible(row + 1); + previewPanel.focusTextPanel(); + } + } + return true; + } + return false; + } + + public void selectBkColorButtonActionPerformed(ActionEvent evt) { + Color newColor = JColorChooser.showDialog(null, AppStrings.translate("dialog.selectbkcolor.title"), View.getSwfBackgroundColor()); + if (newColor != null) { + View.setSwfBackgroundColor(newColor); + reload(true); + } + } + + public void replaceButtonActionPerformed(ActionEvent evt) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof DefineSoundTag) { + File selectedFile = showImportFileChooser("filter.sounds|*.mp3;*.wav|filter.sounds.mp3|*.mp3|filter.sounds.wav|*.wav"); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + DefineSoundTag ds = (DefineSoundTag) item; + int soundFormat = SoundFormat.FORMAT_UNCOMPRESSED_LITTLE_ENDIAN; + if (selfile.getName().toLowerCase().endsWith(".mp3")) { + soundFormat = SoundFormat.FORMAT_MP3; + } + + boolean ok = false; + try { + ok = ds.setSound(new FileInputStream(selfile), soundFormat); + ds.getSwf().clearSoundCache(); + } catch (IOException ex) { + //ignore + } + + if (!ok) { + View.showMessageDialog(null, translate("error.sound.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } else { + reload(true); + } + } + } + if (item instanceof ImageTag) { + ImageTag it = (ImageTag) item; + if (it.importSupported()) { + File selectedFile = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp"); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + Tag newTag = new ImageImporter().importImage(it, data); + SWF swf = it.getSwf(); + if (newTag != null) { + refreshTree(swf); + setTagTreeSelectedNode(newTag); + } + swf.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid image", ex); + View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + + reload(true); + } + } + } + if (item instanceof ShapeTag) { + ShapeTag st = (ShapeTag) item; + String filter = "filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg"; + File selectedFile = showImportFileChooser(filter); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = null; + String svgText = null; + if (".svg".equals(Path.getExtension(selfile))) { + svgText = Helper.readTextFile(selfile.getAbsolutePath()); + showSvgImportWarning(); + } else { + data = Helper.readFile(selfile.getAbsolutePath()); + } + try { + Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText) : new ShapeImporter().importImage(st, data); + SWF swf = st.getSwf(); + if (newTag != null) { + refreshTree(swf); + setTagTreeSelectedNode(newTag); + } + + swf.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid image", ex); + View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + reload(true); + } + } + if (item instanceof DefineBinaryDataTag) { + DefineBinaryDataTag bt = (DefineBinaryDataTag) item; + File selectedFile = showImportFileChooser(""); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + new BinaryDataImporter().importData(bt, data); + refreshTree(bt.getSwf()); + reload(true); + } + } + } + + public void replaceNoFillButtonActionPerformed(ActionEvent evt) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof ShapeTag) { + ShapeTag st = (ShapeTag) item; + String filter = "filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg"; + File selectedFile = showImportFileChooser(filter); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = null; + String svgText = null; + if (".svg".equals(Path.getExtension(selfile))) { + svgText = Helper.readTextFile(selfile.getAbsolutePath()); + showSvgImportWarning(); + } else { + data = Helper.readFile(selfile.getAbsolutePath()); + } + try { + Tag newTag = svgText != null ? new SvgImporter().importSvg(st, svgText, false) : new ShapeImporter().importImage(st, data, 0, false); + SWF swf = st.getSwf(); + if (newTag != null) { + refreshTree(swf); + setTagTreeSelectedNode(newTag); + } + + swf.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid image", ex); + View.showMessageDialog(null, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + reload(true); + } + } + } + + private void showSvgImportWarning() { + View.showMessageDialog(null, AppStrings.translate("message.warning.svgImportExperimental"), AppStrings.translate("message.warning"), JOptionPane.WARNING_MESSAGE, Configuration.warningSvgImport); + } + + public void replaceAlphaButtonActionPerformed(ActionEvent evt) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item == null) { + return; + } + + if (item instanceof DefineBitsJPEG3Tag || item instanceof DefineBitsJPEG4Tag) { + ImageTag it = (ImageTag) item; + if (it.importSupported()) { + File selectedFile = showImportFileChooser(""); + if (selectedFile != null) { + File selfile = Helper.fixDialogFile(selectedFile); + byte[] data = Helper.readFile(selfile.getAbsolutePath()); + try { + new ImageImporter().importImageAlpha(it, data); + SWF swf = it.getSwf(); + swf.clearImageCache(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Invalid alpha channel data", ex); + View.showMessageDialog(null, translate("error.image.alpha.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE); + } + + reload(true); + } + } + } + } + + public void exportJavaSourceActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + exportJavaSource(); + } + + public void exportSwfXmlActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + exportSwfXml(); + } + + public void importSwfXmlActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + importSwfXml(); + } + + public void exportSelectionActionPerformed(ActionEvent evt) { + if (Main.isWorking()) { + return; + } + + export(true); + } + + public File showImportFileChooser(String filter) { + String[] filterArray = filter.length() > 0 ? filter.split("\\|") : new String[0]; + + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(Configuration.lastOpenDir.get())); + boolean first = true; + for (int i = 0; i < filterArray.length; i += 2) { + final String filterName = filterArray[i]; + final String[] extensions = filterArray[i + 1].split(";"); + for (int j = 0; j < extensions.length; j++) { + if (extensions[j].startsWith("*.")) { + extensions[j] = extensions[j].substring(1); + } + } + FileFilter ff = new FileFilter() { + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + String fileName = f.getName().toLowerCase(); + for (String ext : extensions) { + if (fileName.endsWith(ext)) { + return true; + } + } + return false; + } + + @Override + public String getDescription() { + StringBuilder extStr = new StringBuilder(); + boolean first = true; + for (String ext : extensions) { + if (first) { + first = false; + } else { + extStr.append(","); + } + + extStr.append("*").append(ext); + } + + return translate(filterName).replace("%extensions%", extStr); + } + }; + if (first) { + fc.setFileFilter(ff); + } else { + fc.addChoosableFileFilter(ff); + } + first = false; + } + + JFrame f = new JFrame(); + View.setWindowIcon(f); + if (fc.showOpenDialog(f) == JFileChooser.APPROVE_OPTION) { + File result = fc.getSelectedFile(); + Configuration.lastOpenDir.set(Helper.fixDialogFile(result).getParentFile().getAbsolutePath()); + return result; + } + + return null; + } + + private void showDetail(String card) { + CardLayout cl = (CardLayout) (detailPanel.getLayout()); + cl.show(detailPanel, card); + if (card.equals(DETAILCARDEMPTYPANEL)) { + if (detailPanel.isVisible()) { + detailPanel.setVisible(false); + } + } else if (!detailPanel.isVisible()) { + detailPanel.setVisible(true); + } + } + + private void showCard(String card) { + CardLayout cl = (CardLayout) (displayPanel.getLayout()); + cl.show(displayPanel, card); + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + Object source = e.getSource(); + TreeItem treeItem = (TreeItem) e.getPath().getLastPathComponent(); + + if (!(treeItem instanceof SWFList)) { + SWF swf = treeItem.getSwf(); + if (swfs.isEmpty()) { + // show welcome panel after closing swfs + updateUi(); + } else { + if (swf == null) { + swf = swfs.get(0).get(0); + } + + updateUi(swf); + } + } else { + updateUi(); + } + + reload(false); + + if (source == dumpTree) { + Tag t = null; + if (treeItem instanceof DumpInfo) { + DumpInfo di = (DumpInfo) treeItem; + t = di.getTag(); + } + + showPreview(t, dumpPreviewPanel); + } + } + + public void unloadFlashPlayer() { + if (flashPanel != null) { + try { + flashPanel.close(); + } catch (IOException ex) { + // ignore + } + } + if (flashPanel2 != null) { + try { + flashPanel2.close(); + } catch (IOException ex) { + // ignore + } + } + } + + public void clearDebuggerColors() { + if (abcPanel != null) { + abcPanel.decompiledTextArea.removeColorMarkerOnAllLines(DecompiledEditorPane.IP_MARKER); + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clearDebuggerColors(); + } + if (actionPanel != null) { + actionPanel.decompiledEditor.removeColorMarkerOnAllLines(DecompiledEditorPane.IP_MARKER); + actionPanel.editor.removeColorMarkerOnAllLines(DecompiledEditorPane.IP_MARKER); + } + } + + private void stopFlashPlayer() { + if (flashPanel != null) { + if (!flashPanel.isStopped()) { + flashPanel.stopSWF(); + } + } + if (flashPanel2 != null) { + if (!flashPanel2.isStopped()) { + flashPanel2.stopSWF(); + } + } + } + + public boolean isInternalFlashViewerSelected() { + return mainMenu.isInternalFlashViewerSelected(); + } + + public static final int VIEW_RESOURCES = 0; + + public static final int VIEW_DUMP = 1; + + public static final int VIEW_TIMELINE = 2; + + private int getCurrentView() { + return Configuration.dumpView.get() ? VIEW_DUMP : VIEW_RESOURCES; + } + + public void setTreeModel(int view) { + switch (view) { + case VIEW_DUMP: + if (dumpTree.getModel() == null) { + DumpTreeModel dtm = new DumpTreeModel(swfs); + dumpTree.setModel(dtm); + dumpTree.expandFirstLevelNodes(); + } + break; + case VIEW_RESOURCES: + if (tagTree.getModel() == null) { + TagTreeModel ttm = new TagTreeModel(swfs, Configuration.tagTreeShowEmptyFolders.get()); + tagTree.setModel(ttm); + tagTree.expandFirstLevelNodes(); + } + break; + } + } + + private JPanel createDumpViewCard() { + JPanel r = new JPanel(new BorderLayout()); + r.add(new JPersistentSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(dumpTree), dumpPreviewPanel, Configuration.guiDumpSplitPaneDividerLocationPercent), BorderLayout.CENTER); + return r; + } + + private JPanel createResourcesViewCard() { + JPanel r = new JPanel(new BorderLayout()); + r.add(new JScrollPane(tagTree), BorderLayout.CENTER); + r.add(searchPanel, BorderLayout.SOUTH); + return r; + } + + public boolean showView(int view) { + + CardLayout cl = (CardLayout) (contentPanel.getLayout()); + CardLayout cl2 = (CardLayout) (treePanel.getLayout()); + + setTreeModel(view); + switch (view) { + case VIEW_DUMP: + if (!isWelcomeScreen) { + cl.show(contentPanel, SPLIT_PANE1); + } + cl2.show(treePanel, DUMP_VIEW); + treePanelMode = TreePanelMode.DUMP_TREE; + showDetail(DETAILCARDEMPTYPANEL); + reload(true); + return true; + case VIEW_RESOURCES: + if (!isWelcomeScreen) { + cl.show(contentPanel, SPLIT_PANE1); + } + cl2.show(treePanel, RESOURCES_VIEW); + + treePanelMode = TreePanelMode.TAG_TREE; + + treePanel.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + tagTree.scrollPathToVisible(tagTree.getSelectionPath()); + } + }); + + reload(true); + return true; + case VIEW_TIMELINE: + final SWF swf = getCurrentSwf(); + if (swf != null) { + TreeItem item = tagTree.getCurrentTreeItem(); + if (item instanceof TagScript) { + item = ((TagScript) item).getTag(); + } + if (item instanceof Timelined) { + timelineViewPanel.setTimelined((Timelined) item); + } else if (item instanceof Frame) { + timelineViewPanel.setTimelined(((Frame) item).timeline.timelined); + } else { + timelineViewPanel.setTimelined(swf); + } + cl.show(contentPanel, TIMELINE_PANEL); + return true; + } + return false; + } + return false; + + } + + private void dumpViewReload(boolean forceReload) { + showDetail(DETAILCARDEMPTYPANEL); + + DumpInfo dumpInfo = (DumpInfo) dumpTree.getLastSelectedPathComponent(); + if (dumpInfo == null) { + showCard(CARDEMPTYPANEL); + return; + } + + dumpViewPanel.revalidate(); + dumpViewPanel.setSelectedNode(dumpInfo); + showCard(CARDDUMPVIEW); + } + + public void loadFromBinaryTag(final DefineBinaryDataTag binaryDataTag) { + loadFromBinaryTag(Arrays.asList(binaryDataTag)); + } + + public void loadFromBinaryTag(final List binaryDataTags) { + + Main.loadingDialog.setVisible(true); + new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + try { + for (DefineBinaryDataTag binaryDataTag : binaryDataTags) { + try { + InputStream is = new ByteArrayInputStream(binaryDataTag.binaryData.getRangeData()); + SWF bswf = new SWF(is, null, "(SWF Data)", new ProgressListener() { + @Override + public void progress(int p) { + Main.loadingDialog.setPercent(p); + } + }, Configuration.parallelSpeedUp.get()); + binaryDataTag.innerSwf = bswf; + bswf.binaryData = binaryDataTag; + } catch (IOException ex) { + //ignore + } + } + } catch (InterruptedException ex) { + //ignore + } + + return null; + } + + @Override + protected void onStart() { + Main.startWork(AppStrings.translate("work.reading.swf") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + Main.loadingDialog.setVisible(false); + Main.stopWork(); + }); + } + }.execute(); + } + + private void closeTag() { + previewPanel.closeTag(); + } + + public void showPreview(TreeItem treeItem, PreviewPanel previewPanel) { + previewPanel.clear(); + if (treeItem == null) { + previewPanel.showEmpty(); + return; + } + boolean internalViewer = isInternalFlashViewerSelected(); + if (treeItem instanceof SWF) { + SWF swf = (SWF) treeItem; + if (internalViewer) { + previewPanel.showImagePanel(swf, swf, -1); + } else { + previewPanel.setParametersPanelVisible(false); + if (flashPanel != null) { //same for flashPanel2 + previewPanel.showFlashViewerPanel(); + previewPanel.showSwf(swf); + } + } + } else if (treeItem instanceof MetadataTag) { + MetadataTag metadataTag = (MetadataTag) treeItem; + previewPanel.showMetaDataPanel(metadataTag); + } else if (treeItem instanceof DefineBinaryDataTag) { + DefineBinaryDataTag binaryTag = (DefineBinaryDataTag) treeItem; + previewPanel.showBinaryPanel(binaryTag); + } else if (treeItem instanceof ImageTag) { + ImageTag imageTag = (ImageTag) treeItem; + previewPanel.setImageReplaceButtonVisible(!((Tag) imageTag).isReadOnly() && imageTag.importSupported(), imageTag instanceof DefineBitsJPEG3Tag || imageTag instanceof DefineBitsJPEG4Tag); + previewPanel.showImagePanel(imageTag.getImageCached()); + + } else if ((treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) { + final Tag tag = (Tag) treeItem; + DrawableTag d = (DrawableTag) tag; + Timelined timelined; + if (treeItem instanceof Timelined && !(treeItem instanceof ButtonTag)) { + timelined = (Timelined) tag; + } else { + timelined = makeTimelined(tag); + } + + previewPanel.setParametersPanelVisible(false); + previewPanel.showImagePanel(timelined, tag.getSwf(), -1); + } else if (treeItem instanceof Frame && internalViewer) { + Frame fn = (Frame) treeItem; + SWF swf = fn.getSwf(); + previewPanel.showImagePanel(fn.timeline.timelined, swf, fn.frame); + } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { + previewPanel.showImagePanel(new SerializableImage(View.loadImage("sound32"))); + previewPanel.setImageReplaceButtonVisible(((Tag) treeItem).isReadOnly() && (treeItem instanceof DefineSoundTag), false); + try { + SoundTagPlayer soundThread = new SoundTagPlayer((SoundTag) treeItem, Configuration.loopMedia.get() ? Integer.MAX_VALUE : 1, true); + previewPanel.setMedia(soundThread); + } catch (LineUnavailableException | IOException | UnsupportedAudioFileException ex) { + logger.log(Level.SEVERE, null, ex); + } + + } else if ((treeItem instanceof FontTag) && internalViewer) { + previewPanel.showFontPanel((FontTag) treeItem); + } else if ((treeItem instanceof TextTag) && internalViewer) { + previewPanel.showTextPanel((TextTag) treeItem); + } else if ((treeItem instanceof Frame) || (treeItem instanceof CharacterTag) || (treeItem instanceof FontTag) || (treeItem instanceof SoundStreamHeadTypeTag)) { + previewPanel.createAndShowTempSwf(treeItem); + + if (treeItem instanceof TextTag) { + previewPanel.showTextPanel((TextTag) treeItem); + } else if (treeItem instanceof FontTag) { + previewPanel.showFontPanel((FontTag) treeItem); + } else { + previewPanel.setParametersPanelVisible(false); + } + } else { + previewPanel.showEmpty(); + } + } + + public void reload(boolean forceReload) { + tagTree.scrollPathToVisible(tagTree.getSelectionPath()); + if (Configuration.dumpView.get()) { + dumpViewReload(forceReload); + return; + } + + TreeItem treeItem = null; + TreePath treePath = tagTree.getSelectionPath(); + if (treePath != null && tagTree.getModel().treePathExists(treePath)) { + treeItem = (TreeItem) treePath.getLastPathComponent(); + } + + // save last selected node to config + if (treeItem != null && !(treeItem instanceof SWFList)) { + SWF swf = treeItem.getSwf(); + if (swf != null) { + swf = swf.getRootSwf(); + } + + if (swf != null) { + SwfSpecificConfiguration swfConf = Configuration.getOrCreateSwfSpecificConfiguration(swf.getShortFileName()); + swfConf.lastSelectedPath = tagTree.getSelectionPathString(); + } + } + + if (!forceReload && (treeItem == oldItem)) { + return; + } + + if (oldItem != treeItem) { + closeTag(); + } + + oldItem = treeItem; + + // show the preview of the tag when the user clicks to the tagname inside the scripts node, too + // this is a little bit inconsistent, beacuse the frames (FrameScript) are not shown + boolean preferScript = false; + if (treeItem instanceof TagScript) { + treeItem = ((TagScript) treeItem).getTag(); + preferScript = true; + } + + folderPreviewPanel.clear(); + previewPanel.clear(); + stopFlashPlayer(); + + previewPanel.setImageReplaceButtonVisible(false, false); + + boolean internalViewer = isInternalFlashViewerSelected(); + + if (treeItem instanceof ScriptPack) { + final ScriptPack scriptLeaf = (ScriptPack) treeItem; + if (setSourceWorker != null) { + setSourceWorker.cancel(true); + setSourceWorker = null; + } + if (!Main.isInited() || !Main.isWorking() || Main.isDebugging()) { + ABCPanel abcPanel = getABCPanel(); + CancellableWorker worker = new CancellableWorker() { + @Override + protected Void doInBackground() throws Exception { + abcPanel.detailPanel.methodTraitPanel.methodCodePanel.clear(); + abcPanel.setAbc(scriptLeaf.abc); + abcPanel.decompiledTextArea.setScript(scriptLeaf, true); + abcPanel.decompiledTextArea.setNoTrait(); + return null; + } + + @Override + protected void onStart() { + Main.startWork(translate("work.decompiling") + "...", this); + } + + @Override + protected void done() { + View.execInEventDispatch(() -> { + setSourceWorker = null; + try { + get(); + } catch (CancellationException ex) { + abcPanel.decompiledTextArea.setText("// " + AppStrings.translate("work.canceled")); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error", ex); + getABCPanel().decompiledTextArea.setText("// " + AppStrings.translate("decompilationError") + ": " + ex); + } + + Main.stopWork(); + }); + } + }; + + worker.execute(); + setSourceWorker = worker; + } + + showDetail(DETAILCARDAS3NAVIGATOR); + showCard(CARDACTIONSCRIPT3PANEL); + return; + } + + if (treeItem instanceof Tag) { + Tag tag = (Tag) treeItem; + TagInfo tagInfo = new TagInfo(); + tag.getTagInfo(tagInfo); + if (!tagInfo.isEmpty()) { + tagInfoPanel.setTagInfos(tagInfo); + showDetail(DETAILCARDTAGINFO); + } else { + showDetail(DETAILCARDEMPTYPANEL); + } + } else { + showDetail(DETAILCARDEMPTYPANEL); + } + + if (treeItem instanceof HeaderItem) { + headerPanel.load(((HeaderItem) treeItem).getSwf()); + showCard(CARDHEADER); + } else if (treeItem instanceof FolderItem) { + showFolderPreview((FolderItem) treeItem); + } else if (treeItem instanceof SWF) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof MetadataTag) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof DefineBinaryDataTag) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof ASMSource && (!(treeItem instanceof DrawableTag) || preferScript)) { + getActionPanel().setSource((ASMSource) treeItem, !forceReload); + showCard(CARDACTIONSCRIPTPANEL); + } else if (treeItem instanceof ImageTag) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof DrawableTag) && (!(treeItem instanceof TextTag)) && (!(treeItem instanceof FontTag)) && internalViewer) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof FontTag) && internalViewer) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof TextTag) && internalViewer) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof Frame && internalViewer) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof SoundTag)) { //&& isInternalFlashViewerSelected() && (Arrays.asList("mp3", "wav").contains(((SoundTag) tagObj).getExportFormat())))) { + showPreview(treeItem, previewPanel); + showCard(CARDPREVIEWPANEL); + } else if ((treeItem instanceof Frame) || (treeItem instanceof CharacterTag) || (treeItem instanceof FontTag) || (treeItem instanceof SoundStreamHeadTypeTag)) { + showPreview(treeItem, previewPanel); + + showCard(CARDPREVIEWPANEL); + } else if (treeItem instanceof Tag) { + showGenericTag((Tag) treeItem); + } else { + showCard(CARDEMPTYPANEL); + } + } + + public void repaintTree() { + tagTree.repaint(); + reload(true); + } + + public void showGenericTag(Tag tag) { + previewPanel.showGenericTagPanel(tag); + showCard(CARDPREVIEWPANEL); + } + + public void showTextTagWithNewValue(TextTag textTag, TextTag newTextTag) { + + previewPanel.showTextComparePanel(textTag, newTextTag); + } + + private void showFolderPreview(FolderItem item) { + List folderPreviewItems = new ArrayList<>(); + String folderName = item.getName(); + SWF swf = item.swf; + switch (folderName) { + case TagTreeModel.FOLDER_SHAPES: + for (Tag tag : swf.getTags()) { + if (tag instanceof ShapeTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_MORPHSHAPES: + for (Tag tag : swf.getTags()) { + if (tag instanceof MorphShapeTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_SPRITES: + for (Tag tag : swf.getTags()) { + if (tag instanceof DefineSpriteTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_BUTTONS: + for (Tag tag : swf.getTags()) { + if (tag instanceof ButtonTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_FONTS: + for (Tag tag : swf.getTags()) { + if (tag instanceof FontTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_FRAMES: + for (Frame frame : swf.getTimeline().getFrames()) { + folderPreviewItems.add(frame); + } + break; + case TagTreeModel.FOLDER_IMAGES: + for (Tag tag : swf.getTags()) { + if (tag instanceof ImageTag) { + folderPreviewItems.add(tag); + } + } + break; + case TagTreeModel.FOLDER_TEXTS: + for (Tag tag : swf.getTags()) { + if (tag instanceof TextTag) { + folderPreviewItems.add(tag); + } + } + break; + } + + folderPreviewPanel.setItems(folderPreviewItems); + showCard(CARDFOLDERPREVIEWPANEL); + } + + private boolean isFreeing; + + @Override + public boolean isFreeing() { + return isFreeing; + } + + @Override + public void free() { + isFreeing = true; + } + + public void setErrorState(ErrorState errorState) { + statusPanel.setErrorState(errorState); + } + + public static Timelined makeTimelined(final Tag tag) { + return makeTimelined(tag, -1); + } + + public static Timelined makeTimelined(final Tag tag, final int fontFrameNum) { + + return new Timelined() { + private Timeline tim; + + @Override + public Timeline getTimeline() { + if (tim == null) { + Timeline timeline = new Timeline(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); + initTimeline(timeline); + tim = timeline; + } + + return tim; + } + + @Override + public void resetTimeline() { + if (tim != null) { + tim.reset(tag.getSwf(), this, ((CharacterTag) tag).getCharacterId(), getRect()); + initTimeline(tim); + } + } + + private void initTimeline(Timeline timeline) { + if (tag instanceof MorphShapeTag) { + timeline.frameRate = MORPH_SHAPE_ANIMATION_FRAME_RATE; + int framesCnt = (int) (timeline.frameRate * MORPH_SHAPE_ANIMATION_LENGTH); + for (int i = 0; i < framesCnt; i++) { + Frame f = new Frame(timeline, i); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + ds.ratio = i * 65535 / framesCnt; + f.layers.put(1, ds); + f.layersChanged = true; + timeline.addFrame(f); + } + } else if (tag instanceof FontTag) { + int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); + int frame = fontFrameNum; + if (frame < 0 || frame >= pageCount) { + frame = 0; + } + + Frame f = new Frame(timeline, 0); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + f.layers.put(1, ds); + f.layersChanged = true; + timeline.addFrame(f); + timeline.fontFrameNum = frame; + } else { + Frame f = new Frame(timeline, 0); + DepthState ds = new DepthState(tag.getSwf(), f); + ds.characterId = ((CharacterTag) tag).getCharacterId(); + ds.matrix = new MATRIX(); + f.layers.put(1, ds); + timeline.addFrame(f); + } + timeline.displayRect = getRect(); + } + + @Override + public RECT getRect() { + return getRect(new HashSet<>()); + } + + @Override + public RECT getRect(Set added) { + BoundedTag bt = (BoundedTag) tag; + if (!added.contains(bt)) { + return bt.getRect(added); + } + return new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE); + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + + @Override + public void setModified(boolean value) { + } + + @Override + public ReadOnlyTagList getTags() { + return ReadOnlyTagList.EMPTY; + } + + @Override + public void removeTag(int index) { + } + + @Override + public void removeTag(Tag tag) { + } + + @Override + public void addTag(Tag tag) { + } + + @Override + public void addTag(int index, Tag tag) { + } + + @Override + public void replaceTag(int index, Tag newTag) { + } + }; + } + + private void disposeInner(Container container) { + for (Component c : container.getComponents()) { + if (c instanceof Container) { + Container c2 = (Container) c; + disposeInner(c2); + } + } + + container.removeAll(); + container.setLayout(null); + if (container instanceof TagEditorPanel) { + Helper.emptyObject(container); + } + } + + public void dispose() { + setDropTarget(null); + disposeInner(this); + Helper.emptyObject(this); + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java b/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java index d7d926a4a..532fb929c 100644 --- a/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java +++ b/src/com/jpexs/decompiler/flash/gui/TreeNodeType.java @@ -1,54 +1,54 @@ -/* - * Copyright (C) 2010-2016 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui; - -/** - * - * @author JPEXS - */ -public enum TreeNodeType { - - UNKNOWN, - FLASH, - FONT, - TEXT, - IMAGE, - SHAPE, - MORPH_SHAPE, - SPRITE, - BUTTON, - AS, - PACKAGE, - FRAME, - SHOW_FRAME, - MOVIE, - SOUND, - BINARY_DATA, - OTHER_TAG, - FOLDER, - FOLDER_OPEN, - BUNDLE_ZIP, - BUNDLE_SWC, - BUNDLE_BINARY, - BUNDLE_IGGY, - HEADER, - SET_BACKGROUNDCOLOR, - FILE_ATTRIBUTES, - METADATA, - PLACE_OBJECT, - REMOVE_OBJECT -} +/* + * Copyright (C) 2010-2016 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui; + +/** + * + * @author JPEXS + */ +public enum TreeNodeType { + + UNKNOWN, + FLASH, + FONT, + TEXT, + IMAGE, + SHAPE, + MORPH_SHAPE, + SPRITE, + BUTTON, + AS, + PACKAGE, + FRAME, + SHOW_FRAME, + MOVIE, + SOUND, + BINARY_DATA, + OTHER_TAG, + FOLDER, + FOLDER_OPEN, + BUNDLE_ZIP, + BUNDLE_SWC, + BUNDLE_BINARY, + BUNDLE_IGGY, + HEADER, + SET_BACKGROUNDCOLOR, + FILE_ATTRIBUTES, + METADATA, + PLACE_OBJECT, + REMOVE_OBJECT +} diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties index 5d45328f7..c6b0e091f 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame.properties @@ -1,778 +1,778 @@ -# Copyright (C) 2010-2016 JPEXS -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -menu.file = File -menu.file.open = Open... -menu.file.save = Save -menu.file.saveas = Save as... -menu.file.export.fla = Export to FLA -menu.file.export.all = Export all parts -menu.file.export.selection = Export selection -menu.file.exit = Exit - -menu.tools = Tools -menu.tools.searchas = Search All ActionScript... -menu.tools.proxy = Proxy -menu.tools.deobfuscation = Deobfuscation -menu.tools.deobfuscation.pcode = P-code deobfuscation... -menu.tools.deobfuscation.globalrename = Globally rename identifier -menu.tools.deobfuscation.renameinvalid = Rename invalid identifiers -menu.tools.gotoDocumentClass = Go to document class - -menu.settings = Settings -menu.settings.autodeobfuscation = Automatic deobfuscation -menu.settings.internalflashviewer = Use own Flash viewer -menu.settings.parallelspeedup = Parallel SpeedUp -menu.settings.disabledecompilation = Disable decompilation (Disassemble only) -menu.settings.addtocontextmenu = Add FFDec to SWF files context menu -menu.settings.language = Change language -menu.settings.cacheOnDisk = Use caching on disk -menu.settings.gotoMainClassOnStartup = Highlight document class on startup - -menu.help = Help -menu.help.checkupdates = Check for updates... -menu.help.helpus = Help us! -menu.help.homepage = Visit homepage -menu.help.about = About... - -contextmenu.remove = Remove - -button.save = Save -button.edit = Edit -button.cancel = Cancel -button.replace = Replace... - -notavailonthisplatform = Preview of this object is not available on this platform (Windows only). - -swfpreview = SWF preview -swfpreview.internal = SWF preview (Internal viewer) - -parameters = Parameters - -rename.enternew = Enter new name: - -rename.finished.identifier = Identifier renamed. -rename.finished.multiname = %count% multiname(s) renamed. - -node.texts = texts -node.images = images -node.movies = movies -node.sounds = sounds -node.binaryData = binaryData -node.fonts = fonts -node.sprites = sprites -node.shapes = shapes -node.morphshapes = morphshapes -node.buttons = buttons -node.frames = frames -node.scripts = scripts - -message.warning = Warning -message.confirm.experimental = Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue? -message.confirm.parallel = Parallelism can speed up loading and decompilation but uses more memory. -message.confirm.on = Do you want to turn this ON? -message.confirm.off = Do you want to turn this OFF? -message.confirm = Confirm - -message.confirm.autodeobfuscate = Automatic deobfuscation is a way to decompile obfuscated code.\r\nDeobfuscation leads to slower decompilation and some of the dead code may be eliminated.\r\nIf the code is not obfuscated, it's better to turn autodeobfuscation off. - -message.parallel = Parallelism -message.trait.saved = Trait successfully saved - -message.constant.new.string = String "%value%" is not present in constants table. Do you want to add it? -message.constant.new.string.title = Add String -message.constant.new.integer = Integer value "%value%" is not present in constants table. Do you want to add it? -message.constant.new.integer.title = Add Integer -message.constant.new.unsignedinteger = Unsigned integer value "%value%" is not present in constants table. Do you want to add it? -message.constant.new.unsignedinteger.title = Add Unsigned integer -message.constant.new.double = Double value "%value%" is not present in constants table. Do you want to add it? -message.constant.new.double.title = Add Double - -work.buffering = Buffering -work.waitingfordissasembly = Waiting for disassembly -work.gettinghilights = Getting highlights -work.disassembling = Disassembling -work.exporting = Exporting -work.searching = Searching -work.renaming = Renaming -work.exporting.fla = Exporting FLA -work.renaming.identifiers = Renaming identifiers -work.deobfuscating = Deobfuscating -work.decompiling = Decompiling -work.gettingvariables = Getting variables -work.reading.swf = Reading SWF -work.creatingwindow = Creating window -work.buildingscripttree = Building script tree - -work.deobfuscating.complete = Deobfuscation complete - -message.search.notfound = String "%searchtext%" not found. -message.search.notfound.title = Not found - -message.rename.notfound.multiname = No multiname found under cursor -message.rename.notfound.identifier = No identifier found under cursor -message.rename.notfound.title = Not found -message.rename.renamed = Identifiers renamed: %count% - -filter.images = Images (%extensions%) -filter.fla = %version% Document (*.fla) -filter.xfl = %version% Uncompressed Document (*.xfl) -filter.swf = SWF files (*.swf) - -error = Error -error.image.invalid = Invalid image. - -error.text.invalid = Invalid text: %text% on line %line% -error.file.save = Cannot save file -error.file.write = Cannot write to the file -error.export = Error during export - -export.select.directory = Select directory to export -export.finishedin = Exported in %time% - -update.check.title = Update check -update.check.nonewversion = No new version available. - -message.helpus = Please visit\r\n%url%\r\nfor details. -message.homepage = Visit homepage at: \r\n%url% - -proxy = Proxy -proxy.start = Start proxy -proxy.stop = Stop proxy -proxy.show = Show proxy -exit = Exit - -panel.disassembled = P-code source -panel.decompiled = ActionScript source - -search.info = Search for "%text%": -search.script = Script - -constants = Constants -traits = Traits - -pleasewait = Please wait - -#DEPRECATED - see abc.detail.trait.* -abc.detail.methodtrait = Method/Getter/Setter Trait -abc.detail.unsupported = - -#DEPRECATED - see abc.detail.trait.* -abc.detail.slotconsttrait = Slot/Const Trait -abc.detail.traitname = Name: - -abc.detail.body.params.maxstack = Max stack: -abc.detail.body.params.localregcount = Local registers count: -abc.detail.body.params.minscope = Minimum scope depth: -abc.detail.body.params.maxscope = Maximum scope depth: -abc.detail.body.params.autofill = Auto fill on code save (GLOBAL SETTING) -abc.detail.body.params.autofill.experimental = ...EXPERIMENTAL - -abc.detail.methodinfo.methodindex = Method Index: -abc.detail.methodinfo.parameters = Parameters: -abc.detail.methodinfo.returnvalue = Return value type: - -error.methodinfo.params = MethodInfo Params Error -error.methodinfo.returnvalue = MethodInfo Return value type Error - -abc.detail.methodinfo = MethodInfo -abc.detail.body.code = MethodBody Code -abc.detail.body.params = MethodBody params - -abc.detail.slotconst.typevalue = Type and Value: - -error.slotconst.typevalue = SlotConst type value Error - -message.autofill.failed = Cannot get code stats for automatic body params.\r\nUncheck autofill to avoid this message. -info.selecttrait = Select class and click a trait in Actionscript source to edit it. - -button.viewgraph = View Graph -button.viewhex = View Hex - -abc.traitslist.instanceinitializer = instance initializer -abc.traitslist.classinitializer = class initializer - -action.edit.experimental = (Experimental) - -message.action.saved = Code successfully saved - -error.action.save = %error% on line %line% - -message.confirm.remove = Are you sure you want to remove %item%\n and all objects which depend on it? - -#after version 1.6.5u1: - -button.ok = OK -button.cancel = Cancel - -font.name = Font name: -font.isbold = Is bold: -font.isitalic = Is italic: -font.ascent = Ascent: -font.descent = Descent: -font.leading = Leading: -font.characters = Characters: -font.characters.add = Add characters: -value.unknown = ? - -yes = yes -no = no - -errors.present = There are ERRORS in the log. Click to view. -errors.none = There are no errors in the log. - -#after version 1.6.6: - -dialog.message.title = Message -dialog.select.title = Select an Option - -button.yes = Yes -button.no = No - -FileChooser.openButtonText = Open -FileChooser.openButtonToolTipText = Open -FileChooser.lookInLabelText = Look in: -FileChooser.acceptAllFileFilterText = All Files -FileChooser.filesOfTypeLabelText = Files of type: -FileChooser.fileNameLabelText = File name: -FileChooser.listViewButtonToolTipText = List -FileChooser.listViewButtonAccessibleName = List -FileChooser.detailsViewButtonToolTipText = Details -FileChooser.detailsViewButtonAccessibleName = Details -FileChooser.upFolderToolTipText = Up One Level -FileChooser.upFolderAccessibleName = Up One Level -FileChooser.homeFolderToolTipText = Home -FileChooser.homeFolderAccessibleName = Home -FileChooser.fileNameHeaderText = Name -FileChooser.fileSizeHeaderText = Size -FileChooser.fileTypeHeaderText = Type -FileChooser.fileDateHeaderText = Date -FileChooser.fileAttrHeaderText = Attributes -FileChooser.openDialogTitleText = Open -FileChooser.directoryDescriptionText = Directory -FileChooser.directoryOpenButtonText = Open -FileChooser.directoryOpenButtonToolTipText = Open selected directory -FileChooser.fileDescriptionText = Generic File -FileChooser.helpButtonText = Help -FileChooser.helpButtonToolTipText = FileChooser help -FileChooser.newFolderAccessibleName = New Folder -FileChooser.newFolderErrorText = Error creating new folder -FileChooser.newFolderToolTipText = Create New Folder -FileChooser.other.newFolder = NewFolder -FileChooser.other.newFolder.subsequent = NewFolder.{0} -FileChooser.win32.newFolder = New Folder -FileChooser.win32.newFolder.subsequent = New Folder ({0}) -FileChooser.saveButtonText = Save -FileChooser.saveButtonToolTipText = Save selected file -FileChooser.saveDialogTitleText = Save -FileChooser.saveInLabelText = Save in: -FileChooser.updateButtonText = Update -FileChooser.updateButtonToolTipText = Update directory listing - -#after version 1.6.6u2: - -FileChooser.detailsViewActionLabel.textAndMnemonic = Details -FileChooser.detailsViewButtonToolTip.textAndMnemonic = Details -FileChooser.fileAttrHeader.textAndMnemonic = Attributes -FileChooser.fileDateHeader.textAndMnemonic = Modified -FileChooser.fileNameHeader.textAndMnemonic = Name -FileChooser.fileNameLabel.textAndMnemonic = File name: -FileChooser.fileSizeHeader.textAndMnemonic = Size -FileChooser.fileTypeHeader.textAndMnemonic = Type -FileChooser.filesOfTypeLabel.textAndMnemonic = Files of type: -FileChooser.folderNameLabel.textAndMnemonic = Folder name: -FileChooser.homeFolderToolTip.textAndMnemonic = Home -FileChooser.listViewActionLabel.textAndMnemonic = List -FileChooser.listViewButtonToolTip.textAndMnemonic = List -FileChooser.lookInLabel.textAndMnemonic = Look in: -FileChooser.newFolderActionLabel.textAndMnemonic = New Folder -FileChooser.newFolderToolTip.textAndMnemonic = Create New Folder -FileChooser.refreshActionLabel.textAndMnemonic = Refresh -FileChooser.saveInLabel.textAndMnemonic = Save in: -FileChooser.upFolderToolTip.textAndMnemonic = Up One Level -FileChooser.viewMenuButtonAccessibleName = View Menu -FileChooser.viewMenuButtonToolTipText = View Menu -FileChooser.viewMenuLabel.textAndMnemonic = View -FileChooser.newFolderActionLabelText = New Folder -FileChooser.listViewActionLabelText = List -FileChooser.detailsViewActionLabelText = Details -FileChooser.refreshActionLabelText = Refresh -FileChooser.sortMenuLabelText = Arrange Icons By -FileChooser.viewMenuLabelText = View -FileChooser.fileSizeKiloBytes = {0} KB -FileChooser.fileSizeMegaBytes = {0} MB -FileChooser.fileSizeGigaBytes = {0} GB -FileChooser.folderNameLabelText = Folder name: - -error.occured = Error occurred: %error% -button.abort = Abort -button.retry = Retry -button.ignore = Ignore - -font.source = Source Font: - -#after version 1.6.7: - -menu.export = Export -menu.general = General -menu.language = Language - -startup.welcometo = Welcome to -startup.selectopen = Click Open icon on the top panel or drag SWF file to this window to start. - -error.font.nocharacter = Selected source font does not contain character "%char%". - -warning.initializers = Static fields and consts are often initialized in initializers.\nEditing value here is usually not enough! - -#after version 1.7.0u1: - -menu.tools.searchMemory = Search SWFs in memory -menu.file.reload = Reload -message.confirm.reload = This action cancels all unsaved changes and reloads the SWF file again.\nDo you want to continue? - -dialog.selectbkcolor.title = Select background color for SWF display -button.selectbkcolor.hint = Select background color - -ColorChooser.okText = OK -ColorChooser.cancelText = Cancel -ColorChooser.resetText = Reset -ColorChooser.previewText = Preview -ColorChooser.swatchesNameText = Swatches -ColorChooser.swatchesRecentText = Recent: -ColorChooser.sampleText = Sample Text Sample Text - -#after version 1.7.1: - -preview.play = Play -preview.pause = Pause -preview.stop = Stop - -message.confirm.removemultiple = Are you sure you want to remove %count% items\n and all objects which depend on it? - -menu.tools.searchCache = Search browsers cache - -#after version 1.7.2u2 - -error.trait.exists = Trait with name "%name%" already exists. -button.addtrait = Add trait -button.font.embed = Embed... -button.yes.all = Yes to all -button.no.all = No to all -message.font.add.exists = Character %char% already exists in the font tag.\nDo you want to replace it? - -filter.gfx = ScaleForm GFx files (*.gfx) -filter.supported = All supported filetypes -work.canceled = Canceled -work.restoringControlFlow = Restoring control flow -menu.advancedsettings.advancedsettings = Advanced Settings -menu.recentFiles = Recent files - -#after version 1.7.4 -work.restoringControlFlow.complete = Control flow restored -message.confirm.recentFileNotFound = File not found. Do you want to remove it from the recent file list? -contextmenu.closeSwf = Close SWF -menu.settings.autoRenameIdentifiers = Auto rename identifiers -menu.file.saveasexe = Save as Exe... -filter.exe = Executable files (*.exe) - -#after version 1.8.0 -font.updateTexts = Update texts - -#after version 1.8.0u1 -menu.file.close = Close -menu.file.closeAll = Close all -menu.tools.otherTools = Other -menu.tools.otherTools.clearRecentFiles = Clear recent files -fontName.name = Font display name: -fontName.copyright = Font copyright: -button.preview = Preview -button.reset = Reset -errors.info = There are INFORMATIONS in the log. Click to view. -errors.warning = There are WARNINGS in the log. Click to view. - -decompilationError = Decompilation error - -disassemblingProgress.toString = toString -disassemblingProgress.reading = Reading -disassemblingProgress.deobfuscating = Deobfuscating - -contextmenu.moveTag = Move tag to - -filter.swc = SWC component files (*.swc) -filter.zip = ZIP compressed files (*.zip) -filter.binary = Binary search - all files (*.*) - -open.error = Error -open.error.fileNotFound = File not found -open.error.cannotOpen = Cannot open file - -node.others = others - -#after version 1.8.1 -menu.tools.search = Text Search - -#after version 1.8.1u1 -menu.tools.timeline = Timeline - -dialog.selectcolor.title = Select color -button.selectcolor.hint = Click to select color - -#default item name, will be used in following sentences -generictag.array.item = item -generictag.array.insertbeginning = Insert %item% at the beginning -generictag.array.insertbefore = Insert %item% before -generictag.array.remove = Remove %item% -generictag.array.insertafter = Insert %item% after -generictag.array.insertend = Insert %item% at the end - -#after version 2.0.0 -contextmenu.expandAll = Expand all - -filter.sounds = Supported sound formats (*.wav, *.mp3) -filter.sounds.wav = Wave file format (*.wav) -filter.sounds.mp3 = MP3 compressed format (*.mp3) - -error.sound.invalid = Invalid sound. - -button.prev = Previous -button.next = Next - -#after version 2.1.0 -message.action.playerglobal.title = PlayerGlobal library needed -message.action.playerglobal.needed = For ActionScript 3 direct editation, a library called "PlayerGlobal.swc" needs to be downloaded from Adobe homepage.\r\n%adobehomepage%\r\nPress OK to go to the download page. -message.action.playerglobal.place = Download the library called PlayerGlobal(.swc), and place it to directory\r\n%libpath%\r\n Press OK to continue. - -message.confirm.experimental.function = This function is EXPERIMENTAL. It means that you should not trust the results and the SWF file can be disfunctional after saving. -message.confirm.donotshowagain = Do not show again - -menu.import = Import -menu.file.import.text = Import text -import.select.directory = Select directory to import -error.text.import = Error during text import. Do you want to continue? - -#after version 2.1.1 -contextmenu.removeWithDependencies = Remove with dependencies - -abc.action.find-usages = Find usages -abc.action.find-declaration = Find declaration - -contextmenu.rawEdit = Raw edit -contextmenu.jumpToCharacter = Jump to character - -menu.settings.dumpView = Dump view - -menu.view = View -menu.file.view.resources = Resources -menu.file.view.hex = Hex dump - -node.header = header - -header.signature = Signature: -header.compression = Compression: -header.compression.lzma = LZMA -header.compression.zlib = ZLIB -header.compression.none = No compression -header.version = SWF Version: -header.gfx = GFX: -header.filesize = File size: -header.framerate = Frame rate: -header.framecount = Frame count: -header.displayrect = Display rect: -header.displayrect.value.twips = %xmin%,%ymin% => %xmax%,%ymax% twips -header.displayrect.value.pixels = %xmin%,%ymin% => %xmax%,%ymax% pixels - -#after version 2.1.2 -contextmenu.saveToFile = Save to File -contextmenu.parseActions = Parse actions -contextmenu.parseABC = Parse ABC -contextmenu.parseInstructions = Parse AVM2 Instructions - -#after version 2.1.3 -menu.deobfuscation = Deobfuscation -menu.file.deobfuscation.old = Old style -menu.file.deobfuscation.new = New style - -#after version 2.1.4 -contextmenu.openswfinside = Open SWF inside -binarydata.swfInside = It looks like there is SWF inside this binary data tag. Click here to load it as subtree. - -#after version 3.0.0 -button.zoomin.hint = Zoom in -button.zoomout.hint = Zoom out -button.zoomfit.hint = Zoom to fit -button.zoomnone.hint = Zoom to 1:1 -button.snapshot.hint = Take snapshot into clipboard - -editorTruncateWarning = Text truncated at position %chars% in debug mode. - -#Font name which is presented in the SWF Font tag -font.name.intag = Font name in tag: - -menu.debugger = Debugger -menu.debugger.switch = Debugger -menu.debugger.replacetrace = Replace trace calls -menu.debugger.showlog = Show Log - -message.debugger = This SWF Debugger can only be used to print messages to log window, browser console or alerts.\r\nIt is NOT designed for features like step code, breakpoints etc. - -contextmenu.addTag = Add tag - -deobfuscation.comment.tryenable = Tip: You can try enabling "Automatic deobfuscation" in Settings -deobfuscation.comment.failed = Deobfuscation is activated but decompilation still failed. If the file is NOT obfuscated, disable "Automatic deobfuscation" for better results. - -#after version 4.0.2 -preview.nextframe = Next frame -preview.prevframe = Previous frame -preview.gotoframe = Goto frame... - -preview.gotoframe.dialog.title = Goto frame -preview.gotoframe.dialog.message = Enter frame number (%min% - %max%) -preview.gotoframe.dialog.frame.error = Invalid frame number. It must be number between %min% and %max%. - -error.text.invalid.continue = Invalid text: %text% on line %line%. Do you want to continue? - -#after version 4.0.5 -contextmenu.copyTag = Copy tag to -fit = fit -button.setAdvanceValues = Set advance values - -menu.tools.replace = Text Replace - -message.confirm.close = There are unsaved changes. Do you really want to close {swfName}? -message.confirm.closeAll = There are unsaved changes. Do you really want to close all SWFs? - -contextmenu.exportJavaSource = Export Java Source -contextmenu.exportSwfXml = Export SWF as XML -contextmenu.importSwfXml = Import SWF XML - -filter.xml = XML - -#after version 4.1.0 -contextmenu.undo = Undo - -text.align.left = Left align -text.align.right = Right align -text.align.center = Center align -text.align.justify = Justify align - -text.undo = Undo changes - -menu.file.import.xml = Import SWF XML -menu.file.export.xml = Export SWF XML - -#after version 4.1.1 -text.align.translatex.decrease = Decrease TranslateX -text.align.translatex.increase = Increase TranslateX -selectPreviousTag = Select previous tag -selectNextTag = Select next tag -button.ignoreAll = Ignore All -menu.file.import.symbolClass = Import Symbol-Class -text.toggleCase = Toggle case - -#after version 5.0.2 -preview.loop = Loop -menu.file.import.script = Import script -contextmenu.copyTagWithDependencies = Copy tag with dependencies to -button.replaceWithTag = Replace with other character tag -button.resolveConstants = Resolve constants - -#after version 5.1.0 -button.viewConstants = View Constants -work.exported = Exported -button.replaceAlphaChannel = Replace alpha channel... - -tagInfo.header.name = Name -tagInfo.header.value = Value -tagInfo.tagType = Tag Type -tagInfo.characterId = Character Id -tagInfo.offset = Offset -tagInfo.length = Length -tagInfo.bounds = Bounds -tagInfo.width = Width -tagInfo.height = Height -tagInfo.neededCharacters = Needed Characters - -button.viewhexpcode = View Hex with instructions -taginfo.header = Basic tag info - -tagInfo.dependentCharacters = Dependent Characters - -#after version 5.3.0 -header.uncompressed = Uncompressed -header.warning.unsupportedGfxCompression = GFX supports only uncompressed or Zlib compressed content. -header.warning.minimumZlibVersion = Zlib compression needs SWF version 6 or greater. -header.warning.minimumLzmaVersion = LZMA compression needs SWF version 13 or greater. - -tagInfo.codecName = Codec Name -tagInfo.exportFormat = Export Format -tagInfo.samplingRate = Sampling Rate -tagInfo.stereo = Stereo -tagInfo.sampleCount = Sample Count - -filter.dmg = Mac Executable files (*.dmg) -filter.linuxExe = Linux Executable files - -import.script.result = %count% scripts imported. -import.script.as12warning = Import script can import only AS1/2 scripts. - -error.constantPoolTooBig = Constant pool is too big. index=%index%, size=%size% -error.image.alpha.invalid = Invalid alpha channel data. - -#after version 6.0.2 -contextmenu.saveUncompressedToFile = Save to Uncompressed File -abc.traitslist.scriptinitializer = script initializer -menu.settings.autoOpenLoadedSWFs = Open loaded SWFs while playing - -#after version 6.1.1 -menu.file.start = Start -menu.file.start.run = Run -menu.file.start.stop = Stop -menu.file.start.debug = Debug -menu.debugging = Debugging -menu.debugging.debug = Debug -menu.debugging.debug.stop = Stop -menu.debugging.debug.pause = Pause -menu.debugging.debug.stepOver = Step over -menu.debugging.debug.stepInto = Step into -menu.debugging.debug.stepOut = Step out -menu.debugging.debug.continue = Continue -menu.debugging.debug.stack = Stack... -menu.debugging.debug.watch = New watch... - -message.playerpath.notset = Flash Player projector not found. Please configure its path in Advanced Settings / Paths (1). -message.playerpath.debug.notset = Flash Player projector content debugger not found. Please configure its path in Advanced Settings / Paths (2). -message.playerpath.lib.notset = PlayerGlobal (.SWC) not found. Please configure its path in Advanced Settings / Paths (3). - -debugpanel.header = Debugging - -variables.header.registers = Registers -variables.header.locals = Locals -variables.header.arguments = Arguments -variables.header.scopeChain = Scope chain -variables.column.name = Name -variables.column.type = Type -variables.column.value = Value - -callStack.header = Call stack -callStack.header.file = File -callStack.header.line = Line - -stack.header = Stack -stack.header.item = Item - -constantpool.header = Constant pool -constantpool.header.id = Id -constantpool.header.value = Value - -work.running = Running -work.debugging = Debugging -work.debugging.instrumenting = Preparing SWF for debugging -work.breakat = Break at\u0020 -work.halted = Debugging started, execution halted. Add breakpoints and click Continue (F5) to resume running. - -debuglog.header = Log -debuglog.button.clear = Clear - -#after 7.0.1 -work.debugging.wait = Waiting for Flash debug projector to connect - -error.debug.listen = Cannot listen on port %port%. There might be other flash debugger running. - -debug.break.reason.unknown = (Unknown) -debug.break.reason.breakpoint = (Breakpoint) -debug.break.reason.watch = (Watch) -debug.break.reason.fault = (Fault) -debug.break.reason.stopRequest = (Stop request) -debug.break.reason.step = (Step) -debug.break.reason.halt = (Halt) -debug.break.reason.scriptLoaded = (Script loaded) - -menu.file.start.debugpcode = Debug P-code - -#after 7.1.2 -button.replaceNoFill = Replace - Update bounds... -message.warning.svgImportExperimental = Not all SVG features are supported. Please check the log after import. - -message.imported.swf = The SWF file uses assets from an imported SWF file:\n%url%\nDo you want the assets to be loaded from that URL? -message.imported.swf.manually = Cannot load imported SWF\n%url%\nThe file or URL does not exist.\nDo you want to select local file? - -message.warning.hexViewNotUpToDate = Hex View is not up-to-date. Please save and reload the file to update Hex View. -message.font.replace.updateTexts = Some characters were replaced. Do you want to update the existing texts? - -menu.settings.simplifyExpressions = Simplify expressions - -#after 8.0.1 -menu.recentFiles.empty = Recent file list is empty -message.warning.outOfMemory32BitJre = OutOfMemory error occurred. You are running 32bit Java on 64bit system. Please use 64bit Java. - -menu.file.reloadAll = Reload all -message.confirm.reloadAll = This action cancels all unsaved changes in all SWF files and reloads whole application again.\nDo you want to continue? -export.script.singleFilePallelModeWarning = Single file script export is not supported with enabled parallel speedup - -button.showOriginalBytesInPcodeHex = Show original bytes -button.remove = Remove -button.showFileOffsetInPcodeHex = Show file offset - -generic.editor.amf3.title = AMF3 editor -generic.editor.amf3.help = AMF3 value syntax:\n\ - ------------------\n\ - scalar types:\n\ - %scalar_samples%\ - other types:\n\ - %nonscalar_samples%\ - \n\ - Notes:\n\ - \ * Nonscalar datatypes can be referenced by previously declared "id" attributes with # syntax:\n\ - %reference_sample%\n\ - \ * Keys in Dictionary entries can be any type\n -contextmenu.showInResources = Show in Resources -message.flexpath.notset = Flex SDK not found. Please configure its path in Advanced Settings / Paths (4). - - -#add after panel.disassembled string -abc.detail.split = :\u0020 -abc.detail.trait = Trait - %trait_type% -abc.detail.trait.method = Method -abc.detail.trait.getter = Getter -abc.detail.trait.setter = Setter -abc.detail.trait.slot = Slot -abc.detail.trait.const = Const -abc.detail.trait.class = Class -abc.detail.trait.function = Function - -abc.detail.specialmethod = Special method - %specialmethod_type% -abc.detail.specialmethod.scriptinitializer = Script initializer -abc.detail.specialmethod.classinitializer = Class initializer -abc.detail.specialmethod.instanceinitializer = Instance initializer -abc.detail.innerfunction = Inner function - -button.edit.script.decompiled = Edit ActionScript -button.edit.script.disassembled = Edit P-code - -debug.watch.add = Add watch to %name% -debug.watch.add.read = Read -debug.watch.add.write = Write -debug.watch.add.readwrite = Read+Write- - -error.debug.watch.add = Cannot add watch to this variable. - -variables.column.scope = Scope -variables.column.flags = Flags -variables.column.trait = Trait - -message.font.setadvancevalues = This operation will set advance of ALL characters in this tag to selected font source advances. - -menu.tools.deobfuscation.renameColliding = Rename colliding traits/classes - -filter.iggy = Iggy files (*.iggy) +# Copyright (C) 2010-2016 JPEXS +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +menu.file = File +menu.file.open = Open... +menu.file.save = Save +menu.file.saveas = Save as... +menu.file.export.fla = Export to FLA +menu.file.export.all = Export all parts +menu.file.export.selection = Export selection +menu.file.exit = Exit + +menu.tools = Tools +menu.tools.searchas = Search All ActionScript... +menu.tools.proxy = Proxy +menu.tools.deobfuscation = Deobfuscation +menu.tools.deobfuscation.pcode = P-code deobfuscation... +menu.tools.deobfuscation.globalrename = Globally rename identifier +menu.tools.deobfuscation.renameinvalid = Rename invalid identifiers +menu.tools.gotoDocumentClass = Go to document class + +menu.settings = Settings +menu.settings.autodeobfuscation = Automatic deobfuscation +menu.settings.internalflashviewer = Use own Flash viewer +menu.settings.parallelspeedup = Parallel SpeedUp +menu.settings.disabledecompilation = Disable decompilation (Disassemble only) +menu.settings.addtocontextmenu = Add FFDec to SWF files context menu +menu.settings.language = Change language +menu.settings.cacheOnDisk = Use caching on disk +menu.settings.gotoMainClassOnStartup = Highlight document class on startup + +menu.help = Help +menu.help.checkupdates = Check for updates... +menu.help.helpus = Help us! +menu.help.homepage = Visit homepage +menu.help.about = About... + +contextmenu.remove = Remove + +button.save = Save +button.edit = Edit +button.cancel = Cancel +button.replace = Replace... + +notavailonthisplatform = Preview of this object is not available on this platform (Windows only). + +swfpreview = SWF preview +swfpreview.internal = SWF preview (Internal viewer) + +parameters = Parameters + +rename.enternew = Enter new name: + +rename.finished.identifier = Identifier renamed. +rename.finished.multiname = %count% multiname(s) renamed. + +node.texts = texts +node.images = images +node.movies = movies +node.sounds = sounds +node.binaryData = binaryData +node.fonts = fonts +node.sprites = sprites +node.shapes = shapes +node.morphshapes = morphshapes +node.buttons = buttons +node.frames = frames +node.scripts = scripts + +message.warning = Warning +message.confirm.experimental = Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue? +message.confirm.parallel = Parallelism can speed up loading and decompilation but uses more memory. +message.confirm.on = Do you want to turn this ON? +message.confirm.off = Do you want to turn this OFF? +message.confirm = Confirm + +message.confirm.autodeobfuscate = Automatic deobfuscation is a way to decompile obfuscated code.\r\nDeobfuscation leads to slower decompilation and some of the dead code may be eliminated.\r\nIf the code is not obfuscated, it's better to turn autodeobfuscation off. + +message.parallel = Parallelism +message.trait.saved = Trait successfully saved + +message.constant.new.string = String "%value%" is not present in constants table. Do you want to add it? +message.constant.new.string.title = Add String +message.constant.new.integer = Integer value "%value%" is not present in constants table. Do you want to add it? +message.constant.new.integer.title = Add Integer +message.constant.new.unsignedinteger = Unsigned integer value "%value%" is not present in constants table. Do you want to add it? +message.constant.new.unsignedinteger.title = Add Unsigned integer +message.constant.new.double = Double value "%value%" is not present in constants table. Do you want to add it? +message.constant.new.double.title = Add Double + +work.buffering = Buffering +work.waitingfordissasembly = Waiting for disassembly +work.gettinghilights = Getting highlights +work.disassembling = Disassembling +work.exporting = Exporting +work.searching = Searching +work.renaming = Renaming +work.exporting.fla = Exporting FLA +work.renaming.identifiers = Renaming identifiers +work.deobfuscating = Deobfuscating +work.decompiling = Decompiling +work.gettingvariables = Getting variables +work.reading.swf = Reading SWF +work.creatingwindow = Creating window +work.buildingscripttree = Building script tree + +work.deobfuscating.complete = Deobfuscation complete + +message.search.notfound = String "%searchtext%" not found. +message.search.notfound.title = Not found + +message.rename.notfound.multiname = No multiname found under cursor +message.rename.notfound.identifier = No identifier found under cursor +message.rename.notfound.title = Not found +message.rename.renamed = Identifiers renamed: %count% + +filter.images = Images (%extensions%) +filter.fla = %version% Document (*.fla) +filter.xfl = %version% Uncompressed Document (*.xfl) +filter.swf = SWF files (*.swf) + +error = Error +error.image.invalid = Invalid image. + +error.text.invalid = Invalid text: %text% on line %line% +error.file.save = Cannot save file +error.file.write = Cannot write to the file +error.export = Error during export + +export.select.directory = Select directory to export +export.finishedin = Exported in %time% + +update.check.title = Update check +update.check.nonewversion = No new version available. + +message.helpus = Please visit\r\n%url%\r\nfor details. +message.homepage = Visit homepage at: \r\n%url% + +proxy = Proxy +proxy.start = Start proxy +proxy.stop = Stop proxy +proxy.show = Show proxy +exit = Exit + +panel.disassembled = P-code source +panel.decompiled = ActionScript source + +search.info = Search for "%text%": +search.script = Script + +constants = Constants +traits = Traits + +pleasewait = Please wait + +#DEPRECATED - see abc.detail.trait.* +abc.detail.methodtrait = Method/Getter/Setter Trait +abc.detail.unsupported = - +#DEPRECATED - see abc.detail.trait.* +abc.detail.slotconsttrait = Slot/Const Trait +abc.detail.traitname = Name: + +abc.detail.body.params.maxstack = Max stack: +abc.detail.body.params.localregcount = Local registers count: +abc.detail.body.params.minscope = Minimum scope depth: +abc.detail.body.params.maxscope = Maximum scope depth: +abc.detail.body.params.autofill = Auto fill on code save (GLOBAL SETTING) +abc.detail.body.params.autofill.experimental = ...EXPERIMENTAL + +abc.detail.methodinfo.methodindex = Method Index: +abc.detail.methodinfo.parameters = Parameters: +abc.detail.methodinfo.returnvalue = Return value type: + +error.methodinfo.params = MethodInfo Params Error +error.methodinfo.returnvalue = MethodInfo Return value type Error + +abc.detail.methodinfo = MethodInfo +abc.detail.body.code = MethodBody Code +abc.detail.body.params = MethodBody params + +abc.detail.slotconst.typevalue = Type and Value: + +error.slotconst.typevalue = SlotConst type value Error + +message.autofill.failed = Cannot get code stats for automatic body params.\r\nUncheck autofill to avoid this message. +info.selecttrait = Select class and click a trait in Actionscript source to edit it. + +button.viewgraph = View Graph +button.viewhex = View Hex + +abc.traitslist.instanceinitializer = instance initializer +abc.traitslist.classinitializer = class initializer + +action.edit.experimental = (Experimental) + +message.action.saved = Code successfully saved + +error.action.save = %error% on line %line% + +message.confirm.remove = Are you sure you want to remove %item%\n and all objects which depend on it? + +#after version 1.6.5u1: + +button.ok = OK +button.cancel = Cancel + +font.name = Font name: +font.isbold = Is bold: +font.isitalic = Is italic: +font.ascent = Ascent: +font.descent = Descent: +font.leading = Leading: +font.characters = Characters: +font.characters.add = Add characters: +value.unknown = ? + +yes = yes +no = no + +errors.present = There are ERRORS in the log. Click to view. +errors.none = There are no errors in the log. + +#after version 1.6.6: + +dialog.message.title = Message +dialog.select.title = Select an Option + +button.yes = Yes +button.no = No + +FileChooser.openButtonText = Open +FileChooser.openButtonToolTipText = Open +FileChooser.lookInLabelText = Look in: +FileChooser.acceptAllFileFilterText = All Files +FileChooser.filesOfTypeLabelText = Files of type: +FileChooser.fileNameLabelText = File name: +FileChooser.listViewButtonToolTipText = List +FileChooser.listViewButtonAccessibleName = List +FileChooser.detailsViewButtonToolTipText = Details +FileChooser.detailsViewButtonAccessibleName = Details +FileChooser.upFolderToolTipText = Up One Level +FileChooser.upFolderAccessibleName = Up One Level +FileChooser.homeFolderToolTipText = Home +FileChooser.homeFolderAccessibleName = Home +FileChooser.fileNameHeaderText = Name +FileChooser.fileSizeHeaderText = Size +FileChooser.fileTypeHeaderText = Type +FileChooser.fileDateHeaderText = Date +FileChooser.fileAttrHeaderText = Attributes +FileChooser.openDialogTitleText = Open +FileChooser.directoryDescriptionText = Directory +FileChooser.directoryOpenButtonText = Open +FileChooser.directoryOpenButtonToolTipText = Open selected directory +FileChooser.fileDescriptionText = Generic File +FileChooser.helpButtonText = Help +FileChooser.helpButtonToolTipText = FileChooser help +FileChooser.newFolderAccessibleName = New Folder +FileChooser.newFolderErrorText = Error creating new folder +FileChooser.newFolderToolTipText = Create New Folder +FileChooser.other.newFolder = NewFolder +FileChooser.other.newFolder.subsequent = NewFolder.{0} +FileChooser.win32.newFolder = New Folder +FileChooser.win32.newFolder.subsequent = New Folder ({0}) +FileChooser.saveButtonText = Save +FileChooser.saveButtonToolTipText = Save selected file +FileChooser.saveDialogTitleText = Save +FileChooser.saveInLabelText = Save in: +FileChooser.updateButtonText = Update +FileChooser.updateButtonToolTipText = Update directory listing + +#after version 1.6.6u2: + +FileChooser.detailsViewActionLabel.textAndMnemonic = Details +FileChooser.detailsViewButtonToolTip.textAndMnemonic = Details +FileChooser.fileAttrHeader.textAndMnemonic = Attributes +FileChooser.fileDateHeader.textAndMnemonic = Modified +FileChooser.fileNameHeader.textAndMnemonic = Name +FileChooser.fileNameLabel.textAndMnemonic = File name: +FileChooser.fileSizeHeader.textAndMnemonic = Size +FileChooser.fileTypeHeader.textAndMnemonic = Type +FileChooser.filesOfTypeLabel.textAndMnemonic = Files of type: +FileChooser.folderNameLabel.textAndMnemonic = Folder name: +FileChooser.homeFolderToolTip.textAndMnemonic = Home +FileChooser.listViewActionLabel.textAndMnemonic = List +FileChooser.listViewButtonToolTip.textAndMnemonic = List +FileChooser.lookInLabel.textAndMnemonic = Look in: +FileChooser.newFolderActionLabel.textAndMnemonic = New Folder +FileChooser.newFolderToolTip.textAndMnemonic = Create New Folder +FileChooser.refreshActionLabel.textAndMnemonic = Refresh +FileChooser.saveInLabel.textAndMnemonic = Save in: +FileChooser.upFolderToolTip.textAndMnemonic = Up One Level +FileChooser.viewMenuButtonAccessibleName = View Menu +FileChooser.viewMenuButtonToolTipText = View Menu +FileChooser.viewMenuLabel.textAndMnemonic = View +FileChooser.newFolderActionLabelText = New Folder +FileChooser.listViewActionLabelText = List +FileChooser.detailsViewActionLabelText = Details +FileChooser.refreshActionLabelText = Refresh +FileChooser.sortMenuLabelText = Arrange Icons By +FileChooser.viewMenuLabelText = View +FileChooser.fileSizeKiloBytes = {0} KB +FileChooser.fileSizeMegaBytes = {0} MB +FileChooser.fileSizeGigaBytes = {0} GB +FileChooser.folderNameLabelText = Folder name: + +error.occured = Error occurred: %error% +button.abort = Abort +button.retry = Retry +button.ignore = Ignore + +font.source = Source Font: + +#after version 1.6.7: + +menu.export = Export +menu.general = General +menu.language = Language + +startup.welcometo = Welcome to +startup.selectopen = Click Open icon on the top panel or drag SWF file to this window to start. + +error.font.nocharacter = Selected source font does not contain character "%char%". + +warning.initializers = Static fields and consts are often initialized in initializers.\nEditing value here is usually not enough! + +#after version 1.7.0u1: + +menu.tools.searchMemory = Search SWFs in memory +menu.file.reload = Reload +message.confirm.reload = This action cancels all unsaved changes and reloads the SWF file again.\nDo you want to continue? + +dialog.selectbkcolor.title = Select background color for SWF display +button.selectbkcolor.hint = Select background color + +ColorChooser.okText = OK +ColorChooser.cancelText = Cancel +ColorChooser.resetText = Reset +ColorChooser.previewText = Preview +ColorChooser.swatchesNameText = Swatches +ColorChooser.swatchesRecentText = Recent: +ColorChooser.sampleText = Sample Text Sample Text + +#after version 1.7.1: + +preview.play = Play +preview.pause = Pause +preview.stop = Stop + +message.confirm.removemultiple = Are you sure you want to remove %count% items\n and all objects which depend on it? + +menu.tools.searchCache = Search browsers cache + +#after version 1.7.2u2 + +error.trait.exists = Trait with name "%name%" already exists. +button.addtrait = Add trait +button.font.embed = Embed... +button.yes.all = Yes to all +button.no.all = No to all +message.font.add.exists = Character %char% already exists in the font tag.\nDo you want to replace it? + +filter.gfx = ScaleForm GFx files (*.gfx) +filter.supported = All supported filetypes +work.canceled = Canceled +work.restoringControlFlow = Restoring control flow +menu.advancedsettings.advancedsettings = Advanced Settings +menu.recentFiles = Recent files + +#after version 1.7.4 +work.restoringControlFlow.complete = Control flow restored +message.confirm.recentFileNotFound = File not found. Do you want to remove it from the recent file list? +contextmenu.closeSwf = Close SWF +menu.settings.autoRenameIdentifiers = Auto rename identifiers +menu.file.saveasexe = Save as Exe... +filter.exe = Executable files (*.exe) + +#after version 1.8.0 +font.updateTexts = Update texts + +#after version 1.8.0u1 +menu.file.close = Close +menu.file.closeAll = Close all +menu.tools.otherTools = Other +menu.tools.otherTools.clearRecentFiles = Clear recent files +fontName.name = Font display name: +fontName.copyright = Font copyright: +button.preview = Preview +button.reset = Reset +errors.info = There are INFORMATIONS in the log. Click to view. +errors.warning = There are WARNINGS in the log. Click to view. + +decompilationError = Decompilation error + +disassemblingProgress.toString = toString +disassemblingProgress.reading = Reading +disassemblingProgress.deobfuscating = Deobfuscating + +contextmenu.moveTag = Move tag to + +filter.swc = SWC component files (*.swc) +filter.zip = ZIP compressed files (*.zip) +filter.binary = Binary search - all files (*.*) + +open.error = Error +open.error.fileNotFound = File not found +open.error.cannotOpen = Cannot open file + +node.others = others + +#after version 1.8.1 +menu.tools.search = Text Search + +#after version 1.8.1u1 +menu.tools.timeline = Timeline + +dialog.selectcolor.title = Select color +button.selectcolor.hint = Click to select color + +#default item name, will be used in following sentences +generictag.array.item = item +generictag.array.insertbeginning = Insert %item% at the beginning +generictag.array.insertbefore = Insert %item% before +generictag.array.remove = Remove %item% +generictag.array.insertafter = Insert %item% after +generictag.array.insertend = Insert %item% at the end + +#after version 2.0.0 +contextmenu.expandAll = Expand all + +filter.sounds = Supported sound formats (*.wav, *.mp3) +filter.sounds.wav = Wave file format (*.wav) +filter.sounds.mp3 = MP3 compressed format (*.mp3) + +error.sound.invalid = Invalid sound. + +button.prev = Previous +button.next = Next + +#after version 2.1.0 +message.action.playerglobal.title = PlayerGlobal library needed +message.action.playerglobal.needed = For ActionScript 3 direct editation, a library called "PlayerGlobal.swc" needs to be downloaded from Adobe homepage.\r\n%adobehomepage%\r\nPress OK to go to the download page. +message.action.playerglobal.place = Download the library called PlayerGlobal(.swc), and place it to directory\r\n%libpath%\r\n Press OK to continue. + +message.confirm.experimental.function = This function is EXPERIMENTAL. It means that you should not trust the results and the SWF file can be disfunctional after saving. +message.confirm.donotshowagain = Do not show again + +menu.import = Import +menu.file.import.text = Import text +import.select.directory = Select directory to import +error.text.import = Error during text import. Do you want to continue? + +#after version 2.1.1 +contextmenu.removeWithDependencies = Remove with dependencies + +abc.action.find-usages = Find usages +abc.action.find-declaration = Find declaration + +contextmenu.rawEdit = Raw edit +contextmenu.jumpToCharacter = Jump to character + +menu.settings.dumpView = Dump view + +menu.view = View +menu.file.view.resources = Resources +menu.file.view.hex = Hex dump + +node.header = header + +header.signature = Signature: +header.compression = Compression: +header.compression.lzma = LZMA +header.compression.zlib = ZLIB +header.compression.none = No compression +header.version = SWF Version: +header.gfx = GFX: +header.filesize = File size: +header.framerate = Frame rate: +header.framecount = Frame count: +header.displayrect = Display rect: +header.displayrect.value.twips = %xmin%,%ymin% => %xmax%,%ymax% twips +header.displayrect.value.pixels = %xmin%,%ymin% => %xmax%,%ymax% pixels + +#after version 2.1.2 +contextmenu.saveToFile = Save to File +contextmenu.parseActions = Parse actions +contextmenu.parseABC = Parse ABC +contextmenu.parseInstructions = Parse AVM2 Instructions + +#after version 2.1.3 +menu.deobfuscation = Deobfuscation +menu.file.deobfuscation.old = Old style +menu.file.deobfuscation.new = New style + +#after version 2.1.4 +contextmenu.openswfinside = Open SWF inside +binarydata.swfInside = It looks like there is SWF inside this binary data tag. Click here to load it as subtree. + +#after version 3.0.0 +button.zoomin.hint = Zoom in +button.zoomout.hint = Zoom out +button.zoomfit.hint = Zoom to fit +button.zoomnone.hint = Zoom to 1:1 +button.snapshot.hint = Take snapshot into clipboard + +editorTruncateWarning = Text truncated at position %chars% in debug mode. + +#Font name which is presented in the SWF Font tag +font.name.intag = Font name in tag: + +menu.debugger = Debugger +menu.debugger.switch = Debugger +menu.debugger.replacetrace = Replace trace calls +menu.debugger.showlog = Show Log + +message.debugger = This SWF Debugger can only be used to print messages to log window, browser console or alerts.\r\nIt is NOT designed for features like step code, breakpoints etc. + +contextmenu.addTag = Add tag + +deobfuscation.comment.tryenable = Tip: You can try enabling "Automatic deobfuscation" in Settings +deobfuscation.comment.failed = Deobfuscation is activated but decompilation still failed. If the file is NOT obfuscated, disable "Automatic deobfuscation" for better results. + +#after version 4.0.2 +preview.nextframe = Next frame +preview.prevframe = Previous frame +preview.gotoframe = Goto frame... + +preview.gotoframe.dialog.title = Goto frame +preview.gotoframe.dialog.message = Enter frame number (%min% - %max%) +preview.gotoframe.dialog.frame.error = Invalid frame number. It must be number between %min% and %max%. + +error.text.invalid.continue = Invalid text: %text% on line %line%. Do you want to continue? + +#after version 4.0.5 +contextmenu.copyTag = Copy tag to +fit = fit +button.setAdvanceValues = Set advance values + +menu.tools.replace = Text Replace + +message.confirm.close = There are unsaved changes. Do you really want to close {swfName}? +message.confirm.closeAll = There are unsaved changes. Do you really want to close all SWFs? + +contextmenu.exportJavaSource = Export Java Source +contextmenu.exportSwfXml = Export SWF as XML +contextmenu.importSwfXml = Import SWF XML + +filter.xml = XML + +#after version 4.1.0 +contextmenu.undo = Undo + +text.align.left = Left align +text.align.right = Right align +text.align.center = Center align +text.align.justify = Justify align + +text.undo = Undo changes + +menu.file.import.xml = Import SWF XML +menu.file.export.xml = Export SWF XML + +#after version 4.1.1 +text.align.translatex.decrease = Decrease TranslateX +text.align.translatex.increase = Increase TranslateX +selectPreviousTag = Select previous tag +selectNextTag = Select next tag +button.ignoreAll = Ignore All +menu.file.import.symbolClass = Import Symbol-Class +text.toggleCase = Toggle case + +#after version 5.0.2 +preview.loop = Loop +menu.file.import.script = Import script +contextmenu.copyTagWithDependencies = Copy tag with dependencies to +button.replaceWithTag = Replace with other character tag +button.resolveConstants = Resolve constants + +#after version 5.1.0 +button.viewConstants = View Constants +work.exported = Exported +button.replaceAlphaChannel = Replace alpha channel... + +tagInfo.header.name = Name +tagInfo.header.value = Value +tagInfo.tagType = Tag Type +tagInfo.characterId = Character Id +tagInfo.offset = Offset +tagInfo.length = Length +tagInfo.bounds = Bounds +tagInfo.width = Width +tagInfo.height = Height +tagInfo.neededCharacters = Needed Characters + +button.viewhexpcode = View Hex with instructions +taginfo.header = Basic tag info + +tagInfo.dependentCharacters = Dependent Characters + +#after version 5.3.0 +header.uncompressed = Uncompressed +header.warning.unsupportedGfxCompression = GFX supports only uncompressed or Zlib compressed content. +header.warning.minimumZlibVersion = Zlib compression needs SWF version 6 or greater. +header.warning.minimumLzmaVersion = LZMA compression needs SWF version 13 or greater. + +tagInfo.codecName = Codec Name +tagInfo.exportFormat = Export Format +tagInfo.samplingRate = Sampling Rate +tagInfo.stereo = Stereo +tagInfo.sampleCount = Sample Count + +filter.dmg = Mac Executable files (*.dmg) +filter.linuxExe = Linux Executable files + +import.script.result = %count% scripts imported. +import.script.as12warning = Import script can import only AS1/2 scripts. + +error.constantPoolTooBig = Constant pool is too big. index=%index%, size=%size% +error.image.alpha.invalid = Invalid alpha channel data. + +#after version 6.0.2 +contextmenu.saveUncompressedToFile = Save to Uncompressed File +abc.traitslist.scriptinitializer = script initializer +menu.settings.autoOpenLoadedSWFs = Open loaded SWFs while playing + +#after version 6.1.1 +menu.file.start = Start +menu.file.start.run = Run +menu.file.start.stop = Stop +menu.file.start.debug = Debug +menu.debugging = Debugging +menu.debugging.debug = Debug +menu.debugging.debug.stop = Stop +menu.debugging.debug.pause = Pause +menu.debugging.debug.stepOver = Step over +menu.debugging.debug.stepInto = Step into +menu.debugging.debug.stepOut = Step out +menu.debugging.debug.continue = Continue +menu.debugging.debug.stack = Stack... +menu.debugging.debug.watch = New watch... + +message.playerpath.notset = Flash Player projector not found. Please configure its path in Advanced Settings / Paths (1). +message.playerpath.debug.notset = Flash Player projector content debugger not found. Please configure its path in Advanced Settings / Paths (2). +message.playerpath.lib.notset = PlayerGlobal (.SWC) not found. Please configure its path in Advanced Settings / Paths (3). + +debugpanel.header = Debugging + +variables.header.registers = Registers +variables.header.locals = Locals +variables.header.arguments = Arguments +variables.header.scopeChain = Scope chain +variables.column.name = Name +variables.column.type = Type +variables.column.value = Value + +callStack.header = Call stack +callStack.header.file = File +callStack.header.line = Line + +stack.header = Stack +stack.header.item = Item + +constantpool.header = Constant pool +constantpool.header.id = Id +constantpool.header.value = Value + +work.running = Running +work.debugging = Debugging +work.debugging.instrumenting = Preparing SWF for debugging +work.breakat = Break at\u0020 +work.halted = Debugging started, execution halted. Add breakpoints and click Continue (F5) to resume running. + +debuglog.header = Log +debuglog.button.clear = Clear + +#after 7.0.1 +work.debugging.wait = Waiting for Flash debug projector to connect + +error.debug.listen = Cannot listen on port %port%. There might be other flash debugger running. + +debug.break.reason.unknown = (Unknown) +debug.break.reason.breakpoint = (Breakpoint) +debug.break.reason.watch = (Watch) +debug.break.reason.fault = (Fault) +debug.break.reason.stopRequest = (Stop request) +debug.break.reason.step = (Step) +debug.break.reason.halt = (Halt) +debug.break.reason.scriptLoaded = (Script loaded) + +menu.file.start.debugpcode = Debug P-code + +#after 7.1.2 +button.replaceNoFill = Replace - Update bounds... +message.warning.svgImportExperimental = Not all SVG features are supported. Please check the log after import. + +message.imported.swf = The SWF file uses assets from an imported SWF file:\n%url%\nDo you want the assets to be loaded from that URL? +message.imported.swf.manually = Cannot load imported SWF\n%url%\nThe file or URL does not exist.\nDo you want to select local file? + +message.warning.hexViewNotUpToDate = Hex View is not up-to-date. Please save and reload the file to update Hex View. +message.font.replace.updateTexts = Some characters were replaced. Do you want to update the existing texts? + +menu.settings.simplifyExpressions = Simplify expressions + +#after 8.0.1 +menu.recentFiles.empty = Recent file list is empty +message.warning.outOfMemory32BitJre = OutOfMemory error occurred. You are running 32bit Java on 64bit system. Please use 64bit Java. + +menu.file.reloadAll = Reload all +message.confirm.reloadAll = This action cancels all unsaved changes in all SWF files and reloads whole application again.\nDo you want to continue? +export.script.singleFilePallelModeWarning = Single file script export is not supported with enabled parallel speedup + +button.showOriginalBytesInPcodeHex = Show original bytes +button.remove = Remove +button.showFileOffsetInPcodeHex = Show file offset + +generic.editor.amf3.title = AMF3 editor +generic.editor.amf3.help = AMF3 value syntax:\n\ + ------------------\n\ + scalar types:\n\ + %scalar_samples%\ + other types:\n\ + %nonscalar_samples%\ + \n\ + Notes:\n\ + \ * Nonscalar datatypes can be referenced by previously declared "id" attributes with # syntax:\n\ + %reference_sample%\n\ + \ * Keys in Dictionary entries can be any type\n +contextmenu.showInResources = Show in Resources +message.flexpath.notset = Flex SDK not found. Please configure its path in Advanced Settings / Paths (4). + + +#add after panel.disassembled string +abc.detail.split = :\u0020 +abc.detail.trait = Trait - %trait_type% +abc.detail.trait.method = Method +abc.detail.trait.getter = Getter +abc.detail.trait.setter = Setter +abc.detail.trait.slot = Slot +abc.detail.trait.const = Const +abc.detail.trait.class = Class +abc.detail.trait.function = Function + +abc.detail.specialmethod = Special method - %specialmethod_type% +abc.detail.specialmethod.scriptinitializer = Script initializer +abc.detail.specialmethod.classinitializer = Class initializer +abc.detail.specialmethod.instanceinitializer = Instance initializer +abc.detail.innerfunction = Inner function + +button.edit.script.decompiled = Edit ActionScript +button.edit.script.disassembled = Edit P-code + +debug.watch.add = Add watch to %name% +debug.watch.add.read = Read +debug.watch.add.write = Write +debug.watch.add.readwrite = Read+Write- + +error.debug.watch.add = Cannot add watch to this variable. + +variables.column.scope = Scope +variables.column.flags = Flags +variables.column.trait = Trait + +message.font.setadvancevalues = This operation will set advance of ALL characters in this tag to selected font source advances. + +menu.tools.deobfuscation.renameColliding = Rename colliding traits/classes + +filter.iggy = Iggy files (*.iggy) diff --git a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties index 10693c097..057ffa238 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/MainFrame_cs.properties @@ -1,756 +1,756 @@ -# Copyright (C) 2010-2016 JPEXS -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -menu.file = Soubor -menu.file.open = Otev\u0159\u00edt... -menu.file.save = Ulo\u017eit -menu.file.saveas = Ulo\u017eit jako... -menu.file.export.fla = Exportovat do FLA -menu.file.export.all = Exportovat v\u0161e -menu.file.export.selection = Exportovat vybran\u00e9 -menu.file.exit = Ukon\u010dit - -menu.tools = N\u00e1stroje -menu.tools.searchas = Prohledat ActionScript... -menu.tools.proxy = Proxy -menu.tools.deobfuscation = Deobfuskace -menu.tools.deobfuscation.pcode = Deobfuskace P-k\u00f3du... -menu.tools.deobfuscation.globalrename = Glob\u00e1ln\u011b p\u0159ejmenovat identifik\u00e1tor -menu.tools.deobfuscation.renameinvalid = P\u0159ejmenovat neplatn\u00e9 identifik\u00e1tory -menu.tools.gotoDocumentClass = P\u0159ej\u00edt na hlavn\u00ed t\u0159\u00eddu dokumentu - -menu.settings = Nastaven\u00ed -menu.settings.autodeobfuscation = Automatick\u00e1 deobfuskace -menu.settings.internalflashviewer = Pou\u017e\u00edvat vlastn\u00ed prohl\u00ed\u017ee\u010d Flashe -menu.settings.parallelspeedup = Paraleln\u00ed zrychlen\u00ed -menu.settings.disabledecompilation = Zak\u00e1zat dekompilaci (Pouze P-k\u00f3d) -menu.settings.addtocontextmenu = P\u0159idat FFDec do kontextov\u00e9ho menu SWF -menu.settings.language = Zm\u011bnit jazyk -menu.settings.cacheOnDisk = Pou\u017e\u00edvat cache na disku -menu.settings.gotoMainClassOnStartup = Zv\u00fdraznit hlavn\u00ed t\u0159\u00eddu po startu - -menu.help = N\u00e1pov\u011bda -menu.help.checkupdates = Zkontrolovat novou verzi... -menu.help.helpus = Pomo\u017ete n\u00e1m! -menu.help.homepage = Nav\u0161t\u00edvit domovskou str\u00e1nku -menu.help.about = O aplikaci... - -contextmenu.remove = Odebrat - -button.save = Ulo\u017eit -button.edit = Upravit -button.cancel = Storno -button.replace = Nahradit... - -notavailonthisplatform = N\u00e1hled tohoto objektu nen\u00ed dostupn\u00fd na t\u00e9to platform\u011b. (pouze Windows) - -swfpreview = n\u00e1hled SWF -swfpreview.internal = n\u00e1hled SWF (vlastn\u00ed prohl\u00ed\u017ee\u010d) - -parameters = Parametry - -rename.enternew = Zadejte nov\u00fd n\u00e1zev: - -rename.finished.identifier = Identifik\u00e1tor p\u0159ejmenov\u00e1n. -rename.finished.multiname = %count% multiname p\u0159ejmenov\u00e1no. - -node.texts = texty -node.images = obr\u00e1zky -node.movies = videa -node.sounds = zvuky -node.binaryData = bin\u00e1rn\u00ed data -node.fonts = p\u00edsma -node.sprites = sprity -node.shapes = tvary -node.morphshapes = morphshapes -node.buttons = tla\u010d\u00edtka -node.frames = sn\u00edmky -node.scripts = skripty - -message.warning = Varov\u00e1n\u00ed -message.confirm.experimental = N\u00e1sleduj\u00edc\u00ed procedura m\u016f\u017ee po\u0161kodit SWF soubor kter\u00fd mo\u017en\u00e1 nep\u016fjde p\u0159ehr\u00e1t.\r\nPOU\u017d\u00cdVAT NA VLASTN\u00cd RIZIKO. Chcete pokra\u010dovat? -message.confirm.parallel = parallelismus m\u016f\u017ee urychlit na\u010d\u00edt\u00e1n\u00ed a dekompilaci ale pou\u017e\u00edv\u00e1 v\u00edce pam\u011bti. -message.confirm.on = Chcete to ZAPNOUT? -message.confirm.off = Chcete to VYPNOUT? -message.confirm = Potvrzen\u00ed - -message.confirm.autodeobfuscate = Automatick\u00e1 deobfuskace je zp\u016fsob jak dekompilovat obfuskovan\u00fd k\u00f3d.\r\nDeobfuskace vede k pomalej\u0161\u00ed dekompilaci a n\u011bkter\u00fd nepou\u017eit\u00fd k\u00f3d m\u016f\u017ee b\u00fdt odstran\u011bn.\r\nPokud k\u00f3d nen\u00ed obfuskovan\u00fd, je lep\u0161\u00ed autodeobfuskaci vypnout. - -message.parallel = parallelismus -message.trait.saved = Vlastnost \u00fasp\u011b\u0161n\u011b ulo\u017eena - -message.constant.new.string = \u0158et\u011bzec "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? -message.constant.new.string.title = P\u0159idat \u0158et\u011bzec -message.constant.new.integer = Cel\u00e9 \u010d\u00edslo "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? -message.constant.new.integer.title = P\u0159idat Cel\u00e9 \u010d\u00edslo -message.constant.new.unsignedinteger = P\u0159irozen\u00e9 \u010d\u00edslo "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? -message.constant.new.unsignedinteger.title = P\u0159idat P\u0159irozen\u00e9 \u010d\u00edslo -message.constant.new.double = Racion\u00e1ln\u00ed \u010d\u00edslo "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? -message.constant.new.double.title = P\u0159idat Racion\u00e1ln\u00ed \u010d\u00edslo - -work.buffering = Na\u010d\u00edt\u00e1n\u00ed -work.waitingfordissasembly = \u010cek\u00e1n\u00ed na disassemblaci -work.gettinghilights = Z\u00edsk\u00e1v\u00e1n\u00ed zv\u00fdraz\u011bn\u00ed -work.disassembling = Disassemblov\u00e1n\u00ed -work.exporting = Exportov\u00e1n\u00ed -work.searching = Vyhled\u00e1v\u00e1n\u00ed -work.renaming = P\u0159ejmenov\u00e1n\u00ed -work.exporting.fla = Exportov\u00e1n\u00ed FLA -work.renaming.identifiers = P\u0159ejmenov\u00e1n\u00ed identifik\u00e1tor\u016f -work.deobfuscating = Deobfuskov\u00e1n\u00ed -work.decompiling = Dekompilov\u00e1n\u00ed -work.gettingvariables = Z\u00edsk\u00e1v\u00e1m prom\u011bnn\u00e9 -work.reading.swf = \u010cten\u00ed SWF -work.creatingwindow = Vytv\u00e1\u0159en\u00ed okna -work.buildingscripttree = Vytv\u00e1\u0159en\u00ed stromu skript\u016f - -work.deobfuscating.complete = Deobfuskace kompletn\u00ed - -message.search.notfound = \u0158et\u011bzec "%searchtext%" nenalezen. -message.search.notfound.title = Nenalezeno - -message.rename.notfound.multiname = Na m\u00edst\u011b kurzoru nen\u00ed \u017e\u00e1dn\u00e9 multiname -message.rename.notfound.identifier = Na m\u00edst\u011b kurzoru nen\u00ed \u017e\u00e1dn\u00fd identifik\u00e1tor -message.rename.notfound.title = Nenalezeno -message.rename.renamed = Po\u010det p\u0159ejmenovan\u00fdch identifik\u00e1tor\u016f: %count% - -filter.images = Obr\u00e1zky (%extensions%) -filter.fla = Dokument %version% (*.fla) -filter.xfl = Nekomprimovan\u00fd Dokument %version% (*.xfl) -filter.swf = SWF soubory (*.swf) - -error = Chyba -error.image.invalid = Neplatn\u00fd obr\u00e1zek. - -error.text.invalid = Neplatn\u00fd text: %text% na \u0159\u00e1dku %line% -error.file.save = Nelze ulo\u017eit soubor -error.file.write = Nelze zapisovat do souboru -error.export = Chyba b\u011bhem exportu - -export.select.directory = Vyberte adres\u00e1\u0159 pro export -export.finishedin = Exportov\u00e1no za %time% - -update.check.title = Vyhled\u00e1n\u00ed aktualizac\u00ed -update.check.nonewversion = Nov\u011bj\u0161\u00ed verze nebyla nalezena. - -message.helpus = Pros\u00edm nav\u0161tivte\r\n%url%\r\npro detaily. -message.homepage = Nav\u0161tivte domovskou str\u00e1nku na: \r\n%url% - -proxy = Proxy -proxy.start = Spustit proxy -proxy.stop = Zastavit proxy -proxy.show = Zobrazit proxy -exit = Ukon\u010den\u00ed - -panel.disassembled = Zdrojov\u00fd P-k\u00f3d -panel.decompiled = Zdrojov\u00fd ActionScript - -search.info = Hlead\u00e1n\u00ed "%text%" : -search.script = Skript - -constants = Konstanty -traits = Vlastnosti - -pleasewait = Pros\u00edm \u010dekejte - -abc.detail.methodtrait = Vlastnost Metoda/Getter/Setter -abc.detail.unsupported = - -abc.detail.slotconsttrait = Vlastnost Slot/Konstanta -abc.detail.traitname = N\u00e1zev: - -abc.detail.body.params.maxstack = Maxim\u00e1ln\u00ed stack: -abc.detail.body.params.localregcount = Po\u010det lok\u00e1ln\u00edch registr\u016f: -abc.detail.body.params.minscope = Min\u00e1ln\u00ed hloubka scope : -abc.detail.body.params.maxscope = Maxim\u00e1ln\u00ed hloubka scope : -abc.detail.body.params.autofill = Automaticky vyplnit p\u0159i ulo\u017een\u00ed (GLOB\u00c1LN\u00cd NASTAVEN\u00cd) -abc.detail.body.params.autofill.experimental = ...EXPERIMENT\u00c1LN\u00cd - -abc.detail.methodinfo.methodindex = Index metody: -abc.detail.methodinfo.parameters = Parametry: -abc.detail.methodinfo.returnvalue = Typ n\u00e1vratov\u00e9 hodnoty: - -error.methodinfo.params = Chyba parametr\u016f MethodInfo -error.methodinfo.returnvalue = Chyba n\u00e1vrat\u00e9ho typu MethodInfo - -abc.detail.methodinfo = MethodInfo -abc.detail.body.code = MethodBody K\u00f3d -abc.detail.body.params = MethodBody parametry - -abc.detail.slotconst.typevalue = Typ a Hodnota: - -error.slotconst.typevalue = Chyba typu a hodnoty SlotConst - -message.autofill.failed = Nelze z\u00edskat statistiky k\u00f3du pro automatick\u00e9 parametry body.\r\nOd\u0161krtn\u011bte automatick\u00e9 vypl\u0148ov\u00e1n\u00ed pro zamezen\u00ed t\u00e9to zpr\u00e1vy. -info.selecttrait = Vyberte t\u0159\u00eddu a klikn\u011bte na vlastnost v zdrojov\u00e9m ActionScriptu pro \u00fapravy. - -button.viewgraph = Zobrazit Graf -button.viewhex = Zobrazit Hex - -abc.traitslist.instanceinitializer = inicializ\u00e1tor instance -abc.traitslist.classinitializer = inicializ\u00e1tor t\u0159\u00eddy - -action.edit.experimental = (Experiment\u00e1ln\u00ed) - -message.action.saved = K\u00f3d \u00fasp\u011b\u0161n\u011b ulo\u017een - -error.action.save = %error% na \u0159\u00e1dku %line% - -message.confirm.remove = Opravdu chcete odebrat %item%\n a v\u0161echny objekty kter\u00e9 na t\u00e9to polo\u017ece z\u00e1vis\u00ed ? - -#after version 1.6.5u1: - -button.ok = OK -button.cancel = Storno - -font.name = N\u00e1zev p\u00edsma: -font.isbold = Je tu\u010dn\u00e9: -font.isitalic = Je kurz\u00edvou: -font.ascent = Horn\u00ed dotah (ascent): -font.descent = Doln\u00ed dotah (descent): -font.leading = \u0158\u00e1dkov\u00fd proklad (leading): -font.characters = Znaky: -font.characters.add = P\u0159idat znaky: -value.unknown = ? - -yes = ano -no = ne - -errors.present = V logu jsou CHYBY. Klikn\u011bte pro zobrazen\u00ed. -errors.none = V logu nejsou \u017e\u00e1dn\u00e9 chyby - -#after version 1.6.6: - -dialog.message.title = Zpr\u00e1va -dialog.select.title = Vyberte si - -button.yes = Ano -button.no = Ne - -FileChooser.openButtonText = Otev\u0159\u00edt -FileChooser.openButtonToolTipText = Otev\u0159\u00edt -FileChooser.lookInLabelText = Vyhledat v: -FileChooser.acceptAllFileFilterText = V\u0161echny soubory -FileChooser.filesOfTypeLabelText = Soubory typu: -FileChooser.fileNameLabelText = N\u00e1zev souboru: -FileChooser.listViewButtonToolTipText = Seznam -FileChooser.listViewButtonAccessibleName = Seznam -FileChooser.detailsViewButtonToolTipText = Detaily -FileChooser.detailsViewButtonAccessibleName = Detaily -FileChooser.upFolderToolTipText = O \u00farove\u0148 v\u00fd\u0161 -FileChooser.upFolderAccessibleName = O \u00farove\u0148 v\u00fd\u0161 -FileChooser.homeFolderToolTipText = Domovsk\u00e1 slo\u017eka -FileChooser.homeFolderAccessibleName = Dom\u016f -FileChooser.fileNameHeaderText = N\u00e1zev -FileChooser.fileSizeHeaderText = Velikost -FileChooser.fileTypeHeaderText = Typ -FileChooser.fileDateHeaderText = Datum -FileChooser.fileAttrHeaderText = Vlastnosti -FileChooser.openDialogTitleText = Otev\u0159\u00edt -FileChooser.directoryDescriptionText = Slo\u017eka -FileChooser.directoryOpenButtonText = Otev\u0159\u00edt -FileChooser.directoryOpenButtonToolTipText = Otev\u0159\u00edt vybranou slo\u017eku -FileChooser.fileDescriptionText = Obecn\u00fd soubor -FileChooser.helpButtonText = N\u00e1pov\u011bda -FileChooser.helpButtonToolTipText = N\u00e1pov\u011bda v\u00fdb\u011bru souboru -FileChooser.newFolderAccessibleName = Nov\u00e1 slo\u017eka -FileChooser.newFolderErrorText = Chyba p\u0159i vytv\u00e1\u0159en\u00ed nov\u00e9 slo\u017eky -FileChooser.newFolderToolTipText = Vytvo\u0159it novou slo\u017eku -FileChooser.other.newFolder = NovaSlozka -FileChooser.other.newFolder.subsequent = NovaSlozka.{0} -FileChooser.win32.newFolder = Nov\u00e1 Slo\u017eka -FileChooser.win32.newFolder.subsequent = Nov\u00e1 Slo\u017eka ({0}) -FileChooser.saveButtonText = Ulo\u017eit -FileChooser.saveButtonToolTipText = Ulo\u017eit vybran\u00fd soubor -FileChooser.saveDialogTitleText = Ulo\u017eit -FileChooser.saveInLabelText = Ulo\u017eit do: -FileChooser.updateButtonText = Obnoven\u00ed -FileChooser.updateButtonToolTipText = Obnoven\u00ed v\u00fdpisu adres\u00e1\u0159e - -#after version 1.6.6u2: - -FileChooser.detailsViewActionLabel.textAndMnemonic = Detaily -FileChooser.detailsViewButtonToolTip.textAndMnemonic = Detaily -FileChooser.fileAttrHeader.textAndMnemonic = Vlastnosti -FileChooser.fileDateHeader.textAndMnemonic = Zm\u011bn\u011bno -FileChooser.fileNameHeader.textAndMnemonic = N\u00e1zev -FileChooser.fileNameLabel.textAndMnemonic = N\u00e1zev souboru: -FileChooser.fileSizeHeader.textAndMnemonic = Velikost -FileChooser.fileTypeHeader.textAndMnemonic = Typ -FileChooser.filesOfTypeLabel.textAndMnemonic = Soubory typu: -FileChooser.folderNameLabel.textAndMnemonic = N\u00e1zev slo\u017eky: -FileChooser.homeFolderToolTip.textAndMnemonic = Dom\u016f -FileChooser.listViewActionLabel.textAndMnemonic = Seznam -FileChooser.listViewButtonToolTip.textAndMnemonic = Seznam -FileChooser.lookInLabel.textAndMnemonic = Vyhledat v: -FileChooser.newFolderActionLabel.textAndMnemonic = Nov\u00e1 slo\u017eka -FileChooser.newFolderToolTip.textAndMnemonic = Vytvo\u0159it novou slo\u017eku -FileChooser.refreshActionLabel.textAndMnemonic = Obnovit -FileChooser.saveInLabel.textAndMnemonic = Vyhledat v: -FileChooser.upFolderToolTip.textAndMnemonic = O \u00farove\u0148 v\u00fd\u0161 -FileChooser.viewMenuButtonAccessibleName = Menu Zobrazit -FileChooser.viewMenuButtonToolTipText = Menu Zobrazit -FileChooser.viewMenuLabel.textAndMnemonic = Zobrazit -FileChooser.newFolderActionLabelText = Nov\u00e1 slo\u017eka -FileChooser.listViewActionLabelText = Seznam -FileChooser.detailsViewActionLabelText = Detaily -FileChooser.refreshActionLabelText = Obnovit -FileChooser.sortMenuLabelText = Se\u0159adit ikony podle -FileChooser.viewMenuLabelText = Zobrazit -FileChooser.fileSizeKiloBytes = {0} KB -FileChooser.fileSizeMegaBytes = {0} MB -FileChooser.fileSizeGigaBytes = {0} GB -FileChooser.folderNameLabelText = N\u00e1zev slo\u017eky: - -error.occured = Do\u0161lo k chyb\u011b : %error% -button.abort = P\u0159eru\u0161it -button.retry = Znovu -button.ignore = Ignorovat - -font.source = Zdrojov\u00e9 p\u00edsmo: - -#after version 1.6.7: - -menu.export = Export -menu.general = Hlavn\u00ed -menu.language = Jazyk - -startup.welcometo = V\u00edtejte v programu -startup.selectopen = Pro za\u010d\u00e1tek klikn\u011bte na otev\u0159\u00edt v horn\u00edm panelu nebo p\u0159et\u00e1hn\u011bte SWF soubor p\u0159\u00edmo do tohoto okna. - -error.font.nocharacter = Vybran\u00e9 zdrojov\u00e9 p\u00edsmo neobsahuje znak "%char%". - -warning.initializers = Statick\u00e9 atributy a konstanty jsou \u010dasto inicializov\u00e1ny pomoc\u00ed inicializ\u00e1tor\u016f.\nPokud to uprav\u00edte zde, obvykle to nesta\u010d\u00ed! - -#after version 1.7.0u1: - -menu.tools.searchMemory = Hledat SWF v pam\u011bti -menu.file.reload = Znovu na\u010d\u00edst -message.confirm.reload = Tato akce zru\u0161\u00ed v\u0161echny neulo\u017een\u00e9 zm\u011bny a znovu na\u010dte SWF soubor.\nChcete pokra\u010dovat? - -dialog.selectbkcolor.title = Vyberte barvu pozad\u00ed pro zobrazen\u00ed SWF -button.selectbkcolor.hint = Vybrat barvu pozad\u00ed - -ColorChooser.okText = OK -ColorChooser.cancelText = Storno -ColorChooser.resetText = Obnovit -ColorChooser.previewText = N\u00e1hled -ColorChooser.swatchesNameText = Vzorn\u00edk -ColorChooser.swatchesRecentText = Ned\u00e1vn\u00e9: -ColorChooser.sampleText = Vzorov\u00fd Text Vzorov\u00fd Text - -#after version 1.7.1: - -preview.play = P\u0159ehr\u00e1t -preview.pause = Pauza -preview.stop = Zastavit - -message.confirm.removemultiple = Opravdu chcete odebrat %count% polo\u017eek\n a v\u0161echny objekty kter\u00e9 na nich z\u00e1vis\u00ed? - -menu.tools.searchCache = Prohledat cache prohl\u00ed\u017ee\u010d\u016f - -#after version 1.7.2u2 - -error.trait.exists = Vlastnost s n\u00e1zvem "%name%" ji\u017e existuje. -button.addtrait = P\u0159idat vlastnost -button.font.embed = Vlo\u017eit... -button.yes.all = Ano v\u0161em -button.no.all = Ne v\u0161em -message.font.add.exists = Znak %char% ji\u017e v tagu p\u00edsma existuje.\nChcete ho nahradit? - -filter.gfx = ScaleForm GFx soubory (*.gfx) -filter.supported = V\u0161echny podporovan\u00e9 typy - -work.canceled = Stornov\u00e1no -work.restoringControlFlow = Obnovuji control flow -menu.advancedsettings.advancedsettings = Pokro\u010dil\u00e1 nastaven\u00ed -menu.recentFiles = Ned\u00e1vno otev\u0159en\u00e9 - -#after version 1.7.4 -work.restoringControlFlow.complete = Control flow obnoven -message.confirm.recentFileNotFound = Soubor nenalezen. Chcete jej odebrat ze seznamu ned\u00e1vno otev\u0159en\u00fdch? -contextmenu.closeSwf = Zav\u0159\u00edt SWF -menu.settings.autoRenameIdentifiers = Automaticky p\u0159ejmenovat identifik\u00e1tory -menu.file.saveasexe = Ulo\u017eit jako Exe... -filter.exe = Spustiteln\u00e9 soubory (*.exe) - -#after version 1.8.0 -font.updateTexts = Aktualizovat texty - -#after version 1.8.0u1 -menu.file.close = Zav\u0159\u00edt -menu.file.closeAll = Zav\u0159\u00edt v\u0161e -menu.tools.otherTools = Dal\u0161\u00ed -menu.tools.otherTools.clearRecentFiles = Vymazat ned\u00e1vno otev\u0159en\u00e9 -fontName.name = N\u00e1zev pro zobrazen\u00ed: -fontName.copyright = Copyright p\u00edsma: -button.preview = N\u00e1hled -button.reset = Reset -errors.info = V logu jsou INFORMACE. Klikn\u011bte pro zobrazen\u00ed. -errors.warning = V logu jsou VAROV\u00c1N\u00cd. Klikn\u011bte pro zobrazen\u00ed. - -decompilationError = Chyba dekompilace - -disassemblingProgress.toString = toString -disassemblingProgress.reading = \u010cten\u00ed -disassemblingProgress.deobfuscating = Deobfuskace - -contextmenu.moveTag = P\u0159esunout tag do - -filter.swc = SWC soubory komponent (*.swc) -filter.zip = ZIP komprimovan\u00e9 soubory (*.zip) -filter.binary = Bin\u00e1rn\u00ed vyhled\u00e1v\u00e1n\u00ed - v\u0161echny soubory (*.*) - -open.error = Chyba -open.error.fileNotFound = Soubor nenalezen -open.error.cannotOpen = Soubor nelze otev\u0159\u00edt - -node.others = ostatn\u00ed - -#after version 1.8.1 -menu.tools.search = Hled\u00e1n\u00ed Textu - -#after version 1.8.1u1 -menu.tools.timeline = \u010casov\u00e1 osa - -dialog.selectcolor.title = Vyberte barvu -button.selectcolor.hint = Kliknut\u00edm vyberete barvu - -#default item name, will be used in following sentences -generictag.array.item = polo\u017eku -generictag.array.insertbeginning = Vlo\u017eit %item% na za\u010d\u00e1tek -generictag.array.insertbefore = Vlo\u017eit %item% p\u0159ed -generictag.array.remove = Odstranit %item% -generictag.array.insertafter = Vlo\u017eit %item% za -generictag.array.insertend = Vlo\u017eit %item% na konec - -#after version 2.0.0 -contextmenu.expandAll = Rozbalit v\u0161e - -filter.sounds = Podporovan\u00e9 zvukov\u00e9 form\u00e1ty (*.wav, *.mp3) -filter.sounds.wav = Wave form\u00e1t (*.wav) -filter.sounds.mp3 = MP3 komprimovan\u00fd form\u00e1t (*.mp3) - -error.sound.invalid = Neplatn\u00fd zvuk. - -button.prev = P\u0159edchoz\u00ed -button.next = Dal\u0161\u00ed - -#after version 2.1.0 -message.action.playerglobal.title = Vy\u017eadov\u00e1na knihovna PlayerGlobal -message.action.playerglobal.needed = Pro p\u0159\u00edmou editaci ActionScriptu 3 je pot\u0159eba knihovna "PlayerGlobal.swc", kterou lze stahnout ze str\u00e1nek Adobe\r\n%adobehomepage%\r\nStiskn\u011bte OK pro p\u0159echod na stahovac\u00ed str\u00e1nku. -message.action.playerglobal.place = St\u00e1hn\u011bte knihovnu nazvanou PlayerGlobal(.swc), a um\u00edst\u011bte j\u00ed do adres\u00e1\u0159e\r\n%libpath%\r\n Stiskn\u011bte OK pro pokra\u010dov\u00e1n\u00ed. - -message.confirm.experimental.function = Tato funkce je EXPERIMENT\u00c1LN\u00cd. To znamen\u00e1, \u017ee byste nem\u011bli v\u011b\u0159it jej\u00edm v\u00fdsledk\u016fm a SWF soubor m\u016f\u017ee po ulo\u017een\u00ed p\u0159estat fungovat. -message.confirm.donotshowagain = P\u0159\u00ed\u0161t\u011b nezobrazovat - -menu.import = Import -menu.file.import.text = Import textu -import.select.directory = Vyberte adres\u00e1\u0159 pro import -error.text.import = Chyba b\u011bhem importu textu. Chcete pokra\u010dovat? - -#after version 2.1.1 -contextmenu.removeWithDependencies = Odstranit se z\u00e1vislostmi - -abc.action.find-usages = Naj\u00edt pou\u017eit\u00ed -abc.action.find-declaration = Naj\u00edt deklaraci - -contextmenu.rawEdit = P\u0159\u00edm\u00e1 editace -contextmenu.jumpToCharacter = Skok na charakter - -menu.settings.dumpView = Zobrazit Dump - -menu.view = Zobrazen\u00ed -menu.file.view.resources = Zdroje -menu.file.view.hex = Hex dump - -node.header = hlavi\u010dka - -header.signature = Signatura: -header.compression = Komprese: -header.compression.lzma = LZMA -header.compression.zlib = ZLIB -header.compression.none = Bez komprese -header.version = Verze SWF: -header.gfx = GFX: -header.filesize = Velikost souboru: -header.framerate = Frekvence sn\u00edmk\u016f: -header.framecount = Po\u010det sn\u00edmk\u016f: -header.displayrect = Zobrazen\u00fd obd\u00e9ln\u00edk: -header.displayrect.value.twips = %xmin%,%ymin% => %xmax%,%ymax% twip\u016f -header.displayrect.value.pixels = %xmin%,%ymin% => %xmax%,%ymax% pixel\u016f - -contextmenu.saveToFile = Ulo\u017eit do souboru -contextmenu.parseActions = Naparsovat akce -contextmenu.parseABC = Naparsovat ABC -contextmenu.parseInstructions = Naparsovat AVM2 instrukce - -menu.deobfuscation = Deobfuskace -menu.file.deobfuscation.old = Star\u00fd zp\u016fsob -menu.file.deobfuscation.new = Nov\u00fd zp\u016fsob - -contextmenu.openswfinside = Otev\u0159\u00edt SWF uvnit\u0159 -binarydata.swfInside = Vypad\u00e1 to, \u017ee uvnit\u0159 v tomto BinaryData tagu se nach\u00e1z\u00ed SWF soubor. Klikn\u011bte zde pro jeho na\u010dten\u00ed jako podstrom. - -#after version 3.0.0 -button.zoomin.hint = P\u0159ibl\u00ed\u017eit -button.zoomout.hint = Odd\u00e1lit -button.zoomfit.hint = Rozt\u00e1hnout -button.zoomnone.hint = M\u011b\u0159\u00edtko 1:1 -button.snapshot.hint = Sn\u00edmek do schr\u00e1nky - -editorTruncateWarning = Text je o\u0159\u00edznut na pozici %chars% v lad\u00edc\u00edm m\u00f3du. - -font.name.intag = N\u00e1zev p\u00edsma v tagu: - -menu.debugger = Debugger -menu.debugger.switch = Debugger -menu.debugger.replacetrace = Nahradit vol\u00e1n\u00ed trace -menu.debugger.showlog = Zobrazit Log - -message.debugger = Tento SWF Debugger slou\u017e\u00ed jen k pos\u00edl\u00e1n\u00ed zpr\u00e1v do okna logu, konzole prohl\u00ed\u017ee\u010de nebo alert oken.\r\nNEN\u00cd ur\u010den pro krokov\u00e1n\u00ed k\u00f3du, breakpointy atd. - -contextmenu.addTag = P\u0159idat tag - -deobfuscation.comment.tryenable = Tip: m\u016f\u017eete zkusit povolit "Automatickou deobfuskaci" v nastaven\u00ed -deobfuscation.comment.failed = Deobfuskace je aktivn\u00ed, ale dekompilace p\u0159esto selhala. Pokud soubor NEN\u00cd obfuskov\u00e1n, zaka\u017ete "Automatickou deobfuskaci" pro lep\u0161\u00ed v\u00fdsledky. - -#after version 4.0.2 -preview.nextframe = Dal\u0161\u00ed sn\u00edmek -preview.prevframe = P\u0159ede\u0161l\u00fd sn\u00edmek -preview.gotoframe = P\u0159ej\u00edt na sn\u00edmek... - -preview.gotoframe.dialog.title = P\u0159ej\u00edt na sn\u00edmek -preview.gotoframe.dialog.message = Zadejte \u010d\u00edslo sn\u00edmku (%min% - %max%) -preview.gotoframe.dialog.frame.error = Neplatn\u00e9 \u010d\u00edslo sn\u00edmku. Mus\u00ed to b\u00fdt \u010d\u00edslo v rozmez\u00ed %min% a %max%. - -error.text.invalid.continue = Neplatn\u00fd text: %text% na \u0159\u00e1dku %line%. Chcete pokra\u010dovat? - -#after version 4.0.5 -contextmenu.copyTag = Zkop\u00edrovat tag do -fit = p\u0159izp\u016fsobit -button.setAdvanceValues = Nastavit hodnoty advance - -menu.tools.replace = Nahrazen\u00ed textu - -message.confirm.close = Existuj\u00ed neulo\u017een\u00e9 zm\u011bny. Opravdu chcete zav\u0159\u00edt {swfName}? -message.confirm.closeAll = Existuj\u00ed neulo\u017een\u00e9 zm\u011bny. Opravdu chcete zav\u0159\u00edt v\u0161echny SWF? - -contextmenu.exportJavaSource = Exportovat zdroj\u00e1k Javy -contextmenu.exportSwfXml = Exportovat SWF jako XML -contextmenu.importSwfXml = Importovat SWF XML - -filter.xml = XML - -#after version 4.1.0 -contextmenu.undo = Zp\u011bt - -text.align.left = Zarovnat vlevo -text.align.right = Zarovnat vpravo -text.align.center = Zarovnat na st\u0159ed -text.align.justify = Zarovnat do bloku - -text.undo = Vr\u00e1tit zm\u011bny - -menu.file.import.xml = Import SWF XML -menu.file.export.xml = Export SWF XML - -#after version 4.1.1 -text.align.translatex.decrease = Sn\u00ed\u017eit TranslateX -text.align.translatex.increase = Zv\u00fd\u0161it TranslateX -selectPreviousTag = Vybrat p\u0159edchoz\u00ed tag -selectNextTag = Vybrat dal\u0161\u00ed tag -button.ignoreAll = Ignorovat V\u0161e -menu.file.import.symbolClass = Import Symbol-Class -text.toggleCase = P\u0159epnout mal\u00e1/velk\u00e1 - -#after version 5.0.2 -preview.loop = Opakovat -menu.file.import.script = Importovat skript -contextmenu.copyTagWithDependencies = Kop\u00edrovat tagy se z\u00e1vislostmi do -button.replaceWithTag = Nahradit jin\u00fdm charakterov\u00fdm tagem -button.resolveConstants = Resolvovat konstanty - -#after version 5.1.0 -button.viewConstants = Zobrazit konstanty -work.exported = Exportov\u00e1no -button.replaceAlphaChannel = Nahradit alfa kan\u00e1l... - -tagInfo.header.name = N\u00e1zev -tagInfo.header.value = Hodnota -tagInfo.tagType = Typ tagu -tagInfo.characterId = Id charakteru -tagInfo.offset = Offset -tagInfo.length = D\u00e9lka -tagInfo.bounds = Meze -tagInfo.width = \u0160\u00ed\u0159ka -tagInfo.height = V\u00fd\u0161ka -tagInfo.neededCharacters = Pot\u0159ebn\u00e9 charaktery - -button.viewhexpcode = Zobrazit Hex s instrukcemi -taginfo.header = Z\u00e1kladn\u00ed informace o tagu - -tagInfo.dependentCharacters = Z\u00e1visl\u00e9 charaktery - -#after version 5.3.0 -header.uncompressed = Bez komprese -header.warning.unsupportedGfxCompression = GFX podporuje pouze soubor se Zlib nebo \u017e\u00e1dnou kompres\u00ed. -header.warning.minimumZlibVersion = Zlib komprese vy\u017eaduje SWF verze 6 \u010di vy\u0161\u0161\u00ed. -header.warning.minimumLzmaVersion = LZMA komprese vy\u017eaduje SWF verze 13 \u010di vy\u0161\u0161\u00ed. - -tagInfo.codecName = N\u00e1zev kodeku -tagInfo.exportFormat = Form\u00e1t exportu -tagInfo.samplingRate = Vzorkovac\u00ed frekvence -tagInfo.stereo = Stereo -tagInfo.sampleCount = Po\u010det vzork\u016f - -filter.dmg = Spustieln\u00e9 soubory Macu (*.dmg) -filter.linuxExe = Linuxov\u00e9 spustiteln\u00e9 soubory - -import.script.result = %count% skript\u016f importov\u00e1no. -import.script.as12warning = Import skript\u016f funguje jen pro AS1/2 skripty. - -error.constantPoolTooBig = Seznam konstant je moc velk\u00fd index=%index%, velikost=%size% -error.image.alpha.invalid = Neplatn\u00e1 data alpha kan\u00e1lu. - -#after version 6.0.2 -contextmenu.saveUncompressedToFile = Ulo\u017eit nekomprimovan\u011b -abc.traitslist.scriptinitializer = inicializ\u00e1tor skriptu -menu.settings.autoOpenLoadedSWFs = Otev\u00edrat na\u010d\u00edtan\u00e1 SWF b\u011bhem p\u0159ehr\u00e1v\u00e1n\u00ed - -#after version 6.1.1 -menu.file.start = Start -menu.file.start.run = Spustit -menu.file.start.stop = Zastavit -menu.file.start.debug = Ladit -menu.debugging = Lad\u011bn\u00ed -menu.debugging.debug = Lad\u011bn\u00ed -menu.debugging.debug.stop = Ukon\u010dit -menu.debugging.debug.pause = Pauza -menu.debugging.debug.stepOver = Step over -menu.debugging.debug.stepInto = Step into -menu.debugging.debug.stepOut = Step out -menu.debugging.debug.continue = Pokra\u010dovat -menu.debugging.debug.stack = Stack... -menu.debugging.debug.watch = New watch... - -message.playerpath.notset = Flash Player projector nenalezen. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (1). -message.playerpath.debug.notset = Flash Player projector content debugger nenalezen. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (2). -message.playerpath.lib.notset = PlayerGlobal (.SWC) nenalezen. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (3). - -debugpanel.header = Lad\u011bn\u00ed - -variables.header.registers = Registry -variables.header.locals = Lok\u00e1ln\u00ed -variables.header.arguments = Argumenty -variables.header.scopeChain = Scope chain -variables.column.name = N\u00e1zev -variables.column.type = Typ -variables.column.value = Hodnota - -callStack.header = Call stack -callStack.header.file = Soubor -callStack.header.line = \u0158\u00e1dek - -stack.header = Stack -stack.header.item = Polo\u017eka - -work.running = B\u011bh -work.debugging = Lad\u011bn\u00ed -work.debugging.instrumenting = P\u0159\u00edprava SWF pro lad\u011bn\u00ed -work.breakat = Pozastaveno na \u0020 -work.halted = Lad\u011bn\u00ed za\u010dalo, b\u011bh je nyn\u00ed pozastaven. P\u0159idejte breakpointy a klikn\u011bte na Pokra\u010dovat (F5) pro obnoven\u00ed b\u011bhu. - -debuglog.header = Z\u00e1znam -debuglog.button.clear = Vypr\u00e1znit - -#after 7.0.1 -work.debugging.wait = \u010cek\u00e1 se na p\u0159ipojen\u00ed Flash debug projektoru - -error.debug.listen = Nelze naslouchat na portu %port%. Mo\u017en\u00e1 je spu\u0161t\u011bn n\u011bjak\u00fd jin\u00fd debugger. - -debug.break.reason.unknown = (Nezn\u00e1m\u00fd d\u016fvod) -debug.break.reason.breakpoint = (Breakpoint) -debug.break.reason.watch = (Watch) -debug.break.reason.fault = (Selh\u00e1n\u00ed) -debug.break.reason.stopRequest = (Zastaveno) -debug.break.reason.step = (Step) -debug.break.reason.halt = (Halt) -debug.break.reason.scriptLoaded = (Skript na\u010dten) - -menu.file.start.debugpcode = Lad\u011bn\u00ed P-k\u00f3du - -#after 7.1.2 -button.replaceNoFill = Nahradit - aktualizovat hranice... -message.warning.svgImportExperimental = Ne v\u0161echny SVG vlastnosti jsou podporov\u00e1ny. Pros\u00edm zkontrolujte log po importu. - -message.imported.swf = SWF soubor pou\u017e\u00edv\u00e1 assety z importovan\u00e9ho SWF souboru:\n%url%\nChcete tyto assety na\u010d\u00edst z dan\u00e9ho URL? -message.imported.swf.manually = Nelze na\u010d\u00edst importovan\u00e9 SWF\n%url%\nSoubor nebo URL neexistuje.\nChcete vybrat m\u00edstn\u00ed soubor? - -message.warning.hexViewNotUpToDate = Hexa pohled nen\u00ed aktu\u00e1ln\u00ed. Pros\u00edm ulo\u017ete a znovuna\u010dt\u011bte soubor pro aktualizaci hexa pohledu. -message.font.replace.updateTexts = N\u011bkter\u00e9 znaky byly nahrazeny. Chcete aktualizovat existuj\u00edc\u00ed texty? - -menu.settings.simplifyExpressions = Zjednodu\u0161it v\u00fdrazy - -#after 8.0.1 -menu.recentFiles.empty = Seznam ned\u00e1vno otev\u0159en\u00fdch je pr\u00e1zdn\u00fd -message.warning.outOfMemory32BitJre = Nastala chyba z nedostatku pam\u011bti. M\u00e1te spu\u0161t\u011bnou 32bitovou Javu na 64bitov\u00e9m syst\u00e9mu. Pros\u00edm pou\u017eijte 64bitovou Java. - -menu.file.reloadAll = Znovu na\u010d\u00edst v\u0161e -message.confirm.reloadAll = Tato akce stornuje v\u0161echny neulo\u017een\u00e9 zm\u011bny ve v\u0161ech SWF souborech a znovu na\u010dte celou aplikaci.\nChcete pokra\u010dovat? -export.script.singleFilePallelModeWarning = Export skript\u016f do jednoho souboru nen\u00ed dostupn\u00fd se zapnut\u00fdm paralleln\u00edm zrychlen\u00edm - -button.showOriginalBytesInPcodeHex = Zobrazit origin\u00e1ln\u00ed bajty -button.remove = Odstranit -button.showFileOffsetInPcodeHex = Zobrazit offset v souboru - -generic.editor.amf3.title = AMF3 editor -generic.editor.amf3.help = Syntaxe AMF3 hodnoty:\n\ - ------------------\n\ - skal\u00e1rn\u00ed typy:\n\ - %scalar_samples%\ - ostatn\u00ed typy:\n\ - %nonscalar_samples%\ - \n\ - Pozn\u00e1mky:\n\ - \ * Neskal\u00e1rn\u00ed datov\u00e9 typy mohou mohou b\u00fdt odkazov\u00e1ny p\u0159edem deklarovan\u00fdmi "id" atributy s pomoc\u00ed # syntaxe:\n\ - %reference_sample%\n\ - \ * Kl\u00ed\u010de v z\u00e1znamech Dictionary mohou b\u00fdt jak\u00e9hokoli typu\n -contextmenu.showInResources = Zobrazit ve Zdroj\u00edch -message.flexpath.notset = Flex SDK nenalezeno. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (4). - - -#add after panel.disassembled string -abc.detail.split = :\u0020 -abc.detail.trait = Vlastnost - %trait_type% -abc.detail.trait.method = Metoda -abc.detail.trait.getter = Getter -abc.detail.trait.setter = Setter -abc.detail.trait.slot = Slot -abc.detail.trait.const = Konstanta -abc.detail.trait.class = T\u0159\u00edda -abc.detail.trait.function = Funkce - -abc.detail.specialmethod = Speci\u00e1ln\u00ed metoda - %specialmethod_type% -abc.detail.specialmethod.scriptinitializer = Inicializ\u00e1tor skriptu -abc.detail.specialmethod.classinitializer = Inicializ\u00e1tor t\u0159\u00eddy -abc.detail.specialmethod.instanceinitializer = Inicializ\u00e1tor instance -abc.detail.innerfunction = Vnit\u0159n\u00ed funkce - -button.edit.script.decompiled = Upravit ActionScript -button.edit.script.disassembled = Upravit P-k\u00f3d - -message.font.setadvancevalues = Tato operace nastav\u00ed advance V\u0160ECH znak\u016f v tomto tagu na advance hodnoty zdrojov\u00e9ho p\u00edsma. - +# Copyright (C) 2010-2016 JPEXS +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +menu.file = Soubor +menu.file.open = Otev\u0159\u00edt... +menu.file.save = Ulo\u017eit +menu.file.saveas = Ulo\u017eit jako... +menu.file.export.fla = Exportovat do FLA +menu.file.export.all = Exportovat v\u0161e +menu.file.export.selection = Exportovat vybran\u00e9 +menu.file.exit = Ukon\u010dit + +menu.tools = N\u00e1stroje +menu.tools.searchas = Prohledat ActionScript... +menu.tools.proxy = Proxy +menu.tools.deobfuscation = Deobfuskace +menu.tools.deobfuscation.pcode = Deobfuskace P-k\u00f3du... +menu.tools.deobfuscation.globalrename = Glob\u00e1ln\u011b p\u0159ejmenovat identifik\u00e1tor +menu.tools.deobfuscation.renameinvalid = P\u0159ejmenovat neplatn\u00e9 identifik\u00e1tory +menu.tools.gotoDocumentClass = P\u0159ej\u00edt na hlavn\u00ed t\u0159\u00eddu dokumentu + +menu.settings = Nastaven\u00ed +menu.settings.autodeobfuscation = Automatick\u00e1 deobfuskace +menu.settings.internalflashviewer = Pou\u017e\u00edvat vlastn\u00ed prohl\u00ed\u017ee\u010d Flashe +menu.settings.parallelspeedup = Paraleln\u00ed zrychlen\u00ed +menu.settings.disabledecompilation = Zak\u00e1zat dekompilaci (Pouze P-k\u00f3d) +menu.settings.addtocontextmenu = P\u0159idat FFDec do kontextov\u00e9ho menu SWF +menu.settings.language = Zm\u011bnit jazyk +menu.settings.cacheOnDisk = Pou\u017e\u00edvat cache na disku +menu.settings.gotoMainClassOnStartup = Zv\u00fdraznit hlavn\u00ed t\u0159\u00eddu po startu + +menu.help = N\u00e1pov\u011bda +menu.help.checkupdates = Zkontrolovat novou verzi... +menu.help.helpus = Pomo\u017ete n\u00e1m! +menu.help.homepage = Nav\u0161t\u00edvit domovskou str\u00e1nku +menu.help.about = O aplikaci... + +contextmenu.remove = Odebrat + +button.save = Ulo\u017eit +button.edit = Upravit +button.cancel = Storno +button.replace = Nahradit... + +notavailonthisplatform = N\u00e1hled tohoto objektu nen\u00ed dostupn\u00fd na t\u00e9to platform\u011b. (pouze Windows) + +swfpreview = n\u00e1hled SWF +swfpreview.internal = n\u00e1hled SWF (vlastn\u00ed prohl\u00ed\u017ee\u010d) + +parameters = Parametry + +rename.enternew = Zadejte nov\u00fd n\u00e1zev: + +rename.finished.identifier = Identifik\u00e1tor p\u0159ejmenov\u00e1n. +rename.finished.multiname = %count% multiname p\u0159ejmenov\u00e1no. + +node.texts = texty +node.images = obr\u00e1zky +node.movies = videa +node.sounds = zvuky +node.binaryData = bin\u00e1rn\u00ed data +node.fonts = p\u00edsma +node.sprites = sprity +node.shapes = tvary +node.morphshapes = morphshapes +node.buttons = tla\u010d\u00edtka +node.frames = sn\u00edmky +node.scripts = skripty + +message.warning = Varov\u00e1n\u00ed +message.confirm.experimental = N\u00e1sleduj\u00edc\u00ed procedura m\u016f\u017ee po\u0161kodit SWF soubor kter\u00fd mo\u017en\u00e1 nep\u016fjde p\u0159ehr\u00e1t.\r\nPOU\u017d\u00cdVAT NA VLASTN\u00cd RIZIKO. Chcete pokra\u010dovat? +message.confirm.parallel = parallelismus m\u016f\u017ee urychlit na\u010d\u00edt\u00e1n\u00ed a dekompilaci ale pou\u017e\u00edv\u00e1 v\u00edce pam\u011bti. +message.confirm.on = Chcete to ZAPNOUT? +message.confirm.off = Chcete to VYPNOUT? +message.confirm = Potvrzen\u00ed + +message.confirm.autodeobfuscate = Automatick\u00e1 deobfuskace je zp\u016fsob jak dekompilovat obfuskovan\u00fd k\u00f3d.\r\nDeobfuskace vede k pomalej\u0161\u00ed dekompilaci a n\u011bkter\u00fd nepou\u017eit\u00fd k\u00f3d m\u016f\u017ee b\u00fdt odstran\u011bn.\r\nPokud k\u00f3d nen\u00ed obfuskovan\u00fd, je lep\u0161\u00ed autodeobfuskaci vypnout. + +message.parallel = parallelismus +message.trait.saved = Vlastnost \u00fasp\u011b\u0161n\u011b ulo\u017eena + +message.constant.new.string = \u0158et\u011bzec "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? +message.constant.new.string.title = P\u0159idat \u0158et\u011bzec +message.constant.new.integer = Cel\u00e9 \u010d\u00edslo "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? +message.constant.new.integer.title = P\u0159idat Cel\u00e9 \u010d\u00edslo +message.constant.new.unsignedinteger = P\u0159irozen\u00e9 \u010d\u00edslo "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? +message.constant.new.unsignedinteger.title = P\u0159idat P\u0159irozen\u00e9 \u010d\u00edslo +message.constant.new.double = Racion\u00e1ln\u00ed \u010d\u00edslo "%value%" neexistuje v tabulce konstant. Chcete ho p\u0159idat? +message.constant.new.double.title = P\u0159idat Racion\u00e1ln\u00ed \u010d\u00edslo + +work.buffering = Na\u010d\u00edt\u00e1n\u00ed +work.waitingfordissasembly = \u010cek\u00e1n\u00ed na disassemblaci +work.gettinghilights = Z\u00edsk\u00e1v\u00e1n\u00ed zv\u00fdraz\u011bn\u00ed +work.disassembling = Disassemblov\u00e1n\u00ed +work.exporting = Exportov\u00e1n\u00ed +work.searching = Vyhled\u00e1v\u00e1n\u00ed +work.renaming = P\u0159ejmenov\u00e1n\u00ed +work.exporting.fla = Exportov\u00e1n\u00ed FLA +work.renaming.identifiers = P\u0159ejmenov\u00e1n\u00ed identifik\u00e1tor\u016f +work.deobfuscating = Deobfuskov\u00e1n\u00ed +work.decompiling = Dekompilov\u00e1n\u00ed +work.gettingvariables = Z\u00edsk\u00e1v\u00e1m prom\u011bnn\u00e9 +work.reading.swf = \u010cten\u00ed SWF +work.creatingwindow = Vytv\u00e1\u0159en\u00ed okna +work.buildingscripttree = Vytv\u00e1\u0159en\u00ed stromu skript\u016f + +work.deobfuscating.complete = Deobfuskace kompletn\u00ed + +message.search.notfound = \u0158et\u011bzec "%searchtext%" nenalezen. +message.search.notfound.title = Nenalezeno + +message.rename.notfound.multiname = Na m\u00edst\u011b kurzoru nen\u00ed \u017e\u00e1dn\u00e9 multiname +message.rename.notfound.identifier = Na m\u00edst\u011b kurzoru nen\u00ed \u017e\u00e1dn\u00fd identifik\u00e1tor +message.rename.notfound.title = Nenalezeno +message.rename.renamed = Po\u010det p\u0159ejmenovan\u00fdch identifik\u00e1tor\u016f: %count% + +filter.images = Obr\u00e1zky (%extensions%) +filter.fla = Dokument %version% (*.fla) +filter.xfl = Nekomprimovan\u00fd Dokument %version% (*.xfl) +filter.swf = SWF soubory (*.swf) + +error = Chyba +error.image.invalid = Neplatn\u00fd obr\u00e1zek. + +error.text.invalid = Neplatn\u00fd text: %text% na \u0159\u00e1dku %line% +error.file.save = Nelze ulo\u017eit soubor +error.file.write = Nelze zapisovat do souboru +error.export = Chyba b\u011bhem exportu + +export.select.directory = Vyberte adres\u00e1\u0159 pro export +export.finishedin = Exportov\u00e1no za %time% + +update.check.title = Vyhled\u00e1n\u00ed aktualizac\u00ed +update.check.nonewversion = Nov\u011bj\u0161\u00ed verze nebyla nalezena. + +message.helpus = Pros\u00edm nav\u0161tivte\r\n%url%\r\npro detaily. +message.homepage = Nav\u0161tivte domovskou str\u00e1nku na: \r\n%url% + +proxy = Proxy +proxy.start = Spustit proxy +proxy.stop = Zastavit proxy +proxy.show = Zobrazit proxy +exit = Ukon\u010den\u00ed + +panel.disassembled = Zdrojov\u00fd P-k\u00f3d +panel.decompiled = Zdrojov\u00fd ActionScript + +search.info = Hlead\u00e1n\u00ed "%text%" : +search.script = Skript + +constants = Konstanty +traits = Vlastnosti + +pleasewait = Pros\u00edm \u010dekejte + +abc.detail.methodtrait = Vlastnost Metoda/Getter/Setter +abc.detail.unsupported = - +abc.detail.slotconsttrait = Vlastnost Slot/Konstanta +abc.detail.traitname = N\u00e1zev: + +abc.detail.body.params.maxstack = Maxim\u00e1ln\u00ed stack: +abc.detail.body.params.localregcount = Po\u010det lok\u00e1ln\u00edch registr\u016f: +abc.detail.body.params.minscope = Min\u00e1ln\u00ed hloubka scope : +abc.detail.body.params.maxscope = Maxim\u00e1ln\u00ed hloubka scope : +abc.detail.body.params.autofill = Automaticky vyplnit p\u0159i ulo\u017een\u00ed (GLOB\u00c1LN\u00cd NASTAVEN\u00cd) +abc.detail.body.params.autofill.experimental = ...EXPERIMENT\u00c1LN\u00cd + +abc.detail.methodinfo.methodindex = Index metody: +abc.detail.methodinfo.parameters = Parametry: +abc.detail.methodinfo.returnvalue = Typ n\u00e1vratov\u00e9 hodnoty: + +error.methodinfo.params = Chyba parametr\u016f MethodInfo +error.methodinfo.returnvalue = Chyba n\u00e1vrat\u00e9ho typu MethodInfo + +abc.detail.methodinfo = MethodInfo +abc.detail.body.code = MethodBody K\u00f3d +abc.detail.body.params = MethodBody parametry + +abc.detail.slotconst.typevalue = Typ a Hodnota: + +error.slotconst.typevalue = Chyba typu a hodnoty SlotConst + +message.autofill.failed = Nelze z\u00edskat statistiky k\u00f3du pro automatick\u00e9 parametry body.\r\nOd\u0161krtn\u011bte automatick\u00e9 vypl\u0148ov\u00e1n\u00ed pro zamezen\u00ed t\u00e9to zpr\u00e1vy. +info.selecttrait = Vyberte t\u0159\u00eddu a klikn\u011bte na vlastnost v zdrojov\u00e9m ActionScriptu pro \u00fapravy. + +button.viewgraph = Zobrazit Graf +button.viewhex = Zobrazit Hex + +abc.traitslist.instanceinitializer = inicializ\u00e1tor instance +abc.traitslist.classinitializer = inicializ\u00e1tor t\u0159\u00eddy + +action.edit.experimental = (Experiment\u00e1ln\u00ed) + +message.action.saved = K\u00f3d \u00fasp\u011b\u0161n\u011b ulo\u017een + +error.action.save = %error% na \u0159\u00e1dku %line% + +message.confirm.remove = Opravdu chcete odebrat %item%\n a v\u0161echny objekty kter\u00e9 na t\u00e9to polo\u017ece z\u00e1vis\u00ed ? + +#after version 1.6.5u1: + +button.ok = OK +button.cancel = Storno + +font.name = N\u00e1zev p\u00edsma: +font.isbold = Je tu\u010dn\u00e9: +font.isitalic = Je kurz\u00edvou: +font.ascent = Horn\u00ed dotah (ascent): +font.descent = Doln\u00ed dotah (descent): +font.leading = \u0158\u00e1dkov\u00fd proklad (leading): +font.characters = Znaky: +font.characters.add = P\u0159idat znaky: +value.unknown = ? + +yes = ano +no = ne + +errors.present = V logu jsou CHYBY. Klikn\u011bte pro zobrazen\u00ed. +errors.none = V logu nejsou \u017e\u00e1dn\u00e9 chyby + +#after version 1.6.6: + +dialog.message.title = Zpr\u00e1va +dialog.select.title = Vyberte si + +button.yes = Ano +button.no = Ne + +FileChooser.openButtonText = Otev\u0159\u00edt +FileChooser.openButtonToolTipText = Otev\u0159\u00edt +FileChooser.lookInLabelText = Vyhledat v: +FileChooser.acceptAllFileFilterText = V\u0161echny soubory +FileChooser.filesOfTypeLabelText = Soubory typu: +FileChooser.fileNameLabelText = N\u00e1zev souboru: +FileChooser.listViewButtonToolTipText = Seznam +FileChooser.listViewButtonAccessibleName = Seznam +FileChooser.detailsViewButtonToolTipText = Detaily +FileChooser.detailsViewButtonAccessibleName = Detaily +FileChooser.upFolderToolTipText = O \u00farove\u0148 v\u00fd\u0161 +FileChooser.upFolderAccessibleName = O \u00farove\u0148 v\u00fd\u0161 +FileChooser.homeFolderToolTipText = Domovsk\u00e1 slo\u017eka +FileChooser.homeFolderAccessibleName = Dom\u016f +FileChooser.fileNameHeaderText = N\u00e1zev +FileChooser.fileSizeHeaderText = Velikost +FileChooser.fileTypeHeaderText = Typ +FileChooser.fileDateHeaderText = Datum +FileChooser.fileAttrHeaderText = Vlastnosti +FileChooser.openDialogTitleText = Otev\u0159\u00edt +FileChooser.directoryDescriptionText = Slo\u017eka +FileChooser.directoryOpenButtonText = Otev\u0159\u00edt +FileChooser.directoryOpenButtonToolTipText = Otev\u0159\u00edt vybranou slo\u017eku +FileChooser.fileDescriptionText = Obecn\u00fd soubor +FileChooser.helpButtonText = N\u00e1pov\u011bda +FileChooser.helpButtonToolTipText = N\u00e1pov\u011bda v\u00fdb\u011bru souboru +FileChooser.newFolderAccessibleName = Nov\u00e1 slo\u017eka +FileChooser.newFolderErrorText = Chyba p\u0159i vytv\u00e1\u0159en\u00ed nov\u00e9 slo\u017eky +FileChooser.newFolderToolTipText = Vytvo\u0159it novou slo\u017eku +FileChooser.other.newFolder = NovaSlozka +FileChooser.other.newFolder.subsequent = NovaSlozka.{0} +FileChooser.win32.newFolder = Nov\u00e1 Slo\u017eka +FileChooser.win32.newFolder.subsequent = Nov\u00e1 Slo\u017eka ({0}) +FileChooser.saveButtonText = Ulo\u017eit +FileChooser.saveButtonToolTipText = Ulo\u017eit vybran\u00fd soubor +FileChooser.saveDialogTitleText = Ulo\u017eit +FileChooser.saveInLabelText = Ulo\u017eit do: +FileChooser.updateButtonText = Obnoven\u00ed +FileChooser.updateButtonToolTipText = Obnoven\u00ed v\u00fdpisu adres\u00e1\u0159e + +#after version 1.6.6u2: + +FileChooser.detailsViewActionLabel.textAndMnemonic = Detaily +FileChooser.detailsViewButtonToolTip.textAndMnemonic = Detaily +FileChooser.fileAttrHeader.textAndMnemonic = Vlastnosti +FileChooser.fileDateHeader.textAndMnemonic = Zm\u011bn\u011bno +FileChooser.fileNameHeader.textAndMnemonic = N\u00e1zev +FileChooser.fileNameLabel.textAndMnemonic = N\u00e1zev souboru: +FileChooser.fileSizeHeader.textAndMnemonic = Velikost +FileChooser.fileTypeHeader.textAndMnemonic = Typ +FileChooser.filesOfTypeLabel.textAndMnemonic = Soubory typu: +FileChooser.folderNameLabel.textAndMnemonic = N\u00e1zev slo\u017eky: +FileChooser.homeFolderToolTip.textAndMnemonic = Dom\u016f +FileChooser.listViewActionLabel.textAndMnemonic = Seznam +FileChooser.listViewButtonToolTip.textAndMnemonic = Seznam +FileChooser.lookInLabel.textAndMnemonic = Vyhledat v: +FileChooser.newFolderActionLabel.textAndMnemonic = Nov\u00e1 slo\u017eka +FileChooser.newFolderToolTip.textAndMnemonic = Vytvo\u0159it novou slo\u017eku +FileChooser.refreshActionLabel.textAndMnemonic = Obnovit +FileChooser.saveInLabel.textAndMnemonic = Vyhledat v: +FileChooser.upFolderToolTip.textAndMnemonic = O \u00farove\u0148 v\u00fd\u0161 +FileChooser.viewMenuButtonAccessibleName = Menu Zobrazit +FileChooser.viewMenuButtonToolTipText = Menu Zobrazit +FileChooser.viewMenuLabel.textAndMnemonic = Zobrazit +FileChooser.newFolderActionLabelText = Nov\u00e1 slo\u017eka +FileChooser.listViewActionLabelText = Seznam +FileChooser.detailsViewActionLabelText = Detaily +FileChooser.refreshActionLabelText = Obnovit +FileChooser.sortMenuLabelText = Se\u0159adit ikony podle +FileChooser.viewMenuLabelText = Zobrazit +FileChooser.fileSizeKiloBytes = {0} KB +FileChooser.fileSizeMegaBytes = {0} MB +FileChooser.fileSizeGigaBytes = {0} GB +FileChooser.folderNameLabelText = N\u00e1zev slo\u017eky: + +error.occured = Do\u0161lo k chyb\u011b : %error% +button.abort = P\u0159eru\u0161it +button.retry = Znovu +button.ignore = Ignorovat + +font.source = Zdrojov\u00e9 p\u00edsmo: + +#after version 1.6.7: + +menu.export = Export +menu.general = Hlavn\u00ed +menu.language = Jazyk + +startup.welcometo = V\u00edtejte v programu +startup.selectopen = Pro za\u010d\u00e1tek klikn\u011bte na otev\u0159\u00edt v horn\u00edm panelu nebo p\u0159et\u00e1hn\u011bte SWF soubor p\u0159\u00edmo do tohoto okna. + +error.font.nocharacter = Vybran\u00e9 zdrojov\u00e9 p\u00edsmo neobsahuje znak "%char%". + +warning.initializers = Statick\u00e9 atributy a konstanty jsou \u010dasto inicializov\u00e1ny pomoc\u00ed inicializ\u00e1tor\u016f.\nPokud to uprav\u00edte zde, obvykle to nesta\u010d\u00ed! + +#after version 1.7.0u1: + +menu.tools.searchMemory = Hledat SWF v pam\u011bti +menu.file.reload = Znovu na\u010d\u00edst +message.confirm.reload = Tato akce zru\u0161\u00ed v\u0161echny neulo\u017een\u00e9 zm\u011bny a znovu na\u010dte SWF soubor.\nChcete pokra\u010dovat? + +dialog.selectbkcolor.title = Vyberte barvu pozad\u00ed pro zobrazen\u00ed SWF +button.selectbkcolor.hint = Vybrat barvu pozad\u00ed + +ColorChooser.okText = OK +ColorChooser.cancelText = Storno +ColorChooser.resetText = Obnovit +ColorChooser.previewText = N\u00e1hled +ColorChooser.swatchesNameText = Vzorn\u00edk +ColorChooser.swatchesRecentText = Ned\u00e1vn\u00e9: +ColorChooser.sampleText = Vzorov\u00fd Text Vzorov\u00fd Text + +#after version 1.7.1: + +preview.play = P\u0159ehr\u00e1t +preview.pause = Pauza +preview.stop = Zastavit + +message.confirm.removemultiple = Opravdu chcete odebrat %count% polo\u017eek\n a v\u0161echny objekty kter\u00e9 na nich z\u00e1vis\u00ed? + +menu.tools.searchCache = Prohledat cache prohl\u00ed\u017ee\u010d\u016f + +#after version 1.7.2u2 + +error.trait.exists = Vlastnost s n\u00e1zvem "%name%" ji\u017e existuje. +button.addtrait = P\u0159idat vlastnost +button.font.embed = Vlo\u017eit... +button.yes.all = Ano v\u0161em +button.no.all = Ne v\u0161em +message.font.add.exists = Znak %char% ji\u017e v tagu p\u00edsma existuje.\nChcete ho nahradit? + +filter.gfx = ScaleForm GFx soubory (*.gfx) +filter.supported = V\u0161echny podporovan\u00e9 typy + +work.canceled = Stornov\u00e1no +work.restoringControlFlow = Obnovuji control flow +menu.advancedsettings.advancedsettings = Pokro\u010dil\u00e1 nastaven\u00ed +menu.recentFiles = Ned\u00e1vno otev\u0159en\u00e9 + +#after version 1.7.4 +work.restoringControlFlow.complete = Control flow obnoven +message.confirm.recentFileNotFound = Soubor nenalezen. Chcete jej odebrat ze seznamu ned\u00e1vno otev\u0159en\u00fdch? +contextmenu.closeSwf = Zav\u0159\u00edt SWF +menu.settings.autoRenameIdentifiers = Automaticky p\u0159ejmenovat identifik\u00e1tory +menu.file.saveasexe = Ulo\u017eit jako Exe... +filter.exe = Spustiteln\u00e9 soubory (*.exe) + +#after version 1.8.0 +font.updateTexts = Aktualizovat texty + +#after version 1.8.0u1 +menu.file.close = Zav\u0159\u00edt +menu.file.closeAll = Zav\u0159\u00edt v\u0161e +menu.tools.otherTools = Dal\u0161\u00ed +menu.tools.otherTools.clearRecentFiles = Vymazat ned\u00e1vno otev\u0159en\u00e9 +fontName.name = N\u00e1zev pro zobrazen\u00ed: +fontName.copyright = Copyright p\u00edsma: +button.preview = N\u00e1hled +button.reset = Reset +errors.info = V logu jsou INFORMACE. Klikn\u011bte pro zobrazen\u00ed. +errors.warning = V logu jsou VAROV\u00c1N\u00cd. Klikn\u011bte pro zobrazen\u00ed. + +decompilationError = Chyba dekompilace + +disassemblingProgress.toString = toString +disassemblingProgress.reading = \u010cten\u00ed +disassemblingProgress.deobfuscating = Deobfuskace + +contextmenu.moveTag = P\u0159esunout tag do + +filter.swc = SWC soubory komponent (*.swc) +filter.zip = ZIP komprimovan\u00e9 soubory (*.zip) +filter.binary = Bin\u00e1rn\u00ed vyhled\u00e1v\u00e1n\u00ed - v\u0161echny soubory (*.*) + +open.error = Chyba +open.error.fileNotFound = Soubor nenalezen +open.error.cannotOpen = Soubor nelze otev\u0159\u00edt + +node.others = ostatn\u00ed + +#after version 1.8.1 +menu.tools.search = Hled\u00e1n\u00ed Textu + +#after version 1.8.1u1 +menu.tools.timeline = \u010casov\u00e1 osa + +dialog.selectcolor.title = Vyberte barvu +button.selectcolor.hint = Kliknut\u00edm vyberete barvu + +#default item name, will be used in following sentences +generictag.array.item = polo\u017eku +generictag.array.insertbeginning = Vlo\u017eit %item% na za\u010d\u00e1tek +generictag.array.insertbefore = Vlo\u017eit %item% p\u0159ed +generictag.array.remove = Odstranit %item% +generictag.array.insertafter = Vlo\u017eit %item% za +generictag.array.insertend = Vlo\u017eit %item% na konec + +#after version 2.0.0 +contextmenu.expandAll = Rozbalit v\u0161e + +filter.sounds = Podporovan\u00e9 zvukov\u00e9 form\u00e1ty (*.wav, *.mp3) +filter.sounds.wav = Wave form\u00e1t (*.wav) +filter.sounds.mp3 = MP3 komprimovan\u00fd form\u00e1t (*.mp3) + +error.sound.invalid = Neplatn\u00fd zvuk. + +button.prev = P\u0159edchoz\u00ed +button.next = Dal\u0161\u00ed + +#after version 2.1.0 +message.action.playerglobal.title = Vy\u017eadov\u00e1na knihovna PlayerGlobal +message.action.playerglobal.needed = Pro p\u0159\u00edmou editaci ActionScriptu 3 je pot\u0159eba knihovna "PlayerGlobal.swc", kterou lze stahnout ze str\u00e1nek Adobe\r\n%adobehomepage%\r\nStiskn\u011bte OK pro p\u0159echod na stahovac\u00ed str\u00e1nku. +message.action.playerglobal.place = St\u00e1hn\u011bte knihovnu nazvanou PlayerGlobal(.swc), a um\u00edst\u011bte j\u00ed do adres\u00e1\u0159e\r\n%libpath%\r\n Stiskn\u011bte OK pro pokra\u010dov\u00e1n\u00ed. + +message.confirm.experimental.function = Tato funkce je EXPERIMENT\u00c1LN\u00cd. To znamen\u00e1, \u017ee byste nem\u011bli v\u011b\u0159it jej\u00edm v\u00fdsledk\u016fm a SWF soubor m\u016f\u017ee po ulo\u017een\u00ed p\u0159estat fungovat. +message.confirm.donotshowagain = P\u0159\u00ed\u0161t\u011b nezobrazovat + +menu.import = Import +menu.file.import.text = Import textu +import.select.directory = Vyberte adres\u00e1\u0159 pro import +error.text.import = Chyba b\u011bhem importu textu. Chcete pokra\u010dovat? + +#after version 2.1.1 +contextmenu.removeWithDependencies = Odstranit se z\u00e1vislostmi + +abc.action.find-usages = Naj\u00edt pou\u017eit\u00ed +abc.action.find-declaration = Naj\u00edt deklaraci + +contextmenu.rawEdit = P\u0159\u00edm\u00e1 editace +contextmenu.jumpToCharacter = Skok na charakter + +menu.settings.dumpView = Zobrazit Dump + +menu.view = Zobrazen\u00ed +menu.file.view.resources = Zdroje +menu.file.view.hex = Hex dump + +node.header = hlavi\u010dka + +header.signature = Signatura: +header.compression = Komprese: +header.compression.lzma = LZMA +header.compression.zlib = ZLIB +header.compression.none = Bez komprese +header.version = Verze SWF: +header.gfx = GFX: +header.filesize = Velikost souboru: +header.framerate = Frekvence sn\u00edmk\u016f: +header.framecount = Po\u010det sn\u00edmk\u016f: +header.displayrect = Zobrazen\u00fd obd\u00e9ln\u00edk: +header.displayrect.value.twips = %xmin%,%ymin% => %xmax%,%ymax% twip\u016f +header.displayrect.value.pixels = %xmin%,%ymin% => %xmax%,%ymax% pixel\u016f + +contextmenu.saveToFile = Ulo\u017eit do souboru +contextmenu.parseActions = Naparsovat akce +contextmenu.parseABC = Naparsovat ABC +contextmenu.parseInstructions = Naparsovat AVM2 instrukce + +menu.deobfuscation = Deobfuskace +menu.file.deobfuscation.old = Star\u00fd zp\u016fsob +menu.file.deobfuscation.new = Nov\u00fd zp\u016fsob + +contextmenu.openswfinside = Otev\u0159\u00edt SWF uvnit\u0159 +binarydata.swfInside = Vypad\u00e1 to, \u017ee uvnit\u0159 v tomto BinaryData tagu se nach\u00e1z\u00ed SWF soubor. Klikn\u011bte zde pro jeho na\u010dten\u00ed jako podstrom. + +#after version 3.0.0 +button.zoomin.hint = P\u0159ibl\u00ed\u017eit +button.zoomout.hint = Odd\u00e1lit +button.zoomfit.hint = Rozt\u00e1hnout +button.zoomnone.hint = M\u011b\u0159\u00edtko 1:1 +button.snapshot.hint = Sn\u00edmek do schr\u00e1nky + +editorTruncateWarning = Text je o\u0159\u00edznut na pozici %chars% v lad\u00edc\u00edm m\u00f3du. + +font.name.intag = N\u00e1zev p\u00edsma v tagu: + +menu.debugger = Debugger +menu.debugger.switch = Debugger +menu.debugger.replacetrace = Nahradit vol\u00e1n\u00ed trace +menu.debugger.showlog = Zobrazit Log + +message.debugger = Tento SWF Debugger slou\u017e\u00ed jen k pos\u00edl\u00e1n\u00ed zpr\u00e1v do okna logu, konzole prohl\u00ed\u017ee\u010de nebo alert oken.\r\nNEN\u00cd ur\u010den pro krokov\u00e1n\u00ed k\u00f3du, breakpointy atd. + +contextmenu.addTag = P\u0159idat tag + +deobfuscation.comment.tryenable = Tip: m\u016f\u017eete zkusit povolit "Automatickou deobfuskaci" v nastaven\u00ed +deobfuscation.comment.failed = Deobfuskace je aktivn\u00ed, ale dekompilace p\u0159esto selhala. Pokud soubor NEN\u00cd obfuskov\u00e1n, zaka\u017ete "Automatickou deobfuskaci" pro lep\u0161\u00ed v\u00fdsledky. + +#after version 4.0.2 +preview.nextframe = Dal\u0161\u00ed sn\u00edmek +preview.prevframe = P\u0159ede\u0161l\u00fd sn\u00edmek +preview.gotoframe = P\u0159ej\u00edt na sn\u00edmek... + +preview.gotoframe.dialog.title = P\u0159ej\u00edt na sn\u00edmek +preview.gotoframe.dialog.message = Zadejte \u010d\u00edslo sn\u00edmku (%min% - %max%) +preview.gotoframe.dialog.frame.error = Neplatn\u00e9 \u010d\u00edslo sn\u00edmku. Mus\u00ed to b\u00fdt \u010d\u00edslo v rozmez\u00ed %min% a %max%. + +error.text.invalid.continue = Neplatn\u00fd text: %text% na \u0159\u00e1dku %line%. Chcete pokra\u010dovat? + +#after version 4.0.5 +contextmenu.copyTag = Zkop\u00edrovat tag do +fit = p\u0159izp\u016fsobit +button.setAdvanceValues = Nastavit hodnoty advance + +menu.tools.replace = Nahrazen\u00ed textu + +message.confirm.close = Existuj\u00ed neulo\u017een\u00e9 zm\u011bny. Opravdu chcete zav\u0159\u00edt {swfName}? +message.confirm.closeAll = Existuj\u00ed neulo\u017een\u00e9 zm\u011bny. Opravdu chcete zav\u0159\u00edt v\u0161echny SWF? + +contextmenu.exportJavaSource = Exportovat zdroj\u00e1k Javy +contextmenu.exportSwfXml = Exportovat SWF jako XML +contextmenu.importSwfXml = Importovat SWF XML + +filter.xml = XML + +#after version 4.1.0 +contextmenu.undo = Zp\u011bt + +text.align.left = Zarovnat vlevo +text.align.right = Zarovnat vpravo +text.align.center = Zarovnat na st\u0159ed +text.align.justify = Zarovnat do bloku + +text.undo = Vr\u00e1tit zm\u011bny + +menu.file.import.xml = Import SWF XML +menu.file.export.xml = Export SWF XML + +#after version 4.1.1 +text.align.translatex.decrease = Sn\u00ed\u017eit TranslateX +text.align.translatex.increase = Zv\u00fd\u0161it TranslateX +selectPreviousTag = Vybrat p\u0159edchoz\u00ed tag +selectNextTag = Vybrat dal\u0161\u00ed tag +button.ignoreAll = Ignorovat V\u0161e +menu.file.import.symbolClass = Import Symbol-Class +text.toggleCase = P\u0159epnout mal\u00e1/velk\u00e1 + +#after version 5.0.2 +preview.loop = Opakovat +menu.file.import.script = Importovat skript +contextmenu.copyTagWithDependencies = Kop\u00edrovat tagy se z\u00e1vislostmi do +button.replaceWithTag = Nahradit jin\u00fdm charakterov\u00fdm tagem +button.resolveConstants = Resolvovat konstanty + +#after version 5.1.0 +button.viewConstants = Zobrazit konstanty +work.exported = Exportov\u00e1no +button.replaceAlphaChannel = Nahradit alfa kan\u00e1l... + +tagInfo.header.name = N\u00e1zev +tagInfo.header.value = Hodnota +tagInfo.tagType = Typ tagu +tagInfo.characterId = Id charakteru +tagInfo.offset = Offset +tagInfo.length = D\u00e9lka +tagInfo.bounds = Meze +tagInfo.width = \u0160\u00ed\u0159ka +tagInfo.height = V\u00fd\u0161ka +tagInfo.neededCharacters = Pot\u0159ebn\u00e9 charaktery + +button.viewhexpcode = Zobrazit Hex s instrukcemi +taginfo.header = Z\u00e1kladn\u00ed informace o tagu + +tagInfo.dependentCharacters = Z\u00e1visl\u00e9 charaktery + +#after version 5.3.0 +header.uncompressed = Bez komprese +header.warning.unsupportedGfxCompression = GFX podporuje pouze soubor se Zlib nebo \u017e\u00e1dnou kompres\u00ed. +header.warning.minimumZlibVersion = Zlib komprese vy\u017eaduje SWF verze 6 \u010di vy\u0161\u0161\u00ed. +header.warning.minimumLzmaVersion = LZMA komprese vy\u017eaduje SWF verze 13 \u010di vy\u0161\u0161\u00ed. + +tagInfo.codecName = N\u00e1zev kodeku +tagInfo.exportFormat = Form\u00e1t exportu +tagInfo.samplingRate = Vzorkovac\u00ed frekvence +tagInfo.stereo = Stereo +tagInfo.sampleCount = Po\u010det vzork\u016f + +filter.dmg = Spustieln\u00e9 soubory Macu (*.dmg) +filter.linuxExe = Linuxov\u00e9 spustiteln\u00e9 soubory + +import.script.result = %count% skript\u016f importov\u00e1no. +import.script.as12warning = Import skript\u016f funguje jen pro AS1/2 skripty. + +error.constantPoolTooBig = Seznam konstant je moc velk\u00fd index=%index%, velikost=%size% +error.image.alpha.invalid = Neplatn\u00e1 data alpha kan\u00e1lu. + +#after version 6.0.2 +contextmenu.saveUncompressedToFile = Ulo\u017eit nekomprimovan\u011b +abc.traitslist.scriptinitializer = inicializ\u00e1tor skriptu +menu.settings.autoOpenLoadedSWFs = Otev\u00edrat na\u010d\u00edtan\u00e1 SWF b\u011bhem p\u0159ehr\u00e1v\u00e1n\u00ed + +#after version 6.1.1 +menu.file.start = Start +menu.file.start.run = Spustit +menu.file.start.stop = Zastavit +menu.file.start.debug = Ladit +menu.debugging = Lad\u011bn\u00ed +menu.debugging.debug = Lad\u011bn\u00ed +menu.debugging.debug.stop = Ukon\u010dit +menu.debugging.debug.pause = Pauza +menu.debugging.debug.stepOver = Step over +menu.debugging.debug.stepInto = Step into +menu.debugging.debug.stepOut = Step out +menu.debugging.debug.continue = Pokra\u010dovat +menu.debugging.debug.stack = Stack... +menu.debugging.debug.watch = New watch... + +message.playerpath.notset = Flash Player projector nenalezen. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (1). +message.playerpath.debug.notset = Flash Player projector content debugger nenalezen. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (2). +message.playerpath.lib.notset = PlayerGlobal (.SWC) nenalezen. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (3). + +debugpanel.header = Lad\u011bn\u00ed + +variables.header.registers = Registry +variables.header.locals = Lok\u00e1ln\u00ed +variables.header.arguments = Argumenty +variables.header.scopeChain = Scope chain +variables.column.name = N\u00e1zev +variables.column.type = Typ +variables.column.value = Hodnota + +callStack.header = Call stack +callStack.header.file = Soubor +callStack.header.line = \u0158\u00e1dek + +stack.header = Stack +stack.header.item = Polo\u017eka + +work.running = B\u011bh +work.debugging = Lad\u011bn\u00ed +work.debugging.instrumenting = P\u0159\u00edprava SWF pro lad\u011bn\u00ed +work.breakat = Pozastaveno na \u0020 +work.halted = Lad\u011bn\u00ed za\u010dalo, b\u011bh je nyn\u00ed pozastaven. P\u0159idejte breakpointy a klikn\u011bte na Pokra\u010dovat (F5) pro obnoven\u00ed b\u011bhu. + +debuglog.header = Z\u00e1znam +debuglog.button.clear = Vypr\u00e1znit + +#after 7.0.1 +work.debugging.wait = \u010cek\u00e1 se na p\u0159ipojen\u00ed Flash debug projektoru + +error.debug.listen = Nelze naslouchat na portu %port%. Mo\u017en\u00e1 je spu\u0161t\u011bn n\u011bjak\u00fd jin\u00fd debugger. + +debug.break.reason.unknown = (Nezn\u00e1m\u00fd d\u016fvod) +debug.break.reason.breakpoint = (Breakpoint) +debug.break.reason.watch = (Watch) +debug.break.reason.fault = (Selh\u00e1n\u00ed) +debug.break.reason.stopRequest = (Zastaveno) +debug.break.reason.step = (Step) +debug.break.reason.halt = (Halt) +debug.break.reason.scriptLoaded = (Skript na\u010dten) + +menu.file.start.debugpcode = Lad\u011bn\u00ed P-k\u00f3du + +#after 7.1.2 +button.replaceNoFill = Nahradit - aktualizovat hranice... +message.warning.svgImportExperimental = Ne v\u0161echny SVG vlastnosti jsou podporov\u00e1ny. Pros\u00edm zkontrolujte log po importu. + +message.imported.swf = SWF soubor pou\u017e\u00edv\u00e1 assety z importovan\u00e9ho SWF souboru:\n%url%\nChcete tyto assety na\u010d\u00edst z dan\u00e9ho URL? +message.imported.swf.manually = Nelze na\u010d\u00edst importovan\u00e9 SWF\n%url%\nSoubor nebo URL neexistuje.\nChcete vybrat m\u00edstn\u00ed soubor? + +message.warning.hexViewNotUpToDate = Hexa pohled nen\u00ed aktu\u00e1ln\u00ed. Pros\u00edm ulo\u017ete a znovuna\u010dt\u011bte soubor pro aktualizaci hexa pohledu. +message.font.replace.updateTexts = N\u011bkter\u00e9 znaky byly nahrazeny. Chcete aktualizovat existuj\u00edc\u00ed texty? + +menu.settings.simplifyExpressions = Zjednodu\u0161it v\u00fdrazy + +#after 8.0.1 +menu.recentFiles.empty = Seznam ned\u00e1vno otev\u0159en\u00fdch je pr\u00e1zdn\u00fd +message.warning.outOfMemory32BitJre = Nastala chyba z nedostatku pam\u011bti. M\u00e1te spu\u0161t\u011bnou 32bitovou Javu na 64bitov\u00e9m syst\u00e9mu. Pros\u00edm pou\u017eijte 64bitovou Java. + +menu.file.reloadAll = Znovu na\u010d\u00edst v\u0161e +message.confirm.reloadAll = Tato akce stornuje v\u0161echny neulo\u017een\u00e9 zm\u011bny ve v\u0161ech SWF souborech a znovu na\u010dte celou aplikaci.\nChcete pokra\u010dovat? +export.script.singleFilePallelModeWarning = Export skript\u016f do jednoho souboru nen\u00ed dostupn\u00fd se zapnut\u00fdm paralleln\u00edm zrychlen\u00edm + +button.showOriginalBytesInPcodeHex = Zobrazit origin\u00e1ln\u00ed bajty +button.remove = Odstranit +button.showFileOffsetInPcodeHex = Zobrazit offset v souboru + +generic.editor.amf3.title = AMF3 editor +generic.editor.amf3.help = Syntaxe AMF3 hodnoty:\n\ + ------------------\n\ + skal\u00e1rn\u00ed typy:\n\ + %scalar_samples%\ + ostatn\u00ed typy:\n\ + %nonscalar_samples%\ + \n\ + Pozn\u00e1mky:\n\ + \ * Neskal\u00e1rn\u00ed datov\u00e9 typy mohou mohou b\u00fdt odkazov\u00e1ny p\u0159edem deklarovan\u00fdmi "id" atributy s pomoc\u00ed # syntaxe:\n\ + %reference_sample%\n\ + \ * Kl\u00ed\u010de v z\u00e1znamech Dictionary mohou b\u00fdt jak\u00e9hokoli typu\n +contextmenu.showInResources = Zobrazit ve Zdroj\u00edch +message.flexpath.notset = Flex SDK nenalezeno. Pros\u00edm nastavte cestu k n\u011bmu v Pokro\u010dil\u00e1 nastaven\u00ed / Cesty (4). + + +#add after panel.disassembled string +abc.detail.split = :\u0020 +abc.detail.trait = Vlastnost - %trait_type% +abc.detail.trait.method = Metoda +abc.detail.trait.getter = Getter +abc.detail.trait.setter = Setter +abc.detail.trait.slot = Slot +abc.detail.trait.const = Konstanta +abc.detail.trait.class = T\u0159\u00edda +abc.detail.trait.function = Funkce + +abc.detail.specialmethod = Speci\u00e1ln\u00ed metoda - %specialmethod_type% +abc.detail.specialmethod.scriptinitializer = Inicializ\u00e1tor skriptu +abc.detail.specialmethod.classinitializer = Inicializ\u00e1tor t\u0159\u00eddy +abc.detail.specialmethod.instanceinitializer = Inicializ\u00e1tor instance +abc.detail.innerfunction = Vnit\u0159n\u00ed funkce + +button.edit.script.decompiled = Upravit ActionScript +button.edit.script.disassembled = Upravit P-k\u00f3d + +message.font.setadvancevalues = Tato operace nastav\u00ed advance V\u0160ECH znak\u016f v tomto tagu na advance hodnoty zdrojov\u00e9ho p\u00edsma. + filter.iggy = Iggy soubory (*.iggy) \ No newline at end of file diff --git a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java index a7d675e23..9ba56f74f 100644 --- a/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java +++ b/src/com/jpexs/decompiler/flash/gui/tagtree/TagTree.java @@ -1,689 +1,689 @@ -/* - * Copyright (C) 2010-2016 JPEXS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.jpexs.decompiler.flash.gui.tagtree; - -import com.jpexs.decompiler.flash.SWC; -import com.jpexs.decompiler.flash.SWF; -import com.jpexs.decompiler.flash.ZippedSWFBundle; -import com.jpexs.decompiler.flash.abc.ScriptPack; -import com.jpexs.decompiler.flash.gui.MainPanel; -import com.jpexs.decompiler.flash.gui.TreeNodeType; -import com.jpexs.decompiler.flash.gui.View; -import com.jpexs.decompiler.flash.iggy.conversion.IggySwfBundle; -import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; -import com.jpexs.decompiler.flash.tags.DebugIDTag; -import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; -import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; -import com.jpexs.decompiler.flash.tags.DefineBitsTag; -import com.jpexs.decompiler.flash.tags.DefineButton2Tag; -import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; -import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; -import com.jpexs.decompiler.flash.tags.DefineButtonTag; -import com.jpexs.decompiler.flash.tags.DefineEditTextTag; -import com.jpexs.decompiler.flash.tags.DefineFont2Tag; -import com.jpexs.decompiler.flash.tags.DefineFont3Tag; -import com.jpexs.decompiler.flash.tags.DefineFont4Tag; -import com.jpexs.decompiler.flash.tags.DefineFontAlignZonesTag; -import com.jpexs.decompiler.flash.tags.DefineFontInfo2Tag; -import com.jpexs.decompiler.flash.tags.DefineFontInfoTag; -import com.jpexs.decompiler.flash.tags.DefineFontNameTag; -import com.jpexs.decompiler.flash.tags.DefineFontTag; -import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; -import com.jpexs.decompiler.flash.tags.DefineScalingGridTag; -import com.jpexs.decompiler.flash.tags.DefineSceneAndFrameLabelDataTag; -import com.jpexs.decompiler.flash.tags.DefineShape2Tag; -import com.jpexs.decompiler.flash.tags.DefineShape3Tag; -import com.jpexs.decompiler.flash.tags.DefineShape4Tag; -import com.jpexs.decompiler.flash.tags.DefineShapeTag; -import com.jpexs.decompiler.flash.tags.DefineSoundTag; -import com.jpexs.decompiler.flash.tags.DefineSpriteTag; -import com.jpexs.decompiler.flash.tags.DefineText2Tag; -import com.jpexs.decompiler.flash.tags.DefineTextTag; -import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; -import com.jpexs.decompiler.flash.tags.DoABC2Tag; -import com.jpexs.decompiler.flash.tags.DoABCTag; -import com.jpexs.decompiler.flash.tags.DoActionTag; -import com.jpexs.decompiler.flash.tags.DoInitActionTag; -import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag; -import com.jpexs.decompiler.flash.tags.EnableDebuggerTag; -import com.jpexs.decompiler.flash.tags.EnableTelemetryTag; -import com.jpexs.decompiler.flash.tags.ExportAssetsTag; -import com.jpexs.decompiler.flash.tags.FileAttributesTag; -import com.jpexs.decompiler.flash.tags.FrameLabelTag; -import com.jpexs.decompiler.flash.tags.ImportAssets2Tag; -import com.jpexs.decompiler.flash.tags.ImportAssetsTag; -import com.jpexs.decompiler.flash.tags.JPEGTablesTag; -import com.jpexs.decompiler.flash.tags.MetadataTag; -import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; -import com.jpexs.decompiler.flash.tags.PlaceObject3Tag; -import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; -import com.jpexs.decompiler.flash.tags.PlaceObjectTag; -import com.jpexs.decompiler.flash.tags.ProductInfoTag; -import com.jpexs.decompiler.flash.tags.ProtectTag; -import com.jpexs.decompiler.flash.tags.RemoveObject2Tag; -import com.jpexs.decompiler.flash.tags.RemoveObjectTag; -import com.jpexs.decompiler.flash.tags.ScriptLimitsTag; -import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; -import com.jpexs.decompiler.flash.tags.SetTabIndexTag; -import com.jpexs.decompiler.flash.tags.ShowFrameTag; -import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; -import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; -import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; -import com.jpexs.decompiler.flash.tags.StartSound2Tag; -import com.jpexs.decompiler.flash.tags.StartSoundTag; -import com.jpexs.decompiler.flash.tags.SymbolClassTag; -import com.jpexs.decompiler.flash.tags.Tag; -import com.jpexs.decompiler.flash.tags.VideoFrameTag; -import com.jpexs.decompiler.flash.tags.base.ASMSource; -import com.jpexs.decompiler.flash.tags.base.ButtonTag; -import com.jpexs.decompiler.flash.tags.base.FontTag; -import com.jpexs.decompiler.flash.tags.base.ImageTag; -import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; -import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; -import com.jpexs.decompiler.flash.tags.base.RemoveTag; -import com.jpexs.decompiler.flash.tags.base.ShapeTag; -import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; -import com.jpexs.decompiler.flash.tags.base.TextTag; -import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; -import com.jpexs.decompiler.flash.timeline.AS2Package; -import com.jpexs.decompiler.flash.timeline.AS3Package; -import com.jpexs.decompiler.flash.timeline.Frame; -import com.jpexs.decompiler.flash.timeline.FrameScript; -import com.jpexs.decompiler.flash.timeline.TagScript; -import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem; -import com.jpexs.decompiler.flash.treeitems.FolderItem; -import com.jpexs.decompiler.flash.treeitems.HeaderItem; -import com.jpexs.decompiler.flash.treeitems.SWFList; -import com.jpexs.decompiler.flash.treeitems.TreeItem; -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.swing.Icon; -import javax.swing.JTree; -import javax.swing.plaf.basic.BasicLabelUI; -import javax.swing.plaf.basic.BasicTreeUI; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; - -/** - * - * @author JPEXS - */ -public class TagTree extends JTree { - - public TagTreeContextMenu contextPopupMenu; - - private final MainPanel mainPanel; - - private static final Map ICONS; - - static { - ICONS = new HashMap<>(); - for (TreeNodeType treeNodeType : TreeNodeType.values()) { - if (treeNodeType != TreeNodeType.UNKNOWN && treeNodeType != TreeNodeType.SHOW_FRAME) { - String tagTypeStr = treeNodeType.toString().toLowerCase().replace("_", ""); - ICONS.put(treeNodeType, View.getIcon(tagTypeStr + "16")); - } - } - } - - public static Icon getIconForType(TreeNodeType t) { - return ICONS.get(t); - } - - public static class TagTreeCellRenderer extends DefaultTreeCellRenderer { - - private Font plainFont; - - private Font boldFont; - - public TagTreeCellRenderer() { - setUI(new BasicLabelUI()); - setOpaque(false); - //setBackground(Color.green); - setBackgroundNonSelectionColor(Color.white); - //setBackgroundSelectionColor(Color.ORANGE); - - } - - @Override - public Component getTreeCellRendererComponent( - JTree tree, - Object value, - boolean sel, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) { - - TreeItem val = (TreeItem) value; - if (val != null && !(val instanceof SWFList) && val.getSwf() == null) { - // SWF was closed - value = null; - } - - super.getTreeCellRendererComponent( - tree, value, sel, - expanded, leaf, row, - hasFocus); - - if (val == null) { - return this; - } - - TreeNodeType type = getTreeNodeType(val); - - if (type == TreeNodeType.FOLDER && expanded) { - type = TreeNodeType.FOLDER_OPEN; - } - - if ((type == TreeNodeType.FOLDER || type == TreeNodeType.FOLDER_OPEN) && val instanceof FolderItem) { - FolderItem si = (FolderItem) val; - if (!TagTreeRoot.FOLDER_ROOT.equals(si.getName())) { - String itemName = "folder" + si.getName(); - setIcon(View.getIcon(itemName.toLowerCase() + "16")); - } - } else { - setIcon(ICONS.get(type)); - } - - /* boolean isModified = val instanceof Tag && ((Tag) val).isModified(); - if(val instanceof ScriptPack){ - ScriptPack sp=(ScriptPack)val; - if(sp.abc.script_info.get(sp.scriptIndex).isModified()){ - isModified = true; - } - }*/ - boolean isReadOnly = false; - if (val instanceof Tag) { - isReadOnly = ((Tag) val).isReadOnly(); - } - - boolean isModified = val.isModified(); - if (isModified) { - if (boldFont == null) { - Font font = getFont(); - boldFont = font.deriveFont(Font.BOLD); - } - } else if (plainFont == null) { - Font font = getFont(); - plainFont = font.deriveFont(Font.PLAIN); - } - setFont(isModified ? boldFont : plainFont); - if (isReadOnly) { - setForeground(new Color(0xcc, 0xcc, 0xcc)); - } else { - setForeground(Color.BLACK); - } - - return this; - } - } - - public TagTree(TagTreeModel treeModel, MainPanel mainPanel) { - super(treeModel); - this.mainPanel = mainPanel; - setCellRenderer(new TagTreeCellRenderer()); - setRootVisible(false); - setBackground(Color.white); - setRowHeight(Math.max(getFont().getSize() + 5, 16)); - setLargeModel(true); - setUI(new BasicTreeUI() { - { - setHashColor(Color.gray); - } - }); - } - - public void createContextMenu() { - contextPopupMenu = new TagTreeContextMenu(this, mainPanel); - } - - public static TreeNodeType getTreeNodeType(TreeItem t) { - - if (t instanceof TagScript) { - t = ((TagScript) t).getTag(); - } - - if (t instanceof HeaderItem) { - return TreeNodeType.HEADER; - } - - if ((t instanceof DefineFontTag) - || (t instanceof DefineFont2Tag) - || (t instanceof DefineFont3Tag) - || (t instanceof DefineFont4Tag) - || (t instanceof DefineCompactedFont)) { - return TreeNodeType.FONT; - } - - // DefineText, DefineText2, DefineEditTextTag - if (t instanceof TextTag) { - return TreeNodeType.TEXT; - } - - // DefineBits, DefineBitsJPEG2, DefineBitsJPEG3, DefineBitsJPEG4, DefineBitsLossless, DefineBitsLossless2 - if (t instanceof ImageTag) { - return TreeNodeType.IMAGE; - } - - // DefineShape, DefineShape2, DefineShape3, DefineShape4 - if (t instanceof ShapeTag) { - return TreeNodeType.SHAPE; - } - - // DefineMorphShape, DefineMorphShape2 - if (t instanceof MorphShapeTag) { - return TreeNodeType.MORPH_SHAPE; - } - - if (t instanceof DefineSpriteTag) { - return TreeNodeType.SPRITE; - } - - // DefineButton, DefineButton2 - if (t instanceof ButtonTag) { - return TreeNodeType.BUTTON; - } - - if (t instanceof DefineVideoStreamTag) { - return TreeNodeType.MOVIE; - } - - if ((t instanceof DefineSoundTag) || (t instanceof SoundStreamHeadTag) || (t instanceof SoundStreamHead2Tag)) { - return TreeNodeType.SOUND; - } - - if (t instanceof DefineBinaryDataTag) { - return TreeNodeType.BINARY_DATA; - } - - if (t instanceof ASMSource) { - return TreeNodeType.AS; - } - - if (t instanceof ScriptPack) { - return TreeNodeType.AS; - } - - if (t instanceof AS2Package) { - return TreeNodeType.PACKAGE; - } - - if (t instanceof AS3Package) { - return TreeNodeType.PACKAGE; - } - - if ((t instanceof Frame) - || (t instanceof FrameScript)) { - return TreeNodeType.FRAME; - } - - if (t instanceof ShowFrameTag) { - return TreeNodeType.SHOW_FRAME; - } - - if (t instanceof SWF) { - return TreeNodeType.FLASH; - } - - if (t instanceof SWFList) { - SWFList slist = (SWFList) t; - if (slist.isBundle()) { - if (slist.bundle.getClass() == ZippedSWFBundle.class) { - return TreeNodeType.BUNDLE_ZIP; - } else if (slist.bundle.getClass() == SWC.class) { - return TreeNodeType.BUNDLE_SWC; - } else if (slist.bundle.getClass() == IggySwfBundle.class) { - return TreeNodeType.BUNDLE_IGGY; - } else { - return TreeNodeType.BUNDLE_BINARY; - } - } - } - - if (t instanceof SetBackgroundColorTag) { - return TreeNodeType.SET_BACKGROUNDCOLOR; - } - if (t instanceof FileAttributesTag) { - return TreeNodeType.FILE_ATTRIBUTES; - } - if (t instanceof MetadataTag) { - return TreeNodeType.METADATA; - } - if (t instanceof PlaceObjectTypeTag) { - return TreeNodeType.PLACE_OBJECT; - } - if (t instanceof RemoveTag) { - return TreeNodeType.REMOVE_OBJECT; - } - if (t instanceof Tag) { - return TreeNodeType.OTHER_TAG; - } - - if (t instanceof FolderItem) { - return TreeNodeType.FOLDER; - } - - return TreeNodeType.FOLDER; - } - - public List getSwfFolderItemNestedTagIds(String folderName, boolean gfx) { - List ret = null; - switch (folderName) { - case TagTreeModel.FOLDER_SHAPES: - ret = Arrays.asList(DefineShapeTag.ID, DefineShape2Tag.ID, DefineShape3Tag.ID, DefineShape4Tag.ID); - break; - case TagTreeModel.FOLDER_MORPHSHAPES: - ret = Arrays.asList(DefineMorphShapeTag.ID, DefineMorphShape2Tag.ID); - break; - case TagTreeModel.FOLDER_SPRITES: - ret = Arrays.asList(DefineSpriteTag.ID); - break; - case TagTreeModel.FOLDER_TEXTS: - ret = Arrays.asList(DefineTextTag.ID, DefineText2Tag.ID, DefineEditTextTag.ID); - break; - case TagTreeModel.FOLDER_IMAGES: - ret = Arrays.asList(DefineBitsTag.ID, DefineBitsJPEG2Tag.ID, DefineBitsJPEG3Tag.ID, DefineBitsJPEG4Tag.ID, DefineBitsLosslessTag.ID, DefineBitsLossless2Tag.ID); - break; - case TagTreeModel.FOLDER_MOVIES: - ret = Arrays.asList(DefineVideoStreamTag.ID); - break; - case TagTreeModel.FOLDER_SOUNDS: - ret = Arrays.asList(DefineSoundTag.ID); - break; - case TagTreeModel.FOLDER_BUTTONS: - ret = Arrays.asList(DefineButtonTag.ID, DefineButton2Tag.ID); - break; - case TagTreeModel.FOLDER_FONTS: - if (gfx) { - ret = Arrays.asList(DefineFontTag.ID, DefineFont2Tag.ID, DefineFont3Tag.ID, DefineFont4Tag.ID, DefineCompactedFont.ID); - } else { - ret = Arrays.asList(DefineFontTag.ID, DefineFont2Tag.ID, DefineFont3Tag.ID, DefineFont4Tag.ID); - } - break; - case TagTreeModel.FOLDER_BINARY_DATA: - ret = Arrays.asList(DefineBinaryDataTag.ID); - break; - case TagTreeModel.FOLDER_FRAMES: - // same as nested tags of DefineSpriteTag? - ret = Arrays.asList(PlaceObjectTag.ID, PlaceObject2Tag.ID, PlaceObject3Tag.ID, PlaceObject4Tag.ID, - RemoveObjectTag.ID, RemoveObject2Tag.ID, ShowFrameTag.ID, FrameLabelTag.ID, - StartSoundTag.ID, StartSound2Tag.ID, VideoFrameTag.ID, - SoundStreamBlockTag.ID, SoundStreamHeadTag.ID, SoundStreamHead2Tag.ID, - DefineScalingGridTag.ID); - break; - case TagTreeModel.FOLDER_OTHERS: - ret = Arrays.asList( - //CSMTextSettingsTag.ID, - DebugIDTag.ID, - //DefineButtonCxformTag.ID, DefineButtonSoundTag.ID, - //DefineFontAlignZonesTag.ID, DefineFontInfoTag.ID, DefineFontInfo2Tag.ID, DefineFontNameTag.ID, - /*DefineScalingGridTag.ID,*/ DefineSceneAndFrameLabelDataTag.ID, - DoABC2Tag.ID, DoABCTag.ID, DoActionTag.ID, DoInitActionTag.ID, - EnableDebuggerTag.ID, EnableDebugger2Tag.ID, EnableTelemetryTag.ID, - ExportAssetsTag.ID, FileAttributesTag.ID, ImportAssetsTag.ID, ImportAssets2Tag.ID, - JPEGTablesTag.ID, MetadataTag.ID, ProductInfoTag.ID, ProtectTag.ID, ScriptLimitsTag.ID, - SetBackgroundColorTag.ID, SetTabIndexTag.ID, SymbolClassTag.ID); - break; - } - - return ret; - } - - public List getFrameNestedTagIds() { - return Arrays.asList(PlaceObjectTag.ID, PlaceObject2Tag.ID, PlaceObject3Tag.ID, PlaceObject4Tag.ID, - RemoveObjectTag.ID, RemoveObject2Tag.ID, FrameLabelTag.ID, - StartSoundTag.ID, StartSound2Tag.ID, VideoFrameTag.ID, - SoundStreamBlockTag.ID, SoundStreamHeadTag.ID, SoundStreamHead2Tag.ID); - } - - public List getNestedTagIds(Tag obj) { - if (obj instanceof DefineSpriteTag) { - return Arrays.asList(PlaceObjectTag.ID, PlaceObject2Tag.ID, PlaceObject3Tag.ID, PlaceObject4Tag.ID, - RemoveObjectTag.ID, RemoveObject2Tag.ID, ShowFrameTag.ID, FrameLabelTag.ID, - StartSoundTag.ID, StartSound2Tag.ID, VideoFrameTag.ID, - SoundStreamBlockTag.ID, SoundStreamHeadTag.ID, SoundStreamHead2Tag.ID, - DefineScalingGridTag.ID); - } - if (obj instanceof FontTag) { - return Arrays.asList(DefineFontNameTag.ID, DefineFontAlignZonesTag.ID, DefineFontInfoTag.ID, DefineFontInfo2Tag.ID); - } - if (obj instanceof TextTag) { - return Arrays.asList(CSMTextSettingsTag.ID); - } - if (obj instanceof DefineButtonTag) { - return Arrays.asList(DefineButtonCxformTag.ID, DefineButtonSoundTag.ID, DefineScalingGridTag.ID); - } - if (obj instanceof DefineButton2Tag) { - return Arrays.asList(DefineButtonSoundTag.ID, DefineScalingGridTag.ID); - } - return null; - } - - public boolean hasExportableNodes() { - return !getSelection(mainPanel.getCurrentSwf()).isEmpty(); - } - - public void getAllSubs(TreeItem o, List ret) { - TagTreeModel tm = getModel(); - for (TreeItem c : tm.getAllChildren(o)) { - ret.add(c); - getAllSubs(c, ret); - } - } - - public List getAllSelected() { - TreeSelectionModel tsm = getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); - List ret = new ArrayList<>(); - if (tps == null) { - return ret; - } - - for (TreePath tp : tps) { - TreeItem treeNode = (TreeItem) tp.getLastPathComponent(); - ret.add(treeNode); - getAllSubs(treeNode, ret); - } - return ret; - } - - public List getSelected() { - if (!mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { - return new ArrayList<>(mainPanel.folderPreviewPanel.selectedItems.values()); - } - TreeSelectionModel tsm = getSelectionModel(); - TreePath[] tps = tsm.getSelectionPaths(); - List ret = new ArrayList<>(); - if (tps == null) { - return ret; - } - - for (TreePath tp : tps) { - TreeItem treeNode = (TreeItem) tp.getLastPathComponent(); - ret.add(treeNode); - } - return ret; - } - - public List getSelection(SWF swf) { - List sel; - if (mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { - sel = getAllSelected(); - } else { - sel = new ArrayList<>(mainPanel.folderPreviewPanel.selectedItems.values()); - } - return getSelection(swf, sel); - } - - public List getSelection(SWF swf, List sel) { - List ret = new ArrayList<>(); - for (TreeItem d : sel) { - if (d instanceof SWFList) { - continue; - } - if (d.getSwf() != swf) { - continue; - } - - if (d instanceof TagScript) { - Tag tag = ((TagScript) d).getTag(); - if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { - d = tag; - } - } - - if (d instanceof Tag || d instanceof ASMSource) { - TreeNodeType nodeType = TagTree.getTreeNodeType(d); - if (nodeType == TreeNodeType.IMAGE) { - ret.add(d); - } - if (nodeType == TreeNodeType.SHAPE) { - ret.add(d); - } - if (nodeType == TreeNodeType.MORPH_SHAPE) { - ret.add(d); - } - if (nodeType == TreeNodeType.BUTTON) { - ret.add(d); - } - if (nodeType == TreeNodeType.AS) { - ret.add(d); - } - if (nodeType == TreeNodeType.MOVIE) { - ret.add(d); - } - if (nodeType == TreeNodeType.SOUND) { - ret.add(d); - } - if (nodeType == TreeNodeType.BINARY_DATA) { - ret.add(d); - } - if (nodeType == TreeNodeType.TEXT) { - ret.add(d); - } - if (nodeType == TreeNodeType.FONT) { - ret.add(d); - } - if (nodeType == TreeNodeType.OTHER_TAG) { - if (d instanceof SymbolClassTypeTag) { - ret.add(d); - } - } - } - - if (d instanceof Frame) { - ret.add(d); - } - if (d instanceof ScriptPack) { - ret.add(d); - } - } - return ret; - } - - public List getTagsWithType(List list, TreeNodeType type) { - List ret = new ArrayList<>(); - for (AS3ClassTreeItem item : list) { - TreeNodeType ttype = getTreeNodeType(item); - if (type == ttype) { - ret.add(item); - } - } - return ret; - } - - public TreeItem getCurrentTreeItem() { - if (!mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { - return mainPanel.folderPreviewPanel.selectedItems.entrySet().iterator().next().getValue(); - } - - TreeItem item = (TreeItem) getLastSelectedPathComponent(); - return item; - } - - public void updateSwfs(SWF[] swfs) { - TagTreeModel ttm = getModel(); - if (ttm != null) { - List> expandedNodes = View.getExpandedNodes(this); - ttm.updateSwf(null); // todo: honfika: update only the changed swfs, but there was an exception when i tried it - View.expandTreeNodes(this, expandedNodes); - } - } - - @Override - public TagTreeModel getModel() { - return (TagTreeModel) super.getModel(); - } - - public void expandRoot() { - TagTreeModel ttm = getModel(); - TreeItem root = ttm.getRoot(); - expandPath(new TreePath(new Object[]{root})); - } - - public void expandFirstLevelNodes() { - TagTreeModel ttm = getModel(); - TreeItem root = ttm.getRoot(); - int childCount = ttm.getChildCount(root); - for (int i = 0; i < childCount; i++) { - expandPath(new TreePath(new Object[]{root, ttm.getChild(root, i)})); - } - } - - public String getSelectionPathString() { - StringBuilder sb = new StringBuilder(); - TreePath path = getSelectionPath(); - if (path != null) { - boolean first = true; - for (Object p : path.getPath()) { - if (!first) { - sb.append("|"); - } - - first = false; - sb.append(p.toString()); - } - } - - return sb.toString(); - } - - public void setSelectionPathString(String pathStr) { - if (pathStr != null && pathStr.length() > 0) { - String[] path = pathStr.split("\\|"); - - TreePath tp = View.getTreePathByPathStrings(this, Arrays.asList(path)); - if (tp != null) { - // the current view is the Resources view, otherwise tp is null - mainPanel.setTagTreeSelectedNode((TreeItem) tp.getLastPathComponent()); - } - } - } -} +/* + * Copyright (C) 2010-2016 JPEXS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.jpexs.decompiler.flash.gui.tagtree; + +import com.jpexs.decompiler.flash.SWC; +import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.ZippedSWFBundle; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.gui.MainPanel; +import com.jpexs.decompiler.flash.gui.TreeNodeType; +import com.jpexs.decompiler.flash.gui.View; +import com.jpexs.decompiler.flash.iggy.conversion.IggySwfBundle; +import com.jpexs.decompiler.flash.tags.CSMTextSettingsTag; +import com.jpexs.decompiler.flash.tags.DebugIDTag; +import com.jpexs.decompiler.flash.tags.DefineBinaryDataTag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag; +import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag; +import com.jpexs.decompiler.flash.tags.DefineBitsTag; +import com.jpexs.decompiler.flash.tags.DefineButton2Tag; +import com.jpexs.decompiler.flash.tags.DefineButtonCxformTag; +import com.jpexs.decompiler.flash.tags.DefineButtonSoundTag; +import com.jpexs.decompiler.flash.tags.DefineButtonTag; +import com.jpexs.decompiler.flash.tags.DefineEditTextTag; +import com.jpexs.decompiler.flash.tags.DefineFont2Tag; +import com.jpexs.decompiler.flash.tags.DefineFont3Tag; +import com.jpexs.decompiler.flash.tags.DefineFont4Tag; +import com.jpexs.decompiler.flash.tags.DefineFontAlignZonesTag; +import com.jpexs.decompiler.flash.tags.DefineFontInfo2Tag; +import com.jpexs.decompiler.flash.tags.DefineFontInfoTag; +import com.jpexs.decompiler.flash.tags.DefineFontNameTag; +import com.jpexs.decompiler.flash.tags.DefineFontTag; +import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag; +import com.jpexs.decompiler.flash.tags.DefineScalingGridTag; +import com.jpexs.decompiler.flash.tags.DefineSceneAndFrameLabelDataTag; +import com.jpexs.decompiler.flash.tags.DefineShape2Tag; +import com.jpexs.decompiler.flash.tags.DefineShape3Tag; +import com.jpexs.decompiler.flash.tags.DefineShape4Tag; +import com.jpexs.decompiler.flash.tags.DefineShapeTag; +import com.jpexs.decompiler.flash.tags.DefineSoundTag; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.DefineText2Tag; +import com.jpexs.decompiler.flash.tags.DefineTextTag; +import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; +import com.jpexs.decompiler.flash.tags.DoABC2Tag; +import com.jpexs.decompiler.flash.tags.DoABCTag; +import com.jpexs.decompiler.flash.tags.DoActionTag; +import com.jpexs.decompiler.flash.tags.DoInitActionTag; +import com.jpexs.decompiler.flash.tags.EnableDebugger2Tag; +import com.jpexs.decompiler.flash.tags.EnableDebuggerTag; +import com.jpexs.decompiler.flash.tags.EnableTelemetryTag; +import com.jpexs.decompiler.flash.tags.ExportAssetsTag; +import com.jpexs.decompiler.flash.tags.FileAttributesTag; +import com.jpexs.decompiler.flash.tags.FrameLabelTag; +import com.jpexs.decompiler.flash.tags.ImportAssets2Tag; +import com.jpexs.decompiler.flash.tags.ImportAssetsTag; +import com.jpexs.decompiler.flash.tags.JPEGTablesTag; +import com.jpexs.decompiler.flash.tags.MetadataTag; +import com.jpexs.decompiler.flash.tags.PlaceObject2Tag; +import com.jpexs.decompiler.flash.tags.PlaceObject3Tag; +import com.jpexs.decompiler.flash.tags.PlaceObject4Tag; +import com.jpexs.decompiler.flash.tags.PlaceObjectTag; +import com.jpexs.decompiler.flash.tags.ProductInfoTag; +import com.jpexs.decompiler.flash.tags.ProtectTag; +import com.jpexs.decompiler.flash.tags.RemoveObject2Tag; +import com.jpexs.decompiler.flash.tags.RemoveObjectTag; +import com.jpexs.decompiler.flash.tags.ScriptLimitsTag; +import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag; +import com.jpexs.decompiler.flash.tags.SetTabIndexTag; +import com.jpexs.decompiler.flash.tags.ShowFrameTag; +import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag; +import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag; +import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag; +import com.jpexs.decompiler.flash.tags.StartSound2Tag; +import com.jpexs.decompiler.flash.tags.StartSoundTag; +import com.jpexs.decompiler.flash.tags.SymbolClassTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.VideoFrameTag; +import com.jpexs.decompiler.flash.tags.base.ASMSource; +import com.jpexs.decompiler.flash.tags.base.ButtonTag; +import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.tags.base.ImageTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; +import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; +import com.jpexs.decompiler.flash.tags.base.RemoveTag; +import com.jpexs.decompiler.flash.tags.base.ShapeTag; +import com.jpexs.decompiler.flash.tags.base.SymbolClassTypeTag; +import com.jpexs.decompiler.flash.tags.base.TextTag; +import com.jpexs.decompiler.flash.tags.gfx.DefineCompactedFont; +import com.jpexs.decompiler.flash.timeline.AS2Package; +import com.jpexs.decompiler.flash.timeline.AS3Package; +import com.jpexs.decompiler.flash.timeline.Frame; +import com.jpexs.decompiler.flash.timeline.FrameScript; +import com.jpexs.decompiler.flash.timeline.TagScript; +import com.jpexs.decompiler.flash.treeitems.AS3ClassTreeItem; +import com.jpexs.decompiler.flash.treeitems.FolderItem; +import com.jpexs.decompiler.flash.treeitems.HeaderItem; +import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.treeitems.TreeItem; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.Icon; +import javax.swing.JTree; +import javax.swing.plaf.basic.BasicLabelUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +/** + * + * @author JPEXS + */ +public class TagTree extends JTree { + + public TagTreeContextMenu contextPopupMenu; + + private final MainPanel mainPanel; + + private static final Map ICONS; + + static { + ICONS = new HashMap<>(); + for (TreeNodeType treeNodeType : TreeNodeType.values()) { + if (treeNodeType != TreeNodeType.UNKNOWN && treeNodeType != TreeNodeType.SHOW_FRAME) { + String tagTypeStr = treeNodeType.toString().toLowerCase().replace("_", ""); + ICONS.put(treeNodeType, View.getIcon(tagTypeStr + "16")); + } + } + } + + public static Icon getIconForType(TreeNodeType t) { + return ICONS.get(t); + } + + public static class TagTreeCellRenderer extends DefaultTreeCellRenderer { + + private Font plainFont; + + private Font boldFont; + + public TagTreeCellRenderer() { + setUI(new BasicLabelUI()); + setOpaque(false); + //setBackground(Color.green); + setBackgroundNonSelectionColor(Color.white); + //setBackgroundSelectionColor(Color.ORANGE); + + } + + @Override + public Component getTreeCellRendererComponent( + JTree tree, + Object value, + boolean sel, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { + + TreeItem val = (TreeItem) value; + if (val != null && !(val instanceof SWFList) && val.getSwf() == null) { + // SWF was closed + value = null; + } + + super.getTreeCellRendererComponent( + tree, value, sel, + expanded, leaf, row, + hasFocus); + + if (val == null) { + return this; + } + + TreeNodeType type = getTreeNodeType(val); + + if (type == TreeNodeType.FOLDER && expanded) { + type = TreeNodeType.FOLDER_OPEN; + } + + if ((type == TreeNodeType.FOLDER || type == TreeNodeType.FOLDER_OPEN) && val instanceof FolderItem) { + FolderItem si = (FolderItem) val; + if (!TagTreeRoot.FOLDER_ROOT.equals(si.getName())) { + String itemName = "folder" + si.getName(); + setIcon(View.getIcon(itemName.toLowerCase() + "16")); + } + } else { + setIcon(ICONS.get(type)); + } + + /* boolean isModified = val instanceof Tag && ((Tag) val).isModified(); + if(val instanceof ScriptPack){ + ScriptPack sp=(ScriptPack)val; + if(sp.abc.script_info.get(sp.scriptIndex).isModified()){ + isModified = true; + } + }*/ + boolean isReadOnly = false; + if (val instanceof Tag) { + isReadOnly = ((Tag) val).isReadOnly(); + } + + boolean isModified = val.isModified(); + if (isModified) { + if (boldFont == null) { + Font font = getFont(); + boldFont = font.deriveFont(Font.BOLD); + } + } else if (plainFont == null) { + Font font = getFont(); + plainFont = font.deriveFont(Font.PLAIN); + } + setFont(isModified ? boldFont : plainFont); + if (isReadOnly) { + setForeground(new Color(0xcc, 0xcc, 0xcc)); + } else { + setForeground(Color.BLACK); + } + + return this; + } + } + + public TagTree(TagTreeModel treeModel, MainPanel mainPanel) { + super(treeModel); + this.mainPanel = mainPanel; + setCellRenderer(new TagTreeCellRenderer()); + setRootVisible(false); + setBackground(Color.white); + setRowHeight(Math.max(getFont().getSize() + 5, 16)); + setLargeModel(true); + setUI(new BasicTreeUI() { + { + setHashColor(Color.gray); + } + }); + } + + public void createContextMenu() { + contextPopupMenu = new TagTreeContextMenu(this, mainPanel); + } + + public static TreeNodeType getTreeNodeType(TreeItem t) { + + if (t instanceof TagScript) { + t = ((TagScript) t).getTag(); + } + + if (t instanceof HeaderItem) { + return TreeNodeType.HEADER; + } + + if ((t instanceof DefineFontTag) + || (t instanceof DefineFont2Tag) + || (t instanceof DefineFont3Tag) + || (t instanceof DefineFont4Tag) + || (t instanceof DefineCompactedFont)) { + return TreeNodeType.FONT; + } + + // DefineText, DefineText2, DefineEditTextTag + if (t instanceof TextTag) { + return TreeNodeType.TEXT; + } + + // DefineBits, DefineBitsJPEG2, DefineBitsJPEG3, DefineBitsJPEG4, DefineBitsLossless, DefineBitsLossless2 + if (t instanceof ImageTag) { + return TreeNodeType.IMAGE; + } + + // DefineShape, DefineShape2, DefineShape3, DefineShape4 + if (t instanceof ShapeTag) { + return TreeNodeType.SHAPE; + } + + // DefineMorphShape, DefineMorphShape2 + if (t instanceof MorphShapeTag) { + return TreeNodeType.MORPH_SHAPE; + } + + if (t instanceof DefineSpriteTag) { + return TreeNodeType.SPRITE; + } + + // DefineButton, DefineButton2 + if (t instanceof ButtonTag) { + return TreeNodeType.BUTTON; + } + + if (t instanceof DefineVideoStreamTag) { + return TreeNodeType.MOVIE; + } + + if ((t instanceof DefineSoundTag) || (t instanceof SoundStreamHeadTag) || (t instanceof SoundStreamHead2Tag)) { + return TreeNodeType.SOUND; + } + + if (t instanceof DefineBinaryDataTag) { + return TreeNodeType.BINARY_DATA; + } + + if (t instanceof ASMSource) { + return TreeNodeType.AS; + } + + if (t instanceof ScriptPack) { + return TreeNodeType.AS; + } + + if (t instanceof AS2Package) { + return TreeNodeType.PACKAGE; + } + + if (t instanceof AS3Package) { + return TreeNodeType.PACKAGE; + } + + if ((t instanceof Frame) + || (t instanceof FrameScript)) { + return TreeNodeType.FRAME; + } + + if (t instanceof ShowFrameTag) { + return TreeNodeType.SHOW_FRAME; + } + + if (t instanceof SWF) { + return TreeNodeType.FLASH; + } + + if (t instanceof SWFList) { + SWFList slist = (SWFList) t; + if (slist.isBundle()) { + if (slist.bundle.getClass() == ZippedSWFBundle.class) { + return TreeNodeType.BUNDLE_ZIP; + } else if (slist.bundle.getClass() == SWC.class) { + return TreeNodeType.BUNDLE_SWC; + } else if (slist.bundle.getClass() == IggySwfBundle.class) { + return TreeNodeType.BUNDLE_IGGY; + } else { + return TreeNodeType.BUNDLE_BINARY; + } + } + } + + if (t instanceof SetBackgroundColorTag) { + return TreeNodeType.SET_BACKGROUNDCOLOR; + } + if (t instanceof FileAttributesTag) { + return TreeNodeType.FILE_ATTRIBUTES; + } + if (t instanceof MetadataTag) { + return TreeNodeType.METADATA; + } + if (t instanceof PlaceObjectTypeTag) { + return TreeNodeType.PLACE_OBJECT; + } + if (t instanceof RemoveTag) { + return TreeNodeType.REMOVE_OBJECT; + } + if (t instanceof Tag) { + return TreeNodeType.OTHER_TAG; + } + + if (t instanceof FolderItem) { + return TreeNodeType.FOLDER; + } + + return TreeNodeType.FOLDER; + } + + public List getSwfFolderItemNestedTagIds(String folderName, boolean gfx) { + List ret = null; + switch (folderName) { + case TagTreeModel.FOLDER_SHAPES: + ret = Arrays.asList(DefineShapeTag.ID, DefineShape2Tag.ID, DefineShape3Tag.ID, DefineShape4Tag.ID); + break; + case TagTreeModel.FOLDER_MORPHSHAPES: + ret = Arrays.asList(DefineMorphShapeTag.ID, DefineMorphShape2Tag.ID); + break; + case TagTreeModel.FOLDER_SPRITES: + ret = Arrays.asList(DefineSpriteTag.ID); + break; + case TagTreeModel.FOLDER_TEXTS: + ret = Arrays.asList(DefineTextTag.ID, DefineText2Tag.ID, DefineEditTextTag.ID); + break; + case TagTreeModel.FOLDER_IMAGES: + ret = Arrays.asList(DefineBitsTag.ID, DefineBitsJPEG2Tag.ID, DefineBitsJPEG3Tag.ID, DefineBitsJPEG4Tag.ID, DefineBitsLosslessTag.ID, DefineBitsLossless2Tag.ID); + break; + case TagTreeModel.FOLDER_MOVIES: + ret = Arrays.asList(DefineVideoStreamTag.ID); + break; + case TagTreeModel.FOLDER_SOUNDS: + ret = Arrays.asList(DefineSoundTag.ID); + break; + case TagTreeModel.FOLDER_BUTTONS: + ret = Arrays.asList(DefineButtonTag.ID, DefineButton2Tag.ID); + break; + case TagTreeModel.FOLDER_FONTS: + if (gfx) { + ret = Arrays.asList(DefineFontTag.ID, DefineFont2Tag.ID, DefineFont3Tag.ID, DefineFont4Tag.ID, DefineCompactedFont.ID); + } else { + ret = Arrays.asList(DefineFontTag.ID, DefineFont2Tag.ID, DefineFont3Tag.ID, DefineFont4Tag.ID); + } + break; + case TagTreeModel.FOLDER_BINARY_DATA: + ret = Arrays.asList(DefineBinaryDataTag.ID); + break; + case TagTreeModel.FOLDER_FRAMES: + // same as nested tags of DefineSpriteTag? + ret = Arrays.asList(PlaceObjectTag.ID, PlaceObject2Tag.ID, PlaceObject3Tag.ID, PlaceObject4Tag.ID, + RemoveObjectTag.ID, RemoveObject2Tag.ID, ShowFrameTag.ID, FrameLabelTag.ID, + StartSoundTag.ID, StartSound2Tag.ID, VideoFrameTag.ID, + SoundStreamBlockTag.ID, SoundStreamHeadTag.ID, SoundStreamHead2Tag.ID, + DefineScalingGridTag.ID); + break; + case TagTreeModel.FOLDER_OTHERS: + ret = Arrays.asList( + //CSMTextSettingsTag.ID, + DebugIDTag.ID, + //DefineButtonCxformTag.ID, DefineButtonSoundTag.ID, + //DefineFontAlignZonesTag.ID, DefineFontInfoTag.ID, DefineFontInfo2Tag.ID, DefineFontNameTag.ID, + /*DefineScalingGridTag.ID,*/ DefineSceneAndFrameLabelDataTag.ID, + DoABC2Tag.ID, DoABCTag.ID, DoActionTag.ID, DoInitActionTag.ID, + EnableDebuggerTag.ID, EnableDebugger2Tag.ID, EnableTelemetryTag.ID, + ExportAssetsTag.ID, FileAttributesTag.ID, ImportAssetsTag.ID, ImportAssets2Tag.ID, + JPEGTablesTag.ID, MetadataTag.ID, ProductInfoTag.ID, ProtectTag.ID, ScriptLimitsTag.ID, + SetBackgroundColorTag.ID, SetTabIndexTag.ID, SymbolClassTag.ID); + break; + } + + return ret; + } + + public List getFrameNestedTagIds() { + return Arrays.asList(PlaceObjectTag.ID, PlaceObject2Tag.ID, PlaceObject3Tag.ID, PlaceObject4Tag.ID, + RemoveObjectTag.ID, RemoveObject2Tag.ID, FrameLabelTag.ID, + StartSoundTag.ID, StartSound2Tag.ID, VideoFrameTag.ID, + SoundStreamBlockTag.ID, SoundStreamHeadTag.ID, SoundStreamHead2Tag.ID); + } + + public List getNestedTagIds(Tag obj) { + if (obj instanceof DefineSpriteTag) { + return Arrays.asList(PlaceObjectTag.ID, PlaceObject2Tag.ID, PlaceObject3Tag.ID, PlaceObject4Tag.ID, + RemoveObjectTag.ID, RemoveObject2Tag.ID, ShowFrameTag.ID, FrameLabelTag.ID, + StartSoundTag.ID, StartSound2Tag.ID, VideoFrameTag.ID, + SoundStreamBlockTag.ID, SoundStreamHeadTag.ID, SoundStreamHead2Tag.ID, + DefineScalingGridTag.ID); + } + if (obj instanceof FontTag) { + return Arrays.asList(DefineFontNameTag.ID, DefineFontAlignZonesTag.ID, DefineFontInfoTag.ID, DefineFontInfo2Tag.ID); + } + if (obj instanceof TextTag) { + return Arrays.asList(CSMTextSettingsTag.ID); + } + if (obj instanceof DefineButtonTag) { + return Arrays.asList(DefineButtonCxformTag.ID, DefineButtonSoundTag.ID, DefineScalingGridTag.ID); + } + if (obj instanceof DefineButton2Tag) { + return Arrays.asList(DefineButtonSoundTag.ID, DefineScalingGridTag.ID); + } + return null; + } + + public boolean hasExportableNodes() { + return !getSelection(mainPanel.getCurrentSwf()).isEmpty(); + } + + public void getAllSubs(TreeItem o, List ret) { + TagTreeModel tm = getModel(); + for (TreeItem c : tm.getAllChildren(o)) { + ret.add(c); + getAllSubs(c, ret); + } + } + + public List getAllSelected() { + TreeSelectionModel tsm = getSelectionModel(); + TreePath[] tps = tsm.getSelectionPaths(); + List ret = new ArrayList<>(); + if (tps == null) { + return ret; + } + + for (TreePath tp : tps) { + TreeItem treeNode = (TreeItem) tp.getLastPathComponent(); + ret.add(treeNode); + getAllSubs(treeNode, ret); + } + return ret; + } + + public List getSelected() { + if (!mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { + return new ArrayList<>(mainPanel.folderPreviewPanel.selectedItems.values()); + } + TreeSelectionModel tsm = getSelectionModel(); + TreePath[] tps = tsm.getSelectionPaths(); + List ret = new ArrayList<>(); + if (tps == null) { + return ret; + } + + for (TreePath tp : tps) { + TreeItem treeNode = (TreeItem) tp.getLastPathComponent(); + ret.add(treeNode); + } + return ret; + } + + public List getSelection(SWF swf) { + List sel; + if (mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { + sel = getAllSelected(); + } else { + sel = new ArrayList<>(mainPanel.folderPreviewPanel.selectedItems.values()); + } + return getSelection(swf, sel); + } + + public List getSelection(SWF swf, List sel) { + List ret = new ArrayList<>(); + for (TreeItem d : sel) { + if (d instanceof SWFList) { + continue; + } + if (d.getSwf() != swf) { + continue; + } + + if (d instanceof TagScript) { + Tag tag = ((TagScript) d).getTag(); + if (tag instanceof DoActionTag || tag instanceof DoInitActionTag) { + d = tag; + } + } + + if (d instanceof Tag || d instanceof ASMSource) { + TreeNodeType nodeType = TagTree.getTreeNodeType(d); + if (nodeType == TreeNodeType.IMAGE) { + ret.add(d); + } + if (nodeType == TreeNodeType.SHAPE) { + ret.add(d); + } + if (nodeType == TreeNodeType.MORPH_SHAPE) { + ret.add(d); + } + if (nodeType == TreeNodeType.BUTTON) { + ret.add(d); + } + if (nodeType == TreeNodeType.AS) { + ret.add(d); + } + if (nodeType == TreeNodeType.MOVIE) { + ret.add(d); + } + if (nodeType == TreeNodeType.SOUND) { + ret.add(d); + } + if (nodeType == TreeNodeType.BINARY_DATA) { + ret.add(d); + } + if (nodeType == TreeNodeType.TEXT) { + ret.add(d); + } + if (nodeType == TreeNodeType.FONT) { + ret.add(d); + } + if (nodeType == TreeNodeType.OTHER_TAG) { + if (d instanceof SymbolClassTypeTag) { + ret.add(d); + } + } + } + + if (d instanceof Frame) { + ret.add(d); + } + if (d instanceof ScriptPack) { + ret.add(d); + } + } + return ret; + } + + public List getTagsWithType(List list, TreeNodeType type) { + List ret = new ArrayList<>(); + for (AS3ClassTreeItem item : list) { + TreeNodeType ttype = getTreeNodeType(item); + if (type == ttype) { + ret.add(item); + } + } + return ret; + } + + public TreeItem getCurrentTreeItem() { + if (!mainPanel.folderPreviewPanel.selectedItems.isEmpty()) { + return mainPanel.folderPreviewPanel.selectedItems.entrySet().iterator().next().getValue(); + } + + TreeItem item = (TreeItem) getLastSelectedPathComponent(); + return item; + } + + public void updateSwfs(SWF[] swfs) { + TagTreeModel ttm = getModel(); + if (ttm != null) { + List> expandedNodes = View.getExpandedNodes(this); + ttm.updateSwf(null); // todo: honfika: update only the changed swfs, but there was an exception when i tried it + View.expandTreeNodes(this, expandedNodes); + } + } + + @Override + public TagTreeModel getModel() { + return (TagTreeModel) super.getModel(); + } + + public void expandRoot() { + TagTreeModel ttm = getModel(); + TreeItem root = ttm.getRoot(); + expandPath(new TreePath(new Object[]{root})); + } + + public void expandFirstLevelNodes() { + TagTreeModel ttm = getModel(); + TreeItem root = ttm.getRoot(); + int childCount = ttm.getChildCount(root); + for (int i = 0; i < childCount; i++) { + expandPath(new TreePath(new Object[]{root, ttm.getChild(root, i)})); + } + } + + public String getSelectionPathString() { + StringBuilder sb = new StringBuilder(); + TreePath path = getSelectionPath(); + if (path != null) { + boolean first = true; + for (Object p : path.getPath()) { + if (!first) { + sb.append("|"); + } + + first = false; + sb.append(p.toString()); + } + } + + return sb.toString(); + } + + public void setSelectionPathString(String pathStr) { + if (pathStr != null && pathStr.length() > 0) { + String[] path = pathStr.split("\\|"); + + TreePath tp = View.getTreePathByPathStrings(this, Arrays.asList(path)); + if (tp != null) { + // the current view is the Resources view, otherwise tp is null + mainPanel.setTagTreeSelectedNode((TreeItem) tp.getLastPathComponent()); + } + } + } +}