├── cli ├── .gitignore ├── .eclipse-pmd ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── dexesttp │ │ │ └── hkxpack │ │ │ └── cli │ │ │ ├── commands │ │ │ ├── Command.java │ │ │ ├── CommandFactory.java │ │ │ ├── Command_quick.java │ │ │ ├── Command_unpack.java │ │ │ ├── Command_help.java │ │ │ └── Command_pack.java │ │ │ ├── utils │ │ │ ├── WrongSizeException.java │ │ │ ├── FileNameCreationException.java │ │ │ └── StaticProperties.java │ │ │ ├── ConsoleView.java │ │ │ └── loggers │ │ │ └── DirectoryWalkerLoggerFactory.java │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── dexesttp │ │ │ └── hkxpack │ │ │ └── cli │ │ │ └── utils │ │ │ ├── ArgsParserTest.java │ │ │ ├── ArgsParserEmptyTest.java │ │ │ ├── ArgsParserExceptionTest.java │ │ │ └── ArgsParserExistTest.java │ └── debug │ │ └── java │ │ └── com │ │ └── dexesttp │ │ └── hkxpack │ │ └── cli │ │ ├── TestView.java │ │ └── components │ │ ├── Read.java │ │ ├── Write.java │ │ ├── XMLTest.java │ │ └── HKXTest.java └── pom.xml ├── doc ├── uml │ ├── FileReader.png │ └── FileReader.uml └── hkx findings │ ├── 01 - New data after first tests.txt │ ├── Notes on behavior files.txt │ ├── Applying FNIS findings to FO4.txt │ ├── studying __data2__ of Bloatfly.txt │ ├── Studying __data1__ of BloatflyRootBehavior.txt │ ├── Studying animation- DeathChest01 .txt │ ├── studying __data3__ of Bloatfly.txt │ └── 00 - Findings on HKX files.txt ├── core ├── .gitignore ├── src │ ├── test │ │ ├── resources │ │ │ ├── test-base.hkx │ │ │ └── test-base.xml │ │ └── java │ │ │ └── com │ │ │ └── dexesttp │ │ │ └── hkxpack │ │ │ ├── hkxreader │ │ │ ├── HKXReaderTest.java │ │ │ ├── HKXTestBase.java │ │ │ └── HKXReaderExternalResource.java │ │ │ ├── tagreader │ │ │ ├── TagXMLReaderTest.java │ │ │ ├── TagXMLTestBase.java │ │ │ ├── TagXMLReaderExceptionTest.java │ │ │ └── TagXMLReaderExternalResource.java │ │ │ ├── files │ │ │ ├── ReaderExternalResource.java │ │ │ └── TestBase.java │ │ │ ├── resources │ │ │ └── byteutils │ │ │ │ ├── ULongByteUtilsTest.java │ │ │ │ └── SLongByteUtilsTest.java │ │ │ └── tagwriter │ │ │ └── TagXMLWriterTest.java │ └── main │ │ ├── java │ │ └── com │ │ │ └── dexesttp │ │ │ └── hkxpack │ │ │ ├── descriptor │ │ │ ├── enums │ │ │ │ ├── HKXTypeFamily.java │ │ │ │ └── Flag.java │ │ │ ├── exceptions │ │ │ │ ├── ClassFileReadException.java │ │ │ │ └── ClassListReadException.java │ │ │ ├── reader │ │ │ │ ├── ClassXMLReaderFactory.java │ │ │ │ └── ClassXMLList.java │ │ │ ├── HKXDescriptor.java │ │ │ ├── HKXDescriptorFactory.java │ │ │ └── HKXEnumResolver.java │ │ │ ├── data │ │ │ ├── HKXData.java │ │ │ ├── members │ │ │ │ ├── HKXMember.java │ │ │ │ ├── HKXFailedMember.java │ │ │ │ ├── HKXStringMember.java │ │ │ │ ├── HKXDirectMember.java │ │ │ │ └── HKXPointerMember.java │ │ │ └── HKXObject.java │ │ │ ├── hkxwriter │ │ │ ├── utils │ │ │ │ ├── PointerObject.java │ │ │ │ └── PointerResolver.java │ │ │ ├── object │ │ │ │ ├── callbacks │ │ │ │ │ ├── HKXArrayMemberCallback.java │ │ │ │ │ ├── HKXMemberCallback.java │ │ │ │ │ ├── HKXBaseArrayMemberCallback.java │ │ │ │ │ ├── HKXPointerArrayMemberCallback.java │ │ │ │ │ ├── HKXRelArrayMemberCallback.java │ │ │ │ │ └── HKXDefaultArrayMemberCallback.java │ │ │ │ ├── HKXMemberHandler.java │ │ │ │ ├── HKXDirectMemberHandler.java │ │ │ │ ├── HKXPointerMemberHandler.java │ │ │ │ ├── array │ │ │ │ │ └── HKXArrayPointerMemberHandler.java │ │ │ │ ├── HKXObjectMemberHandler.java │ │ │ │ ├── HKXEnumMemberHandler.java │ │ │ │ ├── HKXStringMemberHandler.java │ │ │ │ └── HKXInternalObjectHandler.java │ │ │ ├── exceptions │ │ │ │ └── WrongInputCastException.java │ │ │ ├── header │ │ │ │ └── HKXHeaderFactory.java │ │ │ └── classnames │ │ │ │ └── HKXClassnamesHandler.java │ │ │ ├── hkx │ │ │ ├── data │ │ │ │ ├── DataInternal.java │ │ │ │ ├── DataExternal.java │ │ │ │ └── DataInterface.java │ │ │ ├── exceptions │ │ │ │ ├── UnsupportedVersionError.java │ │ │ │ └── InvalidPositionException.java │ │ │ ├── header │ │ │ │ ├── HeaderData.java │ │ │ │ ├── internals │ │ │ │ │ ├── versions │ │ │ │ │ │ └── HeaderDescriptor_v11.java │ │ │ │ │ ├── SectionDescriptor.java │ │ │ │ │ └── HeaderDescriptor.java │ │ │ │ └── SectionData.java │ │ │ ├── classnames │ │ │ │ ├── Classname.java │ │ │ │ └── ClassnamesData.java │ │ │ ├── types │ │ │ │ ├── handlers │ │ │ │ │ ├── MemberHandler.java │ │ │ │ │ ├── MemberHandlerFactory.java │ │ │ │ │ ├── Vector4Handler.java │ │ │ │ │ ├── BigMemberHandlers.java │ │ │ │ │ ├── MediumMemberHandlers.java │ │ │ │ │ └── NormalMemberHandlers.java │ │ │ │ ├── object │ │ │ │ │ ├── PrimitiveSnap.java │ │ │ │ │ └── ObjectSnap.java │ │ │ │ ├── ObjectSizeResolver.java │ │ │ │ ├── MemberDataResolver.java │ │ │ │ └── MemberSizeResolver.java │ │ │ └── HKXUtils.java │ │ │ ├── tagreader │ │ │ ├── exceptions │ │ │ │ └── InvalidTagXMLException.java │ │ │ ├── serialized │ │ │ │ ├── TagXMLSerializedHandler.java │ │ │ │ ├── TagXMLComplexSerializedHandler.java │ │ │ │ ├── TagXMLEmbeddedObjectSerializedHandler.java │ │ │ │ └── TagXMLDirectSerializedHandler.java │ │ │ ├── members │ │ │ │ ├── TagXMLContentsHandler.java │ │ │ │ └── TagXMLEmbeddedObjectHandler.java │ │ │ └── TagXMLNodeHandler.java │ │ │ ├── resources │ │ │ ├── byteutils │ │ │ │ ├── package-info.java │ │ │ │ ├── StringByteUtils.java │ │ │ │ ├── FloatByteUtils.java │ │ │ │ ├── SLongByteUtils.java │ │ │ │ └── ULongByteUtils.java │ │ │ ├── DisplayProperties.java │ │ │ └── LoggerUtil.java │ │ │ ├── hkxreader │ │ │ ├── member │ │ │ │ ├── HKXMemberReader.java │ │ │ │ ├── arrays │ │ │ │ │ ├── HKXArrayContentsReader.java │ │ │ │ │ ├── HKXDirectArrayContentsReader.java │ │ │ │ │ ├── HKXObjectArrayContentsReader.java │ │ │ │ │ ├── HKXPointerArrayContentsReader.java │ │ │ │ │ └── HKXStringArrayContentsReader.java │ │ │ │ ├── HKXObjectMemberReader.java │ │ │ │ ├── HKXDirectMemberReader.java │ │ │ │ ├── HKXPointerMemberReader.java │ │ │ │ ├── HKXStringMemberReader.java │ │ │ │ ├── HKXRelArrayMemberReader.java │ │ │ │ └── HKXEnumMemberReader.java │ │ │ ├── HKXDescriptorReader.java │ │ │ ├── PointerNameGenerator.java │ │ │ └── HKXReaderConnector.java │ │ │ ├── l10n │ │ │ └── SBundle.java │ │ │ └── tagwriter │ │ │ ├── TagXMLDataCreator.java │ │ │ ├── TagXMLWriter.java │ │ │ └── TagXMLDirectMemberHandler.java │ │ └── resources │ │ └── l10n │ │ └── locale_en.properties ├── .eclipse-pmd └── pom.xml ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .eclipse-pmd ├── LICENSE ├── pom.xml └── .gitignore /cli/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /doc/uml/FileReader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dexesttp/hkxpack/HEAD/doc/uml/FileReader.png -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /src/main/resources/classxml/ 3 | /src/main/resources/properties/classxmllist.txt 4 | -------------------------------------------------------------------------------- /core/src/test/resources/test-base.hkx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dexesttp/hkxpack/HEAD/core/src/test/resources/test-base.hkx -------------------------------------------------------------------------------- /core/src/test/resources/test-base.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/enums/HKXTypeFamily.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor.enums; 2 | 3 | /** 4 | * Classify the HKX types into "families" These families usually have an impact 5 | * on data reading and writing. 6 | */ 7 | public enum HKXTypeFamily { 8 | UNKNOWN, DIRECT, COMPLEX, ENUM, ARRAY, POINTER, STRING, OBJECT 9 | } 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | ## Scope 4 | 5 | 6 | 7 | ## Additions 8 | 9 | - 10 | 11 | 12 | 13 | - 14 | 15 | ## Fixes 16 | 17 | - 18 | 19 | 20 | 21 | - 22 | 23 | ## Notes 24 | 25 | - 26 | - 27 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/data/HKXData.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.data; 2 | 3 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 4 | 5 | /** 6 | * General purpose data storage for a HKX file DOM equivalent. 7 | */ 8 | public interface HKXData { 9 | /** 10 | * Get this object's type. 11 | * 12 | * @return this object's {@link HKXType}. 13 | */ 14 | HKXType getType(); 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/utils/PointerObject.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.utils; 2 | 3 | /** 4 | * Handles pointing from a file location to an object name 5 | */ 6 | public class PointerObject { 7 | /** 8 | * The location the object is pointed from 9 | */ 10 | public long from; 11 | /** 12 | * The name of the target object. 13 | */ 14 | public String to; 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/data/DataInternal.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.data; 2 | 3 | /** 4 | * A data position descriptor aimed at the current section. 5 | */ 6 | public class DataInternal { 7 | /** 8 | * The data parent, in the current section. 9 | */ 10 | public long from; 11 | 12 | /** 13 | * The data position, in the current section. 14 | */ 15 | public long to; 16 | } 17 | -------------------------------------------------------------------------------- /.eclipse-pmd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /cli/.eclipse-pmd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /doc/hkx findings/01 - New data after first tests.txt: -------------------------------------------------------------------------------- 1 | Apaprently, the data is organized differently from what I thought : 2 | 3 | - Members have a flage value, that can be either NONE or SERIALIZED 4 | - Serialized memebrs must not be included in data mining, but be included as comment in the output XML. 5 | - Data3 seems to contain the actual class architecture, while data2 and data1 contains respectvely enums (?) and actual data. 6 | -------------------------------------------------------------------------------- /core/.eclipse-pmd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/hkxreader/HKXReaderTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | import org.junit.runners.Suite.SuiteClasses; 6 | 7 | @RunWith(Suite.class) 8 | @SuiteClasses({ HKXReaderExceptionTest.class, HKXTestBase.class }) 9 | /** 10 | * Launches the relevant test suite for the {@link HKXReader} class 11 | */ 12 | public class HKXReaderTest { 13 | // NO CONTENT 14 | } 15 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/tagreader/TagXMLReaderTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | import org.junit.runners.Suite.SuiteClasses; 6 | 7 | @RunWith(Suite.class) 8 | @SuiteClasses({ TagXMLReaderExceptionTest.class, TagXMLTestBase.class }) 9 | /** 10 | * Launches the test suite for the {@link TagXMLReader} 11 | */ 12 | public class TagXMLReaderTest { 13 | // NO CONTENT 14 | } 15 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/commands/Command.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.commands; 2 | 3 | /** 4 | * A command is a routed behavior of the Command Line Interface. It should be 5 | * returned only by a {@link CommandFactory}. 6 | */ 7 | public interface Command { 8 | /** 9 | * Executes the command. 10 | * 11 | * @param parameters all the command-line arguments 12 | * @return the execution result value 13 | */ 14 | int execute(String... parameters); 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/data/DataExternal.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.data; 2 | 3 | /** 4 | * A data position descriptor aimed at a given section. 5 | */ 6 | public class DataExternal { 7 | /** 8 | * The data parent, in the current section. 9 | */ 10 | public long from; 11 | 12 | /** 13 | * The section the data is in. 14 | */ 15 | public int section; 16 | 17 | /** 18 | * The data position, in the given section. 19 | */ 20 | public long to; 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/data/members/HKXMember.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.data.members; 2 | 3 | import com.dexesttp.hkxpack.data.HKXData; 4 | import com.dexesttp.hkxpack.data.HKXObject; 5 | 6 | /** 7 | * A {@link HKXData} type used as children of a {@link HKXObject}. 8 | */ 9 | public interface HKXMember extends HKXData { 10 | /** 11 | * Get the name of this member. 12 | * 13 | * @return the member's name, as a {@link String}. 14 | */ 15 | String getName(); 16 | } 17 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/utils/WrongSizeException.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.utils; 2 | 3 | /** 4 | * Thrown if there was an error while parsing arguments. 5 | */ 6 | public class WrongSizeException extends Exception { 7 | private static final long serialVersionUID = -4241588440572992978L; 8 | 9 | /** 10 | * Create a {@link WrongSizeException}. 11 | * 12 | * @param option the option that had the wrong size. 13 | */ 14 | public WrongSizeException(final String option) { 15 | super(option); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/exceptions/InvalidTagXMLException.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader.exceptions; 2 | 3 | /** 4 | * Thrown where a non XML error was enocuntered while understanding the TagXML 5 | * file. 6 | */ 7 | public class InvalidTagXMLException extends Exception { 8 | private static final long serialVersionUID = -7902902818953946055L; 9 | 10 | /** 11 | * Creates an {@link InvalidTagXMLException}. 12 | * 13 | * @param string the message. 14 | */ 15 | public InvalidTagXMLException(final String string) { 16 | super(string); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/exceptions/UnsupportedVersionError.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.exceptions; 2 | 3 | /** 4 | * Thrown if a file with a non-supported version was attempted to be read. 5 | */ 6 | public class UnsupportedVersionError extends Exception { 7 | private static final long serialVersionUID = -1869309792519998570L; 8 | 9 | /** 10 | * Creates an {@link UnsupportedVersionError}. 11 | * 12 | * @param versionName the illegal version name 13 | */ 14 | public UnsupportedVersionError(final String versionName) { 15 | super("File version not supported : " + versionName); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/hkxreader/HKXTestBase.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader; 2 | 3 | import org.junit.ClassRule; 4 | 5 | import com.dexesttp.hkxpack.files.ReaderExternalResource; 6 | import com.dexesttp.hkxpack.files.TestBase; 7 | 8 | /** 9 | * {@link TestBase} extension for the HKX file. 10 | */ 11 | public class HKXTestBase extends TestBase { 12 | @ClassRule 13 | public static ReaderExternalResource resource = new HKXReaderExternalResource(BASE_FILE_RESOURCE_NAME); 14 | 15 | /** 16 | * Creates the {@link HKXTestBase}. 17 | */ 18 | public HKXTestBase() { 19 | super(resource.file); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/resources/byteutils/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains data to handle reading and writing from either {@link byte} arrays 3 | * or a {@link java.nio.ByteBuffer}.
4 | * The main entry point is 5 | * {@link com.dexesttp.hkxpack.resources.byteutils.ByteUtils}. 6 | *

