From b1d00fce8da901b31fa52ea59b4bc3c8edb9d9cc Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Fri, 11 Jan 2013 02:25:40 +0100 Subject: CMake build system, big pile of libs: bspatch, quazip, java, the launcher --- .gitignore | 5 + CMakeLists.txt | 132 ++ config.h.in | 17 + java/annotations.cpp | 83 + java/annotations.h | 252 +++ java/classfile.h | 153 ++ java/constants.h | 208 +++ java/endian.h | 62 + java/errors.h | 6 + java/javautils.cpp | 69 + java/javautils.h | 9 + java/membuffer.h | 64 + java/test.cpp | 35 + launcher/CMakeLists.txt | 23 + launcher/MCFrame.java | 123 ++ launcher/MultiMCLauncher.java | 331 ++++ launcher/UseJava.cmake | 881 +++++++++++ launcher/UseJavaClassFilelist.cmake | 52 + launcher/UseJavaSymlinks.cmake | 32 + launcher/net/minecraft/Launcher.java | 154 ++ .../org/simplericity/macify/eawt/Application.java | 176 +++ .../macify/eawt/ApplicationAdapter.java | 48 + .../simplericity/macify/eawt/ApplicationEvent.java | 25 + .../macify/eawt/ApplicationListener.java | 27 + .../macify/eawt/DefaultApplication.java | 418 +++++ multimc.qrc | 57 +- multimc_pragma.h | 49 + patchlib/CMakeLists.txt | 14 + patchlib/LICENSE-bzip2 | 42 + patchlib/blocksort.c | 1095 +++++++++++++ patchlib/bspatch.c | 303 ++++ patchlib/bspatch.h | 27 + patchlib/bzlib.c | 1579 +++++++++++++++++++ patchlib/bzlib.h | 283 ++++ patchlib/bzlib_private.h | 510 +++++++ patchlib/compress.c | 672 ++++++++ patchlib/crctable.c | 104 ++ patchlib/decompress.c | 646 ++++++++ patchlib/huffman.c | 205 +++ patchlib/randtable.c | 84 + quazip/CMakeLists.txt | 26 + quazip/JlCompress.cpp | 469 ++++++ quazip/JlCompress.h | 114 ++ quazip/crypt.h | 135 ++ quazip/ioapi.h | 77 + quazip/qioapi.cpp | 146 ++ quazip/quaadler32.cpp | 28 + quazip/quaadler32.h | 29 + quazip/quachecksum32.h | 54 + quazip/quacrc32.cpp | 28 + quazip/quacrc32.h | 26 + quazip/quagzipfile.cpp | 141 ++ quazip/quagzipfile.h | 35 + quazip/quaziodevice.cpp | 283 ++++ quazip/quaziodevice.h | 27 + quazip/quazip.cpp | 554 +++++++ quazip/quazip.h | 411 +++++ quazip/quazip_global.h | 55 + quazip/quazipdir.cpp | 507 +++++++ quazip/quazipdir.h | 171 +++ quazip/quazipfile.cpp | 488 ++++++ quazip/quazipfile.h | 442 ++++++ quazip/quazipfileinfo.h | 66 + quazip/quazipnewinfo.cpp | 51 + quazip/quazipnewinfo.h | 102 ++ quazip/unzip.c | 1603 ++++++++++++++++++++ quazip/unzip.h | 356 +++++ quazip/zip.c | 1281 ++++++++++++++++ quazip/zip.h | 245 +++ 69 files changed, 16978 insertions(+), 27 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 config.h.in create mode 100644 java/annotations.cpp create mode 100644 java/annotations.h create mode 100644 java/classfile.h create mode 100644 java/constants.h create mode 100644 java/endian.h create mode 100644 java/errors.h create mode 100644 java/javautils.cpp create mode 100644 java/javautils.h create mode 100644 java/membuffer.h create mode 100644 java/test.cpp create mode 100644 launcher/CMakeLists.txt create mode 100644 launcher/MCFrame.java create mode 100644 launcher/MultiMCLauncher.java create mode 100644 launcher/UseJava.cmake create mode 100644 launcher/UseJavaClassFilelist.cmake create mode 100644 launcher/UseJavaSymlinks.cmake create mode 100644 launcher/net/minecraft/Launcher.java create mode 100644 launcher/org/simplericity/macify/eawt/Application.java create mode 100644 launcher/org/simplericity/macify/eawt/ApplicationAdapter.java create mode 100644 launcher/org/simplericity/macify/eawt/ApplicationEvent.java create mode 100644 launcher/org/simplericity/macify/eawt/ApplicationListener.java create mode 100644 launcher/org/simplericity/macify/eawt/DefaultApplication.java create mode 100644 multimc_pragma.h create mode 100644 patchlib/CMakeLists.txt create mode 100644 patchlib/LICENSE-bzip2 create mode 100644 patchlib/blocksort.c create mode 100644 patchlib/bspatch.c create mode 100644 patchlib/bspatch.h create mode 100644 patchlib/bzlib.c create mode 100644 patchlib/bzlib.h create mode 100644 patchlib/bzlib_private.h create mode 100644 patchlib/compress.c create mode 100644 patchlib/crctable.c create mode 100644 patchlib/decompress.c create mode 100644 patchlib/huffman.c create mode 100644 patchlib/randtable.c create mode 100644 quazip/CMakeLists.txt create mode 100644 quazip/JlCompress.cpp create mode 100644 quazip/JlCompress.h create mode 100644 quazip/crypt.h create mode 100644 quazip/ioapi.h create mode 100644 quazip/qioapi.cpp create mode 100644 quazip/quaadler32.cpp create mode 100644 quazip/quaadler32.h create mode 100644 quazip/quachecksum32.h create mode 100644 quazip/quacrc32.cpp create mode 100644 quazip/quacrc32.h create mode 100644 quazip/quagzipfile.cpp create mode 100644 quazip/quagzipfile.h create mode 100644 quazip/quaziodevice.cpp create mode 100644 quazip/quaziodevice.h create mode 100644 quazip/quazip.cpp create mode 100644 quazip/quazip.h create mode 100644 quazip/quazip_global.h create mode 100644 quazip/quazipdir.cpp create mode 100644 quazip/quazipdir.h create mode 100644 quazip/quazipfile.cpp create mode 100644 quazip/quazipfile.h create mode 100644 quazip/quazipfileinfo.h create mode 100644 quazip/quazipnewinfo.cpp create mode 100644 quazip/quazipnewinfo.h create mode 100644 quazip/unzip.c create mode 100644 quazip/unzip.h create mode 100644 quazip/zip.c create mode 100644 quazip/zip.h diff --git a/.gitignore b/.gitignore index 085e8baf..529735db 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ Thumbs.db +.kdev4 +MultiMC5.kdev4 +build +resources/CMakeFiles +resources/MultiMCLauncher.jar diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..45f1ddb0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,132 @@ +cmake_minimum_required(VERSION 2.8.9) +project(multimc5) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + + +#### Check for machine endianness #### +INCLUDE(TestBigEndian) +TEST_BIG_ENDIAN(BIGENDIAN) +IF(${BIGENDIAN}) + ADD_DEFINITIONS(-DMULTIMC_BIG_ENDIAN) +ENDIF(${BIGENDIAN}) + + +#### Find the required Qt parts #### +find_package(Qt5Widgets) +#find_package(Qt5Declarative) + +include_directories(${Qt5Widgets_INCLUDE_DIRS}) + +# find ZLIB for quazip +find_package(ZLIB REQUIRED) + +# Find boost. +set(Boost_USE_STATIC_LIBS ON) +MESSAGE(STATUS "** Finding Boost...") +find_package(Boost 1.46.0 REQUIRED) +MESSAGE(STATUS "** Boost Include: ${Boost_INCLUDE_DIR}") +MESSAGE(STATUS "** Boost Libraries: ${Boost_LIBRARY_DIRS}") + +# Include boost. +include_directories("${Boost_INCLUDE_DIRS}") + +# Add quazip +add_subdirectory(quazip) + +# Add bspatch +add_subdirectory(patchlib) +include_directories(patchlib) + +# add the java launcher +add_subdirectory(launcher) + +IF(UNIX) + # assume GCC, add C++0x/C++11 stuff + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +ELSEIF(MINGW) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x") +ENDIF() + +# Set the path where CMake will look for modules. +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}") + + +set(MultiMC_VERSION_MAJOR 5) +set(MultiMC_VERSION_MINOR 0) +set(MultiMC_VERSION_REV 0) + +SET(MultiMC_VERSION_BUILD 0 CACHE STRING "Build number.") +message(STATUS "MultiMC build #${MultiMC_VERSION_BUILD}") + +IF (DEFINED MultiMC_BUILD_TAG) + message(STATUS "Build tag: ${MultiMC_BUILD_TAG}") +ELSE () + message(STATUS "No build tag specified.") +ENDIF () + +if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + set (MultiMC_ARCH "x64" + CACHE STRING "Architecture we're building for.") +else() + set (MultiMC_ARCH "x86" + CACHE STRING "Architecture we're building for.") +endif() +message (STATUS "Architecture is ${MultiMC_ARCH}") + +SET(MultiMC_Extra_Label "") + +IF (WIN32) + SET(MultiMC_JOB_NAME "MultiMC4Windows" CACHE STRING "Jenkins job name.") +ELSEIF(UNIX AND APPLE) + SET(MultiMC_JOB_NAME "MultiMC4OSX" CACHE STRING "Jenkins job name.") + # This is here because the scheme doesn't exactly apply to every kind of build... + SET(MultiMC_Extra_Label ",label=osx") +ELSE() + SET(MultiMC_JOB_NAME "MultiMC4Linux" CACHE STRING "Jenkins job name.") +ENDIF() + +SET(MultiMC_JOB_URL "http://ci.forkk.net/job/${MultiMC_JOB_NAME}/arch=${MultiMC_ARCH}${MultiMC_Extra_Label}/" + CACHE STRING "URL of the jenkins job to pull updates from.") +message(STATUS "Job URL: ${MultiMC_JOB_URL}") + +configure_file("${PROJECT_SOURCE_DIR}/config.h.in" + "${PROJECT_BINARY_DIR}/config.h") + + +SET(MULTIMC_SOURCES +main.cpp +gui/mainwindow.cpp +data/instancebase.cpp +util/pathutils.cpp + +java/javautils.cpp +java/annotations.cpp +) + +SET(MULTIMC_HEADERS +gui/mainwindow.h +data/instancebase.h +util/pathutils.h +multimc_pragma.h + +java/annotations.h +java/classfile.h +java/constants.h +java/endian.h +java/errors.h +java/javautils.h +java/membuffer.h +) + +SET_SOURCE_FILES_PROPERTIES(resources/MultiMCLauncher.jar GENERATED) + +QT5_WRAP_UI(MULTIMC_UI gui/mainwindow.ui) +QT5_ADD_RESOURCES(MULTIMC_QRC multimc.qrc) + +add_executable(multimc5 ${MULTIMC_SOURCES} ${MULTIMC_HEADERS} ${MULTIMC_UI} ${MULTIMC_QRC}) +qt5_use_modules(multimc5 Widgets) +link_libraries(multimc5 quazip patchlib) +add_dependencies(multimc5 MultiMCLauncher) +install(TARGETS multimc5 RUNTIME DESTINATION bin) diff --git a/config.h.in b/config.h.in new file mode 100644 index 00000000..de53ac93 --- /dev/null +++ b/config.h.in @@ -0,0 +1,17 @@ +#define VERSION_MAJOR @MultiMC_VERSION_MAJOR@ +#define VERSION_MINOR @MultiMC_VERSION_MINOR@ +#define VERSION_REVISION @MultiMC_VERSION_REV@ +#define VERSION_BUILD @MultiMC_VERSION_BUILD@ + +#define VERSION_STR "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_REV@.@MultiMC_VERSION_BUILD@" + +#define x86 1 +#define x64 2 + +#define ARCH @MultiMC_ARCH@ + +#define JENKINS_BUILD_TAG "@MultiMC_BUILD_TAG@" + +#define JENKINS_JOB_URL "@MultiMC_JOB_URL@" + +#define USE_HTTPS @MultiMC_USE_HTTPS@ diff --git a/java/annotations.cpp b/java/annotations.cpp new file mode 100644 index 00000000..fc0c98fa --- /dev/null +++ b/java/annotations.cpp @@ -0,0 +1,83 @@ +#include "classfile.h" +#include "annotations.h" +#include + +namespace java +{ + std::string annotation::toString() + { + std::ostringstream ss; + ss << "Annotation type : " << type_index << " - " << pool[type_index].str_data << std::endl; + ss << "Contains " << name_val_pairs.size() << " pairs:" << std::endl; + for(unsigned i = 0; i < name_val_pairs.size(); i++) + { + std::pair &val = name_val_pairs[i]; + auto name_idx = val.first; + ss << pool[name_idx].str_data << "(" << name_idx << ")" << " = " << val.second->toString() << std::endl; + } + return ss.str(); + } + + annotation * annotation::read (util::membuffer& input, constant_pool& pool) + { + uint16_t type_index = 0; + input.read_be(type_index); + annotation * ann = new annotation(type_index,pool); + + uint16_t num_pairs = 0; + input.read_be(num_pairs); + while(num_pairs) + { + uint16_t name_idx = 0; + // read name index + input.read_be(name_idx); + auto elem = element_value::readElementValue(input,pool); + // read value + ann->add_pair(name_idx, elem); + num_pairs --; + } + return ann; + } + + element_value* element_value::readElementValue ( util::membuffer& input, java::constant_pool& pool ) + { + element_value_type type = INVALID; + input.read(type); + uint16_t index = 0; + uint16_t index2 = 0; + std::vector vals; + switch (type) + { + case PRIMITIVE_BYTE: + case PRIMITIVE_CHAR: + case PRIMITIVE_DOUBLE: + case PRIMITIVE_FLOAT: + case PRIMITIVE_INT: + case PRIMITIVE_LONG: + case PRIMITIVE_SHORT: + case PRIMITIVE_BOOLEAN: + case STRING: + input.read_be(index); + return new element_value_simple(type, index, pool); + case ENUM_CONSTANT: + input.read_be(index); + input.read_be(index2); + return new element_value_enum(type, index, index2, pool); + case CLASS: // Class + input.read_be(index); + return new element_value_class(type, index, pool); + case ANNOTATION: // Annotation + // FIXME: runtime visibility info needs to be passed from parent + return new element_value_annotation(ANNOTATION, annotation::read(input, pool), pool); + case ARRAY: // Array + input.read_be(index); + for (int i = 0; i < index; i++) + { + vals.push_back(element_value::readElementValue(input, pool)); + } + return new element_value_array(ARRAY, vals, pool); + default: + throw new java::classfile_exception(); + } + } +} \ No newline at end of file diff --git a/java/annotations.h b/java/annotations.h new file mode 100644 index 00000000..b115dc0b --- /dev/null +++ b/java/annotations.h @@ -0,0 +1,252 @@ +#pragma once +#include "classfile.h" +#include +#include + +namespace java +{ + enum element_value_type : uint8_t + { + INVALID = 0, + STRING = 's', + ENUM_CONSTANT = 'e', + CLASS = 'c', + ANNOTATION = '@', + ARRAY = '[', // one array dimension + PRIMITIVE_INT = 'I', // integer + PRIMITIVE_BYTE = 'B', // signed byte + PRIMITIVE_CHAR = 'C', // Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16 + PRIMITIVE_DOUBLE = 'D', // double-precision floating-point value + PRIMITIVE_FLOAT = 'F', // single-precision floating-point value + PRIMITIVE_LONG = 'J', // long integer + PRIMITIVE_SHORT = 'S', // signed short + PRIMITIVE_BOOLEAN = 'Z' // true or false + }; + /** + * The element_value structure is a discriminated union representing the value of an element-value pair. + * It is used to represent element values in all attributes that describe annotations + * - RuntimeVisibleAnnotations + * - RuntimeInvisibleAnnotations + * - RuntimeVisibleParameterAnnotations + * - RuntimeInvisibleParameterAnnotations). + * + * The element_value structure has the following format: + */ + class element_value + { + protected: + element_value_type type; + constant_pool & pool; + + public: + element_value(element_value_type type, constant_pool & pool): type(type), pool(pool) {}; + + element_value_type getElementValueType() + { + return type; + } + + virtual std::string toString() = 0; + + static element_value * readElementValue(util::membuffer & input, constant_pool & pool); + }; + + /** + * Each value of the annotations table represents a single runtime-visible annotation on a program element. + * The annotation structure has the following format: + */ + class annotation + { + public: + typedef std::vector< std::pair > value_list; + protected: + /** + * The value of the type_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing a field descriptor representing the annotation type corresponding + * to the annotation represented by this annotation structure. + */ + uint16_t type_index; + /** + * map between element_name_index and value. + * + * The value of the element_name_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure representing + * a valid field descriptor (§4.3.2) that denotes the name of the annotation type element represented + * by this element_value_pairs entry. + */ + value_list name_val_pairs; + /** + * Reference to the parent constant pool + */ + constant_pool & pool; + public: + annotation(uint16_t type_index, constant_pool& pool):type_index(type_index), pool(pool) {}; + ~annotation() + { + for(unsigned i = 0 ; i < name_val_pairs.size(); i++) + { + delete name_val_pairs[i].second; + } + } + void add_pair(uint16_t key, element_value * value) + { + name_val_pairs.push_back(std::make_pair(key, value)); + }; + value_list::const_iterator begin() + { + return name_val_pairs.cbegin(); + } + value_list::const_iterator end() + { + return name_val_pairs.cend(); + } + std::string toString(); + static annotation * read(util::membuffer & input, constant_pool & pool); + }; + typedef std::vector annotation_table; + + + /// type for simple value annotation elements + class element_value_simple : public element_value + { + protected: + /// index of the constant in the constant pool + uint16_t index; + public: + element_value_simple(element_value_type type, uint16_t index , constant_pool& pool): + element_value(type, pool), index(index) + { + // TODO: verify consistency + }; + uint16_t getIndex() + { + return index; + } + virtual std::string toString() + { + return pool[index].toString(); + }; + }; + /// The enum_const_value item is used if the tag item is 'e'. + class element_value_enum : public element_value + { + protected: + /** + * The value of the type_name_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing a valid field descriptor (§4.3.2) that denotes the internal form of the binary + * name (§4.2.1) of the type of the enum constant represented by this element_value structure. + */ + uint16_t typeIndex; + /** + * The value of the const_name_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing the simple name of the enum constant represented by this element_value structure. + */ + uint16_t valueIndex; + public: + element_value_enum(element_value_type type, uint16_t typeIndex, uint16_t valueIndex, constant_pool& pool): + element_value(type, pool), typeIndex(typeIndex), valueIndex(valueIndex) + { + // TODO: verify consistency + } + uint16_t getValueIndex() + { + return valueIndex; + } + uint16_t getTypeIndex() + { + return typeIndex; + } + virtual std::string toString() + { + return "enum value"; + }; + }; + + class element_value_class : public element_value + { + protected: + /** + * The class_info_index item must be a valid index into the constant_pool table. + * The constant_pool entry at that index must be a CONSTANT_Utf8_info (§4.4.7) structure + * representing the return descriptor (§4.3.3) of the type that is reified by the class + * represented by this element_value structure. + * + * For example, 'V' for Void.class, 'Ljava/lang/Object;' for Object, etc. + * + * Or in plain english, you can store type information in annotations. Yay. + */ + uint16_t classIndex; + public: + element_value_class(element_value_type type, uint16_t classIndex, constant_pool& pool): + element_value(type, pool), classIndex(classIndex) + { + // TODO: verify consistency + } + uint16_t getIndex() + { + return classIndex; + } + virtual std::string toString() + { + return "class"; + }; + }; + + /// nested annotations... yay + class element_value_annotation : public element_value + { + private: + annotation * nestedAnnotation; + public: + element_value_annotation(element_value_type type, annotation * nestedAnnotation, constant_pool& pool): + element_value(type, pool), nestedAnnotation(nestedAnnotation) + {}; + ~element_value_annotation() + { + if(nestedAnnotation) + { + delete nestedAnnotation; + nestedAnnotation = nullptr; + } + } + virtual std::string toString() + { + return "nested annotation"; + }; + }; + + /// and arrays! + class element_value_array : public element_value + { + public: + typedef std::vector elem_vec; + protected: + elem_vec values; + public: + element_value_array ( element_value_type type, std::vector & values, constant_pool& pool ): + element_value(type, pool), values(values) + {}; + ~element_value_array () + { + for(unsigned i = 0; i < values.size();i++) + { + delete values[i]; + } + }; + elem_vec::const_iterator begin() + { + return values.cbegin(); + } + elem_vec::const_iterator end() + { + return values.cend(); + } + virtual std::string toString() + { + return "array"; + }; + }; +} \ No newline at end of file diff --git a/java/classfile.h b/java/classfile.h new file mode 100644 index 00000000..33207e99 --- /dev/null +++ b/java/classfile.h @@ -0,0 +1,153 @@ +#pragma once +#include "membuffer.h" +#include "constants.h" +#include "annotations.h" +#include +namespace java +{ + /** + * Class representing a Java .class file + */ + class classfile : public util::membuffer + { + public: + classfile(char * data, std::size_t size) : membuffer(data, size) + { + valid = false; + is_synthetic = false; + read_be(magic); + if(magic != 0xCAFEBABE) + throw new classfile_exception(); + read_be(minor_version); + read_be(major_version); + constants.load(*this); + read_be(access_flags); + read_be(this_class); + read_be(super_class); + + // Interfaces + uint16_t iface_count = 0; + read_be(iface_count); + while (iface_count) + { + uint16_t iface; + read_be(iface); + interfaces.push_back(iface); + iface_count --; + } + + // Fields + // read fields (and attributes from inside fields) (and possible inner classes. yay for recursion!) + // for now though, we will ignore all attributes + /* + * field_info + * { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ + uint16_t field_count = 0; + read_be(field_count); + while (field_count) + { + // skip field stuff + skip(6); + // and skip field attributes + uint16_t attr_count = 0; + read_be(attr_count); + while(attr_count) + { + skip(2); + uint32_t attr_length = 0; + read_be(attr_length); + skip(attr_length); + attr_count --; + } + field_count --; + } + + // class methods + /* + * method_info + * { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + */ + uint16_t method_count = 0; + read_be(method_count); + while( method_count ) + { + skip(6); + // and skip method attributes + uint16_t attr_count = 0; + read_be(attr_count); + while(attr_count) + { + skip(2); + uint32_t attr_length = 0; + read_be(attr_length); + skip(attr_length); + attr_count --; + } + method_count --; + } + + // class attributes + // there are many kinds of attributes. this is just the generic wrapper structure. + // type is decided by attribute name. extensions to the standard are *possible* + // class annotations are one kind of a attribute (one per class) + /* + * attribute_info + * { + * u2 attribute_name_index; + * u4 attribute_length; + * u1 info[attribute_length]; + * } + */ + uint16_t class_attr_count = 0; + read_be(class_attr_count); + while(class_attr_count) + { + uint16_t name_idx = 0; + read_be(name_idx); + uint32_t attr_length = 0; + read_be(attr_length); + + auto name = constants[name_idx]; + if(name.str_data == "RuntimeVisibleAnnotations") + { + uint16_t num_annotations = 0; + read_be(num_annotations); + while (num_annotations) + { + visible_class_annotations.push_back(annotation::read(*this, constants)); + num_annotations --; + } + } + else skip(attr_length); + class_attr_count --; + } + valid = true; + }; + bool valid; + bool is_synthetic; + uint32_t magic; + uint16_t minor_version; + uint16_t major_version; + constant_pool constants; + uint16_t access_flags; + uint16_t this_class; + uint16_t super_class; + // interfaces this class implements ? must be. investigate. + std::vector interfaces; + // FIXME: doesn't free up memory on delete + java::annotation_table visible_class_annotations; + }; +} \ No newline at end of file diff --git a/java/constants.h b/java/constants.h new file mode 100644 index 00000000..2f968d2e --- /dev/null +++ b/java/constants.h @@ -0,0 +1,208 @@ +#pragma once +#include "errors.h" +#include + +namespace java +{ + class constant + { + public: + enum type_t:uint8_t + { + j_hole = 0, // HACK: this is a hole in the array, because java is crazy + j_string_data = 1, + j_int = 3, + j_float = 4, + j_long = 5, + j_double = 6, + j_class = 7, + j_string = 8, + j_fieldref = 9, + j_methodref = 10, + j_interface_methodref = 11, + j_nameandtype = 12 + } type; + constant(util::membuffer & buf ) + { + buf.read(type); + // invalid constant type! + if(type > j_nameandtype || type == (type_t)0 || type == (type_t)2) + throw new classfile_exception(); + + // load data depending on type + switch(type) + { + case j_float: + case j_int: + buf.read_be(int_data); // same as float data really + break; + case j_double: + case j_long: + buf.read_be(long_data); // same as double + break; + case j_class: + buf.read_be(ref_type.class_idx); + break; + case j_fieldref: + case j_methodref: + case j_interface_methodref: + buf.read_be(ref_type.class_idx); + buf.read_be(ref_type.name_and_type_idx); + break; + case j_string: + buf.read_be(index); + break; + case j_string_data: + // HACK HACK: for now, we call these UTF-8 and do no further processing. + // Later, we should do some decoding. It's really modified UTF-8 + // * U+0000 is represented as 0xC0,0x80 invalid character + // * any single zero byte ends the string + // * characters above U+10000 are encoded like in CESU-8 + buf.read_jstr(str_data); + break; + case j_nameandtype: + buf.read_be(name_and_type.name_index); + buf.read_be(name_and_type.descriptor_index); + break; + } + } + constant(int fake) + { + type = j_hole; + } + std::string toString() + { + std::ostringstream ss; + switch(type) + { + case j_hole: + ss << "Fake legacy entry"; + break; + case j_float: + ss << "Float: " << float_data; + break; + case j_double: + ss << "Double: " << double_data; + break; + case j_int: + ss << "Int: " << int_data; + break; + case j_long: + ss << "Long: " << long_data; + break; + case j_string_data: + ss << "StrData: " << str_data; + break; + case j_string: + ss << "Str: " << index; + break; + case j_fieldref: + ss << "FieldRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_methodref: + ss << "MethodRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_interface_methodref: + ss << "IfMethodRef: " << ref_type.class_idx << " " << ref_type.name_and_type_idx; + break; + case j_class: + ss << "Class: " << ref_type.class_idx; + break; + case j_nameandtype: + ss << "NameAndType: " << name_and_type.name_index << " " << name_and_type.descriptor_index; + break; + } + return ss.str(); + } + + std::string str_data; /** String data in 'modified utf-8'.*/ + // store everything here. + union + { + int32_t int_data; + int64_t long_data; + float float_data; + double double_data; + uint16_t index; + struct + { + /** + * Class reference: + * an index within the constant pool to a UTF-8 string containing + * the fully qualified class name (in internal format) + * Used for j_class, j_fieldref, j_methodref and j_interface_methodref + */ + uint16_t class_idx; + // used for j_fieldref, j_methodref and j_interface_methodref + uint16_t name_and_type_idx; + } ref_type; + struct + { + uint16_t name_index; + uint16_t descriptor_index; + } name_and_type; + }; + }; + /** + * A helper class that represents the custom container used in Java class file for storage of constants + */ + class constant_pool + { + public: + /** + * Create a pool of constants + */ + constant_pool(){} + /** + * Load a java constant pool + */ + void load(util::membuffer & buf) + { + uint16_t length = 0; + buf.read_be(length); + length --; + uint16_t index = 1; + const constant * last_constant = nullptr; + while(length) + { + const constant & cnst = constant(buf); + constants.push_back(cnst); + last_constant = &constants[constants.size() - 1]; + if(last_constant->type == constant::j_double || last_constant->type == constant::j_long) + { + // push in a fake constant to preserve indexing + constants.push_back(constant(0)); + length-=2; + index+=2; + } + else + { + length--; + index++; + } + } + }; + typedef std::vector container_type; + /** + * Access constants based on jar file index numbers (index of the first element is 1) + */ + java::constant & operator[](std::size_t constant_index) + { + if(constant_index == 0 || constant_index > constants.size()) + { + throw new classfile_exception(); + } + return constants[constant_index - 1]; + }; + container_type::const_iterator begin() const + { + return constants.begin(); + }; + container_type::const_iterator end() const + { + return constants.end(); + } + private: + container_type constants; + }; +} diff --git a/java/endian.h b/java/endian.h new file mode 100644 index 00000000..fa6207fe --- /dev/null +++ b/java/endian.h @@ -0,0 +1,62 @@ +#pragma once +#include + +/** + * Swap bytes between big endian and local number representation + */ +namespace util +{ +#ifdef MULTIMC_BIG_ENDIAN +inline uint64_t bigswap(uint64_t x) +{ + return x; +}; +inline uint32_t bigswap(uint32_t x) +{ + return x; +}; +inline uint16_t bigswap(uint16_t x) +{ + return x; +}; +inline int64_t bigswap(int64_t x) +{ + return x; +}; +inline int32_t bigswap(int32_t x) +{ + return x; +}; +inline int16_t bigswap(int16_t x) +{ + return x; +}; +#else +inline uint64_t bigswap(uint64_t x) +{ + return (x>>56) | ((x<<40) & 0x00FF000000000000) | ((x<<24) & 0x0000FF0000000000) | ((x<<8) & 0x000000FF00000000) | + ((x>>8) & 0x00000000FF000000) | ((x>>24) & 0x0000000000FF0000) | ((x>>40) & 0x000000000000FF00) | (x<<56); +}; +inline uint32_t bigswap(uint32_t x) +{ + return (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24); +}; +inline uint16_t bigswap(uint16_t x) +{ + return (x>>8) | (x<<8); +}; +inline int64_t bigswap(int64_t x) +{ + return (x>>56) | ((x<<40) & 0x00FF000000000000) | ((x<<24) & 0x0000FF0000000000) | ((x<<8) & 0x000000FF00000000) | + ((x>>8) & 0x00000000FF000000) | ((x>>24) & 0x0000000000FF0000) | ((x>>40) & 0x000000000000FF00) | (x<<56); +}; +inline int32_t bigswap(int32_t x) +{ + return (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24); +}; +inline int16_t bigswap(int16_t x) +{ + return (x>>8) | (x<<8); +}; +#endif +} diff --git a/java/errors.h b/java/errors.h new file mode 100644 index 00000000..c02b07c8 --- /dev/null +++ b/java/errors.h @@ -0,0 +1,6 @@ +#pragma once +#include +namespace java +{ + class classfile_exception : public std::exception {}; +} diff --git a/java/javautils.cpp b/java/javautils.cpp new file mode 100644 index 00000000..a07d1541 --- /dev/null +++ b/java/javautils.cpp @@ -0,0 +1,69 @@ +#include "multimc_pragma.h" +#include "classfile.h" +#include "javautils.h" +//#include +#include +//#include +//#include "mcversionlist.h" + +namespace javautils +{ +QString GetMinecraftJarVersion(QString jar) +{ + return "Unknown"; + /* + wxString fullpath = jar.GetFullPath(); + wxString version = MCVer_Unknown; + if(!jar.FileExists()) + return version; + std::auto_ptr entry; + // convert the local name we are looking for into the internal format + wxString name = wxZipEntry::GetInternalName("net/minecraft/client/Minecraft.class",wxPATH_UNIX); + + // open the zip + wxFFileInputStream inStream(jar.GetFullPath()); + wxZipInputStream zipIn(inStream); + + // call GetNextEntry() until the required internal name is found + do + { + entry.reset(zipIn.GetNextEntry()); + } + while (entry.get() != NULL && entry->GetInternalName() != name); + auto myentry = entry.get(); + if (myentry == NULL) + return version; + + // we got the entry, read the data + std::size_t size = myentry->GetSize(); + char *classdata = new char[size]; + zipIn.Read(classdata,size); + try + { + char * temp = classdata; + java::classfile Minecraft_jar(temp,size); + auto cnst = Minecraft_jar.constants; + auto iter = cnst.begin(); + while (iter != cnst.end()) + { + const java::constant & constant = *iter; + if(constant.type != java::constant::j_string_data) + { + iter++; + continue; + } + auto & str = constant.str_data; + const char * lookfor = "Minecraft Minecraft "; // length = 20 + if(str.compare(0,20,lookfor) == 0) + { + version = str.substr(20).data(); + break; + } + iter++; + } + } catch(java::classfile_exception &){} + delete[] classdata; + return version; + */ +} +} \ No newline at end of file diff --git a/java/javautils.h b/java/javautils.h new file mode 100644 index 00000000..c8b5a793 --- /dev/null +++ b/java/javautils.h @@ -0,0 +1,9 @@ +#pragma once +#include +namespace javautils +{ + /* + * Get the version from a minecraft.jar by parsing its class files. Expensive! + */ + QString GetMinecraftJarVersion(QString jar); +} \ No newline at end of file diff --git a/java/membuffer.h b/java/membuffer.h new file mode 100644 index 00000000..48304b9f --- /dev/null +++ b/java/membuffer.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include +#include +#include "endian.h" + +namespace util +{ + class membuffer + { + public: + membuffer(char * buffer, std::size_t size) + { + current = start = buffer; + end = start + size; + } + ~membuffer() + { + // maybe? possibly? left out to avoid confusion. for now. + //delete start; + } + /** + * Read some value. That's all ;) + */ + template + void read(T& val) + { + val = *(T *)current; + current += sizeof(T); + } + /** + * Read a big-endian number + * valid for 2-byte, 4-byte and 8-byte variables + */ + template + void read_be(T& val) + { + val = util::bigswap(*(T *)current); + current += sizeof(T); + } + /** + * Read a string in the format: + * 2B length (big endian, unsigned) + * length bytes data + */ + void read_jstr(std::string & str) + { + uint16_t length = 0; + read_be(length); + str.append(current,length); + current += length; + } + /** + * Skip N bytes + */ + void skip (std::size_t N) + { + current += N; + } + private: + char * start, *end, *current; + }; +} diff --git a/java/test.cpp b/java/test.cpp new file mode 100644 index 00000000..f73e3c21 --- /dev/null +++ b/java/test.cpp @@ -0,0 +1,35 @@ + +#include "classfile.h" +#include "annotations.h" +#include +#include + +int main(int argc, char* argv[]) +{ + if(argc > 1) + { + std::ifstream file_in(argv[1]); + if(file_in.is_open()) + { + file_in.seekg(0, std::_S_end); + auto length = file_in.tellg(); + char * data = new char[length]; + file_in.seekg(0); + file_in.read(data,length); + java::classfile cf (data, length); + java::annotation_table atable = cf.visible_class_annotations; + for(int i = 0; i < atable.size(); i++) + { + std::cout << atable[i]->toString() << std::endl; + } + return 0; + } + else + { + std::cerr << "Failed to open file : " << argv[1] << std::endl; + return 1; + } + } + std::cerr << "No file to open :(" << std::endl; + return 1; +} \ No newline at end of file diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt new file mode 100644 index 00000000..7f189458 --- /dev/null +++ b/launcher/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.8.6) +project(launcher Java) +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}") +find_package(Java 1.6 REQUIRED COMPONENTS Development) + + +include(UseJava) +set(CMAKE_JAVA_JAR_ENTRY_POINT MultiMCLauncher) +set(CMAKE_JAVA_COMPILE_FLAGS -target 1.6 -source 1.6 -Xlint:deprecation -Xlint:unchecked) +set(CMAKE_JAVA_TARGET_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/../resources") + +set(SRC + MultiMCLauncher.java + org/simplericity/macify/eawt/Application.java + org/simplericity/macify/eawt/ApplicationAdapter.java + org/simplericity/macify/eawt/ApplicationEvent.java + org/simplericity/macify/eawt/ApplicationListener.java + org/simplericity/macify/eawt/DefaultApplication.java + net/minecraft/Launcher.java + MCFrame.java +) + +add_jar(MultiMCLauncher ${SRC}) \ No newline at end of file diff --git a/launcher/MCFrame.java b/launcher/MCFrame.java new file mode 100644 index 00000000..d6ebb240 --- /dev/null +++ b/launcher/MCFrame.java @@ -0,0 +1,123 @@ +// +// Copyright 2012 MultiMC Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import net.minecraft.Launcher; +import java.applet.Applet; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Toolkit; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.net.MalformedURLException; +import java.net.URL; +import java.io.IOException; +import java.io.File; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; + +public class MCFrame extends Frame implements WindowListener +{ + private Launcher appletWrap = null; + public MCFrame(String title) + { + super(title); + BufferedImage image = null; + try + { + image = ImageIO.read(new File("icon.png")); + setIconImage(image); + } + catch (IOException e) + { + e.printStackTrace(); + } + this.addWindowListener(this); + } + + public void start(Applet mcApplet, String user, String session, Dimension winSize, boolean maximize) + { + try + { + appletWrap = new Launcher(mcApplet, new URL("http://www.minecraft.net/game")); + } + catch (MalformedURLException ignored){} + + appletWrap.setParameter("username", user); + appletWrap.setParameter("sessionid", session); + appletWrap.setParameter("stand-alone", "true"); // Show the quit button. + mcApplet.setStub(appletWrap); + + this.add(appletWrap); + appletWrap.setPreferredSize(winSize); + this.pack(); + this.setLocationRelativeTo(null); + this.setResizable(true); + if (maximize) + this.setExtendedState(MAXIMIZED_BOTH); + + validate(); + appletWrap.init(); + appletWrap.start(); + setVisible(true); + } + + @Override + public void windowActivated(WindowEvent e) {} + + @Override + public void windowClosed(WindowEvent e) {} + + @Override + public void windowClosing(WindowEvent e) + { + new Thread() + { + public void run() + { + try + { + Thread.sleep(30000L); + } catch (InterruptedException localInterruptedException) + { + localInterruptedException.printStackTrace(); + } + System.out.println("FORCING EXIT!"); + System.exit(0); + } + } + .start(); + + if (appletWrap != null) + { + appletWrap.stop(); + appletWrap.destroy(); + } + // old minecraft versions can hang without this >_< + System.exit(0); + } + + @Override + public void windowDeactivated(WindowEvent e) {} + + @Override + public void windowDeiconified(WindowEvent e) {} + + @Override + public void windowIconified(WindowEvent e) {} + + @Override + public void windowOpened(WindowEvent e) {} +} \ No newline at end of file diff --git a/launcher/MultiMCLauncher.java b/launcher/MultiMCLauncher.java new file mode 100644 index 00000000..09a019ce --- /dev/null +++ b/launcher/MultiMCLauncher.java @@ -0,0 +1,331 @@ +// +// Copyright 2012 MultiMC Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import java.applet.Applet; +import java.awt.Dimension; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import org.simplericity.macify.eawt.Application; +import org.simplericity.macify.eawt.DefaultApplication; + +public class MultiMCLauncher +{ + /** + * @param args + * The arguments you want to launch Minecraft with. New path, + * Username, Session ID. + */ + public static void main(String[] args) + { + if (args.length < 3) + { + System.out.println("Not enough arguments."); + System.exit(-1); + } + + // Set the OSX application icon first, if we are on OSX. + Application application = new DefaultApplication(); + if(application.isMac()) + { + try + { + BufferedImage image = ImageIO.read(new File("icon.png")); + application.setApplicationIconImage(image); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + String userName = args[0]; + String sessionId = args[1]; + String windowtitle = args[2]; + String windowParams = args[3]; + String lwjgl = args[4]; + String cwd = System.getProperty("user.dir"); + + Dimension winSize = new Dimension(854, 480); + boolean maximize = false; + boolean compatMode = false; + + + String[] dimStrings = windowParams.split("x"); + + if (windowParams.equalsIgnoreCase("compatmode")) + { + compatMode = true; + } + else if (windowParams.equalsIgnoreCase("max")) + { + maximize = true; + } + else if (dimStrings.length == 2) + { + try + { + winSize = new Dimension(Integer.parseInt(dimStrings[0]), + Integer.parseInt(dimStrings[1])); + } + catch (NumberFormatException e) + { + System.out.println("Invalid Window size argument, " + + "using default."); + } + } + else + { + System.out.println("Invalid Window size argument, " + + "using default."); + } + + try + { + File binDir = new File(cwd, "bin"); + File lwjglDir; + if(lwjgl.equalsIgnoreCase("Mojang")) + lwjglDir = binDir; + else + lwjglDir = new File(lwjgl); + + System.out.println("Loading jars..."); + String[] lwjglJars = new String[] { + "lwjgl.jar", "lwjgl_util.jar", "jinput.jar" + }; + + URL[] urls = new URL[4]; + try + { + File f = new File(binDir, "minecraft.jar"); + urls[0] = f.toURI().toURL(); + System.out.println("Loading URL: " + urls[0].toString()); + + for (int i = 1; i < urls.length; i++) + { + File jar = new File(lwjglDir, lwjglJars[i-1]); + urls[i] = jar.toURI().toURL(); + System.out.println("Loading URL: " + urls[i].toString()); + } + } + catch (MalformedURLException e) + { + System.err.println("MalformedURLException, " + e.toString()); + System.exit(5); + } + + System.out.println("Loading natives..."); + String nativesDir = new File(lwjglDir, "natives").toString(); + + System.setProperty("org.lwjgl.librarypath", nativesDir); + System.setProperty("net.java.games.input.librarypath", nativesDir); + + URLClassLoader cl = + new URLClassLoader(urls, MultiMCLauncher.class.getClassLoader()); + + // Get the Minecraft Class. + Class mc = null; + try + { + mc = cl.loadClass("net.minecraft.client.Minecraft"); + + Field f = getMCPathField(mc); + + if (f == null) + { + System.err.println("Could not find Minecraft path field. Launch failed."); + System.exit(-1); + } + + f.setAccessible(true); + f.set(null, new File(cwd)); + // And set it. + System.out.println("Fixed Minecraft Path: Field was " + f.toString()); + } + catch (ClassNotFoundException e) + { + System.err.println("Can't find main class. Searching..."); + + // Look for any class that looks like the main class. + File mcJar = new File(new File(cwd, "bin"), "minecraft.jar"); + ZipFile zip = null; + try + { + zip = new ZipFile(mcJar); + } catch (ZipException e1) + { + e1.printStackTrace(); + System.err.println("Search failed."); + System.exit(-1); + } catch (IOException e1) + { + e1.printStackTrace(); + System.err.println("Search failed."); + System.exit(-1); + } + + Enumeration entries = zip.entries(); + ArrayList classes = new ArrayList(); + + while (entries.hasMoreElements()) + { + ZipEntry entry = entries.nextElement(); + if (entry.getName().endsWith(".class")) + { + String entryName = entry.getName().substring(0, entry.getName().lastIndexOf('.')); + entryName = entryName.replace('/', '.'); + System.out.println("Found class: " + entryName); + classes.add(entryName); + } + } + + for (String clsName : classes) + { + try + { + Class cls = cl.loadClass(clsName); + if (!Runnable.class.isAssignableFrom(cls)) + { + continue; + } + else + { + System.out.println("Found class implementing runnable: " + + cls.getName()); + } + + if (getMCPathField(cls) == null) + { + continue; + } + else + { + System.out.println("Found class implementing runnable " + + "with mcpath field: " + cls.getName()); + } + + mc = cls; + break; + } + catch (ClassNotFoundException e1) + { + // Ignore + continue; + } + } + + if (mc == null) + { + System.err.println("Failed to find Minecraft main class."); + System.exit(-1); + } + else + { + System.out.println("Found main class: " + mc.getName()); + } + } + + System.setProperty("minecraft.applet.TargetDirectory", cwd); + + String[] mcArgs = new String[2]; + mcArgs[0] = userName; + mcArgs[1] = sessionId; + + if (compatMode) + { + System.out.println("Launching in compatibility mode..."); + mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs); + } + else + { + System.out.println("Launching with applet wrapper..."); + try + { + Class MCAppletClass = cl.loadClass( + "net.minecraft.client.MinecraftApplet"); + Applet mcappl = (Applet) MCAppletClass.newInstance(); + MCFrame mcWindow = new MCFrame(windowtitle); + mcWindow.start(mcappl, userName, sessionId, winSize, maximize); + } catch (InstantiationException e) + { + System.out.println("Applet wrapper failed! Falling back " + + "to compatibility mode."); + mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs); + } + } + } catch (ClassNotFoundException e) + { + e.printStackTrace(); + System.exit(1); + } catch (IllegalArgumentException e) + { + e.printStackTrace(); + System.exit(2); + } catch (IllegalAccessException e) + { + e.printStackTrace(); + System.exit(2); + } catch (InvocationTargetException e) + { + e.printStackTrace(); + System.exit(3); + } catch (NoSuchMethodException e) + { + e.printStackTrace(); + System.exit(3); + } catch (SecurityException e) + { + e.printStackTrace(); + System.exit(4); + } + } + + public static Field getMCPathField(Class mc) + { + Field[] fields = mc.getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) + { + Field f = fields[i]; + if (f.getType() != File.class) + { + // Has to be File + continue; + } + if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC)) + { + // And Private Static. + continue; + } + return f; + } + return null; + } +} diff --git a/launcher/UseJava.cmake b/launcher/UseJava.cmake new file mode 100644 index 00000000..1a5ef107 --- /dev/null +++ b/launcher/UseJava.cmake @@ -0,0 +1,881 @@ +# - Use Module for Java +# This file provides functions for Java. It is assumed that FindJava.cmake +# has already been loaded. See FindJava.cmake for information on how to +# load Java into your CMake project. +# +# add_jar(TARGET_NAME SRC1 SRC2 .. SRCN RCS1 RCS2 .. RCSN) +# +# This command creates a .jar. It compiles the given source +# files (SRC) and adds the given resource files (RCS) to the jar file. +# If only resource files are given then just a jar file is created. +# +# Additional instructions: +# To add compile flags to the target you can set these flags with +# the following variable: +# +# set(CMAKE_JAVA_COMPILE_FLAGS -nowarn) +# +# To add a path or a jar file to the class path you can do this +# with the CMAKE_JAVA_INCLUDE_PATH variable. +# +# set(CMAKE_JAVA_INCLUDE_PATH /usr/share/java/shibboleet.jar) +# +# To use a different output name for the target you can set it with: +# +# set(CMAKE_JAVA_TARGET_OUTPUT_NAME shibboleet.jar) +# add_jar(foobar foobar.java) +# +# To use a different output directory than CMAKE_CURRENT_BINARY_DIR +# you can set it with: +# +# set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/bin) +# +# To define an entry point in your jar you can set it with: +# +# set(CMAKE_JAVA_JAR_ENTRY_POINT com/examples/MyProject/Main) +# +# To add a VERSION to the target output name you can set it using +# CMAKE_JAVA_TARGET_VERSION. This will create a jar file with the name +# shibboleet-1.0.0.jar and will create a symlink shibboleet.jar +# pointing to the jar with the version information. +# +# set(CMAKE_JAVA_TARGET_VERSION 1.2.0) +# add_jar(shibboleet shibbotleet.java) +# +# If the target is a JNI library, utilize the following commands to +# create a JNI symbolic link: +# +# set(CMAKE_JNI_TARGET TRUE) +# set(CMAKE_JAVA_TARGET_VERSION 1.2.0) +# add_jar(shibboleet shibbotleet.java) +# install_jar(shibboleet ${LIB_INSTALL_DIR}/shibboleet) +# install_jni_symlink(shibboleet ${JAVA_LIB_INSTALL_DIR}) +# +# If a single target needs to produce more than one jar from its +# java source code, to prevent the accumulation of duplicate class +# files in subsequent jars, set/reset CMAKE_JAR_CLASSES_PREFIX prior +# to calling the add_jar() function: +# +# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/foo) +# add_jar(foo foo.java) +# +# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/bar) +# add_jar(bar bar.java) +# +# Target Properties: +# The add_jar() functions sets some target properties. You can get these +# properties with the +# get_property(TARGET PROPERTY ) +# command. +# +# INSTALL_FILES The files which should be installed. This is used by +# install_jar(). +# JNI_SYMLINK The JNI symlink which should be installed. +# This is used by install_jni_symlink(). +# JAR_FILE The location of the jar file so that you can include +# it. +# CLASS_DIR The directory where the class files can be found. For +# example to use them with javah. +# +# find_jar( +# name | NAMES name1 [name2 ...] +# [PATHS path1 [path2 ... ENV var]] +# [VERSIONS version1 [version2]] +# [DOC "cache documentation string"] +# ) +# +# This command is used to find a full path to the named jar. A cache +# entry named by is created to stor the result of this command. If +# the full path to a jar is found the result is stored in the variable +# and the search will not repeated unless the variable is cleared. If +# nothing is found, the result will be -NOTFOUND, and the search +# will be attempted again next time find_jar is invoked with the same +# variable. +# The name of the full path to a file that is searched for is specified +# by the names listed after NAMES argument. Additional search locations +# can be specified after the PATHS argument. If you require special a +# version of a jar file you can specify it with the VERSIONS argument. +# The argument after DOC will be used for the documentation string in +# the cache. +# +# install_jar(TARGET_NAME DESTINATION) +# +# This command installs the TARGET_NAME files to the given DESTINATION. +# It should be called in the same scope as add_jar() or it will fail. +# +# install_jni_symlink(TARGET_NAME DESTINATION) +# +# This command installs the TARGET_NAME JNI symlinks to the given +# DESTINATION. It should be called in the same scope as add_jar() +# or it will fail. +# +# create_javadoc( +# PACKAGES pkg1 [pkg2 ...] +# [SOURCEPATH ] +# [CLASSPATH ] +# [INSTALLPATH ] +# [DOCTITLE "the documentation title"] +# [WINDOWTITLE "the title of the document"] +# [AUTHOR TRUE|FALSE] +# [USE TRUE|FALSE] +# [VERSION TRUE|FALSE] +# ) +# +# Create java documentation based on files or packages. For more +# details please read the javadoc manpage. +# +# There are two main signatures for create_javadoc. The first +# signature works with package names on a path with source files: +# +# Example: +# create_javadoc(my_example_doc +# PACKAGES com.exmaple.foo com.example.bar +# SOURCEPATH "${CMAKE_CURRENT_SOURCE_DIR}" +# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH} +# WINDOWTITLE "My example" +# DOCTITLE "