7 | * 8 | * @see com.dexesttp.hkxpack.resources.byteutils.FloatByteUtils 9 | * @see com.dexesttp.hkxpack.resources.byteutils.SLongByteUtils 10 | * @see com.dexesttp.hkxpack.resources.byteutils.ULongByteUtils 11 | * @see com.dexesttp.hkxpack.resources.byteutils.StringByteUtils 12 | */ 13 | package com.dexesttp.hkxpack.resources.byteutils; -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/tagreader/TagXMLTestBase.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader; 2 | 3 | import org.junit.ClassRule; 4 | 5 | import com.dexesttp.hkxpack.files.ReaderExternalResource; 6 | import com.dexesttp.hkxpack.files.TestBase; 7 | 8 | /** 9 | * Tests for the TagXML version of the test base 10 | */ 11 | public class TagXMLTestBase extends TestBase { 12 | @ClassRule 13 | public static ReaderExternalResource resource = new TagXMLReaderExternalResource(BASE_FILE_RESOURCE_NAME); 14 | 15 | /** 16 | * Creates a {@link TagXMLTestBase} 17 | */ 18 | public TagXMLTestBase() { 19 | super(resource.file); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/header/HeaderData.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.header; 2 | 3 | /** 4 | * Contains information about a HKX File version and format. 5 | */ 6 | public class HeaderData { 7 | /** 8 | * The version of the file, as an integer. 9 | */ 10 | public int version; 11 | 12 | /** 13 | * The version name and identifier, contains additionnal information about the 14 | * file's version. 15 | */ 16 | public String versionName; 17 | 18 | /** 19 | * The padding after the header. Supported paddings are 0x00 (for most files) 20 | * and 0x10 (for animation files). 21 | */ 22 | public long paddingAfter; 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/callbacks/HKXArrayMemberCallback.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object.callbacks; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * General purpose callback handler for an Array 7 | */ 8 | public interface HKXArrayMemberCallback { 9 | /** 10 | * Proces an array component callback list to its end. 11 | * 12 | * @param memberCallbacks the {@link HKXMemberCallback} to process 13 | * @param position the start position of the array values 14 | * @return the first valid position after the array 15 | */ 16 | long process(List memberCallbacks, long position); 17 | } 18 | -------------------------------------------------------------------------------- /doc/hkx findings/Notes on behavior files.txt: -------------------------------------------------------------------------------- 1 | BloatflyRootBehavior 2 | Tests : 3 | - Duplicated 850 - 8EF 4 | > Crash 5 | - Duplicated 18F0-1B4F 6 | > Crash 7 | - Changed header value 7A8 (03 to 06) 8 | - 06 9 | - 00 10 | - 04 11 | - F3 12 | > Animation : rotation stopped ?!? 13 | > No location change (stuck in place) 14 | - Changed header value 7A5 (00 to 03) 15 | > Crash 16 | - Changed header value 7A9 to 03 from 00 17 | > Same as 7A8 18 | - Checked ~36E0 in file (there is data for targeting). 19 | - Changed "enable" to "disable" @3690 20 | > No notable change 21 | - Changed "2" to "3" @36AE 22 | > No effect 23 | - Changed 20 to 21 @7C5 24 | > No notable change 25 | - -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/enums/Flag.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor.enums; 2 | 3 | /** 4 | * List of known flag values for ClassXML components. 5 | */ 6 | public enum Flag { 7 | UNKNOWN, FLAGS_NONE, NOT_OWNED, ALIGN_8, ALIGN_16, SERIALIZE_IGNORED; 8 | 9 | /** 10 | * Get the relevant {@link Flag} from its name as a {@link String}. 11 | * 12 | * @param string the name of the {@link Flag}. 13 | * @return the {@link Flag} instance. 14 | */ 15 | public static Flag fromString(final String string) { 16 | try { 17 | return Flag.valueOf(string); 18 | } catch (IllegalArgumentException e) { 19 | return Flag.UNKNOWN; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/resources/DisplayProperties.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources; 2 | 3 | /** 4 | * DisplayProperties is used when outputting data in the middle of the HKXPack 5 | * core. I'm not usre it's useful anymore as all data is logged in 6 | * {@link LoggerUtil}. 7 | */ 8 | public class DisplayProperties { 9 | // Output properties 10 | public static boolean ignoreSerialized = true; 11 | public static boolean displayEmbeddedData; 12 | // Display properties 13 | public static boolean displayDebugInfo; 14 | public static boolean displayFileDebugInfo; 15 | public static boolean displayReadTypesInfo; 16 | public static boolean displayClassImportsInfo; 17 | } 18 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/utils/FileNameCreationException.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.utils; 2 | 3 | /** 4 | * Thrown when a file couldn't be converted to its HKX or XML equivalent. 5 | */ 6 | public class FileNameCreationException extends Exception { 7 | private static final long serialVersionUID = -4150191898446776060L; 8 | 9 | /** 10 | * Create a {@link FileNameCreationException} 11 | * 12 | * @param message the message of the exception 13 | * @param innerException the exception that causes this exception to be thrown 14 | */ 15 | public FileNameCreationException(final String message, final Exception innerException) { 16 | super(message, innerException); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cli/src/test/java/com/dexesttp/hkxpack/cli/utils/ArgsParserTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.utils; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | import org.junit.runners.Suite.SuiteClasses; 6 | 7 | @RunWith(Suite.class) 8 | @SuiteClasses({ ArgsParserEmptyTest.class, ArgsParserExistTest.class, ArgsParserSizeTest.class, 9 | ArgsParserValuesTest.class, ArgsParserExceptionTest.class, ArgsParserComplexTest.class, }) 10 | /** 11 | * {@inheritDoc} 12 | */ 13 | public class ArgsParserTest { 14 | public static final String TEST = "test"; 15 | public static final String TEST2 = "test2"; 16 | public static final String TEST3 = "test3"; 17 | public static final String TEST4 = "test4"; 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/callbacks/HKXMemberCallback.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object.callbacks; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Defines a {@link HKXMember} post operations 7 | * 8 | * @see #process(List, long) 9 | */ 10 | public interface HKXMemberCallback { 11 | /** 12 | * Handles a {@link HKXMember} post operations. 13 | * 14 | * @param memberCallbacks the callback list to add to. 15 | * @param position the position in the file at the beginning of the post 16 | * process 17 | * @return the next valid position at the end of the post process 18 | */ 19 | long process(List memberCallbacks, long position); 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/HKXMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXMember; 4 | import com.dexesttp.hkxpack.hkxwriter.object.callbacks.HKXMemberCallback; 5 | 6 | /** 7 | * Handles writing a {@link HKXMember} to a HKX File. 8 | * 9 | * @see #write(HKXMember, long) 10 | */ 11 | public interface HKXMemberHandler { 12 | /** 13 | * Writes a {@link HKXMember}'s contents to the HKX File. 14 | * 15 | * @param member the {@link HKXMember} to write 16 | * @param currentPos the current position of the class in the file 17 | * @return this member's delayed operation while writing. 18 | */ 19 | HKXMemberCallback write(HKXMember member, long currentPos); 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/HKXMemberReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXMember; 4 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 5 | 6 | /** 7 | * Reads a {@link HKXMember} from a HKX file. 8 | */ 9 | public interface HKXMemberReader { 10 | /** 11 | * Reads a {@link HKXMember} from the HKX file. 12 | * 13 | * @param classOffset the offset of the class that contains the member. 14 | * @return the read {@link HKXMember}. 15 | * @throws InvalidPositionException if there was a position error while reading 16 | * the {@link HKXMember}. 17 | */ 18 | HKXMember read(long classOffset) throws InvalidPositionException; 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/arrays/HKXArrayContentsReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member.arrays; 2 | 3 | import com.dexesttp.hkxpack.data.HKXData; 4 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 5 | 6 | /** 7 | * Reads array-specific contents. 8 | */ 9 | public interface HKXArrayContentsReader { 10 | /** 11 | * Reads array contents as {@link HKXData}. 12 | * 13 | * @param arrayStart the start of the array contents. 14 | * @param position the position in the array 15 | * @return the read {@link HKXData}. 16 | * @throws InvalidPositionException if there was an error accessing the array 17 | * data. 18 | */ 19 | HKXData getContents(long arrayStart, int position) throws InvalidPositionException; 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/classnames/Classname.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.classnames; 2 | 3 | /** 4 | * Represents a ClassName object in the {@literal __classnames__} section of a 5 | * HKX File. 6 | */ 7 | public class Classname { 8 | /** 9 | * the name of the class 10 | */ 11 | public transient String name; 12 | /** 13 | * the UUID of the class. 14 | */ 15 | public transient byte[] uuid; 16 | 17 | /** 18 | * Create a ClassName either to write it to the file or when it was read from a 19 | * file. 20 | * 21 | * @param classname the class name. 22 | * @param uuid the class UUID, as defined in the relevant classXML. 23 | */ 24 | public Classname(final String classname, final byte[] uuid) { 25 | this.name = classname; 26 | this.uuid = uuid.clone(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/resources/l10n/locale_en.properties: -------------------------------------------------------------------------------- 1 | # General strings 2 | bug.known = This is a known bug. Please do not report it. 3 | # Error strings 4 | error.hkx.read.type = Can't read type : 5 | error.hkx.read.subtype = Can't read array of subtype : 6 | error.hkx.write.cast = Error while trying to write a parameter. It might be a failed parameter. 7 | error.tag.create.type.unknown = The Tag parser attempted to write a wrong data type to the file. 8 | error.tag.read.type.unknown = The Tag parser attempted to read a wrong data type from the file. 9 | error.tag.read.hkpackfile = There is a wrong number of hkpackfile elements : expected 1, found 10 | error.tag.read.section = The following hksection couldn't be found : 11 | error.tag.read.member = The following member wasn't found in the XML : 12 | error.tag.read.sobject = The following sub-object wasn't found in the XML : -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/handlers/MemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.handlers; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXMember; 4 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 5 | 6 | /** 7 | * Handles a member based on its type 8 | */ 9 | public interface MemberHandler { 10 | /** 11 | * Creates a member from the given arguments. 12 | * 13 | * @param name 14 | * @param type 15 | * @param byteArray 16 | * @return 17 | */ 18 | HKXMember createMember(String name, HKXType type, byte[] byteArray); 19 | 20 | /** 21 | * Reads a member into a byteArray 22 | * 23 | * @param member the member to read 24 | * @return the read byteArray 25 | */ 26 | byte[] readMember(HKXMember member); 27 | 28 | /** 29 | * Retrieves the size of this member 30 | * 31 | * @return the member size 32 | */ 33 | long getSize(); 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/serialized/TagXMLSerializedHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader.serialized; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXMember; 4 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 5 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 6 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 7 | 8 | /** 9 | * Handles a serialized member. 10 | */ 11 | public interface TagXMLSerializedHandler { 12 | /** 13 | * Creates a {@link HKXMember} based on a member's content description as a 14 | * {@link HKXMemberTemplate} 15 | * 16 | * @param memberTemplate the {@link HKXMemberTemplate} to build 17 | * @return an empty {@link HKXMember} 18 | * @throws InvalidTagXMLException 19 | * @throws ClassFileReadException 20 | */ 21 | HKXMember handleMember(HKXMemberTemplate memberTemplate) throws ClassFileReadException, InvalidTagXMLException; 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/resources/LoggerUtil.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Handles all logged operations. 8 | */ 9 | public final class LoggerUtil { 10 | private static List eList = new ArrayList<>(); 11 | 12 | private LoggerUtil() { 13 | // NO OP 14 | } 15 | 16 | /** 17 | * Add an exception as a log. 18 | * 19 | * @param exception the exception to add. 20 | */ 21 | public static void add(final Throwable exception) { 22 | eList.add(exception); 23 | } 24 | 25 | /** 26 | * Add a new generic exception as a log, by its message. 27 | * 28 | * @param message the message to add 29 | */ 30 | public static void addNewException(final String message) { 31 | eList.add(new Exception(message)); 32 | } 33 | 34 | /** 35 | * Get the logger list. 36 | * 37 | * @return 38 | */ 39 | public static List getList() { 40 | return eList; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/ConsoleView.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli; 2 | 3 | import com.dexesttp.hkxpack.cli.commands.Command; 4 | import com.dexesttp.hkxpack.cli.commands.CommandFactory; 5 | import com.dexesttp.hkxpack.cli.commands.Command_help; 6 | 7 | /** 8 | * Entry point for the Command Line Interface. 9 | */ 10 | public final class ConsoleView { 11 | private static final int MINIMUM_ARG_COUNT = 1; 12 | 13 | private ConsoleView() { 14 | // NO OP 15 | } 16 | 17 | /** 18 | * Entry point for the console 19 | * 20 | * @param args the console arguments. 21 | */ 22 | public static void main(final String... args) { 23 | // Set the logging properties 24 | System.setProperty("java.util.logging.SimpleFormatter.format", "[%4$s] %5$s%n"); 25 | Command command; 26 | if (args.length < MINIMUM_ARG_COUNT) { 27 | command = new Command_help(); 28 | } else { 29 | command = new CommandFactory().newInstance(args[0]); 30 | } 31 | command.execute(args); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/files/ReaderExternalResource.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.files; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.xml.parsers.ParserConfigurationException; 6 | 7 | import org.junit.rules.ExternalResource; 8 | import org.xml.sax.SAXException; 9 | 10 | import com.dexesttp.hkxpack.data.HKXFile; 11 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 12 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 13 | 14 | /** 15 | * General purpose resource for a ReaderExternalResource 16 | */ 17 | public abstract class ReaderExternalResource extends ExternalResource { 18 | /** 19 | * The loaded {@link HKXFile}. 20 | */ 21 | public HKXFile file; 22 | 23 | /** 24 | * Override by the loader for {@link #file} 25 | * 26 | * @throws InvalidPositionException 27 | */ 28 | @Override 29 | public abstract void before() throws IOException, ParserConfigurationException, SAXException, 30 | InvalidTagXMLException, InvalidPositionException; 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/resources/byteutils/StringByteUtils.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources.byteutils; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Provides utility functions to read a String from a {@link ByteBuffer}. 7 | */ 8 | final class StringByteUtils { 9 | private StringByteUtils() { 10 | // NO OP 11 | } 12 | 13 | /** 14 | * Reads the next 0-terminated string from the given {@link ByteBuffer}.
15 | * This will change the {@link ByteBuffer#position()} value to just after the 0 16 | * of the Null-terminated string. 17 | * 18 | * @param inputByteBuffer the {@link ByteBuffer} to read from 19 | * @return the read {@link String} 20 | */ 21 | static String readString(final ByteBuffer inputByteBuffer) { 22 | byte readByte = inputByteBuffer.get(); 23 | StringBuffer resultBuffer = new StringBuffer(); 24 | while (readByte != 0) { 25 | resultBuffer.append((char) readByte); 26 | readByte = inputByteBuffer.get(); 27 | } 28 | return resultBuffer.toString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 DexesTTP 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/header/internals/versions/HeaderDescriptor_v11.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.header.internals.versions; 2 | 3 | import com.dexesttp.hkxpack.hkx.header.internals.HeaderDescriptor; 4 | 5 | /** 6 | * Describes the v11 version of the header. 7 | */ 8 | public class HeaderDescriptor_v11 extends HeaderDescriptor { 9 | /** 10 | * The version where this header is necessary 11 | */ 12 | public static final int VERSION_11 = 11; 13 | /** 14 | * Padding bytes found after the Header descriptor, if there is padding. 15 | */ 16 | public byte[] padding = new byte[] { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 17 | 18 | /** 19 | * Creates a {@link HeaderDescriptor_v11}. 20 | */ 21 | public HeaderDescriptor_v11() { 22 | super(); 23 | version = new byte[] { 11, 0, 0, 0 }; 24 | extras = new byte[] { 8, 1, 0, 1 }; 25 | verName = new byte[] { 'h', 'k', '_', '2', '0', '1', '4', '.', '1', '.', '0', '-', 'r', '1' }; 26 | extras11 = new byte[] { 21, 0 }; 27 | padding11 = new byte[] { 16, 0 }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dexesttp 8 | hkxpack 9 | 0.1.6-beta 10 | 11 | 12 | com.dexesttp.hkxpack 13 | core 14 | 15 | HKXPack Core 16 | Set of core modules containing the logic to unpack and pack the hkx files 17 | 18 | 19 | com.google.guava 20 | guava 21 | 19.0 22 | 23 | 24 | junit 25 | junit 26 | 4.12 27 | test 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/l10n/SBundle.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.l10n; 2 | 3 | import java.util.Locale; 4 | import java.util.ResourceBundle; 5 | 6 | /** 7 | * Contains the ResourceBundle for localizaton stuff. 8 | */ 9 | public final class SBundle { 10 | private static String baseName = "l10n/locale"; 11 | private static ResourceBundle instance; 12 | 13 | private SBundle() { 14 | // NO OP 15 | } 16 | 17 | /** 18 | * Get a localized string. 19 | * 20 | * @param stringName the string name 21 | * @return the localized string 22 | */ 23 | public static String getString(final String stringName) { 24 | return getInstance().getString(stringName); 25 | } 26 | 27 | /** 28 | * Get the localized ResourceBundle. 29 | * 30 | * @return the localized {@link ResourceBundle}. 31 | */ 32 | public static ResourceBundle getInstance() { 33 | if (instance == null) { 34 | initInstance(); 35 | } 36 | return instance; 37 | } 38 | 39 | private static void initInstance() { 40 | instance = ResourceBundle.getBundle(baseName, Locale.ENGLISH); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/resources/byteutils/ULongByteUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources.byteutils; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Tests the {@link ULongByteUtils} class 9 | */ 10 | public class ULongByteUtilsTest { 11 | @Test 12 | /** 13 | * Test 14 | */ 15 | public void getUIntSize2ShouldWorkFor0() { 16 | assertArrayEquals("Long(2) : 0 => 0", new byte[] { 0, 0 }, ULongByteUtils.fromLong(0, 2)); 17 | } 18 | 19 | @Test 20 | /** 21 | * Test 22 | */ 23 | public void getUIntSize2ShouldWorkFor1() { 24 | assertArrayEquals("Long(2) : 1 => 1", new byte[] { 1, 0 }, ULongByteUtils.fromLong(1, 2)); 25 | } 26 | 27 | @Test 28 | /** 29 | * Test 30 | */ 31 | public void getUIntSize2ShouldWorkForMaxValue() { 32 | assertArrayEquals("Long(2) : 65535 => 65535", new byte[] { -1, -1 }, ULongByteUtils.fromLong(65535, 2)); 33 | } 34 | 35 | @Test 36 | /** 37 | * Test 38 | */ 39 | public void getUIntSize2ShouldWorkFor30() { 40 | assertArrayEquals("Long(2) : 30 => 30", new byte[] { 30, 0 }, ULongByteUtils.fromLong(30, 2)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/members/TagXMLContentsHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader.members; 2 | 3 | import org.w3c.dom.Node; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXMember; 6 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 7 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 8 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 9 | 10 | /** 11 | * Handle the contents of a {@link Node} into a {@link HKXMember} 12 | */ 13 | public interface TagXMLContentsHandler { 14 | /** 15 | * Creates a {@link HKXMember} based on a {@link Node} and its contents 16 | * description as a {@link HKXMemberTemplate} 17 | * 18 | * @param member the {@link Node} to read. 19 | * @param memberTemplate the {@link HKXMemberTemplate} describing the 20 | * {@link Node}'s contents 21 | * @return a {@link HKXMember} containing all relevant data. 22 | * @throws InvalidTagXMLException 23 | * @throws ClassFileReadException 24 | */ 25 | HKXMember handleNode(Node member, HKXMemberTemplate memberTemplate) 26 | throws ClassFileReadException, InvalidTagXMLException; 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/header/internals/SectionDescriptor.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.header.internals; 2 | 3 | /** 4 | * A general purpose Section descriptor, designed on the v8 and v11 sections. 5 | */ 6 | public class SectionDescriptor { 7 | /** 8 | * Name of the section, null-terminated 9 | */ 10 | public byte[] secName = new byte[16]; 11 | /** 12 | * Constant value 13 | */ 14 | public byte[] constant = new byte[] { 0, 0, 0, -1 }; 15 | /** 16 | * Offset of the section (from the beginning of the file) 17 | */ 18 | public byte[] offset = new byte[4]; 19 | /** 20 | * First data part (position from the offset). 21 | */ 22 | public byte[] data1 = new byte[4]; 23 | /** 24 | * Second data part (position from the offset). 25 | */ 26 | public byte[] data2 = new byte[4]; 27 | /** 28 | * Third data part (position from the offset). 29 | */ 30 | public byte[] data3 = new byte[4]; 31 | /** 32 | * Fourth data part (position from the offset). 33 | */ 34 | public byte[] data4 = new byte[4]; 35 | /** 36 | * Fifth data part (position from the offset). 37 | */ 38 | public byte[] data5 = new byte[4]; 39 | /** 40 | * End of the section (from the offset) 41 | */ 42 | public byte[] end = new byte[4]; 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/HKXObjectMemberReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXMember; 4 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 5 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 6 | import com.dexesttp.hkxpack.hkxreader.HKXObjectReader; 7 | 8 | /** 9 | * Create a {@link HKXObject} from a HKX file, and returns it as 10 | * {@link HKXMember}. 11 | */ 12 | class HKXObjectMemberReader implements HKXMemberReader { 13 | private final transient HKXDescriptor descriptor; 14 | private final transient HKXObjectReader objectReader; 15 | private final transient String name; 16 | private final transient long offset; 17 | 18 | HKXObjectMemberReader(final HKXObjectReader objectReader, final String name, final long offset, 19 | final HKXDescriptor descriptor) { 20 | this.objectReader = objectReader; 21 | this.name = name; 22 | this.offset = offset; 23 | this.descriptor = descriptor; 24 | } 25 | 26 | @Override 27 | /** 28 | * {@inheritDoc} 29 | */ 30 | public HKXMember read(final long classOffset) throws InvalidPositionException { 31 | return objectReader.createHKXObject(name, classOffset + offset, descriptor); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dexesttp 8 | hkxpack 9 | 0.1.6-beta 10 | pom 11 | 12 | HKXPack 13 | HKXPack is a tool to 'pack' and 'unpack' to and from hkx files, which is the Havok compressed file format. 14 | 15 | 16 | core 17 | cli 18 | 19 | 20 | 21 | UTF-8 22 | 23 | 24 | 25 | hkxpack-${project.artifactId} 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.3 31 | 32 | 1.8 33 | 1.8 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/utils/PointerResolver.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | 7 | import com.dexesttp.hkxpack.hkx.data.DataExternal; 8 | 9 | /** 10 | * Resolves a pointer into an address 11 | */ 12 | public class PointerResolver { 13 | private final transient Map map = new HashMap<>(); 14 | 15 | /** 16 | * Add a new known pointer. 17 | * 18 | * @param name the name of the object. 19 | * @param position the position the object was written at. 20 | */ 21 | public void add(final String name, final long position) { 22 | map.put(name, position); 23 | } 24 | 25 | /** 26 | * Resolves a pointer. 27 | * 28 | * @param object the {@link PointerObject} to resolve. 29 | * @return the resolved and filled {@link DataExternal}, or "null" if the 30 | * resolution failed. 31 | */ 32 | public Optional resolve(final PointerObject object) { 33 | if (!map.keySet().contains(object.to)) { 34 | return Optional.empty(); 35 | } 36 | DataExternal res = new DataExternal(); 37 | res.section = 0x02; 38 | res.from = object.from; 39 | res.to = map.get(object.to); 40 | return Optional.of(res); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /doc/hkx findings/Applying FNIS findings to FO4.txt: -------------------------------------------------------------------------------- 1 | Working on the Bloatfly (BloatflyRootBehavior.hkx) 2 | 3 | => header (checking) 4 | __classnames __ 5 | [00 01] [D0 03]*8 6 | => @100 -> Beginning of class definitions 7 | => @100+3D0 @4D0 -> end of class definitions 8 | __types__ 9 | [D0 04] [00 00]*8 10 | => @4D0 is the end of class definitions 11 | __data__ 12 | [D0 04] [40 69] [C0 7A] [40 7E] [90 81]*3 13 | => @4D0 end of __classnames__ 14 | => @6940+4D0 @6E10 beginning of __data__1 15 | => @7AC0+4D0 @7F90 beginning of __data__2 16 | > no more 4 bytes values after 17 | => @7E40+4D0 @8310 beginning of __data__3 18 | > @8308 is all FFs until @8310 19 | => @8190+4D0 @8660 20 | > EOF allright 21 | 22 | => Classes values 23 | => Most class value changed 24 | e.g. "hkbBehaviorGraphData" went from "5D CA 5A 09" [SK] to "22 82 7A 90" [F4] 25 | => Checked throughout two files 26 | => Need to find new value for "hkbBehaviorReferenceGenerator" in the files and/or in the doc. 27 | 28 | => Structure of __data__1 part (@6E10) 29 | [18 00] @18+4D0 @4E8 => value ? 0's 30 | [40 00] @40+4D0 @510 => hkbBehaviorGraph 31 | [98 00] @98+4D0 @568 => value ? 0's 32 | [10 02] @210+4D0 @6E0 => BloatflyRootBehavior.hkb 33 | => Seems like __data__1 format didn't change format. 34 | 35 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/exceptions/WrongInputCastException.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.exceptions; 2 | 3 | /** 4 | * Throwed when an object content doesn't match with its template. 5 | */ 6 | public class WrongInputCastException extends Exception { 7 | private static final long serialVersionUID = 6483999356904172141L; 8 | 9 | /** 10 | * Creates a {@link WrongInputCastException}. 11 | * 12 | * @param message the error message 13 | */ 14 | public WrongInputCastException(final String message) { 15 | super(message); 16 | } 17 | 18 | /** 19 | * Creates a {@link WrongInputCastException}. 20 | * 21 | * @param previousException the exception that caused the 22 | * {@link WrongInputCastException}. 23 | */ 24 | 25 | public WrongInputCastException(final Throwable previousException) { 26 | super(previousException); 27 | } 28 | 29 | /** 30 | * Creates a {@link WrongInputCastException}. 31 | * 32 | * @param message the error message 33 | * @param previousException the exception that caused the 34 | * {@link WrongInputCastException}. 35 | */ 36 | public WrongInputCastException(final String message, final Throwable previousException) { 37 | super(message, previousException); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/exceptions/ClassFileReadException.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor.exceptions; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * A {@link ClassFileReadException} is thrown when a class file described in the 7 | * class list couldn't be read. 8 | */ 9 | public class ClassFileReadException extends IOException { 10 | private static final long serialVersionUID = 3053825597885294212L; 11 | 12 | /** 13 | * Create a {@link ClassFileReadException}. 14 | * 15 | * @param message the error message 16 | */ 17 | public ClassFileReadException(final String message) { 18 | super(message); 19 | } 20 | 21 | /** 22 | * Create a {@link ClassFileReadException}. 23 | * 24 | * @param throwable the exception that caused the 25 | * {@link ClassFileReadException}. 26 | */ 27 | public ClassFileReadException(final Throwable throwable) { 28 | super(throwable); 29 | } 30 | 31 | /** 32 | * Create a {@link ClassFileReadException}. 33 | * 34 | * @param message the error message 35 | * @param throwable the exception that caused the 36 | * {@link ClassFileReadException}. 37 | */ 38 | public ClassFileReadException(final String message, final Throwable throwable) { 39 | super(message, throwable); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .metadata 3 | bin/ 4 | tmp/ 5 | *.tmp 6 | *.bak 7 | *.swp 8 | *~.nib 9 | local.properties 10 | .settings/ 11 | .loadpath 12 | 13 | # Eclipse Core 14 | .project 15 | 16 | # External tool builders 17 | .externalToolBuilders/ 18 | 19 | # Locally stored "Eclipse launch configurations" 20 | *.launch 21 | 22 | # PyDev specific (Python IDE for Eclipse) 23 | *.pydevproject 24 | 25 | # CDT-specific (C/C++ Development Tooling) 26 | .cproject 27 | 28 | # JDT-specific (Eclipse Java Development Tools) 29 | .classpath 30 | 31 | # Java annotation processor (APT) 32 | .factorypath 33 | 34 | # PDT-specific (PHP Development Tools) 35 | .buildpath 36 | 37 | # sbteclipse plugin 38 | .target 39 | 40 | # TeXlipse plugin 41 | .texlipse 42 | 43 | # STS (Spring Tool Suite) 44 | .springBeans 45 | 46 | 47 | # Java 48 | *.class 49 | 50 | # Mobile Tools for Java (J2ME) 51 | .mtj.tmp/ 52 | 53 | # Package Files # 54 | *.jar 55 | *.war 56 | *.ear 57 | 58 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 59 | hs_err_pid* 60 | 61 | 62 | # Maven 63 | /target/ 64 | pom.xml.tag 65 | pom.xml.releaseBackup 66 | pom.xml.versionsBackup 67 | pom.xml.next 68 | release.properties 69 | dependency-reduced-pom.xml 70 | buildNumber.properties 71 | .mvn/timing.properties 72 | /classxml/ 73 | /properties/ 74 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/data/members/HKXFailedMember.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.data.members; 2 | 3 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 4 | 5 | /** 6 | * Represents a member that failed to be imported. 7 | */ 8 | public class HKXFailedMember implements HKXMember { 9 | private final String name; 10 | private final HKXType type; 11 | private final String failMessage; 12 | 13 | /** 14 | * Creates a {@link HKXFailedMember}. 15 | * 16 | * @param name the name of the failed member. 17 | * @param type the intended type of the failed member. 18 | * @param failMessage why the member failed to be imported. 19 | */ 20 | public HKXFailedMember(final String name, final HKXType type, final String failMessage) { 21 | this.name = name; 22 | this.type = type; 23 | this.failMessage = failMessage; 24 | } 25 | 26 | /** 27 | * Returns a sensible message, in english, about the import fail. 28 | * 29 | * @return 30 | */ 31 | public String getFailMessage() { 32 | return "Couldn't read " + name + " : " + failMessage; 33 | } 34 | 35 | @Override 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | @Override 44 | /** 45 | * {@inheritDoc} 46 | */ 47 | public HKXType getType() { 48 | return type; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cli/src/debug/java/com/dexesttp/hkxpack/cli/TestView.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import com.dexesttp.hkxpack.cli.components.HKXTest; 6 | import com.dexesttp.hkxpack.cli.components.Read; 7 | import com.dexesttp.hkxpack.cli.components.Write; 8 | import com.dexesttp.hkxpack.cli.components.XMLTest; 9 | import com.dexesttp.hkxpack.resources.LoggerUtil; 10 | 11 | /** 12 | * Testing interface, used to perform live tests with Eclipse. 13 | *

14 | * @see HKXTest#exec(String, String) 15 | * @see XMLTest#exec(String, String) 16 | * @see Read#exec(String, String) 17 | * @see Write#exec(String, String) 18 | */ 19 | final class TestView { 20 | public static final Logger LOGGER = Logger.getLogger(TestView.class.getName()); 21 | private static final String ROOT_NAME = "D:\\Documents\\SANDBOX\\FO4\\hkx_files\\"; 22 | private static final String TEST_FILE_NAME = "skeleton"; 23 | 24 | /** 25 | * Testing entry point 26 | * @param args 27 | */ 28 | public static void main(final String... args) { 29 | Read.exec(ROOT_NAME, TEST_FILE_NAME); 30 | Write.exec(ROOT_NAME, TEST_FILE_NAME); 31 | Read.exec(ROOT_NAME, TEST_FILE_NAME + "-new"); 32 | for(Throwable e: LoggerUtil.getList()) { 33 | LOGGER.throwing(TestView.class.getName(), "main", e); 34 | } 35 | } 36 | 37 | private TestView() { 38 | // NO OP 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/resources/byteutils/SLongByteUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources.byteutils; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Tests for the {@link SLongByteUtils} class 9 | */ 10 | public class SLongByteUtilsTest { 11 | @Test 12 | /** 13 | * Test 14 | */ 15 | public void getSLongSize2WorksFor0() { 16 | assertArrayEquals("SLong(2) : 0 => 0", new byte[] { 0, 0 }, SLongByteUtils.fromLong(0, 2)); 17 | } 18 | 19 | @Test 20 | /** 21 | * Test 22 | */ 23 | public void getSLongSize2WorksFor1() { 24 | assertArrayEquals("SLong(2) : 1 => 1", new byte[] { 1, 0 }, SLongByteUtils.fromLong(1, 2)); 25 | } 26 | 27 | @Test 28 | /** 29 | * Test 30 | */ 31 | public void getSLongSize2WorksForMinus1() { 32 | assertArrayEquals("SLong(2) : -1 => -1", new byte[] { -1, -1 }, SLongByteUtils.fromLong(-1, 2)); 33 | } 34 | 35 | @Test 36 | /** 37 | * Test 38 | */ 39 | public void getSILongSize2WorksForMaxValue() { 40 | assertArrayEquals("SLong(2) : 32767 => 32767", new byte[] { -1, 127 }, SLongByteUtils.fromLong(32767, 2)); 41 | } 42 | 43 | @Test 44 | /** 45 | * Test 46 | */ 47 | public void getSIntSize2WorksForMinValue() { 48 | assertArrayEquals("SLong(2) : -32768 => -32768", new byte[] { 0, -128 }, SLongByteUtils.fromLong(-32768, 2)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/commands/CommandFactory.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.commands; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | 7 | /** 8 | * Routes a command line interface into the relevant {@link Command}. 9 | */ 10 | public class CommandFactory { 11 | /** 12 | * List of commands associated with the class to execute on read 13 | */ 14 | @SuppressWarnings("rawtypes") 15 | public static Map commandParser = new HashMap<>(); 16 | static { 17 | commandParser.put("extract", Command_unpack.class); 18 | commandParser.put("unpack", Command_unpack.class); 19 | commandParser.put("compress", Command_pack.class); 20 | commandParser.put("pack", Command_pack.class); 21 | commandParser.put("help", Command_help.class); 22 | } 23 | 24 | /** 25 | * Retrieves the relevant {@link Command}. 26 | * 27 | * @param commandName the first argument passed to main. 28 | * @return the relevant {@link Command}. 29 | */ 30 | public Command newInstance(final String commandName) { 31 | @SuppressWarnings("rawtypes") 32 | Optional commandClass = Optional.ofNullable(commandParser.get(commandName)); 33 | try { 34 | return (Command) commandClass.orElse(Command_quick.class).newInstance(); 35 | } catch (InstantiationException | IllegalAccessException e) { 36 | return new Command_quick(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/resources/byteutils/FloatByteUtils.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources.byteutils; 2 | 3 | /** 4 | * Handles converting {@link float} and {@link double} values from and to 5 | * Little-Endian {@link byte} arrays 6 | */ 7 | final class FloatByteUtils { 8 | private static final int DOUBLE_SIZE = 0x08; 9 | 10 | private FloatByteUtils() { 11 | // NO OP 12 | } 13 | 14 | /** 15 | * Convert a {@link double} value to a {@link byte} array. 16 | * 17 | * @param value the {@link double} value to convert 18 | * @param numBytes the number of bytes in the {@link byte} array 19 | * @return the relevant {@link byte} array 20 | */ 21 | static byte[] fromFloat(final double value, final int numBytes) { 22 | if (numBytes == DOUBLE_SIZE) { 23 | long temp = Double.doubleToLongBits(value); 24 | return ULongByteUtils.fromLong(temp, numBytes); 25 | } else { 26 | int temp = Float.floatToIntBits((float) value); 27 | return ULongByteUtils.fromLong(temp, numBytes); 28 | } 29 | } 30 | 31 | /** 32 | * Convert a {@link byte} array containing a {@link float} value to a 33 | * {@link float}. 34 | * 35 | * @param value the {@link byte} array to convert 36 | * @return the converted {@link float} 37 | */ 38 | static float getFloat(final byte[] value) { 39 | int val = (int) ULongByteUtils.getLong(value); 40 | return Float.intBitsToFloat(val); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/header/internals/HeaderDescriptor.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.header.internals; 2 | 3 | /** 4 | * A general purpose Header descriptor, designed based on the v8 and v11 5 | * headers. 6 | */ 7 | public class HeaderDescriptor { 8 | /** 9 | * The file id, same for all hkx files (I assume says that it's a hkx file) 10 | */ 11 | public byte[] fileID = new byte[] { 87, -32, -32, 87, 16, -64, -64, 16, 0, 0, 0, 0 }; 12 | /** 13 | * The file version, over 4 bytes. See docs for what versions are what. 14 | */ 15 | public byte[] version = new byte[4]; 16 | /** 17 | * Extra data. This isn't labeled as constants as it changes between version 8 18 | * and 11. 19 | */ 20 | public byte[] extras = new byte[4]; 21 | /** 22 | * Some constant data. No idea what it is for. 23 | */ 24 | public byte[] constants = new byte[] { 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 0, 0, 0 }; 25 | /** 26 | * A string containing the version name. 27 | */ 28 | public byte[] verName = new byte[14]; 29 | /** 30 | * Some more constants. 31 | */ 32 | public byte[] constants2 = new byte[] { 0, -1, 0, 0, 0, 0 }; 33 | /** 34 | * This is either FF on version 8, or some data on version 11. 35 | */ 36 | public byte[] extras11 = new byte[2]; 37 | /** 38 | * This appears to be a padding before the next data chunk for version 11. 39 | */ 40 | public byte[] padding11 = new byte[2]; 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/HKXDescriptorReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader; 2 | 3 | import com.dexesttp.hkxpack.data.HKXObject; 4 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 5 | 6 | /** 7 | * Reads an {@link HKXObject} based on its {@link HKXDescriptor}. 8 | */ 9 | class HKXDescriptorReader { 10 | private final transient HKXObjectReader creator; 11 | private final transient PointerNameGenerator generator; 12 | 13 | /** 14 | * Create a new HKXDescriptorReader 15 | * 16 | * @param creator the {@link HKXObjectReader} to use while creating the 17 | * {@link HKXObject}. 18 | * @param generator the {@link PointerNameGenerator} to generate names from. 19 | */ 20 | HKXDescriptorReader(final HKXObjectReader creator, final PointerNameGenerator generator) { 21 | this.creator = creator; 22 | this.generator = generator; 23 | } 24 | 25 | /** 26 | * Read an HKXObject from the file. 27 | * 28 | * @param position the position the {@link HKXObject} shoud be read from. 29 | * @param descriptor the {@link HKXDescriptor} describing the {@link HKXObject} 30 | * to read. 31 | * @return an {@link HKXObject} containing all the read contents. 32 | */ 33 | HKXObject read(final long position, final HKXDescriptor descriptor) { 34 | String objectName = generator.get(position); 35 | return creator.createHKXObject(objectName, position, descriptor); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/header/HKXHeaderFactory.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.header; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.dexesttp.hkxpack.data.HKXFile; 7 | import com.dexesttp.hkxpack.data.HKXObject; 8 | import com.dexesttp.hkxpack.hkx.header.HeaderData; 9 | 10 | /** 11 | * Creates a {@link HeaderData} object from the given {@link HKXFile}. 12 | */ 13 | public class HKXHeaderFactory { 14 | private final transient List animClassesList = new ArrayList<>(); 15 | 16 | /** 17 | * Creates a {@link HKXHeaderFactory} 18 | */ 19 | public HKXHeaderFactory() { 20 | animClassesList.add("hkaAnimationContainer"); 21 | animClassesList.add("hclClothData"); 22 | } 23 | 24 | /** 25 | * Creates a {@link HeaderData} from a {@link HKXFile}. 26 | * 27 | * @param file the {@link HKXFile} to get the header from. 28 | * @return the relevant {@link HeaderData}. 29 | */ 30 | public HeaderData create(final HKXFile file) { 31 | boolean isAnim = false; 32 | for (HKXObject object : file.getContentCollection()) { 33 | if (animClassesList.contains(object.getDescriptor().getName())) { 34 | isAnim = true; 35 | } 36 | } 37 | 38 | HeaderData header = new HeaderData(); 39 | header.version = file.getClassVersion(); 40 | header.versionName = file.getContentsVersion(); 41 | header.paddingAfter = isAnim ? 0x10 : 0x00; 42 | 43 | return header; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/resources/byteutils/SLongByteUtils.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources.byteutils; 2 | 3 | /** 4 | * Handles converting signed {@link long} values from and to Little-Endian 5 | * {@link byte} arrays 6 | */ 7 | final class SLongByteUtils { 8 | private static final long BYTE_MASK = 0xFF; 9 | 10 | private SLongByteUtils() { 11 | // NO OP 12 | } 13 | 14 | /** 15 | * Get a signed long from a {@link byte} array. 16 | * 17 | * @param list the Little-Endian {@link byte} array to convert 18 | * @return the converted signed {@link long} value. 19 | */ 20 | static long getLong(final byte[] list) { 21 | final int len = list.length; 22 | int accu = 1; 23 | int res = 0; 24 | for (int i = 0; i < len; i++) { 25 | res += ((int) (list[i] & 0xFF)) * accu; 26 | accu *= 256; 27 | } 28 | return res; 29 | } 30 | 31 | /** 32 | * Get a {@link byte} array from a signed {@link long} value. 33 | * 34 | * @param value the value to convert 35 | * @param numBytes the number of bytes in the output {@link byte} array 36 | * @return a Little-Endian {@link byte} array. 37 | */ 38 | static byte[] fromLong(final long value, final int numBytes) { 39 | long shiftedValue = value; 40 | byte[] res = new byte[numBytes]; 41 | for (int i = 0; i < numBytes; i++) { 42 | res[i] = (byte) (shiftedValue & BYTE_MASK); 43 | shiftedValue = shiftedValue >> 8; 44 | } 45 | return res; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/resources/byteutils/ULongByteUtils.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.resources.byteutils; 2 | 3 | /** 4 | * Handles converting unsigned {@link long} values from and to Little-Endian 5 | * {@link byte} arrays 6 | */ 7 | final class ULongByteUtils { 8 | private static final long BYTE_MASK = 0xFF; 9 | 10 | private ULongByteUtils() { 11 | // NO OP 12 | } 13 | 14 | /** 15 | * Get an unsigned long from a {@link byte} array. 16 | * 17 | * @param list the Little-Endian {@link byte} array to convert 18 | * @return the converted unsigned {@link long} value. 19 | */ 20 | static long getLong(final byte[] list) { 21 | final int len = list.length; 22 | long accu = 1; 23 | long res = 0; 24 | for (int i = 0; i < len; i++) { 25 | res += ((long) (list[i] & 0xFF)) * accu; 26 | accu *= 256; 27 | } 28 | return res; 29 | } 30 | 31 | /** 32 | * Get a {@link byte} array from an unsigned {@link long} value. 33 | * 34 | * @param value the value to convert 35 | * @param numBytes the number of bytes in the output {@link byte} array 36 | * @return a Little-Endian {@link byte} array. 37 | */ 38 | static byte[] fromLong(final long value, final int numBytes) { 39 | long leftedValue = value; 40 | byte[] res = new byte[numBytes]; 41 | for (int i = 0; i < numBytes; i++) { 42 | res[i] = (byte) (leftedValue & BYTE_MASK); 43 | leftedValue = leftedValue >> 8; 44 | } 45 | return res; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/hkxreader/HKXReaderExternalResource.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | 6 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 7 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 8 | import com.dexesttp.hkxpack.files.ReaderExternalResource; 9 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 10 | import com.dexesttp.hkxpack.utils.FileUtils; 11 | 12 | /** 13 | * The external resource to handle the HKX file. 14 | */ 15 | public class HKXReaderExternalResource extends ReaderExternalResource { 16 | private final transient String baseFileResourceName; 17 | 18 | /** 19 | * Creates the HKXReader external resource 20 | * 21 | * @param baseFileResourceName 22 | */ 23 | public HKXReaderExternalResource(final String baseFileResourceName) { 24 | super(); 25 | this.baseFileResourceName = baseFileResourceName; 26 | } 27 | 28 | @Override 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | public void before() throws IOException, InvalidPositionException { 33 | ByteBuffer buffer = FileUtils.resourceToHKXByteBuffer(baseFileResourceName + ".hkx"); 34 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 35 | HKXDescriptorFactory descriptorFactory = new HKXDescriptorFactory(enumResolver); 36 | HKXReader reader = new HKXReader(buffer, descriptorFactory, enumResolver); 37 | file = reader.read(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/exceptions/ClassListReadException.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor.exceptions; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * A ClassListReadError is thrown when the class list couldn't be read properly. 7 | *

8 | * The ClassList path is supposed to be defined as a static, final resource in 9 | * {@link com.dexesttp.hkxpack.descriptor.reader.ClassXMLList}. 10 | */ 11 | public class ClassListReadException extends IOException { 12 | private static final long serialVersionUID = -6189166645845112170L; 13 | 14 | /** 15 | * Creates a {@link ClassListReadException}. 16 | * 17 | * @param message a message to explain the exception. 18 | */ 19 | public ClassListReadException(final String message) { 20 | super(message); 21 | } 22 | 23 | /** 24 | * Creates a {@link ClassListReadException}. 25 | * 26 | * @param throwable the exception that caused the 27 | * {@link ClassListReadException}. 28 | */ 29 | public ClassListReadException(final Throwable throwable) { 30 | super(throwable); 31 | } 32 | 33 | /** 34 | * Creates a {@link ClassListReadException}. 35 | * 36 | * @param message a message to explain the exception. 37 | * @param throwable the exception that caused the 38 | * {@link ClassListReadException}. 39 | */ 40 | public ClassListReadException(final String message, final Throwable throwable) { 41 | super(message, throwable); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/exceptions/InvalidPositionException.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.exceptions; 2 | 3 | /** 4 | * An {@link InvalidPositionException} signals a reading error due to the fact a 5 | * HKXDescriptor asked the HKXFile reading components to go over the file's 6 | * section limits.
7 | * Please report any instance of {@link InvalidPositionException} as a HKXPack 8 | * issue, as it is not intended behavior and can provide additional insight 9 | * about the HXK structure.
10 | * Make sure to provide a copy of the file you attempted to read, or information 11 | * about how to retrieve the file if given file is under copyright.
12 | * Link to HKXPack's issue 13 | * tracker 14 | */ 15 | public class InvalidPositionException extends Exception { 16 | private static final long serialVersionUID = 5256901069828621035L; 17 | private final String section; 18 | 19 | /** 20 | * Creates an {@link InvalidPositionException}. 21 | * 22 | * @param sectName the section name where the exception happened 23 | * @param pos the invalid position 24 | */ 25 | public InvalidPositionException(final String sectName, final long pos) { 26 | super("Invalid position in " + sectName + " : " + pos); 27 | this.section = sectName; 28 | } 29 | 30 | /** 31 | * Get this invalid section's name. 32 | * 33 | * @return the name 34 | */ 35 | public String getSection() { 36 | return section; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /doc/hkx findings/studying __data2__ of Bloatfly.txt: -------------------------------------------------------------------------------- 1 | Values seems to be ordered by 3 : 2 | 2 3 | 4 | Tests on 2 first vals and 2 last ones 5 | 6 | <20> 2 <60> => 40 (d64) 7 | <120> 2 <230> => 110 (d272) 8 | ... 9 | <3308> 2 <3D40> => A38 (d2616) 10 | <3388> 2 <3E90> => B08 (d2824) 11 | 12 | => Inference : 13 | val1 => source 14 | val3 => destination 15 | 16 | === Beginning of part 2 === @7AC0+4D0 @7F90 17 | 18 | 20 00 00 00 @20+4D0 @4F0 8-block ? 19 | 02 00 00 00 2 20 | 60 00 00 00 @60+of2 @ 21 | 20 01 00 00 @120+4D0 @5F0 8-block 22 | 23 | 02 00 00 00 2 24 | 30 02 00 00 @230+of2 @ 25 | 28 01 00 00 @128+4D0 @5F8 8-block (or +) 26 | 02 00 00 00 2 27 | 28 | 20 33 00 00 @3320+of2 @ 29 | 90 03 00 00 @390+4D0 @860 8-bloc 30 | 02 00 00 00 2 31 | A0 03 00 00 @3A0+of2 @ 32 | 33 | 98 03 00 00 @398+4D0 @868 8-bloc (or +) 34 | 02 00 00 00 2 35 | A0 29 00 00 @29A0+of2 @ 36 | E8 03 00 00 @38E+4D0 @85E 8-bloc 37 | 38 | 02 00 00 00 2 39 | 30 04 00 00 @430+of2 @ 40 | F8 03 00 00 @3F8+4D0 @8C8 8-bloc => /!\ Possibly overlapping a value. 41 | 02 00 00 00 2 42 | 43 | 60 04 00 00 @460+of2 44 | D0 05 00 00 @5D0+4D0 @AA0 8-bloc too. 45 | 02 00 00 00 2 46 | E0 05 00 00 @5E0+of2 47 | 48 | D8 05 00 00 49 | 02 00 00 00 50 | E0 1D 00 00 51 | 40 05 00 00 52 | 53 | 02 00 00 00 54 | 20 29 00 00 55 | 38 06 00 00 56 | 02 00 00 00 57 | 58 | Remark : the value 3 seems similar to data3's value 1. 59 | => After testing, all data2's value3 are in data3's value1 AND BACK despite the size differential 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/data/members/HKXStringMember.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.data.members; 2 | 3 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 4 | 5 | /** 6 | * A {@link HKXMember} whose content is a String. 7 | */ 8 | public class HKXStringMember implements HKXMember { 9 | private final String name; 10 | private final HKXType type; 11 | private transient String value; 12 | 13 | /** 14 | * Creates a {@link HKXStringMember}. 15 | * 16 | * @param name the name of the {@link HKXStringMember}. 17 | * @param type the {@link HKXType} of the {@link HKXStringMember}. Note that 18 | * {@link HKXType#getFamily()} should return 19 | * {@link HKXTypeFamily#STRING}, although this isn't checked. 20 | */ 21 | public HKXStringMember(final String name, final HKXType type) { 22 | this.name = name; 23 | this.type = type; 24 | } 25 | 26 | /** 27 | * Set the String of this member. 28 | * 29 | * @param value the new String to affect. 30 | */ 31 | public void set(final String value) { 32 | this.value = value; 33 | } 34 | 35 | /** 36 | * Get this {@link HKXStringMember}'s content. 37 | * 38 | * @return the value of this member. 39 | */ 40 | public String get() { 41 | return value; 42 | } 43 | 44 | @Override 45 | /** 46 | * {@inheritDoc} 47 | */ 48 | public String getName() { 49 | return name; 50 | } 51 | 52 | @Override 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | public HKXType getType() { 57 | return type; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/HKXDirectMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object; 2 | 3 | import java.nio.Buffer; 4 | import java.nio.ByteBuffer; 5 | 6 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 7 | import com.dexesttp.hkxpack.data.members.HKXMember; 8 | import com.dexesttp.hkxpack.hkx.types.MemberDataResolver; 9 | import com.dexesttp.hkxpack.hkxwriter.object.callbacks.HKXMemberCallback; 10 | 11 | /** 12 | * Handles a {@link HKXDirectMember} for writing to a HKX File. 13 | * 14 | * @see MemberDataResolver#fromMember(HKXMember) 15 | */ 16 | public class HKXDirectMemberHandler implements HKXMemberHandler { 17 | private final transient ByteBuffer outFile; 18 | private final transient long memberOffset; 19 | 20 | /** 21 | * Creates a {@link HKXDirectMemberHandler}. 22 | * 23 | * @param outFile the {@link ByteBuffer} to write to. 24 | * @param memberOffset the member offset in its class. 25 | */ 26 | HKXDirectMemberHandler(final ByteBuffer outFile, final long memberOffset) { 27 | this.outFile = outFile; 28 | this.memberOffset = memberOffset; 29 | } 30 | 31 | @Override 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public HKXMemberCallback write(final HKXMember member, final long currentPos) { 36 | byte[] value = MemberDataResolver.fromMember(member); 37 | ((Buffer) outFile).position((int) (currentPos + memberOffset)); 38 | outFile.put(value); 39 | return (memberCallbacks, position) -> { 40 | return 0; 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/object/PrimitiveSnap.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.object; 2 | 3 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 4 | 5 | /** 6 | * Contains all the primitive snap sizes 7 | */ 8 | final class PrimitiveSnap { 9 | private static final long PTR_SIZE = 0x08; 10 | 11 | private PrimitiveSnap() { 12 | // NO OP 13 | } 14 | 15 | /** 16 | * Get the snap sizes for all primitive types 17 | */ 18 | static long primitiveSnap(final HKXType type) { 19 | switch (type) { 20 | // Base values 21 | case TYPE_BOOL: 22 | case TYPE_CHAR: 23 | case TYPE_UINT8: 24 | case TYPE_INT8: 25 | return 0X01; 26 | case TYPE_HALF: 27 | case TYPE_UINT16: 28 | case TYPE_INT16: 29 | return 0X02; 30 | case TYPE_ULONG: 31 | case TYPE_UINT32: 32 | case TYPE_INT32: 33 | case TYPE_REAL: 34 | // Complex values 35 | case TYPE_VECTOR4: 36 | case TYPE_QUATERNION: 37 | case TYPE_TRANSFORM: 38 | case TYPE_QSTRANSFORM: 39 | case TYPE_MATRIX3: 40 | case TYPE_MATRIX4: 41 | // Relative Arrays 42 | case TYPE_RELARRAY: 43 | return 0x04; 44 | // Big base values 45 | case TYPE_UINT64: 46 | case TYPE_INT64: 47 | return 0X08; 48 | // Strings and ptrs 49 | case TYPE_CSTRING: 50 | case TYPE_STRINGPTR: 51 | case TYPE_FUNCTIONPOINTER: 52 | case TYPE_POINTER: 53 | // Arrays 54 | case TYPE_ARRAY: 55 | case TYPE_SIMPLEARRAY: 56 | return PTR_SIZE; 57 | case TYPE_NONE: 58 | case TYPE_VOID: 59 | default: 60 | return 0x00; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/PointerNameGenerator.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Generate a name based on a file position 8 | */ 9 | public class PointerNameGenerator { 10 | private static final int START_VALUE = 90; 11 | private static final int INCREMENT = 1; 12 | private transient int position; 13 | private final transient Map contents = new HashMap<>(); 14 | 15 | /** 16 | * Create a new {@link PointerNameGenerator}, starting at the 17 | * {@value #START_VALUE} value. 18 | */ 19 | PointerNameGenerator() { 20 | this(START_VALUE); 21 | } 22 | 23 | /** 24 | * Create a new {@link PointerNameGenerator}, with names starting at the given 25 | * value. 26 | * 27 | * @param startValue the starting values for names. 28 | */ 29 | public PointerNameGenerator(final int startValue) { 30 | this.position = startValue; 31 | } 32 | 33 | /** 34 | * Creates the next name 35 | * 36 | * @return 37 | */ 38 | private String createName() { 39 | int nameID = position; 40 | position += INCREMENT; 41 | return "#" + nameID; 42 | } 43 | 44 | /** 45 | * Get the name associated with a file position 46 | * 47 | * @param position 48 | * @return 49 | */ 50 | public String get(final long position) { 51 | if (contents.containsKey(position)) { 52 | return contents.get(position); 53 | } 54 | String name = createName(); 55 | contents.put(position, name); 56 | return name; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/arrays/HKXDirectArrayContentsReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member.arrays; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.dexesttp.hkxpack.data.HKXData; 6 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 7 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 8 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 9 | import com.dexesttp.hkxpack.hkx.types.MemberDataResolver; 10 | import com.dexesttp.hkxpack.hkx.types.MemberSizeResolver; 11 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 12 | 13 | /** 14 | * Reads a {@link HKXDirectMember} from an array. 15 | */ 16 | class HKXDirectArrayContentsReader implements HKXArrayContentsReader { 17 | private final transient HKXReaderConnector connector; 18 | private final transient HKXType contentType; 19 | 20 | HKXDirectArrayContentsReader(final HKXReaderConnector connector, final HKXType contentType) { 21 | this.connector = connector; 22 | this.contentType = contentType; 23 | } 24 | 25 | @Override 26 | /** 27 | * {@inheritDoc} 28 | */ 29 | public HKXData getContents(final long arrayStart, final int position) throws InvalidPositionException { 30 | final int contentSize = (int) MemberSizeResolver.getSize(contentType); 31 | byte[] bytesToRead = new byte[contentSize]; 32 | ByteBuffer file = connector.data.setup(arrayStart + position * contentSize); 33 | file.get(bytesToRead); 34 | return MemberDataResolver.getMember("", contentType, bytesToRead); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /doc/hkx findings/Studying __data1__ of BloatflyRootBehavior.txt: -------------------------------------------------------------------------------- 1 | val addr+4D0 content 2 | 00 00 00 00 @4D0 28 block (10 w/ 80) 3 | 10 00 00 00 @4E0 18 block 4 | 10 00 00 00 @4E0 18 block 5 | 28 00 00 00 @4F8 text (class) (18 block ?) 6 | 7 | 18 00 00 00 @4E8 10 block 8 | 40 00 00 00 @510 text (class?) 9 | 98 00 00 00 @5C8 >40 block (8 w/ 80) 10 | 10 02 00 00 @6E0 text (file) (40 block ?) (48 w/ 80) 11 | 12 | 68 02 00 00 @738 >40 block 13 | 80 03 00 00 @850 text (name) 14 | 00 03 00 00 @7D0 >40 block (10 w/ 80) 15 | 90 03 00 00 @860 >40 block (38 w/ 80) 16 | 17 | 00 04 00 00 @8D0 20 block (10 w/ 803F) 18 | 20 04 00 00 @8F0 text (name) 19 | 40 04 00 00 @910 >40 block (10 w/ 80) 20 | 50 04 00 00 @920 >40 block (38 w/ 80) 21 | 22 | 98 04 00 00 @968 >40 block 23 | B0 05 00 00 @A80 text (name) 24 | 30 05 00 00 @A00 >40 block (10 w/ 80) 25 | D0 05 00 00 @AA0 >40 block (38 w/ 80) 26 | 27 | 40 06 00 00 @B10 20 block (10 w/ 803F) 28 | 60 06 00 00 @B30 text (name) 29 | B8 06 00 00 @B88 >40 block 30 | 20 07 00 00 @BF0 text (name) 31 | 32 | 68 07 00 00 @C38 38 block (30 w/ 80) 33 | A0 07 00 00 @C70 text (name) 34 | 88 07 00 00 @C58 18 block (10 w/ 80) 35 | B0 07 00 00 @C80 >40 block 36 | 37 | 08 08 00 00 @CD8 >40 block (34 w/ 803F) 38 | 60 08 00 00 @D30 text (name) 39 | A8 08 00 00 @D78 38 block (30 w/ 80) 40 | E0 08 00 00 @DB0 text (name) 41 | 42 | 30 09 00 00 @E00 >40 block (10 w/ 80) 43 | 40 09 00 00 @E10 >40 block 44 | => Values counting from 00 to 16 45 | A8 09 00 00 @E78 >40 block (weird) (38 w/ 803F) 46 | 20 0A 00 00 @EF0 text (name) 47 | 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/HKXPointerMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXMember; 6 | import com.dexesttp.hkxpack.data.members.HKXPointerMember; 7 | import com.dexesttp.hkxpack.hkxwriter.object.callbacks.HKXMemberCallback; 8 | import com.dexesttp.hkxpack.hkxwriter.utils.PointerObject; 9 | 10 | /** 11 | * Handles a {@link HKXPointerMember} for writing to a HKX File 12 | */ 13 | public class HKXPointerMemberHandler implements HKXMemberHandler { 14 | private final transient long offset; 15 | private final transient List data2; 16 | 17 | /** 18 | * Creates a {@link HKXPointerMemberHandler}. 19 | * 20 | * @param offset the offset of the mmember in the class. 21 | * @param data2List the list of external pointers to put the pointer resolver 22 | * into. 23 | */ 24 | HKXPointerMemberHandler(final long offset, final List data2List) { 25 | this.offset = offset; 26 | this.data2 = data2List; 27 | } 28 | 29 | @Override 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | public HKXMemberCallback write(final HKXMember member, final long currentPos) { 34 | HKXPointerMember ptrMember = (HKXPointerMember) member; 35 | PointerObject ptrObject = new PointerObject(); 36 | ptrObject.from = currentPos + offset; 37 | ptrObject.to = ptrMember.get(); 38 | data2.add(ptrObject); 39 | return (callbacks, position) -> { 40 | return 0; 41 | }; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/tagreader/TagXMLReaderExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.xml.parsers.ParserConfigurationException; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.xml.sax.SAXException; 11 | 12 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 13 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 14 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 15 | import com.dexesttp.hkxpack.utils.FileUtils; 16 | 17 | /** 18 | * Tests for exception throwing in the TagXMLReader class 19 | */ 20 | public class TagXMLReaderExceptionTest { 21 | public static final String TEST_BASE_RESOURCE = "/test-base.xml"; 22 | private transient HKXDescriptorFactory descriptorFactory; 23 | 24 | @Before 25 | /** 26 | * Set up the TagXMLReader tests 27 | */ 28 | public void setUp() throws Exception { 29 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 30 | this.descriptorFactory = new HKXDescriptorFactory(enumResolver); 31 | } 32 | 33 | @Test 34 | /** 35 | * Test reading the file. 36 | * 37 | * @throws Exception 38 | */ 39 | public void baseFileReadingUsingFileDoesntThrowAnException() 40 | throws IOException, ParserConfigurationException, SAXException, InvalidTagXMLException { 41 | File toRead = FileUtils.resourceToTemporaryFile(TEST_BASE_RESOURCE); 42 | TagXMLReader reader = new TagXMLReader(toRead, descriptorFactory); 43 | reader.read(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/reader/ClassXMLReaderFactory.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor.reader; 2 | 3 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 4 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 5 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassListReadException; 6 | 7 | /** 8 | * Creates a {@link ClassXMLReader} from a 9 | * {@link com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory}. 10 | *

11 | * It is expected for the HKXDescriptorFactory to have the ClassXMLReader as 12 | * internal reader.
13 | * That means you must be doing something very technical to have to use a 14 | * ClassXMLReaderFactory yourself. 15 | */ 16 | public class ClassXMLReaderFactory { 17 | private final transient HKXEnumResolver enumResolver; 18 | 19 | /** 20 | * Create a new ClassXMLReaderFactory. 21 | * 22 | * @param enumResolver 23 | * @see ClassXMLReaderFactory 24 | */ 25 | public ClassXMLReaderFactory(final HKXEnumResolver enumResolver) { 26 | this.enumResolver = enumResolver; 27 | } 28 | 29 | /** 30 | * Create a {@link ClassXMLReader}, linked to a {@link HKXDescriptorFactory}. 31 | * 32 | * @param descriptor the {@link HKXDescriptorFactory} to use. 33 | * @return a new {@link ClassXMLReader} 34 | * @throws ClassListReadException 35 | */ 36 | public ClassXMLReader create(final HKXDescriptorFactory descriptor) throws ClassListReadException { 37 | ClassXMLList list = new ClassXMLList(); 38 | return new ClassXMLReader(descriptor, list, enumResolver); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/arrays/HKXObjectArrayContentsReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member.arrays; 2 | 3 | import com.dexesttp.hkxpack.data.HKXData; 4 | import com.dexesttp.hkxpack.data.HKXObject; 5 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 6 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 7 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 8 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 9 | import com.dexesttp.hkxpack.hkx.types.ObjectSizeResolver; 10 | import com.dexesttp.hkxpack.hkxreader.HKXObjectReader; 11 | 12 | /** 13 | * Reads an {@link HKXObject} from an array. 14 | */ 15 | class HKXObjectArrayContentsReader implements HKXArrayContentsReader { 16 | private final transient HKXObjectReader reader; 17 | private final transient HKXDescriptor descriptor; 18 | private final transient int contentSize; 19 | 20 | HKXObjectArrayContentsReader(final HKXObjectReader reader, final HKXDescriptorFactory descriptorFactory, 21 | final HKXDescriptor descriptor) throws ClassFileReadException { 22 | this.reader = reader; 23 | this.descriptor = descriptor; 24 | this.contentSize = (int) ObjectSizeResolver.getSize(descriptor, descriptorFactory); 25 | } 26 | 27 | @Override 28 | /** 29 | * {@inheritDoc} 30 | */ 31 | public HKXData getContents(final long arrayStart, final int position) throws InvalidPositionException { 32 | long contentsPos = arrayStart + position * contentSize; 33 | return reader.createHKXObject("", contentsPos, descriptor); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/HKXDescriptor.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 6 | 7 | /** 8 | * A HKXDescriptor contains a name, a flag and a list of members. 9 | */ 10 | public class HKXDescriptor { 11 | private final String name; 12 | private final long signature; 13 | private final transient List members; 14 | 15 | /** 16 | * Creates a HKXDescriptor 17 | *

18 | * Note : you should use {@link HKXDescriptorFactory#get(String)} to get a 19 | * HKXDescriptor, unless you're doing something very technical. 20 | * 21 | * @param name 22 | * @param signature 23 | * @param members 24 | */ 25 | public HKXDescriptor(final String name, final long signature, final List members) { 26 | this.name = name; 27 | this.signature = signature; 28 | this.members = members; 29 | } 30 | 31 | /** 32 | * Get the descriptor's name. 33 | * 34 | * @return the name, as a {@link String} 35 | */ 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | /** 41 | * Get the descriptor's signature. 42 | * 43 | * @return the signature, as a {@link long} 44 | */ 45 | public long getSignature() { 46 | return signature; 47 | } 48 | 49 | /** 50 | * Get a {@link List} of all the member's templates, as 51 | * {@link HKXMemberTemplate}. 52 | * 53 | * @return the list of members. 54 | */ 55 | public List getMemberTemplates() { 56 | return members; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/arrays/HKXPointerArrayContentsReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member.arrays; 2 | 3 | import com.dexesttp.hkxpack.data.HKXData; 4 | import com.dexesttp.hkxpack.data.members.HKXPointerMember; 5 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 6 | import com.dexesttp.hkxpack.hkx.data.DataExternal; 7 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 8 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 9 | import com.dexesttp.hkxpack.hkxreader.PointerNameGenerator; 10 | 11 | /** 12 | * Reads a {@link HKXPointerMember} from an array. 13 | */ 14 | public class HKXPointerArrayContentsReader implements HKXArrayContentsReader { 15 | private final transient PointerNameGenerator generator; 16 | private final transient HKXReaderConnector connector; 17 | 18 | HKXPointerArrayContentsReader(final HKXReaderConnector connector, final PointerNameGenerator generator) { 19 | this.connector = connector; 20 | this.generator = generator; 21 | } 22 | 23 | @Override 24 | /** 25 | * {@inheritDoc} 26 | */ 27 | public HKXData getContents(final long arrayStart, final int position) throws InvalidPositionException { 28 | long contentsPosition = arrayStart + position * 0x08; 29 | DataExternal data = connector.data2.readNext(); 30 | String target = "null"; 31 | if (data.from == contentsPosition) { 32 | target = generator.get(data.to); 33 | } else { 34 | connector.data2.backtrack(); 35 | } 36 | return new HKXPointerMember("", HKXType.TYPE_POINTER, HKXType.TYPE_NONE, target); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/array/HKXArrayPointerMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object.array; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXPointerMember; 6 | import com.dexesttp.hkxpack.hkx.data.DataExternal; 7 | import com.dexesttp.hkxpack.hkxwriter.utils.PointerObject; 8 | 9 | /** 10 | * Handle a {@link HKXPointerMember}'s array contents by deferring the position 11 | * and content value definition, and deferring its inclusion into the 12 | * {@link DataExternal} list. 13 | */ 14 | public class HKXArrayPointerMemberHandler { 15 | private final transient List data2; 16 | private transient PointerObject data2Instance; 17 | 18 | /** 19 | * Creates a {@link HKXArrayPointerMemberHandler} 20 | * 21 | * @param data2List the {@link DataExternal} list to write the Pointer resolver 22 | * to 23 | */ 24 | public HKXArrayPointerMemberHandler(final List data2List) { 25 | this.data2 = data2List; 26 | } 27 | 28 | /** 29 | * Set this pointer handler's value. 30 | * 31 | * @param internalPointer the {@link HKXPointerMember} to set. 32 | */ 33 | public void setPointer(final HKXPointerMember internalPointer) { 34 | data2Instance = new PointerObject(); 35 | data2Instance.to = internalPointer.get(); 36 | data2.add(data2Instance); 37 | } 38 | 39 | /** 40 | * Resolve the current pointer to a position. 41 | * 42 | * @param newPos 43 | */ 44 | public void resolve(final long newPos) { 45 | data2Instance.from = newPos; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/ObjectSizeResolver.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types; 2 | 3 | import com.dexesttp.hkxpack.data.HKXObject; 4 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 5 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 6 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 7 | import com.dexesttp.hkxpack.hkx.types.object.ObjectSize; 8 | 9 | /** 10 | * Resolve the size of a {@link HKXObject} or a {@link HKXDescriptor}. 11 | */ 12 | public final class ObjectSizeResolver { 13 | 14 | private ObjectSizeResolver() { 15 | // NO OP 16 | } 17 | 18 | /** 19 | * Retrieves the size of a {@link HKXDescriptor}, including end padding if 20 | * needed. 21 | * 22 | * @param descriptor the {@link HKXDescriptor} to retrieve the size from. 23 | * @return the {@link HKXDescriptor}'s size, in bytes. 24 | * @throws ClassFileReadException if there was an error resolving this 25 | * {@link HKXDescriptor}'s subclass 26 | */ 27 | public static long getSize(final HKXDescriptor descriptor, final HKXDescriptorFactory descriptorFactory) 28 | throws ClassFileReadException { 29 | return ObjectSize.getSize(descriptor, descriptorFactory); 30 | } 31 | 32 | /** 33 | * Retrieves the size of a {@link HKXObject}, including end padding if needed. 34 | * 35 | * @param object the {@link HKXObject} to retrieve the size from. 36 | * @return the {@link HKXObject}'s size, in bytes. 37 | */ 38 | public static long getSize(final HKXObject object) { 39 | return ObjectSize.getSize(object); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/serialized/TagXMLComplexSerializedHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader.serialized; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 4 | import com.dexesttp.hkxpack.data.members.HKXMember; 5 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 6 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 7 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 8 | 9 | /** 10 | * Handles generation of complex serialized members. 11 | */ 12 | class TagXMLComplexSerializedHandler implements TagXMLSerializedHandler { 13 | 14 | @Override 15 | /** 16 | * {@inheritDoc} 17 | */ 18 | public HKXMember handleMember(final HKXMemberTemplate memberTemplate) 19 | throws ClassFileReadException, InvalidTagXMLException { 20 | return emptyMember(memberTemplate); 21 | } 22 | 23 | private HKXMember emptyMember(final HKXMemberTemplate memberTemplate) { 24 | HKXDirectMember member = new HKXDirectMember<>(memberTemplate.name, memberTemplate.vtype); 25 | switch (memberTemplate.vtype) { 26 | case TYPE_VECTOR4: 27 | case TYPE_QUATERNION: 28 | member.set(new Double[] { 0., 0., 0., 0. }); 29 | break; 30 | case TYPE_MATRIX3: 31 | case TYPE_QSTRANSFORM: 32 | member.set(new Double[] { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. }); 33 | break; 34 | case TYPE_MATRIX4: 35 | case TYPE_TRANSFORM: 36 | member.set(new Double[] { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. }); 37 | break; 38 | default: 39 | break; 40 | } 41 | return member; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/data/DataInterface.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.data; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.Buffer; 5 | 6 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 7 | import com.dexesttp.hkxpack.hkx.header.SectionData; 8 | 9 | /** 10 | * Interface to connect to the Data section of the file. 11 | */ 12 | public class DataInterface { 13 | private transient ByteBuffer file; 14 | private transient SectionData header; 15 | 16 | /** 17 | * Connect this {@link DataInterface} to a {@link ByteBuffer}, using information 18 | * from the file's header. 19 | * 20 | * @param file the {@link ByteBuffer} to connect to. 21 | * @param dataHeader the {@link SectionData} information relative to the Data 22 | * sections. 23 | */ 24 | public void connect(final ByteBuffer file, final SectionData dataHeader) { 25 | this.file = file; 26 | this.header = dataHeader; 27 | } 28 | 29 | /** 30 | * Setup the file to a specific position. 31 | * 32 | * @param position the position to setup the file in Data at. 33 | * @return the {@link ByteBuffer}, at the given position in Data. 34 | * @throws InvalidPositionException if the position is outside the file's Data 35 | * definition. 36 | */ 37 | public ByteBuffer setup(final long position) throws InvalidPositionException { 38 | if (position < 0 || position > header.data1) { 39 | throw new InvalidPositionException("DATA", position); 40 | } 41 | ((Buffer) file).position((int) (header.offset + position)); 42 | return file; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/HKXDirectMemberReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXMember; 6 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 7 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 8 | import com.dexesttp.hkxpack.hkx.types.MemberDataResolver; 9 | import com.dexesttp.hkxpack.hkx.types.MemberSizeResolver; 10 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 11 | 12 | /** 13 | * Reads a direct or complex member from a HKX file. 14 | * 15 | * @see MemberDataResolver 16 | */ 17 | class HKXDirectMemberReader implements HKXMemberReader { 18 | private final transient HKXReaderConnector connector; 19 | private final transient String name; 20 | private final transient HKXType vtype; 21 | private final transient long memberOffset; 22 | 23 | HKXDirectMemberReader(final HKXReaderConnector connector, final String name, final HKXType contentType, 24 | final long offset) { 25 | this.connector = connector; 26 | this.name = name; 27 | this.vtype = contentType; 28 | this.memberOffset = offset; 29 | } 30 | 31 | @Override 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public HKXMember read(final long classOffset) throws InvalidPositionException { 36 | final int memberSize = (int) MemberSizeResolver.getSize(vtype); 37 | ByteBuffer file = connector.data.setup(classOffset + memberOffset); 38 | byte[] bytesToRead = new byte[memberSize]; 39 | file.get(bytesToRead); 40 | return MemberDataResolver.getMember(name, vtype, bytesToRead); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/callbacks/HKXBaseArrayMemberCallback.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object.callbacks; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.hkx.data.DataInternal; 6 | 7 | /** 8 | * The base array's callback. 9 | * 10 | * @see HKXArrayMemberCallback 11 | */ 12 | public class HKXBaseArrayMemberCallback implements HKXMemberCallback { 13 | private final transient HKXArrayMemberCallback callbackProcessor; 14 | private final transient List data1; 15 | private final transient DataInternal arrData; 16 | 17 | /** 18 | * Create a {@link HKXBaseArrayMemberCallback}. 19 | * 20 | * @param callbackProcessor the {@link HKXArrayMemberCallback} to use for each 21 | * array member. 22 | * @param data1 the {@link DataInternal} list to fill the array's 23 | * position into. 24 | * @param arrData the {@link DataInternal} prepared with the array 25 | * hook's position. 26 | */ 27 | public HKXBaseArrayMemberCallback(final HKXArrayMemberCallback callbackProcessor, final List data1, 28 | final DataInternal arrData) { 29 | this.callbackProcessor = callbackProcessor; 30 | this.data1 = data1; 31 | this.arrData = arrData; 32 | } 33 | 34 | @Override 35 | /** 36 | * {@inheritDoc} 37 | */ 38 | public long process(final List memberCallbacks, final long position) { 39 | arrData.to = position; 40 | data1.add(arrData); 41 | return callbackProcessor.process(memberCallbacks, position); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /doc/hkx findings/Studying animation- DeathChest01 .txt: -------------------------------------------------------------------------------- 1 | What we know (from Highflex's Havok2FBX tool) : 2 | 3 | - Animations conversion software use the following files to create the actual animation data : 4 | A skeleton 5 | An animation file 6 | 7 | - The output can be put in a FBX fle, which will then be self-sufficient. 8 | An FBX file contains : 9 | 10 | There is a number of members that can be related easily to each other : 11 | 12 | hkaSplineCompressedAnimation [#92]: 13 | Number of transform tracks <=> annotation tracks (size) 14 | Num frames <=> reference Frame samples (size) of target extractedMotion 15 | maxFramesPerBlock * frameDuration => blockDuration 16 | 1 / blockDuration => blockInverseDuration 17 | duration <=> blockDuration * numFrames / maxFramesPerBlock 18 | 19 | hkaAnimationBinding [#94]: 20 | Transform Track To Bone Indice (size) <=> number of Transform Tracks of target animation 21 | 22 | Compressed Spline : 23 | http://anarchy.cn/manual/12/HavokSdk_ProgrammersManual/animationcompression.html 24 | B-Splines, or NURBS, are a way to store data in "knots". In addition to this storage, there is a way to remove some of the nodes and replace it by extra data less costly in weight. There is also a general compression algorithm. 25 | => see also : http://dsanta.users.ch/research/These-2770-SantaCruz.pdf (thesis on B-Spline compression) 26 | => the algorithm may be protected behind some patent or commercial secret. In this case, it may not be possible to write an open-source converter. 27 | => There is also the posibility that the algorithm is publicly available, through research papers. 28 | => Thanks to Aerisam for his help -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/commands/Command_quick.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.commands; 2 | 3 | import java.io.File; 4 | import java.util.logging.Logger; 5 | 6 | import com.dexesttp.hkxpack.cli.ConsoleView; 7 | 8 | /** 9 | * Routes an unknown command to either a help, pack or unpack command. 10 | */ 11 | public class Command_quick implements Command { 12 | private static final Logger LOGGER = Logger.getLogger(ConsoleView.class.getName()); 13 | private static final int MINIMUM_PARAMETERS_COUNT = 1; 14 | 15 | @Override 16 | /** 17 | * {@inheritDoc} 18 | */ 19 | public int execute(final String... parameters) { 20 | if (parameters.length > MINIMUM_PARAMETERS_COUNT) { 21 | return new Command_help().execute(parameters); 22 | } 23 | Command command = null; 24 | String inName = parameters[0]; 25 | File inFile = new File(inName); 26 | String name = inFile.getName(); 27 | String commandArg = ""; 28 | try { 29 | if (!inFile.isFile()) { 30 | throw new IllegalArgumentException("The given parameter isn't a HKX or XML file."); 31 | } 32 | String ext = name.substring(name.lastIndexOf('.') + 1); 33 | if (ext.equals("hkx")) { 34 | command = new Command_unpack(); 35 | commandArg = "unpack"; 36 | } else if (ext.equals("xml")) { 37 | command = new Command_pack(); 38 | commandArg = "pack"; 39 | } else { 40 | throw new IllegalArgumentException("Unsupported file type."); 41 | } 42 | } catch (IllegalArgumentException e) { 43 | LOGGER.throwing(this.getClass().getName(), "execute", e); 44 | return 1; 45 | } 46 | return command.execute(commandArg, inName); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/tagreader/TagXMLReaderExternalResource.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.xml.parsers.ParserConfigurationException; 7 | 8 | import org.xml.sax.SAXException; 9 | 10 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 11 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 12 | import com.dexesttp.hkxpack.files.ReaderExternalResource; 13 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 14 | import com.dexesttp.hkxpack.utils.FileUtils; 15 | 16 | /** 17 | * External resource handler for the TagXML file. 18 | */ 19 | public class TagXMLReaderExternalResource extends ReaderExternalResource { 20 | private final transient String baseFileResourceName; 21 | 22 | /** 23 | * Creates a {@link TagXMLReaderExternalResource} 24 | * 25 | * @param baseFileResourceName the resource file's name, without its extension 26 | */ 27 | public TagXMLReaderExternalResource(final String baseFileResourceName) { 28 | super(); 29 | this.baseFileResourceName = baseFileResourceName; 30 | } 31 | 32 | /** 33 | * Set up the resource as the {@link #file} member 34 | */ 35 | @Override 36 | public void before() throws IOException, ParserConfigurationException, SAXException, InvalidTagXMLException { 37 | File baseFile = FileUtils.resourceToTemporaryFile(baseFileResourceName + ".xml"); 38 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 39 | HKXDescriptorFactory descriptorFactory = new HKXDescriptorFactory(enumResolver); 40 | TagXMLReader reader = new TagXMLReader(baseFile, descriptorFactory); 41 | file = reader.read(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/arrays/HKXStringArrayContentsReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member.arrays; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.dexesttp.hkxpack.data.HKXData; 6 | import com.dexesttp.hkxpack.data.members.HKXStringMember; 7 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 8 | import com.dexesttp.hkxpack.hkx.data.DataInternal; 9 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 10 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 11 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 12 | 13 | /** 14 | * Reads a {@link HKXStringMember} from an array 15 | */ 16 | class HKXStringArrayContentsReader implements HKXArrayContentsReader { 17 | private final transient HKXReaderConnector connector; 18 | private final transient HKXType contentsType; 19 | 20 | HKXStringArrayContentsReader(final HKXReaderConnector connector, final HKXType contentsType) { 21 | this.connector = connector; 22 | this.contentsType = contentsType; 23 | } 24 | 25 | @Override 26 | /** 27 | * {@inheritDoc} 28 | */ 29 | public HKXData getContents(final long arrayStart, final int position) throws InvalidPositionException { 30 | long descriptorPosition = arrayStart + position * 0x08; 31 | DataInternal data = connector.data1.readNext(); 32 | String contents = ""; 33 | if (data.from == descriptorPosition) { 34 | ByteBuffer file = connector.data.setup(data.to); 35 | contents = ByteUtils.readString(file); 36 | } else { 37 | connector.data1.backtrack(); 38 | } 39 | HKXStringMember result = new HKXStringMember("", contentsType); 40 | result.set(contents); 41 | return result; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/handlers/MemberHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.handlers; 2 | 3 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 4 | 5 | /** 6 | * Creates a MemberHandler 7 | */ 8 | public final class MemberHandlerFactory { 9 | private MemberHandlerFactory() { 10 | // NO OP 11 | } 12 | 13 | /** 14 | * Get a {@link MemberHandler} from a {@link HKXType}. 15 | * 16 | * @param type the {@link HKXType}. 17 | * @return the {@link MemberHandler}. 18 | */ 19 | public static MemberHandler getMemberHandler(final HKXType type) { 20 | switch (type) { 21 | // Base values 22 | case TYPE_BOOL: 23 | case TYPE_CHAR: 24 | case TYPE_UINT8: 25 | case TYPE_INT8: 26 | return SmallMemberHandlers.createMemberHandler(type); 27 | case TYPE_UINT16: 28 | case TYPE_INT16: 29 | return MediumMemberHandlers.createMemberHandler(type); 30 | case TYPE_INT32: 31 | case TYPE_UINT32: 32 | return NormalMemberHandlers.createMemberHandler(type); 33 | case TYPE_ULONG: 34 | case TYPE_UINT64: 35 | case TYPE_INT64: 36 | return BigMemberHandlers.createMemberHandler(type); 37 | case TYPE_HALF: 38 | case TYPE_REAL: 39 | return RealMemberHandlers.createMemberHandler(type); 40 | // Complex values 41 | case TYPE_VECTOR4: 42 | case TYPE_QUATERNION: 43 | return new Vector4Handler(); 44 | case TYPE_MATRIX3: 45 | case TYPE_QSTRANSFORM: 46 | return new Matrix3Handler(); 47 | case TYPE_MATRIX4: 48 | case TYPE_TRANSFORM: 49 | return new Matrix4Handler(); 50 | // Default 51 | default: 52 | break; 53 | } 54 | throw new IllegalArgumentException(type + " can't be analyzed with MemberTypeResolver#getMember"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cli/src/test/java/com/dexesttp/hkxpack/cli/utils/ArgsParserEmptyTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.utils; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import com.dexesttp.hkxpack.cli.utils.ArgsParser.Options; 9 | 10 | /** 11 | * Tests for the {@link ArgsParser}, on empty argument list 12 | */ 13 | public class ArgsParserEmptyTest { 14 | private transient ArgsParser argsParser; 15 | 16 | @Before 17 | /** 18 | * {@inheritDoc} 19 | */ 20 | public void setUp() { 21 | argsParser = new ArgsParser(); 22 | argsParser.addOption("-a"); 23 | argsParser.addOption("-b", 0); 24 | argsParser.addOption("-c", 1); 25 | } 26 | 27 | @Test 28 | /** 29 | * {@inheritDoc} 30 | */ 31 | public void itShouldNotCatchSpecialOptionsIfThereIsNoArgument() throws WrongSizeException { 32 | Options result = argsParser.parse(""); 33 | assertEquals(1, result.size()); 34 | } 35 | 36 | @Test 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | public void itShouldReturnAnEmptyStringForARequestThatDoesNotExist1() throws WrongSizeException { 41 | Options result = argsParser.parse(""); 42 | assertEquals("", result.get("", 1)); 43 | } 44 | 45 | @Test 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | public void itShouldReturnAnEmptyStringForARequestThatDoesNotExist2() throws WrongSizeException { 50 | Options result = argsParser.parse(""); 51 | assertEquals("", result.get("-a", 0)); 52 | } 53 | 54 | @Test 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | public void itShouldReturnAnEmptyStringForARequestThatDoesNotExist3() throws WrongSizeException { 59 | Options result = argsParser.parse(""); 60 | assertEquals("", result.get("-a", 1)); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/data/members/HKXDirectMember.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.data.members; 2 | 3 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 4 | 5 | /** 6 | * Stores all basic data found as members of an object. 7 | * 8 | * @param the Java type of the stored data. It may be a boxed type, a custom 9 | * object or an array. 10 | */ 11 | public class HKXDirectMember implements HKXMember { 12 | private final HKXType type; 13 | private final String name; 14 | private transient T value; 15 | 16 | /** 17 | * Creates a {@link HKXDirectMember}. 18 | * 19 | * @param name the name of the {@link HKXDirectMember}. 20 | * @param type the type of the {@link HKXDirectMember}, as {@link HKXType}. Note 21 | * that {@link HKXType#getFamily()} should return either 22 | * {@link HKXTypeFamily#DIRECT} or {@link HKXTypeFamily#COMPLEX}, 23 | * although this isn't checked. 24 | */ 25 | public HKXDirectMember(final String name, final HKXType type) { 26 | this.name = name; 27 | this.type = type; 28 | } 29 | 30 | /** 31 | * Set this {@link HKXDirectMember}'s value. 32 | * 33 | * @param value the value to set the member's content to. 34 | */ 35 | public void set(final T value) { 36 | this.value = value; 37 | } 38 | 39 | /** 40 | * Get this {@link HKXDirectMember}'s value. 41 | * 42 | * @return the requested value. 43 | */ 44 | public T get() { 45 | return value; 46 | } 47 | 48 | @Override 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | public String getName() { 53 | return name; 54 | } 55 | 56 | @Override 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | public HKXType getType() { 61 | return type; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /doc/hkx findings/studying __data3__ of Bloatfly.txt: -------------------------------------------------------------------------------- 1 | Value also seems to be ordered by 3 2 | 00 00 00 00 00 3 | 00 00 00 00 (standard 00 ?) 4 | 4B 00 00 00 #4B => @1730 text (pelvisIndex) 5 | @4B+4D0 @51B middle of text 6 | @4B+100 @14B text (hkRootLevelContainer) 7 | 60 00 00 00 #60 => @1DB8 18-block w/ FF 8 | @60+4D0 @530 28-block w/ 80 9 | @60+100 @160 Class UUID (hkbBehaviorGraph) 10 | 00 00 00 00 (standard 00 ?) 11 | 65 00 00 00 #65 => @1610 30-block 12 | @65+100 @165 text (hkbBehaviorGraph) 13 | 30 02 00 00 @230+4D0 28-bloc w/ 80 14 | 00 00 00 00 (standard) 15 | 16 | 7B 00 00 00 @7B+100 @17B hkbStateMachine 17 | A0 03 00 00 @3A0+4D0 28-bloc w/ 80 18 | 00 00 00 00 19 | 90 00 00 00 @190 20 | 21 | 30 04 00 00 22 | 00 00 00 00 23 | AE 00 00 00 @1AE 24 | 60 04 00 00 25 | 26 | 00 00 00 00 27 | 7B 00 00 00 @17B 28 | E0 05 00 00 29 | 00 00 00 00 30 | 31 | 90 00 00 00 @190 32 | 80 06 00 00 33 | 00 00 00 00 34 | D5 00 00 00 @1D5 35 | 36 | 30 07 00 00 @730+4D0 28-block w/ 80 37 | 00 00 00 00 38 | EF 00 00 00 @1EF 39 | D0 07 00 00 @7D0+4D0 28-block w/ 80 40 | 41 | 00 00 00 00 42 | 04 01 00 00 @204 hkbGetUpModifier 43 | 70 08 00 00 @870+1D0 28-block w/ 80 44 | 00 00 00 00 45 | 46 | ... 47 | 81 03 00 00 @481 hkbBehaviorGraphData 48 | 49 | 40 3D 00 00 @3D40+4D0 20-block w/ 80 AND DATA 50 | 00 00 00 00 51 | 9B 03 00 00 @49B hkbVariableValueSet 52 | 53 | 90 3E 00 00 @3E90+4D0 20-block w/ 80 AND DATA 54 | 00 00 00 00 55 | B4 03 00 00 @4B4 hkbBehaviorGraphStringData 56 | 57 | Offsets in file ? 58 | >0 - 0 - 4B 59 | >60 - 0 - 65 60 | >3D40 - 0 - 039B 61 | => @3D40+4D0 @4210 62 | Only 0's (not illogical given there is no AttNames, believed to be second to last values in file) 63 | => @039B+4210 @ 64 | >3E90 - 0 - 03B4 65 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/reader/ClassXMLList.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor.reader; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.net.URL; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassListReadException; 11 | 12 | /** 13 | * List of all the available ClassXML resources. 14 | */ 15 | class ClassXMLList { 16 | private static final String CLASS_RESOURCES_LIST = "/properties/classxmllist.txt"; 17 | public final transient Map filenameConverter = new HashMap(); 18 | 19 | ClassXMLList() throws ClassListReadException { 20 | try { 21 | readEntries(); 22 | } catch (IOException e) { 23 | throw new ClassListReadException(e); 24 | } 25 | } 26 | 27 | private void readEntries() throws IOException { 28 | URL paths = ClassXMLList.class.getResource(CLASS_RESOURCES_LIST); 29 | BufferedReader reader = new BufferedReader(new InputStreamReader(paths.openStream())); 30 | String fileEntry = reader.readLine(); 31 | while (fileEntry != null) { 32 | String className = extractName(fileEntry); 33 | filenameConverter.put(className, "/classxml/" + fileEntry); 34 | fileEntry = reader.readLine(); 35 | } 36 | } 37 | 38 | /** 39 | * Retrieve a filename from the class name 40 | * 41 | * @param classname the class name 42 | * @return the file name to retrieve data from 43 | */ 44 | String getFileName(final String classname) { 45 | return filenameConverter.get(classname); 46 | } 47 | 48 | private String extractName(final String fullName) { 49 | return fullName.substring(0, fullName.indexOf('_')); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/callbacks/HKXPointerArrayMemberCallback.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object.callbacks; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXArrayMember; 6 | import com.dexesttp.hkxpack.data.members.HKXPointerMember; 7 | import com.dexesttp.hkxpack.hkx.HKXUtils; 8 | import com.dexesttp.hkxpack.hkx.types.MemberSizeResolver; 9 | import com.dexesttp.hkxpack.hkxwriter.object.array.HKXArrayPointerMemberHandler; 10 | 11 | /** 12 | * Handles callbacks for {@link HKXPointerMember}'s array components. 13 | */ 14 | public class HKXPointerArrayMemberCallback implements HKXArrayMemberCallback { 15 | private final transient HKXArrayMember arrMember; 16 | private final transient List apmhList; 17 | 18 | /** 19 | * Creates a {@link HKXPointerArrayMemberCallback} 20 | * 21 | * @param arrMember the {@link HKXArrayMember} the callback is for 22 | * @param apmhList the {@link HKXArrayPointerMemberHandler} to base the 23 | * callback on. 24 | */ 25 | public HKXPointerArrayMemberCallback(final HKXArrayMember arrMember, 26 | final List apmhList) { 27 | this.arrMember = arrMember; 28 | this.apmhList = apmhList; 29 | } 30 | 31 | @Override 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public long process(final List memberCallbacks, final long position) { 36 | long newPos = position; 37 | for (HKXArrayPointerMemberHandler apmh : apmhList) { 38 | long objectSize = MemberSizeResolver.getSize(arrMember.getSubType()); 39 | apmh.resolve(newPos); 40 | newPos += objectSize; 41 | } 42 | return HKXUtils.snapLine(newPos) - position; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/classnames/HKXClassnamesHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.classnames; 2 | 3 | import com.dexesttp.hkxpack.data.HKXFile; 4 | import com.dexesttp.hkxpack.data.HKXObject; 5 | import com.dexesttp.hkxpack.hkx.classnames.ClassnamesData; 6 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 7 | 8 | /** 9 | * Creates a {@link ClassnamesData} from a {@link HKXFile}. 10 | */ 11 | public class HKXClassnamesHandler { 12 | private static final long HK_CLASS_ID = 0x33D42383; 13 | private static final long HK_CLASS_MEMBER_ID = 0xB0EFA719; 14 | private static final long HK_CLASS_ENUM_ID = 0x8A3609CF; 15 | private static final long HK_CLASS_ENUM_ITEM_ID = 0xCE6F8A6C; 16 | 17 | /** 18 | * Creates a {@link ClassnamesData} instance from the given {@link HKXFile}. 19 | * 20 | * @param file the {@link HKXFile} to extract data from. 21 | * @return the relevant {@link ClassnamesData}. 22 | */ 23 | public ClassnamesData getClassnames(final HKXFile file) { 24 | ClassnamesData data = new ClassnamesData(); 25 | data.put(5, "hkClass", ByteUtils.fromULong(HK_CLASS_ID, 4)); 26 | data.put(18, "hkClassMember", ByteUtils.fromULong(HK_CLASS_MEMBER_ID, 4)); 27 | data.put(37, "hkClassEnum", ByteUtils.fromULong(HK_CLASS_ENUM_ID, 4)); 28 | data.put(54, "hkClassEnumItem", ByteUtils.fromULong(HK_CLASS_ENUM_ITEM_ID, 4)); 29 | int i = 75; 30 | for (HKXObject object : file.getContentCollection()) { 31 | if (!data.containsClass(object.getDescriptor().getName())) { 32 | data.put(i, object.getDescriptor().getName(), 33 | ByteUtils.fromULong(object.getDescriptor().getSignature(), 4)); 34 | i += object.getDescriptor().getName().length() + 0x06; 35 | } 36 | } 37 | return data; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/HKXPointerMemberReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXMember; 4 | import com.dexesttp.hkxpack.data.members.HKXPointerMember; 5 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 6 | import com.dexesttp.hkxpack.hkx.data.DataExternal; 7 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 8 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 9 | import com.dexesttp.hkxpack.hkxreader.PointerNameGenerator; 10 | 11 | /** 12 | * Reads a {@link HKXPointerMember} from a HKX file. 13 | */ 14 | class HKXPointerMemberReader implements HKXMemberReader { 15 | private final transient HKXReaderConnector connector; 16 | private final transient PointerNameGenerator generator; 17 | private final transient String name; 18 | private final transient HKXType vtype; 19 | private final transient long memberOffset; 20 | 21 | HKXPointerMemberReader(final HKXReaderConnector connector, final PointerNameGenerator generator, final String name, 22 | final HKXType contentType, final long offset) { 23 | this.connector = connector; 24 | this.generator = generator; 25 | this.name = name; 26 | this.vtype = contentType; 27 | this.memberOffset = offset; 28 | } 29 | 30 | @Override 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | public HKXMember read(final long classOffset) throws InvalidPositionException { 35 | DataExternal data = connector.data2.readNext(); 36 | String target = "null"; 37 | if (data.from == memberOffset + classOffset) { 38 | target = generator.get(data.to); 39 | } else { 40 | connector.data2.backtrack(); 41 | } 42 | return new HKXPointerMember(name, HKXType.TYPE_POINTER, vtype, target); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/MemberDataResolver.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXMember; 4 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 5 | import com.dexesttp.hkxpack.hkx.types.handlers.MemberHandlerFactory; 6 | 7 | /** 8 | * Intended to retrieve {@link HKXType}-specific data. 9 | * {@link #getMember(String, HKXType, byte[])} converts a {@link byte} array to 10 | * a {@link HKXMember}, given the {@link HKXType} of the member is standard. 11 | */ 12 | public final class MemberDataResolver { 13 | private MemberDataResolver() { 14 | // NO OP 15 | } 16 | 17 | /** 18 | * Read a simple / defined member from a byte array. 19 | * 20 | * @param name the name of the member to create. 21 | * @param type the {@link HKXType} of data to convert the array into. 22 | * @param byteArray the {@link byte} array to read the member from. 23 | * @return the {@link HKXMember} containing the data. 24 | * @throws IllegalArgumentException if the given {@link HKXType} isn't standard. 25 | */ 26 | public static HKXMember getMember(final String name, final HKXType type, final byte[] byteArray) { 27 | return MemberHandlerFactory.getMemberHandler(type).createMember(name, type, byteArray); 28 | } 29 | 30 | /** 31 | * Write a simple / defined member to a byte array. 32 | * 33 | * @param member the {@link HKXMember} of data to create the array from. 34 | * @return the byte aray containing the data. 35 | * @throws IllegalArgumentException if the given {@link HKXMember} isn't 36 | * standard. 37 | */ 38 | public static byte[] fromMember(final HKXMember member) { 39 | return MemberHandlerFactory.getMemberHandler(member.getType()).readMember(member); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/HKXUtils.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx; 2 | 3 | /** 4 | * Contain utils for the HKX classes. 5 | */ 6 | public final class HKXUtils { 7 | private static final long HALF_LINE = 0x08; 8 | private static final long FULL_LINE = 0x10; 9 | 10 | private HKXUtils() { 11 | // NO OP 12 | } 13 | 14 | /** 15 | * Snap the object's position to the relevant byte. 16 | * 17 | * @param currentPos 18 | * @return 19 | */ 20 | public static long snapObject(final long currentPos) { 21 | if (currentPos % FULL_LINE == 0) { 22 | return currentPos; 23 | } 24 | return (1 + currentPos / FULL_LINE) * FULL_LINE; 25 | } 26 | 27 | /** 28 | * Snap the given position to the next line. 29 | * 30 | * @param currentPos 31 | * @return 32 | */ 33 | public static long snapLine(final long currentPos) { 34 | if (currentPos % FULL_LINE == 0) { 35 | return currentPos; 36 | } 37 | return (1 + currentPos / FULL_LINE) * FULL_LINE; 38 | } 39 | 40 | /** 41 | * Snap the string's size to the relevant size. 42 | * 43 | * @param currentSize the string size. 44 | * @return the snapped size. 45 | */ 46 | public static long snapString(final long currentSize) { 47 | if (currentSize < HALF_LINE) { 48 | return HALF_LINE; 49 | } 50 | if (currentSize % FULL_LINE == 0) { 51 | return currentSize; 52 | } 53 | return (1 + currentSize / FULL_LINE) * FULL_LINE; 54 | } 55 | 56 | /** 57 | * Snap a size to the given snap and offset to the given offset. 58 | * 59 | * @param offset the offset to snap 60 | * @param snap the snap. 61 | * @return 62 | */ 63 | public static long snapSize(final long offset, final long snap) { 64 | if (offset % snap == 0) { 65 | return offset; 66 | } 67 | return (1 + offset / snap) * snap; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/utils/StaticProperties.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Properties; 6 | 7 | /** 8 | * Contains a list of static final properties. 9 | */ 10 | public final class StaticProperties { 11 | private static final String VERSION_NUMBER; 12 | 13 | static { 14 | Properties prop = new Properties(); 15 | String vNum = ""; 16 | try { 17 | InputStream in = StaticProperties.class 18 | .getResourceAsStream("/META-INF/maven/com.dexesttp.hkxpack/cli/pom.properties"); 19 | prop.load(in); 20 | in.close(); 21 | vNum = prop.getProperty("version"); 22 | } catch (IOException e) { 23 | vNum = "error loading version number"; 24 | } 25 | VERSION_NUMBER = vNum; 26 | } 27 | 28 | private StaticProperties() { 29 | // NO OP 30 | } 31 | 32 | /** 33 | *

34 | 	 * Versioning convention:
35 | 	 * 1st digit : main version identifier
36 | 	 * * Supposed to be used in case of a huge fonctionnality change.
37 | 	 * * A change shows a compatibility loss with previous versions.
38 | 	 * 
39 | 	 * 2nd digit : version iteration
40 | 	 * * Supposed to be used when a new functionnality set is added.
41 | 	 * 
42 | 	 * 3rd digit : version state
43 | 	 * * Even = stable, odd = unstable
44 | 	 * * Changed each time a functionnality is added for release.
45 | 	 * 
46 | 	 * Hyphen : state identifier
47 | 	 * * Either alpha|beta|theta|gamma or any state identifier (unstable, snapshot, etc.. works)
48 | 	 * * Linguistic representation of the 1st, 2nd and 3rd digit.
49 | 	 * * Doesn't exist with stable releases (no -HOTFIX or similar, this goes into the merge data or changelog !)
50 | 	 * 
51 | */ 52 | public static String getVersionNumber() { 53 | return VERSION_NUMBER; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cli/src/test/java/com/dexesttp/hkxpack/cli/utils/ArgsParserExceptionTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.utils; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | /** 7 | * Test the behavior of thrown exceptions from {@link ArgsParser}. 8 | */ 9 | public class ArgsParserExceptionTest { 10 | private transient ArgsParser argsParser; 11 | 12 | @Before 13 | /** 14 | * {@inheritDoc} 15 | */ 16 | public void setUp() { 17 | argsParser = new ArgsParser(); 18 | argsParser.addOption("-a"); 19 | argsParser.addOption("-b", 0); 20 | argsParser.addOption("-c", 1); 21 | } 22 | 23 | @Test(expected = WrongSizeException.class) 24 | /** 25 | * {@inheritDoc} 26 | */ 27 | public void itShouldThrowAnExceptionIfThereWasNotEnoughArgumentForASizedOptionWithoutAnythingAfter() 28 | throws WrongSizeException { 29 | argsParser.parse(ArgsParserTest.TEST, "-c"); 30 | } 31 | 32 | @Test(expected = WrongSizeException.class) 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | public void itShouldThrowAnExceptionIfThereWasNotEnoughArgumentForASizedOptionWithThingsAfter() 37 | throws WrongSizeException { 38 | argsParser.parse(ArgsParserTest.TEST, "-c", "-b", ArgsParserTest.TEST2); 39 | } 40 | 41 | @Test(expected = WrongSizeException.class) 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | public void itShouldThrowAnExceptionIfThereWasAnOtherAppearanceOfASizedOption() throws WrongSizeException { 46 | argsParser.parse(ArgsParserTest.TEST, "-c", ArgsParserTest.TEST2, "-c", ArgsParserTest.TEST3); 47 | } 48 | 49 | @Test(expected = WrongSizeException.class) 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public void itShouldHandleAComplexIncorrectCommandLine() throws WrongSizeException { 54 | argsParser.parse(ArgsParserTest.TEST, "-a", ArgsParserTest.TEST2, "-b", ArgsParserTest.TEST3, 55 | ArgsParserTest.TEST4, "-c"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/classnames/ClassnamesData.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.classnames; 2 | 3 | import java.util.LinkedHashMap; 4 | 5 | /** 6 | * Stores all the ClassNames from a file or intended to be stored in a file. 7 | */ 8 | public class ClassnamesData extends LinkedHashMap { 9 | private static final long serialVersionUID = 5525421716171216039L; 10 | 11 | /** 12 | * Add a classname to this ClassnamesData 13 | * 14 | * @param position the position (in {@link bytes}) the classname whould be found 15 | * at. 16 | * @param name the name of the classname to add. 17 | * @param uuid the UUID of the classname to add. 18 | * @return the added {@link Classname} object. 19 | */ 20 | public Classname put(final long position, final String name, final byte[] uuid) { 21 | return super.put(position, new Classname(name, uuid)); 22 | } 23 | 24 | /** 25 | * Returns {@link true} if the {@link ClassnamesData} contains the given 26 | * classname. 27 | * 28 | * @param name the classname to check the existence of. 29 | * @return the existence of the classname. 30 | */ 31 | public boolean containsClass(final String name) { 32 | for (Classname classname : this.values()) { 33 | if (classname.name.equals(name)) { 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | /** 41 | * Retrieves the position of the given classname, or {@literal 0x05} if no 42 | * position was found. 43 | * 44 | * @param name the name to check against. 45 | * @return the classname's position in the {@literal __classnames__} section 46 | */ 47 | public long getPosition(final String name) { 48 | for (java.util.Map.Entry entries : this.entrySet()) { 49 | if (entries.getValue().name.equals(name)) { 50 | return entries.getKey(); 51 | } 52 | } 53 | return 0x05; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cli/src/test/java/com/dexesttp/hkxpack/cli/utils/ArgsParserExistTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.utils; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertNull; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import com.dexesttp.hkxpack.cli.utils.ArgsParser.Options; 11 | 12 | /** 13 | * Tests the behavior of {@link ArgsParser.Options#exists(String)} 14 | */ 15 | public class ArgsParserExistTest { 16 | private transient ArgsParser argsParser; 17 | 18 | @Before 19 | /** 20 | * {@inheritDoc} 21 | */ 22 | public void setUp() { 23 | argsParser = new ArgsParser(); 24 | argsParser.addOption("-a"); 25 | argsParser.addOption("-b", 0); 26 | argsParser.addOption("-c", 1); 27 | } 28 | 29 | @Test 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | public void itShouldNotCatchOneExtraEmptyOptionIfThereIsNoExtraOption() throws WrongSizeException { 34 | Options result = argsParser.parse(ArgsParserTest.TEST, "-a"); 35 | assertNull(result.get("-b")); 36 | } 37 | 38 | @Test 39 | /** 40 | * {@inheritDoc} 41 | */ 42 | public void itShouldShowTheOptionAsExistingIfTheOptionExists() throws WrongSizeException { 43 | Options result = argsParser.parse(ArgsParserTest.TEST, "-a"); 44 | assertTrue(result.exists("-a")); 45 | } 46 | 47 | @Test 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | public void itShouldShowTheOptionAsNotExistingIfTheOptionDoesNotExists() throws WrongSizeException { 52 | Options result = argsParser.parse(ArgsParserTest.TEST, "-a"); 53 | assertFalse(result.exists("-b")); 54 | } 55 | 56 | @Test 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | public void itShouldShowTheOptionAsNotExistingIfTheOptionIsNotDefined() throws WrongSizeException { 61 | Options result = argsParser.parse(ArgsParserTest.TEST, "-a"); 62 | assertFalse(result.exists("-d")); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /cli/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.dexesttp 8 | hkxpack 9 | 0.1.6-beta 10 | 11 | 12 | com.dexesttp.hkxpack 13 | cli 14 | 15 | HKXPack Command Line Interface 16 | Command Line interface for HKX Pack 17 | 18 | 19 | 20 | com.dexesttp.hkxpack 21 | core 22 | ${project.version} 23 | 24 | 25 | junit 26 | junit 27 | 4.12 28 | test 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.apache.maven.plugins 36 | maven-shade-plugin 37 | 2.4.3 38 | 39 | 40 | 42 | com.dexesttp.hkxpack.cli.ConsoleView 43 | 44 | 45 | false 46 | 47 | 48 | 49 | package 50 | 51 | shade 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/header/SectionData.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.header; 2 | 3 | /** 4 | * Contains information about a given section of the file. 5 | */ 6 | public class SectionData { 7 | /** 8 | * Name of the section Supported sections are __classnames__, __types__ and 9 | * __data__ 10 | */ 11 | public String name; 12 | 13 | /** 14 | * Offset of the section in the file. 15 | *

16 | * Note that this is a general offset, from the beginning of the file. 17 | */ 18 | public long offset; 19 | 20 | /** 21 | * Internal offset of the file's first extra data component. 22 | *

23 | * Note that this is an internal offset, based on the general {@link offset} of 24 | * the section. 25 | */ 26 | public long data1; 27 | 28 | /** 29 | * Internal offset of the file's second extra data component. 30 | *

31 | * Note that this is an internal offset, based on the general {@link offset} of 32 | * the section. 33 | */ 34 | public long data2; 35 | 36 | /** 37 | * Internal offset of the file's third extra data component. 38 | *

39 | * Note that this is an internal offset, based on the general {@link offset} of 40 | * the section. 41 | */ 42 | public long data3; 43 | 44 | /** 45 | * Internal offset of the file's fourth extra data component. 46 | *

47 | * Note that this is an internal offset, based on the general {@link offset} of 48 | * the section. 49 | */ 50 | public long data4; 51 | 52 | /** 53 | * Internal offset of the file's fifth extra data component. 54 | *

55 | * Note that this is an internal offset, based on the general {@link offset} of 56 | * the section. 57 | */ 58 | public long data5; 59 | 60 | /** 61 | * Internal offset of the file's section end. 62 | *

63 | * Note that this is an internal offset, based on the general {@link offset} of 64 | * the section. 65 | */ 66 | public long end; 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/HKXStringMemberReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXMember; 6 | import com.dexesttp.hkxpack.data.members.HKXStringMember; 7 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 8 | import com.dexesttp.hkxpack.hkx.data.DataInternal; 9 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 10 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 11 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 12 | 13 | /** 14 | * Reads a {@link HKXStringMember} from a HKX file. 15 | */ 16 | class HKXStringMemberReader implements HKXMemberReader { 17 | private final transient HKXReaderConnector connector; 18 | private final transient String name; 19 | private final transient long memberOffset; 20 | private final transient HKXType vtype; 21 | 22 | HKXStringMemberReader(final HKXReaderConnector connector, final String name, final HKXType vtype, 23 | final long offset) { 24 | this.connector = connector; 25 | this.name = name; 26 | this.memberOffset = offset; 27 | this.vtype = vtype; 28 | } 29 | 30 | @Override 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | public HKXMember read(final long classOffset) throws InvalidPositionException { 35 | String contents = ""; 36 | try { 37 | DataInternal data = connector.data1.readNext(); 38 | if (data.from == memberOffset + classOffset) { 39 | ByteBuffer file = connector.data.setup(data.to); 40 | contents = ByteUtils.readString(file); 41 | } else { 42 | connector.data1.backtrack(); 43 | } 44 | } catch (InvalidPositionException e) { 45 | // NO OP. Met when the last item of the HKX file is a String and is empty. 46 | contents = ""; 47 | } 48 | HKXStringMember result = new HKXStringMember(name, vtype); 49 | result.set(contents); 50 | return result; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/tagwriter/TagXMLWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagwriter; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.io.File; 6 | 7 | import org.junit.BeforeClass; 8 | import org.junit.Test; 9 | 10 | import com.dexesttp.hkxpack.data.HKXFile; 11 | import com.dexesttp.hkxpack.data.HKXObject; 12 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 13 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 14 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 15 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassListReadException; 16 | import com.dexesttp.hkxpack.utils.FileUtils; 17 | import com.google.common.io.Files; 18 | 19 | /** 20 | * Tests for a TagXMLWriter 21 | */ 22 | public class TagXMLWriterTest { 23 | public static final String TEST_BASE_OUTPUT_FILE = "test-base.xml"; 24 | public static final String TEST_BASE_TARGET_RESOURCE = "/test-base.xml"; 25 | private static HKXFile file; 26 | 27 | @BeforeClass 28 | /** 29 | * Set up the test environnement 30 | */ 31 | public static void setupClass() throws ClassListReadException, ClassFileReadException { 32 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 33 | HKXDescriptorFactory descriptorFactory = new HKXDescriptorFactory(enumResolver); 34 | file = new HKXFile("hk-2014.1.0-r1", 11); 35 | file.getContentCollection().add(new HKXObject("#test", descriptorFactory.get("hkBaseObject"))); 36 | } 37 | 38 | @Test 39 | /** 40 | * Tests if the TagXMLWriter output file is equals to the target file. 41 | */ 42 | public void testWriteDefaultFileToPhysicalFile() throws Exception { 43 | File outputFile = File.createTempFile(TEST_BASE_OUTPUT_FILE, ""); 44 | TagXMLWriter writer = new TagXMLWriter(outputFile); 45 | writer.write(file); 46 | assertArrayEquals(Files.toByteArray(outputFile), FileUtils.resourceToByteArray(TEST_BASE_TARGET_RESOURCE)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/serialized/TagXMLEmbeddedObjectSerializedHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader.serialized; 2 | 3 | import com.dexesttp.hkxpack.data.HKXObject; 4 | import com.dexesttp.hkxpack.data.members.HKXMember; 5 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 6 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 7 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 8 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 9 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 10 | 11 | /** 12 | * Handles embedded {@link HKXObject} members in TagXML files. 13 | */ 14 | class TagXMLEmbeddedObjectSerializedHandler implements TagXMLSerializedHandler { 15 | private final transient HKXDescriptorFactory descriptorFactory; 16 | private final transient TagXMLSerializedHandlerFactory serializedHandlerFactory; 17 | 18 | TagXMLEmbeddedObjectSerializedHandler(final TagXMLSerializedHandlerFactory tagXMLSerializedHandlerFactory, 19 | final HKXDescriptorFactory descriptorFactory) { 20 | this.serializedHandlerFactory = tagXMLSerializedHandlerFactory; 21 | this.descriptorFactory = descriptorFactory; 22 | 23 | } 24 | 25 | @Override 26 | /** 27 | * {@inheritDoc} 28 | */ 29 | public HKXMember handleMember(final HKXMemberTemplate objectTemplate) 30 | throws ClassFileReadException, InvalidTagXMLException { 31 | HKXDescriptor classDescriptor = descriptorFactory.get(objectTemplate.target); 32 | // Create object 33 | HKXObject result = new HKXObject("", classDescriptor); 34 | 35 | // Fill object 36 | for (HKXMemberTemplate memberTemplate : classDescriptor.getMemberTemplates()) { 37 | TagXMLSerializedHandler memberHandler = serializedHandlerFactory.getSerializedHandler(memberTemplate.vtype); 38 | result.getMembersList().add(memberHandler.handleMember(memberTemplate)); 39 | } 40 | 41 | return result; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/callbacks/HKXRelArrayMemberCallback.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object.callbacks; 2 | 3 | import java.nio.Buffer; 4 | import java.nio.ByteBuffer; 5 | import java.util.List; 6 | 7 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 8 | 9 | /** 10 | * Handles the {@link HKXMemberCallback} for a Rel (relative) 11 | * {@link HKXArrayMember}. 12 | */ 13 | public class HKXRelArrayMemberCallback implements HKXMemberCallback { 14 | private final transient HKXArrayMemberCallback callbackProcessor; 15 | private final transient ByteBuffer outFile; 16 | private final transient long classPos; 17 | private final transient long argPos; 18 | 19 | /** 20 | * Create a {@link HKXRelArrayMemberCallback}. 21 | * 22 | * @param callbackProcessor the {@link HKXArrayMemberCallback} to use for each 23 | * array member. 24 | * @param outFile the {@link ByteBuffer} to write this array's 25 | * position to. 26 | * @param classPos the position of the array's class. 27 | * @param argPos the position of the RelArray's argument from the 28 | * beginning of the class. 29 | */ 30 | public HKXRelArrayMemberCallback(final HKXArrayMemberCallback callbackProcessor, final ByteBuffer outFile, 31 | final long classPos, final long argPos) { 32 | this.callbackProcessor = callbackProcessor; 33 | this.outFile = outFile; 34 | this.classPos = classPos; 35 | this.argPos = argPos; 36 | } 37 | 38 | @Override 39 | /** 40 | * {@inheritDoc} 41 | */ 42 | public long process(final List memberCallbacks, final long position) { 43 | byte[] offset = ByteUtils.fromULong(position - classPos, 2); 44 | ((Buffer) outFile).position((int) (classPos + argPos + 2)); 45 | outFile.put(offset); 46 | return callbackProcessor.process(memberCallbacks, position); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/HKXDescriptorFactory.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 7 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassListReadException; 8 | import com.dexesttp.hkxpack.descriptor.reader.ClassXMLReader; 9 | import com.dexesttp.hkxpack.descriptor.reader.ClassXMLReaderFactory; 10 | 11 | /** 12 | * A HKXDescriptorFactory uses lazy instanciation to retrieve a non-unique 13 | * HKXDescriptor. Data may be reread when a different factory is used. 14 | */ 15 | public class HKXDescriptorFactory { 16 | private final transient ClassXMLReader reader; 17 | private final transient Map contents = new HashMap<>(); 18 | 19 | /** 20 | * Retrieves a new HKXDescriptorFactory. 21 | * 22 | * @param enumResolver the {@link HKXEnumResolver} to put the read enums into. 23 | * @throws ClassListReadException if there was an error while reading the Class 24 | * List. 25 | */ 26 | public HKXDescriptorFactory(final HKXEnumResolver enumResolver) throws ClassListReadException { 27 | ClassXMLReaderFactory factory = new ClassXMLReaderFactory(enumResolver); 28 | reader = factory.create(this); 29 | } 30 | 31 | /** 32 | * Retrieves a HKXDescriptor from the class name. 33 | * 34 | * @param name the HKXDescriptor's name 35 | * @return the HKXDescriptor 36 | * @throws ClassFileReadException if there was an error while reading the Class 37 | * File. 38 | */ 39 | public HKXDescriptor get(final String name) throws ClassFileReadException { 40 | synchronized (this) { 41 | if (contents.containsKey(name)) { 42 | return contents.get(name); 43 | } 44 | HKXDescriptor descriptor = reader.get(name); 45 | contents.put(name, descriptor); 46 | return descriptor; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/HKXObjectMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.data.HKXObject; 6 | import com.dexesttp.hkxpack.data.members.HKXMember; 7 | import com.dexesttp.hkxpack.hkxwriter.object.callbacks.HKXMemberCallback; 8 | 9 | /** 10 | * Handles a {@link HKXObject} as a member of a class to write to a HKX File. 11 | */ 12 | public class HKXObjectMemberHandler implements HKXMemberHandler { 13 | private final transient HKXMemberHandlerFactory memberHandlerFactory; 14 | private final transient List memberCallbacks; 15 | private final transient long offset; 16 | 17 | /** 18 | * Creates a {@link HKXObjectMemberHandler}. 19 | * 20 | * @param offset the offset of the {@link HKXObject} member in the 21 | * class. 22 | * @param memberHandlerFactory the {@link HKXMemberHandlerFactory} to use while 23 | * resolving the object. 24 | * @param memberCallbacks the list of callbacks to add this object's 25 | * members to. 26 | */ 27 | public HKXObjectMemberHandler(final long offset, final HKXMemberHandlerFactory memberHandlerFactory, 28 | final List memberCallbacks) { 29 | this.offset = offset; 30 | this.memberHandlerFactory = memberHandlerFactory; 31 | this.memberCallbacks = memberCallbacks; 32 | } 33 | 34 | @Override 35 | /** 36 | * {@inheritDoc} 37 | */ 38 | public HKXMemberCallback write(final HKXMember member, final long currentPos) { 39 | final HKXObject object = (HKXObject) member; 40 | HKXInternalObjectHandler internalObjectHandler = new HKXInternalObjectHandler(memberHandlerFactory, 41 | memberCallbacks); 42 | internalObjectHandler.write(object, currentPos + offset); 43 | return (memberCallbacks, position) -> { 44 | return 0; 45 | }; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/loggers/DirectoryWalkerLoggerFactory.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.loggers; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import com.dexesttp.hkxpack.cli.ConsoleView; 7 | import com.dexesttp.hkxpack.resources.LoggerUtil; 8 | 9 | /** 10 | * Creates a {@link DirectoryWalkerLogger} based on the relevant 11 | * {@link CLIProperties}. 12 | */ 13 | public class DirectoryWalkerLoggerFactory { 14 | private static final Logger LOGGER = Logger.getLogger(ConsoleView.class.getName()); 15 | 16 | /** 17 | * Retrieves a suitable {@link DirectoryWalkerLogger}. 18 | * 19 | * @param total the total number of files to walk through. 20 | * @return a suitable {@link DirectoryWalkerLogger}. 21 | */ 22 | public DirectoryWalkerLogger newLogger(final int total) { 23 | if (LOGGER.isLoggable(Level.FINE)) { 24 | LOGGER.info("Detected " + total + " files to handle."); 25 | return (done) -> { 26 | LOGGER.fine("Handled " + done + " files (" + ((float) done / (float) total) + "%)"); 27 | handleErrors(); 28 | }; 29 | } else if (LOGGER.isLoggable(Level.INFO)) { 30 | LOGGER.info("Detected " + total + " files to handle."); 31 | return (done) -> { 32 | handleErrors(); 33 | }; 34 | } else if (LOGGER.isLoggable(Level.SEVERE)) { 35 | return (done) -> { 36 | handleErrors(); 37 | }; 38 | } 39 | return (done) -> { 40 | }; 41 | } 42 | 43 | /** 44 | * Handles the {@link LoggerUtil} logging. 45 | */ 46 | private void handleErrors() { 47 | while (!LoggerUtil.getList().isEmpty()) { 48 | Throwable e = LoggerUtil.getList().remove(0); 49 | LOGGER.throwing(this.getClass().getName(), "handleErrors", e); 50 | } 51 | } 52 | 53 | /** 54 | * Logs a Directory walking progress. 55 | */ 56 | public interface DirectoryWalkerLogger { 57 | /** 58 | * Logs the progress of the walk. 59 | * 60 | * @param done the number of files already walked through. 61 | */ 62 | void log(long done); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagwriter/TagXMLDataCreator.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagwriter; 2 | 3 | import org.w3c.dom.Document; 4 | import org.w3c.dom.Node; 5 | 6 | import com.dexesttp.hkxpack.data.HKXData; 7 | import com.dexesttp.hkxpack.data.HKXObject; 8 | import com.dexesttp.hkxpack.data.members.HKXMember; 9 | import com.dexesttp.hkxpack.l10n.SBundle; 10 | 11 | /** 12 | * Creates {@link Node} from {@link HKXData}. 13 | */ 14 | class TagXMLDataCreator { 15 | private final transient Document document; 16 | private final transient TagXMLMemberCreator memberCreator; 17 | private final transient TagXMLObjectCreator objectCreator; 18 | 19 | /** 20 | * Initialize the {@link TagXMLDataCreator}, linking it definitely to a 21 | * {@link Document}. 22 | * 23 | * @param document 24 | */ 25 | TagXMLDataCreator(final Document document) { 26 | this.document = document; 27 | this.memberCreator = new TagXMLMemberCreator(this); 28 | this.objectCreator = new TagXMLObjectCreator(this); 29 | } 30 | 31 | /** 32 | * Creates a {@link Node} from a {@link HKXData} component. 33 | * 34 | * @param content the {@link HKXData} to convert. 35 | * @return a {@link Node} containing the data. 36 | */ 37 | Node create(final HKXData content) { 38 | if (content instanceof HKXObject) { 39 | return objectCreator.create((HKXObject) content); 40 | } 41 | if (content instanceof HKXMember) { 42 | return memberCreator.create((HKXMember) content); 43 | } 44 | throw new IllegalArgumentException(SBundle.getString("error.tag.create.type.unknown") + "[#060]"); 45 | } 46 | 47 | /** 48 | * Retrieves the specialized {@link TagXMLMemberCreator}. 49 | * 50 | * @return the embedded {@link TagXMLMemberCreator}. 51 | */ 52 | TagXMLMemberCreator getMemberCreator() { 53 | return memberCreator; 54 | } 55 | 56 | /** 57 | * Retrieves the linked {@link Document} 58 | * 59 | * @return the linked {@link Document} 60 | */ 61 | Document getDocument() { 62 | return document; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/members/TagXMLEmbeddedObjectHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader.members; 2 | 3 | import org.w3c.dom.Node; 4 | import org.w3c.dom.NodeList; 5 | 6 | import com.dexesttp.hkxpack.data.HKXObject; 7 | import com.dexesttp.hkxpack.data.members.HKXMember; 8 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 9 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 10 | import com.dexesttp.hkxpack.l10n.SBundle; 11 | import com.dexesttp.hkxpack.tagreader.TagXMLNodeHandler; 12 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 13 | 14 | /** 15 | * Handles reading an embedded {@link HKXObject} form a TagXML file. 16 | */ 17 | public class TagXMLEmbeddedObjectHandler implements TagXMLContentsHandler { 18 | private final transient TagXMLNodeHandler nodeHandler; 19 | 20 | /** 21 | * Create a {@link TagXMLEmbeddedObjectHandler}. 22 | * 23 | * @param nodeHandler the {@link TagXMLNodeHandler} to use while reading this 24 | * object's members. 25 | */ 26 | public TagXMLEmbeddedObjectHandler(final TagXMLNodeHandler nodeHandler) { 27 | this.nodeHandler = nodeHandler; 28 | } 29 | 30 | @Override 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | public HKXMember handleNode(final Node member, final HKXMemberTemplate memberTemplate) 35 | throws ClassFileReadException, InvalidTagXMLException { 36 | String target = memberTemplate.target; 37 | NodeList children = member.getChildNodes(); 38 | for (int i = 0; i < children.getLength(); i++) { 39 | Node objectNode = children.item(i); 40 | if (objectNode.getNodeName().equals("hkobject")) { 41 | return handleNode(objectNode, target); 42 | } 43 | } 44 | throw new InvalidTagXMLException(SBundle.getString("error.tag.read.member") + memberTemplate.name); 45 | } 46 | 47 | HKXMember handleNode(final Node member, final String target) throws ClassFileReadException, InvalidTagXMLException { 48 | return nodeHandler.handleSubObject(member, target); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/commands/Command_unpack.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.commands; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.xml.parsers.ParserConfigurationException; 7 | import javax.xml.transform.TransformerException; 8 | 9 | import com.dexesttp.hkxpack.cli.utils.FileNameCreationException; 10 | import com.dexesttp.hkxpack.data.HKXFile; 11 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 12 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 13 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 14 | import com.dexesttp.hkxpack.hkxreader.HKXReader; 15 | import com.dexesttp.hkxpack.tagwriter.TagXMLWriter; 16 | 17 | /** 18 | * Unpacks a HKX file into a XML file. 19 | * 20 | * @see Command_IO 21 | */ 22 | public class Command_unpack extends Command_IO { 23 | @Override 24 | protected void executionCore(final String inputFileName, final String outputFileName, 25 | final HKXEnumResolver enumResolver, final HKXDescriptorFactory descriptorFactory) 26 | throws IOException, InvalidPositionException, TransformerException, ParserConfigurationException { 27 | // Read HKX file 28 | File inFile = new File(inputFileName); 29 | HKXReader reader = new HKXReader(inFile, descriptorFactory, enumResolver); 30 | HKXFile hkxFile = reader.read(); 31 | 32 | // Write XML file 33 | File outFile = new File(outputFileName); 34 | TagXMLWriter writer = new TagXMLWriter(outFile); 35 | writer.write(hkxFile); 36 | } 37 | 38 | @Override 39 | protected String extractFileName(final String ogName) throws FileNameCreationException { 40 | String newName = ""; 41 | try { 42 | newName = ogName.substring(0, ogName.lastIndexOf('.')) + ".xml"; 43 | } catch (StringIndexOutOfBoundsException e) { 44 | throw new FileNameCreationException("The file : " + ogName + " has a name that can't be converted.", e); 45 | } 46 | return newName; 47 | } 48 | 49 | @Override 50 | protected String[] getFileExtensions() { 51 | return new String[] { ".hkx" }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/HKXEnumMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object; 2 | 3 | import java.nio.Buffer; 4 | import java.nio.ByteBuffer; 5 | 6 | import com.dexesttp.hkxpack.data.members.HKXEnumMember; 7 | import com.dexesttp.hkxpack.data.members.HKXMember; 8 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 9 | import com.dexesttp.hkxpack.hkx.types.MemberSizeResolver; 10 | import com.dexesttp.hkxpack.hkxwriter.object.callbacks.HKXMemberCallback; 11 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 12 | 13 | /** 14 | * Handles {@link HKXEnumMember} for writing them to a HKX File 15 | */ 16 | public class HKXEnumMemberHandler implements HKXMemberHandler { 17 | private final transient ByteBuffer outFile; 18 | private final transient long offset; 19 | private final transient HKXEnumResolver enumResolver; 20 | 21 | /** 22 | * Creates a {@link HKXEnumMemberHandler}. 23 | * 24 | * @param outFile the output {@link ByteBuffer} to write to. 25 | * @param offset the offset of the memebr in the class. 26 | * @param enumResolver the {@link HKXEnumResolver} to use to resolve the 27 | * enumeration into a value. 28 | */ 29 | public HKXEnumMemberHandler(final ByteBuffer outFile, final long offset, final HKXEnumResolver enumResolver) { 30 | this.outFile = outFile; 31 | this.offset = offset; 32 | this.enumResolver = enumResolver; 33 | } 34 | 35 | @Override 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public HKXMemberCallback write(final HKXMember member, final long currentPos) { 40 | HKXEnumMember enumMember = (HKXEnumMember) member; 41 | if (!enumMember.getEnumerationName().isEmpty()) { 42 | ((Buffer) outFile).position((int) (currentPos + offset)); 43 | long enumVal = enumResolver.resolve(enumMember.getEnumerationName(), enumMember.get()); 44 | byte[] res = ByteUtils.fromULong(enumVal, (int) MemberSizeResolver.getSize(enumMember.getSubtype())); 45 | outFile.put(res); 46 | } 47 | return (memberCallbacks, position) -> { 48 | return 0; 49 | }; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /doc/hkx findings/00 - Findings on HKX files.txt: -------------------------------------------------------------------------------- 1 | === HKX File format === 2 | 3 | The HKX file format (Havok Packed File) is defined by its header. 4 | 5 | The header contains : 6 | - 64 bytes of pure header data 7 | - three sections 8 | classname 9 | types 10 | data 11 | 12 | === HKX pure header == 13 | Constant value 57E0 E057 14 | 10C0 C010 15 | 0000 0000 16 | Version number <4 bytes> 17 | A 4-byte value 18 | version 8 04 01 00 00 19 | version 11 08 01 00 00 20 | Constant values 0300 0000 21 | 0200 0000 22 | 0000 0000 23 | 0000 0000 24 | 4B00 0000 25 | the version name <16 bytes> 26 | 4 empty bytes 0000 0000 27 | <4 bytes> 28 | An offset (v. 11) or FF's (v. 8) over 4 bytes 29 | 30 | === HKX Header Sections === 31 | You have to move bytes if v. 11 32 | 33 | The section name <16 bytes> 34 | Constant value 00 00 00 FF 35 | general offset <4 bytes> 36 | part 1 offset <4 bytes> 37 | part 2 offset <4 bytes> 38 | part 3 offset <4 bytes> 39 | part 4 offset <4 bytes> 40 | part 5 offset <4 bytes> 41 | part 6 offset <4 bytes> 42 | 43 | You have to skip 16 bytes if v. 11 (FF bytes) 44 | 45 | === HKX Content : how to read it === 46 | 47 | Build a VLookup of class names using the following format : 48 | ~ FSeek(data.offset + data.part3offset) 49 | ~ UUID <4 bytes> 50 | ~ empty 0000 0000 51 | ~ class address <4 bytes> 52 | ~ goto l.2 until pos + 12 >= (data.offset + data.part4offset) 53 | 54 | You might want to resolve the class address into name and ID by : 55 | ~ FSeek(class address); 56 | ~ readString(); 57 | ~ FSeek(class address - 5); 58 | ~ readClassID (4 bytes) 59 | 60 | Read the following values : 61 | data2 value : 62 | ~ mystery <4 bytes> 63 | ~ const 0200 0000 64 | ~ UUID <4 bytes> 65 | ~ readData(UUID); 66 | ~ goto l.2 until pos + 12 > (data.offset + data.part4offset) 67 | 68 | with readData as such : 69 | ~ For each data needed for the UUID class 70 | ~ read data1 71 | ~ Store data1 as data 72 | ~ Solve data1 from data type needed in class 73 | 74 | and Data1 with the format 75 | ~ source <4 bytes> 76 | ~ value 77 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/data/HKXObject.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.dexesttp.hkxpack.data.members.HKXMember; 7 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 8 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 9 | 10 | /** 11 | * Represents a HKX object instantiating a class, stored in memory. 12 | */ 13 | public class HKXObject implements HKXMember { 14 | private final String name; 15 | private final HKXDescriptor descriptor; 16 | private final transient List members; 17 | 18 | /** 19 | * Creates a {@link HKXObject}. 20 | * 21 | * @param name the name of the object to create 22 | * @param template the template to create the object from. 23 | */ 24 | public HKXObject(final String name, final HKXDescriptor template) { 25 | this(name, template, new ArrayList()); 26 | } 27 | 28 | /** 29 | * Creates a {@link HKXObject} 30 | * 31 | * @param name the name of the object to create 32 | * @param descriptor the descriptor to create the object from. 33 | * @param members the list of members to add to the object. 34 | */ 35 | public HKXObject(final String name, final HKXDescriptor descriptor, final List members) { 36 | this.name = name; 37 | this.descriptor = descriptor; 38 | this.members = members; 39 | } 40 | 41 | /** 42 | * Get this object's name. 43 | */ 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | /** 49 | * Get this object's descriptor. 50 | * 51 | * @return the {@link HKXDescriptor} that represents this object. 52 | */ 53 | public HKXDescriptor getDescriptor() { 54 | return descriptor; 55 | } 56 | 57 | /** 58 | * Get this {@link HKXObject}'s member list. 59 | * 60 | * @return an ordered list of all the members of this object. 61 | */ 62 | public List getMembersList() { 63 | return members; 64 | } 65 | 66 | @Override 67 | /** 68 | * {@inheritDoc} 69 | */ 70 | public HKXType getType() { 71 | return HKXType.TYPE_STRUCT; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/HKXRelArrayMemberReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXArrayMember; 6 | import com.dexesttp.hkxpack.data.members.HKXMember; 7 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 8 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 9 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 10 | import com.dexesttp.hkxpack.hkxreader.member.arrays.HKXArrayContentsReader; 11 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 12 | 13 | /** 14 | * Reads a Relative-positionned Array as a {@link HKXArrayMember} from the HKX 15 | * file. 16 | * 17 | * @see HKXArrayContentsReader 18 | */ 19 | public class HKXRelArrayMemberReader implements HKXMemberReader { 20 | private final transient HKXReaderConnector connector; 21 | private final transient String name; 22 | private final transient HKXType subtype; 23 | private final transient long offset; 24 | private final transient HKXArrayContentsReader internals; 25 | 26 | HKXRelArrayMemberReader(final HKXReaderConnector connector, final String name, final HKXType subtype, 27 | final HKXArrayContentsReader arrayContentsReader, final long offset) { 28 | this.connector = connector; 29 | this.name = name; 30 | this.subtype = subtype; 31 | this.internals = arrayContentsReader; 32 | this.offset = offset; 33 | } 34 | 35 | @Override 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public HKXMember read(final long classOffset) throws InvalidPositionException { 40 | ByteBuffer file = connector.data.setup(classOffset + offset); 41 | byte[] bSize = new byte[2]; 42 | byte[] bOff = new byte[2]; 43 | file.get(bSize); 44 | file.get(bOff); 45 | int size = ByteUtils.getUInt(bSize) - 1; 46 | int offset = ByteUtils.getUInt(bOff); 47 | HKXArrayMember res = new HKXArrayMember(name, HKXType.TYPE_RELARRAY, subtype); 48 | for (int i = 0; i < size; i++) { 49 | res.add(internals.getContents(classOffset + offset, i)); 50 | } 51 | return res; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/serialized/TagXMLDirectSerializedHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader.serialized; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 4 | import com.dexesttp.hkxpack.data.members.HKXMember; 5 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 6 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 7 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 8 | 9 | /** 10 | * Handles direct serialized members generation. 11 | */ 12 | class TagXMLDirectSerializedHandler implements TagXMLSerializedHandler { 13 | 14 | @Override 15 | /** 16 | * {@inheritDoc} 17 | */ 18 | public HKXMember handleMember(final HKXMemberTemplate memberTemplate) 19 | throws ClassFileReadException, InvalidTagXMLException { 20 | return emptyMember(memberTemplate); 21 | } 22 | 23 | @SuppressWarnings("unchecked") 24 | private HKXMember emptyMember(final HKXMemberTemplate memberTemplate) { 25 | HKXMember result = null; 26 | switch (memberTemplate.vtype) { 27 | case TYPE_BOOL: 28 | result = new HKXDirectMember(memberTemplate.name, memberTemplate.vtype); 29 | ((HKXDirectMember) result).set(Boolean.FALSE); 30 | break; 31 | case TYPE_CHAR: 32 | case TYPE_UINT8: 33 | case TYPE_INT8: 34 | result = new HKXDirectMember(memberTemplate.name, memberTemplate.vtype); 35 | ((HKXDirectMember) result).set(Character.valueOf((char) 0)); 36 | break; 37 | case TYPE_UINT16: 38 | case TYPE_ULONG: 39 | case TYPE_UINT32: 40 | case TYPE_UINT64: 41 | case TYPE_INT16: 42 | case TYPE_INT32: 43 | case TYPE_INT64: 44 | result = new HKXDirectMember(memberTemplate.name, memberTemplate.vtype); 45 | ((HKXDirectMember) result).set(Integer.valueOf(0)); 46 | break; 47 | case TYPE_HALF: 48 | case TYPE_REAL: 49 | result = new HKXDirectMember(memberTemplate.name, memberTemplate.vtype); 50 | ((HKXDirectMember) result).set(Double.valueOf(0)); 51 | break; 52 | default: 53 | break; 54 | } 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagwriter/TagXMLWriter.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagwriter; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.xml.parsers.ParserConfigurationException; 7 | import javax.xml.transform.TransformerException; 8 | 9 | import org.w3c.dom.Document; 10 | import org.w3c.dom.Element; 11 | import org.w3c.dom.Node; 12 | 13 | import com.dexesttp.hkxpack.data.HKXFile; 14 | import com.dexesttp.hkxpack.data.HKXObject; 15 | 16 | /** 17 | * Handles writing {@link HKXFile} data into a {@link File} using the TagXML 18 | * notation. 19 | */ 20 | public class TagXMLWriter { 21 | private final transient File outFile; 22 | 23 | /** 24 | * Creates a {@link TagXMLWriter}. 25 | * 26 | * @param outputFile the file to output the data into. 27 | */ 28 | public TagXMLWriter(final File outputFile) { 29 | this.outFile = outputFile; 30 | } 31 | 32 | /** 33 | * Write an {@link HKXFile} as an XML file. 34 | * 35 | * @param hkxFile the HKXFiel to write. 36 | * @throws IOException if the XML file couldn't be written. 37 | * @throws TransformerException if there was a problem handling the 38 | * {@link HKXFile}'s content. 39 | * @throws ParserConfigurationException if there was a problem creating the XML 40 | * {@link Document}. 41 | */ 42 | public void write(final HKXFile hkxFile) throws IOException, TransformerException, ParserConfigurationException { 43 | TagXMLHandler handler = new TagXMLHandler(); 44 | // Create the new Document 45 | Document document = handler.createDOM(hkxFile.getContentsVersion(), hkxFile.getClassVersion()); 46 | 47 | // Create the "__data__" section in the document. 48 | Element root = handler.createSection(document, "__data__"); 49 | 50 | TagXMLDataCreator creator = new TagXMLDataCreator(document); 51 | 52 | for (HKXObject content : hkxFile.getContentCollection()) { 53 | Node contentXML = creator.create(content); 54 | root.appendChild(contentXML); 55 | } 56 | handler.writeToFile(document, outFile); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/callbacks/HKXDefaultArrayMemberCallback.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object.callbacks; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.data.HKXData; 6 | import com.dexesttp.hkxpack.data.members.HKXArrayMember; 7 | import com.dexesttp.hkxpack.data.members.HKXMember; 8 | import com.dexesttp.hkxpack.hkx.HKXUtils; 9 | import com.dexesttp.hkxpack.hkx.types.MemberSizeResolver; 10 | import com.dexesttp.hkxpack.hkxwriter.object.HKXMemberHandler; 11 | import com.dexesttp.hkxpack.hkxwriter.object.HKXMemberHandlerFactory; 12 | 13 | /** 14 | * Handles callbacks for most members. 15 | */ 16 | public class HKXDefaultArrayMemberCallback implements HKXArrayMemberCallback { 17 | private final transient HKXArrayMember arrMember; 18 | private final transient HKXMemberHandlerFactory memberHandlerFactory; 19 | 20 | /** 21 | * Creates a {@link HKXDefaultArrayMemberCallback} 22 | * 23 | * @param arrMember the {@link HKXArrayMember} this callback handles 24 | * @param memberHandlerFactory the {@link HKXMemberHandlerFactory} to use while 25 | * creating the array component's handlers. 26 | */ 27 | public HKXDefaultArrayMemberCallback(final HKXArrayMember arrMember, 28 | final HKXMemberHandlerFactory memberHandlerFactory) { 29 | this.arrMember = arrMember; 30 | this.memberHandlerFactory = memberHandlerFactory; 31 | } 32 | 33 | @Override 34 | /** 35 | * {@inheritDoc} 36 | */ 37 | public long process(final List memberCallbacks, final long position) { 38 | long newPos = position; 39 | long memberSize = MemberSizeResolver.getSize(arrMember.getSubType()); 40 | for (HKXData data : arrMember.getContentsList()) { 41 | if (data instanceof HKXMember) { 42 | HKXMember internalMember = (HKXMember) data; 43 | HKXMemberHandler memberHandler = memberHandlerFactory.create(internalMember.getType(), 0); 44 | memberCallbacks.add(memberHandler.write(internalMember, newPos)); 45 | newPos += memberSize; 46 | } 47 | } 48 | return HKXUtils.snapLine(newPos) - position; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/commands/Command_help.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.commands; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import com.dexesttp.hkxpack.cli.ConsoleView; 7 | import com.dexesttp.hkxpack.cli.utils.StaticProperties; 8 | 9 | /** 10 | * Displays the help of the Command Line Interface. 11 | */ 12 | public class Command_help implements Command { 13 | private static final Logger LOGGER = Logger.getLogger(ConsoleView.class.getName()); 14 | 15 | @Override 16 | /** 17 | * {@inheritDoc} 18 | */ 19 | // TODO prettify help. 20 | public int execute(final String... parameters) { 21 | boolean verbose = false; 22 | if (parameters.length >= 2 && parameters[1].equals("-v")) { 23 | verbose = true; 24 | } 25 | System.setProperty("java.util.logging.SimpleFormatter.format", "%5$s%n"); 26 | LOGGER.setLevel(Level.INFO); 27 | 28 | if (LOGGER.isLoggable(Level.INFO)) { 29 | LOGGER.info("HKXPack version " + StaticProperties.getVersionNumber()); 30 | LOGGER.info("Use : java -jar hkxpack-cli.jar "); 31 | LOGGER.info("Arguments :"); 32 | LOGGER.info("\t" + "unpack" + "\t" + "\t" + "Extracts .hkx into .xml"); 33 | LOGGER.info("\t" + "pack" + "\t" + "\t" + "Compress .xml into .hkx"); 34 | LOGGER.info("\t" + "help" + "\t\t\t" + "Show this window"); 35 | LOGGER.info("Options :"); 36 | LOGGER.info("\t" + "-q\t\t" + "Quiet output"); 37 | LOGGER.info("\t" + "-v\t\t" + "Verbose output"); 38 | LOGGER.info("\t" + "-o \t" + "Set the output file"); 39 | LOGGER.info("Advanced options :"); 40 | if (verbose) { 41 | LOGGER.info("\t" + "-d\t\t" + "Debug output"); 42 | LOGGER.info("\t" + "-t " + "\t" + "Set the maximum numbers of threads to use"); 43 | LOGGER.info("\t" + "-b " + "\t" + "Set the buffer size"); 44 | } else { 45 | LOGGER.info("\t" + "Use the 'help -v' option to see advanced options"); 46 | } 47 | LOGGER.info(""); 48 | LOGGER.info("Report bugs or findings at github.com/dexesttp/hkxpack"); 49 | } 50 | return 0; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/member/HKXEnumMemberReader.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader.member; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.dexesttp.hkxpack.data.members.HKXEnumMember; 6 | import com.dexesttp.hkxpack.data.members.HKXMember; 7 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 8 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 9 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 10 | import com.dexesttp.hkxpack.hkx.types.MemberSizeResolver; 11 | import com.dexesttp.hkxpack.hkxreader.HKXReaderConnector; 12 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 13 | 14 | /** 15 | * Reads an enumeration as a {@link HKXEnumMember} from a HKX file. 16 | */ 17 | public class HKXEnumMemberReader implements HKXMemberReader { 18 | private final transient HKXReaderConnector connector; 19 | private final transient HKXEnumResolver enumResolver; 20 | private final transient String name; 21 | private final transient HKXType vtype; 22 | private final transient HKXType vsubtype; 23 | private final transient String etype; 24 | private final transient long memberOffset; 25 | 26 | HKXEnumMemberReader(final HKXReaderConnector connector, final HKXEnumResolver enumResolver, final String name, 27 | final HKXType vtype, final HKXType vsubtype, final String target, final long offset) { 28 | this.connector = connector; 29 | this.enumResolver = enumResolver; 30 | this.name = name; 31 | this.vtype = vtype; 32 | this.vsubtype = vsubtype; 33 | this.etype = target; 34 | this.memberOffset = offset; 35 | } 36 | 37 | @Override 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public HKXMember read(final long classOffset) throws InvalidPositionException { 42 | final int memberSize = (int) MemberSizeResolver.getSize(vsubtype); 43 | ByteBuffer file = connector.data.setup(classOffset + memberOffset); 44 | byte[] bytesToRead = new byte[memberSize]; 45 | file.get(bytesToRead); 46 | int contents = ByteUtils.getUInt(bytesToRead); 47 | HKXEnumMember result = new HKXEnumMember(name, vtype, vsubtype, etype); 48 | result.set(enumResolver.resolve(etype, contents)); 49 | return result; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/handlers/Vector4Handler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.handlers; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 4 | import com.dexesttp.hkxpack.data.members.HKXMember; 5 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 6 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 7 | 8 | /** 9 | * Handles quaternions 10 | */ 11 | class Vector4Handler implements MemberHandler { 12 | @Override 13 | /** 14 | * {@inheritDoc} 15 | */ 16 | public long getSize() { 17 | return 0x10; 18 | } 19 | 20 | @Override 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public HKXMember createMember(final String name, final HKXType type, final byte[] byteArray) { 25 | byte[] b21 = new byte[] { byteArray[0], byteArray[1], byteArray[2], byteArray[3] }; 26 | byte[] b22 = new byte[] { byteArray[4], byteArray[5], byteArray[6], byteArray[7] }; 27 | byte[] b23 = new byte[] { byteArray[8], byteArray[9], byteArray[10], byteArray[11] }; 28 | byte[] b24 = new byte[] { byteArray[12], byteArray[13], byteArray[14], byteArray[15] }; 29 | HKXDirectMember member8 = new HKXDirectMember<>(name, type); 30 | member8.set(new Double[] { (double) ByteUtils.getFloat(b21), (double) ByteUtils.getFloat(b22), 31 | (double) ByteUtils.getFloat(b23), (double) ByteUtils.getFloat(b24) }); 32 | return member8; 33 | } 34 | 35 | @SuppressWarnings("unchecked") 36 | @Override 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | public byte[] readMember(final HKXMember member) { 41 | HKXDirectMember memberTransform = (HKXDirectMember) member; 42 | byte[] memberTr1 = ByteUtils.fromFloat(memberTransform.get()[0], 4); 43 | byte[] memberTr2 = ByteUtils.fromFloat(memberTransform.get()[1], 4); 44 | byte[] memberTr3 = ByteUtils.fromFloat(memberTransform.get()[2], 4); 45 | byte[] memberTr4 = ByteUtils.fromFloat(memberTransform.get()[3], 4); 46 | return new byte[] { memberTr1[0], memberTr1[1], memberTr1[2], memberTr1[3], memberTr2[0], memberTr2[1], 47 | memberTr2[2], memberTr2[3], memberTr3[0], memberTr3[1], memberTr3[2], memberTr3[3], memberTr4[0], 48 | memberTr4[1], memberTr4[2], memberTr4[3] }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/HKXStringMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object; 2 | 3 | import java.nio.Buffer; 4 | import java.nio.ByteBuffer; 5 | import java.util.List; 6 | 7 | import com.dexesttp.hkxpack.data.members.HKXMember; 8 | import com.dexesttp.hkxpack.data.members.HKXStringMember; 9 | import com.dexesttp.hkxpack.hkx.HKXUtils; 10 | import com.dexesttp.hkxpack.hkx.data.DataInternal; 11 | import com.dexesttp.hkxpack.hkxwriter.object.callbacks.HKXMemberCallback; 12 | 13 | /** 14 | * Handles a {@link HKXStringMember} for output to a HKX file. 15 | * 16 | * @see HKXMemberHandler 17 | */ 18 | public class HKXStringMemberHandler implements HKXMemberHandler { 19 | private final transient ByteBuffer outFile; 20 | private final transient long offset; 21 | private final transient List data1; 22 | 23 | /** 24 | * Creates a {@link HKXStringMemberHandler}. 25 | * 26 | * @param outFile the file to output to 27 | * @param offset the member's offset in the object 28 | * @param data1List the list of internal pointers to put the {@link String}'s 29 | * location reference in. 30 | */ 31 | public HKXStringMemberHandler(final ByteBuffer outFile, final long offset, final List data1List) { 32 | this.outFile = outFile; 33 | this.offset = offset; 34 | this.data1 = data1List; 35 | } 36 | 37 | @Override 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public HKXMemberCallback write(final HKXMember member, final long currentPos) { 42 | final HKXStringMember strMember = (HKXStringMember) member; 43 | if (strMember.get() == null || strMember.get().isEmpty()) { 44 | return (callbacks, position) -> { 45 | return 0; 46 | }; 47 | } 48 | final DataInternal stringData = new DataInternal(); 49 | stringData.from = currentPos + offset; 50 | return (callbacks, position) -> { 51 | stringData.to = position; 52 | data1.add(stringData); 53 | ((Buffer) outFile).position((int) position); 54 | outFile.put(strMember.get().getBytes()); 55 | outFile.put((byte) 0x00); 56 | return HKXUtils.snapString(position + strMember.get().length() + 1) - position; 57 | }; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagwriter/TagXMLDirectMemberHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagwriter; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 4 | 5 | /** 6 | * Handles the conversion between a {@link HKXDirectMember} member and its 7 | * content as a {@link String}. 8 | */ 9 | class TagXMLDirectMemberHandler { 10 | public static final int VECTOR4_LENGTH = 4; 11 | public static final int QSTRANSFORM_LENGTH = 12; 12 | 13 | /** 14 | * Converts a {@link HKXDirectMember} into a {@link String}. 15 | * 16 | * @param member the {@link HKXDirectMember} to convert 17 | * @return a {@link String} containign the value of the {@link HKXDirectMember}. 18 | */ 19 | @SuppressWarnings("unchecked") 20 | String getStringValue(final HKXDirectMember member) { 21 | if (member.get() instanceof Double[]) { 22 | Double[] contents = (Double[]) member.get(); 23 | if (contents.length <= VECTOR4_LENGTH) { 24 | StringBuffer contentsAccu = new StringBuffer(); 25 | contentsAccu.append('('); 26 | for (int i = 0; i < contents.length; i++) { 27 | contentsAccu.append(contents[i]).append(' '); 28 | } 29 | return contentsAccu.substring(0, contentsAccu.length() - 1) + ")"; 30 | } else { 31 | if (contents.length == QSTRANSFORM_LENGTH) { 32 | return "(" + contents[0] + " " + contents[1] + " " + contents[2] + " " + contents[3] + ")" + "(" 33 | + contents[4] + " " + contents[5] + " " + contents[6] + " " + contents[7] + ")" + "(" 34 | + contents[8] + " " + contents[9] + " " + contents[10] + " " + contents[11] + ")"; 35 | } else { 36 | return "(" + contents[0] + " " + contents[1] + " " + contents[2] + " " + contents[3] + ")" + "(" 37 | + contents[4] + " " + contents[5] + " " + contents[6] + " " + contents[7] + ")" + "(" 38 | + contents[8] + " " + contents[9] + " " + contents[10] + " " + contents[11] + ")" + "(" 39 | + contents[12] + " " + contents[13] + " " + contents[14] + " " + contents[15] + ")"; 40 | } 41 | } 42 | } 43 | if (member.get() instanceof Character) { 44 | return Integer.toString((int) ((char) ((HKXDirectMember) member).get())); 45 | } 46 | return member.get().toString(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/test/java/com/dexesttp/hkxpack/files/TestBase.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.files; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import com.dexesttp.hkxpack.data.HKXFile; 10 | import com.dexesttp.hkxpack.data.HKXObject; 11 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 12 | 13 | /** 14 | * The content reading tests for all files based on the "base" file. 15 | */ 16 | @Ignore 17 | public class TestBase { 18 | protected final static String BASE_FILE_RESOURCE_NAME = "/test-base"; 19 | protected final transient HKXFile file; 20 | 21 | /** 22 | * Creates a {@link TestBase}. 23 | * 24 | * @param file the {@link HXKFile} to test 25 | */ 26 | protected TestBase(final HKXFile file) { 27 | this.file = file; 28 | } 29 | 30 | @Test 31 | /** 32 | * Tests if there is the right number of objects in the base file (1) 33 | */ 34 | public void testIfThereIsTheRightNumberOfObjects() { 35 | assertEquals(1, file.getContentCollection().size()); 36 | } 37 | 38 | @Test 39 | /** 40 | * Tests if the the read object's Type is the right one (a STRUCT) 41 | */ 42 | public void testIfTheRightObjectIsPresent() { 43 | for (final HKXObject object : file.getContentCollection()) { 44 | assertEquals(HKXType.TYPE_STRUCT, object.getType()); 45 | } 46 | } 47 | 48 | @Test 49 | /** 50 | * Tests if the the read object's name is valid (not null) 51 | */ 52 | public void testTheObjectName() { 53 | for (final HKXObject object : file.getContentCollection()) { 54 | assertNotNull(object.getName()); 55 | } 56 | } 57 | 58 | @Test 59 | /** 60 | * Tests if the the read object's class name is the right one (hkBaseObject) 61 | */ 62 | public void testTheObjectClassName() { 63 | for (final HKXObject object : file.getContentCollection()) { 64 | assertEquals("hkBaseObject", object.getDescriptor().getName()); 65 | } 66 | } 67 | 68 | @Test 69 | /** 70 | * Tests if the the read object's content size is right (0) 71 | */ 72 | public void testTheObjectContentsSize() { 73 | for (final HKXObject object : file.getContentCollection()) { 74 | assertEquals(0, object.getMembersList().size()); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/MemberSizeResolver.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types; 2 | 3 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 4 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 5 | import com.dexesttp.hkxpack.hkx.types.handlers.MemberHandlerFactory; 6 | 7 | /** 8 | * Intended to retrieve {@link HKXType}-specific data. 9 | *

10 | * {@link #getSize(HKXType)} retrieves the size of a {@link HKXType} 11 | * {@link #getSize(HKXDescriptor)} retrieves the size of a 12 | * {@link HKXDescriptor}, including padding. 13 | */ 14 | public final class MemberSizeResolver { 15 | private static final long PTR_SIZE = 0x08; 16 | 17 | private MemberSizeResolver() { 18 | // NO OP 19 | } 20 | 21 | /** 22 | * Retrieve the size of a standard {@link HKXType}. 23 | * 24 | * @param type the {@link HKXType} to retrieve the size of. 25 | * @return the {@link HKXType}'s size. 26 | * @throws IllegalArgumentException if the given {@link HKXType} isn't standard. 27 | */ 28 | public static long getSize(final HKXType type) { 29 | switch (type) { 30 | case TYPE_NONE: 31 | case TYPE_VOID: 32 | return 0X00; 33 | case TYPE_ENUM: 34 | case TYPE_FLAGS: 35 | return 0X04; 36 | // Base values 37 | case TYPE_BOOL: 38 | case TYPE_CHAR: 39 | case TYPE_UINT8: 40 | case TYPE_INT8: 41 | case TYPE_HALF: 42 | case TYPE_UINT16: 43 | case TYPE_INT16: 44 | case TYPE_ULONG: 45 | case TYPE_UINT32: 46 | case TYPE_INT32: 47 | case TYPE_UINT64: 48 | case TYPE_INT64: 49 | case TYPE_REAL: 50 | // Complex values 51 | case TYPE_VECTOR4: 52 | case TYPE_QUATERNION: 53 | case TYPE_QSTRANSFORM: 54 | case TYPE_MATRIX3: 55 | case TYPE_TRANSFORM: 56 | case TYPE_MATRIX4: 57 | return MemberHandlerFactory.getMemberHandler(type).getSize(); 58 | // Strings and ptrs 59 | case TYPE_CSTRING: 60 | case TYPE_STRINGPTR: 61 | return PTR_SIZE; 62 | case TYPE_FUNCTIONPOINTER: 63 | case TYPE_POINTER: 64 | return PTR_SIZE; 65 | // Arrays 66 | case TYPE_RELARRAY: 67 | return 0x4; 68 | case TYPE_ARRAY: 69 | case TYPE_SIMPLEARRAY: 70 | return 0X10; 71 | default: 72 | break; 73 | } 74 | throw new IllegalArgumentException(type.toString() + " can't be analyzed with MemberTypeResolver#getSize"); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxreader/HKXReaderConnector.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxreader; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.dexesttp.hkxpack.hkx.classnames.ClassnamesData; 6 | import com.dexesttp.hkxpack.hkx.classnames.ClassnamesInterface; 7 | import com.dexesttp.hkxpack.hkx.data.Data1Interface; 8 | import com.dexesttp.hkxpack.hkx.data.Data2Interface; 9 | import com.dexesttp.hkxpack.hkx.data.Data3Interface; 10 | import com.dexesttp.hkxpack.hkx.data.DataInterface; 11 | import com.dexesttp.hkxpack.hkx.header.HeaderData; 12 | import com.dexesttp.hkxpack.hkx.header.HeaderInterface; 13 | import com.dexesttp.hkxpack.hkx.header.SectionData; 14 | import com.dexesttp.hkxpack.hkx.header.SectionInterface; 15 | 16 | /** 17 | * Handles connexion between a {@link ByteBuffer} and a {@link HKXReader}. 18 | *

19 | * Created and managed by {@link HKXReader}. 20 | */ 21 | public class HKXReaderConnector { 22 | public final transient HeaderData header; 23 | public final transient SectionData classnamesHead; 24 | public final transient SectionData dataHead; 25 | public final transient ClassnamesData classnamesdata; 26 | public final transient DataInterface data; 27 | public final transient Data1Interface data1; 28 | public final transient Data2Interface data2; 29 | public final transient Data3Interface data3; 30 | 31 | HKXReaderConnector(final ByteBuffer file) { 32 | // Extract the header 33 | HeaderInterface headInt = new HeaderInterface(); 34 | headInt.connect(file); 35 | header = headInt.extract(); 36 | 37 | // Extract the section interfaces 38 | SectionInterface sectInt = new SectionInterface(); 39 | sectInt.connect(file, header); 40 | classnamesHead = sectInt.extract(0); 41 | dataHead = sectInt.extract(2); 42 | 43 | // Extract the classnames 44 | ClassnamesInterface cnamesInt = new ClassnamesInterface(); 45 | cnamesInt.connect(file, classnamesHead); 46 | classnamesdata = cnamesInt.extract(); 47 | 48 | // Connect the interfaces 49 | data1 = new Data1Interface(); 50 | data1.connect(file, dataHead); 51 | data2 = new Data2Interface(); 52 | data2.connect(file, dataHead); 53 | data3 = new Data3Interface(); 54 | data3.connect(file, dataHead); 55 | data = new DataInterface(); 56 | data.connect(file, dataHead); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cli/src/main/java/com/dexesttp/hkxpack/cli/commands/Command_pack.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.commands; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import javax.xml.parsers.ParserConfigurationException; 7 | 8 | import org.xml.sax.SAXException; 9 | 10 | import com.dexesttp.hkxpack.cli.utils.FileNameCreationException; 11 | import com.dexesttp.hkxpack.data.HKXFile; 12 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 13 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 14 | import com.dexesttp.hkxpack.hkx.exceptions.UnsupportedVersionError; 15 | import com.dexesttp.hkxpack.hkxwriter.HKXWriter; 16 | import com.dexesttp.hkxpack.tagreader.TagXMLReader; 17 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 18 | 19 | /** 20 | * Packs a XML file into a HKX file. 21 | * 22 | * @see Command_IO 23 | */ 24 | public class Command_pack extends Command_IO { 25 | @Override 26 | /** 27 | * {@inheritDoc} 28 | */ 29 | protected void executionCore(final String inputFileName, final String outputFileName, 30 | final HKXEnumResolver enumResolver, final HKXDescriptorFactory descriptorFactory) 31 | throws ParserConfigurationException, SAXException, IOException, InvalidTagXMLException, 32 | UnsupportedVersionError { 33 | // Read XML file 34 | File inFile = new File(inputFileName); 35 | TagXMLReader reader = new TagXMLReader(inFile, descriptorFactory); 36 | HKXFile file = reader.read(); 37 | 38 | // Write HKX file 39 | File outFile = new File(outputFileName); 40 | outFile.createNewFile(); 41 | HKXWriter writer = new HKXWriter(outFile, enumResolver, bufferSize); 42 | writer.write(file); 43 | } 44 | 45 | @Override 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | protected String extractFileName(final String ogName) throws FileNameCreationException { 50 | String newName = ""; 51 | try { 52 | newName = ogName.substring(0, ogName.lastIndexOf('.')) + ".hkx"; 53 | } catch (StringIndexOutOfBoundsException e) { 54 | throw new FileNameCreationException("The file : " + ogName + " has a name that can't be converted.", e); 55 | } 56 | return newName; 57 | } 58 | 59 | @Override 60 | /** 61 | * {@inheritDoc} 62 | */ 63 | protected String[] getFileExtensions() { 64 | return new String[] { ".xml" }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cli/src/debug/java/com/dexesttp/hkxpack/cli/components/Read.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.components; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.logging.Logger; 6 | 7 | import javax.xml.parsers.ParserConfigurationException; 8 | import javax.xml.transform.TransformerException; 9 | 10 | import com.dexesttp.hkxpack.data.HKXFile; 11 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 12 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 13 | import com.dexesttp.hkxpack.hkx.exceptions.InvalidPositionException; 14 | import com.dexesttp.hkxpack.hkxreader.HKXReader; 15 | import com.dexesttp.hkxpack.resources.DisplayProperties; 16 | import com.dexesttp.hkxpack.tagwriter.TagXMLWriter; 17 | 18 | /** 19 | * Testing interface, used to perform live tests with Eclipse. 20 | */ 21 | public final class Read { 22 | private static final Logger LOGGER = Logger.getLogger(Read.class.getName()); 23 | private Read() { 24 | // NO OP 25 | } 26 | /** 27 | * Tests a HKX file {@code .hkx} by reading it and writing it back under {@code .xml} 28 | * @param rootName the root directory 29 | * @param testName the file to test, without its extension 30 | */ 31 | public static void exec(final String rootName, final String name) { 32 | String inputFileName = rootName + name + ".hkx"; 33 | String outputFileName = rootName + name + ".xml"; 34 | DisplayProperties.displayDebugInfo = true; 35 | DisplayProperties.displayFileDebugInfo = true; 36 | DisplayProperties.displayReadTypesInfo = true; 37 | DisplayProperties.displayClassImportsInfo = true; 38 | DisplayProperties.displayEmbeddedData = true; 39 | try { 40 | // Read file 41 | File inFile = new File(inputFileName); 42 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 43 | HKXDescriptorFactory descriptorFactory = new HKXDescriptorFactory(enumResolver); 44 | HKXReader reader = new HKXReader(inFile, descriptorFactory, enumResolver); 45 | HKXFile hkxFile = reader.read(); 46 | 47 | // Write file 48 | File outFile = new File(outputFileName); 49 | TagXMLWriter writer = new TagXMLWriter(outFile); 50 | writer.write(hkxFile); 51 | } catch (IOException | TransformerException | ParserConfigurationException | InvalidPositionException e) { 52 | LOGGER.throwing(Read.class.getName(), "exec", e); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/object/ObjectSnap.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.object; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.data.HKXObject; 6 | import com.dexesttp.hkxpack.descriptor.HKXDescriptor; 7 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 8 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 9 | import com.dexesttp.hkxpack.descriptor.enums.HKXTypeFamily; 10 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 11 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 12 | 13 | /** 14 | * Handle object snapping 15 | */ 16 | final class ObjectSnap { 17 | private ObjectSnap() { 18 | // NO OP 19 | } 20 | 21 | static long getSnap(final HKXDescriptor descriptor, final HKXDescriptorFactory descriptorFactory) 22 | throws ClassFileReadException { 23 | long bestSnap = 0; 24 | List list = descriptor.getMemberTemplates(); 25 | for (int i = 0; i < list.size(); i++) { 26 | HKXMemberTemplate template = list.get(i); 27 | long currSnap = 0; 28 | if (template.vtype.getFamily() == HKXTypeFamily.ENUM) { 29 | currSnap = PrimitiveSnap.primitiveSnap(template.vsubtype); 30 | } else if (template.vtype == HKXType.TYPE_STRUCT) { 31 | HKXDescriptor internalDescriptor = descriptorFactory.get(template.target); 32 | currSnap = getSnap(internalDescriptor, descriptorFactory); 33 | } else { 34 | currSnap = PrimitiveSnap.primitiveSnap(template.vtype); 35 | } 36 | bestSnap = currSnap > bestSnap ? currSnap : bestSnap; 37 | } 38 | return bestSnap; 39 | } 40 | 41 | static long getSnap(final HKXObject object) { 42 | long bestSnap = 0; 43 | List list = object.getDescriptor().getMemberTemplates(); 44 | for (int i = 0; i < list.size(); i++) { 45 | HKXMemberTemplate template = list.get(i); 46 | long currSnap = 0; 47 | if (template.vtype.getFamily() == HKXTypeFamily.ENUM) { 48 | currSnap = PrimitiveSnap.primitiveSnap(template.vsubtype); 49 | } else if (template.vtype == HKXType.TYPE_STRUCT) { 50 | HKXObject internalObject = (HKXObject) object.getMembersList().get(i); 51 | currSnap = getSnap(internalObject); 52 | } else { 53 | currSnap = PrimitiveSnap.primitiveSnap(template.vtype); 54 | } 55 | bestSnap = currSnap > bestSnap ? currSnap : bestSnap; 56 | } 57 | return bestSnap; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/data/members/HKXPointerMember.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.data.members; 2 | 3 | import com.dexesttp.hkxpack.data.HKXObject; 4 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 5 | 6 | /** 7 | * A {@link HKXMember} referencing another {@link HKXObject} 8 | */ 9 | public class HKXPointerMember implements HKXMember { 10 | private final String name; 11 | private final HKXType type; 12 | private final HKXType subtype; 13 | private transient String targetObjectName; 14 | 15 | /** 16 | * Create a {@link HKXPointerMember}, a link to a given {@link HKXObject}. 17 | * 18 | * @param name the name of the {@link HKXPointerMember}. 19 | * @param type the type of the {@link HKXPointerMember}. Note that 20 | * the {@link HKXType#getFamily()} function should be 21 | * {@link HKXTypeFamily#POINTER}, although this isn't 22 | * checked. 23 | * @param subtype the type of the target of the 24 | * {@link HKXPointerMember}, if described in the 25 | * classXML. 26 | * @param targetObjectName the target object's name. 27 | */ 28 | public HKXPointerMember(final String name, final HKXType type, final HKXType subtype, 29 | final String targetObjectName) { 30 | this.name = name; 31 | this.type = type; 32 | this.subtype = subtype; 33 | this.targetObjectName = targetObjectName; 34 | } 35 | 36 | /** 37 | * Set this {@link HKXPointerMember}'s target name. 38 | * 39 | * @param targetObjectName the name of the target object 40 | */ 41 | public void set(final String targetObjectName) { 42 | this.targetObjectName = targetObjectName; 43 | } 44 | 45 | /** 46 | * Get this {@link HKXPointerMember}'s target name. 47 | * 48 | * @return the name of the target object. 49 | */ 50 | public String get() { 51 | return targetObjectName; 52 | } 53 | 54 | /** 55 | * get this {@link HKXPointerMember}'s subtype, if renseigned by the classXML. 56 | * 57 | * @return 58 | */ 59 | public HKXType getSubtype() { 60 | return subtype; 61 | } 62 | 63 | @Override 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | public String getName() { 68 | return name; 69 | } 70 | 71 | @Override 72 | /** 73 | * {@inheritDoc} 74 | */ 75 | public HKXType getType() { 76 | return type; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkxwriter/object/HKXInternalObjectHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkxwriter.object; 2 | 3 | import java.util.List; 4 | 5 | import com.dexesttp.hkxpack.data.HKXObject; 6 | import com.dexesttp.hkxpack.data.members.HKXMember; 7 | import com.dexesttp.hkxpack.descriptor.members.HKXMemberTemplate; 8 | import com.dexesttp.hkxpack.hkx.types.ObjectSizeResolver; 9 | import com.dexesttp.hkxpack.hkxwriter.object.callbacks.HKXMemberCallback; 10 | 11 | /** 12 | * Handles a {@link HKXObject} as a {@link HKXMember} for writing it back to a 13 | * HKX file. 14 | */ 15 | public class HKXInternalObjectHandler { 16 | private final transient HKXMemberHandlerFactory memberHandlerFactory; 17 | private final transient List memberCallbacks; 18 | 19 | /** 20 | * Associates a handler to a series of callbacks for writing to 21 | * 22 | * @param factory the {@link HKXMemberHandlerFactory} to use while 23 | * solving the {@link HKXObject}'s members. 24 | * @param memberCallbacks the list of {@link HKXMemberCallback} to add callbacks 25 | * into. 26 | */ 27 | public HKXInternalObjectHandler(final HKXMemberHandlerFactory factory, 28 | final List memberCallbacks) { 29 | this.memberHandlerFactory = factory; 30 | this.memberCallbacks = memberCallbacks; 31 | } 32 | 33 | /** 34 | * Writes the internal object back to the HKX File. 35 | * 36 | * @param objectAsMember the {@link HKXObject} to write, as a {@link HKXMember}. 37 | * @param currentPos the position of the class. 38 | * @return the new position. 39 | */ 40 | public long write(final HKXMember objectAsMember, final long currentPos) { 41 | HKXObject object = (HKXObject) objectAsMember; 42 | // Prepare the member handlers, and fill the raw structure. 43 | List members = object.getMembersList(); 44 | List memberTemplates = object.getDescriptor().getMemberTemplates(); 45 | for (int i = 0; i < memberTemplates.size(); i++) { 46 | HKXMember member = members.get(i); 47 | HKXMemberTemplate memberTemplate = memberTemplates.get(i); 48 | HKXMemberHandler memberHandler = memberHandlerFactory.create(memberTemplate.vtype, memberTemplate.offset); 49 | memberCallbacks.add(memberHandler.write(member, currentPos)); 50 | } 51 | return currentPos + ObjectSizeResolver.getSize(object); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /cli/src/debug/java/com/dexesttp/hkxpack/cli/components/Write.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.components; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.logging.Logger; 6 | 7 | import javax.xml.parsers.ParserConfigurationException; 8 | 9 | import org.xml.sax.SAXException; 10 | 11 | import com.dexesttp.hkxpack.data.HKXFile; 12 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 13 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 14 | import com.dexesttp.hkxpack.hkx.exceptions.UnsupportedVersionError; 15 | import com.dexesttp.hkxpack.hkxwriter.HKXWriter; 16 | import com.dexesttp.hkxpack.resources.DisplayProperties; 17 | import com.dexesttp.hkxpack.tagreader.TagXMLReader; 18 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 19 | 20 | /** 21 | * Testing interface, used to perform live tests with Eclipse. 22 | */ 23 | public final class Write { 24 | private static final Logger LOGGER = Logger.getLogger(Write.class.getName()); 25 | private Write() { 26 | // NO OP 27 | } 28 | /** 29 | * Tests a HKX file {@code .xml} by reading it and writing it back under {@code .hkx} 30 | * @param rootName the root directory 31 | * @param testName the file to test, without its extension 32 | */ 33 | public static void exec(final String rootName, final String testName) { 34 | String inputFileName = rootName + testName + ".xml"; 35 | String outputFileName = rootName + testName + "-new.hkx"; 36 | DisplayProperties.displayDebugInfo = true; 37 | DisplayProperties.displayFileDebugInfo = true; 38 | DisplayProperties.displayReadTypesInfo = true; 39 | DisplayProperties.displayClassImportsInfo = true; 40 | DisplayProperties.displayEmbeddedData = true; 41 | try { 42 | // Read file 43 | File inFile = new File(inputFileName); 44 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 45 | HKXDescriptorFactory descriptorFactory = new HKXDescriptorFactory(enumResolver); 46 | TagXMLReader reader = new TagXMLReader(inFile, descriptorFactory); 47 | HKXFile file = reader.read(); 48 | 49 | File outFile = new File(outputFileName); 50 | HKXWriter writer = new HKXWriter(outFile, enumResolver); 51 | writer.write(file); 52 | } catch (IOException | UnsupportedVersionError | ParserConfigurationException | SAXException | InvalidTagXMLException e) { 53 | LOGGER.throwing(Write.class.getName(), "exec", e); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cli/src/debug/java/com/dexesttp/hkxpack/cli/components/XMLTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.components; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.logging.Logger; 6 | 7 | import javax.xml.parsers.ParserConfigurationException; 8 | import javax.xml.transform.TransformerException; 9 | 10 | import org.xml.sax.SAXException; 11 | 12 | import com.dexesttp.hkxpack.data.HKXFile; 13 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 14 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 15 | import com.dexesttp.hkxpack.resources.DisplayProperties; 16 | import com.dexesttp.hkxpack.tagreader.TagXMLReader; 17 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 18 | import com.dexesttp.hkxpack.tagwriter.TagXMLWriter; 19 | 20 | /** 21 | * Testing interface, used to perform live tests with Eclipse. 22 | */ 23 | public final class XMLTest { 24 | private static final Logger LOGGER = Logger.getLogger(XMLTest.class.getName()); 25 | private XMLTest() { 26 | // NO OP 27 | } 28 | /** 29 | * Tests a XML file {@code .xml} by reading it and writing it back under {@code -new.xml} 30 | * @param rootName the root directory 31 | * @param testName the file to test, without its extension 32 | */ 33 | public static void exec(final String rootName, final String testName) { 34 | String inputFileName = rootName + testName + ".xml"; 35 | String outputFileName = rootName + testName + "-new.xml"; 36 | DisplayProperties.displayDebugInfo = true; 37 | DisplayProperties.displayFileDebugInfo = true; 38 | DisplayProperties.displayReadTypesInfo = true; 39 | DisplayProperties.displayClassImportsInfo = true; 40 | DisplayProperties.displayEmbeddedData = true; 41 | try { 42 | // Read XML file 43 | File inFile = new File(inputFileName); 44 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 45 | HKXDescriptorFactory descriptorFactory = new HKXDescriptorFactory(enumResolver); 46 | TagXMLReader reader = new TagXMLReader(inFile, descriptorFactory); 47 | HKXFile hkxFile = reader.read(); 48 | 49 | // Write XML file 50 | File outFile = new File(outputFileName); 51 | TagXMLWriter writer = new TagXMLWriter(outFile); 52 | writer.write(hkxFile); 53 | } catch (IOException | TransformerException | ParserConfigurationException | SAXException | InvalidTagXMLException e) { 54 | LOGGER.throwing(XMLTest.class.getName(), "exec", e); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /cli/src/debug/java/com/dexesttp/hkxpack/cli/components/HKXTest.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.cli.components; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.logging.Logger; 6 | 7 | import javax.xml.parsers.ParserConfigurationException; 8 | import javax.xml.transform.TransformerException; 9 | 10 | import org.xml.sax.SAXException; 11 | 12 | import com.dexesttp.hkxpack.data.HKXFile; 13 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 14 | import com.dexesttp.hkxpack.descriptor.HKXEnumResolver; 15 | import com.dexesttp.hkxpack.resources.DisplayProperties; 16 | import com.dexesttp.hkxpack.tagreader.TagXMLReader; 17 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 18 | import com.dexesttp.hkxpack.tagwriter.TagXMLWriter; 19 | 20 | /** 21 | * Testing interface, used to perform live tests with Eclipse. 22 | */ 23 | public final class HKXTest { 24 | private static final Logger LOGGER = Logger.getLogger(HKXTest.class.getName()); 25 | private HKXTest() { 26 | // NO OP 27 | } 28 | /** 29 | * Tests a HKX file {@code .hkx} by reading it and writing it back under {@code -new.hkx} 30 | * @param rootName the root directory 31 | * @param testName the file to test, without its extension 32 | */ 33 | public static void exec(final String rootName, final String testName) { 34 | String inputFileName = rootName + testName + ".hkx"; 35 | String outputFileName = rootName + testName + "-new.hkx"; 36 | DisplayProperties.displayDebugInfo = true; 37 | DisplayProperties.displayFileDebugInfo = true; 38 | DisplayProperties.displayReadTypesInfo = true; 39 | DisplayProperties.displayClassImportsInfo = true; 40 | DisplayProperties.displayEmbeddedData = true; 41 | try { 42 | // Read XML file 43 | File inFile = new File(inputFileName); 44 | HKXEnumResolver enumResolver = new HKXEnumResolver(); 45 | HKXDescriptorFactory descriptorFactory = new HKXDescriptorFactory(enumResolver); 46 | TagXMLReader reader = new TagXMLReader(inFile, descriptorFactory); 47 | HKXFile hkxFile = reader.read(); 48 | 49 | // Write XML file 50 | File outFile = new File(outputFileName); 51 | TagXMLWriter writer = new TagXMLWriter(outFile); 52 | writer.write(hkxFile); 53 | } catch (IOException | TransformerException | ParserConfigurationException | SAXException | InvalidTagXMLException e) { 54 | LOGGER.throwing(HKXTest.class.getName(), "exec", e); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /doc/uml/FileReader.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | package commons { 3 | package parser { 4 | interface Reader { 5 | +connect(RandomAccessFile, position) 6 | +T read() 7 | } 8 | abstract class ConstantReader 9 | abstract class FixedReader 10 | abstract class RandomReader 11 | Reader <|-- ConstantReader 12 | Reader <|-- FixedReader 13 | Reader <|-- RandomReader 14 | } 15 | package resolver { 16 | interface Definer { 17 | +setNext(Resolver) 18 | } 19 | interface Resolver 20 | Definer <|-- RandomReader 21 | Resolver o-- Definer : uses 22 | } 23 | } 24 | 25 | package hkxpack { 26 | package hkx { 27 | package reader { 28 | abstract class TripleLinkReader 29 | ConstantReader <|-- HeaderReader #green 30 | FixedReader <|-- InternalLinkReader #green 31 | RandomReader <|-- TypeReader #green 32 | FixedReader <|-- TripleLinkReader #green 33 | TripleLinkReader <|-- ExternalLinkReader 34 | TripleLinkReader <|-- VirtualLinkReader 35 | InternalLinkReader o-- HeaderReader : creates 36 | ExternalLinkReader o-- HeaderReader : creates 37 | VirtualLinkReader o-- HeaderReader : creates 38 | TypeReader o-- HeaderReader : creates 39 | } 40 | package flags { 41 | interface FlagHandler 42 | class FlagsRef { 43 | +resolve() 44 | } 45 | FlagHandler <|-- FlagsRef 46 | class FlagsData { 47 | ~byte Content 48 | } 49 | FlagsData o-- FlagsRef : resolves into 50 | FlagHandler <|-- FlagsData 51 | ExternalLinkReader *-- FlagHandler : T 52 | VirtualLinkReader *-- FlagHandler : T 53 | } 54 | package classes { 55 | interface ClassHandler 56 | class ClassRef { 57 | +resolve() 58 | } 59 | ClassHandler <|-- ClassRef 60 | class ResolvedClass 61 | ClassHandler <|-- ResolvedClass 62 | ResolvedClass o-- ClassRef : resolved into 63 | TypeReader *-- ResolvedClass : T 64 | InternalLinkReader *-- ClassRef : T 65 | } 66 | } 67 | package xml { 68 | package classxml { 69 | class ClassXMLList << (S, #FF7700) >> { 70 | +ClassXMLDescriptor getClass(String) 71 | } 72 | class ClassXMLDescriptor 73 | class ClassXMLResolver 74 | Resolver <|-- ClassXMLResolver 75 | ClassXMLDescriptor o-u- ClassXMLResolver : uses 76 | ClassXMLList o-u- ClassXMLResolver : uses 77 | } 78 | } 79 | package main { 80 | class Main { 81 | } 82 | HeaderReader o-- Main #blue 83 | ClassHandler o-- Main #blue 84 | FlagHandler o-- Main #blue 85 | ClassXMLList o-- Main #blue 86 | } 87 | } 88 | @enduml 89 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/tagreader/TagXMLNodeHandler.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.tagreader; 2 | 3 | import org.w3c.dom.Node; 4 | 5 | import com.dexesttp.hkxpack.data.HKXObject; 6 | import com.dexesttp.hkxpack.data.members.HKXMember; 7 | import com.dexesttp.hkxpack.descriptor.HKXDescriptorFactory; 8 | import com.dexesttp.hkxpack.descriptor.exceptions.ClassFileReadException; 9 | import com.dexesttp.hkxpack.resources.DOMUtils; 10 | import com.dexesttp.hkxpack.tagreader.exceptions.InvalidTagXMLException; 11 | 12 | /** 13 | * Handles general node data retrieval, discrimining between root 14 | * {@link HKXObject}, embedded {@link HKXObject} and {@link HKXMember}s. 15 | */ 16 | public class TagXMLNodeHandler { 17 | private final transient TagXMLObjectHandler objectHandler; 18 | 19 | TagXMLNodeHandler(final HKXDescriptorFactory descriptorFactory) { 20 | TagXMLMemberHandler memberHandler = new TagXMLMemberHandler(this, descriptorFactory); 21 | this.objectHandler = new TagXMLObjectHandler(descriptorFactory, memberHandler); 22 | } 23 | 24 | /** 25 | * Handles an object {@link Node} into a {@link HKXObject}. 26 | * 27 | * @param objectNode the {@link Node} to handle. 28 | * @return the relevant {@link HKXObject}. 29 | * @throws ClassFileReadException if there was a problem reading the Class data 30 | * from the program's resources. 31 | * @throws InvalidTagXMLException if there was an error parsing the TagXML file. 32 | */ 33 | HKXObject handleObject(final Node objectNode) throws ClassFileReadException, InvalidTagXMLException { 34 | // Retrieve descriptor 35 | String className = DOMUtils.getNodeAttr("class", objectNode); 36 | return objectHandler.handleObject(objectNode, className); 37 | } 38 | 39 | /** 40 | * Handles a subobject (an object with no name nor class, but the className is 41 | * given). 42 | * 43 | * @param objectNode the {@link Node} to read the object from. 44 | * @param className the class name of the object. 45 | * @return the relevant {@link HKXObject}. 46 | * @throws ClassFileReadException if there was a problem reading the Class data 47 | * from the program's resources. 48 | * @throws InvalidTagXMLException if there was an error parsing the TagXML file. 49 | */ 50 | public HKXObject handleSubObject(final Node objectNode, final String className) 51 | throws ClassFileReadException, InvalidTagXMLException { 52 | return objectHandler.handleObject(objectNode, className); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/handlers/BigMemberHandlers.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.handlers; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 4 | import com.dexesttp.hkxpack.data.members.HKXMember; 5 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 6 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 7 | 8 | /** 9 | * Handles big (64) members 10 | */ 11 | final class BigMemberHandlers { 12 | private BigMemberHandlers() { 13 | // NO OP 14 | } 15 | 16 | static MemberHandler createMemberHandler(final HKXType type) { 17 | switch (type) { 18 | case TYPE_UINT64: 19 | case TYPE_ULONG: 20 | return new UInt64Handler(); 21 | case TYPE_INT64: 22 | return new SInt64Handler(); 23 | default: 24 | return null; 25 | } 26 | } 27 | 28 | /** 29 | * Handles UInt64/ULong 30 | */ 31 | static class UInt64Handler implements MemberHandler { 32 | @Override 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | public long getSize() { 37 | return 0x08; 38 | } 39 | 40 | @Override 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | public HKXMember createMember(final String name, final HKXType type, final byte[] byteArray) { 45 | HKXDirectMember member3 = new HKXDirectMember<>(name, type); 46 | member3.set((long) ByteUtils.getULong(byteArray)); 47 | return member3; 48 | } 49 | 50 | @SuppressWarnings("unchecked") 51 | @Override 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | public byte[] readMember(final HKXMember member) { 56 | HKXDirectMember memberUInt64 = (HKXDirectMember) member; 57 | return ByteUtils.fromULong(memberUInt64.get(), 8); 58 | } 59 | } 60 | 61 | /** 62 | * Handles Int64 63 | */ 64 | static class SInt64Handler implements MemberHandler { 65 | @Override 66 | /** 67 | * {@inheritDoc} 68 | */ 69 | public long getSize() { 70 | return 0x08; 71 | } 72 | 73 | @Override 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | public HKXMember createMember(final String name, final HKXType type, final byte[] byteArray) { 78 | HKXDirectMember member4 = new HKXDirectMember<>(name, type); 79 | member4.set((long) ByteUtils.getSInt(byteArray)); 80 | return member4; 81 | } 82 | 83 | @SuppressWarnings("unchecked") 84 | @Override 85 | /** 86 | * {@inheritDoc} 87 | */ 88 | public byte[] readMember(final HKXMember member) { 89 | HKXDirectMember memberInt64 = (HKXDirectMember) member; 90 | return ByteUtils.fromULong(memberInt64.get(), 8); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/handlers/MediumMemberHandlers.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.handlers; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 4 | import com.dexesttp.hkxpack.data.members.HKXMember; 5 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 6 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 7 | 8 | /** 9 | * Handles medium (16-32) members 10 | */ 11 | class MediumMemberHandlers { 12 | private MediumMemberHandlers() { 13 | // NO OP 14 | } 15 | 16 | static MemberHandler createMemberHandler(final HKXType type) { 17 | switch (type) { 18 | case TYPE_UINT16: 19 | return new UInt16MemberHandler(); 20 | case TYPE_INT16: 21 | return new SInt16MemberHandler(); 22 | default: 23 | return null; 24 | } 25 | } 26 | 27 | /** 28 | * Handles UInt16 29 | */ 30 | static class UInt16MemberHandler implements MemberHandler { 31 | @Override 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public long getSize() { 36 | return 0x02; 37 | } 38 | 39 | @Override 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | public HKXMember createMember(final String name, final HKXType type, final byte[] byteArray) { 44 | HKXDirectMember member3 = new HKXDirectMember<>(name, type); 45 | member3.set((int) ByteUtils.getUInt(byteArray)); 46 | return member3; 47 | } 48 | 49 | @SuppressWarnings("unchecked") 50 | @Override 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public byte[] readMember(final HKXMember member) { 55 | HKXDirectMember memberUInt16 = (HKXDirectMember) member; 56 | return ByteUtils.fromULong(memberUInt16.get(), 2); 57 | } 58 | } 59 | 60 | /** 61 | * Handles Int16 62 | */ 63 | static class SInt16MemberHandler implements MemberHandler { 64 | @Override 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | public long getSize() { 69 | return 0x02; 70 | } 71 | 72 | @Override 73 | /** 74 | * {@inheritDoc} 75 | */ 76 | public HKXMember createMember(final String name, final HKXType type, final byte[] byteArray) { 77 | HKXDirectMember member4 = new HKXDirectMember<>(name, type); 78 | member4.set((int) ByteUtils.getSInt(byteArray)); 79 | return member4; 80 | } 81 | 82 | @SuppressWarnings("unchecked") 83 | @Override 84 | /** 85 | * {@inheritDoc} 86 | */ 87 | public byte[] readMember(final HKXMember member) { 88 | HKXDirectMember memberInt16 = (HKXDirectMember) member; 89 | return ByteUtils.fromSLong(memberInt16.get(), 2); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/descriptor/HKXEnumResolver.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.descriptor; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import com.dexesttp.hkxpack.descriptor.reader.ClassXMLReader; 7 | import com.google.common.collect.BiMap; 8 | 9 | /** 10 | * An HKXEnumResolver stores all read enumerations as accessible values. 11 | */ 12 | public class HKXEnumResolver { 13 | private final transient Map contents = new HashMap<>(); 14 | 15 | /** 16 | * An enumeration extracted from a classXML file. 17 | */ 18 | private class HKXEnum { 19 | private final transient BiMap contents; 20 | 21 | HKXEnum(final BiMap contents) { 22 | this.contents = contents; 23 | } 24 | 25 | String get(final int index) { 26 | if (contents.containsValue(index)) { 27 | return contents.inverse().get(index); 28 | } 29 | return Integer.toString(index); 30 | } 31 | 32 | int get(final String enumName) { 33 | if (contents.containsKey(enumName)) { 34 | return contents.get(enumName); 35 | } 36 | try { 37 | return Integer.parseInt(enumName); 38 | } catch (NumberFormatException e) { 39 | return 0; 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Add a new Enum to the resolver. 46 | *

47 | * This should only be done by the {@link ClassXMLReader} method. Please don't 48 | * use this unless you're doing something technical. 49 | * 50 | * @param name 51 | * @param contents 52 | */ 53 | public void add(final String name, final BiMap contents) { 54 | this.contents.put(name, new HKXEnum(contents)); 55 | } 56 | 57 | /** 58 | * Resolve a value from a given enumeration into its name. 59 | * 60 | * @param enumName 61 | * @param value 62 | * @return 63 | */ 64 | public String resolve(final String enumName, final int value) { 65 | HKXEnum enumContainer = contents.get(enumName); 66 | if (enumContainer != null) { 67 | return enumContainer.get(value); 68 | } 69 | return Integer.toString(value); 70 | } 71 | 72 | /** 73 | * Resolve a name from a given enumeration into its value. 74 | * 75 | * @param enumName 76 | * @param value 77 | * @return 78 | */ 79 | public int resolve(final String enumName, final String value) { 80 | HKXEnum enumContainer = contents.get(enumName); 81 | if (enumContainer == null) { 82 | try { 83 | return Integer.parseInt(value); 84 | } catch (NumberFormatException e) { 85 | return 0; 86 | } 87 | } 88 | return enumContainer.get(value); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /core/src/main/java/com/dexesttp/hkxpack/hkx/types/handlers/NormalMemberHandlers.java: -------------------------------------------------------------------------------- 1 | package com.dexesttp.hkxpack.hkx.types.handlers; 2 | 3 | import com.dexesttp.hkxpack.data.members.HKXDirectMember; 4 | import com.dexesttp.hkxpack.data.members.HKXMember; 5 | import com.dexesttp.hkxpack.descriptor.enums.HKXType; 6 | import com.dexesttp.hkxpack.resources.byteutils.ByteUtils; 7 | 8 | /** 9 | * Handles normal (32) members 10 | */ 11 | final class NormalMemberHandlers { 12 | private NormalMemberHandlers() { 13 | // NO OP 14 | } 15 | 16 | static MemberHandler createMemberHandler(final HKXType type) { 17 | switch (type) { 18 | case TYPE_UINT32: 19 | return new UInt32MemberHandler(); 20 | case TYPE_INT32: 21 | return new SInt32MemberHandler(); 22 | default: 23 | return null; 24 | } 25 | } 26 | 27 | /** 28 | * Handles UInt32 29 | */ 30 | static class UInt32MemberHandler implements MemberHandler { 31 | @Override 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public long getSize() { 36 | return 0x04; 37 | } 38 | 39 | @Override 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | public HKXMember createMember(final String name, final HKXType type, final byte[] byteArray) { 44 | HKXDirectMember member3 = new HKXDirectMember<>(name, type); 45 | member3.set((int) ByteUtils.getUInt(byteArray)); 46 | return member3; 47 | } 48 | 49 | @SuppressWarnings("unchecked") 50 | @Override 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public byte[] readMember(final HKXMember member) { 55 | HKXDirectMember memberUInt32 = (HKXDirectMember) member; 56 | return ByteUtils.fromULong(memberUInt32.get(), 4); 57 | } 58 | } 59 | 60 | /** 61 | * Handles Int32 62 | */ 63 | static class SInt32MemberHandler implements MemberHandler { 64 | @Override 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | public long getSize() { 69 | return 0x04; 70 | } 71 | 72 | @Override 73 | /** 74 | * {@inheritDoc} 75 | */ 76 | public HKXMember createMember(final String name, final HKXType type, final byte[] byteArray) { 77 | HKXDirectMember member4 = new HKXDirectMember<>(name, type); 78 | member4.set((int) ByteUtils.getSInt(byteArray)); 79 | return member4; 80 | } 81 | 82 | @SuppressWarnings("unchecked") 83 | @Override 84 | /** 85 | * {@inheritDoc} 86 | */ 87 | public byte[] readMember(final HKXMember member) { 88 | HKXDirectMember memberInt32 = (HKXDirectMember) member; 89 | return ByteUtils.fromSLong(memberInt32.get(), 4); 90 | } 91 | } 92 | } 93 | --------------------------------------------------------------------------------