My example

" +# AUTHOR TRUE +# USE TRUE +# VERSION TRUE +# ) +# +# The second signature for create_javadoc works on a given list of +# files. +# +# create_javadoc( +# FILES file1 [file2 ...] +# [CLASSPATH ] +# [INSTALLPATH ] +# [DOCTITLE "the documentation title"] +# [WINDOWTITLE "the title of the document"] +# [AUTHOR TRUE|FALSE] +# [USE TRUE|FALSE] +# [VERSION TRUE|FALSE] +# ) +# +# Example: +# create_javadoc(my_example_doc +# FILES ${example_SRCS} +# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH} +# WINDOWTITLE "My example" +# DOCTITLE "

My example

" +# AUTHOR TRUE +# USE TRUE +# VERSION TRUE +# ) +# +# Both signatures share most of the options. These options are the +# same as what you can find in the javadoc manpage. Please look at +# the manpage for CLASSPATH, DOCTITLE, WINDOWTITLE, AUTHOR, USE and +# VERSION. +# +# The documentation will be by default installed to +# +# ${CMAKE_INSTALL_PREFIX}/share/javadoc/ +# +# if you don't set the INSTALLPATH. +# + +#============================================================================= +# Copyright 2010-2011 Andreas schneider +# Copyright 2010 Ben Boeckel +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +function (__java_copy_file src dest comment) + add_custom_command( + OUTPUT ${dest} + COMMAND cmake -E copy_if_different + ARGS ${src} + ${dest} + DEPENDS ${src} + COMMENT ${comment}) +endfunction (__java_copy_file src dest comment) + +# define helper scripts +set(_JAVA_CLASS_FILELIST_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaClassFilelist.cmake) +set(_JAVA_SYMLINK_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaSymlinks.cmake) + +function(add_jar _TARGET_NAME) + set(_JAVA_SOURCE_FILES ${ARGN}) + + if (NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR) + set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + endif(NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR) + + if (CMAKE_JAVA_JAR_ENTRY_POINT) + set(_ENTRY_POINT_OPTION e) + set(_ENTRY_POINT_VALUE ${CMAKE_JAVA_JAR_ENTRY_POINT}) + endif (CMAKE_JAVA_JAR_ENTRY_POINT) + + if (LIBRARY_OUTPUT_PATH) + set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH}) + else (LIBRARY_OUTPUT_PATH) + set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR}) + endif (LIBRARY_OUTPUT_PATH) + + set(CMAKE_JAVA_INCLUDE_PATH + ${CMAKE_JAVA_INCLUDE_PATH} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_JAVA_OBJECT_OUTPUT_PATH} + ${CMAKE_JAVA_LIBRARY_OUTPUT_PATH} + ) + + if (WIN32 AND NOT CYGWIN AND NOT CMAKE_CROSSCOMPILING) + set(CMAKE_JAVA_INCLUDE_FLAG_SEP ";") + else () + set(CMAKE_JAVA_INCLUDE_FLAG_SEP ":") + endif() + + foreach (JAVA_INCLUDE_DIR ${CMAKE_JAVA_INCLUDE_PATH}) + set(CMAKE_JAVA_INCLUDE_PATH_FINAL "${CMAKE_JAVA_INCLUDE_PATH_FINAL}${CMAKE_JAVA_INCLUDE_FLAG_SEP}${JAVA_INCLUDE_DIR}") + endforeach(JAVA_INCLUDE_DIR) + + set(CMAKE_JAVA_CLASS_OUTPUT_PATH "${CMAKE_JAVA_TARGET_OUTPUT_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir") + + set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}.jar") + if (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION) + set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar") + set(_JAVA_TARGET_OUTPUT_LINK "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar") + elseif (CMAKE_JAVA_TARGET_VERSION) + set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar") + set(_JAVA_TARGET_OUTPUT_LINK "${_TARGET_NAME}.jar") + elseif (CMAKE_JAVA_TARGET_OUTPUT_NAME) + set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar") + endif (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION) + # reset + set(CMAKE_JAVA_TARGET_OUTPUT_NAME) + + set(_JAVA_CLASS_FILES) + set(_JAVA_COMPILE_FILES) + set(_JAVA_DEPENDS) + set(_JAVA_RESOURCE_FILES) + foreach(_JAVA_SOURCE_FILE ${_JAVA_SOURCE_FILES}) + get_filename_component(_JAVA_EXT ${_JAVA_SOURCE_FILE} EXT) + get_filename_component(_JAVA_FILE ${_JAVA_SOURCE_FILE} NAME_WE) + get_filename_component(_JAVA_PATH ${_JAVA_SOURCE_FILE} PATH) + get_filename_component(_JAVA_FULL ${_JAVA_SOURCE_FILE} ABSOLUTE) + + file(RELATIVE_PATH _JAVA_REL_BINARY_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR} ${_JAVA_FULL}) + file(RELATIVE_PATH _JAVA_REL_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${_JAVA_FULL}) + string(LENGTH ${_JAVA_REL_BINARY_PATH} _BIN_LEN) + string(LENGTH ${_JAVA_REL_SOURCE_PATH} _SRC_LEN) + if (${_BIN_LEN} LESS ${_SRC_LEN}) + set(_JAVA_REL_PATH ${_JAVA_REL_BINARY_PATH}) + else (${_BIN_LEN} LESS ${_SRC_LEN}) + set(_JAVA_REL_PATH ${_JAVA_REL_SOURCE_PATH}) + endif (${_BIN_LEN} LESS ${_SRC_LEN}) + get_filename_component(_JAVA_REL_PATH ${_JAVA_REL_PATH} PATH) + + if (_JAVA_EXT MATCHES ".java") + list(APPEND _JAVA_COMPILE_FILES ${_JAVA_SOURCE_FILE}) + set(_JAVA_CLASS_FILE "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_REL_PATH}/${_JAVA_FILE}.class") + set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES} ${_JAVA_CLASS_FILE}) + + elseif (_JAVA_EXT MATCHES ".jar" + OR _JAVA_EXT MATCHES ".war" + OR _JAVA_EXT MATCHES ".ear" + OR _JAVA_EXT MATCHES ".sar") + list(APPEND CMAKE_JAVA_INCLUDE_PATH ${_JAVA_SOURCE_FILE}) + + elseif (_JAVA_EXT STREQUAL "") + list(APPEND CMAKE_JAVA_INCLUDE_PATH ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}} ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}_CLASSPATH}) + list(APPEND _JAVA_DEPENDS ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}}) + + else (_JAVA_EXT MATCHES ".java") + __java_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/${_JAVA_SOURCE_FILE} + ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_SOURCE_FILE} + "Copying ${_JAVA_SOURCE_FILE} to the build directory") + list(APPEND _JAVA_RESOURCE_FILES ${_JAVA_SOURCE_FILE}) + endif (_JAVA_EXT MATCHES ".java") + endforeach(_JAVA_SOURCE_FILE) + + # create an empty java_class_filelist + if (NOT EXISTS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist) + file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist "") + endif() + + if (_JAVA_COMPILE_FILES) + # Compile the java files and create a list of class files + add_custom_command( + # NOTE: this command generates an artificial dependency file + OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME} + COMMAND ${Java_JAVAC_EXECUTABLE} + ${CMAKE_JAVA_COMPILE_FLAGS} + -classpath "${CMAKE_JAVA_INCLUDE_PATH_FINAL}" + -d ${CMAKE_JAVA_CLASS_OUTPUT_PATH} + ${_JAVA_COMPILE_FILES} + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME} + DEPENDS ${_JAVA_COMPILE_FILES} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Building Java objects for ${_TARGET_NAME}.jar" + ) + add_custom_command( + OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist + COMMAND ${CMAKE_COMMAND} + -DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH} + -DCMAKE_JAR_CLASSES_PREFIX="${CMAKE_JAR_CLASSES_PREFIX}" + -P ${_JAVA_CLASS_FILELIST_SCRIPT} + DEPENDS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + endif (_JAVA_COMPILE_FILES) + + # create the jar file + set(_JAVA_JAR_OUTPUT_PATH + ${CMAKE_JAVA_TARGET_OUTPUT_DIR