commit cbce09e685d3a2308c35c416130ec321a4bf375f Author: Tungstend Date: Wed Oct 19 18:11:29 2022 +0800 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a68e5b57 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Log/OS Files +*.log + +# Android Studio generated files and folders +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +output.json + +# IntelliJ +*.iml +.idea/ + +# Keystore files +*.jks +*.keystore + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Android Profiling +*.hprof diff --git a/FCL/.gitignore b/FCL/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/FCL/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/FCL/build.gradle b/FCL/build.gradle new file mode 100644 index 00000000..17d513c1 --- /dev/null +++ b/FCL/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'com.tungsten.fcl' + compileSdk 32 + + defaultConfig { + applicationId "com.tungsten.fcl" + minSdk 23 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(path: ':FCLCore') + implementation project(path: ':FCLauncher') + implementation 'cat.ereza:customactivityoncrash:2.4.0' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.6.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/FCL/proguard-rules.pro b/FCL/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/FCL/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/FCL/src/androidTest/java/com/tungsten/fcl/ExampleInstrumentedTest.java b/FCL/src/androidTest/java/com/tungsten/fcl/ExampleInstrumentedTest.java new file mode 100644 index 00000000..eee49c7c --- /dev/null +++ b/FCL/src/androidTest/java/com/tungsten/fcl/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.tungsten.fcl; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.tungsten.fcl", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/FCL/src/main/AndroidManifest.xml b/FCL/src/main/AndroidManifest.xml new file mode 100644 index 00000000..62cf2ba1 --- /dev/null +++ b/FCL/src/main/AndroidManifest.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FCL/src/main/assets/app_runtime/lwjgl2/lwjgl.jar b/FCL/src/main/assets/app_runtime/lwjgl2/lwjgl.jar new file mode 100644 index 00000000..9a75819a Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl2/lwjgl.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl2/lwjgl_util.jar b/FCL/src/main/assets/app_runtime/lwjgl2/lwjgl_util.jar new file mode 100644 index 00000000..18fd5131 Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl2/lwjgl_util.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-glfw.jar b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-glfw.jar new file mode 100644 index 00000000..af15ac89 Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-glfw.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-jemalloc.jar b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-jemalloc.jar new file mode 100644 index 00000000..2df695e4 Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-jemalloc.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-openal.jar b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-openal.jar new file mode 100644 index 00000000..8132408a Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-openal.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-opengl.jar b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-opengl.jar new file mode 100644 index 00000000..c4d228cd Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-opengl.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-stb.jar b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-stb.jar new file mode 100644 index 00000000..1cd2966d Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-stb.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-tinyfd.jar b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-tinyfd.jar new file mode 100644 index 00000000..6788744e Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl-tinyfd.jar differ diff --git a/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl.jar b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl.jar new file mode 100644 index 00000000..90779579 Binary files /dev/null and b/FCL/src/main/assets/app_runtime/lwjgl3/lwjgl.jar differ diff --git a/FCL/src/main/assets/java/ASSEMBLY_EXCEPTION b/FCL/src/main/assets/java/ASSEMBLY_EXCEPTION new file mode 100644 index 00000000..065b8d90 --- /dev/null +++ b/FCL/src/main/assets/java/ASSEMBLY_EXCEPTION @@ -0,0 +1,27 @@ + +OPENJDK ASSEMBLY EXCEPTION + +The OpenJDK source code made available by Oracle America, Inc. (Oracle) at +openjdk.java.net ("OpenJDK Code") is distributed under the terms of the GNU +General Public License version 2 +only ("GPL2"), with the following clarification and special exception. + + Linking this OpenJDK Code statically or dynamically with other code + is making a combined work based on this library. Thus, the terms + and conditions of GPL2 cover the whole combination. + + As a special exception, Oracle gives you permission to link this + OpenJDK Code with certain code licensed by Oracle as indicated at + http://openjdk.java.net/legal/exception-modules-2007-05-08.html + ("Designated Exception Modules") to produce an executable, + regardless of the license terms of the Designated Exception Modules, + and to copy and distribute the resulting executable under GPL2, + provided that the Designated Exception Modules continue to be + governed by the licenses under which they were offered by Oracle. + +As such, it allows licensees and sublicensees of Oracle's GPL2 OpenJDK Code +to build an executable that includes those portions of necessary code that +Oracle could not provide under GPL2 (or that Oracle has provided under GPL2 +with the Classpath exception). If you modify or add to the OpenJDK code, +that new GPL2 code may still be combined with Designated Exception Modules +if the new code is made subject to this exception by its copyright holder. diff --git a/FCL/src/main/assets/java/LICENSE b/FCL/src/main/assets/java/LICENSE new file mode 100644 index 00000000..8b400c7a --- /dev/null +++ b/FCL/src/main/assets/java/LICENSE @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code +distributed need not include anything that is normally distributed (in either +source or binary form) with the major components (compiler, kernel, and so on) +of the operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In +such case, this License incorporates the limitation as if written in the body +of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any later +version", you have the option of following the terms and conditions either of +that version or of any later version published by the Free Software Foundation. +If the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/FCL/src/main/assets/java/THIRD_PARTY_README b/FCL/src/main/assets/java/THIRD_PARTY_README new file mode 100644 index 00000000..a00e4186 --- /dev/null +++ b/FCL/src/main/assets/java/THIRD_PARTY_README @@ -0,0 +1,3282 @@ +DO NOT TRANSLATE OR LOCALIZE. +----------------------------- + +%% This notice is provided with respect to ASM Bytecode Manipulation +Framework v5.0.3, which may be included with JRE 8, and JDK 8, and +OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) 2000-2011 France Télécom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +-------------------------------------------------------------------------------- + +%% This notice is provided with respect to BSDiff v4.3, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright 2003-2005 Colin Percival +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted providing that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to CodeViewer 1.0, which may be +included with JDK 8. + +--- begin of LICENSE --- + +Copyright 1999 by CoolServlets.com. + +Any errors or suggested improvements to this class can be reported as +instructed on CoolServlets.com. We hope you enjoy this program... your +comments will encourage further development! This software is distributed +under the terms of the BSD License. Redistribution and use in source and +binary forms, with or without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +Neither name of CoolServlets.com nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY COOLSERVLETS.COM AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Cryptix AES 3.2.0, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Cryptix General License + +Copyright (c) 1995-2005 The Cryptix Foundation Limited. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE CRYPTIX FOUNDATION LIMITED AND +CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE CRYPTIX FOUNDATION LIMITED OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to CUP Parser Generator for +Java 0.11b, which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright 1996-2015 by Scott Hudson, Frank Flannery, C. Scott Ananian, Michael Petter + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both the +copyright notice and this permission notice and warranty disclaimer appear in +supporting documentation, and that the names of the authors or their +employers not be used in advertising or publicity pertaining to distribution of +the software without specific, written prior permission. + +The authors and their employers disclaim all warranties with regard to +this software, including all implied warranties of merchantability and fitness. +In no event shall the authors or their employers be liable for any special, +indirect or consequential damages or any damages whatsoever resulting from +loss of use, data or profits, whether in an action of contract, negligence or +other tortious action, arising out of or in connection with the use or +performance of this software. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to DejaVu fonts v2.34, which may be +included with JRE 8, and JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +TeX Gyre DJV Math +----------------- +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. + +Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski +(on behalf of TeX users groups) are in public domain. + +Letters imported from Euler Fraktur from AMSfonts are (c) American +Mathematical Society (see below). +Bitstream Vera Fonts Copyright +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera +is a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated documentation +files (the "Font Software"), to reproduce and distribute the Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit persons +to whom the Font Software is furnished to do so, subject to the following +conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the +fonts are renamed to names not containing either the words "Bitstream" +or the word "Vera". + +This License becomes null and void to the extent applicable to Fonts or +Font Software that has been modified and is distributed under the +"Bitstream Vera" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN +THE FONT SOFTWARE. +Except as contained in this notice, the names of GNOME, the GNOME +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the GNOME Foundation or +Bitstream Inc., respectively. +For further information, contact: fonts at gnome dot org. + +AMSFonts (v. 2.2) copyright + +The PostScript Type 1 implementation of the AMSFonts produced by and +previously distributed by Blue Sky Research and Y&Y, Inc. are now freely +available for general use. This has been accomplished through the +cooperation +of a consortium of scientific publishers with Blue Sky Research and Y&Y. +Members of this consortium include: + +Elsevier Science IBM Corporation Society for Industrial and Applied +Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) + +In order to assure the authenticity of these fonts, copyright will be +held by the American Mathematical Society. This is not meant to restrict +in any way the legitimate use of the fonts, such as (but not limited to) +electronic distribution of documents containing these fonts, inclusion of +these fonts into other public domain or commercial font collections or computer +applications, use of the outline data to create derivative fonts and/or +faces, etc. However, the AMS does require that the AMS copyright notice be +removed from any derivative versions of the fonts which have been altered in +any way. In addition, to ensure the fidelity of TeX documents using Computer +Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, +has requested that any alterations which yield different font metrics be +given a different name. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Document Object Model (DOM) Level 2 +& 3, which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +W3C SOFTWARE NOTICE AND LICENSE + +http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + +This work (and included software, documentation such as READMEs, or other +related items) is being provided by the copyright holders under the following +license. By obtaining, using and/or copying this work, you (the licensee) +agree that you have read, understood, and will comply with the following terms +and conditions. + +Permission to copy, modify, and distribute this software and its +documentation, with or without modification, for any purpose and without fee +or royalty is hereby granted, provided that you include the following on ALL +copies of the software and documentation or portions thereof, including +modifications: + + 1.The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + + 2.Any pre-existing intellectual property disclaimers, notices, or terms and + conditions. If none exist, the W3C Software Short Notice should be included + (hypertext is preferred, text is permitted) within the body of any + redistributed or derivative code. + + 3.Notice of any changes or modifications to the files, including the date + changes were made. (We recommend you provide URIs to the location from + which the code is derived.) + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS +MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR +PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY +THIRD PARTY PATENTS,COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL +OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR +DOCUMENTATION. The name and trademarks of copyright holders may NOT be used +in advertising or publicity pertaining to the software without specific, +written prior permission. Title to copyright in this software and any +associated documentation will at all times remain with copyright holders. + +____________________________________ + +This formulation of W3C's notice and license became active on December 31 +2002. This version removes the copyright ownership notice such that this +license can be used with materials other than those owned by the W3C, reflects +that ERCIM is now a host of the W3C, includes references to this specific +dated version of the license, and removes the ambiguous grant of "use". +Otherwise, this version is the same as the previous version and is written so +as to preserve the Free Software Foundation's assessment of GPL compatibility +and OSI's certification under the Open Source Definition. Please see our +Copyright FAQ for common questions about using materials from our site, +including specific terms and conditions for packages like libwww, Amaya, and +Jigsaw. Other questions about this notice can be directed to +site-policy@w3.org. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Dynalink v0.5, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) 2009-2013, Attila Szegedi + +All rights reserved.Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following conditions are +met:* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. * Redistributions in +binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. * Neither the name of Attila +Szegedi nor the names of its contributors may be used to endorse or promote +products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Elliptic Curve Cryptography, which +may be included with JRE 8, JDK 8, and OpenJDK 8. + +You are receiving a copy of the Elliptic Curve Cryptography library in source +form with the JDK 8 and OpenJDK 8 source distributions, and as object code in +the JRE 8 & JDK 8 runtimes. + +In the case of the JRE & JDK runtimes, the terms of the Oracle license do +NOT apply to the Elliptic Curve Cryptography library; it is licensed under the +following license, separately from Oracle's JDK & JRE. If you do not wish to +install the Elliptic Curve Cryptography library, you may delete the +Elliptic Curve Cryptography library: + - On Solaris and Linux systems: delete $(JAVA_HOME)/lib/libsunec.so + - On Windows systems: delete $(JAVA_HOME)\bin\sunec.dll + - On Mac systems, delete: + for JRE: /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/libsunec.dylib + for JDK: $(JAVA_HOME)/jre/lib/libsunec.dylib + +Written Offer for ECC Source Code + For third party technology that you receive from Oracle in binary form + which is licensed under an open source license that gives you the right + to receive the source code for that binary, you can obtain a copy of + the applicable source code from this page: + http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/tip/src/share/native/sun/security/ec/impl + + If the source code for the technology was not provided to you with the + binary, you can also receive a copy of the source code on physical + media by submitting a written request to: + + Oracle America, Inc. + Attn: Associate General Counsel, + Development and Engineering Legal + 500 Oracle Parkway, 10th Floor + Redwood Shores, CA 94065 + + Or, you may send an email to Oracle using the form at: + http://www.oracle.com/goto/opensourcecode/request + + Your request should include: + - The name of the component or binary file(s) for which you are requesting + the source code + - The name and version number of the Oracle product containing the binary + - The date you received the Oracle product + - Your name + - Your company name (if applicable) + - Your return mailing address and email and + - A telephone number in the event we need to reach you. + + We may charge you a fee to cover the cost of physical media and processing. + Your request must be sent (i) within three (3) years of the date you + received the Oracle product that included the component or binary + file(s) that are the subject of your request, or (ii) in the case of + code licensed under the GPL v3, for as long as Oracle offers spare + parts or customer support for that product model. + +--- begin of LICENSE --- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to ECMAScript Language +Specification ECMA-262 Edition 5.1 which may be included with +JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright notice +Copyright © 2011 Ecma International +Ecma International +Rue du Rhone 114 +CH-1204 Geneva +Tel: +41 22 849 6000 +Fax: +41 22 849 6001 +Web: http://www.ecma-international.org + +This document and possible translations of it may be copied and furnished to +others, and derivative works that comment on or otherwise explain it or assist +in its implementation may be prepared, copied, published, and distributed, in +whole or in part, without restriction of any kind, provided that the above +copyright notice and this section are included on all such copies and derivative +works. However, this document itself may not be modified in any way, including +by removing the copyright notice or references to Ecma International, except as +needed for the purpose of developing any document or deliverable produced by +Ecma International (in which case the rules applied to copyrights must be +followed) or as required to translate it into languages other than English. The +limited permissions granted above are perpetual and will not be revoked by Ecma +International or its successors or assigns. This document and the information +contained herein is provided on an "AS IS" basis and ECMA INTERNATIONAL +DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY +WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP +RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR +PURPOSE." Software License + +All Software contained in this document ("Software)" is protected by copyright +and is being made available under the "BSD License", included below. This +Software may be subject to third party rights (rights from parties other than +Ecma International), including patent rights, and no licenses under such third +party rights are granted under this license even if the third party concerned is +a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS +AVAILABLE AT http://www.ecma-international.org/memento/codeofconduct.htm FOR +INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO +IMPLEMENT ECMA INTERNATIONAL STANDARDS*. Redistribution and use in source and +binary forms, with or without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the authors nor Ecma International may be used to endorse +or promote products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to freebXML Registry 3.0 & 3.1, +which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +freebxml: Copyright (c) 2001 freebxml.org. All rights reserved. + +The names "The freebXML Registry Project" and "freebxml Software +Foundation" must not be used to endorse or promote products derived +from this software or be used in a product name without prior +written permission. For written permission, please contact +ebxmlrr-team@lists.sourceforge.net. + +This software consists of voluntary contributions made by many individuals +on behalf of the the freebxml Software Foundation. For more information on +the freebxml Software Foundation, please see . + +This product includes software developed by the Apache Software Foundation +(http://www.apache.org/). + +The freebxml License, Version 1.1 5 +Copyright (c) 2001 freebxml.org. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The end-user documentation included with the redistribution, if + any, must include the following acknowlegement: + "This product includes software developed by + freebxml.org (http://www.freebxml.org/)." + Alternately, this acknowlegement may appear in the software itself, + if and wherever such third-party acknowlegements normally appear. + + 4. The names "The freebXML Registry Project", "freebxml Software + Foundation" must not be used to endorse or promote products derived + from this software without prior written permission. For written + permission, please contact ebxmlrr-team@lists.sourceforge.net. + + 5. Products derived from this software may not be called "freebxml", + "freebXML Registry" nor may freebxml" appear in their names without + prior written permission of the freebxml Group. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE freebxml SOFTWARE FOUNDATION OR +ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to IAIK PKCS#11 Wrapper, +which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +IAIK PKCS#11 Wrapper License + +Copyright (c) 2002 Graz University of Technology. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: + + "This product includes software developed by IAIK of Graz University of + Technology." + + Alternately, this acknowledgment may appear in the software itself, if and + wherever such third-party acknowledgments normally appear. + +4. The names "Graz University of Technology" and "IAIK of Graz University of + Technology" must not be used to endorse or promote products derived from this + software without prior written permission. + +5. Products derived from this software may not be called "IAIK PKCS Wrapper", + nor may "IAIK" appear in their name, without prior written permission of + Graz University of Technology. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to ICU4C 4.0.1 and ICU4J 4.4, which +may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) 1995-2010 International Business Machines Corporation and others + +All rights reserved. + +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, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +provided that the above copyright notice(s) and this permission notice appear +in all copies of the Software and that both the above copyright notice(s) and +this permission notice appear in supporting documentation. + +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 OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE +LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. +All trademarks and registered trademarks mentioned herein are the property of +their respective owners. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to IJG JPEG 6b, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +This software is copyright (C) 1991-1998, Thomas G. Lane. +All Rights Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to these +conditions: +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice +unaltered; and any additions, deletions, or changes to the original files +must be clearly indicated in accompanying documentation. +(2) If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the work of +the Independent JPEG Group". +(3) Permission for use of this software is granted only if the user accepts +full responsibility for any undesirable consequences; the authors accept +NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, +not just to the unmodified library. If you use our work, you ought to +acknowledge us. + +Permission is NOT granted for the use of any IJG author's name or company name +in advertising or publicity relating to this software or products derived from +it. This software may be referred to only as "the Independent JPEG Group's +software". + +We specifically permit and encourage the use of this software as the basis of +commercial products, provided that all warranty or liability claims are +assumed by the product vendor. + +--- end of LICENSE --- + +-------------------------------------------------------------------------------- + +%% This notice is provided with respect to Jing 20030619, which may +be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) 2001-2003 Thai Open Source Software Center Ltd All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +Neither the name of the Thai Open Source Software Center Ltd nor +the names of its contributors may be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +--- end of LICENSE --- + +-------------------------------------------------------------------------------- + +%% This notice is provided with respect to Joni v2.1.16, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) 2017 JRuby Team + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to JOpt-Simple v3.0, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + + Copyright (c) 2004-2009 Paul R. Holser, Jr. + + 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: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + 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. + +--- end of LICENSE --- + +-------------------------------------------------------------------------------- + +%% This notice is provided with respect to Kerberos functionality, which +which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + + (C) Copyright IBM Corp. 1999 All Rights Reserved. + Copyright 1997 The Open Group Research Institute. All rights reserved. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Kerberos functionality from +FundsXpress, INC., which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + + Copyright (C) 1998 by the FundsXpress, INC. + + All rights reserved. + + Export of this software from the United States of America may require + a specific license from the United States Government. It is the + responsibility of any person or organization contemplating export to + obtain such a license before exporting. + + WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + distribute this software and its documentation for any purpose and + without fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright notice and + this permission notice appear in supporting documentation, and that + the name of FundsXpress. not be used in advertising or publicity pertaining + to distribution of the software without specific, written prior + permission. FundsXpress makes no representations about the suitability of + this software for any purpose. It is provided "as is" without express + or implied warranty. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Kronos OpenGL headers, which may be +included with JDK 8 and OpenJDK 8 source distributions. + +--- begin of LICENSE --- + + Copyright (c) 2007 The Khronos Group Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and/or associated documentation files (the "Materials"), to + deal in the Materials without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Materials, and to permit persons to whom the Materials are + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Materials. + + THE MATERIALS ARE 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 MATERIALS OR THE USE OR OTHER DEALINGS IN THE + MATERIALS. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% Portions Copyright Eastman Kodak Company 1991-2003 + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to libpng 1.6.37, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +COPYRIGHT NOTICE, DISCLAIMER, and LICENSE +========================================= + +PNG Reference Library License version 2 +--------------------------------------- + + * Copyright (c) 1995-2019 The PNG Reference Library Authors. + * Copyright (c) 2018-2019 Cosmin Truta. + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. + * Copyright (c) 1996-1997 Andreas Dilger. + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + +The software is supplied "as is", without warranty of any kind, +express or implied, including, without limitation, the warranties +of merchantability, fitness for a particular purpose, title, and +non-infringement. In no event shall the Copyright owners, or +anyone distributing the software, be liable for any damages or +other liability, whether in contract, tort or otherwise, arising +from, out of, or in connection with the software, or the use or +other dealings in the software, even if advised of the possibility +of such damage. + +Permission is hereby granted to use, copy, modify, and distribute +this software, or portions hereof, for any purpose, without fee, +subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you + use this software in a product, an acknowledgment in the product + documentation would be appreciated, but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + + +PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) +----------------------------------------------------------------------- + +libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are +Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are +derived from libpng-1.0.6, and are distributed according to the same +disclaimer and license as libpng-1.0.6 with the following individuals +added to the list of Contributing Authors: + + Simon-Pierre Cadieux + Eric S. Raymond + Mans Rullgard + Cosmin Truta + Gilles Vollant + James Yu + Mandar Sahastrabuddhe + Google Inc. + Vadim Barkov + +and with the following additions to the disclaimer: + + There is no warranty against interference with your enjoyment of + the library or against infringement. There is no warranty that our + efforts or the library will fulfill any of your particular purposes + or needs. This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and effort is + with the user. + +Some files in the "contrib" directory and some configure-generated +files that are distributed with libpng have other copyright owners, and +are released under other open source licenses. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from +libpng-0.96, and are distributed according to the same disclaimer and +license as libpng-0.96, with the following individuals added to the +list of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, +and are distributed according to the same disclaimer and license as +libpng-0.88, with the following individuals added to the list of +Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +Some files in the "scripts" directory have other copyright owners, +but are released under this license. + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing +Authors and Group 42, Inc. disclaim all warranties, expressed or +implied, including, without limitation, the warranties of +merchantability and of fitness for any purpose. The Contributing +Authors and Group 42, Inc. assume no liability for direct, indirect, +incidental, special, exemplary, or consequential damages, which may +result from the use of the PNG Reference Library, even if advised of +the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + + 1. The origin of this source code must not be misrepresented. + + 2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, +without fee, and encourage the use of this source code as a component +to supporting the PNG file format in commercial products. If you use +this source code in a product, acknowledgment is not required but would +be appreciated. + +TRADEMARK: + +The name "libpng" has not been registered by the Copyright owner +as a trademark in any jurisdiction. However, because libpng has +been distributed and maintained world-wide, continually since 1995, +the Copyright owner claims "common-law trademark protection" in any +jurisdiction where common-law trademark is recognized. + +OSI CERTIFICATION: + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is +a certification mark of the Open Source Initiative. OSI has not addressed +the additional disclaimers inserted at version 1.0.7. + +EXPORT CONTROL: + +The Copyright owner believes that the Export Control Classification +Number (ECCN) for libpng is EAR99, which means not subject to export +controls or International Traffic in Arms Regulations (ITAR) because +it is open source, publicly available software, that does not contain +any encryption software. See the EAR, paragraphs 734.3(b)(3) and +734.7(b). + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +July 15, 2018 + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to GIFLIB 5.2.1 & libungif 4.1.3, +which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Little CMS 2.11, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Little CMS +Copyright (c) 1998-2020 Marti Maria Saguer + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% Lucida is a registered trademark or trademark of Bigelow & Holmes in the +U.S. and other countries. + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Mesa 3D Graphics Library v4.1, +which may be included with JRE 8, JDK 8, and OpenJDK 8 source distributions. + +--- begin of LICENSE --- + + Mesa 3-D Graphics Library v19.2.1 + + Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + + 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: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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. + +Attention, Contributors + +When contributing to the Mesa project you must agree to the licensing terms +of the component to which you're contributing. +The following section lists the primary components of the Mesa distribution +and their respective licenses. +Mesa Component Licenses + + + +Component Location License +------------------------------------------------------------------ +Main Mesa code src/mesa/ MIT +Device drivers src/mesa/drivers/* MIT, generally + +Gallium code src/gallium/ MIT + +Ext headers GL/glext.h Khronos + GL/glxext.h Khronos + GL/wglext.h Khronos + KHR/khrplatform.h Khronos + +***************************************************************************** + +---- +include/GL/gl.h : + + + Mesa 3-D graphics library + + Copyright (C) 1999-2006 Brian Paul All Rights Reserved. + Copyright (C) 2009 VMware, Inc. All Rights Reserved. + + 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: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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. + + ***************************************************************************** + +---- +include/GL/glext.h +include/GL/glxext.h +include/GL/wglxext.h : + + + Copyright (c) 2013 - 2018 The Khronos Group Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and/or associated documentation files (the + "Materials"), to deal in the Materials without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Materials, and to + permit persons to whom the Materials are furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Materials. + + THE MATERIALS ARE 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 + MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + + ***************************************************************************** + +---- +include/KHR/khrplatform.h : + + Copyright (c) 2008 - 2018 The Khronos Group Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and/or associated documentation files (the + "Materials"), to deal in the Materials without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Materials, and to + permit persons to whom the Materials are furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Materials. + + THE MATERIALS ARE 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 + MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + + ***************************************************************************** + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Mozilla Network Security +Services (NSS), which is supplied with the JDK test suite in the OpenJDK +source code repository. It is licensed under Mozilla Public License (MPL), +version 2.0. + +The NSS libraries are supplied in executable form, built from unmodified +NSS source code labeled with the "NSS_3_16_RTM" HG tag. + +The NSS source code is available in the OpenJDK source code repository at: + jdk/test/sun/security/pkcs11/nss/src + +The NSS libraries are available in the OpenJDK source code repository at: + jdk/test/sun/security/pkcs11/nss/lib + +--- begin of LICENSE --- + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to PC/SC Lite v1.8.26, +which may be included with JRE 8, JDK 8, and OpenJDK 8 on Linux and Solaris. + +--- begin of LICENSE --- + +Copyright (c) 1999-2003 David Corcoran +Copyright (c) 2001-2011 Ludovic Rousseau +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +Changes to this license can be made only by the copyright author with +explicit written consent. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to PorterStemmer v4, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +See: http://tartarus.org/~martin/PorterStemmer + +The software is completely free for any purpose, unless notes at the head of +the program text indicates otherwise (which is rare). In any case, the notes +about licensing are never more restrictive than the BSD License. + +In every case where the software is not written by me (Martin Porter), this +licensing arrangement has been endorsed by the contributor, and it is +therefore unnecessary to ask the contributor again to confirm it. + +I have not asked any contributors (or their employers, if they have them) for +proofs that they have the right to distribute their software in this way. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Relax NG Object/Parser v.20050510, +which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) Kohsuke Kawaguchi + +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: The above copyright +notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +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. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to RelaxNGCC v1.12, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) 2000-2003 Daisuke Okajima and Kohsuke Kawaguchi. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: + + "This product includes software developed by Daisuke Okajima + and Kohsuke Kawaguchi (http://relaxngcc.sf.net/)." + +Alternately, this acknowledgment may appear in the software itself, if and +wherever such third-party acknowledgments normally appear. + +4. The names of the copyright holders must not be used to endorse or promote + products derived from this software without prior written permission. For + written permission, please contact the copyright holders. + +5. Products derived from this software may not be called "RELAXNGCC", nor may + "RELAXNGCC" appear in their name, without prior written permission of the + copyright holders. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE APACHE +SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Relax NG Datatype 1.0, which +may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) 2005, 2010 Thai Open Source Software Center Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + Neither the names of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- end of LICENSE --- + +-------------------------------------------------------------------------------- + +%% This notice is provided with respect to SoftFloat version 2b, which may be +included with JRE 8, JDK 8, and OpenJDK 8 on Linux/ARM. + +--- begin of LICENSE --- + +Use of any of this software is governed by the terms of the license below: + +SoftFloat was written by me, John R. Hauser. This work was made possible in +part by the International Computer Science Institute, located at Suite 600, +1947 Center Street, Berkeley, California 94704. Funding was partially +provided by the National Science Foundation under grant MIP-9311980. The +original version of this code was written as part of a project to build +a fixed-point vector processor in collaboration with the University of +California at Berkeley, overseen by Profs. Nelson Morgan and John Wawrzynek. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL +LOSSES, COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO +FURTHERMORE EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER +SCIENCE INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, +COSTS, OR OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE +SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, provided +that the minimal documentation requirements stated in the source code are +satisfied. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Sparkle 1.5, +which may be included with JRE 8 on Mac OS X. + +--- begin of LICENSE --- + +Copyright (c) 2012 Sparkle.org and Andy Matuschak + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% Portions licensed from Taligent, Inc. + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Thai Dictionary, which may be +included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (C) 1982 The Royal Institute, Thai Royal Government. + +Copyright (C) 1998 National Electronics and Computer Technology Center, +National Science and Technology Development Agency, +Ministry of Science Technology and Environment, +Thai Royal Government. + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Unicode 6.2.0 & CLDR 21.0.1 +which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Unicode Terms of Use + +For the general privacy policy governing access to this site, see the Unicode +Privacy Policy. For trademark usage, see the Unicode® Consortium Name and +Trademark Usage Policy. + +A. Unicode Copyright. + 1. Copyright © 1991-2013 Unicode, Inc. All rights reserved. + + 2. Certain documents and files on this website contain a legend indicating + that "Modification is permitted." Any person is hereby authorized, + without fee, to modify such documents and files to create derivative + works conforming to the Unicode® Standard, subject to Terms and + Conditions herein. + + 3. Any person is hereby authorized, without fee, to view, use, reproduce, + and distribute all documents and files solely for informational + purposes in the creation of products supporting the Unicode Standard, + subject to the Terms and Conditions herein. + + 4. Further specifications of rights and restrictions pertaining to the use + of the particular set of data files known as the "Unicode Character + Database" can be found in Exhibit 1. + + 5. Each version of the Unicode Standard has further specifications of + rights and restrictions of use. For the book editions (Unicode 5.0 and + earlier), these are found on the back of the title page. The online + code charts carry specific restrictions. All other files, including + online documentation of the core specification for Unicode 6.0 and + later, are covered under these general Terms of Use. + + 6. No license is granted to "mirror" the Unicode website where a fee is + charged for access to the "mirror" site. + + 7. Modification is not permitted with respect to this document. All copies + of this document must be verbatim. + +B. Restricted Rights Legend. Any technical data or software which is licensed + to the United States of America, its agencies and/or instrumentalities + under this Agreement is commercial technical data or commercial computer + software developed exclusively at private expense as defined in FAR 2.101, + or DFARS 252.227-7014 (June 1995), as applicable. For technical data, use, + duplication, or disclosure by the Government is subject to restrictions as + set forth in DFARS 202.227-7015 Technical Data, Commercial and Items (Nov + 1995) and this Agreement. For Software, in accordance with FAR 12-212 or + DFARS 227-7202, as applicable, use, duplication or disclosure by the + Government is subject to the restrictions set forth in this Agreement. + +C. Warranties and Disclaimers. + 1. This publication and/or website may include technical or typographical + errors or other inaccuracies . Changes are periodically added to the + information herein; these changes will be incorporated in new editions + of the publication and/or website. Unicode may make improvements and/or + changes in the product(s) and/or program(s) described in this + publication and/or website at any time. + + 2. If this file has been purchased on magnetic or optical media from + Unicode, Inc. the sole and exclusive remedy for any claim will be + exchange of the defective media within ninety (90) days of original + purchase. + + 3. EXCEPT AS PROVIDED IN SECTION C.2, THIS PUBLICATION AND/OR SOFTWARE IS + PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS, IMPLIED, + OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + UNICODE AND ITS LICENSORS ASSUME NO RESPONSIBILITY FOR ERRORS OR + OMISSIONS IN THIS PUBLICATION AND/OR SOFTWARE OR OTHER DOCUMENTS WHICH + ARE REFERENCED BY OR LINKED TO THIS PUBLICATION OR THE UNICODE WEBSITE. + +D. Waiver of Damages. In no event shall Unicode or its licensors be liable for + any special, incidental, indirect or consequential damages of any kind, or + any damages whatsoever, whether or not Unicode was advised of the + possibility of the damage, including, without limitation, those resulting + from the following: loss of use, data or profits, in connection with the + use, modification or distribution of this information or its derivatives. + +E.Trademarks & Logos. + 1. The Unicode Word Mark and the Unicode Logo are trademarks of Unicode, + Inc. “The Unicode Consortium” and “Unicode, Inc.” are trade names of + Unicode, Inc. Use of the information and materials found on this + website indicates your acknowledgement of Unicode, Inc.’s exclusive + worldwide rights in the Unicode Word Mark, the Unicode Logo, and the + Unicode trade names. + + 2. The Unicode Consortium Name and Trademark Usage Policy (“Trademark + Policy”) are incorporated herein by reference and you agree to abide by + the provisions of the Trademark Policy, which may be changed from time + to time in the sole discretion of Unicode, Inc. + + 3. All third party trademarks referenced herein are the property of their + respective owners. + +Miscellaneous. + 1. Jurisdiction and Venue. This server is operated from a location in the + State of California, United States of America. Unicode makes no + representation that the materials are appropriate for use in other + locations. If you access this server from other locations, you are + responsible for compliance with local laws. This Agreement, all use of + this site and any claims and damages resulting from use of this site are + governed solely by the laws of the State of California without regard to + any principles which would apply the laws of a different jurisdiction. + The user agrees that any disputes regarding this site shall be resolved + solely in the courts located in Santa Clara County, California. The user + agrees said courts have personal jurisdiction and agree to waive any + right to transfer the dispute to any other forum. + + 2. Modification by Unicode. Unicode shall have the right to modify this + Agreement at any time by posting it to this site. The user may not + assign any part of this Agreement without Unicode’s prior written + consent. + + 3. Taxes. The user agrees to pay any taxes arising from access to this + website or use of the information herein, except for those based on + Unicode’s net income. + + 4. Severability. If any provision of this Agreement is declared invalid or + unenforceable, the remaining provisions of this Agreement shall remain + in effect. + + 5. Entire Agreement. This Agreement constitutes the entire agreement + between the parties. + +EXHIBIT 1 +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, and +http://www.unicode.org/cldr/data/. Unicode Data Files do not include PDF +online code charts under the directory http://www.unicode.org/Public/. +Software includes any source code published in the Unicode Standard or under +the directories http://www.unicode.org/Public/, +http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/. + +NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, +INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA +FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO +BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT +AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR +SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2012 Unicode, Inc. All rights reserved. Distributed under the +Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the Unicode data files and any associated documentation (the "Data Files") +or Unicode software and any associated documentation (the "Software") to deal +in the Data Files or Software without restriction, including without +limitation the rights to use, copy, modify, merge, publish, distribute, and/or +sell copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that (a) the above +copyright notice(s) and this permission notice appear with all copies of the +Data Files or Software, (b) both the above copyright notice(s) and this +permission notice appear in associated documentation, and (c) there is clear +notice in each modified Data File or in the Software as well as in the +documentation associated with the Data File(s) or Software that the data or +software has been modified. + +THE DATA FILES AND SOFTWARE ARE 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 OF THIRD +PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN +THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE +DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in these Data Files or Software without prior written authorization of the +copyright holder. + +Unicode and the Unicode logo are trademarks of Unicode, Inc. in the United +States and other countries. All third party trademarks referenced herein are +the property of their respective owners. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to UPX v3.01, which may be included +with JRE 8 on Windows. + +--- begin of LICENSE --- + +Use of any of this software is governed by the terms of the license below: + + + ooooo ooo ooooooooo. ooooooo ooooo + `888' `8' `888 `Y88. `8888 d8' + 888 8 888 .d88' Y888..8P + 888 8 888ooo88P' `8888' + 888 8 888 .8PY888. + `88. .8' 888 d8' `888b + `YbodP' o888o o888o o88888o + + + The Ultimate Packer for eXecutables + Copyright (c) 1996-2000 Markus Oberhumer & Laszlo Molnar + http://wildsau.idv.uni-linz.ac.at/mfx/upx.html + http://www.nexus.hu/upx + http://upx.tsx.org + + +PLEASE CAREFULLY READ THIS LICENSE AGREEMENT, ESPECIALLY IF YOU PLAN +TO MODIFY THE UPX SOURCE CODE OR USE A MODIFIED UPX VERSION. + + +ABSTRACT +======== + + UPX and UCL are copyrighted software distributed under the terms + of the GNU General Public License (hereinafter the "GPL"). + + The stub which is imbedded in each UPX compressed program is part + of UPX and UCL, and contains code that is under our copyright. The + terms of the GNU General Public License still apply as compressing + a program is a special form of linking with our stub. + + As a special exception we grant the free usage of UPX for all + executables, including commercial programs. + See below for details and restrictions. + + +COPYRIGHT +========= + + UPX and UCL are copyrighted software. All rights remain with the authors. + + UPX is Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + UPX is Copyright (C) 1996-2000 Laszlo Molnar + + UCL is Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer + + +GNU GENERAL PUBLIC LICENSE +========================== + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + UPX and UCL are distributed in the hope that they will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + + +SPECIAL EXCEPTION FOR COMPRESSED EXECUTABLES +============================================ + + The stub which is imbedded in each UPX compressed program is part + of UPX and UCL, and contains code that is under our copyright. The + terms of the GNU General Public License still apply as compressing + a program is a special form of linking with our stub. + + Hereby Markus F.X.J. Oberhumer and Laszlo Molnar grant you special + permission to freely use and distribute all UPX compressed programs + (including commercial ones), subject to the following restrictions: + + 1. You must compress your program with a completely unmodified UPX + version; either with our precompiled version, or (at your option) + with a self compiled version of the unmodified UPX sources as + distributed by us. + 2. This also implies that the UPX stub must be completely unmodfied, i.e. + the stub imbedded in your compressed program must be byte-identical + to the stub that is produced by the official unmodified UPX version. + 3. The decompressor and any other code from the stub must exclusively get + used by the unmodified UPX stub for decompressing your program at + program startup. No portion of the stub may get read, copied, + called or otherwise get used or accessed by your program. + + +ANNOTATIONS +=========== + + - You can use a modified UPX version or modified UPX stub only for + programs that are compatible with the GNU General Public License. + + - We grant you special permission to freely use and distribute all UPX + compressed programs. But any modification of the UPX stub (such as, + but not limited to, removing our copyright string or making your + program non-decompressible) will immediately revoke your right to + use and distribute a UPX compressed program. + + - UPX is not a software protection tool; by requiring that you use + the unmodified UPX version for your proprietary programs we + make sure that any user can decompress your program. This protects + both you and your users as nobody can hide malicious code - + any program that cannot be decompressed is highly suspicious + by definition. + + - You can integrate all or part of UPX and UCL into projects that + are compatible with the GNU GPL, but obviously you cannot grant + any special exceptions beyond the GPL for our code in your project. + + - We want to actively support manufacturers of virus scanners and + similar security software. Please contact us if you would like to + incorporate parts of UPX or UCL into such a product. + + + +Markus F.X.J. Oberhumer Laszlo Molnar +markus.oberhumer@jk.uni-linz.ac.at ml1050@cdata.tvnet.hu + +Linz, Austria, 25 Feb 2000 + +Additional License(s) + +The UPX license file is at http://upx.sourceforge.net/upx-license.html. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to Xfree86-VidMode Extension 1.0, +which may be included with JRE 8, JDK 8, and OpenJDK 8 on Linux and Solaris. + +--- begin of LICENSE --- + +Version 1.1 of XFree86 ProjectLicence. + +Copyright (C) 1994-2004 The XFree86 Project, Inc. All rights reserved. + +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, sublicence, 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: + + 1. Redistributions of source code must retain the above copyright + notice,this list of conditions, and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution, and in the same place + and form as other copyright, license and disclaimer information. + + 3. The end-user documentation included with the redistribution, if any,must + include the following acknowledgment: "This product includes + software developed by The XFree86 Project, Inc (http://www.xfree86.org/) and + its contributors", in the same place and form as other third-party + acknowledgments. Alternately, this acknowledgment may appear in the software + itself, in the same form and location as other such third-party + acknowledgments. + + 4. Except as contained in this notice, the name of The XFree86 Project,Inc + shall not be used in advertising or otherwise to promote the sale, use + or other dealings in this Software without prior written authorization from + The XFree86 Project, Inc. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL THE XFREE86 PROJECT, INC OR ITS CONTRIBUTORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to xwd v1.0.7, which may be +included with JRE 8, JDK 8, and OpenJDK 8 on Linux and Solaris. + +xwd utility + +--- begin of LICENSE --- + +Copyright 1994 Hewlett-Packard Co. +Copyright 1996, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +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 OPEN GROUP 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. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +--- end of LICENSE --- +_____________________________ +Copyright notice for HPkeysym.h: +/* + +Copyright 1987, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +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 OPEN GROUP 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. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, + +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Hewlett Packard +or Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +HEWLETT-PACKARD MAKES NO WARRANTY OF ANY KIND WITH REGARD +TO THIS SOFWARE, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. Hewlett-Packard shall not be liable for errors +contained herein or direct, indirect, special, incidental or +consequential damages in connection with the furnishing, +performance, or use of this material. + +*/ + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to zlib v1.2.11, which may be included +with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to the following which may be +included with JRE 8, JDK 8, and OpenJDK 8. + + Apache Commons Math 3.2 + Apache Derby 10.11.1.2 + Apache Jakarta BCEL 5.1 + Apache Santuario XML Security for Java 2.1.3 + Apache Xalan-Java 2.7.2 + Apache Xerces Java 2.10.0 + Apache XML Resolver 1.1 + + +--- begin of LICENSE --- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- + +%% This notice is provided with respect to OASIS PKCS #11 Cryptographic Token +Interface v2.40, which may be included with JRE 8, JDK 8, and OpenJDK 8. + +--- begin of LICENSE --- + +Copyright (c) OASIS Open 2016. All Rights Reserved. + +All capitalized terms in the following text have the meanings assigned to them +in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The +full Policy may be found at the OASIS website: +[http://www.oasis-open.org/policies-guidelines/ipr] + +This document and translations of it may be copied and furnished to others, and +derivative works that comment on or otherwise explain it or assist in its +implementation may be prepared, copied, published, and distributed, in whole or +in part, without restriction of any kind, provided that the above copyright +notice and this section are included on all such copies and derivative works. +However, this document itself may not be modified in any way, including by +removing the copyright notice or references to OASIS, except as needed for the +purpose of developing any document or deliverable produced by an OASIS +Technical Committee (in which case the rules applicable to copyrights, as set +forth in the OASIS IPR Policy, must be followed) or as required to translate it +into languages other than English. + +The limited permissions granted above are perpetual and will not be revoked by +OASIS or its successors or assigns. + +This document and the information contained herein is provided on an "AS IS" +basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT +INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR +FITNESS FOR A PARTICULAR PURPOSE. OASIS AND ITS MEMBERS WILL NOT BE LIABLE FOR +ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE +OF THIS DOCUMENT OR ANY PART THEREOF. + +--- end of LICENSE --- + +------------------------------------------------------------------------------- diff --git a/FCL/src/main/assets/java/bin/java b/FCL/src/main/assets/java/bin/java new file mode 100644 index 00000000..513aac94 Binary files /dev/null and b/FCL/src/main/assets/java/bin/java differ diff --git a/FCL/src/main/assets/java/bin/jjs b/FCL/src/main/assets/java/bin/jjs new file mode 100644 index 00000000..a303aff5 Binary files /dev/null and b/FCL/src/main/assets/java/bin/jjs differ diff --git a/FCL/src/main/assets/java/bin/keytool b/FCL/src/main/assets/java/bin/keytool new file mode 100644 index 00000000..4de440ec Binary files /dev/null and b/FCL/src/main/assets/java/bin/keytool differ diff --git a/FCL/src/main/assets/java/bin/orbd b/FCL/src/main/assets/java/bin/orbd new file mode 100644 index 00000000..563cf5fe Binary files /dev/null and b/FCL/src/main/assets/java/bin/orbd differ diff --git a/FCL/src/main/assets/java/bin/pack200 b/FCL/src/main/assets/java/bin/pack200 new file mode 100644 index 00000000..d535cb81 Binary files /dev/null and b/FCL/src/main/assets/java/bin/pack200 differ diff --git a/FCL/src/main/assets/java/bin/policytool b/FCL/src/main/assets/java/bin/policytool new file mode 100644 index 00000000..85b2d90d Binary files /dev/null and b/FCL/src/main/assets/java/bin/policytool differ diff --git a/FCL/src/main/assets/java/bin/rmid b/FCL/src/main/assets/java/bin/rmid new file mode 100644 index 00000000..2642b82a Binary files /dev/null and b/FCL/src/main/assets/java/bin/rmid differ diff --git a/FCL/src/main/assets/java/bin/rmiregistry b/FCL/src/main/assets/java/bin/rmiregistry new file mode 100644 index 00000000..56d19322 Binary files /dev/null and b/FCL/src/main/assets/java/bin/rmiregistry differ diff --git a/FCL/src/main/assets/java/bin/servertool b/FCL/src/main/assets/java/bin/servertool new file mode 100644 index 00000000..210b9574 Binary files /dev/null and b/FCL/src/main/assets/java/bin/servertool differ diff --git a/FCL/src/main/assets/java/bin/tnameserv b/FCL/src/main/assets/java/bin/tnameserv new file mode 100644 index 00000000..18789fb1 Binary files /dev/null and b/FCL/src/main/assets/java/bin/tnameserv differ diff --git a/FCL/src/main/assets/java/bin/unpack200 b/FCL/src/main/assets/java/bin/unpack200 new file mode 100644 index 00000000..e3753772 Binary files /dev/null and b/FCL/src/main/assets/java/bin/unpack200 differ diff --git a/FCL/src/main/assets/java/lib/aarch64/jli/libjli.so b/FCL/src/main/assets/java/lib/aarch64/jli/libjli.so new file mode 100644 index 00000000..adb036e1 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/jli/libjli.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/jvm.cfg b/FCL/src/main/assets/java/lib/aarch64/jvm.cfg new file mode 100644 index 00000000..17c4be37 --- /dev/null +++ b/FCL/src/main/assets/java/lib/aarch64/jvm.cfg @@ -0,0 +1,35 @@ +# Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# +# List of JVMs that can be used as an option to java, javac, etc. +# Order is important -- first in this list is the default JVM. +# NOTE that this both this file and its format are UNSUPPORTED and +# WILL GO AWAY in a future release. +# +# You may also select a JVM in an arbitrary location with the +# "-XXaltjvm=" option, but that too is unsupported +# and may not be available in a future release. +# +-server KNOWN +-client IGNORE diff --git a/FCL/src/main/assets/java/lib/aarch64/libawt.so b/FCL/src/main/assets/java/lib/aarch64/libawt.so new file mode 100644 index 00000000..1da97890 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libawt.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libawt_headless.so b/FCL/src/main/assets/java/lib/aarch64/libawt_headless.so new file mode 100644 index 00000000..7ee45c94 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libawt_headless.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libawt_xawt.so b/FCL/src/main/assets/java/lib/aarch64/libawt_xawt.so new file mode 100644 index 00000000..add175f5 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libawt_xawt.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libdt_socket.so b/FCL/src/main/assets/java/lib/aarch64/libdt_socket.so new file mode 100644 index 00000000..905af22f Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libdt_socket.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libfontmanager.so b/FCL/src/main/assets/java/lib/aarch64/libfontmanager.so new file mode 100644 index 00000000..bf33c1ec Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libfontmanager.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libfreetype.so b/FCL/src/main/assets/java/lib/aarch64/libfreetype.so new file mode 100644 index 00000000..cde75559 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libfreetype.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libhprof.so b/FCL/src/main/assets/java/lib/aarch64/libhprof.so new file mode 100644 index 00000000..8cfdd625 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libhprof.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libinstrument.so b/FCL/src/main/assets/java/lib/aarch64/libinstrument.so new file mode 100644 index 00000000..35d628d5 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libinstrument.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libj2gss.so b/FCL/src/main/assets/java/lib/aarch64/libj2gss.so new file mode 100644 index 00000000..af7dfd34 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libj2gss.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libj2pcsc.so b/FCL/src/main/assets/java/lib/aarch64/libj2pcsc.so new file mode 100644 index 00000000..2682eeaf Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libj2pcsc.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libj2pkcs11.so b/FCL/src/main/assets/java/lib/aarch64/libj2pkcs11.so new file mode 100644 index 00000000..da2a96b1 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libj2pkcs11.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjaas_unix.so b/FCL/src/main/assets/java/lib/aarch64/libjaas_unix.so new file mode 100644 index 00000000..785689d8 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjaas_unix.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjava.so b/FCL/src/main/assets/java/lib/aarch64/libjava.so new file mode 100644 index 00000000..affa4689 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjava.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjava_crw_demo.so b/FCL/src/main/assets/java/lib/aarch64/libjava_crw_demo.so new file mode 100644 index 00000000..3b96c8c9 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjava_crw_demo.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjawt.so b/FCL/src/main/assets/java/lib/aarch64/libjawt.so new file mode 100644 index 00000000..0c6aaa89 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjawt.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjdwp.so b/FCL/src/main/assets/java/lib/aarch64/libjdwp.so new file mode 100644 index 00000000..23a7f655 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjdwp.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjpeg.so b/FCL/src/main/assets/java/lib/aarch64/libjpeg.so new file mode 100644 index 00000000..0ab52040 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjpeg.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjsdt.so b/FCL/src/main/assets/java/lib/aarch64/libjsdt.so new file mode 100644 index 00000000..d0181b6e Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjsdt.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjsig.so b/FCL/src/main/assets/java/lib/aarch64/libjsig.so new file mode 100644 index 00000000..f875a175 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjsig.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libjsound.so b/FCL/src/main/assets/java/lib/aarch64/libjsound.so new file mode 100644 index 00000000..cc851c18 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libjsound.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/liblcms.so b/FCL/src/main/assets/java/lib/aarch64/liblcms.so new file mode 100644 index 00000000..9c5a648b Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/liblcms.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libmanagement.so b/FCL/src/main/assets/java/lib/aarch64/libmanagement.so new file mode 100644 index 00000000..b3f8ae78 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libmanagement.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libmlib_image.so b/FCL/src/main/assets/java/lib/aarch64/libmlib_image.so new file mode 100644 index 00000000..58c5bba7 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libmlib_image.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libnet.so b/FCL/src/main/assets/java/lib/aarch64/libnet.so new file mode 100644 index 00000000..caeab07c Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libnet.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libnio.so b/FCL/src/main/assets/java/lib/aarch64/libnio.so new file mode 100644 index 00000000..eac7204a Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libnio.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libnpt.so b/FCL/src/main/assets/java/lib/aarch64/libnpt.so new file mode 100644 index 00000000..a85ca35b Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libnpt.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libsctp.so b/FCL/src/main/assets/java/lib/aarch64/libsctp.so new file mode 100644 index 00000000..373f73bf Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libsctp.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libsunec.so b/FCL/src/main/assets/java/lib/aarch64/libsunec.so new file mode 100644 index 00000000..01b333d0 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libsunec.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libtinyiconv.so b/FCL/src/main/assets/java/lib/aarch64/libtinyiconv.so new file mode 100644 index 00000000..f6262858 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libtinyiconv.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libunpack.so b/FCL/src/main/assets/java/lib/aarch64/libunpack.so new file mode 100644 index 00000000..9bed36fe Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libunpack.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libverify.so b/FCL/src/main/assets/java/lib/aarch64/libverify.so new file mode 100644 index 00000000..69bc71dc Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libverify.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/libzip.so b/FCL/src/main/assets/java/lib/aarch64/libzip.so new file mode 100644 index 00000000..d302d058 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/libzip.so differ diff --git a/FCL/src/main/assets/java/lib/aarch64/server/Xusage.txt b/FCL/src/main/assets/java/lib/aarch64/server/Xusage.txt new file mode 100644 index 00000000..d24ada9c --- /dev/null +++ b/FCL/src/main/assets/java/lib/aarch64/server/Xusage.txt @@ -0,0 +1,24 @@ + -Xmixed mixed mode execution (default) + -Xint interpreted mode execution only + -Xbootclasspath: + set search path for bootstrap classes and resources + -Xbootclasspath/a: + append to end of bootstrap class path + -Xbootclasspath/p: + prepend in front of bootstrap class path + -Xnoclassgc disable class garbage collection + -Xincgc enable incremental garbage collection + -Xloggc: log GC status to a file with time stamps + -Xbatch disable background compilation + -Xms set initial Java heap size + -Xmx set maximum Java heap size + -Xss set java thread stack size + -Xprof output cpu profiling data + -Xfuture enable strictest checks, anticipating future default + -Xrs reduce use of OS signals by Java/VM (see documentation) + -Xcheck:jni perform additional checks for JNI functions + -Xshare:off do not attempt to use shared class data + -Xshare:auto use shared class data if possible (default) + -Xshare:on require using shared class data, otherwise fail. + +The -X options are non-standard and subject to change without notice. diff --git a/FCL/src/main/assets/java/lib/aarch64/server/libjvm.so b/FCL/src/main/assets/java/lib/aarch64/server/libjvm.so new file mode 100644 index 00000000..b599ba16 Binary files /dev/null and b/FCL/src/main/assets/java/lib/aarch64/server/libjvm.so differ diff --git a/FCL/src/main/assets/java/lib/calendars.properties b/FCL/src/main/assets/java/lib/calendars.properties new file mode 100644 index 00000000..1196c291 --- /dev/null +++ b/FCL/src/main/assets/java/lib/calendars.properties @@ -0,0 +1,62 @@ +# Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Japanese imperial calendar +# +# Meiji since 1868-01-01 00:00:00 local time (Gregorian) +# Taisho since 1912-07-30 00:00:00 local time (Gregorian) +# Showa since 1926-12-25 00:00:00 local time (Gregorian) +# Heisei since 1989-01-08 00:00:00 local time (Gregorian) +# Reiwa since 2019-05-01 00:00:00 local time (Gregorian) +calendar.japanese.type= LocalGregorianCalendar +calendar.japanese.eras= \ + name=Meiji,abbr=M,since=-3218832000000; \ + name=Taisho,abbr=T,since=-1812153600000; \ + name=Showa,abbr=S,since=-1357603200000; \ + name=Heisei,abbr=H,since=600220800000; \ + name=Reiwa,abbr=R,since=1556668800000 + +# +# Taiwanese calendar +# Minguo since 1911-01-01 00:00:00 local time (Gregorian) +calendar.taiwanese.type= LocalGregorianCalendar +calendar.taiwanese.eras= \ + name=MinGuo,since=-1830384000000 + +# +# Thai Buddhist calendar +# Buddhist Era since -542-01-01 00:00:00 local time (Gregorian) +calendar.thai-buddhist.type= LocalGregorianCalendar +calendar.thai-buddhist.eras= \ + name=BuddhistEra,abbr=B.E.,since=-79302585600000 +calendar.thai-buddhist.year-boundary= \ + day1=4-1,since=-79302585600000; \ + day1=1-1,since=-915148800000 + +# +# Hijrah calendars +# +calendar.hijrah.Hijrah-umalqura= hijrah-config-umalqura.properties +calendar.hijrah.Hijrah-umalqura.type= islamic-umalqura diff --git a/FCL/src/main/assets/java/lib/charsets.jar b/FCL/src/main/assets/java/lib/charsets.jar new file mode 100644 index 00000000..f585ab91 Binary files /dev/null and b/FCL/src/main/assets/java/lib/charsets.jar differ diff --git a/FCL/src/main/assets/java/lib/classlist b/FCL/src/main/assets/java/lib/classlist new file mode 100644 index 00000000..2a3915c0 --- /dev/null +++ b/FCL/src/main/assets/java/lib/classlist @@ -0,0 +1,2559 @@ +java/lang/Object +java/lang/String +java/io/Serializable +java/lang/Comparable +java/lang/CharSequence +java/lang/Class +java/lang/reflect/GenericDeclaration +java/lang/reflect/AnnotatedElement +java/lang/reflect/Type +java/lang/Cloneable +java/lang/ClassLoader +java/lang/System +java/lang/Throwable +java/lang/Error +java/lang/ThreadDeath +java/lang/Exception +java/lang/RuntimeException +java/lang/SecurityManager +java/security/ProtectionDomain +java/security/AccessControlContext +java/security/SecureClassLoader +java/lang/ClassNotFoundException +java/lang/ReflectiveOperationException +java/lang/NoClassDefFoundError +java/lang/LinkageError +java/lang/ClassCastException +java/lang/ArrayStoreException +java/lang/VirtualMachineError +java/lang/OutOfMemoryError +java/lang/StackOverflowError +java/lang/IllegalMonitorStateException +java/lang/ref/Reference +java/lang/ref/SoftReference +java/lang/ref/WeakReference +java/lang/ref/FinalReference +java/lang/ref/PhantomReference +sun/misc/Cleaner +java/lang/ref/Finalizer +java/lang/Thread +java/lang/Runnable +java/lang/ThreadGroup +java/lang/Thread$UncaughtExceptionHandler +java/util/Properties +java/util/Hashtable +java/util/Map +java/util/Dictionary +java/lang/reflect/AccessibleObject +java/lang/reflect/Field +java/lang/reflect/Member +java/lang/reflect/Parameter +java/lang/reflect/Method +java/lang/reflect/Executable +java/lang/reflect/Constructor +sun/reflect/MagicAccessorImpl +sun/reflect/MethodAccessorImpl +sun/reflect/MethodAccessor +sun/reflect/ConstructorAccessorImpl +sun/reflect/ConstructorAccessor +sun/reflect/DelegatingClassLoader +sun/reflect/ConstantPool +sun/reflect/UnsafeStaticFieldAccessorImpl +sun/reflect/UnsafeFieldAccessorImpl +sun/reflect/FieldAccessorImpl +sun/reflect/FieldAccessor +sun/reflect/CallerSensitive +java/lang/annotation/Annotation +java/lang/invoke/DirectMethodHandle +java/lang/invoke/MethodHandle +java/lang/invoke/MemberName +java/lang/invoke/MethodHandleNatives +java/lang/invoke/LambdaForm +java/lang/invoke/MethodType +java/lang/BootstrapMethodError +java/lang/invoke/CallSite +java/lang/invoke/ConstantCallSite +java/lang/invoke/MutableCallSite +java/lang/invoke/VolatileCallSite +java/lang/StringBuffer +java/lang/AbstractStringBuilder +java/lang/Appendable +java/lang/StringBuilder +sun/misc/Unsafe +java/io/ByteArrayInputStream +java/io/InputStream +java/io/Closeable +java/lang/AutoCloseable +java/io/File +java/net/URLClassLoader +java/net/URL +java/util/jar/Manifest +sun/misc/Launcher +sun/misc/Launcher$AppClassLoader +sun/misc/Launcher$ExtClassLoader +java/security/CodeSource +java/lang/StackTraceElement +java/nio/Buffer +java/lang/Boolean +java/lang/Character +java/lang/Float +java/lang/Number +java/lang/Double +java/lang/Byte +java/lang/Short +java/lang/Integer +java/lang/Long +java/lang/NullPointerException +java/lang/ArithmeticException +java/io/ObjectStreamField +java/lang/String$CaseInsensitiveComparator +java/util/Comparator +java/lang/RuntimePermission +java/security/BasicPermission +java/security/Permission +java/security/Guard +java/security/AccessController +java/lang/reflect/ReflectPermission +sun/reflect/ReflectionFactory$GetReflectionFactoryAction +java/security/PrivilegedAction +java/security/cert/Certificate +java/util/Vector +java/util/List +java/util/Collection +java/lang/Iterable +java/util/RandomAccess +java/util/AbstractList +java/util/AbstractCollection +java/util/Stack +sun/reflect/ReflectionFactory +java/lang/ref/Reference$Lock +java/lang/ref/Reference$ReferenceHandler +java/lang/ref/ReferenceQueue +java/lang/ref/ReferenceQueue$Null +java/lang/ref/ReferenceQueue$Lock +java/lang/ref/Finalizer$FinalizerThread +sun/misc/VM +java/util/Hashtable$Entry +java/util/Map$Entry +java/lang/Math +java/nio/charset/Charset +sun/nio/cs/StandardCharsets +sun/nio/cs/FastCharsetProvider +java/nio/charset/spi/CharsetProvider +sun/nio/cs/StandardCharsets$Aliases +sun/util/PreHashedMap +java/util/AbstractMap +sun/nio/cs/StandardCharsets$Classes +sun/nio/cs/StandardCharsets$Cache +java/lang/ThreadLocal +java/util/concurrent/atomic/AtomicInteger +java/lang/NoSuchMethodError +java/lang/IncompatibleClassChangeError +java/util/ArrayList +java/util/Collections +java/util/Collections$EmptySet +java/util/AbstractSet +java/util/Set +java/util/Collections$EmptyList +java/util/Collections$EmptyMap +java/util/Collections$UnmodifiableRandomAccessList +java/util/Collections$UnmodifiableList +java/util/Collections$UnmodifiableCollection +sun/reflect/Reflection +java/util/HashMap +java/util/HashMap$Node +java/lang/Class$3 +java/lang/Class$ReflectionData +java/lang/Class$Atomic +sun/reflect/generics/repository/ClassRepository +sun/reflect/generics/repository/GenericDeclRepository +sun/reflect/generics/repository/AbstractRepository +java/lang/Class$AnnotationData +sun/reflect/annotation/AnnotationType +java/lang/ClassValue$ClassValueMap +java/util/WeakHashMap +java/lang/reflect/Modifier +java/lang/reflect/ReflectAccess +sun/reflect/LangReflectAccess +java/util/Arrays +sun/nio/cs/UTF_8 +sun/nio/cs/Unicode +sun/nio/cs/HistoricallyNamedCharset +java/lang/Class$1 +sun/reflect/ReflectionFactory$1 +sun/reflect/NativeConstructorAccessorImpl +sun/reflect/DelegatingConstructorAccessorImpl +java/lang/StringCoding +java/lang/ThreadLocal$ThreadLocalMap +java/lang/ThreadLocal$ThreadLocalMap$Entry +java/lang/StringCoding$StringDecoder +sun/nio/cs/UTF_8$Decoder +sun/nio/cs/ArrayDecoder +java/nio/charset/CharsetDecoder +java/nio/charset/CodingErrorAction +java/util/Hashtable$EntrySet +java/util/Collections$SynchronizedSet +java/util/Collections$SynchronizedCollection +java/util/Objects +java/util/Hashtable$Enumerator +java/util/Enumeration +java/util/Iterator +java/lang/Runtime +sun/misc/Version +java/io/FileInputStream +java/io/FileDescriptor +java/io/FileDescriptor$1 +sun/misc/JavaIOFileDescriptorAccess +sun/misc/SharedSecrets +java/io/FileOutputStream +java/io/OutputStream +java/io/Flushable +java/io/BufferedInputStream +java/io/FilterInputStream +java/util/concurrent/atomic/AtomicReferenceFieldUpdater +java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl +java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1 +java/security/PrivilegedExceptionAction +sun/reflect/misc/ReflectUtil +java/io/PrintStream +java/io/FilterOutputStream +java/io/BufferedOutputStream +java/io/OutputStreamWriter +java/io/Writer +sun/nio/cs/StreamEncoder +sun/security/action/GetPropertyAction +sun/nio/cs/UTF_8$Encoder +sun/nio/cs/ArrayEncoder +java/nio/charset/CharsetEncoder +java/nio/ByteBuffer +java/nio/HeapByteBuffer +java/nio/Bits +java/nio/ByteOrder +java/nio/Bits$1 +sun/misc/JavaNioAccess +java/io/BufferedWriter +java/io/DefaultFileSystem +java/io/UnixFileSystem +java/io/FileSystem +java/io/ExpiringCache +java/io/ExpiringCache$1 +java/util/LinkedHashMap +java/io/File$PathStatus +java/lang/Enum +java/nio/file/Path +java/nio/file/Watchable +java/lang/StringCoding$StringEncoder +java/lang/ClassLoader$3 +java/io/ExpiringCache$Entry +java/util/LinkedHashMap$Entry +java/lang/ClassLoader$NativeLibrary +java/lang/Terminator +java/lang/Terminator$1 +sun/misc/SignalHandler +sun/misc/Signal +sun/misc/NativeSignalHandler +java/lang/Integer$IntegerCache +sun/misc/OSEnvironment +java/lang/System$2 +sun/misc/JavaLangAccess +java/lang/IllegalArgumentException +java/lang/Compiler +java/lang/Compiler$1 +sun/misc/Launcher$Factory +java/net/URLStreamHandlerFactory +sun/security/util/Debug +java/lang/ClassLoader$ParallelLoaders +java/util/WeakHashMap$Entry +java/util/Collections$SetFromMap +java/util/WeakHashMap$KeySet +java/net/URLClassLoader$7 +sun/misc/JavaNetAccess +java/util/StringTokenizer +sun/misc/Launcher$ExtClassLoader$1 +sun/misc/MetaIndex +java/io/BufferedReader +java/io/Reader +java/lang/Readable +java/io/FileReader +java/io/InputStreamReader +sun/nio/cs/StreamDecoder +java/nio/CharBuffer +java/nio/HeapCharBuffer +java/nio/charset/CoderResult +java/nio/charset/CoderResult$1 +java/nio/charset/CoderResult$Cache +java/nio/charset/CoderResult$2 +java/lang/reflect/Array +java/io/FileInputStream$1 +sun/net/www/ParseUtil +java/util/BitSet +java/util/Locale +java/util/Locale$Cache +sun/util/locale/LocaleObjectCache +java/util/concurrent/ConcurrentHashMap +java/util/concurrent/ConcurrentMap +java/util/concurrent/ConcurrentHashMap$Segment +java/util/concurrent/locks/ReentrantLock +java/util/concurrent/locks/Lock +java/util/concurrent/ConcurrentHashMap$Node +java/util/concurrent/ConcurrentHashMap$CounterCell +java/util/concurrent/ConcurrentHashMap$KeySetView +java/util/concurrent/ConcurrentHashMap$CollectionView +java/util/concurrent/ConcurrentHashMap$ValuesView +java/util/concurrent/ConcurrentHashMap$EntrySetView +sun/util/locale/BaseLocale +sun/util/locale/BaseLocale$Cache +sun/util/locale/BaseLocale$Key +sun/util/locale/LocaleObjectCache$CacheEntry +java/util/Locale$LocaleKey +sun/util/locale/LocaleUtils +java/lang/CharacterData +java/lang/CharacterDataLatin1 +java/net/Parts +sun/net/www/protocol/file/Handler +java/net/URLStreamHandler +java/util/HashMap$TreeNode +java/security/ProtectionDomain$JavaSecurityAccessImpl +sun/misc/JavaSecurityAccess +java/security/ProtectionDomain$2 +sun/misc/JavaSecurityProtectionDomainAccess +java/security/ProtectionDomain$Key +java/security/Principal +java/util/HashSet +sun/misc/URLClassPath +sun/net/www/protocol/jar/Handler +sun/misc/Launcher$AppClassLoader$1 +java/lang/SystemClassLoaderAction +java/lang/invoke/MethodHandleImpl +java/lang/invoke/MethodHandleImpl$1 +java/lang/invoke/MethodHandleImpl$2 +java/util/function/Function +java/lang/invoke/MethodHandleImpl$3 +java/lang/invoke/MethodHandleImpl$4 +java/lang/ClassValue +java/lang/ClassValue$Entry +java/lang/ClassValue$Identity +java/lang/ClassValue$Version +java/lang/invoke/MemberName$Factory +java/lang/invoke/MethodHandleStatics +java/lang/invoke/MethodHandleStatics$1 +sun/misc/PostVMInitHook +sun/usagetracker/UsageTrackerClient +java/util/concurrent/atomic/AtomicBoolean +sun/usagetracker/UsageTrackerClient$1 +sun/usagetracker/UsageTrackerClient$4 +sun/usagetracker/UsageTrackerClient$3 +java/io/FileOutputStream$1 +sun/launcher/LauncherHelper +java/net/URLClassLoader$1 +sun/net/util/URLUtil +sun/misc/URLClassPath$3 +sun/misc/URLClassPath$JarLoader +sun/misc/URLClassPath$Loader +java/util/zip/ZipFile +java/util/zip/ZipConstants +java/util/zip/ZipFile$1 +sun/misc/JavaUtilZipFileAccess +sun/misc/URLClassPath$JarLoader$1 +sun/misc/FileURLMapper +java/util/jar/JarFile +java/util/jar/JavaUtilJarAccessImpl +sun/misc/JavaUtilJarAccess +java/nio/charset/StandardCharsets +sun/nio/cs/US_ASCII +sun/nio/cs/ISO_8859_1 +sun/nio/cs/UTF_16BE +sun/nio/cs/UTF_16LE +sun/nio/cs/UTF_16 +java/util/ArrayDeque +java/util/Deque +java/util/Queue +java/util/zip/ZipCoder +sun/misc/PerfCounter +sun/misc/Perf$GetPerfAction +sun/misc/Perf +sun/misc/PerfCounter$CoreCounters +sun/nio/ch/DirectBuffer +java/nio/DirectByteBuffer +java/nio/MappedByteBuffer +java/nio/DirectLongBufferU +java/nio/LongBuffer +sun/misc/JarIndex +sun/misc/ExtensionDependency +java/util/zip/ZipEntry +java/util/jar/JarFile$JarFileEntry +java/util/jar/JarEntry +java/util/zip/ZipFile$ZipFileInputStream +java/util/zip/Inflater +java/util/zip/ZStreamRef +java/util/zip/ZipFile$ZipFileInflaterInputStream +java/util/zip/InflaterInputStream +sun/misc/IOUtils +sun/misc/URLClassPath$JarLoader$2 +sun/misc/Resource +sun/nio/ByteBuffered +java/security/Permissions +java/security/PermissionCollection +sun/net/www/protocol/file/FileURLConnection +sun/net/www/URLConnection +java/net/URLConnection +sun/net/www/MessageHeader +java/io/FilePermission +java/io/FilePermission$1 +java/io/FilePermissionCollection +java/security/AllPermission +java/security/UnresolvedPermission +java/security/BasicPermissionCollection +javax/swing/JLabel +javax/swing/SwingConstants +javax/accessibility/Accessible +javax/swing/JComponent +javax/swing/TransferHandler$HasGetTransferHandler +java/awt/Container +java/awt/Component +java/awt/image/ImageObserver +java/awt/MenuContainer +sun/launcher/LauncherHelper$FXHelper +java/lang/Class$MethodArray +java/lang/InterruptedException +javax/swing/JFrame +javax/swing/WindowConstants +javax/swing/RootPaneContainer +java/awt/Frame +java/awt/Window +java/util/concurrent/ConcurrentHashMap$ForwardingNode +java/awt/Graphics +java/lang/Void +sun/util/logging/PlatformLogger +sun/util/logging/PlatformLogger$Level +sun/util/logging/PlatformLogger$1 +sun/util/logging/PlatformLogger$DefaultLoggerProxy +sun/util/logging/PlatformLogger$LoggerProxy +sun/util/logging/PlatformLogger$JavaLoggerProxy +sun/util/logging/LoggingSupport +sun/util/logging/LoggingSupport$1 +java/util/logging/LoggingProxyImpl +sun/util/logging/LoggingProxy +sun/reflect/UnsafeFieldAccessorFactory +sun/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl +sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl +java/util/HashMap$EntrySet +java/util/HashMap$EntryIterator +java/util/HashMap$HashIterator +sun/util/logging/LoggingSupport$2 +java/util/Date +sun/util/calendar/CalendarSystem +sun/util/calendar/Gregorian +sun/util/calendar/BaseCalendar +sun/util/calendar/AbstractCalendar +java/awt/Component$AWTTreeLock +java/awt/Toolkit +java/awt/Toolkit$4 +sun/awt/AWTAccessor$ToolkitAccessor +sun/awt/AWTAccessor +java/awt/Toolkit$5 +sun/util/CoreResourceBundleControl +java/util/ResourceBundle$Control +java/util/Arrays$ArrayList +java/util/ResourceBundle$Control$CandidateListCache +java/util/ResourceBundle +java/util/ResourceBundle$1 +java/util/spi/ResourceBundleControlProvider +java/util/ServiceLoader +java/util/ServiceLoader$LazyIterator +java/util/ServiceLoader$1 +java/util/LinkedHashMap$LinkedEntrySet +java/util/LinkedHashMap$LinkedEntryIterator +java/util/LinkedHashMap$LinkedHashIterator +sun/misc/Launcher$BootClassPathHolder +sun/misc/Launcher$BootClassPathHolder$1 +sun/misc/URLClassPath$2 +java/lang/ClassLoader$2 +sun/misc/URLClassPath$1 +java/net/URLClassLoader$3 +sun/misc/CompoundEnumeration +java/io/FileNotFoundException +java/io/IOException +java/security/PrivilegedActionException +java/net/URLClassLoader$3$1 +java/util/ResourceBundle$RBClassLoader +java/util/ResourceBundle$RBClassLoader$1 +java/util/ResourceBundle$CacheKey +java/util/ResourceBundle$LoaderReference +java/util/ResourceBundle$CacheKeyReference +java/util/ResourceBundle$SingleFormatControl +java/util/LinkedList +java/util/AbstractSequentialList +java/util/LinkedList$Node +sun/awt/resources/awt +java/util/ListResourceBundle +java/awt/Toolkit$3 +java/awt/GraphicsEnvironment +java/lang/invoke/LambdaMetafactory +java/lang/invoke/MethodHandles$Lookup +java/lang/invoke/MethodType$ConcurrentWeakInternSet +java/lang/invoke/MethodTypeForm +java/lang/invoke/Invokers +java/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry +java/lang/invoke/MethodHandles +sun/invoke/util/Wrapper +sun/invoke/util/Wrapper$Format +java/lang/Byte$ByteCache +java/lang/Short$ShortCache +java/lang/Character$CharacterCache +java/lang/Long$LongCache +sun/invoke/util/VerifyAccess +sun/invoke/util/ValueConversions +java/lang/NoSuchMethodException +java/lang/invoke/LambdaForm$BasicType +java/lang/invoke/LambdaForm$Name +java/lang/invoke/LambdaForm$NamedFunction +java/lang/invoke/SimpleMethodHandle +java/lang/invoke/BoundMethodHandle +java/lang/invoke/BoundMethodHandle$SpeciesData +java/lang/invoke/BoundMethodHandle$Factory +java/lang/invoke/BoundMethodHandle$Species_L +java/util/HashMap$Values +java/util/HashMap$ValueIterator +sun/invoke/util/BytecodeDescriptor +java/lang/invoke/DirectMethodHandle$Lazy +java/lang/InstantiationException +java/util/Collections$UnmodifiableCollection$1 +java/util/AbstractList$Itr +java/lang/invoke/InvokerBytecodeGenerator +jdk/internal/org/objectweb/asm/ClassWriter +jdk/internal/org/objectweb/asm/ClassVisitor +jdk/internal/org/objectweb/asm/ByteVector +jdk/internal/org/objectweb/asm/Item +jdk/internal/org/objectweb/asm/MethodWriter +jdk/internal/org/objectweb/asm/MethodVisitor +jdk/internal/org/objectweb/asm/Type +jdk/internal/org/objectweb/asm/Label +jdk/internal/org/objectweb/asm/Frame +jdk/internal/org/objectweb/asm/AnnotationWriter +jdk/internal/org/objectweb/asm/AnnotationVisitor +java/lang/invoke/MethodHandleImpl$Intrinsic +java/lang/invoke/InvokerBytecodeGenerator$2 +sun/invoke/util/VerifyType +sun/invoke/empty/Empty +java/lang/NoSuchFieldException +java/lang/invoke/InvokerBytecodeGenerator$CpPatch +java/lang/invoke/DirectMethodHandle$Accessor +java/util/ArrayList$Itr +java/util/RandomAccessSubList +java/util/SubList +java/util/SubList$1 +java/util/ListIterator +java/util/AbstractList$ListItr +java/lang/invoke/MethodHandleImpl$AsVarargsCollector +java/lang/invoke/DelegatingMethodHandle +java/lang/invoke/WrongMethodTypeException +java/lang/invoke/MethodHandleImpl$Lazy +java/lang/invoke/MethodHandleImpl$IntrinsicMethodHandle +java/lang/NoSuchFieldError +java/lang/IllegalAccessException +java/lang/invoke/LambdaFormEditor +java/lang/invoke/LambdaFormEditor$Transform$Kind +java/lang/invoke/LambdaFormEditor$Transform +java/lang/invoke/LambdaFormBuffer +jdk/internal/org/objectweb/asm/FieldWriter +jdk/internal/org/objectweb/asm/FieldVisitor +java/lang/invoke/InnerClassLambdaMetafactory +java/lang/invoke/AbstractValidatingLambdaMetafactory +java/util/PropertyPermission +java/security/AccessController$1 +sun/security/util/SecurityConstants +java/net/NetPermission +java/security/SecurityPermission +java/net/SocketPermission +sun/security/action/GetBooleanAction +java/security/AllPermissionCollection +java/lang/invoke/InfoFromMemberName +java/lang/invoke/MethodHandleInfo +java/lang/invoke/InnerClassLambdaMetafactory$ForwardingMethodGenerator +java/lang/invoke/TypeConvertingMethodAdapter +java/lang/invoke/InnerClassLambdaMetafactory$1 +java/lang/ProcessEnvironment +java/lang/ProcessEnvironment$Variable +java/lang/ProcessEnvironment$ExternalData +java/lang/ProcessEnvironment$Value +java/lang/ProcessEnvironment$StringEnvironment +java/util/Collections$UnmodifiableMap +java/awt/Toolkit$1 +java/awt/event/KeyEvent +java/awt/event/InputEvent +java/awt/event/ComponentEvent +java/awt/AWTEvent +java/util/EventObject +java/awt/AWTEvent$1 +sun/awt/AWTAccessor$AWTEventAccessor +java/awt/event/NativeLibLoader +java/awt/event/NativeLibLoader$1 +java/awt/event/InputEvent$1 +sun/awt/AWTAccessor$InputEventAccessor +java/awt/event/KeyEvent$1 +sun/awt/AWTAccessor$KeyEventAccessor +java/awt/Component$1 +sun/awt/AWTAccessor$ComponentAccessor +java/awt/Component$DummyRequestFocusController +sun/awt/RequestFocusController +java/awt/LayoutManager +java/awt/LightweightDispatcher +java/awt/event/AWTEventListener +java/util/EventListener +java/awt/Dimension +java/awt/geom/Dimension2D +java/awt/Container$1 +sun/awt/AWTAccessor$ContainerAccessor +javax/swing/JComponent$1 +java/awt/ComponentOrientation +java/awt/Component$3 +sun/awt/AppContext +java/util/IdentityHashMap +java/util/Collections$SynchronizedMap +sun/awt/AppContext$GetAppContextLock +sun/awt/AppContext$6 +sun/misc/JavaAWTAccess +sun/awt/AppContext$3 +sun/awt/AppContext$2 +sun/awt/SunToolkit +sun/awt/WindowClosingSupport +sun/awt/WindowClosingListener +sun/awt/ComponentFactory +sun/awt/InputMethodSupport +sun/awt/KeyboardFocusManagerPeerProvider +java/util/concurrent/locks/ReentrantLock$NonfairSync +java/util/concurrent/locks/ReentrantLock$Sync +java/util/concurrent/locks/AbstractQueuedSynchronizer +java/util/concurrent/locks/AbstractOwnableSynchronizer +java/util/concurrent/locks/AbstractQueuedSynchronizer$Node +java/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject +java/util/concurrent/locks/Condition +sun/misc/SoftCache +sun/awt/AppContext$State +sun/awt/AppContext$1 +java/awt/EventQueue +java/awt/EventQueue$1 +java/awt/EventQueue$2 +sun/awt/AWTAccessor$EventQueueAccessor +java/awt/Queue +sun/awt/MostRecentKeyValue +sun/awt/PostEventQueue +javax/swing/event/EventListenerList +javax/swing/SwingUtilities +javax/swing/RepaintManager +javax/swing/RepaintManager$DisplayChangedHandler +sun/awt/DisplayChangedListener +javax/swing/RepaintManager$1 +sun/swing/SwingAccessor$RepaintManagerAccessor +sun/swing/SwingAccessor +sun/awt/X11GraphicsEnvironment +sun/java2d/SunGraphicsEnvironment +sun/awt/X11GraphicsEnvironment$1 +sun/awt/X11/XErrorHandlerUtil +sun/awt/X11/XlibWrapper +sun/java2d/UnixSurfaceManagerFactory +sun/java2d/SurfaceManagerFactory +sun/awt/SunDisplayChanger +sun/java2d/SunGraphicsEnvironment$1 +sun/misc/FloatingDecimal +sun/misc/FloatingDecimal$ExceptionalBinaryToASCIIBuffer +sun/misc/FloatingDecimal$BinaryToASCIIConverter +sun/misc/FloatingDecimal$BinaryToASCIIBuffer +sun/misc/FloatingDecimal$1 +sun/misc/FloatingDecimal$PreparedASCIIToBinaryBuffer +sun/misc/FloatingDecimal$ASCIIToBinaryConverter +java/lang/NumberFormatException +java/lang/StringIndexOutOfBoundsException +java/lang/IndexOutOfBoundsException +java/awt/Font +java/awt/Font$FontAccessImpl +sun/font/FontAccess +java/awt/geom/AffineTransform +sun/font/AttributeValues +sun/font/EAttribute +java/text/AttributedCharacterIterator$Attribute +java/lang/Class$4 +sun/reflect/NativeMethodAccessorImpl +sun/reflect/DelegatingMethodAccessorImpl +java/awt/font/TextAttribute +java/awt/Toolkit$2 +sun/awt/X11/XToolkit +sun/awt/UNIXToolkit +java/util/TreeMap +java/util/NavigableMap +java/util/SortedMap +java/awt/GraphicsDevice +sun/awt/X11GraphicsDevice +sun/awt/X11GraphicsConfig +sun/awt/image/SurfaceManager$ProxiedGraphicsConfig +java/awt/GraphicsConfiguration +java/awt/ImageCapabilities +sun/java2d/x11/X11SurfaceData +sun/java2d/x11/XSurfaceData +sun/java2d/SurfaceData +java/awt/Transparency +sun/java2d/DisposerTarget +sun/java2d/StateTrackable +sun/java2d/Surface +sun/java2d/InvalidPipeException +java/lang/IllegalStateException +sun/java2d/NullSurfaceData +sun/java2d/StateTrackable$State +sun/java2d/loops/SurfaceType +sun/awt/image/PixelConverter +sun/awt/image/PixelConverter$Xrgb +sun/awt/image/PixelConverter$Argb +sun/awt/image/PixelConverter$ArgbPre +sun/awt/image/PixelConverter$Xbgr +sun/awt/image/PixelConverter$Rgba +sun/awt/image/PixelConverter$RgbaPre +sun/awt/image/PixelConverter$Ushort565Rgb +sun/awt/image/PixelConverter$Ushort555Rgb +sun/awt/image/PixelConverter$Ushort555Rgbx +sun/awt/image/PixelConverter$Ushort4444Argb +sun/awt/image/PixelConverter$ByteGray +sun/awt/image/PixelConverter$UshortGray +sun/awt/image/PixelConverter$Rgbx +sun/awt/image/PixelConverter$Bgrx +sun/awt/image/PixelConverter$ArgbBm +java/awt/image/ColorModel +java/awt/image/ColorModel$1 +java/awt/image/DirectColorModel +java/awt/image/PackedColorModel +java/awt/color/ColorSpace +java/awt/color/ICC_Profile +sun/java2d/cmm/ProfileDeferralInfo +sun/java2d/cmm/ProfileDeferralMgr +java/awt/color/ICC_ProfileRGB +java/awt/color/ICC_Profile$1 +sun/java2d/cmm/ProfileActivator +java/awt/color/ICC_ColorSpace +sun/java2d/StateTrackableDelegate +sun/java2d/StateTrackableDelegate$2 +sun/java2d/pipe/NullPipe +sun/java2d/pipe/PixelDrawPipe +sun/java2d/pipe/PixelFillPipe +sun/java2d/pipe/ShapeDrawPipe +sun/java2d/pipe/TextPipe +sun/java2d/pipe/DrawImagePipe +java/awt/image/IndexColorModel +sun/java2d/pipe/LoopPipe +sun/java2d/pipe/ParallelogramPipe +sun/java2d/pipe/LoopBasedPipe +sun/java2d/pipe/RenderingEngine +sun/java2d/pipe/RenderingEngine$1 +sun/dc/DuctusRenderingEngine +sun/java2d/pipe/OutlineTextRenderer +sun/java2d/pipe/SolidTextRenderer +sun/java2d/pipe/GlyphListLoopPipe +sun/java2d/pipe/GlyphListPipe +sun/java2d/pipe/AATextRenderer +sun/java2d/pipe/LCDTextRenderer +sun/java2d/pipe/AlphaColorPipe +sun/java2d/pipe/CompositePipe +sun/java2d/SurfaceData$PixelToShapeLoopConverter +sun/java2d/pipe/PixelToShapeConverter +sun/java2d/SurfaceData$PixelToPgramLoopConverter +sun/java2d/pipe/PixelToParallelogramConverter +sun/java2d/pipe/TextRenderer +sun/java2d/pipe/SpanClipRenderer +sun/java2d/pipe/Region +sun/java2d/pipe/RegionIterator +sun/java2d/pipe/Region$ImmutableRegion +sun/java2d/pipe/AAShapePipe +sun/java2d/pipe/AlphaPaintPipe +sun/java2d/pipe/SpanShapeRenderer$Composite +sun/java2d/pipe/SpanShapeRenderer +sun/java2d/pipe/GeneralCompositePipe +sun/java2d/pipe/DrawImage +sun/java2d/loops/RenderCache +sun/java2d/loops/RenderCache$Entry +sun/java2d/loops/XORComposite +java/awt/Composite +sun/font/X11TextRenderer +sun/java2d/loops/GraphicsPrimitive +sun/java2d/x11/X11PMBlitLoops +sun/java2d/loops/Blit +sun/java2d/loops/GraphicsPrimitiveMgr +sun/java2d/loops/CompositeType +sun/java2d/SunGraphics2D +sun/awt/ConstrainableGraphics +sun/java2d/DestSurfaceProvider +java/awt/Graphics2D +java/awt/Color +java/awt/Paint +java/awt/AlphaComposite +java/awt/geom/Path2D +java/awt/Shape +java/awt/geom/Path2D$Float +sun/awt/SunHints +sun/java2d/loops/BlitBg +sun/java2d/loops/ScaledBlit +sun/java2d/loops/FillRect +sun/java2d/loops/FillSpans +sun/java2d/loops/FillParallelogram +sun/java2d/loops/DrawParallelogram +sun/java2d/loops/DrawLine +sun/java2d/loops/DrawRect +sun/java2d/loops/DrawPolygons +sun/java2d/loops/DrawPath +sun/java2d/loops/FillPath +sun/java2d/loops/MaskBlit +sun/java2d/loops/MaskFill +sun/java2d/loops/DrawGlyphList +sun/java2d/loops/DrawGlyphListAA +sun/java2d/loops/DrawGlyphListLCD +sun/java2d/loops/TransformHelper +java/awt/BasicStroke +java/awt/Stroke +sun/misc/PerformanceLogger +sun/misc/PerformanceLogger$TimeData +sun/java2d/pipe/ValidatePipe +sun/awt/SunHints$Key +java/awt/RenderingHints$Key +sun/awt/SunHints$Value +sun/awt/SunHints$LCDContrastKey +sun/java2d/loops/CustomComponent +sun/java2d/loops/GraphicsPrimitiveProxy +sun/java2d/loops/GeneralRenderer +sun/java2d/loops/GraphicsPrimitiveMgr$1 +sun/java2d/loops/GraphicsPrimitiveMgr$2 +sun/java2d/x11/X11PMBlitLoops$DelegateBlitLoop +sun/java2d/x11/X11PMBlitBgLoops +sun/java2d/x11/X11SurfaceData$LazyPipe +sun/awt/X11GraphicsConfig$X11GCDisposerRecord +sun/java2d/DisposerRecord +sun/java2d/Disposer +sun/java2d/Disposer$1 +sun/misc/ThreadGroupUtils +sun/awt/X11/XModifierKeymap +sun/awt/X11/XWrapperBase +sun/awt/X11/Native +sun/awt/X11/Native$1 +sun/awt/X11/XToolkit$3 +sun/awt/X11/XToolkit$4 +sun/awt/X11/XEventDispatcher +java/awt/Toolkit$DesktopPropertyChangeSupport +java/beans/PropertyChangeSupport +java/beans/PropertyChangeSupport$PropertyChangeListenerMap +java/beans/ChangeListenerMap +java/beans/PropertyChangeListener +sun/awt/SunToolkit$ModalityListenerList +sun/awt/ModalityListener +sun/font/FontConfigManager +sun/font/FontConfigManager$FontConfigInfo +sun/awt/X11/AwtScreenData +sun/awt/X11/XToolkit$1 +java/lang/invoke/DirectMethodHandle$Special +sun/awt/X11/XToolkit$2 +java/lang/ApplicationShutdownHooks +java/lang/ApplicationShutdownHooks$1 +java/lang/Shutdown +java/lang/Shutdown$Lock +sun/awt/X11/XWM +sun/awt/X11/XAtom +java/awt/Insets +sun/awt/X11/XWM$1 +sun/awt/X11/XErrorHandler$XBaseErrorHandler +sun/awt/X11/XErrorHandler +sun/awt/X11/XSetWindowAttributes +sun/awt/X11/XNETProtocol +sun/awt/X11/XStateProtocol +sun/awt/X11/XLayerProtocol +sun/awt/X11/XProtocol +sun/awt/X11/WindowPropertyGetter +sun/awt/X11/UnsafeXDisposerRecord +sun/awt/X11/XPropertyCache +sun/awt/X11/XWINProtocol +sun/awt/X11/XAtomList +java/awt/Rectangle +java/awt/geom/Rectangle2D +sun/awt/X11/XEvent +java/awt/geom/RectangularShape +javax/swing/RepaintManager$ProcessingRunnable +com/sun/java/swing/SwingUtilities3 +javax/swing/UIManager +javax/swing/UIManager$LookAndFeelInfo +sun/awt/OSInfo +sun/awt/OSInfo$WindowsVersion +sun/awt/OSInfo$1 +sun/awt/OSInfo$OSType +sun/swing/SwingUtilities2 +java/awt/font/FontRenderContext +java/awt/RenderingHints +sun/swing/StringUIClientPropertyKey +sun/swing/UIClientPropertyKey +sun/swing/SwingUtilities2$LSBCacheEntry +javax/swing/UIManager$LAFState +javax/swing/UIDefaults +javax/swing/MultiUIDefaults +javax/swing/UIManager$1 +javax/swing/plaf/metal/MetalLookAndFeel +javax/swing/plaf/basic/BasicLookAndFeel +javax/swing/LookAndFeel +sun/swing/DefaultLookup +javax/swing/plaf/metal/OceanTheme +javax/swing/plaf/metal/DefaultMetalTheme +javax/swing/plaf/metal/MetalTheme +javax/swing/plaf/ColorUIResource +javax/swing/plaf/UIResource +sun/swing/PrintColorUIResource +javax/swing/plaf/metal/DefaultMetalTheme$FontDelegate +javax/swing/plaf/FontUIResource +sun/swing/SwingLazyValue +javax/swing/UIDefaults$LazyValue +javax/swing/UIDefaults$ActiveValue +javax/swing/plaf/InsetsUIResource +javax/swing/plaf/BorderUIResource$EmptyBorderUIResource +javax/swing/border/EmptyBorder +javax/swing/border/AbstractBorder +javax/swing/border/Border +sun/swing/SwingUtilities2$2 +javax/swing/plaf/basic/BasicLookAndFeel$2 +javax/swing/plaf/DimensionUIResource +javax/swing/UIDefaults$LazyInputMap +javax/swing/plaf/metal/MetalLookAndFeel$FontActiveValue +sun/swing/SwingUtilities2$AATextInfo +sun/awt/HeadlessToolkit +sun/awt/X11/XAWTXSettings +sun/awt/X11/XMSelectionListener +sun/awt/XSettings +sun/awt/X11/XMSelection +sun/awt/X11/XMSelection$1 +sun/awt/X11/XMSelection$3 +sun/awt/X11/XErrorHandler$IgnoreBadWindowHandler +sun/awt/XSettings$Update +java/beans/PropertyChangeEvent +java/awt/Toolkit$DesktopPropertyChangeSupport$1 +java/util/IdentityHashMap$Values +java/util/IdentityHashMap$ValueIterator +java/util/IdentityHashMap$IdentityHashMapIterator +java/util/HashMap$KeySet +java/util/HashMap$KeyIterator +javax/swing/plaf/metal/MetalLookAndFeel$AATextListener +java/beans/PropertyChangeListenerProxy +java/util/EventListenerProxy +javax/swing/plaf/metal/OceanTheme$1 +javax/swing/plaf/metal/OceanTheme$2 +javax/swing/plaf/metal/OceanTheme$3 +javax/swing/plaf/metal/OceanTheme$4 +javax/swing/plaf/metal/OceanTheme$5 +javax/swing/plaf/metal/OceanTheme$6 +javax/swing/SwingPaintEventDispatcher +sun/awt/PaintEventDispatcher +java/awt/KeyboardFocusManager +java/awt/KeyEventDispatcher +java/awt/KeyEventPostProcessor +java/awt/KeyboardFocusManager$1 +sun/awt/AWTAccessor$KeyboardFocusManagerAccessor +java/awt/AWTKeyStroke +java/awt/AWTKeyStroke$1 +java/awt/DefaultKeyboardFocusManager +java/awt/DefaultKeyboardFocusManager$1 +sun/awt/AWTAccessor$DefaultKeyboardFocusManagerAccessor +java/awt/DefaultFocusTraversalPolicy +java/awt/ContainerOrderFocusTraversalPolicy +java/awt/FocusTraversalPolicy +java/util/Collections$UnmodifiableSet +sun/awt/X11/XKeyboardFocusManagerPeer +sun/awt/KeyboardFocusManagerPeerImpl +java/awt/peer/KeyboardFocusManagerPeer +javax/swing/UIManager$2 +javax/swing/JRootPane +javax/swing/UIDefaults$TextAndMnemonicHashMap +com/sun/swing/internal/plaf/metal/resources/metal +sun/util/ResourceBundleEnumeration +com/sun/swing/internal/plaf/basic/resources/basic +javax/swing/plaf/metal/MetalLabelUI +javax/swing/plaf/basic/BasicLabelUI +javax/swing/plaf/LabelUI +javax/swing/plaf/ComponentUI +sun/reflect/misc/MethodUtil +sun/reflect/misc/MethodUtil$1 +sun/net/www/protocol/jar/JarURLConnection +java/net/JarURLConnection +sun/net/www/protocol/jar/JarFileFactory +sun/net/www/protocol/jar/URLJarFile$URLJarFileCloseController +java/net/HttpURLConnection +sun/net/www/protocol/jar/URLJarFile +sun/net/www/protocol/jar/URLJarFile$URLJarFileEntry +sun/net/www/protocol/jar/JarURLConnection$JarURLInputStream +java/lang/UnsupportedOperationException +java/lang/reflect/InvocationTargetException +javax/swing/plaf/metal/DefaultMetalTheme$FontDelegate$1 +javax/swing/plaf/basic/BasicHTML +sun/awt/util/IdentityArrayList +java/awt/Window$1 +sun/awt/AWTAccessor$WindowAccessor +java/awt/Frame$1 +sun/awt/AWTAccessor$FrameAccessor +java/awt/Window$Type +java/awt/Cursor +java/awt/Cursor$1 +sun/awt/AWTAccessor$CursorAccessor +java/awt/BorderLayout +java/awt/LayoutManager2 +sun/awt/X11/XlibUtil +java/util/concurrent/locks/LockSupport +sun/nio/ch/Interruptible +java/awt/Dialog$ModalExclusionType +java/awt/Window$WindowDisposerRecord +javax/swing/JPanel +java/awt/FlowLayout +javax/swing/plaf/basic/BasicPanelUI +javax/swing/plaf/PanelUI +java/awt/Component$BaselineResizeBehavior +sun/swing/SwingLazyValue$1 +javax/swing/JLayeredPane +javax/swing/JRootPane$1 +javax/swing/ArrayTable +javax/swing/JRootPane$RootLayout +javax/swing/BufferStrategyPaintManager +javax/swing/RepaintManager$PaintManager +javax/swing/FocusManager +javax/swing/LayoutFocusTraversalPolicy +javax/swing/SortingFocusTraversalPolicy +javax/swing/InternalFrameFocusTraversalPolicy +javax/swing/SwingContainerOrderFocusTraversalPolicy +javax/swing/SortingFocusTraversalPolicy$1 +java/util/Spliterator$OfLong +java/util/Spliterator$OfPrimitive +java/util/Spliterator +java/util/Spliterator$OfInt +java/util/Spliterator$OfDouble +java/util/stream/IntStream +java/util/stream/BaseStream +java/util/stream/Stream +java/util/stream/DoubleStream +java/util/stream/LongStream +java/util/function/DoubleBinaryOperator +java/util/function/IntBinaryOperator +java/util/function/LongBinaryOperator +java/util/function/BinaryOperator +java/util/function/BiFunction +java/util/function/IntToDoubleFunction +java/util/function/IntFunction +java/util/function/IntToLongFunction +java/util/function/IntUnaryOperator +javax/swing/SwingDefaultFocusTraversalPolicy +javax/swing/LayoutComparator +javax/swing/plaf/metal/MetalRootPaneUI +javax/swing/plaf/basic/BasicRootPaneUI +javax/swing/plaf/RootPaneUI +javax/swing/plaf/basic/BasicRootPaneUI$RootPaneInputMap +javax/swing/plaf/ComponentInputMapUIResource +javax/swing/ComponentInputMap +javax/swing/InputMap +javax/swing/plaf/InputMapUIResource +javax/swing/KeyStroke +java/awt/VKCollection +sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl +javax/swing/plaf/basic/LazyActionMap +javax/swing/plaf/ActionMapUIResource +javax/swing/ActionMap +sun/awt/X11/XFramePeer +java/awt/peer/FramePeer +java/awt/peer/WindowPeer +java/awt/peer/ContainerPeer +java/awt/peer/ComponentPeer +sun/awt/X11/XDecoratedPeer +sun/awt/X11/XWindowPeer +sun/awt/X11/XPanelPeer +java/awt/peer/PanelPeer +sun/awt/X11/XCanvasPeer +java/awt/peer/CanvasPeer +sun/awt/X11/XComponentPeer +java/awt/dnd/peer/DropTargetPeer +sun/java2d/BackBufferCapsProvider +sun/awt/X11/XWindow +sun/awt/X11ComponentPeer +sun/awt/X11/XBaseWindow +sun/awt/X11/XCreateWindowParams +sun/awt/X11/XBaseWindow$InitialiseState +sun/awt/X11/XBaseWindow$StateLock +sun/awt/X11/AwtGraphicsConfigData +sun/awt/X11/XVisualInfo +java/awt/SystemColor +sun/awt/AWTAccessor$SystemColorAccessor +sun/awt/X11/MotifColorUtilities +sun/awt/X11/XRepaintArea +sun/awt/RepaintArea +sun/awt/X11/XWindowAttributesData +sun/awt/X11/WindowDimensions +java/awt/Point +java/awt/geom/Point2D +java/util/TreeMap$Entry +sun/awt/X11/XSizeHints +sun/awt/X11/XRootWindow +sun/awt/X11/XRootWindow$LazyHolder +sun/nio/cs/ISO_8859_1$Encoder +sun/nio/cs/Surrogate$Parser +sun/nio/cs/Surrogate +sun/java2d/x11/X11SurfaceData$X11WindowSurfaceData +sun/awt/X11/XDropTargetEventProcessor +sun/java2d/loops/RenderLoops +sun/awt/X11/XDragSourceContextPeer +sun/java2d/loops/GraphicsPrimitiveMgr$PrimitiveSpec +sun/awt/X11/XDragSourceProtocolListener +java/util/Arrays$LegacyMergeSort +sun/awt/dnd/SunDragSourceContextPeer +java/awt/dnd/peer/DragSourceContextPeer +java/util/TimSort +sun/awt/X11/XAnyEvent +sun/awt/X11/XAwtState +sun/awt/X11/XBaseWindow$1 +sun/awt/X11/XPropertyEvent +sun/java2d/DefaultDisposerRecord +sun/java2d/SurfaceDataProxy +sun/awt/image/SurfaceManager$FlushableCacheData +sun/java2d/SurfaceDataProxy$1 +sun/java2d/StateTracker +sun/java2d/StateTracker$1 +sun/java2d/StateTracker$2 +sun/java2d/x11/X11Renderer +sun/awt/X11/XGlobalCursorManager +sun/awt/GlobalCursorManager +sun/awt/IconInfo +sun/awt/AWTIcon32_java_icon16_png +sun/awt/AWTIcon32_java_icon24_png +sun/awt/AWTIcon32_java_icon32_png +sun/awt/AWTIcon32_java_icon48_png +sun/awt/X11/XClientMessageEvent +sun/awt/X11/XContentWindow +sun/awt/X11/XFocusProxyWindow +sun/awt/X11/XWMHints +java/util/LinkedList$ListItr +sun/awt/AWTAutoShutdown +java/awt/peer/LightweightPeer +sun/awt/NullComponentPeer +java/awt/SplashScreen +java/awt/Dialog +java/awt/Dialog$ModalityType +sun/awt/X11/PropMwmHints +sun/awt/X11/XWindowPeer$4 +sun/awt/GlobalCursorManager$NativeUpdater +java/awt/event/WindowEvent +java/awt/EventQueue$5 +java/awt/EventDispatchThread +sun/awt/PeerEvent +java/awt/EventDispatchThread$1 +java/awt/Conditional +java/awt/event/InvocationEvent +java/awt/ActiveEvent +java/awt/EventDispatchThread$HierarchyEventFilter +java/awt/EventFilter +java/awt/event/PaintEvent +sun/awt/X11/XReparentEvent +java/awt/event/MouseEvent +java/awt/ModalEventFilter +sun/awt/EventQueueItem +sun/awt/EventQueueDelegate +java/awt/EventFilter$FilterAction +java/awt/EventQueue$3 +java/awt/EventQueue$4 +sun/awt/dnd/SunDropTargetEvent +sun/awt/X11/XConfigureEvent +java/awt/event/InputMethodEvent +java/awt/event/ActionEvent +java/awt/event/FocusEvent +java/util/Vector$Itr +java/awt/event/InvocationEvent$1 +sun/awt/X11/XWindowPeer$2 +sun/awt/AWTAccessor$InvocationEventAccessor +java/applet/Applet +java/awt/Panel +sun/awt/X11/XVisibilityEvent +sun/awt/X11/XExposeEvent +sun/awt/event/IgnorePaintEvent +sun/awt/X11/XTranslateCoordinates +sun/awt/X11/XFocusChangeEvent +java/awt/SequencedEvent +java/awt/SequencedEvent$1 +sun/awt/AWTAccessor$SequencedEventAccessor +sun/awt/X11/XComponentPeer$1 +java/awt/MenuComponent +java/awt/TrayIcon +java/util/IdentityHashMap$KeySet +java/util/IdentityHashMap$KeyIterator +javax/swing/RepaintManager$4 +java/awt/GraphicsCallback$PaintCallback +java/awt/GraphicsCallback +sun/awt/SunGraphicsCallback +javax/swing/BufferStrategyPaintManager$BufferInfo +java/awt/event/WindowListener +java/awt/event/ComponentAdapter +java/awt/event/ComponentListener +java/awt/AWTEventMulticaster +java/awt/event/ContainerListener +java/awt/event/FocusListener +java/awt/event/KeyListener +java/awt/event/MouseListener +java/awt/event/MouseMotionListener +java/awt/event/WindowFocusListener +java/awt/event/WindowStateListener +java/awt/event/ActionListener +java/awt/event/ItemListener +java/awt/event/AdjustmentListener +java/awt/event/TextListener +java/awt/event/InputMethodListener +java/awt/event/HierarchyListener +java/awt/event/HierarchyBoundsListener +java/awt/event/MouseWheelListener +java/awt/BufferCapabilities +java/awt/Component$BltSubRegionBufferStrategy +sun/awt/SubRegionShowable +java/awt/Component$BltBufferStrategy +java/awt/image/BufferStrategy +java/awt/image/VolatileImage +java/awt/Image +sun/awt/image/SunVolatileImage +java/awt/Image$1 +sun/awt/image/SurfaceManager$ImageAccessor +sun/awt/image/SurfaceManager +sun/awt/image/BufferedImageGraphicsConfig +sun/print/PrinterGraphicsConfig +sun/java2d/opengl/GLXGraphicsConfig +sun/java2d/opengl/OGLGraphicsConfig +sun/java2d/pipe/hw/AccelGraphicsConfig +sun/java2d/pipe/hw/BufferedContextProvider +sun/java2d/xr/XRGraphicsConfig +sun/java2d/x11/X11VolatileSurfaceManager +sun/awt/image/VolatileSurfaceManager +sun/java2d/x11/X11SurfaceData$X11PixmapSurfaceData +sun/font/FontDesignMetrics +java/awt/FontMetrics +sun/font/SunFontManager +sun/java2d/FontSupport +sun/font/FontManagerForSGE +sun/font/FontManager +sun/font/SunFontManager$TTFilter +java/io/FilenameFilter +sun/font/SunFontManager$T1Filter +sun/font/SunFontManager$1 +sun/font/FontManagerNativeLibrary +sun/font/FontManagerNativeLibrary$1 +sun/font/FontUtilities +sun/font/FontUtilities$1 +sun/font/TrueTypeFont +sun/font/FileFont +sun/font/PhysicalFont +sun/font/Font2D +sun/font/Type1Font +java/awt/geom/Point2D$Float +sun/font/StrikeMetrics +java/awt/geom/Rectangle2D$Float +java/awt/geom/GeneralPath +sun/font/CharToGlyphMapper +sun/font/PhysicalStrike +sun/font/FontStrike +sun/font/StrikeCache +sun/font/StrikeCache$1 +sun/font/GlyphList +sun/font/FontManagerFactory +sun/font/FontManagerFactory$1 +sun/awt/X11FontManager +sun/font/CompositeFont +sun/font/SunFontManager$2 +java/io/StreamTokenizer +sun/font/SunFontManager$FontRegistrationInfo +sun/awt/motif/MFontConfiguration +sun/awt/FontConfiguration +sun/awt/FontDescriptor +java/util/Properties$LineReader +sun/font/FcFontConfiguration +java/net/InetAddress +java/net/InetAddress$1 +java/net/InetAddress$InetAddressHolder +java/net/InetAddress$Cache +java/net/InetAddress$Cache$Type +java/net/InetAddressImplFactory +java/net/Inet4AddressImpl +java/net/InetAddressImpl +java/net/InetAddress$2 +sun/net/spi/nameservice/NameService +java/net/Inet4Address +java/net/Inet6Address +java/net/Inet6Address$Inet6AddressHolder +sun/net/InetAddressCachePolicy +sun/net/InetAddressCachePolicy$1 +java/security/Security +java/security/Security$1 +sun/net/InetAddressCachePolicy$2 +java/util/LinkedHashMap$LinkedKeySet +java/util/LinkedHashMap$LinkedKeyIterator +java/net/InetAddress$CacheEntry +sun/font/FontConfigManager$FcCompFont +sun/font/FontConfigManager$FontConfigFont +sun/font/CompositeFontDescriptor +sun/font/Font2DHandle +sun/font/FontFamily +sun/font/SunFontManager$3 +sun/font/FontDesignMetrics$MetricsKey +sun/font/FontStrikeDesc +sun/font/CompositeStrike +sun/font/FontStrikeDisposer +sun/java2d/Disposer$PollDisposable +sun/font/StrikeCache$SoftDisposerRef +sun/font/StrikeCache$DisposableStrike +sun/font/TrueTypeFont$TTDisposerRecord +sun/font/TrueTypeFont$1 +java/io/RandomAccessFile +java/io/DataOutput +java/io/DataInput +sun/nio/ch/FileChannelImpl +java/nio/channels/FileChannel +java/nio/channels/SeekableByteChannel +java/nio/channels/ByteChannel +java/nio/channels/ReadableByteChannel +java/nio/channels/Channel +java/nio/channels/WritableByteChannel +java/nio/channels/GatheringByteChannel +java/nio/channels/ScatteringByteChannel +java/nio/channels/spi/AbstractInterruptibleChannel +java/nio/channels/InterruptibleChannel +java/nio/file/attribute/FileAttribute +sun/nio/ch/IOUtil +sun/nio/ch/IOUtil$1 +sun/nio/ch/NativeThreadSet +sun/nio/ch/FileDispatcherImpl +sun/nio/ch/FileDispatcher +sun/nio/ch/NativeDispatcher +java/nio/channels/spi/AbstractInterruptibleChannel$1 +sun/nio/ch/NativeThread +sun/nio/ch/IOStatus +sun/nio/ch/Util +sun/nio/ch/Util$1 +sun/nio/ch/Util$BufferCache +java/nio/DirectByteBuffer$Deallocator +java/nio/ByteBufferAsIntBufferB +java/nio/IntBuffer +sun/font/TrueTypeFont$DirectoryEntry +java/nio/ByteBufferAsShortBufferB +java/nio/ShortBuffer +sun/nio/cs/UTF_16$Decoder +sun/nio/cs/UnicodeDecoder +sun/font/FileFontStrike +sun/font/FontScaler +sun/font/T2KFontScaler +sun/font/T2KFontScaler$1 +sun/font/TrueTypeGlyphMapper +sun/font/CMap +sun/font/CMap$NullCMapClass +sun/font/CMap$CMapFormat12 +sun/font/FontDesignMetrics$KeyReference +sun/font/CompositeGlyphMapper +java/awt/print/PrinterGraphics +java/awt/PrintGraphics +sun/java2d/loops/FontInfo +sun/awt/X11/XSystemTrayPeer +java/awt/peer/SystemTrayPeer +java/util/jar/Attributes +java/util/jar/Manifest$FastInputStream +java/util/jar/Attributes$Name +sun/misc/ASCIICaseInsensitiveComparator +java/util/jar/JarVerifier +java/security/CodeSigner +java/util/jar/JarVerifier$3 +java/io/ByteArrayOutputStream +java/lang/Package +sun/security/util/SignatureFileVerifier +sun/security/util/ManifestEntryVerifier +java/util/MissingResourceException +javax/swing/JDialog +javax/swing/text/JTextComponent +javax/swing/Scrollable +javax/swing/JTextArea +javax/swing/JScrollPane +javax/swing/ScrollPaneConstants +javax/swing/AbstractButton +java/awt/ItemSelectable +javax/swing/JButton +java/io/StringWriter +java/lang/SecurityException +javax/swing/JWindow +java/io/UnsupportedEncodingException +sun/misc/URLClassPath$FileLoader +java/lang/CloneNotSupportedException +java/lang/InternalError +java/net/UnknownHostException +java/net/Socket +java/net/SocketAddress +java/nio/channels/SocketChannel +java/nio/channels/NetworkChannel +java/nio/channels/spi/AbstractSelectableChannel +java/nio/channels/SelectableChannel +java/net/SocketException +java/net/SocketImplFactory +java/net/InetSocketAddress +java/net/InetSocketAddress$InetSocketAddressHolder +java/net/Proxy +java/net/SocketImpl +java/net/SocketOptions +java/net/SocksSocketImpl +java/net/SocksConsts +java/net/PlainSocketImpl +java/net/AbstractPlainSocketImpl +java/net/AbstractPlainSocketImpl$1 +sun/net/util/IPAddressUtil +java/net/SocksSocketImpl$3 +java/net/ProxySelector +sun/net/spi/DefaultProxySelector +sun/net/spi/DefaultProxySelector$1 +sun/net/NetProperties +sun/net/NetProperties$1 +java/net/URI +java/net/URI$Parser +sun/net/spi/DefaultProxySelector$NonProxyInfo +sun/net/spi/DefaultProxySelector$3 +java/net/Proxy$Type +sun/net/NetHooks +sun/net/sdp/SdpProvider +sun/net/NetHooks$Provider +java/net/ConnectException +java/net/MalformedURLException +java/lang/UnsatisfiedLinkError +javax/swing/UnsupportedLookAndFeelException +sun/misc/FloatingDecimal$ASCIIToBinaryBuffer +sun/misc/FDBigInteger +java/util/ResourceBundle$Control$1 +java/net/URLClassLoader$2 +java/util/PropertyResourceBundle +java/util/ResourceBundle$BundleReference +java/util/logging/Level +java/util/logging/Level$KnownLevel +java/util/logging/Logger +java/util/logging/Handler +java/util/logging/Logger$LoggerBundle +java/util/concurrent/CopyOnWriteArrayList +java/util/logging/LogManager +java/util/logging/LogManager$1 +java/util/logging/LogManager$SystemLoggerContext +java/util/logging/LogManager$LoggerContext +java/util/logging/LogManager$LogNode +java/util/logging/LoggingPermission +java/util/logging/LogManager$Cleaner +java/util/logging/LogManager$2 +java/util/logging/LogManager$3 +java/util/logging/LogManager$LoggerWeakRef +java/util/logging/LogManager$LoggerContext$1 +java/util/logging/LogManager$RootLogger +java/util/logging/LogManager$5 +java/util/logging/Logger$1 +sun/util/logging/resources/logging +javax/swing/Box +javax/swing/Box$Filler +javax/swing/Icon +javax/swing/BoxLayout +javax/swing/plaf/basic/BasicPopupMenuUI +javax/swing/plaf/PopupMenuUI +javax/swing/ImageIcon +javax/swing/ImageIcon$1 +javax/swing/ImageIcon$2 +javax/swing/ImageIcon$2$1 +java/awt/dnd/DropTarget +java/awt/dnd/DropTargetListener +javax/accessibility/AccessibleContext +sun/reflect/UnsafeObjectFieldAccessorImpl +java/awt/MediaTracker +sun/misc/SoftCache$ValueCell +sun/awt/image/URLImageSource +sun/awt/image/InputStreamImageSource +java/awt/image/ImageProducer +sun/awt/image/ImageFetchable +sun/awt/image/ToolkitImage +sun/awt/image/NativeLibLoader +sun/awt/image/NativeLibLoader$1 +javax/swing/ImageIcon$3 +java/awt/ImageMediaEntry +java/awt/MediaEntry +sun/awt/image/MultiResolutionToolkitImage +sun/awt/image/MultiResolutionImage +sun/awt/image/ImageRepresentation +java/awt/image/ImageConsumer +sun/awt/image/ImageWatched +sun/awt/image/ImageWatched$Link +sun/awt/image/ImageWatched$WeakLink +sun/awt/image/ImageConsumerQueue +sun/awt/image/ImageFetcher +sun/awt/image/FetcherInfo +sun/awt/image/ImageFetcher$1 +sun/net/ProgressMonitor +sun/net/DefaultProgressMeteringPolicy +sun/net/ProgressMeteringPolicy +sun/net/www/MimeTable +java/net/FileNameMap +sun/net/www/MimeTable$1 +sun/net/www/MimeTable$DefaultInstanceHolder +sun/net/www/MimeTable$DefaultInstanceHolder$1 +sun/net/www/MimeEntry +java/net/URLConnection$1 +java/text/SimpleDateFormat +java/text/DateFormat +java/text/Format +java/text/DateFormat$Field +java/text/Format$Field +java/util/TimeZone +sun/util/calendar/ZoneInfo +sun/util/calendar/ZoneInfoFile +sun/util/calendar/ZoneInfoFile$1 +java/io/DataInputStream +sun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule +sun/util/calendar/ZoneInfoFile$Checksum +java/util/zip/CRC32 +java/util/zip/Checksum +java/util/TimeZone$1 +java/util/Calendar +sun/util/spi/CalendarProvider +java/util/spi/LocaleServiceProvider +sun/util/locale/provider/LocaleProviderAdapter +sun/util/locale/provider/JRELocaleProviderAdapter +sun/util/locale/provider/ResourceBundleBasedAdapter +sun/util/locale/provider/SPILocaleProviderAdapter +sun/util/locale/provider/AuxLocaleProviderAdapter +sun/util/locale/provider/AuxLocaleProviderAdapter$NullProvider +sun/util/locale/provider/LocaleProviderAdapter$Type +sun/util/locale/provider/LocaleProviderAdapter$1 +sun/util/locale/provider/CalendarProviderImpl +sun/util/locale/provider/AvailableLanguageTags +sun/util/locale/provider/LocaleDataMetaInfo +sun/util/locale/provider/JRELocaleProviderAdapter$1 +java/util/Calendar$Builder +java/util/GregorianCalendar +sun/util/locale/provider/CalendarDataUtility +java/util/spi/CalendarDataProvider +sun/util/locale/provider/LocaleServiceProviderPool +java/text/spi/BreakIteratorProvider +java/text/spi/CollatorProvider +java/text/spi/DateFormatProvider +java/text/spi/DateFormatSymbolsProvider +java/text/spi/DecimalFormatSymbolsProvider +java/text/spi/NumberFormatProvider +java/util/spi/CurrencyNameProvider +java/util/spi/LocaleNameProvider +java/util/spi/TimeZoneNameProvider +sun/util/locale/provider/CalendarDataProviderImpl +sun/util/locale/provider/SPILocaleProviderAdapter$1 +sun/util/locale/provider/CalendarDataUtility$CalendarWeekParameterGetter +sun/util/locale/provider/LocaleServiceProviderPool$LocalizedObjectGetter +sun/util/locale/provider/LocaleResources +sun/util/resources/LocaleData +sun/util/resources/LocaleData$1 +sun/util/resources/LocaleData$LocaleDataResourceBundleControl +sun/util/locale/LanguageTag +java/util/Collections$EmptyIterator +sun/util/resources/CalendarData +sun/util/resources/LocaleNamesBundle +sun/util/resources/OpenListResourceBundle +sun/util/resources/en/CalendarData_en +sun/util/locale/provider/LocaleResources$ResourceReference +sun/util/calendar/Gregorian$Date +sun/util/calendar/BaseCalendar$Date +sun/util/calendar/CalendarDate +sun/util/calendar/CalendarUtils +java/text/DateFormatSymbols +sun/util/locale/provider/DateFormatSymbolsProviderImpl +sun/text/resources/FormatData +sun/util/resources/ParallelListResourceBundle +java/util/concurrent/atomic/AtomicMarkableReference +java/util/concurrent/atomic/AtomicMarkableReference$Pair +sun/text/resources/en/FormatData_en +sun/text/resources/en/FormatData_en_US +sun/util/resources/ParallelListResourceBundle$KeySet +java/text/NumberFormat +sun/util/locale/provider/NumberFormatProviderImpl +java/text/DecimalFormatSymbols +sun/util/locale/provider/DecimalFormatSymbolsProviderImpl +java/util/Currency +java/util/Currency$1 +sun/util/locale/provider/CurrencyNameProviderImpl +java/util/Currency$CurrencyNameGetter +sun/util/resources/CurrencyNames +sun/util/resources/en/CurrencyNames_en_US +java/text/DecimalFormat +java/text/FieldPosition +java/text/DigitList +java/math/RoundingMode +java/text/DontCareFieldPosition +java/text/DontCareFieldPosition$1 +java/text/Format$FieldDelegate +sun/awt/image/GifImageDecoder +sun/awt/image/ImageDecoder +sun/awt/image/GifFrame +java/awt/image/Raster +java/awt/image/DataBufferByte +java/awt/image/DataBuffer +java/awt/image/DataBuffer$1 +sun/awt/image/SunWritableRaster$DataStealer +sun/awt/image/SunWritableRaster +java/awt/image/WritableRaster +java/awt/image/PixelInterleavedSampleModel +java/awt/image/ComponentSampleModel +java/awt/image/SampleModel +sun/awt/image/ByteInterleavedRaster +sun/awt/image/ByteComponentRaster +java/awt/image/BufferedImage +java/awt/image/WritableRenderedImage +java/awt/image/RenderedImage +java/awt/image/BufferedImage$1 +sun/awt/image/IntegerComponentRaster +sun/awt/image/BytePackedRaster +javax/swing/plaf/BorderUIResource +javax/swing/BorderFactory +javax/swing/border/BevelBorder +javax/swing/border/EtchedBorder +javax/swing/plaf/metal/MetalIconFactory +javax/swing/plaf/metal/MetalIconFactory$TreeFolderIcon +javax/swing/plaf/metal/MetalIconFactory$FolderIcon16 +java/util/zip/ZipInputStream +java/io/PushbackInputStream +java/util/zip/ZipUtils +java/io/RandomAccessFile$1 +java/lang/Thread$State +javax/swing/SwingUtilities$SharedOwnerFrame +sun/awt/X11/XWindowAttributes +javax/swing/border/LineBorder +javax/swing/SizeRequirements +java/util/ArrayList$ListItr +javax/swing/CellRendererPane +javax/swing/RepaintManager$3 +javax/swing/JRadioButton +javax/swing/JToggleButton +java/lang/ClassFormatError +java/io/ObjectInputStream +java/io/ObjectInput +java/io/ObjectStreamConstants +sun/awt/image/BufImgSurfaceManager +sun/awt/image/BufImgSurfaceData +sun/awt/image/BufImgSurfaceData$ICMColorData +sun/java2d/x11/X11SurfaceDataProxy +sun/java2d/x11/X11SurfaceDataProxy$Bitmask +sun/java2d/StateTrackableDelegate$1 +javax/swing/JTabbedPane +java/awt/geom/RectIterator +java/awt/geom/PathIterator +javax/swing/event/ChangeListener +java/awt/Polygon +javax/swing/event/MenuListener +javax/swing/DefaultSingleSelectionModel +javax/swing/SingleSelectionModel +javax/swing/JTabbedPane$ModelListener +javax/swing/plaf/metal/MetalTabbedPaneUI +javax/swing/plaf/basic/BasicTabbedPaneUI +javax/swing/plaf/TabbedPaneUI +javax/swing/plaf/metal/MetalTabbedPaneUI$TabbedPaneLayout +javax/swing/plaf/basic/BasicTabbedPaneUI$TabbedPaneLayout +javax/swing/plaf/basic/BasicTabbedPaneUI$TabbedPaneScrollLayout +javax/swing/plaf/basic/BasicTabbedPaneUI$Handler +sun/reflect/MethodAccessorGenerator +sun/reflect/AccessorGenerator +sun/reflect/ClassFileConstants +sun/reflect/ByteVectorFactory +sun/reflect/ByteVectorImpl +sun/reflect/ByteVector +sun/reflect/ClassFileAssembler +sun/reflect/UTF8 +sun/reflect/Label +sun/reflect/Label$PatchInfo +sun/reflect/MethodAccessorGenerator$1 +sun/reflect/ClassDefiner +sun/reflect/ClassDefiner$1 +sun/reflect/BootstrapConstructorAccessorImpl +javax/swing/JTextField +javax/swing/JViewport +java/awt/CardLayout +javax/swing/text/Document +javax/swing/text/JTextComponent$1 +sun/swing/SwingAccessor$JTextComponentAccessor +javax/swing/text/JTextComponent$4 +com/sun/beans/util/Cache +com/sun/beans/util/Cache$Kind +com/sun/beans/util/Cache$Kind$1 +com/sun/beans/util/Cache$Kind$2 +com/sun/beans/util/Cache$Kind$3 +com/sun/beans/util/Cache$CacheEntry +javax/swing/Action +javax/swing/JTextField$NotifyAction +javax/swing/text/TextAction +javax/swing/AbstractAction +java/lang/ArrayIndexOutOfBoundsException +javax/swing/DropMode +javax/swing/text/JTextComponent$MutableCaretEvent +javax/swing/event/CaretEvent +javax/swing/plaf/metal/MetalTextFieldUI +javax/swing/plaf/basic/BasicTextFieldUI +javax/swing/plaf/basic/BasicTextUI +javax/swing/text/ViewFactory +javax/swing/plaf/TextUI +javax/swing/plaf/basic/BasicTextUI$BasicCursor +javax/swing/text/DefaultEditorKit +javax/swing/text/EditorKit +javax/swing/text/DefaultEditorKit$InsertContentAction +javax/swing/text/DefaultEditorKit$DeletePrevCharAction +javax/swing/text/DefaultEditorKit$DeleteNextCharAction +javax/swing/text/DefaultEditorKit$ReadOnlyAction +javax/swing/text/DefaultEditorKit$DeleteWordAction +javax/swing/text/DefaultEditorKit$WritableAction +javax/swing/text/DefaultEditorKit$CutAction +javax/swing/text/DefaultEditorKit$CopyAction +javax/swing/text/DefaultEditorKit$PasteAction +javax/swing/text/DefaultEditorKit$VerticalPageAction +javax/swing/text/DefaultEditorKit$PageAction +javax/swing/text/DefaultEditorKit$InsertBreakAction +javax/swing/text/DefaultEditorKit$BeepAction +javax/swing/text/DefaultEditorKit$NextVisualPositionAction +javax/swing/text/DefaultEditorKit$BeginWordAction +javax/swing/text/DefaultEditorKit$EndWordAction +javax/swing/text/DefaultEditorKit$PreviousWordAction +javax/swing/text/DefaultEditorKit$NextWordAction +javax/swing/text/DefaultEditorKit$BeginLineAction +javax/swing/text/DefaultEditorKit$EndLineAction +javax/swing/text/DefaultEditorKit$BeginParagraphAction +javax/swing/text/DefaultEditorKit$EndParagraphAction +javax/swing/text/DefaultEditorKit$BeginAction +javax/swing/text/DefaultEditorKit$EndAction +javax/swing/text/DefaultEditorKit$DefaultKeyTypedAction +javax/swing/text/DefaultEditorKit$InsertTabAction +javax/swing/text/DefaultEditorKit$SelectWordAction +javax/swing/text/DefaultEditorKit$SelectLineAction +javax/swing/text/DefaultEditorKit$SelectParagraphAction +javax/swing/text/DefaultEditorKit$SelectAllAction +javax/swing/text/DefaultEditorKit$UnselectAction +javax/swing/text/DefaultEditorKit$ToggleComponentOrientationAction +javax/swing/text/DefaultEditorKit$DumpModelAction +javax/swing/plaf/basic/BasicTextUI$TextTransferHandler +javax/swing/TransferHandler +javax/swing/TransferHandler$TransferAction +sun/swing/UIAction +javax/swing/text/Position$Bias +javax/swing/plaf/basic/BasicTextUI$RootView +javax/swing/text/View +javax/swing/plaf/basic/BasicTextUI$UpdateHandler +javax/swing/event/DocumentListener +javax/swing/plaf/basic/BasicTextUI$DragListener +javax/swing/plaf/basic/DragRecognitionSupport$BeforeDrag +javax/swing/event/MouseInputAdapter +javax/swing/event/MouseInputListener +java/awt/event/MouseAdapter +javax/swing/plaf/metal/MetalBorders +javax/swing/plaf/BorderUIResource$CompoundBorderUIResource +javax/swing/border/CompoundBorder +javax/swing/plaf/metal/MetalBorders$TextFieldBorder +javax/swing/plaf/metal/MetalBorders$Flush3DBorder +javax/swing/plaf/basic/BasicBorders$MarginBorder +javax/swing/plaf/basic/BasicTextUI$BasicCaret +javax/swing/text/DefaultCaret +javax/swing/text/Caret +javax/swing/text/DefaultCaret$Handler +java/awt/datatransfer/ClipboardOwner +javax/swing/Timer +javax/swing/Timer$DoPostEvent +javax/swing/plaf/basic/BasicTextUI$BasicHighlighter +javax/swing/text/DefaultHighlighter +javax/swing/text/LayeredHighlighter +javax/swing/text/Highlighter +javax/swing/text/Highlighter$Highlight +javax/swing/text/DefaultHighlighter$DefaultHighlightPainter +javax/swing/text/LayeredHighlighter$LayerPainter +javax/swing/text/Highlighter$HighlightPainter +javax/swing/text/DefaultHighlighter$SafeDamager +javax/swing/ClientPropertyKey +javax/swing/ClientPropertyKey$1 +sun/awt/AWTAccessor$ClientPropertyKeyAccessor +javax/swing/TransferHandler$SwingDropTarget +java/awt/dnd/DropTargetContext +java/awt/datatransfer/SystemFlavorMap +java/awt/datatransfer/FlavorMap +java/awt/datatransfer/FlavorTable +java/awt/datatransfer/SystemFlavorMap$SoftCache +javax/swing/TransferHandler$DropHandler +javax/swing/TransferHandler$TransferSupport +javax/swing/text/PlainDocument +javax/swing/text/AbstractDocument +javax/swing/text/GapContent +javax/swing/text/AbstractDocument$Content +javax/swing/text/GapVector +javax/swing/text/GapContent$MarkVector +javax/swing/text/GapContent$MarkData +javax/swing/text/StyleContext +javax/swing/text/AbstractDocument$AttributeContext +javax/swing/text/StyleConstants +javax/swing/text/StyleConstants$CharacterConstants +javax/swing/text/AttributeSet$CharacterAttribute +javax/swing/text/StyleConstants$FontConstants +javax/swing/text/AttributeSet$FontAttribute +javax/swing/text/StyleConstants$ColorConstants +javax/swing/text/AttributeSet$ColorAttribute +javax/swing/text/StyleConstants$ParagraphConstants +javax/swing/text/AttributeSet$ParagraphAttribute +javax/swing/text/StyleContext$FontKey +javax/swing/text/SimpleAttributeSet +javax/swing/text/MutableAttributeSet +javax/swing/text/AttributeSet +javax/swing/text/SimpleAttributeSet$EmptyAttributeSet +javax/swing/text/StyleContext$NamedStyle +javax/swing/text/Style +java/util/Collections$EmptyEnumeration +javax/swing/text/StyleContext$SmallAttributeSet +java/util/Collections$3 +javax/swing/text/AbstractDocument$BidiRootElement +javax/swing/text/AbstractDocument$BranchElement +javax/swing/text/AbstractDocument$AbstractElement +javax/swing/text/Element +javax/swing/tree/TreeNode +javax/swing/text/AbstractDocument$1 +javax/swing/text/AbstractDocument$BidiElement +javax/swing/text/AbstractDocument$LeafElement +javax/swing/text/GapContent$StickyPosition +javax/swing/text/Position +javax/swing/text/StyleContext$KeyEnumeration +javax/swing/text/FieldView +javax/swing/text/PlainView +javax/swing/text/TabExpander +javax/swing/text/JTextComponent$DefaultKeymap +javax/swing/text/Keymap +javax/swing/text/JTextComponent$KeymapWrapper +javax/swing/text/JTextComponent$KeymapActionMap +javax/swing/plaf/basic/BasicTextUI$FocusAction +javax/swing/plaf/basic/BasicTextUI$TextActionWrapper +javax/swing/plaf/synth/SynthUI +javax/swing/plaf/synth/SynthConstants +javax/swing/JEditorPane +javax/swing/DefaultBoundedRangeModel +javax/swing/BoundedRangeModel +javax/swing/JTextField$ScrollRepainter +javax/swing/DefaultButtonModel +javax/swing/ButtonModel +javax/swing/AbstractButton$Handler +javax/swing/plaf/basic/BasicButtonUI +javax/swing/plaf/ButtonUI +javax/swing/plaf/metal/MetalBorders$ButtonBorder +javax/swing/plaf/basic/BasicButtonListener +javax/swing/event/AncestorListener +java/beans/VetoableChangeListener +javax/swing/plaf/metal/MetalComboBoxButton +javax/swing/plaf/basic/BasicArrowButton +javax/swing/plaf/metal/MetalScrollButton +sun/swing/ImageIconUIResource +javax/swing/GrayFilter +java/awt/image/RGBImageFilter +java/awt/image/ImageFilter +java/awt/image/FilteredImageSource +javax/swing/plaf/basic/BasicGraphicsUtils +javax/swing/ButtonGroup +org/w3c/dom/Node +org/xml/sax/SAXException +javax/xml/parsers/ParserConfigurationException +org/xml/sax/EntityResolver +java/security/NoSuchAlgorithmException +java/security/GeneralSecurityException +java/util/zip/GZIPInputStream +java/util/zip/DeflaterOutputStream +java/io/StringReader +org/xml/sax/InputSource +javax/xml/parsers/DocumentBuilderFactory +javax/xml/parsers/FactoryFinder +javax/xml/parsers/SecuritySupport +javax/xml/parsers/SecuritySupport$2 +javax/xml/parsers/SecuritySupport$5 +javax/xml/parsers/FactoryFinder$1 +javax/xml/parsers/DocumentBuilder +org/xml/sax/ErrorHandler +org/w3c/dom/Document +org/xml/sax/helpers/DefaultHandler +org/xml/sax/DTDHandler +org/xml/sax/ContentHandler +org/xml/sax/SAXNotRecognizedException +org/xml/sax/SAXNotSupportedException +org/xml/sax/Locator +org/xml/sax/SAXParseException +org/w3c/dom/NodeList +org/w3c/dom/events/EventTarget +org/w3c/dom/traversal/DocumentTraversal +org/w3c/dom/events/DocumentEvent +org/w3c/dom/ranges/DocumentRange +org/w3c/dom/Element +org/w3c/dom/CharacterData +org/w3c/dom/CDATASection +org/w3c/dom/Text +org/w3c/dom/Entity +org/xml/sax/AttributeList +org/w3c/dom/DOMException +org/w3c/dom/DocumentType +org/w3c/dom/Attr +org/w3c/dom/EntityReference +org/w3c/dom/DocumentFragment +org/w3c/dom/ProcessingInstruction +org/w3c/dom/Notation +org/w3c/dom/Comment +org/w3c/dom/events/EventException +org/w3c/dom/events/Event +org/w3c/dom/events/MutationEvent +org/w3c/dom/traversal/TreeWalker +org/w3c/dom/ranges/Range +org/w3c/dom/traversal/NodeIterator +org/w3c/dom/NamedNodeMap +java/awt/GridLayout +javax/swing/JToggleButton$ToggleButtonModel +javax/swing/plaf/metal/MetalRadioButtonUI +javax/swing/plaf/basic/BasicRadioButtonUI +javax/swing/plaf/basic/BasicToggleButtonUI +javax/swing/plaf/basic/BasicBorders +javax/swing/plaf/basic/BasicBorders$RadioButtonBorder +javax/swing/plaf/basic/BasicBorders$ButtonBorder +javax/swing/plaf/metal/MetalIconFactory$RadioButtonIcon +javax/swing/plaf/basic/BasicRadioButtonUI$KeyHandler +javax/swing/plaf/basic/BasicRadioButtonUI$SelectPreviousBtn +javax/swing/plaf/basic/BasicRadioButtonUI$SelectNextBtn +javax/swing/event/ChangeEvent +java/awt/event/ItemEvent +javax/swing/ToolTipManager +javax/swing/ToolTipManager$insideTimerAction +javax/swing/ToolTipManager$outsideTimerAction +javax/swing/ToolTipManager$stillInsideTimerAction +javax/swing/ToolTipManager$MoveBeforeEnterListener +java/awt/event/MouseMotionAdapter +javax/swing/ToolTipManager$AccessibilityKeyListener +java/awt/event/KeyAdapter +java/awt/CardLayout$Card +javax/swing/JComboBox +javax/swing/event/ListDataListener +javax/swing/JCheckBox +javax/swing/JPopupMenu +javax/swing/MenuElement +javax/swing/DefaultComboBoxModel +javax/swing/MutableComboBoxModel +javax/swing/ComboBoxModel +javax/swing/ListModel +javax/swing/AbstractListModel +javax/swing/JComboBox$1 +javax/swing/AncestorNotifier +javax/swing/plaf/metal/MetalComboBoxUI +javax/swing/plaf/basic/BasicComboBoxUI +javax/swing/plaf/ComboBoxUI +javax/swing/plaf/metal/MetalComboBoxUI$MetalComboBoxLayoutManager +javax/swing/plaf/basic/BasicComboBoxUI$ComboBoxLayoutManager +javax/swing/plaf/basic/BasicComboPopup +javax/swing/plaf/basic/ComboPopup +javax/swing/plaf/basic/BasicComboPopup$EmptyListModelClass +javax/swing/plaf/basic/BasicLookAndFeel$AWTEventHelper +java/awt/event/AWTEventListenerProxy +java/awt/Toolkit$SelectiveAWTEventListener +java/awt/Toolkit$ToolkitEventMulticaster +javax/swing/plaf/basic/BasicLookAndFeel$1 +javax/swing/plaf/basic/DefaultMenuLayout +javax/swing/plaf/metal/MetalBorders$PopupMenuBorder +javax/swing/plaf/basic/BasicPopupMenuUI$BasicPopupMenuListener +javax/swing/event/PopupMenuListener +javax/swing/plaf/basic/BasicPopupMenuUI$BasicMenuKeyListener +javax/swing/event/MenuKeyListener +javax/swing/plaf/basic/BasicPopupMenuUI$MouseGrabber +javax/swing/MenuSelectionManager +javax/swing/plaf/basic/BasicPopupMenuUI$MenuKeyboardHelper +javax/swing/plaf/basic/BasicPopupMenuUI$MenuKeyboardHelper$1 +java/awt/event/FocusAdapter +javax/swing/plaf/basic/BasicComboPopup$1 +javax/swing/JList +javax/swing/DefaultListSelectionModel +javax/swing/ListSelectionModel +javax/swing/plaf/basic/BasicListUI +javax/swing/plaf/ListUI +javax/swing/plaf/basic/BasicListUI$ListTransferHandler +javax/swing/DefaultListCellRenderer$UIResource +javax/swing/DefaultListCellRenderer +javax/swing/ListCellRenderer +javax/swing/plaf/basic/BasicListUI$Handler +javax/swing/event/ListSelectionListener +javax/swing/JMenu +javax/swing/JMenuItem +javax/swing/event/ListSelectionEvent +javax/swing/plaf/basic/BasicComboPopup$Handler +javax/swing/ScrollPaneLayout$UIResource +javax/swing/ScrollPaneLayout +javax/swing/ViewportLayout +javax/swing/plaf/basic/BasicViewportUI +javax/swing/plaf/ViewportUI +javax/swing/JScrollPane$ScrollBar +javax/swing/JScrollBar +java/awt/Adjustable +javax/swing/JScrollBar$ModelListener +javax/swing/plaf/metal/MetalScrollBarUI +javax/swing/plaf/basic/BasicScrollBarUI +javax/swing/plaf/ScrollBarUI +javax/swing/plaf/metal/MetalBumps +javax/swing/plaf/basic/BasicScrollBarUI$TrackListener +javax/swing/plaf/basic/BasicScrollBarUI$ArrowButtonListener +javax/swing/plaf/basic/BasicScrollBarUI$ModelListener +javax/swing/plaf/metal/MetalScrollBarUI$ScrollBarListener +javax/swing/plaf/basic/BasicScrollBarUI$PropertyChangeHandler +javax/swing/plaf/basic/BasicScrollBarUI$Handler +javax/swing/plaf/basic/BasicScrollBarUI$ScrollListener +javax/swing/JViewport$ViewListener +javax/swing/plaf/metal/MetalScrollPaneUI +javax/swing/plaf/basic/BasicScrollPaneUI +javax/swing/plaf/ScrollPaneUI +javax/swing/plaf/metal/MetalBorders$ScrollPaneBorder +javax/swing/plaf/basic/BasicScrollPaneUI$Handler +javax/swing/plaf/metal/MetalScrollPaneUI$1 +javax/swing/plaf/basic/BasicComboBoxRenderer$UIResource +javax/swing/plaf/basic/BasicComboBoxRenderer +javax/swing/plaf/metal/MetalComboBoxEditor$UIResource +javax/swing/plaf/metal/MetalComboBoxEditor +javax/swing/plaf/basic/BasicComboBoxEditor +javax/swing/ComboBoxEditor +javax/swing/plaf/basic/BasicComboBoxEditor$BorderlessTextField +javax/swing/plaf/basic/BasicComboBoxEditor$UIResource +javax/swing/text/Segment +java/text/CharacterIterator +javax/swing/plaf/metal/MetalComboBoxEditor$1 +javax/swing/plaf/metal/MetalComboBoxEditor$EditorBorder +javax/swing/JToolBar +javax/swing/plaf/metal/MetalComboBoxUI$MetalPropertyChangeListener +javax/swing/plaf/basic/BasicComboBoxUI$PropertyChangeHandler +javax/swing/plaf/basic/BasicComboBoxUI$Handler +javax/swing/plaf/metal/MetalComboBoxIcon +javax/swing/plaf/metal/MetalComboBoxButton$1 +javax/swing/plaf/basic/BasicComboBoxUI$DefaultKeySelectionManager +javax/swing/JComboBox$KeySelectionManager +javax/swing/plaf/metal/MetalCheckBoxUI +javax/swing/plaf/metal/MetalIconFactory$CheckBoxIcon +java/lang/ExceptionInInitializerError +com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI +javax/swing/JProgressBar +javax/swing/JProgressBar$ModelListener +javax/swing/plaf/metal/MetalProgressBarUI +javax/swing/plaf/basic/BasicProgressBarUI +javax/swing/plaf/ProgressBarUI +javax/swing/plaf/BorderUIResource$LineBorderUIResource +javax/swing/plaf/basic/BasicProgressBarUI$Handler +javax/swing/JTable +javax/swing/event/TableModelListener +javax/swing/event/TableColumnModelListener +javax/swing/event/CellEditorListener +javax/swing/event/RowSorterListener +javax/swing/tree/TreeModel +javax/swing/table/JTableHeader +javax/swing/table/AbstractTableModel +javax/swing/table/TableModel +javax/swing/table/TableCellRenderer +javax/swing/event/TreeExpansionListener +javax/swing/table/DefaultTableCellRenderer +javax/swing/JCheckBoxMenuItem +javax/swing/JTree +javax/swing/tree/TreeSelectionModel +javax/swing/tree/DefaultTreeCellRenderer +javax/swing/tree/TreeCellRenderer +javax/swing/table/TableCellEditor +javax/swing/CellEditor +javax/swing/JToolTip +javax/swing/table/TableColumn +javax/swing/table/DefaultTableColumnModel +javax/swing/table/TableColumnModel +javax/swing/table/DefaultTableModel +javax/swing/event/TableModelEvent +sun/swing/table/DefaultTableCellHeaderRenderer +sun/swing/table/DefaultTableCellHeaderRenderer$EmptyIcon +javax/swing/plaf/basic/BasicTableHeaderUI +javax/swing/plaf/TableHeaderUI +javax/swing/plaf/basic/BasicTableHeaderUI$1 +javax/swing/plaf/basic/BasicTableHeaderUI$MouseInputHandler +javax/swing/DefaultCellEditor +javax/swing/tree/TreeCellEditor +javax/swing/AbstractCellEditor +javax/swing/plaf/basic/BasicTableUI +javax/swing/plaf/TableUI +javax/swing/plaf/basic/BasicTableUI$TableTransferHandler +javax/swing/plaf/basic/BasicTableUI$Handler +javax/swing/tree/DefaultTreeSelectionModel +javax/swing/tree/TreePath +javax/swing/plaf/metal/MetalTreeUI +javax/swing/plaf/basic/BasicTreeUI +javax/swing/plaf/TreeUI +javax/swing/plaf/basic/BasicTreeUI$Actions +javax/swing/plaf/basic/BasicTreeUI$TreeTransferHandler +javax/swing/plaf/metal/MetalTreeUI$LineListener +javax/swing/plaf/basic/BasicTreeUI$Handler +javax/swing/event/TreeModelListener +javax/swing/event/TreeSelectionListener +javax/swing/event/SwingPropertyChangeSupport +javax/swing/tree/VariableHeightLayoutCache +javax/swing/tree/AbstractLayoutCache +javax/swing/tree/RowMapper +javax/swing/plaf/basic/BasicTreeUI$NodeDimensionsHandler +javax/swing/tree/AbstractLayoutCache$NodeDimensions +javax/swing/JTree$TreeModelHandler +javax/swing/tree/VariableHeightLayoutCache$TreeStateNode +javax/swing/tree/DefaultMutableTreeNode +javax/swing/tree/MutableTreeNode +javax/swing/tree/DefaultMutableTreeNode$PreorderEnumeration +java/util/Vector$1 +javax/swing/event/TableColumnModelEvent +javax/swing/JPopupMenu$Separator +javax/swing/JSeparator +java/text/ParseException +java/text/NumberFormat$Field +javax/swing/text/GapContent$InsertUndo +javax/swing/undo/AbstractUndoableEdit +javax/swing/undo/UndoableEdit +javax/swing/text/AbstractDocument$DefaultDocumentEvent +javax/swing/event/DocumentEvent +javax/swing/undo/CompoundEdit +javax/swing/event/DocumentEvent$EventType +javax/swing/text/Utilities +javax/swing/text/SegmentCache +javax/swing/text/SegmentCache$CachedSegment +javax/swing/event/DocumentEvent$ElementChange +javax/swing/event/UndoableEditEvent +javax/swing/event/UndoableEditListener +java/awt/Canvas +java/util/Locale$Category +java/util/Locale$1 +javax/swing/filechooser/FileFilter +java/io/FileWriter +javax/swing/tree/DefaultTreeModel +javax/swing/tree/DefaultTreeCellEditor +javax/swing/tree/DefaultTreeCellEditor$1 +javax/swing/tree/DefaultTreeCellEditor$DefaultTextField +javax/swing/DefaultCellEditor$1 +javax/swing/DefaultCellEditor$EditorDelegate +javax/swing/tree/DefaultTreeCellEditor$EditorContainer +javax/swing/JTree$TreeSelectionRedirector +javax/swing/JMenuItem$MenuItemFocusListener +javax/swing/plaf/basic/BasicMenuItemUI +javax/swing/plaf/MenuItemUI +javax/swing/plaf/metal/MetalBorders$MenuItemBorder +javax/swing/plaf/metal/MetalIconFactory$MenuItemArrowIcon +sun/swing/MenuItemLayoutHelper +javax/swing/plaf/basic/BasicMenuItemUI$Handler +javax/swing/event/MenuDragMouseListener +javax/swing/event/TreeModelEvent +javax/swing/JSplitPane +javax/swing/plaf/metal/MetalSplitPaneUI +javax/swing/plaf/basic/BasicSplitPaneUI +javax/swing/plaf/SplitPaneUI +javax/swing/plaf/basic/BasicSplitPaneDivider +javax/swing/plaf/basic/BasicBorders$SplitPaneBorder +javax/swing/plaf/metal/MetalSplitPaneDivider +javax/swing/plaf/basic/BasicSplitPaneDivider$DividerLayout +javax/swing/plaf/basic/BasicSplitPaneDivider$MouseHandler +javax/swing/plaf/basic/BasicBorders$SplitPaneDividerBorder +javax/swing/plaf/basic/BasicSplitPaneUI$BasicHorizontalLayoutManager +javax/swing/plaf/basic/BasicSplitPaneUI$1 +javax/swing/plaf/basic/BasicSplitPaneUI$Handler +javax/swing/plaf/metal/MetalSplitPaneDivider$1 +javax/swing/plaf/basic/BasicSplitPaneDivider$OneTouchActionHandler +javax/swing/plaf/metal/MetalSplitPaneDivider$2 +javax/swing/border/TitledBorder +javax/swing/plaf/basic/BasicTextAreaUI +javax/swing/text/AbstractDocument$ElementEdit +java/util/Random +java/util/concurrent/atomic/AtomicLong +java/io/InterruptedIOException +java/net/NoRouteToHostException +java/net/BindException +javax/swing/tree/PathPlaceHolder +javax/swing/event/TreeSelectionEvent +javax/swing/JList$3 +javax/swing/JList$ListSelectionHandler +javax/swing/JSlider +javax/swing/JSlider$ModelListener +javax/swing/plaf/metal/MetalSliderUI +javax/swing/plaf/basic/BasicSliderUI +javax/swing/plaf/SliderUI +javax/swing/plaf/basic/BasicSliderUI$Actions +javax/swing/plaf/metal/MetalIconFactory$HorizontalSliderThumbIcon +javax/swing/plaf/metal/MetalIconFactory$VerticalSliderThumbIcon +javax/swing/plaf/basic/BasicSliderUI$TrackListener +javax/swing/plaf/basic/BasicSliderUI$Handler +javax/swing/plaf/basic/BasicSliderUI$ScrollListener +javax/swing/plaf/metal/MetalSliderUI$MetalPropertyListener +javax/swing/plaf/basic/BasicSliderUI$PropertyChangeHandler +java/util/concurrent/ConcurrentHashMap$KeyIterator +java/util/concurrent/ConcurrentHashMap$BaseIterator +java/util/concurrent/ConcurrentHashMap$Traverser +sun/font/Type1Font$1 +java/nio/channels/FileChannel$MapMode +sun/nio/ch/FileChannelImpl$Unmapper +sun/nio/ch/Util$4 +java/nio/DirectByteBufferR +sun/nio/cs/US_ASCII$Decoder +sun/font/SunFontManager$10 +java/util/concurrent/ConcurrentHashMap$ValueIterator +javax/swing/DefaultListModel +javax/swing/event/ListDataEvent +javax/sound/sampled/DataLine +javax/sound/sampled/Line +javax/sound/sampled/LineUnavailableException +javax/sound/sampled/UnsupportedAudioFileException +javax/sound/sampled/Line$Info +javax/sound/sampled/DataLine$Info +javax/sound/sampled/Control$Type +javax/sound/sampled/FloatControl$Type +javax/swing/JMenuBar +javax/swing/plaf/basic/BasicMenuBarUI +javax/swing/plaf/MenuBarUI +javax/swing/plaf/metal/MetalBorders$MenuBarBorder +javax/swing/plaf/basic/BasicMenuBarUI$Handler +javax/swing/KeyboardManager +javax/swing/JRadioButtonMenuItem +javax/swing/JMenu$MenuChangeListener +javax/swing/plaf/basic/BasicMenuUI +javax/swing/plaf/metal/MetalIconFactory$MenuArrowIcon +javax/swing/plaf/basic/BasicMenuUI$Handler +javax/swing/JMenuItem$AccessibleJMenuItem +javax/swing/AbstractButton$AccessibleAbstractButton +javax/accessibility/AccessibleAction +javax/accessibility/AccessibleValue +javax/accessibility/AccessibleText +javax/accessibility/AccessibleExtendedComponent +javax/accessibility/AccessibleComponent +javax/swing/JComponent$AccessibleJComponent +java/awt/Container$AccessibleAWTContainer +java/awt/Component$AccessibleAWTComponent +javax/accessibility/AccessibleContext$1 +sun/awt/AWTAccessor$AccessibleContextAccessor +javax/accessibility/AccessibleRelationSet +javax/swing/JMenu$WinListener +java/awt/event/WindowAdapter +javax/swing/plaf/metal/MetalPopupMenuSeparatorUI +javax/swing/plaf/metal/MetalSeparatorUI +javax/swing/plaf/basic/BasicSeparatorUI +javax/swing/plaf/SeparatorUI +javax/accessibility/AccessibleState +javax/accessibility/AccessibleBundle +javax/swing/plaf/basic/BasicCheckBoxMenuItemUI +javax/swing/plaf/metal/MetalIconFactory$CheckBoxMenuItemIcon +javax/swing/JCheckBoxMenuItem$AccessibleJCheckBoxMenuItem +javax/swing/plaf/basic/BasicRadioButtonMenuItemUI +javax/swing/plaf/metal/MetalIconFactory$RadioButtonMenuItemIcon +java/awt/event/ContainerEvent +sun/awt/image/ImageDecoder$1 +java/awt/im/InputContext +sun/awt/im/InputMethodContext +java/awt/im/spi/InputMethodContext +java/awt/im/InputMethodRequests +sun/awt/im/InputContext +sun/awt/im/InputMethodManager +sun/awt/im/ExecutableInputMethodManager +sun/awt/X11/XInputMethodDescriptor +sun/awt/X11InputMethodDescriptor +java/awt/im/spi/InputMethodDescriptor +sun/awt/im/InputMethodLocator +sun/awt/im/ExecutableInputMethodManager$3 +javax/swing/JTabbedPane$Page +java/net/DatagramSocket +java/net/MulticastSocket +java/net/DatagramPacket +java/net/DatagramPacket$1 +java/net/DefaultDatagramSocketImplFactory +java/net/PlainDatagramSocketImpl +java/net/AbstractPlainDatagramSocketImpl +java/net/DatagramSocketImpl +java/net/AbstractPlainDatagramSocketImpl$1 +java/net/NetworkInterface +java/net/NetworkInterface$1 +java/net/InterfaceAddress +java/net/DefaultInterface +java/net/DatagramSocket$1 +java/net/SocketOption +sun/net/ResourceManager +java/text/Collator +sun/util/locale/provider/CollatorProviderImpl +java/util/Collections$UnmodifiableList$1 +sun/text/resources/CollationData +java/text/RuleBasedCollator +java/text/RBCollationTables +java/net/ServerSocket +java/text/RBTableBuilder +java/text/RBCollationTables$BuildAPI +sun/text/IntHashtable +sun/text/UCompactIntArray +sun/text/normalizer/NormalizerImpl +sun/text/normalizer/ICUData +sun/text/normalizer/NormalizerDataReader +sun/text/normalizer/ICUBinary$Authenticate +sun/text/normalizer/ICUBinary +sun/text/normalizer/NormalizerImpl$FCDTrieImpl +sun/text/normalizer/Trie$DataManipulate +sun/text/normalizer/NormalizerImpl$NormTrieImpl +sun/text/normalizer/NormalizerImpl$AuxTrieImpl +sun/text/normalizer/IntTrie +sun/text/normalizer/Trie +sun/text/normalizer/CharTrie +sun/text/normalizer/CharTrie$FriendAgent +sun/text/normalizer/UnicodeSet +sun/text/normalizer/UnicodeMatcher +sun/text/normalizer/NormalizerImpl$DecomposeArgs +java/text/MergeCollation +java/text/PatternEntry$Parser +java/text/PatternEntry +java/text/EntryPair +sun/text/ComposedCharIter +sun/text/normalizer/UTF16 +sun/net/www/protocol/http/Handler +java/security/SignatureException +java/security/InvalidKeyException +java/security/KeyException +java/security/Signature +java/security/SignatureSpi +java/io/ObjectInputStream$BlockDataInputStream +java/io/ObjectInputStream$PeekInputStream +java/io/ObjectInputStream$HandleTable +java/io/ObjectInputStream$HandleTable$HandleList +java/io/ObjectInputStream$ValidationList +java/io/Bits +java/io/ObjectStreamClass +java/io/ObjectOutputStream +java/io/ObjectOutput +sun/security/provider/DSAPublicKey +java/security/interfaces/DSAPublicKey +java/security/interfaces/DSAKey +java/security/PublicKey +java/security/Key +sun/security/x509/X509Key +java/io/ObjectStreamClass$Caches +java/io/ObjectStreamClass$WeakClassKey +java/io/ObjectStreamClass$EntryFuture +java/lang/reflect/Proxy +java/lang/reflect/InvocationHandler +java/lang/reflect/WeakCache +java/lang/reflect/Proxy$KeyFactory +java/lang/reflect/Proxy$ProxyClassFactory +java/io/Externalizable +java/io/ObjectStreamClass$2 +java/util/ComparableTimSort +sun/security/x509/AlgorithmId +sun/security/util/DerEncoder +sun/reflect/SerializationConstructorAccessorImpl +sun/security/util/BitArray +sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl +java/io/ObjectStreamClass$FieldReflectorKey +java/io/ObjectStreamClass$FieldReflector +java/io/ObjectStreamClass$1 +java/io/DataOutputStream +java/io/ObjectStreamClass$MemberSignature +java/io/ObjectStreamClass$3 +sun/security/util/DerOutputStream +java/io/ObjectStreamClass$4 +java/io/ObjectStreamClass$5 +sun/security/util/DerValue +java/security/MessageDigest +java/security/MessageDigestSpi +sun/security/jca/GetInstance +sun/security/jca/Providers +java/lang/InheritableThreadLocal +sun/security/jca/ProviderList +sun/security/jca/ProviderConfig +java/math/BigInteger +java/security/Provider +sun/security/jca/ProviderList$3 +sun/security/jca/ProviderList$1 +java/security/Provider$ServiceKey +java/security/Provider$EngineDescription +java/security/interfaces/DSAParams +sun/security/jca/ProviderList$2 +sun/security/jca/ProviderConfig$2 +java/io/ObjectStreamClass$ClassDataSlot +sun/security/provider/Sun +java/io/SerialCallbackContext +sun/security/provider/SunEntries +sun/security/provider/SunEntries$1 +sun/security/provider/NativePRNG +java/security/SecureRandomSpi +sun/security/provider/NativePRNG$Variant +sun/security/util/DerInputStream +sun/security/provider/NativePRNG$1 +sun/security/provider/NativePRNG$2 +sun/security/provider/NativePRNG$RandomIO +sun/security/util/DerInputBuffer +sun/security/provider/NativePRNG$Blocking +sun/security/provider/NativePRNG$NonBlocking +sun/security/util/ObjectIdentifier +java/security/Provider$Service +java/security/Provider$UString +sun/security/provider/SHA +sun/security/provider/DigestBase +sun/security/jca/GetInstance$Instance +java/security/MessageDigest$Delegate +sun/security/provider/ByteArrayAccess +java/io/ObjectOutputStream$BlockDataOutputStream +java/security/AlgorithmParameters +java/io/ObjectOutputStream$HandleTable +java/io/ObjectOutputStream$ReplaceTable +java/security/AlgorithmParametersSpi +sun/security/provider/DSAParameters +sun/security/util/ByteArrayLexOrder +sun/security/util/ByteArrayTagOrder +sun/security/util/DerIndefLenConverter +java/io/ObjectStreamClass$ExceptionInfo +java/io/ObjectInputStream$GetFieldImpl +java/io/ObjectInputStream$GetField +java/math/BigInteger$UnsafeHolder +sun/security/jca/ServiceId +sun/security/jca/ProviderList$ServiceList +sun/security/jca/ProviderList$ServiceList$1 +java/security/Signature$Delegate +java/util/ArrayList$SubList +java/util/ArrayList$SubList$1 +java/security/interfaces/DSAPrivateKey +java/security/PrivateKey +javax/security/auth/Destroyable +sun/security/provider/DSA$SHA1withDSA +sun/security/provider/DSA$LegacyDSA +sun/security/provider/DSA +java/security/spec/DSAParameterSpec +java/security/spec/AlgorithmParameterSpec +java/math/MutableBigInteger +java/math/SignedMutableBigInteger +javax/swing/TimerQueue +java/util/concurrent/DelayQueue +java/util/concurrent/BlockingQueue +java/util/AbstractQueue +java/util/PriorityQueue +javax/swing/TimerQueue$1 +javax/swing/TimerQueue$DelayedTimer +java/util/concurrent/Delayed +java/util/concurrent/TimeUnit +java/util/concurrent/TimeUnit$1 +java/util/concurrent/TimeUnit$2 +java/util/concurrent/TimeUnit$3 +java/util/concurrent/TimeUnit$4 +java/util/concurrent/TimeUnit$5 +java/util/concurrent/TimeUnit$6 +java/util/concurrent/TimeUnit$7 +java/awt/Window$1DisposeAction +java/awt/EventQueue$1AWTInvocationLock +sun/awt/X11/XUnmapEvent +java/awt/LightweightDispatcher$2 +java/awt/Component$FlipBufferStrategy +java/awt/SentEvent +sun/java2d/cmm/CMSManager +java/awt/image/DataBufferInt +java/awt/image/SinglePixelPackedSampleModel +sun/awt/image/IntegerInterleavedRaster +sun/awt/X11/XDropTargetRegistry +sun/awt/X11/XEmbeddedFramePeer +sun/awt/X11/XDragAndDropProtocols +sun/awt/X11/XDropTargetContextPeer +sun/awt/dnd/SunDropTargetContextPeer +java/awt/dnd/peer/DropTargetContextPeer +java/awt/datatransfer/Transferable +sun/awt/X11/XDropTargetContextPeer$XDropTargetProtocolListenerImpl +sun/awt/X11/XDropTargetProtocolListener +sun/awt/X11/XDnDDragSourceProtocol +sun/awt/X11/XDragSourceProtocol +sun/awt/X11/MotifDnDDragSourceProtocol +sun/awt/X11/XDnDDropTargetProtocol +sun/awt/X11/XDropTargetProtocol +sun/awt/X11/MotifDnDDropTargetProtocol +sun/awt/X11/XErrorHandler$VerifyChangePropertyHandler +sun/awt/X11/XDnDConstants +sun/awt/X11/XSelection +sun/awt/X11/XSelection$IncrementalTransferHandler +sun/awt/X11/XSelection$SelectionEventHandler +sun/awt/X11/MotifDnDConstants +javax/swing/JLayer +javax/swing/JInternalFrame +javax/swing/KeyboardManager$ComponentKeyStrokePair +sun/awt/EmbeddedFrame +sun/swing/MenuItemLayoutHelper$RectSize +javax/swing/JTable$2 +javax/swing/JTable$Resizable3 +javax/swing/JTable$Resizable2 +javax/swing/JTable$5 +javax/swing/event/AncestorEvent +java/awt/Label +sun/awt/X11/XLabelPeer +java/awt/peer/LabelPeer +sun/awt/TimedWindowEvent +java/awt/DefaultKeyboardFocusManager$DefaultKeyboardFocusManagerSentEvent +sun/awt/CausedFocusEvent$Cause +java/awt/KeyboardFocusManager$HeavyweightFocusRequest +java/awt/DefaultKeyboardFocusManager$TypeAheadMarker +java/awt/KeyboardFocusManager$LightweightFocusRequest +sun/awt/CausedFocusEvent +java/awt/KeyboardFocusManager$3 +sun/awt/X11/XInputMethod +sun/awt/X11InputMethod +sun/awt/im/InputMethodAdapter +java/awt/im/spi/InputMethod +java/awt/Event +java/net/Authenticator +java/lang/Throwable$WrappedPrintStream +java/lang/Throwable$PrintStreamOrWriter +sun/awt/image/PNGImageDecoder +sun/awt/image/PNGFilterInputStream +sun/util/locale/provider/TimeZoneNameUtility +sun/util/locale/provider/TimeZoneNameProviderImpl +sun/util/locale/provider/TimeZoneNameUtility$TimeZoneNameGetter +sun/util/resources/TimeZoneNames +sun/util/resources/TimeZoneNamesBundle +sun/util/resources/en/TimeZoneNames_en +sun/awt/image/OffScreenImage +java/lang/ProcessBuilder +java/lang/ProcessImpl +java/lang/UNIXProcess +java/lang/Process +java/lang/UNIXProcess$Platform +java/lang/UNIXProcess$LaunchMechanism +java/util/EnumSet +java/util/RegularEnumSet +sun/java2d/x11/X11SurfaceDataProxy$Opaque +java/lang/UNIXProcess$1 +java/util/concurrent/Executor +java/util/concurrent/ThreadFactory +java/util/concurrent/Executors +java/util/concurrent/ThreadPoolExecutor +java/util/concurrent/AbstractExecutorService +java/util/concurrent/ExecutorService +java/util/concurrent/ThreadPoolExecutor$AbortPolicy +java/util/concurrent/RejectedExecutionHandler +java/util/concurrent/SynchronousQueue +java/util/concurrent/SynchronousQueue$TransferStack +java/util/concurrent/SynchronousQueue$Transferer +java/util/concurrent/SynchronousQueue$TransferStack$SNode +java/lang/UNIXProcess$ProcessPipeOutputStream +java/lang/UNIXProcess$ProcessPipeInputStream +java/util/concurrent/ThreadPoolExecutor$Worker +java/lang/ProcessBuilder$NullOutputStream +java/io/FilterReader +sun/awt/X11/XClipboard +sun/awt/X11/OwnershipListener +sun/awt/datatransfer/SunClipboard +java/awt/datatransfer/Clipboard +java/io/EOFException +java/util/jar/JarFile$JarEntryIterator +java/util/zip/ZipFile$ZipEntryIterator +java/lang/IllegalAccessError +java/text/MessageFormat +java/text/MessageFormat$Field +java/util/Hashtable$ValueCollection +javax/swing/event/CaretListener +javax/swing/plaf/metal/MetalButtonUI +javax/swing/plaf/metal/MetalToggleButtonUI +javax/swing/plaf/metal/MetalBorders$ToggleButtonBorder +javax/swing/event/MenuEvent +javax/swing/border/MatteBorder +sun/font/StandardGlyphVector +java/awt/font/GlyphVector +sun/font/StandardGlyphVector$GlyphStrike +sun/font/CoreMetrics +sun/font/FontLineMetrics +java/awt/font/LineMetrics +javax/swing/JToolBar$DefaultToolBarLayout +javax/swing/plaf/metal/MetalToolBarUI +javax/swing/plaf/basic/BasicToolBarUI +javax/swing/plaf/ToolBarUI +javax/swing/plaf/metal/MetalBorders$ToolBarBorder +javax/swing/plaf/metal/MetalBorders$RolloverButtonBorder +javax/swing/plaf/metal/MetalBorders$RolloverMarginBorder +javax/swing/plaf/basic/BasicBorders$RolloverMarginBorder +javax/swing/plaf/metal/MetalToolBarUI$MetalDockingListener +javax/swing/plaf/basic/BasicToolBarUI$DockingListener +javax/swing/plaf/basic/BasicToolBarUI$Handler +javax/swing/JToolBar$Separator +javax/swing/plaf/basic/BasicToolBarSeparatorUI +java/awt/event/AdjustmentEvent +java/awt/MenuBar +sun/awt/X11/XErrorEvent +# eea35d9d56e0006e diff --git a/FCL/src/main/assets/java/lib/cmm/CIEXYZ.pf b/FCL/src/main/assets/java/lib/cmm/CIEXYZ.pf new file mode 100644 index 00000000..48c9dd80 Binary files /dev/null and b/FCL/src/main/assets/java/lib/cmm/CIEXYZ.pf differ diff --git a/FCL/src/main/assets/java/lib/cmm/GRAY.pf b/FCL/src/main/assets/java/lib/cmm/GRAY.pf new file mode 100644 index 00000000..8af309cc Binary files /dev/null and b/FCL/src/main/assets/java/lib/cmm/GRAY.pf differ diff --git a/FCL/src/main/assets/java/lib/cmm/LINEAR_RGB.pf b/FCL/src/main/assets/java/lib/cmm/LINEAR_RGB.pf new file mode 100644 index 00000000..3e0b1777 Binary files /dev/null and b/FCL/src/main/assets/java/lib/cmm/LINEAR_RGB.pf differ diff --git a/FCL/src/main/assets/java/lib/cmm/PYCC.pf b/FCL/src/main/assets/java/lib/cmm/PYCC.pf new file mode 100644 index 00000000..0cee0158 Binary files /dev/null and b/FCL/src/main/assets/java/lib/cmm/PYCC.pf differ diff --git a/FCL/src/main/assets/java/lib/cmm/sRGB.pf b/FCL/src/main/assets/java/lib/cmm/sRGB.pf new file mode 100644 index 00000000..7b552add Binary files /dev/null and b/FCL/src/main/assets/java/lib/cmm/sRGB.pf differ diff --git a/FCL/src/main/assets/java/lib/content-types.properties b/FCL/src/main/assets/java/lib/content-types.properties new file mode 100644 index 00000000..81c132c5 --- /dev/null +++ b/FCL/src/main/assets/java/lib/content-types.properties @@ -0,0 +1,280 @@ +#sun.net.www MIME content-types table +# +# Property fields: +# +# ::= 'description' '=' +# ::= 'file_extensions' '=' +# ::= 'icon' '=' +# ::= 'browser' | 'application' | 'save' | 'unknown' +# ::= 'application' '=' +# + +# +# The "we don't know anything about this data" type(s). +# Used internally to mark unrecognized types. +# +content/unknown= description=Unknown Content +unknown/unknown= description=Unknown Data Type + +# +# The template we should use for temporary files when launching an application +# to view a document of given type. +# +temp.file.template= /tmp/%s + +# +# The "real" types. +# +application/octet-stream= \ + description=Generic Binary Stream;\ + file_extensions=.saveme,.dump,.hqx,.arc,.o,.a,.bin,.exe,.z,.gz + +application/oda= \ + description=ODA Document;\ + file_extensions=.oda + +application/pdf= \ + description=Adobe PDF Format;\ + file_extensions=.pdf + +application/postscript= \ + description=Postscript File;\ + file_extensions=.eps,.ai,.ps;\ + icon=ps;\ + action=application;\ + application=imagetool %s + +application/x-dvi= \ + description=TeX DVI File;\ + file_extensions=.dvi;\ + action=application;\ + application=xdvi %s + +application/x-hdf= \ + description=Hierarchical Data Format;\ + file_extensions=.hdf;\ + action=save + +application/x-latex= \ + description=LaTeX Source;\ + file_extensions=.latex + +application/x-netcdf= \ + description=Unidata netCDF Data Format;\ + file_extensions=.nc,.cdf;\ + action=save + +application/x-tex= \ + description=TeX Source;\ + file_extensions=.tex + +application/x-texinfo= \ + description=Gnu Texinfo;\ + file_extensions=.texinfo,.texi + +application/x-troff= \ + description=Troff Source;\ + file_extensions=.t,.tr,.roff;\ + action=application;\ + application=xterm -title troff -e sh -c \"nroff %s | col | more -w\" + +application/x-troff-man= \ + description=Troff Manpage Source;\ + file_extensions=.man;\ + action=application;\ + application=xterm -title troff -e sh -c \"nroff -man %s | col | more -w\" + +application/x-troff-me= \ + description=Troff ME Macros;\ + file_extensions=.me;\ + action=application;\ + application=xterm -title troff -e sh -c \"nroff -me %s | col | more -w\" + +application/x-troff-ms= \ + description=Troff MS Macros;\ + file_extensions=.ms;\ + action=application;\ + application=xterm -title troff -e sh -c \"nroff -ms %s | col | more -w\" + +application/x-wais-source= \ + description=Wais Source;\ + file_extensions=.src,.wsrc + +application/zip= \ + description=Zip File;\ + file_extensions=.zip;\ + icon=zip;\ + action=save + +application/x-bcpio= \ + description=Old Binary CPIO Archive;\ + file_extensions=.bcpio; action=save + +application/x-cpio= \ + description=Unix CPIO Archive;\ + file_extensions=.cpio; action=save + +application/x-gtar= \ + description=Gnu Tar Archive;\ + file_extensions=.gtar;\ + icon=tar;\ + action=save + +application/x-shar= \ + description=Shell Archive;\ + file_extensions=.sh,.shar;\ + action=save + +application/x-sv4cpio= \ + description=SVR4 CPIO Archive;\ + file_extensions=.sv4cpio; action=save + +application/x-sv4crc= \ + description=SVR4 CPIO with CRC;\ + file_extensions=.sv4crc; action=save + +application/x-tar= \ + description=Tar Archive;\ + file_extensions=.tar;\ + icon=tar;\ + action=save + +application/x-ustar= \ + description=US Tar Archive;\ + file_extensions=.ustar;\ + action=save + +audio/basic= \ + description=Basic Audio;\ + file_extensions=.snd,.au;\ + icon=audio;\ + action=application;\ + application=audiotool %s + +audio/x-aiff= \ + description=Audio Interchange Format File;\ + file_extensions=.aifc,.aif,.aiff;\ + icon=aiff + +audio/x-wav= \ + description=Wav Audio;\ + file_extensions=.wav;\ + icon=wav + +image/gif= \ + description=GIF Image;\ + file_extensions=.gif;\ + icon=gif;\ + action=browser + +image/ief= \ + description=Image Exchange Format;\ + file_extensions=.ief + +image/jpeg= \ + description=JPEG Image;\ + file_extensions=.jfif,.jfif-tbnl,.jpe,.jpg,.jpeg;\ + icon=jpeg;\ + action=browser;\ + application=imagetool %s + +image/tiff= \ + description=TIFF Image;\ + file_extensions=.tif,.tiff;\ + icon=tiff + +image/vnd.fpx= \ + description=FlashPix Image;\ + file_extensions=.fpx,.fpix + +image/x-cmu-rast= \ + description=CMU Raster Image;\ + file_extensions=.ras + +image/x-portable-anymap= \ + description=PBM Anymap Format;\ + file_extensions=.pnm + +image/x-portable-bitmap= \ + description=PBM Bitmap Format;\ + file_extensions=.pbm + +image/x-portable-graymap= \ + description=PBM Graymap Format;\ + file_extensions=.pgm + +image/x-portable-pixmap= \ + description=PBM Pixmap Format;\ + file_extensions=.ppm + +image/x-rgb= \ + description=RGB Image;\ + file_extensions=.rgb + +image/x-xbitmap= \ + description=X Bitmap Image;\ + file_extensions=.xbm,.xpm + +image/x-xwindowdump= \ + description=X Window Dump Image;\ + file_extensions=.xwd + +image/png= \ + description=PNG Image;\ + file_extensions=.png;\ + icon=png;\ + action=browser + +image/bmp= \ + description=Bitmap Image;\ + file_extensions=.bmp; + +text/html= \ + description=HTML Document;\ + file_extensions=.htm,.html;\ + icon=html + +text/plain= \ + description=Plain Text;\ + file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el;\ + icon=text;\ + action=browser + +text/tab-separated-values= \ + description=Tab Separated Values Text;\ + file_extensions=.tsv + +text/x-setext= \ + description=Structure Enhanced Text;\ + file_extensions=.etx + +video/mpeg= \ + description=MPEG Video Clip;\ + file_extensions=.mpg,.mpe,.mpeg;\ + icon=mpeg;\ + action=application;\ + application=mpeg_play %s + +video/quicktime= \ + description=QuickTime Video Clip;\ + file_extensions=.mov,.qt + +application/x-troff-msvideo= \ + description=AVI Video;\ + file_extensions=.avi;\ + icon=avi + +video/x-sgi-movie= \ + description=SGI Movie;\ + file_extensions=.movie,.mv + +message/rfc822= \ + description=Internet Email Message;\ + file_extensions=.mime + +application/xml= \ + description=XML document;\ + file_extensions=.xml + + + diff --git a/FCL/src/main/assets/java/lib/currency.data b/FCL/src/main/assets/java/lib/currency.data new file mode 100644 index 00000000..6be2222c Binary files /dev/null and b/FCL/src/main/assets/java/lib/currency.data differ diff --git a/FCL/src/main/assets/java/lib/ext/cldrdata.jar b/FCL/src/main/assets/java/lib/ext/cldrdata.jar new file mode 100644 index 00000000..9abe16ba Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/cldrdata.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/dnsns.jar b/FCL/src/main/assets/java/lib/ext/dnsns.jar new file mode 100644 index 00000000..63035a7f Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/dnsns.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/jaccess.jar b/FCL/src/main/assets/java/lib/ext/jaccess.jar new file mode 100644 index 00000000..63adcd9b Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/jaccess.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/localedata.jar b/FCL/src/main/assets/java/lib/ext/localedata.jar new file mode 100644 index 00000000..5dbc3169 Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/localedata.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/meta-index b/FCL/src/main/assets/java/lib/ext/meta-index new file mode 100644 index 00000000..c66e758a --- /dev/null +++ b/FCL/src/main/assets/java/lib/ext/meta-index @@ -0,0 +1,28 @@ +% VERSION 2 +% WARNING: this file is auto-generated; do not edit +% UNSUPPORTED: this file and its format may change and/or +% may be removed in a future release +! cldrdata.jar +sun/text +sun/util +# dnsns.jar +META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor +sun/net +! jaccess.jar +com/sun/java/accessibility/ +# localedata.jar +sun/text +sun/util +# nashorn.jar +jdk/nashorn +META-INF/services/javax.script.ScriptEngineFactory +jdk/internal +! sunec.jar +sun/security +! sunjce_provider.jar +com/sun/crypto/ +! sunpkcs11.jar +sun/security +# zipfs.jar +META-INF/services/java.nio.file.spi.FileSystemProvider +com/sun/nio/ diff --git a/FCL/src/main/assets/java/lib/ext/nashorn.jar b/FCL/src/main/assets/java/lib/ext/nashorn.jar new file mode 100644 index 00000000..f808f2b9 Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/nashorn.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/sunec.jar b/FCL/src/main/assets/java/lib/ext/sunec.jar new file mode 100644 index 00000000..91fb9648 Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/sunec.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/sunjce_provider.jar b/FCL/src/main/assets/java/lib/ext/sunjce_provider.jar new file mode 100644 index 00000000..31ad3b32 Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/sunjce_provider.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/sunpkcs11.jar b/FCL/src/main/assets/java/lib/ext/sunpkcs11.jar new file mode 100644 index 00000000..bd30b5a6 Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/sunpkcs11.jar differ diff --git a/FCL/src/main/assets/java/lib/ext/zipfs.jar b/FCL/src/main/assets/java/lib/ext/zipfs.jar new file mode 100644 index 00000000..1c232264 Binary files /dev/null and b/FCL/src/main/assets/java/lib/ext/zipfs.jar differ diff --git a/FCL/src/main/assets/java/lib/flavormap.properties b/FCL/src/main/assets/java/lib/flavormap.properties new file mode 100644 index 00000000..223002a6 --- /dev/null +++ b/FCL/src/main/assets/java/lib/flavormap.properties @@ -0,0 +1,78 @@ +# +# This properties file is used to initialize the default +# java.awt.datatransfer.SystemFlavorMap. It contains the X11 platform-specific, +# default mappings between common X11 selection atoms and platform-independent +# MIME type strings, which will be converted into +# java.awt.datatransfer.DataFlavors. +# +# These default mappings may be augmented by specifying the +# +# AWT.DnD.flavorMapFileURL +# +# property in the appropriate awt.properties file. The specified properties URL +# will be loaded into the SystemFlavorMap. +# +# The standard format is: +# +# = +# +# should be a string identifier that the native platform will +# recognize as a valid data format. should specify both a MIME +# primary type and a MIME subtype separated by a '/'. The MIME type may include +# parameters, where each parameter is a key/value pair separated by '=', and +# where each parameter to the MIME type is separated by a ';'. +# +# Because SystemFlavorMap implements FlavorTable, developers are free to +# duplicate both native keys and DataFlavor values. If a mapping contains a +# duplicate key or value, earlier mappings which included this key or value +# will be preferred. +# +# Mappings whose values specify DataFlavors with primary MIME types of +# "text", and which support the charset parameter, should specify the exact +# format in which the native platform expects the data. The "charset" +# parameter specifies the char to byte encoding, the "eoln" parameter +# specifies the end-of-line marker, and the "terminators" parameter specifies +# the number of terminating NUL bytes. Note that "eoln" and "terminators" +# are not standardized MIME type parameters. They are specific to this file +# format ONLY. They will not appear in any of the DataFlavors returned by the +# SystemFlavorMap at the Java level. +# +# If the "charset" parameter is omitted, or has zero length, the platform +# default encoding is assumed. If the "eoln" parameter is omitted, or has +# zero length, "\n" is assumed. If the "terminators" parameter is omitted, +# or has a value less than zero, zero is assumed. +# +# Upon initialization, the data transfer subsystem will record the specified +# details of the native text format, but the default SystemFlavorMap will +# present a large set of synthesized DataFlavors which map, in both +# directions, to the native. After receiving data from the application in one +# of the synthetic DataFlavors, the data transfer subsystem will transform +# the data stream into the format specified in this file before passing the +# transformed stream to the native system. +# +# Mappings whose values specify DataFlavors with primary MIME types of +# "text", but which do not support the charset parameter, will be treated as +# opaque, 8-bit data. They will not undergo any transformation process, and +# any "charset", "eoln", or "terminators" parameters specified in this file +# will be ignored. +# +# See java.awt.datatransfer.DataFlavor.selectBestTextFlavor for a list of +# text flavors which support the charset parameter. + +UTF8_STRING=text/plain;charset=UTF-8;eoln="\n";terminators=0 + +# The COMPOUND_TEXT support for inter-client text transfer is disabled by +# default. The reason is that many native applications prefer this format over +# other native text formats, but are unable to decode the textual data in this +# format properly. This results in java-to-native text transfer failures. +# To enable the COMPOUND_TEXT support for this JRE installation uncomment +# the line below. + +# COMPOUND_TEXT=text/plain;charset=x-compound-text;eoln="\n";terminators=0 + +TEXT=text/plain;eoln="\n";terminators=0 +STRING=text/plain;charset=iso8859-1;eoln="\n";terminators=0 +FILE_NAME=application/x-java-file-list;class=java.util.List +text/uri-list=application/x-java-file-list;class=java.util.List +PNG=image/x-java-image;class=java.awt.Image +JFIF=image/x-java-image;class=java.awt.Image diff --git a/FCL/src/main/assets/java/lib/fontconfig.Linux.bfc b/FCL/src/main/assets/java/lib/fontconfig.Linux.bfc new file mode 100644 index 00000000..25c5c462 Binary files /dev/null and b/FCL/src/main/assets/java/lib/fontconfig.Linux.bfc differ diff --git a/FCL/src/main/assets/java/lib/fontconfig.Linux.properties.src b/FCL/src/main/assets/java/lib/fontconfig.Linux.properties.src new file mode 100644 index 00000000..c37c3cb1 --- /dev/null +++ b/FCL/src/main/assets/java/lib/fontconfig.Linux.properties.src @@ -0,0 +1,144 @@ +# +# Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. +# + +# Version + +version=1 +# Component Font Mappings + +allfonts.chinese-gbk=-tlc-songti-medium-r-normal--*-%d-*-*-c-*-gbk-0 +allfonts.chinese-gb2312=-tlc-songti-medium-r-normal--*-%d-*-*-c-*-gb2312.1980-0 +allfonts.chinese-iso10646=-tlc-songti-medium-r-normal--*-%d-*-*-c-*-iso10646-1 +allfonts.chinese-big5=-tlc-songti-medium-r-normal--*-%d-*-*-c-*-big5-0 +allfonts.lucida=-b&h-lucidasans-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1 + +serif.plain.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +serif.plain.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +serif.plain.latin-1=-b&h-lucidabright-medium-r-normal--*-%d-*-*-p-*-iso8859-1 + +serif.bold.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +serif.bold.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +serif.bold.latin-1=-b&h-lucidabright-demibold-r-normal--*-%d-*-*-p-*-iso8859-1 + +serif.italic.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +serif.italic.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +serif.italic.latin-1=-b&h-lucidabright-medium-i-normal--*-%d-*-*-p-*-iso8859-1 + +serif.bolditalic.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +serif.bolditalic.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +serif.bolditalic.latin-1=-b&h-lucidabright-demibold-i-normal--*-%d-*-*-p-*-iso8859-1 + +sansserif.plain.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +sansserif.plain.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +sansserif.plain.latin-1=-b&h-lucidasans-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1 + +sansserif.bold.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +sansserif.bold.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +sansserif.bold.latin-1=-b&h-lucidasans-bold-r-normal-sans-*-%d-*-*-p-*-iso8859-1 + +sansserif.italic.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +sansserif.italic.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +sansserif.italic.latin-1=-b&h-lucidasans-medium-i-normal-sans-*-%d-*-*-p-*-iso8859-1 + +sansserif.bolditalic.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +sansserif.bolditalic.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +sansserif.bolditalic.latin-1=-b&h-lucidasans-bold-i-normal-sans-*-%d-*-*-p-*-iso8859-1 + +monospaced.plain.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +monospaced.plain.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +monospaced.plain.latin-1=-b&h-lucidatypewriter-medium-r-normal-sans-*-%d-*-*-m-*-iso8859-1 + +monospaced.bold.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +monospaced.bold.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +monospaced.bold.latin-1=-b&h-lucidatypewriter-bold-r-normal-sans-*-%d-*-*-m-*-iso8859-1 + +monospaced.italic.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +monospaced.italic.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +monospaced.italic.latin-1=-b&h-lucidatypewriter-medium-i-normal-sans-*-%d-*-*-m-*-iso8859-1 + +monospaced.bolditalic.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +monospaced.bolditalic.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +monospaced.bolditalic.latin-1=-b&h-lucidatypewriter-bold-i-normal-sans-*-%d-*-*-m-*-iso8859-1 + +dialog.plain.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialog.plain.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialog.plain.latin-1=-b&h-lucidasans-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1 + +dialog.bold.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialog.bold.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialog.bold.latin-1=-b&h-lucidasans-bold-r-normal-sans-*-%d-*-*-p-*-iso8859-1 + +dialog.italic.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialog.italic.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialog.italic.latin-1=-b&h-lucidasans-medium-i-normal-sans-*-%d-*-*-p-*-iso8859-1 + +dialog.bolditalic.japanese-x0201=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialog.bolditalic.japanese-x0208=-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialog.bolditalic.latin-1=-b&h-lucidasans-bold-i-normal-sans-*-%d-*-*-p-*-iso8859-1 + +dialoginput.plain.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialoginput.plain.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialoginput.plain.latin-1=-b&h-lucidatypewriter-medium-r-normal-sans-*-%d-*-*-m-*-iso8859-1 + +dialoginput.bold.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialoginput.bold.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialoginput.bold.latin-1=-b&h-lucidatypewriter-bold-r-normal-sans-*-%d-*-*-m-*-iso8859-1 + +dialoginput.italic.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialoginput.italic.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialoginput.italic.latin-1=-b&h-lucidatypewriter-medium-i-normal-sans-*-%d-*-*-m-*-iso8859-1 + +dialoginput.bolditalic.japanese-x0201=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0 +dialoginput.bolditalic.japanese-x0208=-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0 +dialoginput.bolditalic.latin-1=-b&h-lucidatypewriter-bold-i-normal-sans-*-%d-*-*-m-*-iso8859-1 + + +# Search Sequences + +sequence.allfonts=latin-1 +sequence.allfonts.Big5=latin-1,chinese-big5 +sequence.allfonts.Big5-HKSCS=latin-1,chinese-big5 +sequence.allfonts.GB18030=latin-1,chinese-gbk,chinese-iso10646 +sequence.allfonts.GBK=latin-1,chinese-gbk +sequence.allfonts.GB2312=latin-1,chinese-gb2312 +sequence.allfonts.x-euc-jp-linux=latin-1,japanese-x0208,japanese-x0201 +sequence.allfonts.UTF-8.ja.JP=latin-1,japanese-x0208,japanese-x0201,chinese-iso10646 +sequence.allfonts.UTF-8.zh=latin-1,chinese-iso10646,japanese-x0208,japanese-x0201 +sequence.fallback=lucida,chinese-big5,chinese-iso10646,japanese-x0208 + +# Exclusion Ranges +exclusion.japanese-x0201=0390-03d6,2200-22ef,2701-27be +exclusion.japanese-x0208=0390-03d6,2200-22ef,2701-27be + +# Font File Names +filename.-b&h-lucidasans-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaSansRegular.ttf +filename.-b&h-lucidabright-medium-r-normal--*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaBrightRegular.ttf +filename.-b&h-lucidabright-demibold-r-normal--*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaBrightDemiBold.ttf +filename.-b&h-lucidabright-medium-i-normal--*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaBrightItalic.ttf +filename.-b&h-lucidabright-demibold-i-normal--*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaBrightDemiItalic.ttf +filename.-b&h-lucidasans-bold-r-normal-sans-*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaSansDemiBold.ttf +filename.-b&h-lucidasans-medium-i-normal-sans-*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaSansRegular.ttf +filename.-b&h-lucidasans-bold-i-normal-sans-*-%d-*-*-p-*-iso8859-1=$JRE_LIB_FONTS/LucidaSansDemiBold.ttf +filename.-b&h-lucidatypewriter-medium-r-normal-sans-*-%d-*-*-m-*-iso8859-1=$JRE_LIB_FONTS/LucidaTypewriterRegular.ttf +filename.-b&h-lucidatypewriter-bold-r-normal-sans-*-%d-*-*-m-*-iso8859-1=$JRE_LIB_FONTS/LucidaTypewriterBold.ttf +filename.-b&h-lucidatypewriter-medium-i-normal-sans-*-%d-*-*-m-*-iso8859-1=$JRE_LIB_FONTS/LucidaTypewriterRegular.ttf +filename.-b&h-lucidatypewriter-bold-i-normal-sans-*-%d-*-*-m-*-iso8859-1=$JRE_LIB_FONTS/LucidaTypewriterBold.ttf + +filename.-tlc-songti-medium-r-normal--*-%d-*-*-c-*-big5-0=/usr/share/fonts/zh_TW/TrueType/bsmi00lp.ttf +filename.-tlc-songti-medium-r-normal--*-%d-*-*-c-*-iso10646-1=/usr/share/fonts/zh_CN/TrueType/dwstzk.ttf +filename.-tlc-songti-medium-r-normal--*-%d-*-*-c-*-gbk-0=/usr/share/fonts/zh_CN/TrueType/dwstzk.ttf +filename.-tlc-songti-medium-r-normal--*-%d-*-*-c-*-gb2312.1980-0=/usr/share/fonts/zh_CN/TrueType/dwstzk.ttf +filename.-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0=/usr/share/fonts/ja/TrueType/tlgothic.ttc +filename.-ricoh-gothic-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0=/usr/share/fonts/ja/TrueType/tlgothic.ttc +filename.-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0201.1976-0=/usr/share/fonts/ja/TrueType/tlmincho.ttc +filename.-ricoh-mincho-medium-r-normal--*-%d-*-*-c-*-jisx0208.1983-0=/usr/share/fonts/ja/TrueType/tlmincho.ttc + + +# AWT X11 font paths +awtfontpath.chinese-big5=/usr/share/fonts/zh_TW/TrueType +awtfontpath.chinese-gb2312=/usr/share/fonts/zh_CN/TrueType +awtfontpath.chinese-gbk=/usr/share/fonts/zh_CN/TrueType +awtfontpath.chinese-iso10646=/usr/share/fonts/zh_CN/TrueType +awtfontpath.japanese-x0201=/usr/share/fonts/ja/TrueType +awtfontpath.japanese-x0208=/usr/share/fonts/ja/TrueType diff --git a/FCL/src/main/assets/java/lib/fontconfig.bfc b/FCL/src/main/assets/java/lib/fontconfig.bfc new file mode 100644 index 00000000..71cd4d32 Binary files /dev/null and b/FCL/src/main/assets/java/lib/fontconfig.bfc differ diff --git a/FCL/src/main/assets/java/lib/fontconfig.properties b/FCL/src/main/assets/java/lib/fontconfig.properties new file mode 100644 index 00000000..7d339ded --- /dev/null +++ b/FCL/src/main/assets/java/lib/fontconfig.properties @@ -0,0 +1,4 @@ +version=1 +allfonts.chinese-arphic1=-microsoft-microsoft yahei-medium-r-normal--0-0-0-0-p-0-iso10646-1 +sequence.allfonts=default +sequence.fallback=chinese-arphic1 \ No newline at end of file diff --git a/FCL/src/main/assets/java/lib/fontconfig.properties.src b/FCL/src/main/assets/java/lib/fontconfig.properties.src new file mode 100644 index 00000000..1cb4b471 --- /dev/null +++ b/FCL/src/main/assets/java/lib/fontconfig.properties.src @@ -0,0 +1,46 @@ +# +# Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. +# + +# Version + +version=1 + +# Component Font Mappings + + +serif.plain.latin-1=-b&h-lucidabright-medium-r-normal--*-%d-*-*-p-*-iso8859-1 +serif.bold.latin-1=-b&h-lucidabright-demibold-r-normal--*-%d-*-*-p-*-iso8859-1 +serif.italic.latin-1=-b&h-lucidabright-medium-i-normal--*-%d-*-*-p-*-iso8859-1 +serif.bolditalic.latin-1=-b&h-lucidabright-demibold-i-normal--*-%d-*-*-p-*-iso8859-1 + +sansserif.plain.latin-1=-b&h-lucidasans-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1 +sansserif.bold.latin-1=-b&h-lucidasans-bold-r-normal-sans-*-%d-*-*-p-*-iso8859-1 +sansserif.italic.latin-1=-b&h-lucidasans-medium-i-normal-sans-*-%d-*-*-p-*-iso8859-1 +sansserif.bolditalic.latin-1=-b&h-lucidasans-bold-i-normal-sans-*-%d-*-*-p-*-iso8859-1 + +monospaced.plain.latin-1=-b&h-lucidatypewriter-medium-r-normal-sans-*-%d-*-*-m-*-iso8859-1 +monospaced.bold.latin-1=-b&h-lucidatypewriter-bold-r-normal-sans-*-%d-*-*-m-*-iso8859-1 +monospaced.italic.latin-1=-b&h-lucidatypewriter-medium-i-normal-sans-*-%d-*-*-m-*-iso8859-1 +monospaced.bolditalic.latin-1=-b&h-lucidatypewriter-bold-i-normal-sans-*-%d-*-*-m-*-iso8859-1 + +dialog.plain.latin-1=-b&h-lucidasans-medium-r-normal-sans-*-%d-*-*-p-*-iso8859-1 +dialog.bold.latin-1=-b&h-lucidasans-bold-r-normal-sans-*-%d-*-*-p-*-iso8859-1 +dialog.italic.latin-1=-b&h-lucidasans-medium-i-normal-sans-*-%d-*-*-p-*-iso8859-1 +dialog.bolditalic.latin-1=-b&h-lucidasans-bold-i-normal-sans-*-%d-*-*-p-*-iso8859-1 + +dialoginput.plain.latin-1=-b&h-lucidatypewriter-medium-r-normal-sans-*-%d-*-*-m-*-iso8859-1 +dialoginput.bold.latin-1=-b&h-lucidatypewriter-bold-r-normal-sans-*-%d-*-*-m-*-iso8859-1 +dialoginput.italic.latin-1=-b&h-lucidatypewriter-medium-i-normal-sans-*-%d-*-*-m-*-iso8859-1 +dialoginput.bolditalic.latin-1=-b&h-lucidatypewriter-bold-i-normal-sans-*-%d-*-*-m-*-iso8859-1 + +# Search Sequences + +sequence.allfonts=latin-1 + +# Exclusion Ranges + +# Font File Names + + + diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaBrightDemiBold.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaBrightDemiBold.ttf new file mode 100644 index 00000000..8073c35e Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaBrightDemiBold.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaBrightDemiItalic.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaBrightDemiItalic.ttf new file mode 100644 index 00000000..bae8c67b Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaBrightDemiItalic.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaBrightItalic.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaBrightItalic.ttf new file mode 100644 index 00000000..c26afa3b Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaBrightItalic.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaBrightRegular.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaBrightRegular.ttf new file mode 100644 index 00000000..79738f19 Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaBrightRegular.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaSansDemiBold.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaSansDemiBold.ttf new file mode 100644 index 00000000..a15910e1 Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaSansDemiBold.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaSansRegular.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaSansRegular.ttf new file mode 100644 index 00000000..4cabe6e7 Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaSansRegular.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaTypewriterBold.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaTypewriterBold.ttf new file mode 100644 index 00000000..ac66d4f2 Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaTypewriterBold.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/LucidaTypewriterRegular.ttf b/FCL/src/main/assets/java/lib/fonts/LucidaTypewriterRegular.ttf new file mode 100644 index 00000000..02951842 Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/LucidaTypewriterRegular.ttf differ diff --git a/FCL/src/main/assets/java/lib/fonts/fonts.dir b/FCL/src/main/assets/java/lib/fonts/fonts.dir new file mode 100644 index 00000000..b4e5355c --- /dev/null +++ b/FCL/src/main/assets/java/lib/fonts/fonts.dir @@ -0,0 +1,50 @@ +49 +LucidaBrightRegular.ttf -b&h-lucidabright-medium-r-normal--0-0-0-0-p-0-iso8859-1 +LucidaBrightItalic.ttf -b&h-lucidabright-medium-i-normal--0-0-0-0-p-0-iso8859-1 +LucidaBrightDemiBold.ttf -b&h-lucidabright-demibold-r-normal--0-0-0-0-p-0-iso8859-1 +LucidaBrightDemiItalic.ttf -b&h-lucidabright-demibold-i-normal--0-0-0-0-p-0-iso8859-1 +LucidaSansRegular.ttf -b&h-lucidasans-medium-r-normal-sans-0-0-0-0-p-0-iso8859-1 +LucidaSansDemiBold.ttf -b&h-lucidasans-bold-r-normal-sans-0-0-0-0-p-0-iso8859-1 +LucidaTypewriterRegular.ttf -b&h-lucidatypewriter-medium-r-normal-sans-0-0-0-0-m-0-iso8859-1 +LucidaTypewriterBold.ttf -b&h-lucidatypewriter-bold-r-normal-sans-0-0-0-0-m-0-iso8859-1 +LucidaBrightRegular.ttf -b&h-lucidabright-medium-r-normal--0-0-0-0-p-0-iso8859-2 +LucidaBrightItalic.ttf -b&h-lucidabright-medium-i-normal--0-0-0-0-p-0-iso8859-2 +LucidaBrightDemiBold.ttf -b&h-lucidabright-demibold-r-normal--0-0-0-0-p-0-iso8859-2 +LucidaBrightDemiItalic.ttf -b&h-lucidabright-demibold-i-normal--0-0-0-0-p-0-iso8859-2 +LucidaSansRegular.ttf -b&h-lucidasans-medium-r-normal-sans-0-0-0-0-p-0-iso8859-2 +LucidaSansDemiBold.ttf -b&h-lucidasans-bold-r-normal-sans-0-0-0-0-p-0-iso8859-2 +LucidaTypewriterRegular.ttf -b&h-lucidatypewriter-medium-r-normal-sans-0-0-0-0-m-0-iso8859-2 +LucidaTypewriterBold.ttf -b&h-lucidatypewriter-bold-r-normal-sans-0-0-0-0-m-0-iso8859-2 +LucidaBrightRegular.ttf -b&h-lucidabright-medium-r-normal--0-0-0-0-p-0-iso8859-4 +LucidaBrightItalic.ttf -b&h-lucidabright-medium-i-normal--0-0-0-0-p-0-iso8859-4 +LucidaBrightDemiBold.ttf -b&h-lucidabright-demibold-r-normal--0-0-0-0-p-0-iso8859-4 +LucidaBrightDemiItalic.ttf -b&h-lucidabright-demibold-i-normal--0-0-0-0-p-0-iso8859-4 +LucidaSansRegular.ttf -b&h-lucidasans-medium-r-normal-sans-0-0-0-0-p-0-iso8859-4 +LucidaSansDemiBold.ttf -b&h-lucidasans-bold-r-normal-sans-0-0-0-0-p-0-iso8859-4 +LucidaTypewriterRegular.ttf -b&h-lucidatypewriter-medium-r-normal-sans-0-0-0-0-m-0-iso8859-4 +LucidaTypewriterBold.ttf -b&h-lucidatypewriter-bold-r-normal-sans-0-0-0-0-m-0-iso8859-4 +LucidaBrightRegular.ttf -b&h-lucidabright-medium-r-normal--0-0-0-0-p-0-iso8859-5 +LucidaBrightItalic.ttf -b&h-lucidabright-medium-i-normal--0-0-0-0-p-0-iso8859-5 +LucidaBrightDemiBold.ttf -b&h-lucidabright-demibold-r-normal--0-0-0-0-p-0-iso8859-5 +LucidaBrightDemiItalic.ttf -b&h-lucidabright-demibold-i-normal--0-0-0-0-p-0-iso8859-5 +LucidaSansRegular.ttf -b&h-lucidasans-medium-r-normal-sans-0-0-0-0-p-0-iso8859-5 +LucidaSansDemiBold.ttf -b&h-lucidasans-bold-r-normal-sans-0-0-0-0-p-0-iso8859-5 +LucidaTypewriterRegular.ttf -b&h-lucidatypewriter-medium-r-normal-sans-0-0-0-0-m-0-iso8859-5 +LucidaTypewriterBold.ttf -b&h-lucidatypewriter-bold-r-normal-sans-0-0-0-0-m-0-iso8859-5 +LucidaBrightRegular.ttf -b&h-lucidabright-medium-r-normal--0-0-0-0-p-0-iso8859-7 +LucidaBrightItalic.ttf -b&h-lucidabright-medium-i-normal--0-0-0-0-p-0-iso8859-7 +LucidaBrightDemiBold.ttf -b&h-lucidabright-demibold-r-normal--0-0-0-0-p-0-iso8859-7 +LucidaBrightDemiItalic.ttf -b&h-lucidabright-demibold-i-normal--0-0-0-0-p-0-iso8859-7 +LucidaSansRegular.ttf -b&h-lucidasans-medium-r-normal-sans-0-0-0-0-p-0-iso8859-7 +LucidaSansDemiBold.ttf -b&h-lucidasans-bold-r-normal-sans-0-0-0-0-p-0-iso8859-7 +LucidaTypewriterRegular.ttf -b&h-lucidatypewriter-medium-r-normal-sans-0-0-0-0-m-0-iso8859-7 +LucidaTypewriterBold.ttf -b&h-lucidatypewriter-bold-r-normal-sans-0-0-0-0-m-0-iso8859-7 +LucidaBrightRegular.ttf -b&h-lucidabright-medium-r-normal--0-0-0-0-p-0-iso8859-9 +LucidaBrightItalic.ttf -b&h-lucidabright-medium-i-normal--0-0-0-0-p-0-iso8859-9 +LucidaBrightDemiBold.ttf -b&h-lucidabright-demibold-r-normal--0-0-0-0-p-0-iso8859-9 +LucidaBrightDemiItalic.ttf -b&h-lucidabright-demibold-i-normal--0-0-0-0-p-0-iso8859-9 +LucidaSansRegular.ttf -b&h-lucidasans-medium-r-normal-sans-0-0-0-0-p-0-iso8859-9 +LucidaSansDemiBold.ttf -b&h-lucidasans-bold-r-normal-sans-0-0-0-0-p-0-iso8859-9 +LucidaTypewriterRegular.ttf -b&h-lucidatypewriter-medium-r-normal-sans-0-0-0-0-m-0-iso8859-9 +LucidaTypewriterBold.ttf -b&h-lucidatypewriter-bold-r-normal-sans-0-0-0-0-m-0-iso8859-9 +微软雅黑.ttf -microsoft-microsoft yahei-medium-r-normal--0-0-0-0-p-0-iso10646-1 diff --git a/FCL/src/main/assets/java/lib/fonts/微软雅黑.ttf b/FCL/src/main/assets/java/lib/fonts/微软雅黑.ttf new file mode 100644 index 00000000..aa23ae1f Binary files /dev/null and b/FCL/src/main/assets/java/lib/fonts/微软雅黑.ttf differ diff --git a/FCL/src/main/assets/java/lib/hijrah-config-umalqura.properties b/FCL/src/main/assets/java/lib/hijrah-config-umalqura.properties new file mode 100644 index 00000000..1786c14d --- /dev/null +++ b/FCL/src/main/assets/java/lib/hijrah-config-umalqura.properties @@ -0,0 +1,369 @@ +# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# This properties file defines a Hijrah calendar variant. +# +# Fields: +# +# ::= 'version' '=' +# ::= 'id' '=' +# ::= 'type' '=' +# ::= 'iso-start' '=' +# ::= '=' +# +# version ... (Required) +# +# id ... (Required) +# Identifies the Java Chronology +# +# type ... (Required) +# Identifies the type of calendar in the standard calendar ID scheme +# iso-start ... (Required) +# Specifies the corresponding ISO date to the first Hijrah day +# in the defined range of dates +# +# year ... (Required) +# Number of days for each month of a Hijrah year +# * Each line defines a year. The years must be in chronological +# order and no gap is allowed. +# * Each line is in the form indicated above. is a Hijrah year and +# nn is the number of days for a month listed in the order of the months. +# * Each year must have 12 months. +# * Each month should be 29 or 30 days long. +# * There must be one or more space characters between the months. +# + +# Version of this definition +version=1.8.0_1 + +# Java chronology ID +id=Hijrah-umalqura + +# Standard calendar type specification +type=islamic-umalqura + +# defines the corresponding ISO date to the earliest Hijrah date +iso-start=1882-11-12 + +# 1 2 3 4 5 6 7 8 9 10 11 12 +1300=30 29 30 29 30 29 30 29 30 29 30 29 +1301=30 30 29 30 29 30 29 30 29 30 29 29 +1302=30 30 30 29 30 30 29 29 30 29 29 30 +1303=29 30 30 29 30 30 29 30 29 30 29 29 +1304=29 30 30 29 30 30 30 29 30 29 30 29 +1305=29 29 30 30 29 30 30 29 30 30 29 29 +1306=30 29 30 29 30 29 30 29 30 30 29 30 +1307=29 30 29 30 29 30 29 30 29 30 29 30 +1308=29 30 30 29 30 29 30 29 30 29 29 30 +1309=29 30 30 30 30 29 29 30 29 29 30 29 +1310=30 29 30 30 30 29 30 29 30 29 29 30 +1311=29 30 29 30 30 30 29 30 29 30 29 29 +1312=30 29 30 29 30 30 29 30 30 29 30 29 +1313=29 30 29 30 29 30 29 30 30 30 29 29 +1314=30 30 29 30 29 29 30 29 30 30 29 30 +1315=29 30 30 29 30 29 29 30 29 30 29 30 +1316=29 30 30 30 29 30 29 29 30 29 30 29 +1317=30 29 30 30 29 30 29 30 29 30 29 29 +1318=30 29 30 30 29 30 30 29 30 29 30 29 +1319=29 30 29 30 30 29 30 29 30 30 29 30 +1320=29 30 29 29 30 29 30 29 30 30 30 29 +1321=30 29 30 29 29 30 29 29 30 30 30 30 +1322=29 30 29 30 29 29 29 30 29 30 30 30 +1323=29 30 30 29 30 29 29 29 30 29 30 30 +1324=29 30 30 29 30 29 30 29 29 30 29 30 +1325=30 29 30 29 30 30 29 30 29 30 29 30 +1326=29 29 30 29 30 30 29 30 29 30 30 29 +1327=30 29 29 30 29 30 29 30 30 29 30 30 +1328=29 30 29 29 30 29 29 30 30 30 29 30 +1329=30 29 30 29 29 30 29 29 30 30 29 30 +1330=30 30 29 30 29 29 30 29 29 30 30 29 +1331=30 30 29 30 30 29 29 30 29 30 29 30 +1332=29 30 29 30 30 29 30 29 30 30 29 29 +1333=30 29 29 30 30 29 30 30 29 30 30 29 +1334=29 29 30 29 30 29 30 30 30 29 30 29 +1335=30 29 30 29 29 30 29 30 30 29 30 30 +1336=29 30 29 30 29 29 30 29 30 29 30 30 +1337=30 29 30 29 30 29 29 30 29 30 29 30 +1338=29 30 30 29 30 30 29 29 30 29 30 29 +1339=30 29 30 29 30 30 30 29 30 29 29 30 +1340=29 29 30 29 30 30 30 30 29 30 29 29 +1341=30 29 29 30 29 30 30 30 29 30 30 29 +1342=29 29 30 29 30 29 30 30 29 30 30 29 +1343=30 29 29 30 29 30 29 30 29 30 30 29 +1344=30 29 30 29 30 30 29 29 30 29 30 29 +1345=30 29 30 30 30 29 30 29 29 30 29 29 +1346=30 29 30 30 30 30 29 30 29 29 30 29 +1347=29 30 29 30 30 30 29 30 30 29 29 30 +1348=29 29 30 29 30 30 29 30 30 30 29 29 +1349=30 29 29 30 29 30 30 29 30 30 29 30 +1350=29 30 29 30 29 30 29 29 30 30 29 30 +1351=30 29 30 29 30 29 30 29 29 30 29 30 +1352=30 29 30 30 29 30 29 30 29 29 30 29 +1353=30 29 30 30 30 29 30 29 29 30 29 30 +1354=29 30 29 30 30 29 30 30 29 30 29 29 +1355=30 29 29 30 30 29 30 30 29 30 30 29 +1356=29 30 29 30 29 30 29 30 29 30 30 30 +1357=29 29 30 29 30 29 29 30 29 30 30 30 +1358=29 30 29 30 29 30 29 29 30 29 30 30 +1359=29 30 30 29 30 29 30 29 29 29 30 30 +1360=29 30 30 30 29 30 29 30 29 29 30 29 +1361=30 29 30 30 29 30 30 29 29 30 29 30 +1362=29 30 29 30 29 30 30 29 30 29 30 29 +1363=30 29 30 29 30 29 30 29 30 29 30 30 +1364=29 30 29 30 29 29 30 29 30 29 30 30 +1365=30 30 29 29 30 29 29 30 29 30 29 30 +1366=30 30 29 30 29 30 29 29 30 29 30 29 +1367=30 30 29 30 30 29 30 29 29 30 29 30 +1368=29 30 29 30 30 30 29 29 30 29 30 29 +1369=30 29 30 29 30 30 29 30 29 30 30 29 +1370=30 29 29 30 29 30 29 30 29 30 30 30 +1371=29 30 29 29 30 29 30 29 30 29 30 30 +1372=30 29 29 30 29 30 29 29 30 29 30 30 +1373=30 29 30 29 30 29 30 29 29 30 29 30 +1374=30 29 30 30 29 30 29 30 29 29 30 29 +1375=30 29 30 30 29 30 30 29 30 29 30 29 +1376=29 30 29 30 29 30 30 30 29 30 29 30 +1377=29 29 30 29 29 30 30 30 29 30 30 29 +1378=30 29 29 29 30 29 30 30 29 30 30 30 +1379=29 30 29 29 29 30 29 30 30 29 30 30 +1380=29 30 29 30 29 30 29 30 29 30 29 30 +1381=29 30 29 30 30 29 30 29 30 29 29 30 +1382=29 30 29 30 30 29 30 30 29 30 29 29 +1383=30 29 29 30 30 30 29 30 30 29 30 29 +1384=29 30 29 29 30 30 29 30 30 30 29 30 +1385=29 29 30 29 29 30 30 29 30 30 30 29 +1386=30 29 29 30 29 29 30 30 29 30 30 29 +1387=30 29 30 29 30 29 30 29 30 29 30 29 +1388=30 30 29 30 29 30 29 30 29 30 29 29 +1389=30 30 29 30 30 29 30 30 29 29 30 29 +1390=29 30 29 30 30 30 29 30 29 30 29 30 +1391=29 29 30 29 30 30 29 30 30 29 30 29 +1392=30 29 29 30 29 30 29 30 30 29 30 30 +1393=29 30 29 29 30 29 30 29 30 29 30 30 +1394=30 29 30 29 29 30 29 30 29 30 29 30 +1395=30 29 30 30 29 30 29 29 30 29 29 30 +1396=30 29 30 30 29 30 30 29 29 30 29 29 +1397=30 29 30 30 29 30 30 30 29 29 29 30 +1398=29 30 29 30 30 29 30 30 29 30 29 29 +1399=30 29 30 29 30 29 30 30 29 30 29 30 +1400=30 29 30 29 29 30 29 30 29 30 29 30 +1401=30 30 29 30 29 29 30 29 29 30 29 30 +1402=30 30 30 29 30 29 29 30 29 29 30 29 +1403=30 30 30 29 30 30 29 29 30 29 29 30 +1404=29 30 30 29 30 30 29 30 29 30 29 29 +1405=30 29 30 29 30 30 30 29 30 29 29 30 +1406=30 29 29 30 29 30 30 29 30 29 30 30 +1407=29 30 29 29 30 29 30 29 30 29 30 30 +1408=30 29 30 29 30 29 29 30 29 29 30 30 +1409=30 30 29 30 29 30 29 29 30 29 29 30 +1410=30 30 29 30 30 29 30 29 29 30 29 29 +1411=30 30 29 30 30 29 30 30 29 29 30 29 +1412=30 29 30 29 30 29 30 30 30 29 29 30 +1413=29 30 29 29 30 29 30 30 30 29 30 29 +1414=30 29 30 29 29 30 29 30 30 29 30 30 +1415=29 30 29 30 29 29 30 29 30 29 30 30 +1416=30 29 30 29 30 29 29 30 29 30 29 30 +1417=30 29 30 30 29 29 30 29 30 29 30 29 +1418=30 29 30 30 29 30 29 30 29 30 29 30 +1419=29 30 29 30 29 30 29 30 30 30 29 29 +1420=29 30 29 29 30 29 30 30 30 30 29 30 +1421=29 29 30 29 29 29 30 30 30 30 29 30 +1422=30 29 29 30 29 29 29 30 30 30 29 30 +1423=30 29 30 29 30 29 29 30 29 30 29 30 +1424=30 29 30 30 29 30 29 29 30 29 30 29 +1425=30 29 30 30 29 30 29 30 30 29 30 29 +1426=29 30 29 30 29 30 30 29 30 30 29 30 +1427=29 29 30 29 30 29 30 30 29 30 30 29 +1428=30 29 29 30 29 29 30 30 30 29 30 30 +1429=29 30 29 29 30 29 29 30 30 29 30 30 +1430=29 30 30 29 29 30 29 30 29 30 29 30 +1431=29 30 30 29 30 29 30 29 30 29 29 30 +1432=29 30 30 30 29 30 29 30 29 30 29 29 +1433=30 29 30 30 29 30 30 29 30 29 30 29 +1434=29 30 29 30 29 30 30 29 30 30 29 29 +1435=30 29 30 29 30 29 30 29 30 30 29 30 +1436=29 30 29 30 29 30 29 30 29 30 29 30 +1437=30 29 30 30 29 29 30 29 30 29 29 30 +1438=30 29 30 30 30 29 29 30 29 29 30 29 +1439=30 29 30 30 30 29 30 29 30 29 29 30 +1440=29 30 29 30 30 30 29 30 29 30 29 29 +1441=30 29 30 29 30 30 29 30 30 29 30 29 +1442=29 30 29 30 29 30 29 30 30 29 30 29 +1443=30 29 30 29 30 29 30 29 30 29 30 30 +1444=29 30 29 30 30 29 29 30 29 30 29 30 +1445=29 30 30 30 29 30 29 29 30 29 29 30 +1446=29 30 30 30 29 30 30 29 29 30 29 29 +1447=30 29 30 30 30 29 30 29 30 29 30 29 +1448=29 30 29 30 30 29 30 30 29 30 29 30 +1449=29 29 30 29 30 29 30 30 29 30 30 29 +1450=30 29 30 29 29 30 29 30 29 30 30 29 +1451=30 30 30 29 29 30 29 29 30 30 29 30 +1452=30 29 30 30 29 29 30 29 29 30 29 30 +1453=30 29 30 30 29 30 29 30 29 29 30 29 +1454=30 29 30 30 29 30 30 29 30 29 30 29 +1455=29 30 29 30 30 29 30 29 30 30 29 30 +1456=29 29 30 29 30 29 30 29 30 30 30 29 +1457=30 29 29 30 29 29 30 29 30 30 30 30 +1458=29 30 29 29 30 29 29 30 29 30 30 30 +1459=29 30 30 29 29 30 29 29 30 29 30 30 +1460=29 30 30 29 30 29 30 29 29 30 29 30 +1461=29 30 30 29 30 29 30 29 30 30 29 29 +1462=30 29 30 29 30 30 29 30 29 30 30 29 +1463=29 30 29 30 29 30 29 30 30 30 29 30 +1464=29 30 29 29 30 29 29 30 30 30 29 30 +1465=30 29 30 29 29 30 29 29 30 30 29 30 +1466=30 30 29 30 29 29 29 30 29 30 30 29 +1467=30 30 29 30 30 29 29 30 29 30 29 30 +1468=29 30 29 30 30 29 30 29 30 29 30 29 +1469=29 30 29 30 30 29 30 30 29 30 29 30 +1470=29 29 30 29 30 30 29 30 30 29 30 29 +1471=30 29 29 30 29 30 29 30 30 29 30 30 +1472=29 30 29 29 30 29 30 29 30 30 29 30 +1473=29 30 29 30 30 29 29 30 29 30 29 30 +1474=29 30 30 29 30 30 29 29 30 29 30 29 +1475=29 30 30 29 30 30 30 29 29 30 29 29 +1476=30 29 30 29 30 30 30 29 30 29 30 29 +1477=29 30 29 29 30 30 30 30 29 30 29 30 +1478=29 29 30 29 30 29 30 30 29 30 30 29 +1479=30 29 29 30 29 30 29 30 29 30 30 29 +1480=30 29 30 29 30 29 30 29 30 29 30 29 +1481=30 29 30 30 29 30 29 30 29 30 29 29 +1482=30 29 30 30 30 30 29 30 29 29 30 29 +1483=29 30 29 30 30 30 29 30 30 29 29 30 +1484=29 29 30 29 30 30 30 29 30 29 30 29 +1485=30 29 29 30 29 30 30 29 30 30 29 30 +1486=29 30 29 29 30 29 30 29 30 30 29 30 +1487=30 29 30 29 30 29 29 30 29 30 29 30 +1488=30 29 30 30 29 30 29 29 30 29 30 29 +1489=30 29 30 30 30 29 30 29 29 30 29 30 +1490=29 30 29 30 30 29 30 30 29 29 30 29 +1491=30 29 29 30 30 29 30 30 29 30 29 30 +1492=29 30 29 29 30 30 29 30 29 30 30 29 +1493=30 29 30 29 30 29 29 30 29 30 30 30 +1494=29 30 29 30 29 30 29 29 29 30 30 30 +1495=29 30 30 29 30 29 29 30 29 29 30 30 +1496=29 30 30 30 29 30 29 29 30 29 29 30 +1497=30 29 30 30 29 30 29 30 29 30 29 30 +1498=29 30 29 30 29 30 30 29 30 29 30 29 +1499=30 29 30 29 29 30 30 29 30 29 30 30 +1500=29 30 29 30 29 29 30 29 30 29 30 30 +1501=30 29 30 29 30 29 29 29 30 29 30 30 +1502=30 30 29 30 29 30 29 29 29 30 30 29 +1503=30 30 29 30 30 29 30 29 29 29 30 30 +1504=29 30 29 30 30 30 29 29 30 29 30 29 +1505=30 29 30 29 30 30 29 30 29 30 30 29 +1506=29 30 29 29 30 30 29 30 30 29 30 30 +1507=29 29 30 29 29 30 30 29 30 29 30 30 +1508=30 29 29 30 29 30 29 29 30 29 30 30 +1509=30 29 30 29 30 29 30 29 29 30 29 30 +1510=30 29 30 30 29 30 29 30 29 29 30 29 +1511=30 29 30 30 29 30 30 29 30 29 29 30 +1512=29 30 29 30 29 30 30 30 29 30 29 30 +1513=29 29 29 30 29 30 30 30 29 30 30 29 +1514=30 29 29 29 30 29 30 30 29 30 30 30 +1515=29 29 30 29 29 30 29 30 30 29 30 30 +1516=29 30 29 30 29 29 30 29 30 29 30 30 +1517=29 30 29 30 29 30 30 29 29 30 29 30 +1518=29 30 29 30 30 29 30 30 29 30 29 29 +1519=30 29 29 30 30 30 29 30 30 29 30 29 +1520=29 30 29 29 30 30 30 29 30 30 29 30 +1521=29 29 29 30 29 30 30 29 30 30 29 30 +1522=30 29 29 29 30 29 30 30 29 30 30 29 +1523=30 29 30 29 30 29 30 29 29 30 30 29 +1524=30 30 29 30 29 30 29 30 29 29 30 29 +1525=30 30 29 30 30 29 30 29 30 29 29 30 +1526=29 30 29 30 30 30 29 30 29 30 29 29 +1527=30 29 30 29 30 30 29 30 30 29 30 29 +1528=30 29 29 30 29 30 29 30 30 29 30 30 +1529=29 30 29 29 30 29 30 29 30 29 30 30 +1530=29 30 30 29 29 30 29 30 29 29 30 30 +1531=29 30 30 30 29 29 30 29 30 29 29 30 +1532=29 30 30 30 29 30 30 29 29 29 30 29 +1533=30 29 30 30 30 29 30 29 30 29 29 30 +1534=29 30 29 30 30 29 30 30 29 29 30 29 +1535=30 29 30 29 30 29 30 30 29 30 29 30 +1536=29 30 29 30 29 30 29 30 29 30 29 30 +1537=30 29 30 30 29 29 30 29 29 30 29 30 +1538=30 30 29 30 30 29 29 30 29 29 30 29 +1539=30 30 30 29 30 30 29 29 30 29 29 30 +1540=29 30 30 29 30 30 29 30 29 29 30 29 +1541=30 29 30 29 30 30 30 29 30 29 29 30 +1542=29 30 29 30 29 30 30 29 30 29 30 30 +1543=29 30 29 29 30 29 30 29 30 29 30 30 +1544=30 29 30 29 29 30 29 30 29 30 29 30 +1545=30 30 29 30 29 29 30 29 30 29 29 30 +1546=30 30 29 30 29 30 29 30 29 30 29 29 +1547=30 30 29 30 30 29 30 29 30 29 30 29 +1548=30 29 29 30 30 29 30 30 29 30 29 30 +1549=29 30 29 29 30 29 30 30 30 29 30 29 +1550=30 29 30 29 29 29 30 30 30 29 30 30 +1551=29 30 29 29 30 29 29 30 30 29 30 30 +1552=30 29 30 29 29 30 29 29 30 30 29 30 +1553=30 29 30 29 30 29 30 29 30 29 30 29 +1554=30 29 30 29 30 30 29 30 29 30 29 30 +1555=29 29 30 29 30 30 29 30 30 29 30 29 +1556=30 29 29 30 29 30 29 30 30 30 29 30 +1557=29 30 29 29 29 30 29 30 30 30 30 29 +1558=30 29 30 29 29 29 30 29 30 30 30 29 +1559=30 30 29 29 30 29 29 30 30 29 30 29 +1560=30 30 29 30 29 30 29 30 29 30 29 30 +1561=29 30 30 29 30 29 30 30 29 29 30 29 +1562=29 30 30 29 30 29 30 30 30 29 29 30 +1563=29 30 29 29 30 29 30 30 30 29 30 29 +1564=30 29 30 29 29 30 29 30 30 30 29 30 +1565=29 30 29 30 29 29 30 29 30 30 29 30 +1566=30 29 30 29 30 29 29 30 29 30 29 30 +1567=30 29 30 30 29 30 29 30 29 29 30 29 +1568=30 29 30 30 30 29 30 29 30 29 29 29 +1569=30 29 30 30 30 29 30 30 29 30 29 29 +1570=29 30 29 30 30 29 30 30 30 29 29 30 +1571=29 29 30 29 30 30 29 30 30 29 30 29 +1572=30 29 29 30 29 30 29 30 30 29 30 29 +1573=30 29 30 30 29 30 29 29 30 29 30 29 +1574=30 30 29 30 30 29 30 29 29 30 29 29 +1575=30 30 30 29 30 30 29 30 29 29 29 30 +1576=29 30 30 29 30 30 30 29 30 29 29 29 +1577=30 29 30 30 29 30 30 29 30 29 30 29 +1578=29 30 29 30 29 30 30 29 30 30 29 30 +1579=29 30 29 30 29 29 30 30 29 30 29 30 +1580=29 30 30 29 30 29 29 30 29 30 29 30 +1581=30 30 29 30 29 30 29 29 30 29 30 29 +1582=30 30 29 30 30 29 30 29 30 29 29 29 +1583=30 30 29 30 30 30 29 30 29 30 29 29 +1584=29 30 30 29 30 30 29 30 30 29 30 29 +1585=29 30 29 30 29 30 29 30 30 29 30 30 +1586=29 29 30 29 30 29 29 30 30 30 29 30 +1587=29 30 30 29 29 29 30 29 30 29 30 30 +1588=30 29 30 30 29 29 29 30 29 30 29 30 +1589=30 29 30 30 29 30 29 29 30 29 30 29 +1590=30 29 30 30 30 29 29 30 29 30 29 30 +1591=29 30 29 30 30 29 30 29 30 29 30 29 +1592=30 29 30 29 30 29 30 29 30 30 30 29 +1593=30 29 29 30 29 29 30 29 30 30 30 29 +1594=30 30 29 29 30 29 29 29 30 30 30 30 +1595=29 30 29 30 29 29 30 29 29 30 30 30 +1596=29 30 30 29 30 29 29 30 29 30 29 30 +1597=29 30 30 29 30 29 30 29 30 29 30 29 +1598=30 29 30 29 30 30 29 30 29 30 30 29 +1599=29 30 29 30 29 30 29 30 30 30 29 30 +1600=29 29 30 29 30 29 29 30 30 30 29 30 diff --git a/FCL/src/main/assets/java/lib/images/cursors/cursors.properties b/FCL/src/main/assets/java/lib/images/cursors/cursors.properties new file mode 100644 index 00000000..30e9f903 --- /dev/null +++ b/FCL/src/main/assets/java/lib/images/cursors/cursors.properties @@ -0,0 +1,40 @@ +# +# +# Cursors Properties file +# +# Names GIF89 sources for Custom Cursors and their associated HotSpots +# +# Note: the syntax of the property name is significant and is parsed +# by java.awt.Cursor +# +# The syntax is: Cursor...File= +# Cursor...HotSpot=, +# Cursor...Name= +# +Cursor.CopyDrop.32x32.File=motif_CopyDrop32x32.gif +Cursor.CopyDrop.32x32.HotSpot=0,0 +Cursor.CopyDrop.32x32.Name=CopyDrop32x32 +# +Cursor.MoveDrop.32x32.File=motif_MoveDrop32x32.gif +Cursor.MoveDrop.32x32.HotSpot=0,0 +Cursor.MoveDrop.32x32.Name=MoveDrop32x32 +# +Cursor.LinkDrop.32x32.File=motif_LinkDrop32x32.gif +Cursor.LinkDrop.32x32.HotSpot=0,0 +Cursor.LinkDrop.32x32.Name=LinkDrop32x32 +# +Cursor.CopyNoDrop.32x32.File=motif_CopyNoDrop32x32.gif +Cursor.CopyNoDrop.32x32.HotSpot=6,2 +Cursor.CopyNoDrop.32x32.Name=CopyNoDrop32x32 +# +Cursor.MoveNoDrop.32x32.File=motif_MoveNoDrop32x32.gif +Cursor.MoveNoDrop.32x32.HotSpot=6,2 +Cursor.MoveNoDrop.32x32.Name=MoveNoDrop32x32 +# +Cursor.LinkNoDrop.32x32.File=motif_LinkNoDrop32x32.gif +Cursor.LinkNoDrop.32x32.HotSpot=6,2 +Cursor.LinkNoDrop.32x32.Name=LinkNoDrop32x32 +# +Cursor.Invalid.32x32.File=invalid32x32.gif +Cursor.Invalid.32x32.HotSpot=6,2 +Cursor.Invalid.32x32.Name=Invalid32x32 diff --git a/FCL/src/main/assets/java/lib/images/cursors/invalid32x32.gif b/FCL/src/main/assets/java/lib/images/cursors/invalid32x32.gif new file mode 100644 index 00000000..64c265d3 Binary files /dev/null and b/FCL/src/main/assets/java/lib/images/cursors/invalid32x32.gif differ diff --git a/FCL/src/main/assets/java/lib/images/cursors/motif_CopyDrop32x32.gif b/FCL/src/main/assets/java/lib/images/cursors/motif_CopyDrop32x32.gif new file mode 100644 index 00000000..a9ec2471 Binary files /dev/null and b/FCL/src/main/assets/java/lib/images/cursors/motif_CopyDrop32x32.gif differ diff --git a/FCL/src/main/assets/java/lib/images/cursors/motif_CopyNoDrop32x32.gif b/FCL/src/main/assets/java/lib/images/cursors/motif_CopyNoDrop32x32.gif new file mode 100644 index 00000000..64c265d3 Binary files /dev/null and b/FCL/src/main/assets/java/lib/images/cursors/motif_CopyNoDrop32x32.gif differ diff --git a/FCL/src/main/assets/java/lib/images/cursors/motif_LinkDrop32x32.gif b/FCL/src/main/assets/java/lib/images/cursors/motif_LinkDrop32x32.gif new file mode 100644 index 00000000..10c84c13 Binary files /dev/null and b/FCL/src/main/assets/java/lib/images/cursors/motif_LinkDrop32x32.gif differ diff --git a/FCL/src/main/assets/java/lib/images/cursors/motif_LinkNoDrop32x32.gif b/FCL/src/main/assets/java/lib/images/cursors/motif_LinkNoDrop32x32.gif new file mode 100644 index 00000000..64c265d3 Binary files /dev/null and b/FCL/src/main/assets/java/lib/images/cursors/motif_LinkNoDrop32x32.gif differ diff --git a/FCL/src/main/assets/java/lib/images/cursors/motif_MoveDrop32x32.gif b/FCL/src/main/assets/java/lib/images/cursors/motif_MoveDrop32x32.gif new file mode 100644 index 00000000..8304aa0d Binary files /dev/null and b/FCL/src/main/assets/java/lib/images/cursors/motif_MoveDrop32x32.gif differ diff --git a/FCL/src/main/assets/java/lib/images/cursors/motif_MoveNoDrop32x32.gif b/FCL/src/main/assets/java/lib/images/cursors/motif_MoveNoDrop32x32.gif new file mode 100644 index 00000000..64c265d3 Binary files /dev/null and b/FCL/src/main/assets/java/lib/images/cursors/motif_MoveNoDrop32x32.gif differ diff --git a/FCL/src/main/assets/java/lib/jce.jar b/FCL/src/main/assets/java/lib/jce.jar new file mode 100644 index 00000000..678653c1 Binary files /dev/null and b/FCL/src/main/assets/java/lib/jce.jar differ diff --git a/FCL/src/main/assets/java/lib/jexec b/FCL/src/main/assets/java/lib/jexec new file mode 100644 index 00000000..c5efa299 Binary files /dev/null and b/FCL/src/main/assets/java/lib/jexec differ diff --git a/FCL/src/main/assets/java/lib/jfr.jar b/FCL/src/main/assets/java/lib/jfr.jar new file mode 100644 index 00000000..72c68c07 Binary files /dev/null and b/FCL/src/main/assets/java/lib/jfr.jar differ diff --git a/FCL/src/main/assets/java/lib/jfr/default.jfc b/FCL/src/main/assets/java/lib/jfr/default.jfc new file mode 100644 index 00000000..92b302fe --- /dev/null +++ b/FCL/src/main/assets/java/lib/jfr/default.jfc @@ -0,0 +1,845 @@ + + + + + + + + true + everyChunk + + + + true + 1000 ms + + + + true + everyChunk + + + + true + 1000 ms + + + + true + + + + true + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + false + true + 20 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + + + + false + true + 0 ms + + + + false + true + + + + false + + + + true + beginChunk + + + + true + beginChunk + + + + true + 20 ms + + + + true + 20 ms + + + + true + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + true + 10 ms + + + + true + true + + + + true + everyChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + everyChunk + + + + true + everyChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + false + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + true + + + + true + true + + + + true + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + true + 0 ms + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + + + + false + + + + true + + + + false + true + + + + true + + + + false + everyChunk + + + + false + + + + true + false + 0 ns + + + + true + beginChunk + + + + true + 1000 ms + + + + true + 1000 ms + + + + true + 60 s + + + + false + + + + false + + + + true + beginChunk + + + + true + everyChunk + + + + true + 100 ms + + + + true + beginChunk + + + + true + everyChunk + + + + true + + + + true + beginChunk + + + + true + beginChunk + + + + true + 10 s + + + + true + 1000 ms + + + + true + 10 s + + + + true + beginChunk + + + + true + endChunk + + + + true + 5 s + + + + true + beginChunk + + + + true + everyChunk + + + + false + true + + + + false + true + + + + true + everyChunk + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + false + true + + + + true + true + + + + true + 1000 ms + + + + true + + + + true + + + + true + + + + true + + + + true + 10 ms + + + + true + 0 ms + + + + true + 10 ms + + + + true + 10 ms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20 ms + + 20 ms + + 20 ms + + false + + + + diff --git a/FCL/src/main/assets/java/lib/jfr/profile.jfc b/FCL/src/main/assets/java/lib/jfr/profile.jfc new file mode 100644 index 00000000..1070a45f --- /dev/null +++ b/FCL/src/main/assets/java/lib/jfr/profile.jfc @@ -0,0 +1,846 @@ + + + + + + + + true + everyChunk + + + + true + 1000 ms + + + + true + everyChunk + + + + true + 1000 ms + + + + true + + + + true + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + + + + false + true + 0 ms + + + + false + true + + + + false + + + + true + beginChunk + + + + true + beginChunk + + + + true + 10 ms + + + + true + 20 ms + + + + true + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + true + 0 ms + + + + true + true + + + + true + 60 s + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + everyChunk + + + + true + everyChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + false + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + true + + + + true + true + + + + true + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + true + 0 ms + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + true + + + + true + + + + false + everyChunk + + + + false + + + + true + true + 0 ns + + + + true + beginChunk + + + + true + 1000 ms + + + + true + 100 ms + + + + true + 10 s + + + + true + + + + false + + + + true + beginChunk + + + + true + everyChunk + + + + true + 100 ms + + + + true + beginChunk + + + + true + everyChunk + + + + true + + + + true + beginChunk + + + + true + beginChunk + + + + true + 10 s + + + + true + 1000 ms + + + + true + 10 s + + + + true + beginChunk + + + + true + endChunk + + + + true + 5 s + + + + true + beginChunk + + + + true + everyChunk + + + + true + true + + + + true + true + + + + true + everyChunk + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + false + true + + + + true + true + + + + true + 1000 ms + + + + true + + + + true + + + + true + + + + true + + + + true + 10 ms + + + + true + 0 ms + + + + 10 ms + true + + + + true + 10 ms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 ms + + 10 ms + + 10 ms + + false + + + + diff --git a/FCL/src/main/assets/java/lib/jsse.jar b/FCL/src/main/assets/java/lib/jsse.jar new file mode 100644 index 00000000..7935fa32 Binary files /dev/null and b/FCL/src/main/assets/java/lib/jsse.jar differ diff --git a/FCL/src/main/assets/java/lib/jvm.hprof.txt b/FCL/src/main/assets/java/lib/jvm.hprof.txt new file mode 100644 index 00000000..3c8e5fcd --- /dev/null +++ b/FCL/src/main/assets/java/lib/jvm.hprof.txt @@ -0,0 +1,86 @@ +Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of Oracle nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Header for -agentlib:hprof (or -Xrunhprof) ASCII Output (JDK 5.0 JVMTI based) + +WARNING! This file format is under development, and is subject to +change without notice. + +This file contains the following types of records: + +THREAD START +THREAD END mark the lifetime of Java threads + +TRACE represents a Java stack trace. Each trace consists + of a series of stack frames. Other records refer to + TRACEs to identify (1) where object allocations have + taken place, (2) the frames in which GC roots were + found, and (3) frequently executed methods. + +HEAP DUMP is a complete snapshot of all live objects in the Java + heap. Following distinctions are made: + + ROOT root set as determined by GC + CLS classes + OBJ instances + ARR arrays + +SITES is a sorted list of allocation sites. This identifies + the most heavily allocated object types, and the TRACE + at which those allocations occurred. + +CPU SAMPLES is a statistical profile of program execution. The VM + periodically samples all running threads, and assigns + a quantum to active TRACEs in those threads. Entries + in this record are TRACEs ranked by the percentage of + total quanta they consumed; top-ranked TRACEs are + typically hot spots in the program. + +CPU TIME is a profile of program execution obtained by measuring + the time spent in individual methods (excluding the time + spent in callees), as well as by counting the number of + times each method is called. Entries in this record are + TRACEs ranked by the percentage of total CPU time. The + "count" field indicates the number of times each TRACE + is invoked. + +MONITOR TIME is a profile of monitor contention obtained by measuring + the time spent by a thread waiting to enter a monitor. + Entries in this record are TRACEs ranked by the percentage + of total monitor contention time and a brief description + of the monitor. The "count" field indicates the number of + times the monitor was contended at that TRACE. + +MONITOR DUMP is a complete snapshot of all the monitors and threads in + the System. + +HEAP DUMP, SITES, CPU SAMPLES|TIME and MONITOR DUMP|TIME records are generated +at program exit. They can also be obtained during program execution by typing +Ctrl-\ (on Solaris) or by typing Ctrl-Break (on Win32). diff --git a/FCL/src/main/assets/java/lib/logging.properties b/FCL/src/main/assets/java/lib/logging.properties new file mode 100644 index 00000000..65cf1b1b --- /dev/null +++ b/FCL/src/main/assets/java/lib/logging.properties @@ -0,0 +1,59 @@ +############################################################ +# Default Logging Configuration File +# +# You can use a different file by specifying a filename +# with the java.util.logging.config.file system property. +# For example java -Djava.util.logging.config.file=myfile +############################################################ + +############################################################ +# Global properties +############################################################ + +# "handlers" specifies a comma separated list of log Handler +# classes. These handlers will be installed during VM startup. +# Note that these classes must be on the system classpath. +# By default we only configure a ConsoleHandler, which will only +# show messages at the INFO and above levels. +handlers= java.util.logging.ConsoleHandler + +# To also add the FileHandler, use the following line instead. +#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler + +# Default global logging level. +# This specifies which kinds of events are logged across +# all loggers. For any given facility this global level +# can be overriden by a facility specific level +# Note that the ConsoleHandler also has a separate level +# setting to limit messages printed to the console. +.level= INFO + +############################################################ +# Handler specific properties. +# Describes specific configuration info for Handlers. +############################################################ + +# default file output is in user's home directory. +java.util.logging.FileHandler.pattern = %h/java%u.log +java.util.logging.FileHandler.limit = 50000 +java.util.logging.FileHandler.count = 1 +java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter + +# Limit the message that are printed on the console to INFO and above. +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + +# Example to customize the SimpleFormatter output format +# to print one-line log message like this: +# : [] +# +# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n + +############################################################ +# Facility specific properties. +# Provides extra control for each logger. +############################################################ + +# For example, set the com.xyz.foo logger to only log SEVERE +# messages: +com.xyz.foo.level = SEVERE diff --git a/FCL/src/main/assets/java/lib/management-agent.jar b/FCL/src/main/assets/java/lib/management-agent.jar new file mode 100644 index 00000000..e015ba3d Binary files /dev/null and b/FCL/src/main/assets/java/lib/management-agent.jar differ diff --git a/FCL/src/main/assets/java/lib/management/jmxremote.access b/FCL/src/main/assets/java/lib/management/jmxremote.access new file mode 100644 index 00000000..ce80b47a --- /dev/null +++ b/FCL/src/main/assets/java/lib/management/jmxremote.access @@ -0,0 +1,79 @@ +###################################################################### +# Default Access Control File for Remote JMX(TM) Monitoring +###################################################################### +# +# Access control file for Remote JMX API access to monitoring. +# This file defines the allowed access for different roles. The +# password file (jmxremote.password by default) defines the roles and their +# passwords. To be functional, a role must have an entry in +# both the password and the access files. +# +# The default location of this file is $JRE/lib/management/jmxremote.access +# You can specify an alternate location by specifying a property in +# the management config file $JRE/lib/management/management.properties +# (See that file for details) +# +# The file format for password and access files is syntactically the same +# as the Properties file format. The syntax is described in the Javadoc +# for java.util.Properties.load. +# A typical access file has multiple lines, where each line is blank, +# a comment (like this one), or an access control entry. +# +# An access control entry consists of a role name, and an +# associated access level. The role name is any string that does not +# itself contain spaces or tabs. It corresponds to an entry in the +# password file (jmxremote.password). The access level is one of the +# following: +# "readonly" grants access to read attributes of MBeans. +# For monitoring, this means that a remote client in this +# role can read measurements but cannot perform any action +# that changes the environment of the running program. +# "readwrite" grants access to read and write attributes of MBeans, +# to invoke operations on them, and optionally +# to create or remove them. This access should be granted +# only to trusted clients, since they can potentially +# interfere with the smooth operation of a running program. +# +# The "readwrite" access level can optionally be followed by the "create" and/or +# "unregister" keywords. The "unregister" keyword grants access to unregister +# (delete) MBeans. The "create" keyword grants access to create MBeans of a +# particular class or of any class matching a particular pattern. Access +# should only be granted to create MBeans of known and trusted classes. +# +# For example, the following entry would grant readwrite access +# to "controlRole", as well as access to create MBeans of the class +# javax.management.monitor.CounterMonitor and to unregister any MBean: +# controlRole readwrite \ +# create javax.management.monitor.CounterMonitorMBean \ +# unregister +# or equivalently: +# controlRole readwrite unregister create javax.management.monitor.CounterMBean +# +# The following entry would grant readwrite access as well as access to create +# MBeans of any class in the packages javax.management.monitor and +# javax.management.timer: +# controlRole readwrite \ +# create javax.management.monitor.*,javax.management.timer.* \ +# unregister +# +# The \ character is defined in the Properties file syntax to allow continuation +# lines as shown here. A * in a class pattern matches a sequence of characters +# other than dot (.), so javax.management.monitor.* matches +# javax.management.monitor.CounterMonitor but not +# javax.management.monitor.foo.Bar. +# +# A given role should have at most one entry in this file. If a role +# has no entry, it has no access. +# If multiple entries are found for the same role name, then the last +# access entry is used. +# +# +# Default access control entries: +# o The "monitorRole" role has readonly access. +# o The "controlRole" role has readwrite access and can create the standard +# Timer and Monitor MBeans defined by the JMX API. + +monitorRole readonly +controlRole readwrite \ + create javax.management.monitor.*,javax.management.timer.* \ + unregister diff --git a/FCL/src/main/assets/java/lib/management/jmxremote.password.template b/FCL/src/main/assets/java/lib/management/jmxremote.password.template new file mode 100644 index 00000000..a7e7daac --- /dev/null +++ b/FCL/src/main/assets/java/lib/management/jmxremote.password.template @@ -0,0 +1,64 @@ +# ---------------------------------------------------------------------- +# Template for jmxremote.password +# +# o Copy this template to jmxremote.password +# o Set the user/password entries in jmxremote.password +# o Change the permission of jmxremote.password to read-only +# by the owner. +# +# See below for the location of jmxremote.password file. +# ---------------------------------------------------------------------- + +############################################################## +# Password File for Remote JMX Monitoring +############################################################## +# +# Password file for Remote JMX API access to monitoring. This +# file defines the different roles and their passwords. The access +# control file (jmxremote.access by default) defines the allowed +# access for each role. To be functional, a role must have an entry +# in both the password and the access files. +# +# Default location of this file is $JRE/lib/management/jmxremote.password +# You can specify an alternate location by specifying a property in +# the management config file $JRE/lib/management/management.properties +# or by specifying a system property (See that file for details). + + +############################################################## +# File permissions of the jmxremote.password file +############################################################## +# Since there are cleartext passwords stored in this file, +# this file must be readable by ONLY the owner, +# otherwise the program will exit with an error. +# +# The file format for password and access files is syntactically the same +# as the Properties file format. The syntax is described in the Javadoc +# for java.util.Properties.load. +# Typical password file has multiple lines, where each line is blank, +# a comment (like this one), or a password entry. +# +# +# A password entry consists of a role name and an associated +# password. The role name is any string that does not itself contain +# spaces or tabs. The password is again any string that does not +# contain spaces or tabs. Note that passwords appear in the clear in +# this file, so it is a good idea not to use valuable passwords. +# +# A given role should have at most one entry in this file. If a role +# has no entry, it has no access. +# If multiple entries are found for the same role name, then the last one +# is used. +# +# In a typical installation, this file can be read by anybody on the +# local machine, and possibly by people on other machines. +# For # security, you should either restrict the access to this file, +# or specify another, less accessible file in the management config file +# as described above. +# +# Following are two commented-out entries. The "measureRole" role has +# password "QED". The "controlRole" role has password "R&D". +# +# monitorRole QED +# controlRole R&D + diff --git a/FCL/src/main/assets/java/lib/management/management.properties b/FCL/src/main/assets/java/lib/management/management.properties new file mode 100644 index 00000000..70efa2ee --- /dev/null +++ b/FCL/src/main/assets/java/lib/management/management.properties @@ -0,0 +1,331 @@ +##################################################################### +# Default Configuration File for Java Platform Management +##################################################################### +# +# The Management Configuration file (in java.util.Properties format) +# will be read if one of the following system properties is set: +# -Dcom.sun.management.jmxremote.port= +# or -Dcom.sun.management.snmp.port= +# or -Dcom.sun.management.config.file= +# +# The default Management Configuration file is: +# +# $JRE/lib/management/management.properties +# +# Another location for the Management Configuration File can be specified +# by the following property on the Java command line: +# +# -Dcom.sun.management.config.file= +# +# If -Dcom.sun.management.config.file= is set, the port +# number for the management agent can be specified in the config file +# using the following lines: +# +# ################ Management Agent Port ######################### +# +# For setting the JMX RMI agent port use the following line +# com.sun.management.jmxremote.port= +# +# For setting the SNMP agent port use the following line +# com.sun.management.snmp.port= + +##################################################################### +# Optional Instrumentation +##################################################################### +# +# By default only the basic instrumentation with low overhead is on. +# The following properties allow to selectively turn on optional +# instrumentation which are off by default and may have some +# additional overhead. +# +# com.sun.management.enableThreadContentionMonitoring +# +# This option enables thread contention monitoring if the +# Java virtual machine supports such instrumentation. +# Refer to the specification for the java.lang.management.ThreadMBean +# interface - see isThreadContentionMonitoringSupported() method. +# + +# To enable thread contention monitoring, uncomment the following line +# com.sun.management.enableThreadContentionMonitoring + +##################################################################### +# SNMP Management Properties +##################################################################### +# +# If the system property -Dcom.sun.management.snmp.port= +# is set then +# - The SNMP agent (with the Java virtual machine MIB) is started +# that listens on the specified port for incoming SNMP requests. +# - the following properties for read for SNMP management. +# +# The configuration can be specified only at startup time. +# Later changes to the above system property (e.g. via setProperty method), this +# config file, or the ACL file has no effect to the running SNMP agent. +# + +# +# ##################### SNMP Trap Port ######################### +# +# com.sun.management.snmp.trap= +# Specifies the remote port number at which managers are expected +# to listen for trap. For each host defined in the ACL file, +# the SNMP agent will send traps at : +# Default for this property is 162. +# + +# To set port for sending traps to a different port use the following line +# com.sun.management.snmp.trap= + +# +# ################ SNMP listen interface ######################### +# +# com.sun.management.snmp.interface= +# Specifies the local interface on which the SNMP agent will bind. +# This is useful when running on machines which have several +# interfaces defined. It makes it possible to listen to a specific +# subnet accessible through that interface. +# Default for this property is "localhost". +# +# The format of the value for that property is any string accepted +# by java.net.InetAddress.getByName(String). +# + +# For restricting the port on which SNMP agent listens use the following line +# com.sun.management.snmp.interface= + +# +# #################### SNMP ACL file ######################### +# +# com.sun.management.snmp.acl=true|false +# Default for this property is true. (Case for true/false ignored) +# If this property is specified as false then the ACL file +# is not checked: all manager hosts are allowed all access. +# + +# For SNMP without checking ACL file uncomment the following line +# com.sun.management.snmp.acl=false + +# +# com.sun.management.snmp.acl.file=filepath +# Specifies location for ACL file +# This is optional - default location is +# $JRE/lib/management/snmp.acl +# +# If the property "com.sun.management.snmp.acl" is set to false, +# then this property and the ACL file are ignored. +# Otherwise the ACL file must exist and be in the valid format. +# If the ACL file is empty or non existent then no access is allowed. +# +# The SNMP agent will read the ACL file at startup time. +# Modification to the ACL file has no effect to any running SNMP +# agents which read that ACL file at startup. +# + +# For a non-default acl file location use the following line +# com.sun.management.snmp.acl.file=filepath + +##################################################################### +# RMI Management Properties +##################################################################### +# +# If system property -Dcom.sun.management.jmxremote.port= +# is set then +# - A MBean server is started +# - JRE Platform MBeans are registered in the MBean server +# - RMI connector is published in a private readonly registry at +# specified port using a well known name, "jmxrmi" +# - the following properties are read for JMX remote management. +# +# The configuration can be specified only at startup time. +# Later changes to above system property (e.g. via setProperty method), +# this config file, the password file, or the access file have no effect to the +# running MBean server, the connector, or the registry. +# + +# +# ########## RMI connector settings for local management ########## +# +# com.sun.management.jmxremote.local.only=true|false +# Default for this property is true. (Case for true/false ignored) +# If this property is specified as true then the local JMX RMI connector +# server will only accept connection requests from clients running on +# the host where the out-of-the-box JMX management agent is running. +# In order to ensure backwards compatibility this property could be +# set to false. However, deploying the local management agent in this +# way is discouraged because the local JMX RMI connector server will +# accept connection requests from any client either local or remote. +# For remote management the remote JMX RMI connector server should +# be used instead with authentication and SSL/TLS encryption enabled. +# + +# For allowing the local management agent accept local +# and remote connection requests use the following line +# com.sun.management.jmxremote.local.only=false + +# +# ###################### RMI SSL ############################# +# +# com.sun.management.jmxremote.ssl=true|false +# Default for this property is true. (Case for true/false ignored) +# If this property is specified as false then SSL is not used. +# + +# For RMI monitoring without SSL use the following line +# com.sun.management.jmxremote.ssl=false + +# com.sun.management.jmxremote.ssl.config.file=filepath +# Specifies the location of the SSL configuration file. A properties +# file can be used to supply the keystore and truststore location and +# password settings thus avoiding to pass them as cleartext in the +# command-line. +# +# The current implementation of the out-of-the-box management agent will +# look up and use the properties specified below to configure the SSL +# keystore and truststore, if present: +# javax.net.ssl.keyStore= +# javax.net.ssl.keyStorePassword= +# javax.net.ssl.trustStore= +# javax.net.ssl.trustStorePassword= +# Any other properties in the file will be ignored. This will allow us +# to extend the property set in the future if required by the default +# SSL implementation. +# +# If the property "com.sun.management.jmxremote.ssl" is set to false, +# then this property is ignored. +# + +# For supplying the keystore settings in a file use the following line +# com.sun.management.jmxremote.ssl.config.file=filepath + +# com.sun.management.jmxremote.ssl.enabled.cipher.suites= +# The value of this property is a string that is a comma-separated list +# of SSL/TLS cipher suites to enable. This property can be specified in +# conjunction with the previous property "com.sun.management.jmxremote.ssl" +# in order to control which particular SSL/TLS cipher suites are enabled +# for use by accepted connections. If this property is not specified then +# the SSL/TLS RMI Server Socket Factory uses the SSL/TLS cipher suites that +# are enabled by default. +# + +# com.sun.management.jmxremote.ssl.enabled.protocols= +# The value of this property is a string that is a comma-separated list +# of SSL/TLS protocol versions to enable. This property can be specified in +# conjunction with the previous property "com.sun.management.jmxremote.ssl" +# in order to control which particular SSL/TLS protocol versions are +# enabled for use by accepted connections. If this property is not +# specified then the SSL/TLS RMI Server Socket Factory uses the SSL/TLS +# protocol versions that are enabled by default. +# + +# com.sun.management.jmxremote.ssl.need.client.auth=true|false +# Default for this property is false. (Case for true/false ignored) +# If this property is specified as true in conjunction with the previous +# property "com.sun.management.jmxremote.ssl" then the SSL/TLS RMI Server +# Socket Factory will require client authentication. +# + +# For RMI monitoring with SSL client authentication use the following line +# com.sun.management.jmxremote.ssl.need.client.auth=true + +# com.sun.management.jmxremote.registry.ssl=true|false +# Default for this property is false. (Case for true/false ignored) +# If this property is specified as true then the RMI registry used +# to bind the RMIServer remote object is protected with SSL/TLS +# RMI Socket Factories that can be configured with the properties: +# com.sun.management.jmxremote.ssl.config.file +# com.sun.management.jmxremote.ssl.enabled.cipher.suites +# com.sun.management.jmxremote.ssl.enabled.protocols +# com.sun.management.jmxremote.ssl.need.client.auth +# If the two properties below are true at the same time, i.e. +# com.sun.management.jmxremote.ssl=true +# com.sun.management.jmxremote.registry.ssl=true +# then the RMIServer remote object and the RMI registry are +# both exported with the same SSL/TLS RMI Socket Factories. +# + +# For using an SSL/TLS protected RMI registry use the following line +# com.sun.management.jmxremote.registry.ssl=true + +# +# ################ RMI User authentication ################ +# +# com.sun.management.jmxremote.authenticate=true|false +# Default for this property is true. (Case for true/false ignored) +# If this property is specified as false then no authentication is +# performed and all users are allowed all access. +# + +# For RMI monitoring without any checking use the following line +# com.sun.management.jmxremote.authenticate=false + +# +# ################ RMI Login configuration ################### +# +# com.sun.management.jmxremote.login.config= +# Specifies the name of a JAAS login configuration entry to use when +# authenticating users of RMI monitoring. +# +# Setting this property is optional - the default login configuration +# specifies a file-based authentication that uses the password file. +# +# When using this property to override the default login configuration +# then the named configuration entry must be in a file that gets loaded +# by JAAS. In addition, the login module(s) specified in the configuration +# should use the name and/or password callbacks to acquire the user's +# credentials. See the NameCallback and PasswordCallback classes in the +# javax.security.auth.callback package for more details. +# +# If the property "com.sun.management.jmxremote.authenticate" is set to +# false, then this property and the password & access files are ignored. +# + +# For a non-default login configuration use the following line +# com.sun.management.jmxremote.login.config= + +# +# ################ RMI Password file location ################## +# +# com.sun.management.jmxremote.password.file=filepath +# Specifies location for password file +# This is optional - default location is +# $JRE/lib/management/jmxremote.password +# +# If the property "com.sun.management.jmxremote.authenticate" is set to +# false, then this property and the password & access files are ignored. +# Otherwise the password file must exist and be in the valid format. +# If the password file is empty or non-existent then no access is allowed. +# + +# For a non-default password file location use the following line +# com.sun.management.jmxremote.password.file=filepath + +# +# ################ RMI Access file location ##################### +# +# com.sun.management.jmxremote.access.file=filepath +# Specifies location for access file +# This is optional - default location is +# $JRE/lib/management/jmxremote.access +# +# If the property "com.sun.management.jmxremote.authenticate" is set to +# false, then this property and the password & access files are ignored. +# Otherwise, the access file must exist and be in the valid format. +# If the access file is empty or non-existent then no access is allowed. +# + +# For a non-default password file location use the following line +# com.sun.management.jmxremote.access.file=filepath +# + +# ################ Management agent listen interface ######################### +# +# com.sun.management.jmxremote.host= +# Specifies the local interface on which the JMX RMI agent will bind. +# This is useful when running on machines which have several +# interfaces defined. It makes it possible to listen to a specific +# subnet accessible through that interface. +# +# The format of the value for that property is any string accepted +# by java.net.InetAddress.getByName(String). +# diff --git a/FCL/src/main/assets/java/lib/management/snmp.acl.template b/FCL/src/main/assets/java/lib/management/snmp.acl.template new file mode 100644 index 00000000..0e766764 --- /dev/null +++ b/FCL/src/main/assets/java/lib/management/snmp.acl.template @@ -0,0 +1,110 @@ +# ---------------------------------------------------------------------- +# Template for SNMP Access Control List File +# +# o Copy this template to snmp.acl +# o Set access control for SNMP support +# o Change the permission of snmp.acl to be read-only +# by the owner. +# +# See below for the location of snmp.acl file. +# ---------------------------------------------------------------------- + +############################################################ +# SNMP Access Control List File +############################################################ +# +# Default location of this file is $JRE/lib/management/snmp.acl. +# You can specify an alternate location by specifying a property in +# the management config file $JRE/lib/management/management.properties +# or by specifying a system property (See that file for details). +# + + +############################################################## +# File permissions of the snmp.acl file +############################################################## +# +# Since there are cleartext community strings stored in this file, +# this ACL file must be readable by ONLY the owner, +# otherwise the program will exit with an error. +# +############################################################## +# Format of the acl group +############################################################## +# +# communities: a list of SNMP community strings to which the +# access control applies separated by commas. +# +# access: either "read-only" or "read-write". +# +# managers: a list of hosts to be granted the access rights. +# Each can be expressed as any one of the following: +# - hostname: hubble +# - ip v4 and v6 addresses: 123.456.789.12 , fe80::a00:20ff:fe9b:ea82 +# - ip v4 and v6 netmask prefix notation: 123.456.789.0/24, +# fe80::a00:20ff:fe9b:ea82/64 +# see RFC 2373 (http://www.ietf.org/rfc/rfc2373.txt) +# +# An example of two community groups for multiple hosts: +# acl = { +# { +# communities = public, private +# access = read-only +# managers = hubble, snowbell, nanak +# } +# { +# communities = jerry +# access = read-write +# managers = hubble, telescope +# } +# } +# +############################################################## +# Format of the trap group +############################################################## +# +# trap-community: a single SNMP community string that will be included +# in the traps sent to the hosts. +# +# hosts: a list of hosts to which the SNMP agent will send traps. +# +# An example of two trap community definitions for multiple hosts: +# trap = { +# { +# trap-community = public +# hosts = hubble, snowbell +# } +# { +# trap-community = private +# hosts = telescope +# } +# } +# +############################################################ +# +# Update the community strings (public and private) below +# before copying this template file +# +# Common SNMP ACL Example +# ------------------------ +# +# o Only localhost can connect, and access rights +# are limited to read-only +# o Traps are sent to localhost only +# +# +# acl = { +# { +# communities = public, private +# access = read-only +# managers = localhost +# } +# } +# +# +# trap = { +# { +# trap-community = public +# hosts = localhost +# } +# } diff --git a/FCL/src/main/assets/java/lib/meta-index b/FCL/src/main/assets/java/lib/meta-index new file mode 100644 index 00000000..3a10c815 --- /dev/null +++ b/FCL/src/main/assets/java/lib/meta-index @@ -0,0 +1,88 @@ +% VERSION 2 +% WARNING: this file is auto-generated; do not edit +% UNSUPPORTED: this file and its format may change and/or +% may be removed in a future release +# charsets.jar +sun/nio +sun/awt +! jce.jar +javax/crypto +sun/security +# jfr.jar +jdk/jfr +jdk/management +! jsse.jar +sun/security +com/sun/net/ +! management-agent.jar +@ resources.jar +com/sun/java/util/jar/pack/ +META-INF/services/sun.util.spi.XmlPropertiesProvider +META-INF/services/javax.print.PrintServiceLookup +com/sun/corba/ +META-INF/services/javax.sound.midi.spi.SoundbankReader +sun/print +META-INF/services/javax.sound.midi.spi.MidiFileReader +META-INF/services/sun.java2d.cmm.CMMServiceProvider +javax/swing +META-INF/services/javax.sound.sampled.spi.AudioFileReader +META-INF/services/javax.sound.midi.spi.MidiDeviceProvider +sun/net +META-INF/services/javax.sound.sampled.spi.AudioFileWriter +com/sun/imageio/ +META-INF/services/sun.java2d.pipe.RenderingEngine +META-INF/mimetypes.default +META-INF/services/javax.sound.midi.spi.MidiFileWriter +sun/rmi +javax/sql +META-INF/services/com.sun.tools.internal.ws.wscompile.Plugin +com/sun/rowset/ +META-INF/services/javax.print.StreamPrintServiceFactory +META-INF/mailcap.default +java/lang +sun/text +javax/xml +META-INF/services/javax.sound.sampled.spi.MixerProvider +com/sun/xml/ +META-INF/services/com.sun.tools.internal.xjc.Plugin +com/sun/java/swing/ +com/sun/jndi/ +com/sun/org/ +META-INF/services/javax.sound.sampled.spi.FormatConversionProvider +! rt.jar +com/sun/java/util/jar/pack/ +java/ +org/ietf/ +com/sun/beans/ +com/sun/tracing/ +com/sun/java/browser/ +com/sun/corba/ +com/sun/media/ +com/sun/awt/ +com/sun/management/ +sun/ +com/sun/jmx/ +com/sun/demo/ +com/sun/imageio/ +com/sun/net/ +com/sun/rmi/ +org/w3c/ +com/sun/swing/ +com/sun/activation/ +com/sun/nio/ +com/sun/rowset/ +org/jcp/ +com/sun/istack/ +jdk/ +com/sun/naming/ +org/xml/ +org/omg/ +com/sun/security/ +com/sun/xml/ +com/sun/java/swing/ +com/oracle/ +com/sun/java_cup/ +com/sun/jndi/ +com/sun/accessibility/ +com/sun/org/ +javax/ diff --git a/FCL/src/main/assets/java/lib/net.properties b/FCL/src/main/assets/java/lib/net.properties new file mode 100644 index 00000000..a541eef9 --- /dev/null +++ b/FCL/src/main/assets/java/lib/net.properties @@ -0,0 +1,121 @@ +############################################################ +# Default Networking Configuration File +# +# This file may contain default values for the networking system properties. +# These values are only used when the system properties are not specified +# on the command line or set programatically. +# For now, only the various proxy settings can be configured here. +############################################################ + +# Whether or not the DefaultProxySelector will default to System Proxy +# settings when they do exist. +# Set it to 'true' to enable this feature and check for platform +# specific proxy settings +# Note that the system properties that do explicitely set proxies +# (like http.proxyHost) do take precedence over the system settings +# even if java.net.useSystemProxies is set to true. + +java.net.useSystemProxies=false + +#------------------------------------------------------------------------ +# Proxy configuration for the various protocol handlers. +# DO NOT uncomment these lines if you have set java.net.useSystemProxies +# to true as the protocol specific properties will take precedence over +# system settings. +#------------------------------------------------------------------------ + +# HTTP Proxy settings. proxyHost is the name of the proxy server +# (e.g. proxy.mydomain.com), proxyPort is the port number to use (default +# value is 80) and nonProxyHosts is a '|' separated list of hostnames which +# should be accessed directly, ignoring the proxy server (default value is +# localhost & 127.0.0.1). +# +# http.proxyHost= +# http.proxyPort=80 +http.nonProxyHosts=localhost|127.*|[::1] +# +# HTTPS Proxy Settings. proxyHost is the name of the proxy server +# (e.g. proxy.mydomain.com), proxyPort is the port number to use (default +# value is 443). The HTTPS protocol handlers uses the http nonProxyHosts list. +# +# https.proxyHost= +# https.proxyPort=443 +# +# FTP Proxy settings. proxyHost is the name of the proxy server +# (e.g. proxy.mydomain.com), proxyPort is the port number to use (default +# value is 80) and nonProxyHosts is a '|' separated list of hostnames which +# should be accessed directly, ignoring the proxy server (default value is +# localhost & 127.0.0.1). +# +# ftp.proxyHost= +# ftp.proxyPort=80 +ftp.nonProxyHosts=localhost|127.*|[::1] +# +# Gopher Proxy settings. proxyHost is the name of the proxy server +# (e.g. proxy.mydomain.com), proxyPort is the port number to use (default +# value is 80) +# +# gopher.proxyHost= +# gopher.proxyPort=80 +# +# Socks proxy settings. socksProxyHost is the name of the proxy server +# (e.g. socks.domain.com), socksProxyPort is the port number to use +# (default value is 1080) +# +# socksProxyHost= +# socksProxyPort=1080 +# +# HTTP Keep Alive settings. remainingData is the maximum amount of data +# in kilobytes that will be cleaned off the underlying socket so that it +# can be reused (default value is 512K), queuedConnections is the maximum +# number of Keep Alive connections to be on the queue for clean up (default +# value is 10). +# http.KeepAlive.remainingData=512 +# http.KeepAlive.queuedConnections=10 + +# Authentication Scheme restrictions for HTTP and HTTPS. +# +# In some environments certain authentication schemes may be undesirable +# when proxying HTTP or HTTPS. For example, "Basic" results in effectively the +# cleartext transmission of the user's password over the physical network. +# This section describes the mechanism for disabling authentication schemes +# based on the scheme name. Disabled schemes will be treated as if they are not +# supported by the implementation. +# +# The 'jdk.http.auth.tunneling.disabledSchemes' property lists the authentication +# schemes that will be disabled when tunneling HTTPS over a proxy, HTTP CONNECT. +# The 'jdk.http.auth.proxying.disabledSchemes' property lists the authentication +# schemes that will be disabled when proxying HTTP. +# +# In both cases the property is a comma-separated list of, case-insensitive, +# authentication scheme names, as defined by their relevant RFCs. An +# implementation may, but is not required to, support common schemes whose names +# include: 'Basic', 'Digest', 'NTLM', 'Kerberos', 'Negotiate'. A scheme that +# is not known, or not supported, by the implementation is ignored. +# +# Note: This property is currently used by the JDK Reference implementation. It +# is not guaranteed to be examined and used by other implementations. +# +#jdk.http.auth.proxying.disabledSchemes= +jdk.http.auth.tunneling.disabledSchemes=Basic + +# +# Transparent NTLM HTTP authentication mode on Windows. Transparent authentication +# can be used for the NTLM scheme, where the security credentials based on the +# currently logged in user's name and password can be obtained directly from the +# operating system, without prompting the user. This property has three possible +# values which regulate the behavior as shown below. Other unrecognized values +# are handled the same as 'disabled'. Note, that NTLM is not considered to be a +# strongly secure authentication scheme and care should be taken before enabling +# this mechanism. +# +# Transparent authentication never used. +#jdk.http.ntlm.transparentAuth=disabled +# +# Enabled for all hosts. +#jdk.http.ntlm.transparentAuth=allHosts +# +# Enabled for hosts that are trusted in Windows Internet settings +#jdk.http.ntlm.transparentAuth=trustedHosts +# +jdk.http.ntlm.transparentAuth=disabled diff --git a/FCL/src/main/assets/java/lib/psfont.properties.ja b/FCL/src/main/assets/java/lib/psfont.properties.ja new file mode 100644 index 00000000..d17cf40d --- /dev/null +++ b/FCL/src/main/assets/java/lib/psfont.properties.ja @@ -0,0 +1,119 @@ +# +# +# Copyright (c) 1996, 2000, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Japanese PostScript printer property file +# +font.num=16 +# +serif=serif +timesroman=serif +sansserif=sansserif +helvetica=sansserif +monospaced=monospaced +courier=monospaced +dialog=sansserif +dialoginput=monospaced +# +serif.latin1.plain=Times-Roman +serif.latin1.italic=Times-Italic +serif.latin1.bolditalic=Times-BoldItalic +serif.latin1.bold=Times-Bold +# +sansserif.latin1.plain=Helvetica +sansserif.latin1.italic=Helvetica-Oblique +sansserif.latin1.bolditalic=Helvetica-BoldOblique +sansserif.latin1.bold=Helvetica-Bold +# +monospaced.latin1.plain=Courier +monospaced.latin1.italic=Courier-Oblique +monospaced.latin1.bolditalic=Courier-BoldOblique +monospaced.latin1.bold=Courier-Bold +# +serif.x11jis0208.plain=Ryumin-Light-H +serif.x11jis0208.italic=Ryumin-Light-H +serif.x11jis0208.bolditalic=Ryumin-Light-H +serif.x11jis0208.bold=Ryumin-Light-H +# +sansserif.x11jis0208.plain=GothicBBB-Medium-H +sansserif.x11jis0208.italic=GothicBBB-Medium-H +sansserif.x11jis0208.bolditalic=GothicBBB-Medium-H +sansserif.x11jis0208.bold=GothicBBB-Medium-H +# +monospaced.x11jis0208.plain=GothicBBB-Medium-H +monospaced.x11jis0208.italic=GothicBBB-Medium-H +monospaced.x11jis0208.bolditalic=GothicBBB-Medium-H +monospaced.x11jis0208.bold=GothicBBB-Medium-H +# +serif.x11jis0201.plain=Ryumin-Light.Hankaku +serif.x11jis0201.italic=Ryumin-Light.Hankaku +serif.x11jis0201.bolditalic=Ryumin-Light.Hankaku +serif.x11jis0201.bold=Ryumin-Light.Hankaku +# +sansserif.x11jis0201.plain=GothicBBB-Medium.Hankaku +sansserif.x11jis0201.italic=GothicBBB-Medium.Hankaku +sansserif.x11jis0201.bolditalic=GothicBBB-Medium.Hankaku +sansserif.x11jis0201.bold=GothicBBB-Medium.Hankaku +# +monospaced.x11jis0201.plain=GothicBBB-Medium.Hankaku +monospaced.x11jis0201.italic=GothicBBB-Medium.Hankaku +monospaced.x11jis0201.bolditalic=GothicBBB-Medium.Hankaku +monospaced.x11jis0201.bold=GothicBBB-Medium.Hankaku +# +Helvetica=0 +Helvetica-Bold=1 +Helvetica-Oblique=2 +Helvetica-BoldOblique=3 +Times-Roman=4 +Times-Bold=5 +Times-Italic=6 +Times-BoldItalic=7 +Courier=8 +Courier-Bold=9 +Courier-Oblique=10 +Courier-BoldOblique=11 +GothicBBB-Medium-H=12 +Ryumin-Light-H=13 +GothicBBB-Medium.Hankaku=14 +Ryumin-Light.Hankaku=15 +# +font.0=Helvetica ISOF +font.1=Helvetica-Bold ISOF +font.2=Helvetica-Oblique ISOF +font.3=Helvetica-BoldOblique ISOF +font.4=Times-Roman ISOF +font.5=Times-Bold ISOF +font.6=Times-Italic ISOF +font.7=Times-BoldItalic ISOF +font.8=Courier ISOF +font.9=Courier-Bold ISOF +font.10=Courier-Oblique ISOF +font.11=Courier-BoldOblique ISOF +font.12=GothicBBB-Medium-H findfont +font.13=Ryumin-Light-H findfont +font.14=GothicBBB-Medium.Hankaku findfont +font.15=Ryumin-Light.Hankaku findfont +# diff --git a/FCL/src/main/assets/java/lib/psfontj2d.properties b/FCL/src/main/assets/java/lib/psfontj2d.properties new file mode 100644 index 00000000..5eb2c4b8 --- /dev/null +++ b/FCL/src/main/assets/java/lib/psfontj2d.properties @@ -0,0 +1,323 @@ +# +# +# Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# +# PostScript printer property file for Java 2D printing. +# +# WARNING: This is an internal implementation file, not a public file. +# Any customisation or reliance on the existence of this file and its +# contents or syntax is discouraged and unsupported. +# It may be incompatibly changed or removed without any notice. +# +# +font.num=35 +# +# Legacy logical font family names and logical font aliases should all +# map to the primary logical font names. +# +serif=serif +times=serif +timesroman=serif +sansserif=sansserif +helvetica=sansserif +dialog=sansserif +dialoginput=monospaced +monospaced=monospaced +courier=monospaced +# +# Next, physical fonts which can be safely mapped to standard postscript fonts +# These keys generally map to a value which is the same as the key, so +# the key/value is just a way to say the font has a mapping. +# Sometimes however we map more than one screen font to the same PS font. +# +avantgarde=avantgarde_book +avantgarde_book=avantgarde_book +avantgarde_demi=avantgarde_demi +avantgarde_book_oblique=avantgarde_book_oblique +avantgarde_demi_oblique=avantgarde_demi_oblique +# +itcavantgarde=avantgarde_book +itcavantgarde=avantgarde_book +itcavantgarde_demi=avantgarde_demi +itcavantgarde_oblique=avantgarde_book_oblique +itcavantgarde_demi_oblique=avantgarde_demi_oblique +# +bookman=bookman_light +bookman_light=bookman_light +bookman_demi=bookman_demi +bookman_light_italic=bookman_light_italic +bookman_demi_italic=bookman_demi_italic +# +# Exclude "helvetica" on its own as that's a legacy name for a logical font +helvetica_bold=helvetica_bold +helvetica_oblique=helvetica_oblique +helvetica_bold_oblique=helvetica_bold_oblique +# +itcbookman_light=bookman_light +itcbookman_demi=bookman_demi +itcbookman_light_italic=bookman_light_italic +itcbookman_demi_italic=bookman_demi_italic +# +# Exclude "courier" on its own as that's a legacy name for a logical font +courier_bold=courier_bold +courier_oblique=courier_oblique +courier_bold_oblique=courier_bold_oblique +# +courier_new=courier +courier_new_bold=courier_bold +# +monotype_century_schoolbook=newcenturyschoolbook +monotype_century_schoolbook_bold=newcenturyschoolbook_bold +monotype_century_schoolbook_italic=newcenturyschoolbook_italic +monotype_century_schoolbook_bold_italic=newcenturyschoolbook_bold_italic +# +newcenturyschoolbook=newcenturyschoolbook +newcenturyschoolbook_bold=newcenturyschoolbook_bold +newcenturyschoolbook_italic=newcenturyschoolbook_italic +newcenturyschoolbook_bold_italic=newcenturyschoolbook_bold_italic +# +palatino=palatino +palatino_bold=palatino_bold +palatino_italic=palatino_italic +palatino_bold_italic=palatino_bold_italic +# +# Exclude "times" on its own as that's a legacy name for a logical font +times_bold=times_roman_bold +times_italic=times_roman_italic +times_bold_italic=times_roman_bold_italic +# +times_roman=times_roman +times_roman_bold=times_roman_bold +times_roman_italic=times_roman_italic +times_roman_bold_italic=times_roman_bold_italic +# +times_new_roman=times_roman +times_new_roman_bold=times_roman_bold +times_new_roman_italic=times_roman_italic +times_new_roman_bold_italic=times_roman_bold_italic +# +zapfchancery_italic=zapfchancery_italic +itczapfchancery_italic=zapfchancery_italic +# +# Next the mapping of the font name + charset + style to Postscript font name +# for the logical fonts. +# +serif.latin1.plain=Times-Roman +serif.latin1.bold=Times-Bold +serif.latin1.italic=Times-Italic +serif.latin1.bolditalic=Times-BoldItalic +serif.symbol.plain=Symbol +serif.dingbats.plain=ZapfDingbats +serif.symbol.bold=Symbol +serif.dingbats.bold=ZapfDingbats +serif.symbol.italic=Symbol +serif.dingbats.italic=ZapfDingbats +serif.symbol.bolditalic=Symbol +serif.dingbats.bolditalic=ZapfDingbats +# +sansserif.latin1.plain=Helvetica +sansserif.latin1.bold=Helvetica-Bold +sansserif.latin1.italic=Helvetica-Oblique +sansserif.latin1.bolditalic=Helvetica-BoldOblique +sansserif.symbol.plain=Symbol +sansserif.dingbats.plain=ZapfDingbats +sansserif.symbol.bold=Symbol +sansserif.dingbats.bold=ZapfDingbats +sansserif.symbol.italic=Symbol +sansserif.dingbats.italic=ZapfDingbats +sansserif.symbol.bolditalic=Symbol +sansserif.dingbats.bolditalic=ZapfDingbats +# +monospaced.latin1.plain=Courier +monospaced.latin1.bold=Courier-Bold +monospaced.latin1.italic=Courier-Oblique +monospaced.latin1.bolditalic=Courier-BoldOblique +monospaced.symbol.plain=Symbol +monospaced.dingbats.plain=ZapfDingbats +monospaced.symbol.bold=Symbol +monospaced.dingbats.bold=ZapfDingbats +monospaced.symbol.italic=Symbol +monospaced.dingbats.italic=ZapfDingbats +monospaced.symbol.bolditalic=Symbol +monospaced.dingbats.bolditalic=ZapfDingbats +# +# Next the mapping of the font name + charset + style to Postscript font name +# for the physical fonts. Since these always report style as plain, the +# style key is always plain. So we map using the face name to the correct +# style for the postscript font. This is possible since the face names can +# be replied upon to be different for each style. +# However an application may try to create a Font applying a style to an +# physical name. We want to map to the correct Postscript font there too +# if possible but we do not map cases where the application tries to +# augment a style (eg ask for a bold version of a bold font) +# Defer to the 2D package to attempt create an artificially styled version +# +avantgarde_book.latin1.plain=AvantGarde-Book +avantgarde_demi.latin1.plain=AvantGarde-Demi +avantgarde_book_oblique.latin1.plain=AvantGarde-BookOblique +avantgarde_demi_oblique.latin1.plain=AvantGarde-DemiOblique +# +avantgarde_book.latin1.bold=AvantGarde-Demi +avantgarde_book.latin1.italic=AvantGarde-BookOblique +avantgarde_book.latin1.bolditalic=AvantGarde-DemiOblique +avantgarde_demi.latin1.italic=AvantGarde-DemiOblique +avantgarde_book_oblique.latin1.bold=AvantGarde-DemiOblique +# +bookman_light.latin1.plain=Bookman-Light +bookman_demi.latin1.plain=Bookman-Demi +bookman_light_italic.latin1.plain=Bookman-LightItalic +bookman_demi_italic.latin1.plain=Bookman-DemiItalic +# +bookman_light.latin1.bold=Bookman-Demi +bookman_light.latin1.italic=Bookman-LightItalic +bookman_light.latin1.bolditalic=Bookman-DemiItalic +bookman_light_bold.latin1.italic=Bookman-DemiItalic +bookman_light_italic.latin1.bold=Bookman-DemiItalic +# +courier.latin1.plain=Courier +courier_bold.latin1.plain=Courier-Bold +courier_oblique.latin1.plain=Courier-Oblique +courier_bold_oblique.latin1.plain=Courier-BoldOblique +courier.latin1.bold=Courier-Bold +courier.latin1.italic=Courier-Oblique +courier.latin1.bolditalic=Courier-BoldOblique +courier_bold.latin1.italic=Courier-BoldOblique +courier_italic.latin1.bold=Courier-BoldOblique +# +helvetica_bold.latin1.plain=Helvetica-Bold +helvetica_oblique.latin1.plain=Helvetica-Oblique +helvetica_bold_oblique.latin1.plain=Helvetica-BoldOblique +helvetica.latin1.bold=Helvetica-Bold +helvetica.latin1.italic=Helvetica-Oblique +helvetica.latin1.bolditalic=Helvetica-BoldOblique +helvetica_bold.latin1.italic=Helvetica-BoldOblique +helvetica_italic.latin1.bold=Helvetica-BoldOblique +# +newcenturyschoolbook.latin1.plain=NewCenturySchlbk-Roman +newcenturyschoolbook_bold.latin1.plain=NewCenturySchlbk-Bold +newcenturyschoolbook_italic.latin1.plain=NewCenturySchlbk-Italic +newcenturyschoolbook_bold_italic.latin1.plain=NewCenturySchlbk-BoldItalic +newcenturyschoolbook.latin1.bold=NewCenturySchlbk-Bold +newcenturyschoolbook.latin1.italic=NewCenturySchlbk-Italic +newcenturyschoolbook.latin1.bolditalic=NewCenturySchlbk-BoldItalic +newcenturyschoolbook_bold.latin1.italic=NewCenturySchlbk-BoldItalic +newcenturyschoolbook_italic.latin1.bold=NewCenturySchlbk-BoldItalic +# +palatino.latin1.plain=Palatino-Roman +palatino_bold.latin1.plain=Palatino-Bold +palatino_italic.latin1.plain=Palatino-Italic +palatino_bold_italic.latin1.plain=Palatino-BoldItalic +palatino.latin1.bold=Palatino-Bold +palatino.latin1.italic=Palatino-Italic +palatino.latin1.bolditalic=Palatino-BoldItalic +palatino_bold.latin1.italic=Palatino-BoldItalic +palatino_italic.latin1.bold=Palatino-BoldItalic +# +times_roman.latin1.plain=Times-Roman +times_roman_bold.latin1.plain=Times-Bold +times_roman_italic.latin1.plain=Times-Italic +times_roman_bold_italic.latin1.plain=Times-BoldItalic +times_roman.latin1.bold=Times-Bold +times_roman.latin1.italic=Times-Italic +times_roman.latin1.bolditalic=Times-BoldItalic +times_roman_bold.latin1.italic=Times-BoldItalic +times_roman_italic.latin1.bold=Times-BoldItalic +# +zapfchancery_italic.latin1.plain=ZapfChancery-MediumItalic +# +# Finally the mappings of PS font names to indexes. +# +AvantGarde-Book=0 +AvantGarde-BookOblique=1 +AvantGarde-Demi=2 +AvantGarde-DemiOblique=3 +Bookman-Demi=4 +Bookman-DemiItalic=5 +Bookman-Light=6 +Bookman-LightItalic=7 +Courier=8 +Courier-Bold=9 +Courier-BoldOblique=10 +Courier-Oblique=11 +Helvetica=12 +Helvetica-Bold=13 +Helvetica-BoldOblique=14 +Helvetica-Narrow=15 +Helvetica-Narrow-Bold=16 +Helvetica-Narrow-BoldOblique=17 +Helvetica-Narrow-Oblique=18 +Helvetica-Oblique=19 +NewCenturySchlbk-Bold=20 +NewCenturySchlbk-BoldItalic=21 +NewCenturySchlbk-Italic=22 +NewCenturySchlbk-Roman=23 +Palatino-Bold=24 +Palatino-BoldItalic=25 +Palatino-Italic=26 +Palatino-Roman=27 +Symbol=28 +Times-Bold=29 +Times-BoldItalic=30 +Times-Italic=31 +Times-Roman=32 +ZapfDingbats=33 +ZapfChancery-MediumItalic=34 +# +font.0=AvantGarde-Book ISOF +font.1=AvantGarde-BookOblique ISOF +font.2=AvantGarde-Demi ISOF +font.3=AvantGarde-DemiOblique ISOF +font.4=Bookman-Demi ISOF +font.5=Bookman-DemiItalic ISOF +font.6=Bookman-Light ISOF +font.7=Bookman-LightItalic ISOF +font.8=Courier ISOF +font.9=Courier-Bold ISOF +font.10=Courier-BoldOblique ISOF +font.11=Courier-Oblique ISOF +font.12=Helvetica ISOF +font.13=Helvetica-Bold ISOF +font.14=Helvetica-BoldOblique ISOF +font.15=Helvetica-Narrow ISOF +font.16=Helvetica-Narrow-Bold ISOF +font.17=Helvetica-Narrow-BoldOblique ISOF +font.18=Helvetica-Narrow-Oblique ISOF +font.19=Helvetica-Oblique ISOF +font.20=NewCenturySchlbk-Bold ISOF +font.21=NewCenturySchlbk-BoldItalic ISOF +font.22=NewCenturySchlbk-Italic ISOF +font.23=NewCenturySchlbk-Roman ISOF +font.24=Palatino-Bold ISOF +font.25=Palatino-BoldItalic ISOF +font.26=Palatino-Italic ISOF +font.27=Palatino-Roman ISOF +font.28=Symbol findfont +font.29=Times-Bold ISOF +font.30=Times-BoldItalic ISOF +font.31=Times-Italic ISOF +font.32=Times-Roman ISOF +font.33=ZapfDingbats findfont +font.34=ZapfChancery-MediumItalic ISOF +# diff --git a/FCL/src/main/assets/java/lib/resources.jar b/FCL/src/main/assets/java/lib/resources.jar new file mode 100644 index 00000000..2d389f9e Binary files /dev/null and b/FCL/src/main/assets/java/lib/resources.jar differ diff --git a/FCL/src/main/assets/java/lib/rt.jar b/FCL/src/main/assets/java/lib/rt.jar new file mode 100644 index 00000000..0f071834 Binary files /dev/null and b/FCL/src/main/assets/java/lib/rt.jar differ diff --git a/FCL/src/main/assets/java/lib/security/blacklisted.certs b/FCL/src/main/assets/java/lib/security/blacklisted.certs new file mode 100644 index 00000000..beded9ed --- /dev/null +++ b/FCL/src/main/assets/java/lib/security/blacklisted.certs @@ -0,0 +1,39 @@ +Algorithm=SHA-256 +03DB9E5E79FE6117177F81C11595AF598CB176AF766290DBCEB2C318B32E39A2 +08C396C006A21055D00826A5781A5CCFCE2C8D053AB3C197637A4A7A5BB9A650 +14E6D2764A4B06701C6CBC376A253775F79C782FBCB6C0EE6F99DE4BA1024ADD +1C5E6985ACC09221DBD1A4B7BBC6D3A8C3F8540D19F20763A9537FDD42B4FFE7 +1F6BF8A3F2399AF7FD04516C2719C566CBAD51F412738F66D0457E1E6BDE6F2D +2A464E4113141352C7962FBD1706ED4B88533EF24D7BBA6CCC5D797FD202F1C4 +31C8FD37DB9B56E708B03D1F01848B068C6DA66F36FB5D82C008C6040FA3E133 +3946901F46B0071E90D78279E82FABABCA177231A704BE72C5B0E8918566EA66 +3E11CF90719F6FB44D94EAC9A156B89BEBE7B8598F28EC58913F2BFCAF91D0C0 +423279423B9FC8CB06F1BB7C3B247522B948D5F18939F378ECC901126DE40BFB +450F1B421BB05C8609854884559C323319619E8B06B001EA2DCBB74A23AA3BE2 +4CBBF8256BC9888A8007B2F386940A2E394378B0D903CBB3863C5A6394B889CE +4FEE0163686ECBD65DB968E7494F55D84B25486D438E9DE558D629D28CD4D176 +535D04DFCE027C70BD5F8A9E0AD4F218E9AFDCF5BBCF9B6DE0D81E148E2E3172 +568FAF38D9F155F624838E2181B1CEB4D8459305EE652B0F810C97C3611BFE19 +585CFE6B7436CBD4E732763A2137D7F49599BA9B1790E688FCEC799C58EB84A6 +5E83124D68D24E8E177E306DF643D5EA99C5A94D6FC34B072F7544A1CABB7C7B +71CB00749B9130FB2707A2664BFF958D0FCC8E161D9674C7450BA0FC2BEAF9D3 +76A45A496031E4DD2D7ED23E8F6FF97DBDEA980BAAC8B0BA94D7EDB551348645 +8A1BD21661C60015065212CC98B1ABB50DFD14C872A208E66BAE890F25C448AF +9ED8F9B0E8E42A1656B8E1DD18F42BA42DC06FE52686173BA2FC70E756F207DC +9FADCE80D62A959F9930D748488C1E22E821F4E1E4A43584B848C2FC11E04D77 +A686FEE577C88AB664D0787ECDFFF035F4806F3DE418DC9E4D516324FFF02083 +A90132CEA1D4F7185E4F688EFFD16F6AC14DFD78356A807599A5DABBEEF3333E +B8686723E415534BC0DBD16326F9486F85B0B0799BF6639334E61DAAE67F36CD +C0D1F42B9F4BF7ACC045B7BB5D4805E10737F67B6310CE505248D543D0D5FE07 +D0156949F1381943442C6974E9B5B49EF441BB799EF20477B90A89C3F33620CE +D151962D954970501C60079258EBCFA38502E0A9F03CD640322B08C0A3117FE5 +D24566BF315F4E597D6E381C87119FB4198F5E9E2607F5F4AB362EF7E2E7672F +D3A936E1A7775A45217C8296A1F22AC5631DCDEC45594099E78EEEBBEDCBA967 +D6CEAE5D9E047FAF7D797858D229AC991AD44316D1E2A37A21926D763153593A +DF21016B00FC54F9FE3BC8B039911BB216E9162FAD2FD14D990AB96E951B49BE +E0E740E4B0F8B3548181FF75B5372FAF4C70B99EC995D694ED0FB91B03FF8D21 +EC30C9C3065A06BB07DC5B1C6B497F370C1CA65C0F30C08E042BA6BCECC78F2C +F5B6F88F75D391A4B1EB336F9E201239FB6B1377DB8CFA7B84736216E5AFFFD7 +FBB12938ABD86C125796EDF4162D291028890A7D6C0C1CCA75FD4B95EBFA7A1A +FC02FD48DB92D4DCE6F11679D38354CF750CFC7F584A520EB90BDE80E241F2BD +FDEDB5BDFCB67411513A61AEE5CB5B5D7C52AF06028EFC996CC1B05B1D6CEA2B diff --git a/FCL/src/main/assets/java/lib/security/cacerts b/FCL/src/main/assets/java/lib/security/cacerts new file mode 100644 index 00000000..86b4c72a Binary files /dev/null and b/FCL/src/main/assets/java/lib/security/cacerts differ diff --git a/FCL/src/main/assets/java/lib/security/java.policy b/FCL/src/main/assets/java/lib/security/java.policy new file mode 100644 index 00000000..59d99a96 --- /dev/null +++ b/FCL/src/main/assets/java/lib/security/java.policy @@ -0,0 +1,51 @@ + +// Standard extensions get all permissions by default + +grant codeBase "file:${{java.ext.dirs}}/*" { + permission java.security.AllPermission; +}; + +// default permissions granted to all domains + +grant { + // Allows any thread to stop itself using the java.lang.Thread.stop() + // method that takes no argument. + // Note that this permission is granted by default only to remain + // backwards compatible. + // It is strongly recommended that you either remove this permission + // from this policy file or further restrict it to code sources + // that you specify, because Thread.stop() is potentially unsafe. + // See the API specification of java.lang.Thread.stop() for more + // information. + permission java.lang.RuntimePermission "stopThread"; + + // allows anyone to listen on dynamic ports + permission java.net.SocketPermission "localhost:0", "listen"; + + // "standard" properies that can be read by anyone + + permission java.util.PropertyPermission "java.version", "read"; + permission java.util.PropertyPermission "java.vendor", "read"; + permission java.util.PropertyPermission "java.vendor.url", "read"; + permission java.util.PropertyPermission "java.class.version", "read"; + permission java.util.PropertyPermission "os.name", "read"; + permission java.util.PropertyPermission "os.version", "read"; + permission java.util.PropertyPermission "os.arch", "read"; + permission java.util.PropertyPermission "file.separator", "read"; + permission java.util.PropertyPermission "path.separator", "read"; + permission java.util.PropertyPermission "line.separator", "read"; + + permission java.util.PropertyPermission "java.specification.version", "read"; + permission java.util.PropertyPermission "java.specification.vendor", "read"; + permission java.util.PropertyPermission "java.specification.name", "read"; + + permission java.util.PropertyPermission "java.vm.specification.version", "read"; + permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; + permission java.util.PropertyPermission "java.vm.specification.name", "read"; + permission java.util.PropertyPermission "java.vm.version", "read"; + permission java.util.PropertyPermission "java.vm.vendor", "read"; + permission java.util.PropertyPermission "java.vm.name", "read"; + + permission java.util.PropertyPermission "sun.security.pkcs11.disableKeyExtraction", "read"; +}; + diff --git a/FCL/src/main/assets/java/lib/security/java.security b/FCL/src/main/assets/java/lib/security/java.security new file mode 100644 index 00000000..87f8a313 --- /dev/null +++ b/FCL/src/main/assets/java/lib/security/java.security @@ -0,0 +1,1222 @@ +# +# This is the "master security properties file". +# +# An alternate java.security properties file may be specified +# from the command line via the system property +# +# -Djava.security.properties= +# +# This properties file appends to the master security properties file. +# If both properties files specify values for the same key, the value +# from the command-line properties file is selected, as it is the last +# one loaded. +# +# Also, if you specify +# +# -Djava.security.properties== (2 equals), +# +# then that properties file completely overrides the master security +# properties file. +# +# To disable the ability to specify an additional properties file from +# the command line, set the key security.overridePropertiesFile +# to false in the master security properties file. It is set to true +# by default. + +# In this file, various security properties are set for use by +# java.security classes. This is where users can statically register +# Cryptography Package Providers ("providers" for short). The term +# "provider" refers to a package or set of packages that supply a +# concrete implementation of a subset of the cryptography aspects of +# the Java Security API. A provider may, for example, implement one or +# more digital signature algorithms or message digest algorithms. +# +# Each provider must implement a subclass of the Provider class. +# To register a provider in this master security properties file, +# specify the Provider subclass name and priority in the format +# +# security.provider.= +# +# This declares a provider, and specifies its preference +# order n. The preference order is the order in which providers are +# searched for requested algorithms (when no specific provider is +# requested). The order is 1-based; 1 is the most preferred, followed +# by 2, and so on. +# +# must specify the subclass of the Provider class whose +# constructor sets the values of various properties that are required +# for the Java Security API to look up the algorithms or other +# facilities implemented by the provider. +# +# There must be at least one provider specification in java.security. +# There is a default provider that comes standard with the JDK. It +# is called the "SUN" provider, and its Provider subclass +# named Sun appears in the sun.security.provider package. Thus, the +# "SUN" provider is registered via the following: +# +# security.provider.1=sun.security.provider.Sun +# +# (The number 1 is used for the default provider.) +# +# Note: Providers can be dynamically registered instead by calls to +# either the addProvider or insertProviderAt method in the Security +# class. + +# +# List of providers and their preference orders (see above): +# +security.provider.1=sun.security.provider.Sun +security.provider.2=sun.security.rsa.SunRsaSign +security.provider.3=sun.security.ec.SunEC +security.provider.4=com.sun.net.ssl.internal.ssl.Provider +security.provider.5=com.sun.crypto.provider.SunJCE +security.provider.6=sun.security.jgss.SunProvider +security.provider.7=com.sun.security.sasl.Provider +security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI +security.provider.9=sun.security.smartcardio.SunPCSC + +# +# Sun Provider SecureRandom seed source. +# +# Select the primary source of seed data for the "SHA1PRNG" and +# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# (Other SecureRandom implementations might also use this property.) +# +# On Unix-like systems (for example, Solaris/Linux/MacOS), the +# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# special device files such as file:/dev/random. +# +# On Windows systems, specifying the URLs "file:/dev/random" or +# "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding +# mechanism for SHA1PRNG. +# +# By default, an attempt is made to use the entropy gathering device +# specified by the "securerandom.source" Security property. If an +# exception occurs while accessing the specified URL: +# +# SHA1PRNG: +# the traditional system/thread activity algorithm will be used. +# +# NativePRNG: +# a default value of /dev/random will be used. If neither +# are available, the implementation will be disabled. +# "file" is the only currently supported protocol type. +# +# The entropy gathering device can also be specified with the System +# property "java.security.egd". For example: +# +# % java -Djava.security.egd=file:/dev/random MainClass +# +# Specifying this System property will override the +# "securerandom.source" Security property. +# +# In addition, if "file:/dev/random" or "file:/dev/urandom" is +# specified, the "NativePRNG" implementation will be more preferred than +# SHA1PRNG in the Sun provider. +# +securerandom.source=file:/dev/random + +# +# A list of known strong SecureRandom implementations. +# +# To help guide applications in selecting a suitable strong +# java.security.SecureRandom implementation, Java distributions should +# indicate a list of known strong implementations using the property. +# +# This is a comma-separated list of algorithm and/or algorithm:provider +# entries. +# +securerandom.strongAlgorithms=NativePRNGBlocking:SUN + +# +# Class to instantiate as the javax.security.auth.login.Configuration +# provider. +# +login.configuration.provider=sun.security.provider.ConfigFile + +# +# Default login configuration file +# +#login.config.url.1=file:${user.home}/.java.login.config + +# +# Class to instantiate as the system Policy. This is the name of the class +# that will be used as the Policy object. +# +policy.provider=sun.security.provider.PolicyFile + +# The default is to have a single system-wide policy file, +# and a policy file in the user's home directory. +policy.url.1=file:${java.home}/lib/security/java.policy +policy.url.2=file:${user.home}/.java.policy + +# whether or not we expand properties in the policy file +# if this is set to false, properties (${...}) will not be expanded in policy +# files. +policy.expandProperties=true + +# whether or not we allow an extra policy to be passed on the command line +# with -Djava.security.policy=somefile. Comment out this line to disable +# this feature. +policy.allowSystemProperty=true + +# whether or not we look into the IdentityScope for trusted Identities +# when encountering a 1.1 signed JAR file. If the identity is found +# and is trusted, we grant it AllPermission. +policy.ignoreIdentityScope=false + +# +# Default keystore type. +# +keystore.type=jks + +# +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageAccess unless the +# corresponding RuntimePermission ("accessClassInPackage."+package) has +# been granted. +package.access=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.resolver.helpers.,\ + com.sun.org.apache.xml.internal.resolver.readers.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + jdk.xml.internal.,\ + com.sun.activation.registries.,\ + jdk.jfr.events.,\ + jdk.jfr.internal.,\ + jdk.management.jfr.internal. + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageDefinition unless the +# corresponding RuntimePermission ("defineClassInPackage."+package) has +# been granted. +# +# by default, none of the class loaders supplied with the JDK call +# checkPackageDefinition. +# +package.definition=sun.,\ + com.sun.xml.internal.,\ + com.sun.imageio.,\ + com.sun.istack.internal.,\ + com.sun.jmx.,\ + com.sun.media.sound.,\ + com.sun.naming.internal.,\ + com.sun.proxy.,\ + com.sun.corba.se.,\ + com.sun.org.apache.bcel.internal.,\ + com.sun.org.apache.regexp.internal.,\ + com.sun.org.apache.xerces.internal.,\ + com.sun.org.apache.xpath.internal.,\ + com.sun.org.apache.xalan.internal.extensions.,\ + com.sun.org.apache.xalan.internal.lib.,\ + com.sun.org.apache.xalan.internal.res.,\ + com.sun.org.apache.xalan.internal.templates.,\ + com.sun.org.apache.xalan.internal.utils.,\ + com.sun.org.apache.xalan.internal.xslt.,\ + com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ + com.sun.org.apache.xalan.internal.xsltc.compiler.,\ + com.sun.org.apache.xalan.internal.xsltc.trax.,\ + com.sun.org.apache.xalan.internal.xsltc.util.,\ + com.sun.org.apache.xml.internal.res.,\ + com.sun.org.apache.xml.internal.resolver.helpers.,\ + com.sun.org.apache.xml.internal.resolver.readers.,\ + com.sun.org.apache.xml.internal.security.,\ + com.sun.org.apache.xml.internal.serializer.utils.,\ + com.sun.org.apache.xml.internal.utils.,\ + com.sun.org.glassfish.,\ + com.oracle.xmlns.internal.,\ + com.oracle.webservices.internal.,\ + oracle.jrockit.jfr.,\ + org.jcp.xml.dsig.internal.,\ + jdk.internal.,\ + jdk.nashorn.internal.,\ + jdk.nashorn.tools.,\ + jdk.xml.internal.,\ + com.sun.activation.registries.,\ + jdk.jfr.events.,\ + jdk.jfr.internal.,\ + jdk.management.jfr.internal. + +# +# Determines whether this properties file can be appended to +# or overridden on the command line via -Djava.security.properties +# +security.overridePropertiesFile=true + +# +# Determines the default key and trust manager factory algorithms for +# the javax.net.ssl package. +# +ssl.KeyManagerFactory.algorithm=SunX509 +ssl.TrustManagerFactory.algorithm=PKIX + +# +# The Java-level namelookup cache policy for successful lookups: +# +# any negative value: caching forever +# any positive value: the number of seconds to cache an address for +# zero: do not cache +# +# default value is forever (FOREVER). For security reasons, this +# caching is made forever when a security manager is set. When a security +# manager is not set, the default behavior in this implementation +# is to cache for 30 seconds. +# +# NOTE: setting this to anything other than the default value can have +# serious security implications. Do not set it unless +# you are sure you are not exposed to DNS spoofing attack. +# +#networkaddress.cache.ttl=-1 + +# The Java-level namelookup cache policy for failed lookups: +# +# any negative value: cache forever +# any positive value: the number of seconds to cache negative lookup results +# zero: do not cache +# +# In some Microsoft Windows networking environments that employ +# the WINS name service in addition to DNS, name service lookups +# that fail may take a noticeably long time to return (approx. 5 seconds). +# For this reason the default caching policy is to maintain these +# results for 10 seconds. +# +# +networkaddress.cache.negative.ttl=10 + +# +# Properties to configure OCSP for certificate revocation checking +# + +# Enable OCSP +# +# By default, OCSP is not used for certificate revocation checking. +# This property enables the use of OCSP when set to the value "true". +# +# NOTE: SocketPermission is required to connect to an OCSP responder. +# +# Example, +# ocsp.enable=true + +# +# Location of the OCSP responder +# +# By default, the location of the OCSP responder is determined implicitly +# from the certificate being validated. This property explicitly specifies +# the location of the OCSP responder. The property is used when the +# Authority Information Access extension (defined in RFC 5280) is absent +# from the certificate or when it requires overriding. +# +# Example, +# ocsp.responderURL=http://ocsp.example.net:80 + +# +# Subject name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. In cases where +# the subject name alone is not sufficient to uniquely identify the certificate +# then both the "ocsp.responderCertIssuerName" and +# "ocsp.responderCertSerialNumber" properties must be used instead. When this +# property is set then those two properties are ignored. +# +# Example, +# ocsp.responderCertSubjectName="CN=OCSP Responder, O=XYZ Corp" + +# +# Issuer name of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# distinguished name (defined in RFC 2253) which identifies a certificate in +# the set of certificates supplied during cert path validation. When this +# property is set then the "ocsp.responderCertSerialNumber" property must also +# be set. When the "ocsp.responderCertSubjectName" property is set then this +# property is ignored. +# +# Example, +# ocsp.responderCertIssuerName="CN=Enterprise CA, O=XYZ Corp" + +# +# Serial number of the OCSP responder's certificate +# +# By default, the certificate of the OCSP responder is that of the issuer +# of the certificate being validated. This property identifies the certificate +# of the OCSP responder when the default does not apply. Its value is a string +# of hexadecimal digits (colon or space separators may be present) which +# identifies a certificate in the set of certificates supplied during cert path +# validation. When this property is set then the "ocsp.responderCertIssuerName" +# property must also be set. When the "ocsp.responderCertSubjectName" property +# is set then this property is ignored. +# +# Example, +# ocsp.responderCertSerialNumber=2A:FF:00 + +# +# Policy for failed Kerberos KDC lookups: +# +# When a KDC is unavailable (network error, service failure, etc), it is +# put inside a blacklist and accessed less often for future requests. The +# value (case-insensitive) for this policy can be: +# +# tryLast +# KDCs in the blacklist are always tried after those not on the list. +# +# tryLess[:max_retries,timeout] +# KDCs in the blacklist are still tried by their order in the configuration, +# but with smaller max_retries and timeout values. max_retries and timeout +# are optional numerical parameters (default 1 and 5000, which means once +# and 5 seconds). Please notes that if any of the values defined here is +# more than what is defined in krb5.conf, it will be ignored. +# +# Whenever a KDC is detected as available, it is removed from the blacklist. +# The blacklist is reset when krb5.conf is reloaded. You can add +# refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is +# reloaded whenever a JAAS authentication is attempted. +# +# Example, +# krb5.kdc.bad.policy = tryLast +# krb5.kdc.bad.policy = tryLess:2,2000 +krb5.kdc.bad.policy = tryLast + +# +# Kerberos cross-realm referrals (RFC 6806) +# +# OpenJDK's Kerberos client supports cross-realm referrals as defined in +# RFC 6806. This allows to setup more dynamic environments in which clients +# do not need to know in advance how to reach the realm of a target principal +# (either a user or service). +# +# When a client issues an AS or a TGS request, the "canonicalize" option +# is set to announce support of this feature. A KDC server may fulfill the +# request or reply referring the client to a different one. If referred, +# the client will issue a new request and the cycle repeats. +# +# In addition to referrals, the "canonicalize" option allows the KDC server +# to change the client name in response to an AS request. For security reasons, +# RFC 6806 (section 11) FAST scheme is enforced. +# +# Disable Kerberos cross-realm referrals. Value may be overwritten with a +# System property (-Dsun.security.krb5.disableReferrals). +sun.security.krb5.disableReferrals=false + +# Maximum number of AS or TGS referrals to avoid infinite loops. Value may +# be overwritten with a System property (-Dsun.security.krb5.maxReferrals). +sun.security.krb5.maxReferrals=5 + +# +# This property contains a list of disabled EC Named Curves that can be included +# in the jdk.[tls|certpath|jar].disabledAlgorithms properties. To include this +# list in any of the disabledAlgorithms properties, add the property name as +# an entry. +jdk.disabled.namedCurves = secp112r1, secp112r2, secp128r1, secp128r2, \ + secp160k1, secp160r1, secp160r2, secp192k1, secp192r1, secp224k1, \ + secp224r1, secp256k1, sect113r1, sect113r2, sect131r1, sect131r2, \ + sect163k1, sect163r1, sect163r2, sect193r1, sect193r2, sect233k1, \ + sect233r1, sect239k1, sect283k1, sect283r1, sect409k1, sect409r1, \ + sect571k1, sect571r1, X9.62 c2tnb191v1, X9.62 c2tnb191v2, \ + X9.62 c2tnb191v3, X9.62 c2tnb239v1, X9.62 c2tnb239v2, X9.62 c2tnb239v3, \ + X9.62 c2tnb359v1, X9.62 c2tnb431r1, X9.62 prime192v2, X9.62 prime192v3, \ + X9.62 prime239v1, X9.62 prime239v2, X9.62 prime239v3, brainpoolP256r1, \ + brainpoolP320r1, brainpoolP384r1, brainpoolP512r1 + +# +# Algorithm restrictions for certification path (CertPath) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# for certification path building and validation. For example, "MD2" is +# generally no longer considered to be a secure hash algorithm. This section +# describes the mechanism for disabling algorithms based on algorithm name +# and/or key length. This includes algorithms used in certificates, as well +# as revocation information such as CRLs and signed OCSP Responses. +# The syntax of the disabled algorithm string is described as follows: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] { '&' Constraint } | IncludeProperty +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint | CAConstraint | DenyAfterConstraint | +# UsageConstraint +# +# KeySizeConstraint: +# keySize Operator KeyLength +# +# Operator: +# <= | < | == | != | >= | > +# +# KeyLength: +# Integer value of the algorithm's key length in bits +# +# CAConstraint: +# jdkCA +# +# DenyAfterConstraint: +# denyAfter YYYY-MM-DD +# +# UsageConstraint: +# usage [TLSServer] [TLSClient] [SignedJAR] +# +# IncludeProperty: +# include +# +# The "AlgorithmName" is the standard algorithm name of the disabled +# algorithm. See "Java Cryptography Architecture Standard Algorithm Name +# Documentation" for information about Standard Algorithm Names. Matching +# is performed using a case-insensitive sub-element matching rule. (For +# example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and +# "ECDSA" for signatures.) If the assertion "AlgorithmName" is a +# sub-element of the certificate algorithm name, the algorithm will be +# rejected during certification path building and validation. For example, +# the assertion algorithm name "DSA" will disable all certificate algorithms +# that rely on DSA, such as NONEwithDSA, SHA1withDSA. However, the assertion +# will not disable algorithms related to "ECDSA". +# +# The "IncludeProperty" allows a implementation-defined security property that +# can be included in the disabledAlgorithms properties. These properties are +# to help manage common actions easier across multiple disabledAlgorithm +# properties. +# There is one defined security property: jdk.disabled.NamedCurves +# See the property for more specific details. +# +# +# A "Constraint" defines restrictions on the keys and/or certificates for +# a specified AlgorithmName: +# +# KeySizeConstraint: +# keySize Operator KeyLength +# The constraint requires a key of a valid size range if the +# "AlgorithmName" is of a key algorithm. The "KeyLength" indicates +# the key size specified in number of bits. For example, +# "RSA keySize <= 1024" indicates that any RSA key with key size less +# than or equal to 1024 bits should be disabled, and +# "RSA keySize < 1024, RSA keySize > 2048" indicates that any RSA key +# with key size less than 1024 or greater than 2048 should be disabled. +# This constraint is only used on algorithms that have a key size. +# +# CAConstraint: +# jdkCA +# This constraint prohibits the specified algorithm only if the +# algorithm is used in a certificate chain that terminates at a marked +# trust anchor in the lib/security/cacerts keystore. If the jdkCA +# constraint is not set, then all chains using the specified algorithm +# are restricted. jdkCA may only be used once in a DisabledAlgorithm +# expression. +# Example: To apply this constraint to SHA-1 certificates, include +# the following: "SHA1 jdkCA" +# +# DenyAfterConstraint: +# denyAfter YYYY-MM-DD +# This constraint prohibits a certificate with the specified algorithm +# from being used after the date regardless of the certificate's +# validity. JAR files that are signed and timestamped before the +# constraint date with certificates containing the disabled algorithm +# will not be restricted. The date is processed in the UTC timezone. +# This constraint can only be used once in a DisabledAlgorithm +# expression. +# Example: To deny usage of RSA 2048 bit certificates after Feb 3 2020, +# use the following: "RSA keySize == 2048 & denyAfter 2020-02-03" +# +# UsageConstraint: +# usage [TLSServer] [TLSClient] [SignedJAR] +# This constraint prohibits the specified algorithm for +# a specified usage. This should be used when disabling an algorithm +# for all usages is not practical. 'TLSServer' restricts the algorithm +# in TLS server certificate chains when server authentication is +# performed. 'TLSClient' restricts the algorithm in TLS client +# certificate chains when client authentication is performed. +# 'SignedJAR' constrains use of certificates in signed jar files. +# The usage type follows the keyword and more than one usage type can +# be specified with a whitespace delimiter. +# Example: "SHA1 usage TLSServer TLSClient" +# +# When an algorithm must satisfy more than one constraint, it must be +# delimited by an ampersand '&'. For example, to restrict certificates in a +# chain that terminate at a distribution provided trust anchor and contain +# RSA keys that are less than or equal to 1024 bits, add the following +# constraint: "RSA keySize <= 1024 & jdkCA". +# +# All DisabledAlgorithms expressions are processed in the order defined in the +# property. This requires lower keysize constraints to be specified +# before larger keysize constraints of the same algorithm. For example: +# "RSA keySize < 1024 & jdkCA, RSA keySize < 2048". +# +# Note: The algorithm restrictions do not apply to trust anchors or +# self-signed certificates. +# +# Note: This property is currently used by Oracle's PKIX implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048 +# +# +jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \ + RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224, \ + include jdk.disabled.namedCurves + +# +# Legacy algorithms for certification path (CertPath) processing and +# signed JAR files. +# +# In some environments, a certain algorithm or key length may be undesirable +# but is not yet disabled. +# +# Tools such as keytool and jarsigner may emit warnings when these legacy +# algorithms are used. See the man pages for those tools for more information. +# +# The syntax is the same as the "jdk.certpath.disabledAlgorithms" and +# "jdk.jar.disabledAlgorithms" security properties. +# +# Note: This property is currently used by the JDK Reference +# implementation. It is not guaranteed to be examined and used by other +# implementations. + +jdk.security.legacyAlgorithms=SHA1, \ + RSA keySize < 2048, DSA keySize < 2048 + +# +# Algorithm restrictions for signed JAR files +# +# In some environments, certain algorithms or key lengths may be undesirable +# for signed JAR validation. For example, "MD2" is generally no longer +# considered to be a secure hash algorithm. This section describes the +# mechanism for disabling algorithms based on algorithm name and/or key length. +# JARs signed with any of the disabled algorithms or key sizes will be treated +# as unsigned. +# +# The syntax of the disabled algorithm string is described as follows: +# DisabledAlgorithms: +# " DisabledAlgorithm { , DisabledAlgorithm } " +# +# DisabledAlgorithm: +# AlgorithmName [Constraint] { '&' Constraint } +# +# AlgorithmName: +# (see below) +# +# Constraint: +# KeySizeConstraint | DenyAfterConstraint +# +# KeySizeConstraint: +# keySize Operator KeyLength +# +# DenyAfterConstraint: +# denyAfter YYYY-MM-DD +# +# Operator: +# <= | < | == | != | >= | > +# +# KeyLength: +# Integer value of the algorithm's key length in bits +# +# Note: This property is currently used by the JDK Reference +# implementation. It is not guaranteed to be examined and used by other +# implementations. +# +# See "jdk.certpath.disabledAlgorithms" for syntax descriptions. +# +jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ + DSA keySize < 1024, include jdk.disabled.namedCurves + +# +# Algorithm restrictions for Secure Socket Layer/Transport Layer Security +# (SSL/TLS) processing +# +# In some environments, certain algorithms or key lengths may be undesirable +# when using SSL/TLS. This section describes the mechanism for disabling +# algorithms during SSL/TLS security parameters negotiation, including +# protocol version negotiation, cipher suites selection, peer authentication +# and key exchange mechanisms. +# +# Disabled algorithms will not be negotiated for SSL/TLS connections, even +# if they are enabled explicitly in an application. +# +# For PKI-based peer authentication and key exchange mechanisms, this list +# of disabled algorithms will also be checked during certification path +# building and validation, including algorithms used in certificates, as +# well as revocation information such as CRLs and signed OCSP Responses. +# This is in addition to the jdk.certpath.disabledAlgorithms property above. +# +# See the specification of "jdk.certpath.disabledAlgorithms" for the +# syntax of the disabled algorithm string. +# +# Note: The algorithm restrictions do not apply to trust anchors or +# self-signed certificates. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 +jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \ + DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ + include jdk.disabled.namedCurves + +# Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) +# processing in JSSE implementation. +# +# In some environments, a certain algorithm may be undesirable but it +# cannot be disabled because of its use in legacy applications. Legacy +# algorithms may still be supported, but applications should not use them +# as the security strength of legacy algorithms are usually not strong enough +# in practice. +# +# During SSL/TLS security parameters negotiation, legacy algorithms will +# not be negotiated unless there are no other candidates. +# +# The syntax of the legacy algorithms string is described as this Java +# BNF-style: +# LegacyAlgorithms: +# " LegacyAlgorithm { , LegacyAlgorithm } " +# +# LegacyAlgorithm: +# AlgorithmName (standard JSSE algorithm name) +# +# See the specification of security property "jdk.certpath.disabledAlgorithms" +# for the syntax and description of the "AlgorithmName" notation. +# +# Per SSL/TLS specifications, cipher suites have the form: +# SSL_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# or +# TLS_KeyExchangeAlg_WITH_CipherAlg_MacAlg +# +# For example, the cipher suite TLS_RSA_WITH_AES_128_CBC_SHA uses RSA as the +# key exchange algorithm, AES_128_CBC (128 bits AES cipher algorithm in CBC +# mode) as the cipher (encryption) algorithm, and SHA-1 as the message digest +# algorithm for HMAC. +# +# The LegacyAlgorithm can be one of the following standard algorithm names: +# 1. JSSE cipher suite name, e.g., TLS_RSA_WITH_AES_128_CBC_SHA +# 2. JSSE key exchange algorithm name, e.g., RSA +# 3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC +# 4. JSSE message digest algorithm name, e.g., SHA +# +# See SSL/TLS specifications and "Java Cryptography Architecture Standard +# Algorithm Name Documentation" for information about the algorithm names. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# There is no guarantee the property will continue to exist or be of the +# same syntax in future releases. +# +# Example: +# jdk.tls.legacyAlgorithms=DH_anon, DES_CBC, SSL_RSA_WITH_RC4_128_MD5 +# +jdk.tls.legacyAlgorithms= \ + K_NULL, C_NULL, M_NULL, \ + DH_anon, ECDH_anon, \ + RC4_128, RC4_40, DES_CBC, DES40_CBC, \ + 3DES_EDE_CBC + +# The pre-defined default finite field Diffie-Hellman ephemeral (DHE) +# parameters for Transport Layer Security (SSL/TLS/DTLS) processing. +# +# In traditional SSL/TLS/DTLS connections where finite field DHE parameters +# negotiation mechanism is not used, the server offers the client group +# parameters, base generator g and prime modulus p, for DHE key exchange. +# It is recommended to use dynamic group parameters. This property defines +# a mechanism that allows you to specify custom group parameters. +# +# The syntax of this property string is described as this Java BNF-style: +# DefaultDHEParameters: +# DefinedDHEParameters { , DefinedDHEParameters } +# +# DefinedDHEParameters: +# "{" DHEPrimeModulus , DHEBaseGenerator "}" +# +# DHEPrimeModulus: +# HexadecimalDigits +# +# DHEBaseGenerator: +# HexadecimalDigits +# +# HexadecimalDigits: +# HexadecimalDigit { HexadecimalDigit } +# +# HexadecimalDigit: one of +# 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f +# +# Whitespace characters are ignored. +# +# The "DefinedDHEParameters" defines the custom group parameters, prime +# modulus p and base generator g, for a particular size of prime modulus p. +# The "DHEPrimeModulus" defines the hexadecimal prime modulus p, and the +# "DHEBaseGenerator" defines the hexadecimal base generator g of a group +# parameter. It is recommended to use safe primes for the custom group +# parameters. +# +# If this property is not defined or the value is empty, the underlying JSSE +# provider's default group parameter is used for each connection. +# +# If the property value does not follow the grammar, or a particular group +# parameter is not valid, the connection will fall back and use the +# underlying JSSE provider's default group parameter. +# +# Note: This property is currently used by OpenJDK's JSSE implementation. It +# is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.tls.server.defaultDHEParameters= +# { \ +# FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 \ +# 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD \ +# EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 \ +# E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED \ +# EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 \ +# FFFFFFFF FFFFFFFF, 2} + +# +# TLS key limits on symmetric cryptographic algorithms +# +# This security property sets limits on algorithms key usage in TLS 1.3. +# When the amount of data encrypted exceeds the algorithm value listed below, +# a KeyUpdate message will trigger a key change. This is for symmetric ciphers +# with TLS 1.3 only. +# +# The syntax for the property is described below: +# KeyLimits: +# " KeyLimit { , KeyLimit } " +# +# WeakKeyLimit: +# AlgorithmName Action Length +# +# AlgorithmName: +# A full algorithm transformation. +# +# Action: +# KeyUpdate +# +# Length: +# The amount of encrypted data in a session before the Action occurs +# This value may be an integer value in bytes, or as a power of two, 2^29. +# +# KeyUpdate: +# The TLS 1.3 KeyUpdate handshake process begins when the Length amount +# is fulfilled. +# +# Note: This property is currently used by OpenJDK's JSSE implementation. It +# is not guaranteed to be examined and used by other implementations. +# +jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37 + +# Cryptographic Jurisdiction Policy defaults +# +# Import and export control rules on cryptographic software vary from +# country to country. By default, the JDK provides two different sets of +# cryptographic policy files: +# +# unlimited: These policy files contain no restrictions on cryptographic +# strengths or algorithms. +# +# limited: These policy files contain more restricted cryptographic +# strengths, and are still available if your country or +# usage requires the traditional restrictive policy. +# +# The JDK JCE framework uses the unlimited policy files by default. +# However the user may explicitly choose a set either by defining the +# "crypto.policy" Security property or by installing valid JCE policy +# jar files into the traditional JDK installation location. To better +# support older JDK Update releases, the "crypto.policy" property is not +# defined by default. See below for more information. +# +# The following logic determines which policy files are used: +# +# refers to the directory where the JRE was +# installed and may be determined using the "java.home" +# System property. +# +# 1. If the Security property "crypto.policy" has been defined, +# then the following mechanism is used: +# +# The policy files are stored as jar files in subdirectories of +# /lib/security/policy. Each directory contains a complete +# set of policy files. +# +# The "crypto.policy" Security property controls the directory +# selection, and thus the effective cryptographic policy. +# +# The default set of directories is: +# +# limited | unlimited +# +# 2. If the "crypto.policy" property is not set and the traditional +# US_export_policy.jar and local_policy.jar files +# (e.g. limited/unlimited) are found in the legacy +# /lib/security directory, then the rules embedded within +# those jar files will be used. This helps preserve compatibility +# for users upgrading from an older installation. +# +# 3. If the jar files are not present in the legacy location +# and the "crypto.policy" Security property is not defined, +# then the JDK will use the unlimited settings (equivalent to +# crypto.policy=unlimited) +# +# Please see the JCA documentation for additional information on these +# files and formats. +# +# YOU ARE ADVISED TO CONSULT YOUR EXPORT/IMPORT CONTROL COUNSEL OR ATTORNEY +# TO DETERMINE THE EXACT REQUIREMENTS. +# +# Please note that the JCE for Java SE, including the JCE framework, +# cryptographic policy files, and standard JCE providers provided with +# the Java SE, have been reviewed and approved for export as mass market +# encryption item by the US Bureau of Industry and Security. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +#crypto.policy=unlimited + +# +# The policy for the XML Signature secure validation mode. The mode is +# enabled by setting the property "org.jcp.xml.dsig.secureValidation" to +# true with the javax.xml.crypto.XMLCryptoContext.setProperty() method, +# or by running the code with a SecurityManager. +# +# Policy: +# Constraint {"," Constraint } +# Constraint: +# AlgConstraint | MaxTransformsConstraint | MaxReferencesConstraint | +# ReferenceUriSchemeConstraint | KeySizeConstraint | OtherConstraint +# AlgConstraint +# "disallowAlg" Uri +# MaxTransformsConstraint: +# "maxTransforms" Integer +# MaxReferencesConstraint: +# "maxReferences" Integer +# ReferenceUriSchemeConstraint: +# "disallowReferenceUriSchemes" String { String } +# KeySizeConstraint: +# "minKeySize" KeyAlg Integer +# OtherConstraint: +# "noDuplicateIds" | "noRetrievalMethodLoops" +# +# For AlgConstraint, Uri is the algorithm URI String that is not allowed. +# See the XML Signature Recommendation for more information on algorithm +# URI Identifiers. For KeySizeConstraint, KeyAlg is the standard algorithm +# name of the key type (ex: "RSA"). If the MaxTransformsConstraint, +# MaxReferencesConstraint or KeySizeConstraint (for the same key type) is +# specified more than once, only the last entry is enforced. +# +# Note: This property is currently used by the JDK Reference implementation. It +# is not guaranteed to be examined and used by other implementations. +# +jdk.xml.dsig.secureValidationPolicy=\ + disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\ + disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\ + maxTransforms 5,\ + maxReferences 30,\ + disallowReferenceUriSchemes file http https,\ + minKeySize RSA 1024,\ + minKeySize DSA 1024,\ + minKeySize EC 224,\ + noDuplicateIds,\ + noRetrievalMethodLoops + +# +# Serialization process-wide filter +# +# A filter, if configured, is used by java.io.ObjectInputStream during +# deserialization to check the contents of the stream. +# A filter is configured as a sequence of patterns, each pattern is either +# matched against the name of a class in the stream or defines a limit. +# Patterns are separated by ";" (semicolon). +# Whitespace is significant and is considered part of the pattern. +# +# If the system property jdk.serialFilter is also specified on the command +# line, it supersedes the security property value defined here. +# +# If a pattern includes a "=", it sets a limit. +# If a limit appears more than once the last value is used. +# Limits are checked before classes regardless of the order in the sequence of patterns. +# If any of the limits are exceeded, the filter status is REJECTED. +# +# maxdepth=value - the maximum depth of a graph +# maxrefs=value - the maximum number of internal references +# maxbytes=value - the maximum number of bytes in the input stream +# maxarray=value - the maximum array length allowed +# +# Other patterns, from left to right, match the class or package name as +# returned from Class.getName. +# If the class is an array type, the class or package to be matched is the element type. +# Arrays of any number of dimensions are treated the same as the element type. +# For example, a pattern of "!example.Foo", rejects creation of any instance or +# array of example.Foo. +# +# If the pattern starts with "!", the status is REJECTED if the remaining pattern +# is matched; otherwise the status is ALLOWED if the pattern matches. +# If the pattern ends with ".**" it matches any class in the package and all subpackages. +# If the pattern ends with ".*" it matches any class in the package. +# If the pattern ends with "*", it matches any class with the pattern as a prefix. +# If the pattern is equal to the class name, it matches. +# Otherwise, the status is UNDECIDED. +# +# Primitive types are not configurable with this filter. +# +#jdk.serialFilter=pattern;pattern + +# +# RMI Registry Serial Filter +# +# The filter pattern uses the same format as jdk.serialFilter. +# This filter can override the builtin filter if additional types need to be +# allowed or rejected from the RMI Registry or to decrease limits but not +# to increase limits. +# If the limits (maxdepth, maxrefs, or maxbytes) are exceeded, the object is rejected. +# +# The maxdepth of any array passed to the RMI Registry is set to +# 10000. The maximum depth of the graph is set to 20. +# These limits can be reduced via the maxarray, maxdepth limits. +# +#sun.rmi.registry.registryFilter=pattern;pattern + +# +# Array construction of any component type, including subarrays and arrays of +# primitives, are allowed unless the length is greater than the maxarray limit. +# The filter is applied to each array element. +# +# The built-in filter allows subclasses of allowed classes and +# can approximately be represented as the pattern: +# +#sun.rmi.registry.registryFilter=\ +# maxarray=1000000;\ +# maxdepth=20;\ +# java.lang.String;\ +# java.lang.Number;\ +# java.lang.reflect.Proxy;\ +# java.rmi.Remote;\ +# sun.rmi.server.UnicastRef;\ +# sun.rmi.server.RMIClientSocketFactory;\ +# sun.rmi.server.RMIServerSocketFactory;\ +# java.rmi.activation.ActivationID;\ +# java.rmi.server.UID +# +# RMI Distributed Garbage Collector (DGC) Serial Filter +# +# The filter pattern uses the same format as jdk.serialFilter. +# This filter can override the builtin filter if additional types need to be +# allowed or rejected from the RMI DGC. +# +# The builtin DGC filter can approximately be represented as the filter pattern: +# +#sun.rmi.transport.dgcFilter=\ +# java.rmi.server.ObjID;\ +# java.rmi.server.UID;\ +# java.rmi.dgc.VMID;\ +# java.rmi.dgc.Lease;\ +# maxdepth=5;maxarray=10000 + +# CORBA ORBIorTypeCheckRegistryFilter +# Type check enhancement for ORB::string_to_object processing +# +# An IOR type check filter, if configured, is used by an ORB during +# an ORB::string_to_object invocation to check the veracity of the type encoded +# in the ior string. +# +# The filter pattern consists of a semi-colon separated list of class names. +# The configured list contains the binary class names of the IDL interface types +# corresponding to the IDL stub class to be instantiated. +# As such, a filter specifies a list of IDL stub classes that will be +# allowed by an ORB when an ORB::string_to_object is invoked. +# It is used to specify a white list configuration of acceptable +# IDL stub types which may be contained in a stringified IOR +# parameter passed as input to an ORB::string_to_object method. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +#com.sun.CORBA.ORBIorTypeCheckRegistryFilter=binary_class_name;binary_class_name + +# +# JCEKS Encrypted Key Serial Filter +# +# This filter, if configured, is used by the JCEKS KeyStore during the +# deserialization of the encrypted Key object stored inside a key entry. +# If not configured or the filter result is UNDECIDED (i.e. none of the patterns +# matches), the filter configured by jdk.serialFilter will be consulted. +# +# If the system property jceks.key.serialFilter is also specified, it supersedes +# the security property value defined here. +# +# The filter pattern uses the same format as jdk.serialFilter. The default +# pattern allows java.lang.Enum, java.security.KeyRep, java.security.KeyRep$Type, +# and javax.crypto.spec.SecretKeySpec and rejects all the others. +jceks.key.serialFilter = java.lang.Enum;java.security.KeyRep;\ + java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;!* + +# The iteration count used for password-based encryption (PBE) in JCEKS +# keystores. Values in the range 10000 to 5000000 are considered valid. +# If the value is out of this range, or is not a number, or is unspecified; +# a default of 200000 is used. +# +# If the system property jdk.jceks.iterationCount is also specified, it +# supersedes the security property value defined here. +# +#jdk.jceks.iterationCount = 200000 + +# +# Disabled mechanisms for the Simple Authentication and Security Layer (SASL) +# +# Disabled mechanisms will not be negotiated by both SASL clients and servers. +# These mechanisms will be ignored if they are specified in the "mechanisms" +# argument of "Sasl.createSaslClient" or the "mechanism" argument of +# "Sasl.createSaslServer". +# +# The value of this property is a comma-separated list of SASL mechanisms. +# The mechanisms are case-sensitive. Whitespaces around the commas are ignored. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# Example: +# jdk.sasl.disabledMechanisms=PLAIN, CRAM-MD5, DIGEST-MD5 +jdk.sasl.disabledMechanisms= + +# +# Policies for distrusting Certificate Authorities (CAs). +# +# This is a comma separated value of one or more case-sensitive strings, each +# of which represents a policy for determining if a CA should be distrusted. +# The supported values are: +# +# +# SYMANTEC_TLS : Distrust TLS Server certificates anchored by a Symantec +# root CA and issued after April 16, 2019 unless issued by one of the +# following subordinate CAs which have a later distrust date: +# 1. Apple IST CA 2 - G1, SHA-256 fingerprint: +# AC2B922ECFD5E01711772FEA8ED372DE9D1E2245FCE3F57A9CDBEC77296A424B +# Distrust after December 31, 2019. +# 2. Apple IST CA 8 - G1, SHA-256 fingerprint: +# A4FE7C7F15155F3F0AEF7AAA83CF6E06DEB97CA3F909DF920AC1490882D488ED +# Distrust after December 31, 2019. +# Leading and trailing whitespace surrounding each value are ignored. +# Unknown values are ignored. If the property is commented out or set to the +# empty String, no policies are enforced. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be supported by other SE implementations. Also, this +# property does not override other security properties which can restrict +# certificates such as jdk.tls.disabledAlgorithms or +# jdk.certpath.disabledAlgorithms; those restrictions are still enforced even +# if this property is not enabled. +# +jdk.security.caDistrustPolicies=SYMANTEC_TLS + +# +# Policies for the proxy_impersonator Kerberos ccache configuration entry +# +# The proxy_impersonator ccache configuration entry indicates that the ccache +# is a synthetic delegated credential for use with S4U2Proxy by an intermediate +# server. The ccache file should also contain the TGT of this server and +# an evidence ticket from the default principal of the ccache to this server. +# +# This security property determines how Java uses this configuration entry. +# There are 3 possible values: +# +# no-impersonate - Ignore this configuration entry, and always act as +# the owner of the TGT (if it exists). +# +# try-impersonate - Try impersonation when this configuration entry exists. +# If no matching TGT or evidence ticket is found, +# fallback to no-impersonate. +# +# always-impersonate - Always impersonate when this configuration entry exists. +# If no matching TGT or evidence ticket is found, +# no initial credential is read from the ccache. +# +# The default value is "always-impersonate". +# +# If a system property of the same name is also specified, it supersedes the +# security property value defined here. +# +#jdk.security.krb5.default.initiate.credential=always-impersonate + +# +# Trust Anchor Certificates - CA Basic Constraint check +# +# X.509 v3 certificates used as Trust Anchors (to validate signed code or TLS +# connections) must have the cA Basic Constraint field set to 'true'. Also, if +# they include a Key Usage extension, the keyCertSign bit must be set. These +# checks, enabled by default, can be disabled for backward-compatibility +# purposes with the jdk.security.allowNonCaAnchor System and Security +# properties. In the case that both properties are simultaneously set, the +# System value prevails. The default value of the property is "false". +# +#jdk.security.allowNonCaAnchor=true + +# +# JNDI Object Factories Filter +# +# This filter is used by the JNDI runtime to control the set of object factory classes +# which will be allowed to instantiate objects from object references returned by +# naming/directory systems. The factory class named by the reference instance will be +# matched against this filter. The filter property supports pattern-based filter syntax +# with the same format as jdk.serialFilter. +# +# Each pattern is matched against the factory class name to allow or disallow it's +# instantiation. The access to a factory class is allowed unless the filter returns +# REJECTED. +# +# Note: This property is currently used by the JDK Reference implementation. +# It is not guaranteed to be examined and used by other implementations. +# +# If the system property jdk.jndi.object.factoriesFilter is also specified, it supersedes +# the security property value defined here. The default value of the property is "*". +# +# The default pattern value allows any object factory class specified by the reference +# instance to recreate the referenced object. +#jdk.jndi.object.factoriesFilter=* \ No newline at end of file diff --git a/FCL/src/main/assets/java/lib/security/policy/limited/US_export_policy.jar b/FCL/src/main/assets/java/lib/security/policy/limited/US_export_policy.jar new file mode 100644 index 00000000..d127b37b Binary files /dev/null and b/FCL/src/main/assets/java/lib/security/policy/limited/US_export_policy.jar differ diff --git a/FCL/src/main/assets/java/lib/security/policy/limited/local_policy.jar b/FCL/src/main/assets/java/lib/security/policy/limited/local_policy.jar new file mode 100644 index 00000000..ad34cf9e Binary files /dev/null and b/FCL/src/main/assets/java/lib/security/policy/limited/local_policy.jar differ diff --git a/FCL/src/main/assets/java/lib/security/policy/unlimited/US_export_policy.jar b/FCL/src/main/assets/java/lib/security/policy/unlimited/US_export_policy.jar new file mode 100644 index 00000000..d127b37b Binary files /dev/null and b/FCL/src/main/assets/java/lib/security/policy/unlimited/US_export_policy.jar differ diff --git a/FCL/src/main/assets/java/lib/security/policy/unlimited/local_policy.jar b/FCL/src/main/assets/java/lib/security/policy/unlimited/local_policy.jar new file mode 100644 index 00000000..a2023205 Binary files /dev/null and b/FCL/src/main/assets/java/lib/security/policy/unlimited/local_policy.jar differ diff --git a/FCL/src/main/assets/java/lib/sound.properties b/FCL/src/main/assets/java/lib/sound.properties new file mode 100644 index 00000000..68309d11 --- /dev/null +++ b/FCL/src/main/assets/java/lib/sound.properties @@ -0,0 +1,39 @@ +############################################################ +# Sound Configuration File +############################################################ +# +# This properties file is used to specify default service +# providers for javax.sound.midi.MidiSystem and +# javax.sound.sampled.AudioSystem. +# +# The following keys are recognized by MidiSystem methods: +# +# javax.sound.midi.Receiver +# javax.sound.midi.Sequencer +# javax.sound.midi.Synthesizer +# javax.sound.midi.Transmitter +# +# The following keys are recognized by AudioSystem methods: +# +# javax.sound.sampled.Clip +# javax.sound.sampled.Port +# javax.sound.sampled.SourceDataLine +# javax.sound.sampled.TargetDataLine +# +# The values specify the full class name of the service +# provider, or the device name. +# +# See the class descriptions for details. +# +# Example 1: +# Use MyDeviceProvider as default for SourceDataLines: +# javax.sound.sampled.SourceDataLine=com.xyz.MyDeviceProvider +# +# Example 2: +# Specify the default Synthesizer by its name "InternalSynth". +# javax.sound.midi.Synthesizer=#InternalSynth +# +# Example 3: +# Specify the default Receiver by provider and name: +# javax.sound.midi.Receiver=com.sun.media.sound.MidiProvider#SunMIDI1 +# diff --git a/FCL/src/main/assets/java/lib/tzdb.dat b/FCL/src/main/assets/java/lib/tzdb.dat new file mode 100644 index 00000000..d070e457 Binary files /dev/null and b/FCL/src/main/assets/java/lib/tzdb.dat differ diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/java.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/java.1 new file mode 100644 index 00000000..dd99bcde --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/java.1 @@ -0,0 +1,2931 @@ +'\" t +.\" Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: java +.\" Language: Japanese +.\" Date: 2015年3月3日 +.\" SectDesc: 基本ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "java" "1" "2015年3月3日" "JDK 8" "基本ツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +java \- Javaアプリケーションを起動します。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava\fR [\fIoptions\fR] \fIclassname\fR [\fIargs\fR] +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava\fR [\fIoptions\fR] \fB\-jar\fR \fIfilename\fR [\fIargs\fR] +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +空白で区切られたコマンド行オプション。オプションを参照してください。 +.RE +.PP +\fIclassname\fR +.RS 4 +起動するクラスの名前。 +.RE +.PP +\fIfilename\fR +.RS 4 +呼び出されるJavaアーカイブ(JAR)ファイルの名前。\fB\-jar\fRオプションと一緒にのみ使用します。 +.RE +.PP +\fIargs\fR +.RS 4 +空白で区切られた\fBmain()\fRメソッドに渡される引数。 +.RE +.SH "説明" +.PP +\fBjava\fRコマンドはJavaアプリケーションを開始します。Java Runtime Environment (JRE)を起動した後、指定したクラスをロードし、そのクラスの\fBmain()\fRメソッドを呼び出すことにより、これを行います。このメソッドは、\fIpublic\fRおよび\fIstatic\fRとして宣言する必要があります。また、値は返せません。さらに、\fBString\fR配列をパラメータとして指定できる必要があります。メソッド宣言は次の形式を含みます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpublic static void main(String[] args)\fR + +.fi +.if n \{\ +.RE +.\} +.PP +\fBjava\fRコマンドを使用して、\fBmain()\fRメソッドがあるか\fBjavafx\&.application\&.Application\fRを拡張するクラスをロードすることで、JavaFXアプリケーションを起動できます。後者の場合、起動ツールは\fBApplication\fRクラスのインスタンスを構成し、その\fBinit()\fRメソッドをコールし、\fBstart(javafx\&.stage\&.Stage)\fRメソッドをコールします。 +.PP +デフォルトでは、\fBjava\fRコマンドのオプションではない最初の引数は、呼び出されるクラスの完全修飾名です。\fB\-jar\fRオプションを指定した場合、その引数は、アプリケーションのクラス・ファイルとリソース・ファイルを含むJARファイルの名前になります。起動クラスは、ソース・コードの\fBMain\-Class\fRマニフェスト・ヘッダーによって指定される必要があります。 +.PP +JREは、ブートストラップ・クラス・パス、インストール済拡張機能およびユーザーのクラス・パスの3箇所から、起動クラス(およびアプリケーションで使用されている他のクラス)を検索します。 +.PP +クラス・ファイル名またはJARファイル名の後の引数は、\fBmain()\fRメソッドに渡されます。 +.SH "オプション" +.PP +\fBjava\fRコマンドは、次のカテゴリに分類できる広範なオプションをサポートしています。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +標準オプション +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +非標準オプション +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +高度なランタイム・オプション +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +高度なJITコンパイラ・オプション +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +高度なサービスアビリティ・オプション +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +高度なガベージ・コレクション・オプション +.RE +.PP +標準のオプションは、Java Virtual Machine (JVM)のすべての実装でサポートすることが保証されます。これらは、JREのバージョンの確認、クラス・パスの設定、冗長な出力の有効化などの一般的なアクションに使用されます。 +.PP +標準以外のオプションは、Java HotSpot仮想マシンに固有の汎用オプションであるため、すべてのJVM実装でサポートされることは保証されず、変更される可能性があります。これらのオプションは\fB\-X\fRで開始します。 +.PP +拡張オプションは、不用意に使用しないことをお薦めします。これらは、特定のシステム要件を持つことが多く、システム構成パラメータへの特権アクセスが必要な場合があるJava HotSpot仮想マシン操作の特定の領域を調整するために使用される開発者オプションです。これらは、すべてのJVM実装でサポートされることは保証されず、変更される可能性があります。拡張オプションは\fB\-XX\fRで始まります。 +.PP +最新リリースで非推奨または削除されるオプションを追跡するために、ドキュメントの最後に非推奨で削除されたオプションという名前のセクションがあります。 +.PP +ブール・オプションは、デフォルトで無効になっている機能を有効にしたり、デフォルトで有効になっている機能を無効にするために使用されます。このようなオプションは、パラメータを必要としません。ブール値\fB\-XX\fRオプションは、プラス記号(\fB\-XX:+\fR\fIOptionName\fR)を使用して有効にし、マイナス記号(\fB\-XX:\-\fR\fIOptionName\fR)を使用して無効にします。 +.PP +引数が必要なオプションの場合、引数は、オプション名を空白、コロン(:)または等号(=)で区切ったものになるか、あるいは引数がオプションの後に直接続く場合もあります(正確な構文は各オプションによって異なります)。サイズをバイト単位で指定するよう求められている場合、接尾辞を使用しないか、あるいはキロバイト(KB)には接尾辞\fBk\fRまたは\fBK\fR、メガバイト(MB)には接尾辞\fBm\fRまたは\fBM\fR、ギガバイト(GB)には接尾辞\fBg\fRまたは\fBG\fRを使用します。たとえば、サイズを8GBに設定するには、\fB8g\fR、\fB8192m\fR、\fB8388608k\fRまたは\fB8589934592\fRのいずれかを引数として指定できます。パーセントの指定が必要な場合は、0から1の数値を使用します(たとえば、25%の場合は\fB0\&.25\fRを指定します)。 +.SS "標準オプション" +.PP +これらは、JVMのすべての実装でサポートされる最も一般的に使用されるオプションです。 +.PP +\-agentlib:\fIlibname\fR[=\fIoptions\fR] +.RS 4 +指定したネイティブ・エージェント・ライブラリをロードします。ライブラリ名の後に、ライブラリに固有のオプションのカンマ区切りリストを使用できます。 +.sp +オプション\fB\-agentlib:foo\fRを指定した場合、JVMは、\fBLD_LIBRARY_PATH\fRシステム変数(OS Xでは、この変数は\fBDYLD_LIBRARY_PATH\fRになります)で指定された場所に\fBlibfoo\&.so\fRという名前のライブラリをロードしようとします。 +.sp +次の例では、スタックの深さ3で、20ミリ秒ごとにヒープ・プロファイリング・ツール(HPROF)ライブラリをロードして、サンプルのCPU情報を取得する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-agentlib:hprof=cpu=samples,interval=20,depth=3\fR + +.fi +.if n \{\ +.RE +.\} +次の例では、メイン・クラスのロード前にJVMを一時停止して、Javaデバッグ・ワイヤ・プロトコル(JDWP)ライブラリをロードして、ポート8000でのソケット接続用にリスニングする方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-agentlib:jdwp=transport=dt_socket,server=y,address=8000\fR + +.fi +.if n \{\ +.RE +.\} +ネイティブ・エージェント・ライブラリの詳細は、次を参照してください。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +http://docs\&.oracle\&.com/javase/8/docs/api/java/lang/instrument/package\-summary\&.htmlにある\fBjava\&.lang\&.instrument\fRパッケージの説明 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +http://docs\&.oracle\&.com/javase/8/docs/platform/jvmti/jvmti\&.html#startingにあるJVMツール・インタフェース・ガイドのエージェントのコマンド行オプションに関する項 +.RE +.RE +.PP +\-agentpath:\fIpathname\fR[=\fIoptions\fR] +.RS 4 +絶対パス名で指定されたネイティブ・エージェント・ライブラリをロードします。このオプションは\fB\-agentlib\fRと同等ですが、ライブラリのフル・パスおよびファイル名を使用します。 +.RE +.PP +\-client +.RS 4 +Java HotSpot Client VMを選択します。64ビット・バージョンのJava SE Development Kit (JDK)では、現在、このオプションは無視され、かわりにServer JVMが使用されます。 +.sp +デフォルトのJVM選択は、http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/server\-class\&.htmlにある +サーバークラス・マシンの検出を参照してください。 +.RE +.PP +\-D\fIproperty\fR=\fIvalue\fR +.RS 4 +システム・プロパティの値を設定します。\fIproperty\fR変数は、プロパティの名前を表す、空白のない文字列です。\fIvalue\fR変数は、プロパティの値を表す文字列です。\fIvalue\fRが空白を含む文字列の場合、それを引用符で囲みます(例: +\fB\-Dfoo="foo bar"\fR)。 +.RE +.PP +\-d32 +.RS 4 +アプリケーションを32ビット環境で実行します。32ビット環境がインストールされていないかサポートされていない場合は、エラーが報告されます。デフォルトでは、64ビット・システムが使用されている場合を除き、アプリケーションは32ビット環境で実行されます。 +.RE +.PP +\-d64 +.RS 4 +アプリケーションを64ビット環境で実行します。64ビット環境がインストールされていないかサポートされていない場合は、エラーが報告されます。デフォルトでは、64ビット・システムが使用されている場合を除き、アプリケーションは32ビット環境で実行されます。 +.sp +現在のところ、Java HotSpot Server VMのみが64ビットの操作をサポートしているため、\fB\-d64\fR使用時には\fB\-server\fRオプションが暗黙的に使用されます。\fB\-d64\fR使用時には、\fB\-client\fRオプションは無視されます。この仕様は、将来のリリースでは変更になる可能性があります。 +.RE +.PP +\-disableassertions[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.br +\-da[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.RS 4 +アサーションを無効にします。デフォルトでは、アサーションはすべてのパッケージおよびクラスで無効になっています。 +.sp +引数なしの\fB\-disableassertions\fR +(\fB\-da\fR)を指定すると、すべてのパッケージおよびクラスでアサーションが無効になります。\fB\&.\&.\&.\fRで終わる\fIpackagename\fR引数を指定すると、指定したパッケージとそのサブパッケージ内でアサーションが無効になります。引数として\fB\&.\&.\&.\fRのみを指定すると、現在の作業ディレクトリにある名前のないパッケージ内でアサーションが無効になります。\fIclassname\fR引数を指定すると、切替えによって、指定したクラス内でアサーションが無効になります。 +.sp +\fB\-disableassertions\fR +(\fB\-da\fR)オプションは、すべてのクラス・ローダーおよびシステム・クラスに適用されます(システム・クラスにはクラス・ローダーはありません)。このルールには1つ例外があります。オプションの引数が指定されていない場合は、システム・クラスに適用されません。これにより、システム・クラスを除くすべてのクラスでアサーションを簡単に無効にすることができます。\fB\-disablesystemassertions\fRオプションを使用すると、すべてのシステム・クラスでアサーションを無効にすることができます。 +.sp +特定のパッケージやクラスでアサーションを明示的に有効にするには、\fB\-enableassertions\fR +(\fB\-ea\fR)オプションを使用します。両方のオプションを同時に使用できます。たとえば、パッケージ\fBcom\&.wombat\&.fruitbat\fR +(およびそのサブパッケージ)ではアサーションを有効にして、クラス\fBcom\&.wombat\&.fruitbat\&.Brickbat\fRではアサーションを無効にして、\fBMyClass\fRアプリケーションを実行するには、次のコマンドを使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-ea:com\&.wombat\&.fruitbat\&.\&.\&. \-da:com\&.wombat\&.fruitbat\&.Brickbat MyClass\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-disablesystemassertions +.br +\-dsa +.RS 4 +すべてのシステム・クラス内でアサーションを無効にします。 +.RE +.PP +\-enableassertions[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.br +\-ea[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.RS 4 +アサーションを有効にします。デフォルトでは、アサーションはすべてのパッケージおよびクラスで無効になっています。 +.sp +引数なしの\fB\-enableassertions\fR +(\fB\-ea\fR)を指定すると、すべてのパッケージおよびクラスでアサーションが有効になります。\fB\&.\&.\&.\fRで終わる\fIpackagename\fR引数を指定すると、指定したパッケージとそのサブパッケージ内でアサーションが有効になります。引数として\fB\&.\&.\&.\fRのみを指定すると、現在の作業ディレクトリにある名前のないパッケージ内でアサーションが有効になります。\fIclassname\fR引数を指定すると、切替えによって、指定したクラス内でアサーションが有効になります。 +.sp +\fB\-enableassertions\fR +(\fB\-ea\fR)オプションは、すべてのクラス・ローダーおよびシステム・クラスに適用されます(システム・クラスにはクラス・ローダーはありません)。このルールには1つ例外があります。オプションの引数が指定されていない場合は、システム・クラスに適用されません。これにより、システム・クラスを除くすべてのクラスでアサーションを簡単に有効にすることができます。\fB\-enablesystemassertions\fRオプションは、すべてのシステム・クラスでアサーションを有効にする別のスイッチを提供します。 +.sp +特定のパッケージやクラスでアサーションを明示的に無効にするには、\fB\-disableassertions\fR +(\fB\-da\fR)オプションを使用します。単一コマンドにこれらのスイッチのインスタンスを複数指定した場合は、指定したスイッチが順番に処理されてからクラスがロードされます。たとえば、パッケージ\fBcom\&.wombat\&.fruitbat\fR +(およびそのサブパッケージ)でのみアサーションを有効にして、クラス\fBcom\&.wombat\&.fruitbat\&.Brickbat\fRではアサーションを無効にして、\fBMyClass\fRアプリケーションを実行するには、次のコマンドを使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-ea:com\&.wombat\&.fruitbat\&.\&.\&. \-da:com\&.wombat\&.fruitbat\&.Brickbat MyClass\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-enablesystemassertions +.br +\-esa +.RS 4 +すべてのシステム・クラス内でアサーションを有効にします。 +.RE +.PP +\-help +.br +\-? +.RS 4 +実際にJVMを実行せずに、\fBjava\fRコマンドの使用状況情報を表示します。 +.RE +.PP +\-jar \fIfilename\fR +.RS 4 +JARファイルにカプセル化されたプログラムを実行します。\fIfilename\fR引数は、使用するアプリケーションの開始点として機能する\fBpublic static void main(String[] args)\fRメソッドを定義する、\fBMain\-Class:\fR\fIclassname\fRという形式の1行を含むマニフェストを持つJARファイルの名前です。 +.sp +\fB\-jar\fRオプションを使用すると、指定したJARファイルがすべてのユーザー・クラスのソースになり、クラス・パスの他の設定は無視されます。 +.sp +JARファイルの詳細は、次のリソースを参照してください。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jar(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/jar/index\&.htmlにあるJavaアーカイブ(JAR)ファイルのガイド +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +http://docs\&.oracle\&.com/javase/tutorial/deployment/jar/index\&.htmlにある +「レッスン: JARファイルのプログラムのパッケージ化」 +.RE +.RE +.PP +\-javaagent:\fIjarpath\fR[=\fIoptions\fR] +.RS 4 +指定したJavaプログラミング言語エージェントをロードします。Javaアプリケーションのインストゥルメントの詳細は、http://docs\&.oracle\&.com/javase/8/docs/api/java/lang/instrument/package\-summary\&.htmlにあるJava APIドキュメントの\fBjava\&.lang\&.instrument\fRパッケージの説明を参照してください。 +.RE +.PP +\-jre\-restrict\-search +.RS 4 +ユーザー・プライベートなJREをバージョン検索に含めます。 +.RE +.PP +\-no\-jre\-restrict\-search +.RS 4 +ユーザー・プライベートなJREをバージョン検索から除外します。 +.RE +.PP +\-server +.RS 4 +Java HotSpot Server VMを選択します。64ビット・バージョンのJDKでは、Server VMのみをサポートしているため、その場合、このオプションは暗黙的です。 +.sp +デフォルトのJVM選択は、http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/server\-class\&.htmlにある +サーバークラス・マシンの検出を参照してください。 +.RE +.PP +\-showversion +.RS 4 +バージョン情報を表示し、アプリケーションの実行を続行します。このオプションは\fB\-version\fRオプションと同等ですが、\-versionはバージョン情報の表示後にJVMに終了を指示する点が異なります。 +.RE +.PP +\-splash:\fIimgname\fR +.RS 4 +\fIimgname\fRで指定されたイメージを含むスプラッシュ画面を表示します。たとえば、アプリケーションの起動時に\fBimages\fRディレクトリの\fBsplash\&.gif\fRファイルを表示するには、次のオプションを使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-splash:images/splash\&.gif\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-verbose:class +.RS 4 +ロードされた各クラスに関する情報を表示します。 +.RE +.PP +\-verbose:gc +.RS 4 +各ガベージ・コレクション(GC)イベントに関する情報を表示します。 +.RE +.PP +\-verbose:jni +.RS 4 +ネイティブ・メソッドの使用およびその他のJava Native Interface (JNI)アクティビティに関する情報を表示します。 +.RE +.PP +\-version +.RS 4 +バージョン情報を表示してから終了します。このオプションは\fB\-showversion\fRオプションと同等ですが、\-showversionはバージョン情報の表示後にJVMに終了を指示しない点が異なります。 +.RE +.PP +\-version:\fIrelease\fR +.RS 4 +アプリケーションの実行に使用するリリース・バージョンを指定します。コールされた\fBjava\fRコマンドのバージョンがこの指定内容を満たさず、かつ適切な実装がシステム上で見つかった場合には、その適切な実装が使用されます。 +.sp +\fIrelease\fR引数は、正確なバージョン文字列、または空白で区切られたバージョン文字列とバージョン範囲のリストを指定します。\fIバージョン文字列\fRは、次の形式で、開発者のバージョン番号を指定します: +\fB1\&.\fR\fIx\fR\fB\&.0_\fR\fIu\fR +(\fIx\fRはメジャー・バージョン番号、\fIu\fRは更新バージョン番号です)。\fIバージョン範囲\fRは、このバージョン以降を指定するにはバージョン文字列の後にプラス記号(\fB+\fR)を続けたもの、または一致する接頭辞を含む任意のバージョン文字列を指定するには一部のバージョン文字列の後にアスタリスク(\fB*\fR)を続けたもので構成されます。論理\fIOR\fRの組合せには空白、2つのバージョンの文字列/範囲の論理\fIAND\fRの組合せにはアンパサンド(\fB&\fR)を使用して、バージョン文字列とバージョン範囲を組み合せることができます。たとえば、クラスまたはJARファイルの実行にJRE 6u13 (1\&.6\&.0_13)または6u10 (1\&.6\&.0_10)以降の任意のJRE 6のいずれかを必要とする場合、次を指定します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-version:"1\&.6\&.0_13 1\&.6* & 1\&.6\&.0_10+"\fR + +.fi +.if n \{\ +.RE +.\} +引用符が必要なのは、\fIrelease\fRパラメータに空白がある場合のみです。 +.sp +JARファイルの場合は、バージョン要件をコマンド行に指定するよりも、JARファイルのマニフェスト内に指定することが推奨されています。 +.RE +.SS "非標準オプション" +.PP +これらのオプションは、Java HotSpot仮想マシンに固有の汎用オプションです。 +.PP +\-X +.RS 4 +使用可能なすべての\fB\-X\fRオプションのヘルプを表示します。 +.RE +.PP +\-Xbatch +.RS 4 +バックグラウンド・コンパイルを無効にします。デフォルトでは、JVMでは、バックグラウンド・コンパイルが終了するまで、メソッドをバックグラウンド・タスクとしてコンパイルし、インタプリタ・モードでメソッドを実行します。\fB\-Xbatch\fRフラグを指定すると、バックグラウンド・コンパイルが無効になり、すべてのメソッドのコンパイルが完了するまでフォアグラウンド・タスクとして処理されます。 +.sp +このオプションは\fB\-XX:\-BackgroundCompilation\fRと同等です。 +.RE +.PP +\-Xbootclasspath:\fIpath\fR +.RS 4 +ブート・クラス・ファイルを検索するディレクトリ、JARファイルおよびZIPアーカイブの、コロン(:)で区切られたリストを指定します。これらは、JDKに含まれるブート・クラス・ファイルのかわりに使用されます。 +.sp +JREバイナリ・コード・ライセンスに違反するため、\fBrt\&.jar\fRでクラスをオーバーライドする目的で、このオプションを使用するアプリケーションをデプロイしないでください。 +.RE +.PP +\-Xbootclasspath/a:\fIpath\fR +.RS 4 +デフォルトのブートストラップ・クラス・パスの最後に追加するディレクトリ、JARファイルおよびZIPアーカイブの、コロン(:)で区切られたリストを指定します。 +.sp +JREバイナリ・コード・ライセンスに違反するため、\fBrt\&.jar\fRでクラスをオーバーライドする目的で、このオプションを使用するアプリケーションをデプロイしないでください。 +.RE +.PP +\-Xbootclasspath/p:\fIpath\fR +.RS 4 +デフォルトのブートストラップ・クラス・パスの先頭に追加するディレクトリ、JARファイルおよびZIPアーカイブの、コロン(:)で区切られたリストを指定します。 +.sp +JREバイナリ・コード・ライセンスに違反するため、\fBrt\&.jar\fRでクラスをオーバーライドする目的で、このオプションを使用するアプリケーションをデプロイしないでください。 +.RE +.PP +\-Xcheck:jni +.RS 4 +Java Native Interface (JNI)機能に対して追加チェックを行います。具体的には、これは、JNIリクエストを処理する前に、JNI関数に渡されるパラメータと実行環境のデータを検証します。無効なデータが見つかった場合は、ネイティブ・コードに問題があることを示しているため、JVMはリカバリ不能なエラーを発生して終了します。このオプションを使用すると、パフォーマンス低下が予想されます。 +.RE +.PP +\-Xcomp +.RS 4 +最初の呼出しでメソッドのコンパイルを強制的に実行します。デフォルトでは、クライアントVM(\fB\-client\fR)は1,000の解釈されたメソッド呼出しを実行し、サーバーVM(\fB\-server\fR)は10,000の解釈されたメソッド呼出しを実行して、効率的なコンパイルのための情報を収集します。\fB\-Xcomp\fRオプションを指定すると、解釈されたメソッド呼出しが無効になり、効率を犠牲にしてコンパイルのパフォーマンスが向上します。 +.sp +\fB\-XX:CompileThreshold\fRオプションを使用して、コンパイルの前に、解釈されたメソッド呼出しの数を変更することもできます。 +.RE +.PP +\-Xdebug +.RS 4 +何も行いません。後方互換性のために用意されています。 +.RE +.PP +\-Xdiag +.RS 4 +追加の診断メッセージを表示します。 +.RE +.PP +\-Xfuture +.RS 4 +クラス・ファイル形式の仕様への準拠を強化する、厳密なクラス・ファイル形式のチェックが有効になります。将来のリリースでは、より厳密なチェックがデフォルトになるため、新しいコードを開発するときには、開発者はこのフラグを使用することをお薦めします。 +.RE +.PP +\-Xint +.RS 4 +インタプリタ専用モードでアプリケーションを実行します。ネイティブ・コードへのコンパイルは無効になり、すべてのバイトコードがインタプリタによって実行されます。ジャスト・イン・タイム(JIT)コンパイラが提供するパフォーマンス上の利点は、このモードでは実現されません。 +.RE +.PP +\-Xinternalversion +.RS 4 +\fB\-version\fRオプションより詳細なJVMバージョン情報を表示してから終了します。 +.RE +.PP +\-Xloggc:\fIfilename\fR +.RS 4 +詳細なGCイベント情報をロギング用にリダイレクトするファイルを設定します。このファイルに書き込まれる情報は、記録された各イベントの前に行われる最初のGCイベント以降に経過した時間を指定した\fB\-verbose:gc\fRの出力と類似しています。\fB\-Xloggc\fRオプションは\fB\-verbose:gc\fRをオーバーライドします(これらの両方が同じ\fBjava\fRコマンドで指定された場合)。 +.sp +例: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xloggc:garbage\-collection\&.log\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-Xmaxjitcodesize=\fIsize\fR +.RS 4 +JITコンパイルされたコードの最大コード・キャッシュ・サイズ(バイト単位)を指定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルトでは、この値は48MBに設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xmaxjitcodesize=48m\fR + +.fi +.if n \{\ +.RE +.\} +このオプションは\fB\-XX:ReservedCodeCacheSize\fRと同等です。 +.RE +.PP +\-Xmixed +.RS 4 +ネイティブ・コードにコンパイルされたホット・メソッドを除き、インタプリタによってすべてのバイトコードを実行します。 +.RE +.PP +\-Xmn\fIsize\fR +.RS 4 +若い世代(ナーサリ)のヒープの初期サイズおよび最大サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。 +.sp +ヒープの若い世代リージョンは新しいオブジェクトに使用されます。GCは、他のリージョンよりこのリージョンで、より頻繁に実行されます。若い世代のサイズが小さすぎる場合、多数のマイナー・ガベージ・コレクションが実行されます。サイズが大きすぎる場合、フル・ガベージ・コレクションのみが実行されますが、完了までに時間がかかることがあります。若い世代のサイズは、全体のヒープ・サイズの半分から4分の1の間にしておくことをお薦めします。 +.sp +次の例では、若い世代の初期サイズおよび最大サイズを様々な単位を使用して256MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xmn256m\fR +\fB\-Xmn262144k\fR +\fB\-Xmn268435456\fR + +.fi +.if n \{\ +.RE +.\} +若い世代のヒープの初期サイズと最大サイズの両方を設定する\fB\-Xmn\fRオプションのかわりに、初期サイズの設定には\fB\-XX:NewSize\fRを、最大サイズの設定には\fB\-XX:MaxNewSize\fRを使用できます。 +.RE +.PP +\-Xms\fIsize\fR +.RS 4 +ヒープの初期サイズ(バイト単位)を設定します。指定する値は、1MBより大きい1024の倍数にする必要があります。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。 +.sp +次の例では、割り当てられたメモリーのサイズを様々な単位を使用して6MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xms6291456\fR +\fB\-Xms6144k\fR +\fB\-Xms6m\fR + +.fi +.if n \{\ +.RE +.\} +このオプションを設定しない場合、初期サイズは、古い世代と若い世代に割り当てられたサイズの合計として設定されます。若い世代のヒープの初期サイズは、\fB\-Xmn\fRオプションまたは\fB\-XX:NewSize\fRオプションを使用して設定できます。 +.RE +.PP +\-Xmx\fIsize\fR +.RS 4 +メモリー割当てプールの最大サイズ(バイト単位)を指定します。指定する値は、2MBより大きい1024の倍数にする必要があります。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルト値は、実行時にシステム構成に基づいて選択されます。サーバー・デプロイメントでは、\fB\-Xms\fRおよび\fB\-Xmx\fRは通常同じ値に設定されます。http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/gctuning/index\&.htmlの\fIJava SE HotSpot仮想マシンのガベージ・コレクション・チューニング・ガイド\fRのエルゴノミクスに関する項を参照してください。 +.sp +次の例では、割り当てられたメモリーの許可される最大サイズを様々な単位を使用して80MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xmx83886080\fR +\fB\-Xmx81920k\fR +\fB\-Xmx80m\fR + +.fi +.if n \{\ +.RE +.\} +\fB\-Xmx\fRオプションは\fB\-XX:MaxHeapSize\fRと同等です。 +.RE +.PP +\-Xnoclassgc +.RS 4 +クラスのガベージ・コレクション(GC)を無効にします。これにより、GC時間を節約でき、アプリケーション実行中の中断が短縮されます。 +.sp +起動時に\fB\-Xnoclassgc\fRを指定すると、アプリケーション内のクラス・オブジェクトは、GCの間はそのまま残り、常にライブであるとみなされます。これにより、より多くのメモリーが永久に占有されることになり、注意して使用しないと、メモリー不足の例外がスローされます。 +.RE +.PP +\-Xprof +.RS 4 +実行中のプログラムをプロファイルし、プロファイリング・データを標準出力に送信します。このオプションは、プログラム開発用のユーティリティとして提供されています。本番稼働システムでの使用を目的としたものではありません。 +.RE +.PP +\-Xrs +.RS 4 +JVMによるオペレーティング・システム・シグナルの使用を減らします。 +.sp +シャットダウン・フックは、JVMが突然終了した場合でも、シャットダウン時にユーザー・クリーンアップ・コード(データベース接続のクローズなど)を実行することによって、Javaアプリケーションのシャットダウンを順番に有効にします。 +.sp +JVMは、予期しない終了のシャットダウン・フックを実装するためにシグナルをキャッチします。JVMは、\fBSIGHUP\fR、\fBSIGINT\fRおよび\fBSIGTERM\fRを使用して、シャットダウン・フックの実行を開始します。 +.sp +JVMは、デバッグの目的でスレッド・スタックをダンプするという機能を実現するために、同様のメカニズムを使用します。JVMは、スレッド・ダンプを実行するために\fBSIGQUIT\fRを使用します。 +.sp +JVMを埋め込んでいるアプリケーションは、\fBSIGINT\fRや\fBSIGTERM\fRなどのシグナルを頻繁にトラップする必要があり、その結果、JVMのシグナル・ハンドラと衝突する可能性があります。\fB\-Xrs\fRオプションは、この問題に対処するために使用できます。\fB\-Xrs\fRが使用されている場合、\fBSIGINT\fR、\fBSIGTERM\fR、\fBSIGHUP\fRおよび\fBSIGQUIT\fRのシグナル・マスクはJVMによって変更されず、これらのシグナルのシグナル・ハンドラはインストールされません。 +.sp +\fB\-Xrs\fRを指定すると、次の2つの結果が生じます: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBSIGQUIT\fRによるスレッド・ダンプは使用できません。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +シャットダウン・フック処理の実行は、JVMが終了しようとしている時点で\fBSystem\&.exit()\fRを呼び出すなどして、ユーザー・コード側で行う必要があります。 +.RE +.RE +.PP +\-Xshare:\fImode\fR +.RS 4 +クラス・データ共有モードを設定します。このオプションで使用可能な\fImode\fR引数は次のとおりです。 +.PP +auto +.RS 4 +可能な場合、共有クラスのデータを使用します。これは、Java HotSpot 32\-Bit Client VMの場合のデフォルト値です。 +.RE +.PP +on +.RS 4 +クラス・データ共有の使用が必要です。クラス・データ共有を使用できない場合、エラー・メッセージを出力して終了します。 +.RE +.PP +off +.RS 4 +共有クラス・データを使用しません。これは、Java HotSpot 32\-Bit Server VM、Java HotSpot 64\-Bit Client VMおよびJava HotSpot 64\-Bit Server VMの場合のデフォルト値です。 +.RE +.PP +dump +.RS 4 +クラス・データ共有アーカイブを手動で生成します。 +.RE +.RE +.PP +\-XshowSettings:\fIcategory\fR +.RS 4 +設定を表示して続行します。このオプションで使用可能な\fIcategory\fR引数は次のとおりです。 +.PP +all +.RS 4 +設定のすべてのカテゴリを表示します。これがデフォルト値です。 +.RE +.PP +locale +.RS 4 +ロケールに関連する設定を表示します。 +.RE +.PP +properties +.RS 4 +システム・プロパティに関連する設定を表示します。 +.RE +.PP +vm +.RS 4 +JVMの設定を表示します。 +.RE +.RE +.PP +\-Xss\fIsize\fR +.RS 4 +スレッドのスタック・サイズ(バイト単位)を設定します。KBを示す場合は文字\fBk\fRまたは\fBK\fR、MBを示す場合は文字\fBm\fRまたは\fBM\fR、GBを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルト値はプラットフォームによって異なります。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/ARM (32ビット): 320KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/i386 (32ビット): 320KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/x64 (64ビット): 1024KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +OS X (64ビット): 1024KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/i386 (32ビット): 320KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/x64 (64ビット): 1024KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Windows: 仮想メモリーによって異なります。 +.RE +.sp +次の例では、スレッド・スタック・サイズを異なる単位で1024KBに設定します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xss1m\fR +\fB\-Xss1024k\fR +\fB\-Xss1048576\fR + +.fi +.if n \{\ +.RE +.\} +このオプションは\fB\-XX:ThreadStackSize\fRと同等です。 +.RE +.PP +\-Xusealtsigs +.RS 4 +JVM内部シグナルの\fBSIGUSR1\fRおよび\fBSIGUSR2\fRのかわりに、代替シグナルを使用します。このオプションは\fB\-XX:+UseAltSigs\fRと同等です。 +.RE +.PP +\-Xverify:\fImode\fR +.RS 4 +バイトコード・ベリファイアのモードを設定します。バイトコードの検証は一部の問題のトラブルシューティングに役立ちますが、実行中のアプリケーションへのオーバーヘッドを増大させます。このオプションで使用可能な\fImode\fR引数は次のとおりです。 +.PP +なし +.RS 4 +バイトコードを検証しません。これにより、起動時間が短縮され、Javaによって提供される保護も軽減されます。 +.RE +.PP +remote +.RS 4 +ブートストラップ・クラス・ローダーでロードされていないこれらのクラスを検証します。これは、\fB\-Xverify\fRオプションを指定しない場合のデフォルトの動作です。 +.RE +.PP +all +.RS 4 +すべてのクラスを検証します。 +.RE +.RE +.SS "高度なランタイム・オプション" +.PP +これらのオプションは、Java HotSpot VMの実行時の動作を制御します。 +.PP +\-XX:+DisableAttachMechanism +.RS 4 +JVMにツールをアタッチするメカニズムを無効にするオプションを有効にします。デフォルトでは、このオプションは無効になっており、これは、アタッチ・メカニズムを有効にすると、\fBjcmd\fR、\fBjstack\fR、\fBjmap\fR、\fBjinfo\fRなどのツールを使用できることを意味します。 +.RE +.PP +\-XX:ErrorFile=\fIfilename\fR +.RS 4 +リカバリ不能なエラーが発生した場合にエラー・データが書き込まれるパスおよびファイル名を指定します。デフォルトでは、このファイルは、現在の作業ディレクトリに作成され、名前は\fBhs_err_pid\fR\fIpid\fR\fB\&.log\fR +(\fIpid\fRはエラーの原因となったプロセスの識別子)になります。次の例では、デフォルトのログ・ファイルを設定する方法を示します(プロセスの識別子は\fB%p\fRとして指定されます)。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ErrorFile=\&./hs_err_pid%p\&.log\fR + +.fi +.if n \{\ +.RE +.\} +次の例では、エラー・ログを\fB/var/log/java/java_error\&.log\fRに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ErrorFile=/var/log/java/java_error\&.log\fR + +.fi +.if n \{\ +.RE +.\} +(領域不足、権限の問題または別の問題により)指定したディレクトリにファイルを作成できない場合、ファイルはオペレーティング・システムの一時ディレクトリに作成されます。一時ディレクトリは\fB/tmp\fRです。 +.RE +.PP +\-XX:+FailOverToOldVerifier +.RS 4 +新しいタイプ・チェッカが失敗した場合の、古いベリファイアへの自動フェイルオーバーを有効にします。デフォルトでは、このオプションは無効になっており、最近のバイトコード・バージョンを使用したクラスには、これは無視されます(つまり、無効として処理されます)。古いバージョンのバイトコードを使用したクラスには、これを有効化できます。 +.RE +.PP +\-XX:LargePageSizeInBytes=\fIsize\fR +.RS 4 +Solarisでは、Javaヒープに使用されるラージ・ページの最大サイズ(バイト単位)を設定します。\fIsize\fR引数は、2の累乗(2、4、8、16、\&.\&.\&.)である必要があります。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルトでは、サイズは0に設定され、これは、JVMではラージ・ページのサイズが自動的に選択されていることを意味します。 +.sp +次の例では、ラージ・ページのサイズを4メガバイト(MB)に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:LargePageSizeInBytes=4m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxDirectMemorySize=\fIsize\fR +.RS 4 +新規I/O (\fBjava\&.nio\fRパッケージ)の直接バッファ割当ての最大合計サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルトでは、サイズは0に設定され、これは、JVMではNIOの直接バッファ割当てのサイズが自動的に選択されていることを意味します。 +.sp +次の例では、NIOサイズを異なる単位で1024KBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxDirectMemorySize=1m\fR +\fB\-XX:MaxDirectMemorySize=1024k\fR +\fB\-XX:MaxDirectMemorySize=1048576\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:NativeMemoryTracking=\fImode\fR +.RS 4 +JVMのネイティブ・メモリー使用状況のトラッキングのモードを指定します。このオプションで使用可能な\fImode\fR引数は次のとおりです。 +.PP +off +.RS 4 +JVMのネイティブ・メモリー使用状況を追跡しません。これは、\fB\-XX:NativeMemoryTracking\fRオプションを指定しない場合のデフォルトの動作です。 +.RE +.PP +summary +.RS 4 +JVMサブシステム(Javaヒープ、クラス、コード、スレッドなど)によるメモリー使用状況のみ追跡します。 +.RE +.PP +detail +.RS 4 +JVMサブシステムによるメモリー使用状況のトラッキングに加えて、個々の\fBCallSite\fR +(個々の仮想メモリー・リージョンおよびそのコミット済リージョン)によるメモリー使用状況を追跡します。 +.RE +.RE +.PP +\-XX:ObjectAlignmentInBytes=\fIalignment\fR +.RS 4 +Javaオブジェクトのメモリー配置を設定します(バイト単位)。デフォルトでは、値が8バイトに設定されます。指定される値は、2の累乗にして8から256(両端を含む)の範囲内にする必要があります。このオプションにより、大きいJavaヒープ・サイズで圧縮ポインタを使用できます。 +.sp +バイト単位のヒープ・サイズ制限は次のように計算されます: +.sp +\fB4GB * ObjectAlignmentInBytes\fR +.sp +注意: 配置の値が増えると、オブジェクト間の未使用の領域も増えます。結果として、大きいヒープ・サイズで圧縮ポインタを使用するメリットがわからない可能性があります。 +.RE +.PP +\-XX:OnError=\fIstring\fR +.RS 4 +リカバリ不能なエラーが発生したときに実行する、カスタム・コマンドまたは一連のセミコロン区切りのコマンドを設定します。文字列に空白が含まれている場合は、引用符で囲む必要があります。 +.sp +次の例では、\fB\-XX:OnError\fRオプションを使用してコア・イメージを作成するために\fBgcore\fRコマンドを実行する方法、およびリカバリ不能なエラーの場合にデバッガを起動してプロセスに接続する方法を示します(\fB%p\fRは現在のプロセスを指定します)。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:OnError="gcore %p;dbx \- %p"\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:OnOutOfMemoryError=\fIstring\fR +.RS 4 +\fBOutOfMemoryError\fR例外が最初にスローされたときに実行する、カスタム・コマンドまたは一連のセミコロン区切りのコマンドを設定します。文字列に空白が含まれている場合は、引用符で囲む必要があります。コマンド文字列の例は、\fB\-XX:OnError\fRオプションの説明を参照してください。 +.RE +.PP +\-XX:+PerfDataSaveToFile +.RS 4 +有効な場合、Javaアプリケーションの終了時にjstat(1)バイナリ・データを保存します。このバイナリ・データは\fBhsperfdata_\fR\fI\fRという名前のファイルに保存されます。\fI\fRは、実行したJavaアプリケーションのプロセス識別子です。次のように\fBjstat\fRを使用して、このファイルに含まれるパフォーマンス・データを表示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjstat \-class file:///\fR\fB\fI\fR\fR\fB/hsperfdata_\fR\fB\fI\fR\fR +\fBjstat \-gc file:///\fR\fB\fI\fR\fR\fB/hsperfdata_\fR\fB\fI\fR\fR +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+PrintCommandLineFlags +.RS 4 +コマンド行に表示された、人間工学的に選択したJVMフラグの印刷を有効にします。これは、ヒープ領域サイズや選択されたガベージ・コレクタなど、JVMによって設定されたエルゴノミック値を確認する場合に役立ちます。デフォルトでは、このオプションは無効であり、フラグは印刷されません。 +.RE +.PP +\-XX:+PrintNMTStatistics +.RS 4 +ネイティブ・メモリーのトラッキングが有効な場合に、JVMの終了時に収集されたネイティブ・メモリーのトラッキング・データの印刷を有効にします(\fB\-XX:NativeMemoryTracking\fRを参照してください)。デフォルトでは、このオプションは無効であり、ネイティブ・メモリーのトラッキング・データは印刷されません。 +.RE +.PP +\-XX:+RelaxAccessControlCheck +.RS 4 +ベリファイア内のアクセス制御チェックの量を減らします。デフォルトでは、このオプションは無効になっており、最近のバイトコード・バージョンを使用したクラスには、これは無視されます(つまり、無効として処理されます)。古いバージョンのバイトコードを使用したクラスには、これを有効化できます。 +.RE +.PP +\-XX:+ShowMessageBoxOnError +.RS 4 +JVMでリカバリ不能なエラーが発生した場合、ダイアログ・ボックスの表示を有効にします。これにより、JVMにデバッガを接続してエラーの原因を調査できるように、JVMを終了しないようにして、プロセスをアクティブなままにします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:ThreadStackSize=\fIsize\fR +.RS 4 +スレッドのスタック・サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルト値はプラットフォームによって異なります。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/ARM (32ビット): 320KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/i386 (32ビット): 320KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/x64 (64ビット): 1024KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +OS X (64ビット): 1024KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/i386 (32ビット): 320KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/x64 (64ビット): 1024KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Windows: 仮想メモリーによって異なります。 +.RE +.sp +次の例では、スレッド・スタック・サイズを異なる単位で1024KBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ThreadStackSize=1m\fR +\fB\-XX:ThreadStackSize=1024k\fR +\fB\-XX:ThreadStackSize=1048576\fR + +.fi +.if n \{\ +.RE +.\} +このオプションは\fB\-Xss\fRと同等です。 +.RE +.PP +\-XX:+TraceClassLoading +.RS 4 +クラスがロードされるときのクラスのトレースを有効にします。デフォルトでは、このオプションは無効であり、クラスはトレースされません。 +.RE +.PP +\-XX:+TraceClassLoadingPreorder +.RS 4 +クラスが参照される順序で、ロードされたすべてのクラスのトレースを有効にします。デフォルトでは、このオプションは無効であり、クラスはトレースされません。 +.RE +.PP +\-XX:+TraceClassResolution +.RS 4 +定数プールの解決のトレースを有効にします。デフォルトでは、このオプションは無効であり、定数プールの解決はトレースされません。 +.RE +.PP +\-XX:+TraceClassUnloading +.RS 4 +クラスがアンロードされるときのクラスのトレースを有効にします。デフォルトでは、このオプションは無効であり、クラスはトレースされません。 +.RE +.PP +\-XX:+TraceLoaderConstraints +.RS 4 +ローダー制約の記録のトレースを有効にします。デフォルトでは、このオプションは無効であり、ローダー制約の記録は追跡されません。 +.RE +.PP +\-XX:+UseAltSigs +.RS 4 +JVM内部シグナルの\fBSIGUSR1\fRおよび\fBSIGUSR2\fRのかわりに、代替シグナルの使用を有効にします。デフォルトでは、このオプションは無効であり、代替シグナルは使用されません。このオプションは\fB\-Xusealtsigs\fRと同等です。 +.RE +.PP +\-XX:\-UseBiasedLocking +.RS 4 +バイアス・ロックの使用を無効にします。かなりの量の非競合の同期化がある一部のアプリケーションは、このフラグを有効にすると大幅な高速化が実現しますが、特定のパターンのロックがあるアプリケーションは、速度が低下することがあります。バイアス・ロックの方法の詳細は、http://www\&.oracle\&.com/technetwork/java/tuning\-139912\&.html#section4\&.2\&.5にあるJavaチューニングのホワイト・ペーパーの例を参照してください。 +.sp +デフォルトでは、このオプションは有効になっています。 +.RE +.PP +\-XX:\-UseCompressedOops +.RS 4 +圧縮されたポインタの使用を無効にします。デフォルトではこのオプションが有効であり、Javaヒープ・サイズが32GBより小さい場合に圧縮ポインタが使用されます。このオプションを有効にすると、オブジェクト参照は、64ビットのポインタではなく32ビットのオフセットとして表され、これにより、32GBより小さいJavaヒープ・サイズのアプリケーションの実行時に、通常、パフォーマンスが向上します。このオプションは、64ビットのJVMでのみ機能します。 +.sp +Javaヒープ・サイズが32GBより大きい場合にも圧縮ポインタを使用できます。\fB\-XX:ObjectAlignmentInBytes\fRオプションを参照してください。 +.RE +.PP +\-XX:+UseHugeTLBFS +.RS 4 +Linux用のこのオプションは、\fB\-XX:+UseLargePages\fRを指定するのと同じです。このオプションは、デフォルトでは無効になっています。このオプションは、メモリーの予約時にすべてのラージ・ページを事前に割り当てます。そのため、JVMはラージ・ページ・メモリー領域を動的に拡張または縮小できません。この動作を行う場合は、\fB\-XX:UseTransparentHugePages\fRを参照してください。 +.sp +詳細は、"ラージ・ページ"を参照してください。 +.RE +.PP +\-XX:+UseLargePages +.RS 4 +ラージ・ページのメモリーの使用を有効にします。デフォルトでは、このオプションは無効であり、ラージ・ページのメモリーは使用されません。 +.sp +詳細は、"ラージ・ページ"を参照してください。 +.RE +.PP +\-XX:+UseMembar +.RS 4 +スレッドの状態の遷移でメンバーの発行を有効にします。このオプションは、有効になっているARMサーバーを除くすべてのプラットフォーム上で、デフォルトでは無効になっています。(ARMサーバーでこのオプションを無効にしないことをお薦めします。) +.RE +.PP +\-XX:+UsePerfData +.RS 4 +\fBperfdata\fR機能を有効にします。このオプションはデフォルトで有効になっており、JVMのモニタリングおよびパフォーマンス・テストが可能になります。これを無効にすると、\fBhsperfdata_userid\fRディレクトリの作成を抑制します。\fBperfdata\fR機能を無効にするには、\fB\-XX:\-UsePerfData\fRを指定します。 +.RE +.PP +\-XX:+UseTransparentHugePages +.RS 4 +Linuxでは、動的に拡張または縮小できるラージ・ページの使用を有効化してください。このオプションは、デフォルトでは無効になっています。OSが他のページを移動してヒュージ・ページを作成するため、透過的ヒュージ・ページでパフォーマンスの問題が検出される場合があります。このオプションは試験的に使用できます。 +.sp +詳細は、"ラージ・ページ"を参照してください。 +.RE +.PP +\-XX:+AllowUserSignalHandlers +.RS 4 +アプリケーションによるシグナル・ハンドラのインストールを有効にします。デフォルトでは、このオプションは無効であり、アプリケーションはシグナル・ハンドラをインストールすることは許可されていません。 +.RE +.SS "高度なJITコンパイラ・オプション" +.PP +これらのオプションは、Java HotSpot VMで実行される動的なjust\-in\-time (JIT)コンパイラを制御します。 +.PP +\-XX:+AggressiveOpts +.RS 4 +積極的なパフォーマンス最適化機能の使用を有効にします。これは今後のリリースでデフォルトになる予定です。デフォルトでは、このオプションは無効であり、試験的なパフォーマンス機能は使用されません。 +.RE +.PP +\-XX:AllocateInstancePrefetchLines=\fIlines\fR +.RS 4 +インスタンス割当てポインタの前にプリフェッチする行数を設定します。デフォルトでは、プリフェッチする行数は1に設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocateInstancePrefetchLines=1\fR + +.fi +.if n \{\ +.RE +.\} +Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:AllocatePrefetchDistance=\fIsize\fR +.RS 4 +オブジェクト割当てのプリフェッチ距離のサイズ(バイト単位)を設定します。新規オブジェクトの値で書き込もうとするメモリーは、最後に割り当てられたオブジェクトのアドレスから、この距離までプリフェッチされます。各Javaスレッドには独自の割当てポイントがあります。 +.sp +負の値は、プリフェッチ距離はプラットフォームに基づいて選択されることを示します。正の値は、プリフェッチするバイト数です。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルト値は\-1に設定されています。 +.sp +次の例では、プリフェッチ距離を1024バイトに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchDistance=1024\fR + +.fi +.if n \{\ +.RE +.\} +Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:AllocatePrefetchInstr=\fIinstruction\fR +.RS 4 +割当てポインタの前にプリフェッチするプリフェッチ命令を設定します。Java HotSpot Server VMのみが、このオプションをサポートしています。使用可能な値は0から3までです。値の背後にある実際の命令は、プラットフォームによって異なります。デフォルトでは、プリフェッチ命令は0に設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchInstr=0\fR + +.fi +.if n \{\ +.RE +.\} +Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:AllocatePrefetchLines=\fIlines\fR +.RS 4 +コンパイルされたコードで生成されるプリフェッチ命令を使用して、最後のオブジェクト割当て後にロードするキャッシュ行数を設定します。最後に割り当てられたオブジェクトがインスタンスの場合は、デフォルト値は1になり、配列の場合は3になります。 +.sp +次の例では、ロードされるキャッシュ行数を5に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchLines=5\fR + +.fi +.if n \{\ +.RE +.\} +Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:AllocatePrefetchStepSize=\fIsize\fR +.RS 4 +順次プリフェッチ命令のステップ・サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルトでは、ステップ・サイズは16バイトに設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchStepSize=16\fR + +.fi +.if n \{\ +.RE +.\} +Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:AllocatePrefetchStyle=\fIstyle\fR +.RS 4 +プリフェッチ命令に生成されるコード・スタイルを設定します。\fIstyle\fR引数は、0から3までの整数です。 +.PP +0 +.RS 4 +プリフェッチ命令を生成しません。 +.RE +.PP +1 +.RS 4 +各割当ての後で、プリフェッチ命令を実行します。これはデフォルトのパラメータです。 +.RE +.PP +2 +.RS 4 +スレッド・ローカルな割当てブロック(TLAB)ウォーターマーク・ポインタを使用して、プリフェッチ命令を実行するタイミングを決定します。 +.RE +.PP +3 +.RS 4 +割当てプリフェッチ用のSPARCでBIS命令を使用します。 +.RE +.sp +Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:+BackgroundCompilation +.RS 4 +バックグラウンド・コンパイルを有効にします。このオプションはデフォルトで有効になっています。バックグラウンド・コンパイルを無効にするには、\fB\-XX:\-BackgroundCompilation\fRを指定します(これは\fB\-Xbatch\fRを指定するのと同等です)。 +.RE +.PP +\-XX:CICompilerCount=\fIthreads\fR +.RS 4 +コンパイルに使用するコンパイラ・スレッドの数を設定します。デフォルトでは、スレッド数は、サーバーJVMの場合は2、クライアントJVMの場合は1に設定されており、層コンパイルが使用されている場合、コア数に合せて増減します。次の例では、スレッドの数を2に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CICompilerCount=2\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CodeCacheMinimumFreeSpace=\fIsize\fR +.RS 4 +コンパイルに必要な最小空き領域(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。最小空き領域より少ない領域しか残っていない場合、コンパイルは停止します。デフォルトでは、このオプションは500KBに設定されています。次の例では、最小空き領域を1024MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CodeCacheMinimumFreeSpace=1024m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CompileCommand=\fIcommand\fR,\fImethod\fR[,\fIoption\fR] +.RS 4 +メソッドで実行するコマンドを指定します。たとえば、コンパイル元から\fBString\fRクラスの\fBindexOf()\fRメソッドを実行するには、次を使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=exclude,java/lang/String\&.indexOf\fR + +.fi +.if n \{\ +.RE +.\} +スラッシュ(\fB/\fR)で区切られたすべてのパッケージおよびサブパッケージを含む、完全クラス名を指定します。切取りと貼付けの操作を容易にするために、\fB\-XX:+PrintCompilation\fRオプションおよび\fB\-XX:+LogCompilation\fRオプションによって生成されるメソッド名の形式を使用することもできます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=exclude,java\&.lang\&.String::indexOf\fR + +.fi +.if n \{\ +.RE +.\} +署名なしでメソッドを指定すると、コマンドは指定した名前を持つすべてのメソッドに適用されます。ただし、クラス・ファイル形式でメソッドの署名を指定することもできます。この場合、引数を引用符で囲む必要があり、囲まないと、シェルによりセミコロンがコマンドの終了として扱われます。たとえば、コンパイル元から\fBString\fRクラスの\fBindexOf(String)\fRメソッドのみ除外するには、次を使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand="exclude,java/lang/String\&.indexOf,(Ljava/lang/String;)I"\fR + +.fi +.if n \{\ +.RE +.\} +また、クラス名およびメソッド名にワイルドカードとしてアスタリスク(*)を使用できます。たとえば、コンパイル元からすべてのクラスのすべての\fBindexOf()\fRメソッドを除外するには、次を使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=exclude,*\&.indexOf\fR + +.fi +.if n \{\ +.RE +.\} +カンマとピリオドは空白の別名で、これにより、シェルを介してコンパイラ・コマンドを渡すことが容易になります。引数を引用符で囲むことで、空白をセパレータとして使用して\fB\-XX:CompileCommand\fRに引数を渡すことができます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand="exclude java/lang/String indexOf"\fR + +.fi +.if n \{\ +.RE +.\} +\fB\-XX:CompileCommand\fRオプションを使用してコマンド行で渡されたコマンドを解析した後に、JITコンパイラは\fB\&.hotspot_compiler\fRファイルからコマンドを読み取ります。このファイルにコマンドを追加するか、または\fB\-XX:CompileCommandFile\fRオプションを使用して別のファイルを指定することができます。 +.sp +複数のコマンドを追加するには、\fB\-XX:CompileCommand\fRオプションを複数回指定するか、または改行セパレータ(\fB\en\fR)を使用して各引数を区切ります。次のコマンドを使用できます。 +.PP +break +.RS 4 +指定したメソッドのコンパイルの最初に停止するために、JVMのデバッグ時のブレークポイントを設定します。 +.RE +.PP +compileonly +.RS 4 +指定したメソッドを除いたすべてのメソッドを、コンパイルから除外します。別の方法として、\fB\-XX:CompileOnly\fRオプションを使用して複数のメソッドを指定できます。 +.RE +.PP +dontinline +.RS 4 +指定したメソッドをインライン化しないようにします。 +.RE +.PP +exclude +.RS 4 +指定したメソッドをコンパイルから除外します。 +.RE +.PP +help +.RS 4 +\fB\-XX:CompileCommand\fRオプションのヘルプ・メッセージを印刷します。 +.RE +.PP +inline +.RS 4 +指定したメソッドをインライン化しようとします。 +.RE +.PP +log +.RS 4 +指定したメソッドを除くすべてのメソッドに対して、(\fB\-XX:+LogCompilation\fRオプションを使用して)コンパイル・ロギングを除外します。デフォルトでは、コンパイルされたすべてのメソッドにロギングが実行されます。 +.RE +.PP +option +.RS 4 +このコマンドは、最後の引数(\fIoption\fR)のかわりに、指定したメソッドにJITコンパイル・オプションを渡すために使用できます。コンパイル・オプションは、メソッド名の後の末尾に設定されます。たとえば、\fBStringBuffer\fRクラスの\fBappend()\fRメソッドに対して\fBBlockLayoutByFrequency\fRオプションを有効にするには、次を使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=option,java/lang/StringBuffer\&.append,BlockLayoutByFrequency\fR + +.fi +.if n \{\ +.RE +.\} +カンマまたは空白で区切って、複数のコンパイル・オプションを指定できます。 +.RE +.PP +print +.RS 4 +指定したメソッドのコンパイル後に生成されたアセンブラ・コードを出力します。 +.RE +.PP +quiet +.RS 4 +コンパイル・コマンドを出力しません。デフォルトでは、\fB\-XX:CompileCommand\fRオプションを使用して指定したコマンドが出力されます。たとえば、\fBString\fRクラスの\fBindexOf()\fRメソッドのコンパイルから除外する場合、次が標準出力に出力されます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCompilerOracle: exclude java/lang/String\&.indexOf\fR + +.fi +.if n \{\ +.RE +.\} +他の\fB\-XX:CompileCommand\fRオプションの前に\fB\-XX:CompileCommand=quiet\fRオプションを指定することによって、これを抑制できます。 +.RE +.RE +.PP +\-XX:CompileCommandFile=\fIfilename\fR +.RS 4 +JITコンパイラ・コマンドの読取り元のファイルを設定します。デフォルトでは、JITコンパイラによって実行されるコマンドを格納するために、\fB\&.hotspot_compiler\fRファイルが使用されます。 +.sp +コマンド・ファイルの各行は、コマンドが使用されるコマンド、クラス名およびメソッド名を表します。たとえば、次の行は、\fBString\fRクラスの\fBtoString()\fRメソッドに対してアセンブリ・コードを出力します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBprint java/lang/String toString\fR + +.fi +.if n \{\ +.RE +.\} +メソッドで実行するJITコンパイラのコマンドの指定の詳細は、\fB\-XX:CompileCommand\fRオプションを参照してください。 +.RE +.PP +\-XX:CompileOnly=\fImethods\fR +.RS 4 +コンパイルを制限する(カンマで区切られた)メソッドのリストを設定します。指定したメソッドのみがコンパイルされます。完全クラス名(パッケージおよびサブパッケージを含む)で各メソッドを指定します。たとえば、\fBString\fRクラスの\fBlength()\fRメソッドおよび\fBList\fRクラスの\fBsize()\fRメソッドのみをコンパイルするには、次を使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileOnly=java/lang/String\&.length,java/util/List\&.size\fR + +.fi +.if n \{\ +.RE +.\} +スラッシュ(\fB/\fR)で区切られたすべてのパッケージおよびサブパッケージを含む、完全クラス名を指定します。切取りと貼付けの操作を容易にするために、\fB\-XX:+PrintCompilation\fRオプションおよび\fB\-XX:+LogCompilation\fRオプションによって生成されるメソッド名の形式を使用することもできます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileOnly=java\&.lang\&.String::length,java\&.util\&.List::size\fR + +.fi +.if n \{\ +.RE +.\} +ワイルドカードはサポートされていませんが、クラス名またはパッケージ名だけを指定してクラスまたはパッケージのすべてのメソッドをコンパイルすることも、メソッドだけを指定して任意のクラスのこの名前を持つメソッドをコンパイルすることもできます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileOnly=java/lang/String\fR +\fB\-XX:CompileOnly=java/lang\fR +\fB\-XX:CompileOnly=\&.length\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CompileThreshold=\fIinvocations\fR +.RS 4 +コンパイル前に解釈されたメソッド呼出しの数を設定します。デフォルトでは、サーバーJVMでは、JITコンパイラは、10,000の解釈されたメソッド呼出しを実行して、効率的なコンパイルのための情報を収集します。クライアントJVMの場合、デフォルト設定は1,500呼出しです。層コンパイルが有効な場合、このオプションは無視されます。オプション\fB\-XX:+TieredCompilation\fRを参照してください。次の例では、解釈されたメソッド呼出しの数を5,000に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileThreshold=5000\fR + +.fi +.if n \{\ +.RE +.\} +\fB\-Xcomp\fRオプションを指定して、コンパイルの前に、Javaメソッドの解釈を完全に無効にすることができます。 +.RE +.PP +\-XX:+DoEscapeAnalysis +.RS 4 +エスケープ分析の使用を有効にします。このオプションはデフォルトで有効になっています。エスケープ分析の使用を無効にするには、\fB\-XX:\-DoEscapeAnalysis\fRを指定します。Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:InitialCodeCacheSize=\fIsize\fR +.RS 4 +初期コード・キャッシュ・サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルト値は500KBに設定されています。初期コード・キャッシュ・サイズをシステムの最小メモリー・ページ・サイズより小さくしないでください。次の例では、初期コード・キャッシュ・サイズを32KBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitialCodeCacheSize=32k\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+Inline +.RS 4 +メソッドのインライン化を有効にします。このオプションは、パフォーマンスを向上させるためにデフォルトで有効になっています。メソッドのインライン化を無効にするには、\fB\-XX:\-Inline\fRを指定します。 +.RE +.PP +\-XX:InlineSmallCode=\fIsize\fR +.RS 4 +インライン化が必要なコンパイルされたメソッドの最大コード・サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。指定したサイズより小さいサイズのコンパイルされたメソッドのみが、インライン化されます。デフォルトでは、最大コード・サイズは1000バイトに設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InlineSmallCode=1000\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+LogCompilation +.RS 4 +現在の作業ディレクトリにある\fBhotspot\&.log\fRという名前のファイルへのコンパイル・アクティビティのロギングを有効にします。\fB\-XX:LogFile\fRオプションを使用して、異なるログ・ファイル・パスと名前を指定できます。 +.sp +デフォルトでは、このオプションは無効であり、コンパイル・アクティビティは記録されません。\fB\-XX:+LogCompilation\fRオプションは、診断JVMオプションのロックを解除する\fB\-XX:UnlockDiagnosticVMOptions\fRオプションとともに使用する必要があります。 +.sp +\fB\-XX:+PrintCompilation\fRオプションを使用して、メソッドをコンパイルするたびに、コンソールに出力されたメッセージを含む詳細な診断出力を有効化できます。 +.RE +.PP +\-XX:MaxInlineSize=\fIsize\fR +.RS 4 +インライン化するメソッドの最大バイトコード・サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルトでは、最大バイトコード・サイズは35バイトに設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxInlineSize=35\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxNodeLimit=\fInodes\fR +.RS 4 +単一のメソッドのコンパイル時に使用されるノードの最大数を設定します。デフォルトでは、ノードの最大数は65,000に設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxNodeLimit=65000\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxTrivialSize=\fIsize\fR +.RS 4 +インライン化する単純メソッドの最大バイトコード・サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルトでは、単純メソッドの最大バイトコード・サイズは6バイトに設定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxTrivialSize=6\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+OptimizeStringConcat +.RS 4 +\fBString\fR連結操作の最適化を有効にします。このオプションはデフォルトで有効になっています。\fBString\fR連結操作の最適化を無効にするには、\fB\-XX:\-OptimizeStringConcat\fRを指定します。Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:+PrintAssembly +.RS 4 +外部の\fBdisassembler\&.so\fRライブラリを使用して、バイトコード化されたネイティブのメソッドのアセンブリ・コードの出力を有効にします。これにより、生成されたコードを表示することができ、パフォーマンスの問題の診断に役立ちます。 +.sp +デフォルトでは、このオプションは無効であり、アセンブリ・コードは印刷されません。\fB\-XX:+PrintAssembly\fRオプションは、診断JVMオプションのロックを解除する\fB\-XX:UnlockDiagnosticVMOptions\fRオプションとともに使用する必要があります。 +.RE +.PP +\-XX:+PrintCompilation +.RS 4 +メソッドをコンパイルするたびに、コンソールにメッセージを出力することによって、JVMからの詳細な診断出力を有効にします。これにより、実際にコンパイルされるメソッドを確認できます。デフォルトでは、このオプションは無効であり、診断出力は印刷されません。 +.sp +\fB\-XX:+LogCompilation\fRオプションを使用して、コンパイル・アクティビティをファイルに記録することもできます。 +.RE +.PP +\-XX:+PrintInlining +.RS 4 +インライン化の決定内容の出力を有効にします。これにより、インライン化されるメソッドを確認できます。 +.sp +デフォルトでは、このオプションは無効であり、インライン化情報は出力されません。\fB\-XX:+PrintInlining\fRオプションは、診断JVMオプションのロックを解除する\fB\-XX:+UnlockDiagnosticVMOptions\fRオプションとともに使用する必要があります。 +.RE +.PP +\-XX:ReservedCodeCacheSize=\fIsize\fR +.RS 4 +JITコンパイルされたコードの最大コード・キャッシュ・サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。このオプションは2GBの制限があります。そうでない場合は、エラーが生成されます。最大コード・キャッシュ・サイズを初期コード・キャッシュ・サイズより小さくしないでください。\fB\-XX:InitialCodeCacheSize\fRオプションを参照してください。このオプションは\fB\-Xmaxjitcodesize\fRと同等です。 +.RE +.PP +\-XX:RTMAbortRatio=\fIabort_ratio\fR +.RS 4 +RTM中止率は、すべての実行済RTMトランザクションに対するパーセンテージ(%)として指定されます。中止されたトランザクション数がこの率を超えた場合、コンパイルされたコードが非最適化されます。この率は、\fB\-XX:+UseRTMDeopt\fRオプションが有効な場合に使用されます。このオプションのデフォルト値は50です。つまり、すべてのトランザクションの50%が中止された場合、コンパイルされたコードが非最適化されます。 +.RE +.PP +\-XX:RTMRetryCount=\fInumber_of_retries\fR +.RS 4 +中止またはビジーの場合、RTMロック・コードは、標準のロック・メカニズムにフォールバックする前にこのオプションによって指定された回数再試行されます。このオプションのデフォルト値は5です。\fB\-XX:UseRTMLocking\fRオプションを有効化する必要があります。 +.RE +.PP +\-XX:+TieredCompilation +.RS 4 +層コンパイルの使用を有効にします。デフォルトでは、このオプションは有効になっています。Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:+UseAES +.RS 4 +Intel、AMDおよびSPARCハードウェアに対して、ハードウェアベースのAES組込みを有効化します。Intel Westmere (2010以降)、AMD Bulldozer (2011以降)およびSPARC (T4以降)が、サポートされているハードウェアです。UseAESは、UseAESIntrinsicsとともに使用します。 +.RE +.PP +\-XX:+UseAESIntrinsics +.RS 4 +UseAESとUseAESIntrinsicsフラグはデフォルトで有効化されており、Java HotSpot Server VM 32ビットおよび64ビットに対してのみサポートされています。ハードウェアベースのAES組込みを無効化するには、\fB\-XX:\-UseAES \-XX:\-UseAESIntrinsics\fRを指定します。たとえば、ハードウェアAESを有効化するには、次のフラグを使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:+UseAES \-XX:+UseAESIntrinsics\fR + +.fi +.if n \{\ +.RE +.\} +32ビットおよび64ビットに対してUseAESおよびUseAESIntrinsicsフラグをサポートするには、\fB\-server\fRオプションを使用してJava HotSpot Server VMを選択します。これらのフラグは、クライアントVMではサポートされていません。 +.RE +.PP +\-XX:+UseCodeCacheFlushing +.RS 4 +コンパイラをシャットダウンする前に、コード・キャッシュのフラッシュを有効にします。このオプションはデフォルトで有効になっています。コンパイラをシャットダウンする前にコード・キャッシュのフラッシュを無効にするには\fB\-XX:\-UseCodeCacheFlushing\fRを指定します。 +.RE +.PP +\-XX:+UseCondCardMark +.RS 4 +カード表の更新前に、カードがすでにマークされているかどうかのチェックを有効にします。このオプションは、デフォルトでは無効になっており、複数のソケットを持つマシン上でのみ使用する必要があります。これにより、同時操作にかなり依存しているJavaアプリケーションのパフォーマンスが向上します。Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.PP +\-XX:+UseRTMDeopt +.RS 4 +中止率に応じて、RTMロックを自動調整します。この率は、\fB\-XX:RTMAbortRatio\fRオプションによって指定されます。中止されたトランザクション数が中止率を超えた場合、ロックを含むメソッドがすべてのロックで標準のロックとして非最適化および再コンパイルされます。このオプションは、デフォルトでは無効になっています。\fB\-XX:+UseRTMLocking\fRオプションを有効化する必要があります。 +.RE +.PP +\-XX:+UseRTMLocking +.RS 4 +フォールバック・ハンドラとして標準のロック・メカニズムを使用して、展開されたすべてのロックに対してRestricted Transactional Memory (RTM)ロック・コードを生成します。このオプションは、デフォルトでは無効になっています。RTMに関連するオプションは、Transactional Synchronization Extensions (TSX)をサポートするx86 CPU上のJava HotSpot Server VMに対してのみ使用可能です。 +.sp +RTMは、x86命令セット拡張でマルチスレッド・アプリケーションの作成を容易にするIntelのTSXの一部です。RTMでは、新しい命令 +\fBXBEGIN\fR、\fBXABORT\fR、\fBXEND\fRおよび\fBXTEST\fRが導入されています。\fBXBEGIN\fRおよび\fBXEND\fR命令は、トランザクションとして実行するための命令セットを囲みます。トランザクションの実行時に競合が見つからなかった場合、メモリーとレジスタの変更が、\fBXEND\fR命令で同時にコミットされます。\fBXABORT\fR命令ではトランザクションを明示的に中止でき、\fBXEND\fR命令では命令セットがトランザクション内で実行中かどうかを確認できます。 +.sp +トランザクションのロックは、別のスレッドが同じトランザクションにアクセスしようとしたときに展開されます。したがって、そのトランザクションへのアクセスを最初にリクエストしなかったスレッドはブロックされます。RTMでは、トランザクションが中止または失敗した場合のために、フォールバックの操作セットを指定する必要があります。RTMロックとは、TSXのシステムに委譲されているロックです。 +.sp +RTMにより、重要なリージョンにおいて衝突が少なく競合度の高いロックのパフォーマンスが向上されます(これは、複数のスレッドによって同時にアクセスできないコードです)。また、RTMにより、粗粒度ロックのパフォーマンスも向上されますが、一般的にマルチスレッド・アプリケーションでのパフォーマンスはよくありません。(粗粒度ロックとは、ロックの取得および解放のオーバーヘッドを最小化するために長い期間ロックを保持する戦略であり、一方、細粒度ロックとは必要な場合のみロックし可能なかぎり早期にロック解除することで最大限の並行処理の達成を試みる戦略です。)さらに、異なるスレッドによって使用されている軽度な競合ロックの場合、RTMにより、誤ったキャッシュ・ライン共有(キャッシュ・ライン・ピンポンとも呼ばれる)を削減できます。これは、異なるプロセッサからの複数のスレッドが異なるリソースにアクセスしている場合に発生しますが、リソースは同じキャッシュ・ラインを共有します。結果として、プロセッサは他のプロセッサのキャッシュ・ラインを繰り返し無効にし、これにより、キャッシュではなくメイン・メモリーからの読取りが強制されます。 +.RE +.PP +\-XX:+UseSHA +.RS 4 +SPARCハードウェアのSHA暗号化ハッシュ関数のハードウェアベースの組込みを有効にします。\fBUseSHA\fRは、\fBUseSHA1Intrinsics\fR、\fBUseSHA256Intrinsics\fRおよび\fBUseSHA512Intrinsics\fRオプションと組み合せて使用します。 +.sp +\fBUseSHA\fRおよび\fBUseSHA*Intrinsics\fRフラグはデフォルトで有効であり、SPARC T4以上のJava HotSpot Server VM 64ビットでのみサポートされます。 +.sp +SHA操作に対して\fBsun\&.security\&.provider\&.Sun\fRプロバイダを使用する場合のみ、この機能を適用できます。 +.sp +すべてのハードウェアベースのSHA組込みを無効化するには、\fB\-XX:\-UseSHA\fRを指定してください。特定のSHA組込みのみ無効化するには、適切な対応するオプションを使用してください。たとえば、\fB\-XX:\-UseSHA256Intrinsics\fRなどです。 +.RE +.PP +\-XX:+UseSHA1Intrinsics +.RS 4 +SHA\-1暗号化ハッシュ関数の組込みを有効にします。 +.RE +.PP +\-XX:+UseSHA256Intrinsics +.RS 4 +SHA\-224およびSHA\-256暗号化ハッシュ関数の組込みを有効にします。 +.RE +.PP +\-XX:+UseSHA512Intrinsics +.RS 4 +SHA\-384およびSHA\-512暗号化ハッシュ関数の組込みを有効にします。 +.RE +.PP +\-XX:+UseSuperWord +.RS 4 +スカラー演算のスーパーワード演算への変換を有効にします。このオプションはデフォルトで有効になっています。スカラー演算のスーパーワード演算への変換を無効にするには、\fB\-XX:\-UseSuperWord\fRを指定します。Java HotSpot Server VMのみが、このオプションをサポートしています。 +.RE +.SS "高度なサービスアビリティ・オプション" +.PP +これらのオプションは、システム情報を収集し、詳細なデバッグを実行する機能を提供します。 +.PP +\-XX:+ExtendedDTraceProbes +.RS 4 +パフォーマンスに影響を与える追加の\fBdtrace\fRツール・プローブを有効にします。デフォルトでは、このオプションは無効になっており、\fBdtrace\fRは標準プローブのみを実行します。 +.RE +.PP +\-XX:+HeapDumpOnOutOfMemory +.RS 4 +\fBjava\&.lang\&.OutOfMemoryError\fR例外がスローされた場合に、ヒープ・プロファイラ(HPROF)を使用して、現在のディレクトリ内のファイルへのJavaヒープのダンプを有効にします。\fB\-XX:HeapDumpPath\fRオプションを使用して、ヒープ・ダンプ・ファイルのパスおよび名前を明示的に設定できます。デフォルトでは、このオプションは無効であり、\fBOutOfMemoryError\fR例外がスローされた場合にヒープはダンプされません。 +.RE +.PP +\-XX:HeapDumpPath=\fIpath\fR +.RS 4 +\fB\-XX:+HeapDumpOnOutOfMemoryError\fRオプションが設定されている場合、ヒープ・プロファイラ(HPROF)が提供するヒープ・ダンプを書き込むパスおよびファイル名を設定します。デフォルトでは、このファイルは、現在の作業ディレクトリに作成され、名前は\fBjava_pid\fR\fIpid\fR\fB\&.hprof\fR +(\fIpid\fRはエラーの原因となったプロセスの識別子)になります。次の例では、デフォルトのファイルを明示的に設定する方法を示します(\fB%p\fRは現在のプロセスの識別子を表します)。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:HeapDumpPath=\&./java_pid%p\&.hprof\fR + +.fi +.if n \{\ +.RE +.\} +次の例では、ヒープ・ダンプ・ファイルを\fB/var/log/java/java_heapdump\&.hprof\fRに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:HeapDumpPath=/var/log/java/java_heapdump\&.hprof\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:LogFile=\fIpath\fR +.RS 4 +ログ・データが書き込まれるパスおよびファイル名を設定します。デフォルトでは、ファイルは現在の作業ディレクトリに作成され、名前は\fBhotspot\&.log\fRです。 +.sp +次の例では、ログ・ファイルを\fB/var/log/java/hotspot\&.log\fRに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:LogFile=/var/log/java/hotspot\&.log\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+PrintClassHistogram +.RS 4 +\fB[Control]+[C]\fRイベント(\fBSIGTERM\fR)後にクラス・インスタンスのヒストグラムの印刷を有効にします。デフォルトでは、このオプションは無効です。 +.sp +このオプションを設定すると、\fBjmap \-histo\fRコマンド、または\fBjcmd \fR\fIpid\fR\fB GC\&.class_histogram\fRコマンド(\fIpid\fRは現在のJavaプロセスの識別子)を実行する場合と同じになります。 +.RE +.PP +\-XX:+PrintConcurrentLocks +.RS 4 +印刷を有効にします ロック(次の後) \- イベント。デフォルトでは、このオプションは無効です。 +.sp +\fB[Control]+[C]\fRイベント(\fBSIGTERM\fR)後に\fBjava\&.util\&.concurrent\fRロックの印刷を有効にします。デフォルトでは、このオプションは無効です。 +.sp +このオプションを設定すると、\fBjstack \-l\fRコマンド、または\fBjcmd \fR\fIpid\fR\fB Thread\&.print \-l\fRコマンド(\fIpid\fRは現在のJavaプロセスの識別子)を実行する場合と同じになります。 +.RE +.PP +\-XX:+UnlockDiagnosticVMOptions +.RS 4 +JVMの診断を目的としたオプションをアンロックします。デフォルトでは、このオプションは無効であり、診断オプションは使用できません。 +.RE +.SS "高度なガベージ・コレクション・オプション" +.PP +これらのオプションは、ガベージ・コレクション(GC)がJava HotSpot VMによってどのように実行されるかを制御します。 +.PP +\-XX:+AggressiveHeap +.RS 4 +Javaヒープの最適化を有効にします。これにより、コンピュータの構成(RAMおよびCPU)に基づいて、様々なパラメータが、メモリー割当てが集中した長時間実行ジョブに最適になるように設定されます。デフォルトでは、このオプションは無効であり、ヒープは最適化されません。 +.RE +.PP +\-XX:+AlwaysPreTouch +.RS 4 +JVMの初期化中にJavaヒープ上のすべてのページのタッチを有効にします。これにより、\fBmain()\fRメソッドの入力前に、すべてのページがメモリーに取得されます。このオプションは、物理メモリーにマップされたすべての仮想メモリーを含む長時間実行のシステムをシミュレートするテストで使用できます。デフォルトでは、このオプションは無効になっており、JVMヒープ領域がいっぱいになると、すべてのページがコミットされます。 +.RE +.PP +\-XX:+CMSClassUnloadingEnabled +.RS 4 +並行マークスイープ(CMS)ガベージ・コレクタを使用する場合に、アンロードするクラスを有効にします。このオプションはデフォルトで有効になっています。CMSガベージ・コレクタのクラス・アンロードを無効にするには、\fB\-XX:\-CMSClassUnloadingEnabled\fRを指定します。 +.RE +.PP +\-XX:CMSExpAvgFactor=\fIpercent\fR +.RS 4 +並行コレクション統計の指数平均を計算する際に、現在のサンプルを重み付けするために使用される時間の割合(0から100まで)を設定します。デフォルトでは、指数平均係数は25%に設定されています。次の例では、係数を15%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CMSExpAvgFactor=15\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CMSInitiatingOccupancyFraction=\fIpercent\fR +.RS 4 +CMS収集サイクルを開始する古い世代の占有率(0から100まで)を設定します。デフォルト値は\-1に設定されています。負の値(デフォルトを含む)は、\fB\-XX:CMSTriggerRatio\fRが開始占有率の値を定義するために使用されることを意味します。 +.sp +次の例では、占有率を20%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CMSInitiatingOccupancyFraction=20\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+CMSScavengeBeforeRemark +.RS 4 +CMSコメント・ステップの前にスカベンジの試行を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:CMSTriggerRatio=\fIpercent\fR +.RS 4 +CMS収集サイクルが開始する前に割り当てられる\fB\-XX:MinHeapFreeRatio\fRによって指定される値の割合(0から100まで)を設定します。デフォルト値は80%に設定されています。 +.sp +次の例では、占有率を75%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CMSTriggerRatio=75\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:ConcGCThreads=\fIthreads\fR +.RS 4 +並行GCに使用されるスレッドの数を設定します。デフォルト値は、JVMに使用できるCPUの数によって異なります。 +.sp +たとえば、並行GCのスレッド数を2に設定するには、次のオプションを指定します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ConcGCThreads=2\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+DisableExplicitGC +.RS 4 +\fBSystem\&.gc()\fRの呼出しの処理を無効にするオプションを有効にします。このオプションはデフォルトで無効になっており、これは\fBSystem\&.gc()\fRへの呼出しが処理されることを意味します。\fBSystem\&.gc()\fRの呼出しの処理が無効になっている場合、JVMは必要に応じてGCを実行します。 +.RE +.PP +\-XX:+ExplicitGCInvokesConcurrent +.RS 4 +\fBSystem\&.gc()\fRリクエストを使用することによって、並行GCの呼出しを有効にします。このオプションはデフォルトで無効になっており、\fB\-XX:+UseConcMarkSweepGC\fRオプションとともに使用する場合のみ、有効にすることができます。 +.RE +.PP +\-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses +.RS 4 +\fBSystem\&.gc()\fRリクエストを使用し、並行GCサイクル中にクラスをアンロードすることによって、並行GCの呼出しを有効にします。このオプションはデフォルトで無効になっており、\fB\-XX:+UseConcMarkSweepGC\fRオプションとともに使用する場合のみ、有効にすることができます。 +.RE +.PP +\-XX:G1HeapRegionSize=\fIsize\fR +.RS 4 +ガベージファースト(G1)コレクタを使用する際にJavaヒープを細分化するリージョンのサイズを設定します。値には、1MBから32MBまでを指定できます。デフォルトのリージョン・サイズは、ヒープ・サイズに基づいて人間工学的に決定されます。 +.sp +次の例では、細分化されたサイズを16MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:G1HeapRegionSize=16m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+G1PrintHeapRegions +.RS 4 +割り当てられたリージョンおよびG1コレクタによって再要求されたものに関する情報の印刷を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:G1ReservePercent=\fIpercent\fR +.RS 4 +G1コレクタの昇格が失敗する可能性を減らすためのfalseの上限として予約されたヒープの割合(0から50まで)を設定します。デフォルトでは、このオプションは10%に設定されています。 +.sp +次の例では、予約されたヒープを20%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:G1ReservePercent=20\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:InitialHeapSize=\fIsize\fR +.RS 4 +メモリー割当てプールの初期サイズ(バイト単位)を設定します。指定する値は、0、または1MBより大きい1024の倍数のいずれかにする必要があります。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルト値は、実行時にシステム構成に基づいて選択されます。http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/gctuning/index\&.htmlの\fIJava SE HotSpot仮想マシンのガベージ・コレクション・チューニング・ガイド\fRのエルゴノミクスに関する項を参照してください。 +.sp +次の例では、割り当てられたメモリーのサイズを様々な単位を使用して6MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitialHeapSize=6291456\fR +\fB\-XX:InitialHeapSize=6144k\fR +\fB\-XX:InitialHeapSize=6m\fR + +.fi +.if n \{\ +.RE +.\} +このオプションを0に設定した場合、初期サイズは、古い世代と若い世代に割り当てられたサイズの合計として設定されます。若い世代のヒープのサイズは、\fB\-XX:NewSize\fRオプションを使用して設定できます。 +.RE +.PP +\-XX:InitialSurvivorRatio=\fIratio\fR +.RS 4 +スループット・ガベージ・コレクタが使用するサバイバ領域の初期比を設定します(\fB\-XX:+UseParallelGC\fRおよび/または\fB\-XX:+UseParallelOldGC\fRオプションによって有効になります)。\fB\-XX:+UseParallelGC\fRオプションおよび\fB\-XX:+UseParallelOldGC\fRオプションを使用することによって、スループット・ガベージ・コレクタで適応サイズ指定をデフォルトで有効にします。初期値から始めて、アプリケーションの動作に従って、サバイバ領域がサイズ変更されます。(\fB\-XX:\-UseAdaptiveSizePolicy\fRオプションを使用して)適応サイズ指定を無効にした場合、\fB\-XX:SurvivorRatio\fRオプションを使用して、アプリケーションの実行全体のサバイバ領域のサイズを設定する必要があります。 +.sp +次の式を使用して、若い世代のサイズ(Y)およびサバイバ領域の初期比(R)に基づいて、サバイバ領域の初期サイズ(S)を計算できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBS=Y/(R+2)\fR + +.fi +.if n \{\ +.RE +.\} +等式内の2は、2つのサバイバ領域を示します。サバイバ領域の初期比に指定する値を大きくすると、サバイバ領域の初期サイズは小さくなります。 +.sp +デフォルトでは、サバイバ領域の初期比は8に設定されています。若い世代の領域サイズのデフォルト値(2MB)を使用した場合、サバイバ領域の初期サイズは0\&.2MBになります。 +.sp +次の例では、サバイバ領域の初期比を4に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitialSurvivorRatio=4\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:InitiatingHeapOccupancyPercent=\fIpercent\fR +.RS 4 +並行GCサイクルを開始するヒープ占有率(0から100まで)を設定します。これは、1つの世代のみ(たとえばG1ガベージ・コレクタなど)ではなく、ヒープ全体の占有に基づいて並行GCサイクルをトリガーするガベージ・コレクタによって使用されます。 +.sp +デフォルトでは、開始値は45%に設定されています。値0は、GCサイクルが停止しないことを意味します。次の例では、開始ヒープ占有率を75%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitiatingHeapOccupancyPercent=75\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxGCPauseMillis=\fItime\fR +.RS 4 +最大GC休止時間(ミリ秒単位)のターゲットを設定します。これはソフト・ゴールのため、JVMは実現のために最善の努力をします。デフォルトでは、休止時間の最大値はありません。 +.sp +次の例では、最大ターゲット休止時間を500ミリ秒に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxGCPauseMillis=500\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxHeapSize=\fIsize\fR +.RS 4 +メモリー割当てプールの最大サイズ(バイト単位)を設定します。指定する値は、2MBより大きい1024の倍数にする必要があります。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。デフォルト値は、実行時にシステム構成に基づいて選択されます。サーバー・デプロイメントでは、\fB\-XX:InitialHeapSize\fRおよび\fB\-XX:MaxHeapSize\fRは通常同じ値に設定されます。http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/gctuning/index\&.htmlの\fIJava SE HotSpot仮想マシンのガベージ・コレクション・チューニング・ガイド\fRのエルゴノミクスに関する項を参照してください。 +.sp +次の例では、割り当てられたメモリーの許可される最大サイズを様々な単位を使用して80MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxHeapSize=83886080\fR +\fB\-XX:MaxHeapSize=81920k\fR +\fB\-XX:MaxHeapSize=80m\fR + +.fi +.if n \{\ +.RE +.\} +Oracle Solaris 7およびOracle Solaris 8 SPARCプラットフォームの場合のこの値の上限は、およそ4,000MBからオーバーヘッドの量を引いたものです。Oracle Solaris 2\&.6およびx86プラットフォームの場合の上限は、およそ2,000MBからオーバーヘッドの量を引いたものです。Linuxプラットフォームの場合の上限は、およそ2,000MBからオーバーヘッドの量を引いたものです。 +.sp +\fB\-XX:MaxHeapSize\fRオプションは\fB\-Xmx\fRと同等です。 +.RE +.PP +\-XX:MaxHeapFreeRatio=\fIpercent\fR +.RS 4 +GCイベント後の空きヒープ領域の許可されている最大の割合(0から100まで)を設定します。空きヒープ領域がこの値を超えて拡大した場合、そのヒープは縮小します。デフォルトでは、この値は70%に設定されています。 +.sp +次の例では、空きヒープの最大比率を75%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxHeapFreeRatio=75\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxMetaspaceSize=\fIsize\fR +.RS 4 +クラス・メタデータに割り当てることができるネイティブ・メモリーの最大量を設定します。デフォルトでは、このサイズは制限されていません。アプリケーションのメタデータの量は、アプリケーション自体、他の実行中アプリケーション、およびシステムで使用可能なメモリーの量によって異なります。 +.sp +次の例では、クラス・メタデータの最大サイズを256MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxMetaspaceSize=256m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxNewSize=\fIsize\fR +.RS 4 +若い世代(ナーサリ)のヒープの最大サイズ(バイト単位)を設定します。デフォルト値は人間工学的に設定されます。 +.RE +.PP +\-XX:MaxTenuringThreshold=\fIthreshold\fR +.RS 4 +適応GCサイズ指定で使用する最大殿堂入りしきい値を設定します。最大値は15です。デフォルト値は、パラレル(スループット)コレクタの場合は15、CMSコレクタの場合は6です。 +.sp +次の例では、最大殿堂入りしきい値を10に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxTenuringThreshold=10\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MetaspaceSize=\fIsize\fR +.RS 4 +最初に超えたときにガベージ・コレクションをトリガーする、割り当てられたクラス・メタデータ領域のサイズを設定します。このガベージ・コレクションのしきい値は、使用されるメタデータの量によって増加または減少します。デフォルトのサイズはプラットフォームによって異なります。 +.RE +.PP +\-XX:MinHeapFreeRatio=\fIpercent\fR +.RS 4 +GCイベント後の空きヒープ領域の許可されている最小の割合(0から100まで)を設定します。空きヒープ領域がこの値を下回った場合、そのヒープは拡大します。デフォルトでは、この値は40%に設定されています。 +.sp +次の例では、空きヒープの最小比率を25%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MinHeapFreeRatio=25\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:NewRatio=\fIratio\fR +.RS 4 +若い世代のサイズと古い世代のサイズとの比率を設定します。デフォルトでは、このオプションは2に設定されています。次の例では、若い/古いの比率を1に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:NewRatio=1\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:NewSize=\fIsize\fR +.RS 4 +若い世代(ナーサリ)のヒープの初期サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。 +.sp +ヒープの若い世代リージョンは新しいオブジェクトに使用されます。GCは、他のリージョンよりこのリージョンで、より頻繁に実行されます。若い世代のサイズが小さすぎる場合、多数のマイナーGCが実行されます。サイズが大きすぎる場合、フルGCのみが実行されますが、完了までに時間がかかることがあります。若い世代のサイズは、全体のヒープ・サイズの半分から4分の1の間にしておくことをお薦めします。 +.sp +次の例では、若い世代の初期サイズを様々な単位を使用して256MBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:NewSize=256m\fR +\fB\-XX:NewSize=262144k\fR +\fB\-XX:NewSize=268435456\fR + +.fi +.if n \{\ +.RE +.\} +\fB\-XX:NewSize\fRオプションは\fB\-Xmn\fRと同等です。 +.RE +.PP +\-XX:ParallelGCThreads=\fIthreads\fR +.RS 4 +若い世代と古い世代でパラレル・ガベージ・コレクションに使用するスレッドの数を設定します。デフォルト値は、JVMに使用できるCPUの数によって異なります。 +.sp +たとえば、パラレルGCのスレッド数を2に設定するには、次のオプションを指定します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ParallelGCThreads=2\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+ParallelRefProcEnabled +.RS 4 +パラレル参照処理を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintAdaptiveSizePolicy +.RS 4 +適応世代サイズ指定に関する情報の出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintGC +.RS 4 +GCごとのメッセージの出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintGCApplicationConcurrentTime +.RS 4 +最後の休止(たとえばGC休止など)以降に経過した時間の出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintGCApplicationStoppedTime +.RS 4 +休止(たとえばGC休止など)が継続した時間の出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintGCDateStamps +.RS 4 +GCごとの日付スタンプの出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintGCDetails +.RS 4 +GCごとの詳細メッセージの出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintGCTaskTimeStamps +.RS 4 +個々のGCワーカー・スレッド・タスクごとのタイムスタンプの出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintGCTimeStamps +.RS 4 +GCごとのタイムスタンプの出力を有効にします。デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+PrintStringDeduplicationStatistics +.RS 4 +詳細な重複除外統計を印刷します。デフォルトでは、このオプションは無効です。\fB\-XX:+UseStringDeduplication\fRオプションを参照してください。 +.RE +.PP +\-XX:+PrintTenuringDistribution +.RS 4 +殿堂入り期間情報の出力を有効にします。次に、出力の例を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBDesired survivor size 48286924 bytes, new threshold 10 (max 10)\fR +\fB\- age 1: 28992024 bytes, 28992024 total\fR +\fB\- age 2: 1366864 bytes, 30358888 total\fR +\fB\- age 3: 1425912 bytes, 31784800 total\fR +\fB\&.\&.\&.\fR + +.fi +.if n \{\ +.RE +.\} +期間1オブジェクトは、最も若いサバイバです(前のスカベンジの後に作成され、最新のスカベンジで存続し、Eden領域からサバイバ領域に移動しました)。期間2オブジェクトは、2つのスカベンジで存続します(2番目のスカベンジ中に、あるサバイバ領域から次の領域にコピーされました)。このように続きます。 +.sp +前述の例では、28,992,024バイトが1つのスカベンジで存続し、Eden領域からサバイバ領域にコピーされました。1,366,864バイトは期間2オブジェクトなどにより占有されています。各行の3番目の値は、期間n以下のオブジェクトの累積サイズです。 +.sp +デフォルトでは、このオプションは無効です。 +.RE +.PP +\-XX:+ScavengeBeforeFullGC +.RS 4 +それぞれのフルGCの前に若い世代のGCを有効にします。このオプションはデフォルトで有効になっています。フルGCの前に若い世代のスカベンジを行うと、古い世代領域から若い世代領域へのアクセスが可能なオブジェクトの数を減らすことができるため、これを無効に\fIしない\fRことをお薦めします。各フルGCの前に若い世代のGCを無効にするには、\fB\-XX:\-ScavengeBeforeFullGC\fRを指定します。 +.RE +.PP +\-XX:SoftRefLRUPolicyMSPerMB=\fItime\fR +.RS 4 +ソフト・アクセスが可能なオブジェクトが最後に参照されてからヒープ上でアクティブなままになっている時間(ミリ秒単位)を設定します。デフォルト値は、ヒープ内の空きメガバイト当たりで1秒の存続期間です。\fB\-XX:SoftRefLRUPolicyMSPerMB\fRオプションは、現在のヒープ・サイズ(Java HotSpot Client VM用)または最大可能ヒープ・サイズ(Java HotSpot Server VM用)の1メガバイト当たりのミリ秒を表す整数値を受け入れます。この違いは、Client VMは、ヒープを大きくするのではなく、ソフト参照をフラッシュする傾向があるのに対し、Server VMは、ソフト参照をフラッシュするのではなく、ヒープを大きくする傾向があることを意味します。後者の場合、\fB\-Xmx\fRオプションの値は、ソフト参照がどのくらい迅速にガベージ・コレクションされるかに重要な影響を及ぼします。 +.sp +次の例では、値を2\&.5秒に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:SoftRefLRUPolicyMSPerMB=2500\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:StringDeduplicationAgeThreshold=\fIthreshold\fR +.RS 4 +指定した期間に到達しつつある\fBString\fRオブジェクトは、重複除外の候補とみなされます。オブジェクトの期間は、オブジェクトがガベージ・コレクションで存続した回数の測定値です。これは、殿堂入りと呼ばれる場合もあります。\fB\-XX:+PrintTenuringDistribution\fRオプションを参照してください。この期間に到達する前に古いヒープ・リージョンに昇格された\fBString\fRオブジェクトは、常に重複除外の候補とみなされます。このオプションのデフォルト値は\fB3\fRです。\fB\-XX:+UseStringDeduplication\fRオプションを参照してください。 +.RE +.PP +\-XX:SurvivorRatio=\fIratio\fR +.RS 4 +Eden領域のサイズとサバイバ領域のサイズとの比率を設定します。デフォルトでは、このオプションは8に設定されています。次の例では、Eden/サバイバ領域の比率を4に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:SurvivorRatio=4\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:TargetSurvivorRatio=\fIpercent\fR +.RS 4 +若いガベージ・コレクションの後に使用されるサバイバ領域の目的の割合(0から100まで)を設定します。デフォルトでは、このオプションは50%に設定されています。 +.sp +次の例では、ターゲットのサバイバ領域の比率を30%に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:TargetSurvivorRatio=30\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:TLABSize=\fIsize\fR +.RS 4 +スレッド・ローカルな割当てバッファ(TLAB)の初期サイズ(バイト単位)を設定します。キロバイトを示す場合は文字\fBk\fRまたは\fBK\fR、メガバイトを示す場合は文字\fBm\fRまたは\fBM\fR、ギガバイトを示す場合は文字\fBg\fRまたは\fBG\fRを追加します。このオプションが0に設定されている場合、JVMでは初期サイズが自動的に選択されます。 +.sp +次の例では、TLABの初期サイズを512KBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:TLABSize=512k\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+UseAdaptiveSizePolicy +.RS 4 +適応世代サイズ指定の使用を有効にします。このオプションはデフォルトで有効になっています。適応世代サイズ指定を無効にするには、\fB\-XX:\-UseAdaptiveSizePolicy\fRを指定し、メモリー割当てプールのサイズを明示的に設定します(\fB\-XX:SurvivorRatio\fRオプションを参考にしてください)。 +.RE +.PP +\-XX:+UseCMSInitiatingOccupancyOnly +.RS 4 +CMSコレクタの開始のための唯一の基準としての占有値の使用を有効にします。デフォルトでは、このオプションは無効になっており、他の基準が使用されます。 +.RE +.PP +\-XX:+UseConcMarkSweepGC +.RS 4 +古い世代へのCMSガベージ・コレクタの使用を有効にします。アプリケーションの待機時間の要件を、スループット(\fB\-XX:+UseParallelGC\fR)ガベージ・コレクタによって満たすことができない場合、CMSガベージ・コレクタを使用することをお薦めします。G1ガベージ・コレクタ(\fB\-XX:+UseG1GC\fR)は別の代替となります。 +.sp +デフォルトでは、このオプションは無効になっており、コレクタは、マシンの構成およびJVMのタイプに基づいて、自動的に選択されます。このオプションが有効な場合、\fB\-XX:+UseParNewGC\fRオプションは自動的に設定され、無効化しない必要がありますが、理由はJDK 8では\fB\-XX:+UseConcMarkSweepGC \-XX:\-UseParNewGC\fRオプションの組合せが非推奨であるためです。 +.RE +.PP +\-XX:+UseG1GC +.RS 4 +ガベージファースト(G1)・ガベージ・コレクタの使用を有効にします。これはサーバー形式のガベージ・コレクタで、大量のRAMを持つマルチプロセッサ・マシンを対象としています。高い確率でGC休止時間の目標を達成し、同時に適切なスループットも維持します。G1コレクタは、GC待機時間の限定された要件(安定した予測可能な0\&.5秒未満の休止時間)を持つ、大きいヒープ(約6GB以上のサイズ)が必要なアプリケーションに推奨されます。 +.sp +デフォルトでは、このオプションは無効になっており、コレクタは、マシンの構成およびJVMのタイプに基づいて、自動的に選択されます。 +.RE +.PP +\-XX:+UseGCOverheadLimit +.RS 4 +\fBOutOfMemoryError\fR例外がスローされるまでに、GCでJVMによって要した時間の割合を制限するポリシーの使用を有効にします。デフォルトでは、このオプションは有効になっており、ガベージ・コレクションに合計時間の98%より多く費やされ、ヒープのリカバリが2%未満である場合、パラレルGCによって\fBOutOfMemoryError\fRがスローされます。ヒープが小さい場合、この機能は、アプリケーションが長期間ほとんどまたはまったく進捗なく実行している状態を回避するために使用できます。このオプションを無効にするには、\fB\-XX:\-UseGCOverheadLimit\fRを指定します。 +.RE +.PP +\-XX:+UseNUMA +.RS 4 +アプリケーションで短い待機時間のメモリーの使用を増加させることで、不均一なメモリー・アーキテクチャ(NUMA)を使用したマシン上のアプリケーションのパフォーマンス最適化を有効にします。デフォルトでは、このオプションは無効になっており、NUMAに対する最適化は行われません。このオプションは、パラレル・ガベージ・コレクタ(\fB\-XX:+UseParallelGC\fR)が使用されている場合のみ使用可能です。 +.RE +.PP +\-XX:+UseParallelGC +.RS 4 +複数のプロセッサを利用してアプリケーションのパフォーマンスを向上させる、パラレル・スカベンジ・ガベージ・コレクタ(スループット・コレクタとも呼ばれる)の使用を有効にします。 +.sp +デフォルトでは、このオプションは無効になっており、コレクタは、マシンの構成およびJVMのタイプに基づいて、自動的に選択されます。これを有効にした場合、明示的に無効にしないかぎり、\fB\-XX:+UseParallelOldGC\fRオプションが自動的に有効になります。 +.RE +.PP +\-XX:+UseParallelOldGC +.RS 4 +フルGCへのパラレル・ガベージ・コレクタの使用を有効にします。デフォルトでは、このオプションは無効です。これを有効にすると、\fB\-XX:+UseParallelGC\fRオプションが自動的に有効になります。 +.RE +.PP +\-XX:+UseParNewGC +.RS 4 +若い世代でのコレクションへのパラレル・スレッドの使用を有効にします。デフォルトでは、このオプションは無効です。\fB\-XX:+UseConcMarkSweepGC\fRオプションを設定すると、これは自動的に有効になります。JDK 8では、\fB\-XX:+UseConcMarkSweepGC\fRオプションを指定せずに\fB\-XX:+UseParNewGC\fRオプションを使用することは非推奨です。 +.RE +.PP +\-XX:+UseSerialGC +.RS 4 +シリアル・ガベージ・コレクタの使用を有効にします。ガベージ・コレクションから特別な機能を必要としない、小規模で単純なアプリケーションの場合には、これは一般に最適な選択です。デフォルトでは、このオプションは無効になっており、コレクタは、マシンの構成およびJVMのタイプに基づいて、自動的に選択されます。 +.RE +.PP +\-XX:+UseSHM +.RS 4 +Linuxでは、JVMで共有メモリーを使用してラージ・ページを設定できるようにします。 +.sp +詳細は、"ラージ・ページ"を参照してください。 +.RE +.PP +\-XX:+UseStringDeduplication +.RS 4 +文字列の重複除外を有効化します。デフォルトでは、このオプションは無効です。このオプションを使用するには、ガベージファースト(G1)・ガベージ・コレクタを有効にする必要があります。\fB\-XX:+UseG1GC\fRオプションを参照してください。 +.sp +多くの\fBString\fRオブジェクトが同じであるということから、\fIString deduplication\fRにより、Javaヒープ上の\fBString\fRオブジェクトのメモリー・フットプリントが削減されます。各\fBString\fRオブジェクトが独自の文字配列をポイントするのではなく、同一の\fBString\fRオブジェクトは同じ文字配列をポイントし共有できます。 +.RE +.PP +\-XX:+UseTLAB +.RS 4 +若い世代の領域でのスレッド・ローカルな割当てブロック(TLAB)の使用を有効にします。このオプションはデフォルトで有効になっています。TLABの使用を無効にするには、\fB\-XX:\-UseTLAB\fRを指定します。 +.RE +.SS "非推奨で削除されたオプション" +.PP +これらのオプションは、以前のリリースには含まれていましたが、以降は不要とみなされています。 +.PP +\-Xincgc +.RS 4 +インクリメンタル・ガベージ・コレクションを有効にします。このオプションはJDK 8では非推奨で、それに代わるものはありません。 +.RE +.PP +\-Xrun\fIlibname\fR +.RS 4 +指定したデバッグ/プロファイリングのライブラリをロードします。このオプションは、\fB\-agentlib\fRオプションに取って代わられました。 +.RE +.PP +\-XX:CMSIncrementalDutyCycle=\fIpercent\fR +.RS 4 +並行コレクタの実行が許可されているマイナー・コレクション間の時間の割合(0から100まで)を設定します。\fB\-XX:+CMSIncrementalMode\fRオプションの非推奨を受けて、このオプションはJDK 8では非推奨となり、それに代わるものはありません。 +.RE +.PP +\-XX:CMSIncrementalDutyCycleMin=\fIpercent\fR +.RS 4 +\fB\-XX:+CMSIncrementalPacing\fRが有効な場合にデューティ・サイクルの下限であるマイナー・コレクション間の時間の割合(0から100まで)を設定します。\fB\-XX:+CMSIncrementalMode\fRオプションの非推奨を受けて、このオプションはJDK 8では非推奨となり、それに代わるものはありません。 +.RE +.PP +\-XX:+CMSIncrementalMode +.RS 4 +CMSコレクタの増分モードを有効にします。\fBCMSIncremental\fRで始まるその他のオプションとともに、このオプションはJDK 8では非推奨となり、それに代わるものはありません +.RE +.PP +\-XX:CMSIncrementalOffset=\fIpercent\fR +.RS 4 +増分モードのデューティ・サイクルをマイナー・コレクション間で期間内に右に移動する時間の割合(0から100まで)を設定します。\fB\-XX:+CMSIncrementalMode\fRオプションの非推奨を受けて、このオプションはJDK 8では非推奨となり、それに代わるものはありません。 +.RE +.PP +\-XX:+CMSIncrementalPacing +.RS 4 +JVMの実行中に収集された統計に基づいて、増分モードのデューティ・サイクルの自動調整を有効にします。\fB\-XX:+CMSIncrementalMode\fRオプションの非推奨を受けて、このオプションはJDK 8では非推奨となり、それに代わるものはありません。 +.RE +.PP +\-XX:CMSIncrementalSafetyFactor=\fIpercent\fR +.RS 4 +デューティ・サイクルを計算する際に、保守を追加するために使用される時間の割合(0から100まで)を設定します。\fB\-XX:+CMSIncrementalMode\fRオプションの非推奨を受けて、このオプションはJDK 8では非推奨となり、それに代わるものはありません。 +.RE +.PP +\-XX:CMSInitiatingPermOccupancyFraction=\fIpercent\fR +.RS 4 +GCを開始する永久世代占有率(0から100まで)を設定します。このオプションはJDK 8では非推奨で、それに代わるものはありません。 +.RE +.PP +\-XX:MaxPermSize=\fIsize\fR +.RS 4 +永久世代領域の最大サイズ(バイト単位)を設定します。このオプションは、JDK 8で非推奨になり、\fB\-XX:MaxMetaspaceSize\fRオプションに取って代わられました。 +.RE +.PP +\-XX:PermSize=\fIsize\fR +.RS 4 +超えた場合にはガベージ・コレクションをトリガーする、永久世代に割り当てられた領域(バイト単位)を設定します。このオプションは、JDK 8で非推奨になり、\fB\-XX:MetaspaceSize\fRオプションに取って代わられました。 +.RE +.PP +\-XX:+UseSplitVerifier +.RS 4 +検証プロセスの分割を有効にします。デフォルトでは、このオプションは以前のリリースでは有効になっており、検証は、タイプ参照(コンパイラによって実行)と、タイプ・チェック(JVMランタイムによって実行)の2つのフェーズに分割されていました。このオプションはJDK 8で非推奨となり、検証はデフォルトで分割され、無効にする方法はありません。 +.RE +.PP +\-XX:+UseStringCache +.RS 4 +一般に割り当てられた文字列のキャッシングを有効にします。このオプションはJDK 8から削除され、それに代わるものはありません。 +.RE +.SH "パフォーマンス・チューニングの例" +.PP +次の例では、スループットの最適化またはレスポンス時間の短縮化のいずれかを行うための、試験的なチューニング・フラグの使用方法を示します。 +.PP +\fB例 1 \fRスループットを向上するためのチューニング +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-d64 \-server \-XX:+AggressiveOpts \-XX:+UseLargePages \-Xmn10g \-Xms26g \-Xmx26g\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fB例 2 \fRレスポンス時間を速くするためのチューニング +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-d64 \-XX:+UseG1GC \-Xms26g Xmx26g \-XX:MaxGCPauseMillis=500 \-XX:+PrintGCTimeStamp\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.SH "ラージ・ページ" +.PP +ヒュージ・ページとも呼ばれるラージ・ページは、標準のメモリー・ページ・サイズ(プロセッサおよびオペレーティング・システムによって異なります)よりはるかに大きいメモリー・ページです。ラージ・ページは、プロセッサのTranslation\-Lookaside Bufferを最適化します。 +.PP +Translation\-Lookaside Buffer (TLB)は、最近使用された仮想から物理へのアドレス変換を保持するページ変換キャッシュです。TLBは、少ないシステム・リソースです。プロセッサが複数のメモリー・アクセスが必要な場合のある階層ページ表から読み取る必要があるため、TLBミスは負荷がかかる可能性があります。大きいメモリー・ページ・サイズを使用して、単一のTLBエントリで大きいメモリー範囲を表すことができます。TLB不足が少なくなり、メモリー集約型のアプリケーションのパフォーマンスが向上する可能性があります。 +.PP +ただし、ラージ・ページのページ・メモリーは、システムのパフォーマンスに悪影響を与える場合があります。たとえば、大量のメモリーがアプリケーションで確保される場合、通常メモリー不足や他のアプリケーションの過剰なページングが発生し、システム全体が遅くなる可能性があります。また、長時間稼働しているシステムは、過剰な断片化が発生する可能性があります。これにより、十分な大きさのページ・メモリーを予約できない可能性があります。これが発生した場合、OSまたはJVMのいずれかが通常のページの使用に戻ります。 +.SS "ラージ・ページのサポート" +.PP +SolarisおよびLinuxは、ラージ・ページをサポートします。 +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBSolaris\fR +.RS 4 +.PP +Solaris 9以上には、Multiple Page Size Support(MPSS)が含まれています。追加の構成は必要ありません。http://www\&.oracle\&.com/technetwork/server\-storage/solaris10/overview/solaris9\-features\-scalability\-135663\&.htmlを参照してください。 +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBLinux\fR +.RS 4 +.PP +2\&.6カーネルは、ラージ・ページをサポートします。一部のベンダーは、2\&.4ベースのリリースのコードをバックポートしています。システムがラージ・ページ・メモリーをサポートしているかどうかを確認するには、次を試行してください: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB# cat /proc/meminfo | grep Huge\fR +\fBHugePages_Total: 0\fR +\fBHugePages_Free: 0\fR +\fBHugepagesize: 2048 kB\fR + +.fi +.if n \{\ +.RE +.\} +.PP +出力に3つの"Huge"変数が示されている場合、システムはラージ・ページ・メモリーをサポートしていますが、構成する必要があります。コマンドが何も出力しない場合、システムはラージ・ページをサポートしていません。ラージ・ページ・メモリーを使用するシステムを構成するには、\fBroot\fRとしてログインして、次の手順を実行してください: +.sp +.RS 4 +.ie n \{\ +\h'-04' 1.\h'+01'\c +.\} +.el \{\ +.sp -1 +.IP " 1." 4.2 +.\} +オプション\fB\-XX:+UseSHM\fR(\fB\-XX:+UseHugeTLBFS\fRのかわり)を使用する場合、\fBSHMMAX\fR値を増やしてください。Javaヒープ・サイズより大きくする必要があります。4GB以下の物理RAMを使用したシステムで、次によりすべてのメモリーが共有可能になります: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB# echo 4294967295 > /proc/sys/kernel/shmmax\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04' 2.\h'+01'\c +.\} +.el \{\ +.sp -1 +.IP " 2." 4.2 +.\} +オプション\fB\-XX:+UseSHM\fRまたは\fB\-XX:+UseHugeTLBFS\fRを使用する場合、ラージ・ページの数を指定してください。次の例では、4GBシステムの3GBがラージ・ページに予約されます(2048KBのラージ・ページ・サイズを仮定する場合、3GB = 3 * 1024MB = 3072MB = 3072 * 1024KB = 3145728KB and 3145728KB / 2048KB = 1536): +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB# echo 1536 > /proc/sys/vm/nr_hugepages\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fB注記\fR +.ps -1 +.br +.TS +allbox tab(:); +l. +T{ +注意 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +システムを再起動した後に\fB/proc\fRに含まれる値がリセットされるので注意してください。そのため、初期化スクリプト(\fBrc\&.local\fRや\fBsysctl\&.conf\fRなど)で設定できます。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +OSカーネル・パラメータ\fB/proc/sys/kernel/shmmax\fRまたは\fB/proc/sys/vm/nr_hugepages\fRを構成(またはサイズ変更)する場合、JavaプロセスがJavaヒープ以外の領域に対してラージ・ページを割り当てることがあります。これらの手順を使用して、次の領域に対してラージ・ページを割り当てることができます: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Javaヒープ +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Permanent世代 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +コード・キャッシュ +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +パラレルGCのマーキング・ビットマップ・データ構造 +.RE +.sp +その結果、Javaヒープのサイズに\fBnr_hugepages\fRパラメータを構成すると、領域のサイズが非常に大きいためにJVMがPermanent世代およびラージ・ページのコード・キャッシュ領域の割当てに失敗する場合があります。 +.RE +T} +.TE +.sp 1 +.sp .5v +.RE +.RE +.SH "終了ステータス" +.PP +通常、次の終了値が起動ツールから返されるのは、起動元が不正な引数で呼び出されたか、深刻なエラーが発生したか、あるいはJVMにより例外がスローされた場合です。ただし、Javaアプリケーションは、API呼出し\fBSystem\&.exit(exitValue)\fRを使用して任意の値を返すことを選択することもできます。値は次のとおりです。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB0\fR: 正常終了 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB>0\fR: エラー発生 +.RE +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +javac(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jdb(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +javah(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jar(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jstat(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/jjs.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/jjs.1 new file mode 100644 index 00000000..93d89f0a --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/jjs.1 @@ -0,0 +1,434 @@ +'\" t +.\" Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: jjs +.\" Language: Japanese +.\" Date: 2015年3月3日 +.\" SectDesc: 基本ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "jjs" "1" "2015年3月3日" "JDK 8" "基本ツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +jjs \- Nashornエンジンを呼び出します。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\fBjjs\fR\fR\fB [\fR\fB\fIoptions\fR\fR\fB] [\fR\fB\fIscript\-files\fR\fR\fB] [\-\- \fR\fB\fIarguments\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +空白文字で区切られた、\fBjjs\fRコマンドの1つ以上のオプション。詳細は、オプションを参照してください。 +.RE +.PP +\fIscript\-files\fR +.RS 4 +空白文字で区切られた、Nashornを使用して解釈する1つ以上のスクリプト・ファイル。ファイルが指定されない場合は、対話型シェルが起動されます。 +.RE +.PP +\fIarguments\fR +.RS 4 +二重ハイフン・マーカー(\fB\-\-\fR)の後のすべての値が、引数としてスクリプトまたは対話型シェルに渡されます。これらの値には\fBarguments\fRプロパティを使用してアクセスできます(Example 3を参照してください)。 +.RE +.SH "説明" +.PP +\fBjjs\fRコマンド行ツールを使用してNashornエンジンを呼び出します。これを使用して、1つまたは複数のスクリプト・ファイルを解釈したり、対話型シェルを実行することができます。 +.SH "オプション" +.PP +\fBjjs\fRコマンドのオプションはスクリプトがNashornによって解釈される条件を制御します。 +.PP +\-ccs=\fIsize\fR +.br +\-\-class\-cache\-size=\fIsize\fR +.RS 4 +クラス・キャッシュ・サイズをバイト単位で設定します。キロバイト(KB)を示すために\fBk\fRまたは\fBK\fRの文字を追加し、メガバイト(MB)を示すために\fBm\fRまたは\fBM\fRの文字を追加し、ギガバイト(GB)を示すために\fBg\fRまたは\fBG\fRを追加します。デフォルトでは、クラス・キャッシュ・サイズは50バイトに設定されます。次の例は、1024バイト(1 KB)に設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-css=100\fR +\fB\-css=1k\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-co +.br +\-\-compile\-only +.RS 4 +スクリプトを実行せずにコンパイルします。 +.RE +.PP +\-cp \fIpath\fR +.br +\-classpath \fIpath\fR +.RS 4 +サポートするクラスへのパスを指定します。複数のパスを設定するには、このオプションを繰り返すか、または各パスをコロン(:)で区切ります。 +.RE +.PP +\-D\fIname\fR=\fIvalue\fR +.RS 4 +プロパティ名に値を割り当てることで、スクリプトに渡すシステム・プロパティを設定します。次の例で、対話型モードでNashornを呼び出して、\fBmyValue\fRを\fBmyKey\fRという名前のプロパティに割り当てる方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB>> \fR\fB\fBjjs \-DmyKey=myValue\fR\fR +\fBjjs> \fR\fB\fBjava\&.lang\&.System\&.getProperty("myKey")\fR\fR +\fBmyValue\fR +\fBjjs>\fR + +.fi +.if n \{\ +.RE +.\} +このオプションを繰り返し使用すると、複数のプロパティを設定できます。 +.RE +.PP +\-d=\fIpath\fR +.br +\-\-dump\-debug\-dir=\fIpath\fR +.RS 4 +クラス・ファイルをダンプするディレクトリへのパスを指定します。 +.RE +.PP +\-\-debug\-lines +.RS 4 +クラス・ファイル内の行番号表を生成します。デフォルトでは、このオプションは有効になっています。無効にするには\fB\-\-debug\-lines=false\fRを指定します。 +.RE +.PP +\-\-debug\-locals +.RS 4 +クラス・ファイル内のローカル変数を生成します。 +.RE +.PP +\-doe +.br +\-\-dump\-on\-error +.RS 4 +エラーが発生したときに、フル・スタック・トレースを提供します。デフォルトでは、簡単なエラー・メッセージのみが出力されます。 +.RE +.PP +\-\-early\-lvalue\-error +.RS 4 +無効な左辺値式が早期エラーとして(つまり、コードが解析されるときに)報告されます。デフォルトでは、このオプションは有効になっています。無効にするには\fB\-\-early\-lvalue\-error=false\fRを指定します。無効な場合、無効な左辺値式はコードが実行されるまで報告されません。 +.RE +.PP +\-\-empty\-statements +.RS 4 +空の文をJavaの抽象構文ツリーに保存します。 +.RE +.PP +\-fv +.br +\-\-fullversion +.RS 4 +完全なNashornバージョン文字列を出力します。 +.RE +.PP +\-\-function\-statement\-error +.RS 4 +関数の宣言が文として使用されるとエラー・メッセージが出力されます。 +.RE +.PP +\-\-function\-statement\-warning +.RS 4 +関数の宣言が文として使用されると警告メッセージが出力されます。 +.RE +.PP +\-fx +.RS 4 +スクリプトをJavaFXアプリケーションとして起動します。 +.RE +.PP +\-h +.br +\-help +.RS 4 +オプションのリストとその説明を出力します。 +.RE +.PP +\-J\fIoption\fR +.RS 4 +指定した\fBjava\fR起動オプションをJVMに渡します。次の例で、対話型モードでNashornを呼び出して、JVMによって使用される最大メモリーを4 GBに設定する方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB>> \fR\fB\fBjjs \-J\-Xmx4g\fR\fR +\fBjjs> \fR\fB\fBjava\&.lang\&.Runtime\&.getRuntime()\&.maxMemory()\fR\fR +\fB3817799680\fR +\fBjjs>\fR + +.fi +.if n \{\ +.RE +.\} +このオプションを繰り返し使用すると、複数の\fBjava\fRコマンド・オプションを渡すことができます。 +.RE +.PP +\-\-language=[es5] +.RS 4 +ECMAScript言語バージョンを指定します。デフォルトのバージョンはES5です。 +.RE +.PP +\-\-lazy\-compilation +.RS 4 +レイジー・コード生成戦略(つまり、スクリプト全体が一度にコンパイルされない)を有効にします。このオプションは試験的なものです。 +.RE +.PP +\-\-loader\-per\-compile +.RS 4 +コンパイルごとに新しいクラス・ローダーを作成します。デフォルトでは、このオプションは有効になっています。無効にするには\fB\-\-loader\-per\-compile=false\fRを指定します。 +.RE +.PP +\-\-log=\fIsubsystem\fR:\fIlevel\fR +.RS 4 +指定されたサブシステムに対して、特定のレベルでロギングを実行します。カンマで区切って複数のサブシステムのロギング・レベルを指定できます。次に例を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-\-log=fields:finest,codegen:info\fR +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-\-optimistic\-types=[true|false] +.RS 4 +再コンパイルの最適化を解除してオプティミスティック・タイプ仮定を有効または無効にします。オプティミスティック・タイプを使用した実行によって最終的な速度が向上しますが、ウォームアップ時間が増える場合があります。 +.RE +.PP +\-\-package=\fIname\fR +.RS 4 +生成されたクラス・ファイルを追加するパッケージを指定します。 +.RE +.PP +\-\-parse\-only +.RS 4 +コンパイルせずにコードを解析します。 +.RE +.PP +\-\-print\-ast +.RS 4 +抽象構文ツリーを出力します。 +.RE +.PP +\-\-print\-code +.RS 4 +バイトコードを出力します。 +.RE +.PP +\-\-print\-lower\-ast +.RS 4 +掘り下げた抽象構文ツリーを出力します。 +.RE +.PP +\-\-print\-lower\-parse +.RS 4 +掘り下げた解析ツリーを出力します。 +.RE +.PP +\-\-print\-no\-newline +.RS 4 +その他の\fB\-\-print*\fRオプションで強制的に1行で出力します。 +.RE +.PP +\-\-print\-parse +.RS 4 +解析ツリーを出力します。 +.RE +.PP +\-\-print\-symbols +.RS 4 +記号表を出力します。 +.RE +.PP +\-pcs +.br +\-\-profile\-callsites +.RS 4 +呼び出しサイトのプロファイル・データをダンプします。 +.RE +.PP +\-scripting +.RS 4 +シェルのスクリプト機能を有効にします。 +.RE +.PP +\-\-stderr=\fIfilename\fR|\fIstream\fR|\fItty\fR +.RS 4 +標準エラー・ストリームを指定したファイル、ストリーム(たとえば\fBstdout\fR)に、またはテキスト端末にリダイレクトします。 +.RE +.PP +\-\-stdout=\fIfilename\fR|\fIstream\fR|\fItty\fR +.RS 4 +標準出力ストリームを指定したファイル、ストリーム(たとえば\fBstderr\fR)に、またはテキスト端末にリダイレクトします。 +.RE +.PP +\-strict +.RS 4 +標準(ECMAScript Edition 5\&.1)への準拠を強化するstrictモードを有効にし、これにより共通のコーディング・エラーを簡単に検出できるようになります。 +.RE +.PP +\-t=\fIzone\fR +.br +\-timezone=\fIzone\fR +.RS 4 +スクリプトの実行に対し指定したタイムゾーンを設定します。OSで設定されたタイムゾーンをオーバーライドし、\fBDate\fRオブジェクトで使用されます。 +.RE +.PP +\-tcs=\fIparameter\fR +.br +\-\-trace\-callsites=\fIparameter\fR +.RS 4 +呼出しサイトのトレースのモードを有効にします。使用可能なパラメータは、次のとおりです。 +.PP +miss +.RS 4 +呼出しサイトのミスをトレースします。 +.RE +.PP +enterexit +.RS 4 +呼出しサイトへの出入りをトレースします。 +.RE +.PP +objects +.RS 4 +オブジェクトのプロパティを出力します。 +.RE +.RE +.PP +\-\-verify\-code +.RS 4 +バイトコードを実行する前に検証します。 +.RE +.PP +\-v +.br +\-version +.RS 4 +Nashornバージョン文字列を出力します。 +.RE +.PP +\-xhelp +.RS 4 +コマンド行オプションの拡張ヘルプを出力します。 +.RE +.SH "例" +.PP +\fB例 1 \fRNashornを使用したスクリプトの実行 +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjjs script\&.js\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fB例 2 \fR対話型モードでのNashornの実行 +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB>> \fR\fB\fBjjs\fR\fR +\fBjjs> \fR\fB\fBprintln("Hello, World!")\fR\fR +\fBHello, World!\fR +\fBjjs> \fR\fB\fBquit()\fR\fR +\fB>>\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fB例 3 \fRNashornへの引数の渡し +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB>> \fR\fB\fBjjs \-\- a b c\fR\fR +\fBjjs> \fR\fB\fBarguments\&.join(", ")\fR\fR +\fBa, b, c\fR +\fBjjs>\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.SH "関連項目" +.PP +\fBjrunscript\fR +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/keytool.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/keytool.1 new file mode 100644 index 00000000..7935009f --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/keytool.1 @@ -0,0 +1,2248 @@ +'\" t +.\" Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: keytool +.\" Language: Japanese +.\" Date: 2015年3月3日 +.\" SectDesc: セキュリティ・ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "keytool" "1" "2015年3月3日" "JDK 8" "セキュリティ・ツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +keytool \- 暗号化鍵、X\&.509証明書チェーンおよび信頼できる証明書を含むキーストア(データベース)を管理します。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool\fR [\fIcommands\fR] +.fi +.if n \{\ +.RE +.\} +.PP +\fIcommands\fR +.RS 4 +コマンドを参照してください。これらのコマンドは、次のようにタスク別に分類されます。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +キーストアへのデータの作成または追加 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-gencert +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-genkeypair +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-genseckey +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-importcert +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-importpassword +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +別のキーストアの内容のインポート +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-importkeystore +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +証明書リクエストの生成 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-certreq +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +データのエクスポート +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-exportcert +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +データの表示 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-list +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-printcert +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-printcertreq +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-printcrl +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +キーストアの管理 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-storepasswd +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-keypasswd +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-delete +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-changealias +.RE +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +ヘルプの表示 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-help +.RE +.RE +.RE +.SH "説明" +.PP +\fBkeytool\fRコマンドは、鍵と証明書を管理するためのユーティリティです。これにより、ユーザーは自分の公開鍵と秘密鍵のペアおよび関連する証明書を管理し、デジタル署名を使用した自己認証(他のユーザーまたはサービスに対して自分自身を認証すること)や、データの整合性と証明書に関するサービスを利用することができます。\fBkeytool\fRコマンドでは、通信しているピアの公開鍵をキャッシュすることもできます(証明書のフォームで)。 +.PP +証明書とは、あるエンティティ(人物、会社など)からのデジタル署名付きの文書のことです。証明書には、他のあるエンティティの公開鍵(およびその他の情報)が特別な値を持っていることが書かれています。(証明書を参照してください。)データにデジタル署名が付いている場合は、デジタル署名を検証することで、データの整合性およびデータが本物であることをチェックできます。データの整合性とは、データが変更されたり、改変されたりしていないことを意味します。また、データが本物であるとは、そのデータが、データを作成して署名したと称する人物から渡されたデータであることを意味します。 +.PP +また、\fBkeytool\fRコマンドを使用すれば、対称暗号化/復号化(DES)で使用される秘密鍵およびパスフレーズを管理することもできます。 +.PP +\fBkeytool\fRコマンドは、鍵と証明書をキーストアに格納します。キーストアの別名を参照してください。 +.SH "コマンドとオプションに関する注意" +.PP +様々なコマンドとその説明については、コマンドを参照してください。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +どのコマンド名およびオプション名にも先頭にマイナス記号(\-)が付きます。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +各コマンドのオプションは任意の順序で指定できます。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +イタリックになっていないすべての項目、または中カッコか角カッコで囲まれているすべての項目は、そのとおりに指定する必要があります。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +オプションを囲む中カッコは、そのオプションをコマンド行で指定しなかった場合に、デフォルト値が使用されることを意味します。オプションのデフォルト値を参照してください。中カッコは、\fB\-v\fR、\fB\-rfc\fRおよび\fB\-J\fRオプションを囲むためにも使用されますが、これらのオプションはコマンド行で指定された場合にのみ意味を持ちます。指定されていない場合以外、デフォルト値はありません。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +オプションを囲む角カッコは、そのオプションをコマンド行で指定しなかった場合に、値の入力を求められることを意味します。\fB\-keypass\fRオプションの場合、オプションをコマンド行で指定しなかった場合は、\fBkeytool\fRコマンドがまずキーストアのパスワードを使用して非公開/秘密鍵の復元を試みます。この試みが失敗した場合、\fBkeytool\fRコマンドにより、非公開/秘密鍵のパスワードの入力を求められます。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +イタリックの項目の実際の値(オプションの値)は、指定する必要があります。たとえば、\fB\-printcert\fRコマンドの形式は次のとおりです。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-printcert {\-file \fR\fB\fIcert_file\fR\fR\fB} {\-v}\fR +.fi +.if n \{\ +.RE +.\} +.sp +\fB\-printcert\fRコマンドを指定する場合は、\fIcert_file\fRを実際のファイル名で置き換えます。例: +\fBkeytool \-printcert \-file VScert\&.cer\fR +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +オプションの値に空白(スペース)が含まれている場合は、値を引用符で囲む必要があります。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB\-help\fRオプションがデフォルトです。\fBkeytool\fRコマンドは、\fBkeytool \-help\fRと同じです。 +.RE +.SH "オプションのデフォルト値" +.PP +次の例で、様々なオプション値のデフォルト値を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-alias "mykey"\fR +\fB \fR +\fB\-keyalg\fR +\fB "DSA" (when using \-genkeypair)\fR +\fB "DES" (when using \-genseckey)\fR +\fB \fR +\fB\-keysize\fR +\fB 2048 (when using \-genkeypair and \-keyalg is "RSA")\fR +\fB 1024 (when using \-genkeypair and \-keyalg is "DSA")\fR +\fB 256 (when using \-genkeypair and \-keyalg is "EC")\fR +\fB 56 (when using \-genseckey and \-keyalg is "DES")\fR +\fB 168 (when using \-genseckey and \-keyalg is "DESede")\fR +\fB \fR +\fB\-validity 90\fR +\fB \fR +\fB\-keystore \fR +\fB \fR +\fB\-storetype \fR +\fB \fR +\fB\-file\fR +\fB stdin (if reading)\fR +\fB stdout (if writing)\fR +\fB \fR +\fB\-protected false\fR + +.fi +.if n \{\ +.RE +.\} +.PP +公開/秘密鍵ペアの生成において、署名アルゴリズム(\fB\-sigalg\fRオプション)は、基になる秘密鍵のアルゴリズムから派生します。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +基になる秘密鍵がDSAタイプである場合は、\fB\-sigalg\fRオプションのデフォルト値はSHA1withDSAになります。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +基になる秘密鍵がRSAタイプである場合は、\fB\-sigalg\fRオプションのデフォルト値はSHA256withRSAになります。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +基になる秘密鍵がECタイプである場合は、\fB\-sigalg\fRオプションのデフォルト値はSHA256withECDSAになります。 +.RE +.PP +\fB\-keyalg\fRおよび\fB\-sigalg\fR引数の完全なリストについては、 +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec\&.html#AppAの「Java Cryptography Architecture (JCA) Reference Guide」を参照してください。 +.SH "一般オプション" +.PP +\fB\-v\fRオプションは、\fB\-help\fRコマンドを除くすべてのコマンドで使用できます。\fB\-v\fRオプションを指定した場合、コマンドは冗長モードで実行され、詳細な情報が出力されます。 +.PP +任意のコマンドで指定できる\fB\-Jjavaoption\fR引数もあります。\fB\-Jjavaoption\fRを指定した場合、指定された\fBjavaoption\fR文字列がJavaインタプリタに直接渡されます。このオプションには、空白を含めることはできません。このオプションは、実行環境またはメモリー使用を調整する場合に便利です。指定できるインタプリタ・オプションを一覧表示するには、コマンド行で\fBjava \-h\fRまたは\fBjava \-X\fRと入力してください。 +.PP +次のオプションは、キーストアに対する操作を行うすべてのコマンドで指定できます。 +.PP +\-storetype \fIstoretype\fR +.RS 4 +この修飾子は、インスタンスを生成するキーストアのタイプを指定します。 +.RE +.PP +\-keystore \fIkeystore\fR +.RS 4 +キーストアの場所を指定します。 +.sp +特定の\fBkeytool\fRコマンドを実行する際に、JKS +\fBstoretype\fRが使用され、かつキーストア・ファイルがまだ存在していなかった場合、新しいキーストア・ファイルが作成されます。たとえば、\fBkeytool \-genkeypair\fRの呼出し時に\fB\-keystore\fRオプションが指定されなかった場合、\fB\&.keystore\fRという名前のデフォルト・キーストア・ファイルがユーザーのホーム・ディレクトリ内にまだ存在していなければ、そこに作成されます。同様に、\fB\-keystore ks_file\fRというオプションが指定されてもそのks_fileが存在しなかった場合、そのファイルが作成されます。JKS +\fBstoretype\fRの詳細は、\fIの\fRKeyStoreの実装キーストアの別名に関する項を参照してください。 +.sp +\fB\-keystore\fRオプションからの入力ストリームは、\fBKeyStore\&.load\fRメソッドに渡されます。URLとして\fBNONE\fRが指定されている場合は、nullのストリームが\fBKeyStore\&.load\fRメソッドに渡されます。\fBNONE\fRは、KeyStoreがファイルベースではない場合に指定してください。たとえば、ハードウェア・トークン・デバイス上に存在している場合などです。 +.RE +.PP +\-storepass[:\fIenv\fR| :\fIfile\fR] argument +.RS 4 +キーストアの整合性を保護するために使用するパスワードを指定します。 +.sp +修飾子\fBenv\fRまたは\fBfile\fRを指定しない場合、パスワードの値は\fBargument\fRになります。この値は、6文字以上にする必要があります。それ以外の場合、パスワードは次のようにして取得されます。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBenv\fR: +\fBargument\fRという名前の環境変数からパスワードを取得します。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBfile\fR: argumentという名前のファイルからパスワードを取得します。 +.RE +.sp +\fB注意:\fR +\fB\-keypass\fR、\fB\-srckeypass\fR、\-\fBdestkeypass\fR、\fB\-srcstorepass\fR、\fB\-deststorepass\fRなどのパスワードを必要とするその他のオプションはすべて、\fIenv\fRおよび\fIfile\fR修飾子を受け付けます。パスワード・オプションと修飾子は、必ずコロン(:)で区切ってください。 +.sp +パスワードは、キーストアの内容にアクセスするすべてのコマンドで使用されます。この種のコマンドを実行するときに、コマンド行で\fB\-storepass\fRオプションを指定しなかった場合は、パスワードの入力を求められます。 +.sp +キーストアから情報を取得する場合、パスワードは省略可能です。パスワードが指定されていない場合は、取得した情報の整合性を検証できず、警告が表示されます。 +.RE +.PP +\-providerName \fIprovider_name\fR +.RS 4 +セキュリティ・プロパティ・ファイル内に含まれる暗号化サービス・プロバイダ名を特定するために使用されます。 +.RE +.PP +\-providerClass \fIprovider_class_name\fR +.RS 4 +暗号化サービス・プロバイダがセキュリティ・プロパティ・ファイルに指定されていないときは、そのマスター・クラス・ファイルの名前を指定するときに使用されます。 +.RE +.PP +\-providerArg \fIprovider_arg\fR +.RS 4 +\fB\-providerClass\fRオプションとともに使用され、\fBprovider_class_name\fRのコンストラクタのオプションの文字列入力引数を表します。 +.RE +.PP +\-protected +.RS 4 +\fBtrue\fRまたは\fBfalse\fRのいずれか。パスワードを専用PINリーダーなどの保護された認証パス経由で指定する必要がある場合は、この値を\fBtrue\fRに指定する必要があります。\fB\-importkeystore\fRコマンドには2つのキーストアが関連しているため、ソース・キーストアと宛先キーストアにそれぞれ次の2つのオプション、\fB\-srcprotected\fRと\-\fBdestprotected\fRが用意されています。 +.RE +.PP +\-ext \fI{name{:critical} {=value}}\fR +.RS 4 +X\&.509証明書エクステンションを示します。このオプションを\fB\-genkeypair\fRおよび\fB\-gencert\fRで使用して、生成される証明書または\fB\-certreq\fRにエクステンションを埋め込み、証明書リクエストでリクエストされるエクステンションを示すことができます。このオプションは複数回指定できます。\fBname\fR引数には、サポートされているエクステンション名(名前付きエクステンションを参照)または任意のOID番号を指定できます。指定されている場合、\fBvalue\fR引数はエクステンションの引数を指します。\fIvalue\fRを省略した場合、エクステンションのデフォルト値またはエクステンションが引数を必要としないことを示します。\fB:critical\fR修飾子が指定された場合、エクステンションの\fBisCritical\fR属性は\fBtrue\fRで、指定されない場合は\fBfalse\fRです。\fB:critical\fRのかわりに\fB:c\fRを使用できます。 +.RE +.SH "名前付きエクステンション" +.PP +\fBkeytool\fRコマンドは、次の名前のエクステンションをサポートしています。名前の大/小文字は区別されません。 +.PP +BCまたはBasicContraints +.RS 4 +\fB値\fR: 完全な形式は、\fBca:{true|false}[,pathlen:]\fRまたは\fB\fR(\fBca:true,pathlen:\fRの短縮形)です。<\fBlen\fR>を省略すると、\fBca:true\fRの意味になります。 +.RE +.PP +KUまたはKeyUsage +.RS 4 +\fB値\fR: +\fBusage\fR(\fBusage\fR)*、\fIusage\fRには\fBdigitalSignature\fR、\fBnonRepudiation\fR +(contentCommitment)、\fBkeyEncipherment\fR、\fBdataEncipherment\fR、\fBkeyAgreement\fR、\fBkeyCertSign\fR、\fBcRLSign\fR、\fBencipherOnly\fR、\fBdecipherOnly\fRのいずれかを指定できます。\fIusage\fR引数は、不明確にならないかぎり、最初の数文字(\fBdigitalSignature\fRの場合は\fBdig\fR)またはキャメルケース・スタイル(\fBdigitalSignature\fRの場合は\fBdS\fR、\fBcRLSign\fRの場合は\fBcRLS\fR)で省略表記できます。\fBusage\fR値は、大文字と小文字が区別されます。 +.RE +.PP +EKUまたはExtendedKeyUsage +.RS 4 +\fB値\fR: +\fBusage\fR(\fBusage\fR)*、\fIusage\fRには\fBanyExtendedKeyUsage\fR、\fBserverAuth\fR、\fBclientAuth\fR、\fBcodeSigning\fR、\fBemailProtection\fR、\fBtimeStamping\fR、\fBOCSPSigning\fRまたは任意の\fIOID文字列\fRのいずれかを指定できます。\fIusage\fR引数は、不明確にならないかぎり、最初の数文字またはキャメルケース・スタイルで省略表記できます。\fBusage\fR値は、大文字と小文字が区別されます。 +.RE +.PP +SANまたはSubjectAlternativeName +.RS 4 +\fB値\fR: +\fBtype\fR:\fBvalue\fR(t\fBype:value\fR)*、\fBtype\fRには\fBEMAIL\fR、\fBURI\fR、\fBDNS\fR、\fBIP\fRまたは\fBOID\fRを指定できます。\fBvalue\fR引数は、\fBtype\fRの文字列形式の値です。 +.RE +.PP +IANまたはIssuerAlternativeName +.RS 4 +\fB値\fR: +\fBSubjectAlternativeName\fRと同じです。 +.RE +.PP +SIAまたはSubjectInfoAccess +.RS 4 +\fB値\fR: +\fBmethod\fR:\fBlocation\-type\fR:\fBlocation\-value\fR +(\fBmethod:location\-type\fR:\fBlocation\-value\fR)*、\fBmethod\fRには\fBtimeStamping\fR、\fBcaRepository\fRまたは任意のOIDを指定できます。\fBlocation\-type\fRおよび\fBlocation\-value\fR引数には、\fBSubjectAlternativeName\fRエクステンションでサポートされる任意の\fBtype\fR:\fBvalue\fRを指定できます。 +.RE +.PP +AIAまたはAuthorityInfoAccess +.RS 4 +\fB値\fR: +\fBSubjectInfoAccess\fRと同じです。\fBmethod\fR引数には、\fBocsp\fR、\fBcaIssuers\fRまたは任意のOIDを指定できます。 +.RE +.PP +\fBname\fRがOIDの場合、OCTET STRINGタイプと長さのバイトを除外したエクステンションについては、値は\fBextnValue\fRの16進ダンプのDERエンコーディングです。HEX文字列では、標準の16進数(0\-9、a\-f、A\-F)以外の文字は無視されます。したがって、01:02:03:04と01020304の両方とも同一の値として受け付けられます。値がない場合、エクステンションの値フィールドは空になります。 +.PP +\fB\-gencert\fRでのみ使用する\fBhonored\fRという特別な名前は、証明書リクエストに含まれるエクステンションを優先する方法を示します。この名前の値は、\fBall\fR(リクエストされるすべてのエクステンションが優先される)、\fBname{:[critical|non\-critical]}\fR(名前付きのエクステンションが優先されるが、別の\fBisCritical\fR属性を使用する)、および\fB\-name\fR(\fBall\fRとともに使用し、例外を示す)のカンマ区切りリストです。デフォルトでは、リクエストされるエクステンションは優先されません。 +.PP +\fB\-ext honored\fRオプションに加え、別の名前の、またはOID +\fB\-ext\fRのオプションを指定した場合は、このエクステンションが、すでに優先されているエクステンションに追加されます。ただし、この名前(またはOID)を優先される値でも使用した場合は、その値と重要性がリクエストに含まれるものをオーバーライドします。 +.PP +\fBsubjectKeyIdentifier\fRエクステンションは常に作成されます。自己署名でない証明書の場合は、\fBauthorityKeyIdentifier\fRが作成されます。 +.PP +\fB注意:\fR +ユーザーは、エクステンション(および証明書の他のフィールド)の組合せによっては、インターネットの標準に準拠しない場合があることに注意してください。証明書の準拠に関する警告を参照してください。 +.SH "コマンド" +.PP +\-gencert +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-rfc} {\-infile \fR\fB\fIinfile\fR\fR\fB} {\-outfile \fR\fB\fIoutfile\fR\fR\fB} {\-alias \fR\fB\fIalias\fR\fR\fB} {\-sigalg \fR\fB\fIsigalg\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-dname \fR\fB\fIdname\fR\fR\fB} {\-startdate \fR\fB\fIstartdate\fR\fR\fB {\-ext \fR\fB\fIext\fR\fR\fB}* {\-validity \fR\fB\fIvalDays\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-keypass \fR\fB\fIkeypass\fR\fR\fB] {\-keystore \fR\fB\fIkeystore\fR\fR\fB} [\-storepass \fR\fB\fIstorepass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-providername \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +証明書リクエスト・ファイル(\fBkeytool\fR +\fB\-certreq\fRコマンドで作成可能)に対するレスポンスとして証明書を生成します。このコマンドは、\fIinfile\fRから(省略した場合は、標準入力から)リクエストを読み込み、別名の秘密鍵を使用してそのリクエストに署名して、X\&.509証明書を\fIoutfile\fRに(省略した場合は、標準出力に)出力します。\fB\-rfc\fRを指定した場合、出力形式はBASE64符号化のPEMになります。それ以外の場合は、バイナリDERが作成されます。 +.sp +\fBsigalg\fR値には、証明書に署名を付けるときに使用するアルゴリズムを指定します。\fBstartdate\fR引数は、証明書の有効開始日時です。\fBvalDays\fR引数は、証明書の有効日数を示します。 +.sp +\fBdname\fRを指定すると、生成される証明書の主体として使用されます。それ以外の場合は、証明書リクエストからの名前が使用されます。 +.sp +\fBext\fR値は、証明書に埋め込まれるX\&.509エクステンションを示します。\fB\-ext\fRの構文については、一般オプションを参照してください。 +.sp +\fB\-gencert\fRオプションを使用すると、証明書チェーンを作成できます。次の例では、\fBe1\fRという証明書を作成します。この証明書の証明書チェーンには、3つの証明書が含まれています。 +.sp +次のコマンドは、\fBca\fR、\fBca1\fR、\fBca2\fRおよび\fBe1\fRの4つの鍵ペアを作成します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-alias ca \-dname CN=CA \-genkeypair\fR +\fBkeytool \-alias ca1 \-dname CN=CA \-genkeypair\fR +\fBkeytool \-alias ca2 \-dname CN=CA \-genkeypair\fR +\fBkeytool \-alias e1 \-dname CN=E1 \-genkeypair\fR + +.fi +.if n \{\ +.RE +.\} +次の2つのコマンドは、署名付き証明書のチェーンを作成します。\fBca\fRは\fBca1\fRに署名し、\fBca1\fRは\fBca2\fRに署名します。すべて自己発行です。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-alias ca1 \-certreq |\fR +\fB keytool \-alias ca \-gencert \-ext san=dns:ca1 |\fR +\fB keytool \-alias ca1 \-importcert\fR + +\fBkeytool \-alias ca2 \-certreq |\fR +\fB $KT \-alias ca1 \-gencert \-ext san=dns:ca2 |\fR +\fB $KT \-alias ca2 \-importcert\fR + +.fi +.if n \{\ +.RE +.\} +次のコマンドは、証明書\fBe1\fRを作成してファイル\fBe1\&.cert\fRに格納します。この証明書は\fBca2\fRによって署名されます。その結果、\fBe1\fRの証明書チェーンには\fBca\fR、\fBca1\fRおよび\fBca2\fRが含まれることになります。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-alias e1 \-certreq | keytool \-alias ca2 \-gencert > e1\&.cert\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-genkeypair +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} {\-keyalg \fR\fB\fIkeyalg\fR\fR\fB} {\-keysize \fR\fB\fIkeysize\fR\fR\fB} {\-sigalg \fR\fB\fIsigalg\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-dname \fR\fB\fIdname\fR\fR\fB] [\-keypass \fR\fB\fIkeypass\fR\fR\fB] {\-startdate \fR\fB\fIvalue\fR\fR\fB} {\-ext \fR\fB\fIext\fR\fR\fB}*\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-validity \fR\fB\fIvalDays\fR\fR\fB} {\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-storepass \fR\fB\fIstorepass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +鍵のペア(公開鍵および関連する秘密鍵)を生成します。公開鍵はX\&.509 v3自己署名証明書でラップされます。証明書は、単一の要素を持つ証明書チェーンとして格納されます。この証明書チェーンと秘密鍵は、aliasで特定される新しいキーストア・エントリに格納されます。 +.sp +\fBkeyalg\fR値は鍵ペアの生成に使用するアルゴリズムを、\fBkeysize\fR値は生成する各鍵のサイズを、それぞれ指定します。\fBsigalg\fR値は、自己署名証明書に署名を付けるために使用するアルゴリズムを指定します。このアルゴリズムは\fBkeyalg\fR値と互換性がある必要があります。 +.sp +\fBdname\fR値には、\fBalias\fR値に関連付け、自己署名証明書のissuerフィールドとsubjectフィールドとして使用するX\&.500識別名を指定します。コマンド行で識別名を指定しなかった場合は、識別名の入力を求められます。 +.sp +\fBkeypass\fR値には、生成される鍵のペアのうち、秘密鍵を保護するのに使用するパスワードを指定します。パスワードを指定しなかった場合は、パスワードの入力を求められます。このとき、[Return]キーを押すと、キーストアのパスワードと同じパスワードが鍵のパスワードに設定されます。\fBkeypass\fR値は、6文字以上にする必要があります。 +.sp +\fBstartdate\fR値には、証明書の発行時刻を指定します。これは、X\&.509証明書の「Validity」フィールドの「Not Before」値とも呼ばれます。 +.sp +オプションの値は、次の2つの形式のいずれかで設定できます。 +.sp +\fB([+\-]nnn[ymdHMS])+\fR +.sp +\fB[yyyy/mm/dd] [HH:MM:SS]\fR +.sp +最初の形式では、発行時刻は、指定される値の分、現在の時刻から移ります。指定される値は、一連の下位の値を連結したものになります。下位の各値で、プラス記号(「+」)は時間が進むことを、マイナス記号(「\-」)は時間が戻ることを意味しています。移る時間は\fBnnn\fRで、単位は年、月、日、時間、分または秒です(それぞれ、1文字の\fBy\fR、\fBm\fR、\fBd\fR、\fBH\fR、\fBM\fRまたは\fBS\fR」で示されています)。下位の各値で\fBjava\&.util\&.GregorianCalendar\&.add(int field, int amount)\fRメソッドを使用することで、発行時刻の追加の値が左から右へ計算されます。たとえば、指定すると、発行時刻は次のようになります。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCalendar c = new GregorianCalendar();\fR +\fBc\&.add(Calendar\&.YEAR, \-1);\fR +\fBc\&.add(Calendar\&.MONTH, 1);\fR +\fBc\&.add(Calendar\&.DATE, \-1);\fR +\fBreturn c\&.getTime()\fR + +.fi +.if n \{\ +.RE +.\} +2番目の形式では、ユーザーは、年/月/日と時間:分:秒の2つの部分で厳密な開始時刻を設定します(地元の時間帯を使用)。ユーザーは、1つの部分のみを指定できます。これは、もう1つの部分は現在の日付(または時刻)と同じになるということです。ユーザーは、形式の定義に示されているように、桁数を厳密に指定する必要があります(短い場合は0で埋めます)。日付と時刻の両方が指定された状態で、2つの部分の間に空白文字が1つ(1つのみ)あります。時間は常に24時間形式で指定してください。 +.sp +オプションを指定しないと、開始日付は現在の時刻になります。オプションは、最大で1回指定できます。 +.sp +\fBvalDays\fRの値には、証明書の有効日数を指定します(\fB\-startdate\fRで指定された日付、または\fB\-startdate\fRが指定されていない場合は現在の日付から始まります)。 +.sp +このコマンドは、以前のリリースでは\fB\-genkey\fRという名前でした。このリリースでは、引き続き古い名前がサポートされています。今後は、新しい名前\fB\-genkeypair\fRが優先されます。 +.RE +.PP +\-genseckey +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} {\-keyalg \fR\fB\fIkeyalg\fR\fR\fB} {\-keysize \fR\fB\fIkeysize\fR\fR\fB} [\-keypass \fR\fB\fIkeypass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB} [\-storepass \fR\fB\fIstorepass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}} {\-v}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +秘密鍵を生成し、それを新しい\fBKeyStore\&.SecretKeyEntry\fR(\fBalias\fRで特定される)内に格納します。 +.sp +\fBkeyalg\fR値は鍵ペアの生成に使用するアルゴリズムを、\fBkeysize\fR値は生成する各鍵のサイズを、それぞれ指定します。\fBkeypass\fR値は、秘密鍵を保護するパスワードです。パスワードを指定しなかった場合は、パスワードの入力を求められます。このとき、[Return]キーを押すと、\fBkeystore\fRのパスワードと同じパスワードが鍵のパスワードに設定されます。\fBkeypass\fR値は、6文字以上にする必要があります。 +.RE +.PP +\-importcert +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} {\-file \fR\fB\fIcert_file\fR\fR\fB} [\-keypass \fR\fB\fIkeypass\fR\fR\fB] {\-noprompt} {\-trustcacerts}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB} [\-storepass \fR\fB\fIstorepass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerName \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +ファイル\fBcert_file\fRから証明書または証明書チェーン(証明書チェーンの場合は、PKCS#7形式の応答または一連のX\&.509証明書で提供されるもの)を読み込み、\fBalias\fRによって特定される\fBkeystore\fRエントリに格納します。ファイルが指定されていない場合は、\fBstdin\fRから証明書または証明書チェーンを読み込みます。 +.sp +\fBkeytool\fRコマンドでは、X\&.509 v1、v2、v3の証明書、およびPKCS#7形式の証明書から構成されているPKCS#7形式の証明書チェーンをインポートできます。インポートするデータは、バイナリ符号化方式、または出力可能符号化方式(Base64符号化とも呼ばれる)のどちらかで提供する必要があります。出力可能符号化方式は、インターネットRFC 1421証明書符号化規格で定義されています。この符号化方式の場合、証明書は\fB\-\fR\fB\-\-\-\-BEGIN\fRで始まる文字列で開始され、\fB\-\-\-\-\-END\fRで始まる文字列で終了する必要があります。 +.sp +証明書は、信頼できる証明書のリストに追加するため、および認証局(CA)に証明書署名リクエストを送信した結果としてCAから受信した証明書応答をインポートするため(\fBの\fR\-certreqコマンドオプションを参照)という2つの理由でインポートします。 +.sp +どちらのタイプのインポートを行うかは、\fB\-alias\fRオプションの値によって指定します。別名がキー・エントリをポイントしない場合、\fBkeytool\fRコマンドはユーザーが信頼できる証明書エントリを追加しようとしているものとみなします。この場合、別名がキーストア内に存在していないことが必要です。別名がすでに存在している場合、その別名の信頼できる証明書がすでに存在することになるので、\fBkeytool\fRコマンドはエラーを出力し、証明書のインポートを行いません。別名がキー・エントリをポイントする場合、\fBkeytool\fRコマンドはユーザーが証明書応答をインポートしようとしているものとみなします。 +.RE +.PP +\-importpassword +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} [\-keypass \fR\fB\fIkeypass\fR\fR\fB] {\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-storepass \fR\fB\fIstorepass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +パスフレーズをインポートし、\fBalias\fRで識別される新規\fBKeyStore\&.SecretKeyEntry\fRに格納します。パスフレーズは、標準入力ストリームを介して提供できます。または、ユーザーにそのプロンプトが表示されます。\fBkeypass\fRは、インポートされるパスフレーズの保護に使用されるパスワードです。パスワードを指定しなかった場合は、パスワードの入力を求められます。このとき、[Return]キーを押すと、\fBkeystore\fRのパスワードと同じパスワードが鍵のパスワードに設定されます。\fBkeypass\fRは、6文字以上にする必要があります。 +.RE +.PP +\-importkeystore +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-srcstoretype \fR\fB\fIsrcstoretype\fR\fR\fB} {\-deststoretype \fR\fB\fIdeststoretype\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-srcstorepass \fR\fB\fIsrcstorepass\fR\fR\fB] [\-deststorepass \fR\fB\fIdeststorepass\fR\fR\fB] {\-srcprotected}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-destprotected} \fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-srcalias \fR\fB\fIsrcalias\fR\fR\fB {\-destalias \fR\fB\fIdestalias\fR\fR\fB} [\-srckeypass \fR\fB\fIsrckeypass\fR\fR\fB]} \fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-destkeypass \fR\fB\fIdestkeypass\fR\fR\fB] {\-noprompt}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-srcProviderName \fR\fB\fIsrc_provider_name\fR\fR\fB} {\-destProviderName \fR\fB\fIdest_provider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}} {\-v}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +ソース・キーストアからターゲット・キーストアへ、単一のエントリまたはすべてのエントリをインポートします。 +.sp +\fB\-srcalias\fRオプションが指定された場合、このコマンドは、その別名で特定される単一のエントリをターゲット・キーストアにインポートします。\fBdestalias\fR経由でターゲット別名が指定されなかった場合、\fBsrcalias\fRがターゲット別名として使用されます。ソースのエントリがパスワードで保護されていた場合、\fBsrckeypass\fRを使用してそのエントリが回復されます。\fIsrckeypass\fRが指定されなかった場合、\fBkeytool\fRコマンドは\fBsrcstorepass\fRを使用してそのエントリを回復しようとします。\fBsrcstorepass\fRが指定されなかったか正しくなかった場合、ユーザーはパスワードの入力を求められます。ターゲットのエントリは\fBdestkeypass\fRによって保護されます。\fBdestkeypass\fRが指定されなかった場合、ターゲット・エントリはソース・エントリのパスワードによって保護されます。たとえば、ほとんどのサード・パーティ・ツールでは、PKCS #12キーストアで\fBstorepass\fRと\fBkeypass\fRが同じである必要があります。これらのツールのPKCS #12キーストアを作成する場合は、常に\fB\-destkeypass\fRと\fB\-deststorepass\fRが同じになるように指定します。 +.sp +\fB\-srcalias\fRオプションが指定されなかった場合、ソース・キーストア内のすべてのエントリがターゲット・キーストア内にインポートされます。各ターゲット・エントリは対応するソース・エントリの別名の下に格納されます。ソースのエントリがパスワードで保護されていた場合、\fBsrcstorepass\fRを使用してそのエントリが回復されます。\fBsrcstorepass\fRが指定されなかったか正しくなかった場合、ユーザーはパスワードの入力を求められます。ソース・キーストア内のあるエントリ・タイプがターゲット・キーストアでサポートされていない場合や、あるエントリをターゲット・キーストアに格納する際にエラーが発生した場合、ユーザーはそのエントリをスキップして処理を続行するか、または中止するかの選択を求められます。ターゲット・エントリはソース・エントリのパスワードによって保護されます。 +.sp +ターゲット別名がターゲット・キーストア内にすでに存在していた場合、ユーザーは、そのエントリを上書きするか、あるいは異なる別名の下で新しいエントリを作成するかの選択を求められます。 +.sp + +\fB\-noprompt\fRオプションを指定した場合、ユーザーは新しいターゲット別名の入力を求められません。既存のエントリがそのターゲット別名で上書きされます。インポートできないエントリはスキップされ、警告が出力されます。 +.RE +.PP +\-printcertreq +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-file \fR\fB\fIfile\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +PKCS#10形式の証明書リクエストの内容を出力します。このリクエストは、\fBkeytool\fR +\fB\-certreq\fRコマンドで生成できます。このコマンドは、ファイルからリクエストを読み取ります。ファイルが存在しない場合、リクエストは標準入力から読み取られます。 +.RE +.PP +\-certreq +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} {\-dname \fR\fB\fIdname\fR\fR\fB} {\-sigalg \fR\fB\fIsigalg\fR\fR\fB} {\-file \fR\fB\fIcertreq_file\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-keypass \fR\fB\fIkeypass\fR\fR\fB] {\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-storepass \fR\fB\fIstorepass\fR\fR\fB] {\-providerName \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +PKCS#10形式を使用して証明書署名リクエスト(CSR)を生成します。 +.sp +CSRは、証明書発行局(CA)に送信することを目的としたものです。CAは、証明書要求者を(通常はオフラインで)認証し、証明書または証明書チェーンを送り返します。この証明書または証明書チェーンは、キーストア内の既存の証明書チェーン(最初は1つの自己署名証明書から構成される)に置き換えて使用します。 +.sp +aliasに関連付けられた秘密鍵は、PKCS#10証明書リクエストを作成するのに使用されます。秘密鍵にアクセスするには、正しいパスワードを指定する必要があります。コマンド行で\fBkeypass\fRを指定しておらず、秘密鍵のパスワードがキーストアのパスワードと異なる場合は、秘密鍵のパスワードの入力を求められます。\fBdname\fRが指定されている場合は、それがCSRで主体として使用されます。それ以外の場合は、別名に関連付けられたX\&.500識別名が使用されます。 +.sp +\fBsigalg\fR値には、CSRに署名を付けるときに使用するアルゴリズムを指定します。 +.sp +CSRは、ファイルcertreq_fileに格納されます。ファイルが指定されていない場合は、\fBstdout\fRにCSRが出力されます。 +.sp +CAからのレスポンスをインポートするには、\fBimportcert\fRコマンドを使用します。 +.RE +.PP +\-exportcert +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} {\-file \fR\fB\fIcert_file\fR\fR\fB} {\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-storepass \fR\fB\fIstorepass\fR\fR\fB] {\-providerName \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-rfc} {\-v} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +\fIalias\fRに関連付けられた証明書をキーストアから読み込み、ファイルcert_fileに格納します。ファイルが指定されていない場合は、\fBstdout\fRに証明書が出力されます。 +.sp +デフォルトでは、証明書はバイナリ符号化で出力されます。\fB\-rfc\fRオプションが指定されている場合、出力可能符号化方式の出力はインターネットRFC 1421証明書符号化規格で定義されます。 +.sp +\fBalias\fRが、信頼できる証明書を参照している場合は、該当する証明書が出力されます。それ以外の場合、\fBalias\fRは、関連付けられた証明書チェーンを持つ鍵エントリを参照します。この場合は、チェーン内の最初の証明書が返されます。この証明書は、\fBalias\fRによって表されるエンティティの公開鍵を認証する証明書です。 +.sp +このコマンドは、以前のリリースでは\fB\-export\fRという名前でした。このリリースでは、引き続き古い名前がサポートされています。今後は、新しい名前\fB\-exportcert\fRが優先されます。 +.RE +.PP +\-list +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} {\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB} [\-storepass \fR\fB\fIstorepass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerName \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v | \-rfc} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +\fBalias\fRで特定されるキーストア・エントリの内容を\fBstdout\fRに出力します。\fBalias\fRが指定されていない場合は、キーストア全体の内容が表示されます。 +.sp +このコマンドは、デフォルトでは証明書のSHA1フィンガープリントを表示します。 +\fB\-v\fRオプションが指定されている場合は、所有者、発行者、シリアル番号、拡張機能などの付加的な情報とともに、人間が読むことのできる形式で証明書が表示されます。\fB\-rfc\fRオプションが指定されている場合は、出力可能符号化方式で証明書の内容が出力されます。出力可能符号化方式は、インターネットRFC 1421証明書符号化規格で定義されています。 +.sp +\fB\-v\fRオプションと\fB\-rfc\fRオプションを同時に指定することはできません。 +.RE +.PP +\-printcert +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-file \fR\fB\fIcert_file\fR\fR\fB | \-sslserver \fR\fB\fIhost\fR\fR\fB[:\fR\fB\fIport\fR\fR\fB]} {\-jarfile \fR\fB\fIJAR_file\fR\fR\fB {\-rfc} {\-v}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +ファイルcert_file、host:portにあるSSLサーバー、または署名付きJARファイル\fBJAR_file\fR(\fB\-jarfile\fRオプションを指定)から証明書を読み込み、人間が読むことのできる形式で証明書の内容を表示します。ポートが指定されていない場合は、標準のHTTPSポート443が想定されます。\fB\-sslserver\fRおよび\-fileオプションを同時に指定することはできません。それ以外の場合、エラーが報告されます。オプションが指定されていない場合は、\fBstdin\fRから証明書を読み込みます。 +.sp +\fB\-rfc\fRが指定されている場合、\fBkeytool\fRコマンドは、インターネットRFC 1421証明書符号化標準で定義されているように、PEMモードで証明書を出力します。インターネットRFC 1421証明書符号化規格を参照してください。 +.sp +ファイルまたは\fBstdin\fRから証明書を読み込む場合、その証明書は、インターネットRFC 1421証明書符号化標準で定義されているように、バイナリ符号化方式または出力可能符号化方式で表示できます。 +.sp +SSLサーバーがファイアウォールの背後にある場合は、\fB\-J\-Dhttps\&.proxyHost=proxyhost\fRおよび\fB\-J\-Dhttps\&.proxyPort=proxyport\fRオプションをコマンド行で指定して、プロキシ・トンネリングを使用できます。http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide\&.htmlの +「Java Secure Socket Extension (JSSE) Reference Guide」を参照してください +.sp +\fB注意:\fR +このオプションはキーストアとは関係なく使用できます。 +.RE +.PP +\-printcrl +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-file \fR\fB\fIcrl_\fR\fR\fB {\-v}\fR +.fi +.if n \{\ +.RE +.\} +ファイル\fBcrl_\fRから証明書失効リスト(CRL)を読み込みます。CRLは、発行したCAによって失効されたデジタル証明書のリストです。CAは、\fBcrl_\fRを生成します。 +.sp +\fB注意:\fR +このオプションはキーストアとは関係なく使用できます。 +.RE +.PP +\-storepasswd +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-new \fR\fB\fInew_storepass\fR\fR\fB] {\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-storepass \fR\fB\fIstorepass\fR\fR\fB] {\-providerName \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +キーストアの内容の整合性を保護するために使用するパスワードを変更します。\fBnew_storepass\fRには、新しいパスワードを指定します。new_storepassは、6文字以上である必要があります。 +.RE +.PP +\-keypasswd +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} [\-keypass \fR\fB\fIold_keypass\fR\fR\fB] [\-new \fR\fB\fInew_keypass\fR\fR\fB] {\-storetype \fR\fB\fIstoretype\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-keystore \fR\fB\fIkeystore\fR\fR\fB} [\-storepass \fR\fB\fIstorepass\fR\fR\fB] {\-providerName \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}} {\-v}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +\fBalias\fRによって特定される非公開/秘密鍵を保護するためのパスワードを、\fBold_keypass\fRから\fBnew_keypass\fRに変更します。new_keypassは、6文字以上である必要があります。 +.sp +コマンド行で\fB\-keypass\fRオプションを指定しておらず、鍵のパスワードがキーストアのパスワードと異なる場合は、鍵のパスワードの入力を求められます。 +.sp +コマンド行で\fB\-new\fRオプションを指定しなかった場合は、新しいパスワードの入力を求められます。 +.RE +.PP +\-delete +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB[\-alias \fR\fB\fIalias\fR\fR\fB] {\-storetype \fR\fB\fIstoretype\fR\fR\fB} {\-keystore \fR\fB\fIkeystore\fR\fR\fB} [\-storepass \fR\fB\fIstorepass\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerName \fR\fB\fIprovider_name\fR\fR\fB} \fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-v} {\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +\fBalias\fRによって特定されるエントリをキーストアから削除します。コマンド行で別名を指定しなかった場合は、別名の入力を求められます。 +.RE +.PP +\-changealias +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-alias \fR\fB\fIalias\fR\fR\fB} [\-destalias \fR\fB\fIdestalias\fR\fR\fB] [\-keypass \fR\fB\fIkeypass\fR\fR\fB] {\-storetype \fR\fB\fIstoretype\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-keystore \fR\fB\fIkeystore\fR\fR\fB} [\-storepass \fR\fB\fIstorepass\fR\fR\fB] {\-providerName \fR\fB\fIprovider_name\fR\fR\fB}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-providerClass \fR\fB\fIprovider_class_name\fR\fR\fB {\-providerArg \fR\fB\fIprovider_arg\fR\fR\fB}} {\-v}\fR +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB{\-protected} {\-Jjavaoption}\fR +.fi +.if n \{\ +.RE +.\} +指定された\fBalias\fRから新しい別名\fBdestalias\fRへ、既存のキーストア・エントリを移動します。ターゲット別名を指定しなかった場合、ターゲット別名の入力を求められます。元のエントリがエントリ・パスワードで保護されていた場合、\fB\-keypass\fRオプションでそのパスワードを指定できます。鍵パスワードが指定されなかった場合、\fBstorepass\fR(指定された場合)がまず試みられます。その試みが失敗すると、ユーザーはパスワードの入力を求められます。 +.RE +.PP +\-help +.RS 4 +基本的なコマンドとそのオプションの一覧を表示します。 +.sp +特定のコマンドの詳細を参照するには、次のように入力してください: +\fBkeytool \-command_name \-help\fR。\fBcommand_name\fRはコマンドの名前です。 +.RE +.SH "例" +.PP +この例では、公開/秘密鍵のペアおよび信頼できるエンティティからの証明書を管理するためのキーストアを作成する手順を示します。 +.SS "鍵のペアの生成" +.PP +まず、キーストアを作成して鍵のペアを生成します。単一行に入力する、次のようなコマンドを使用できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-genkeypair \-dname "cn=Mark Jones, ou=Java, o=Oracle, c=US"\fR +\fB \-alias business \-keypass \fR +\fB \-keystore /working/mykeystore\fR +\fB \-storepass \-validity 180\fR + +.fi +.if n \{\ +.RE +.\} +.PP +コマンドは、workingディレクトリに\fBmykeystore\fRという名前のキーストアを作成し(キーストアはまだ存在していないと仮定)、作成したキーストアに、\fB\fRで指定したパスワードを割り当てます。生成する公開鍵と秘密鍵のペアに対応するエンティティの「識別名」は、通称がMark Jones、組織単位がJava、組織がOracle、2文字の国番号がUSです。公開鍵と秘密鍵のサイズはどちらも1024ビットで、鍵の作成にはデフォルトのDSA鍵生成アルゴリズムを使用します。 +.PP +このコマンドは、デフォルトのSHA1withDSA署名アルゴリズムを使用して、公開鍵と識別名情報を含む自己署名証明書を作成します。証明書の有効期間は180日です。証明書は、別名\fBbusiness\fRで特定されるキーストア・エントリ内の秘密鍵に関連付けられます。秘密鍵には、\fB\fRで指定したパスワードが割り当てられます。 +.PP +オプションのデフォルト値を使用する場合、コマンドは大幅に短くなります。この場合、オプションは不要です。デフォルト値を持つオプションでは、オプションを指定しなければデフォルト値が使用されます。必須値の入力を求められます。使用可能な値は次のとおりです。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-genkeypair\fR + +.fi +.if n \{\ +.RE +.\} +.PP +この場合は、\fBmykey\fRという別名でキーストア・エントリが作成され、新しく生成された鍵のペア、および90日間有効な証明書がこのエントリに格納されます。このエントリは、ホーム・ディレクトリ内の\fB\&.keystore\fRという名前のキーストアに置かれます。キーストアは、まだ存在していない場合に作成されます。識別名情報、キーストアのパスワードおよび秘密鍵のパスワードの入力を求められます。 +.PP +以降では、オプションを指定しないで\fB\-genkeypair\fRコマンドを実行したものとして例を示します。情報の入力を求められた場合は、最初に示した\fB\-genkeypair\fRコマンドの値を入力したものとします。たとえば識別名には\fBcn=Mark Jones\fR、\fBou=Java\fR、\fBo=Oracle\fR、\fBc=US\fRと指定します。 +.SS "CAからの署名付き証明書のリクエスト" +.PP +自己署名証明書を作成する鍵のペアの生成。証明書に認証局(CA)の署名が付いていれば、他のユーザーから証明書が信頼される可能性も高くなります。CAの署名を取得するには、まず、証明書署名リクエスト(CSR)を生成します。たとえば、次のようにします。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-certreq \-file MarkJ\&.csr\fR + +.fi +.if n \{\ +.RE +.\} +.PP +CSR(デフォルト別名\fBmykey\fRによって特定されるエンティティのCSR)が作成され、MarkJ\&.csrという名前のファイルに置かれます。このファイルをCA (VeriSignなど)に提出します。CAは要求者を(通常はオフラインで)認証し、要求者の公開鍵を認証した署名付きの証明書を送り返します。場合によっては、CAが証明書のチェーンを返すこともあります。証明書のチェーンでは、各証明書がチェーン内のその前の署名者の公開鍵を認証します。 +.SS "CAからの証明書のインポート" +.PP +作成した自己署名証明書は、証明書チェーンで置き換える必要があります。証明書チェーンでは、各証明書が、「ルート」CAを起点とするチェーン内の次の証明書の署名者の公開鍵を認証します。 +.PP +CAからの証明書応答をインポートするには、キーストアか、\fBcacerts\fRキーストア・ファイル内に1つ以上の信頼できる証明書がある必要があります。コマンドの\fB\-importcert\fRを参照してください。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +証明応答が証明書チェーンの場合は、チェーンの最上位証明書が必要です。CAの公開鍵を認証するルートCA証明書。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +証明書応答が単一の証明書の場合は、発行CA(署名した)の証明書が必要です。その証明書が自己署名でない場合は、その署名者の証明書が必要であり、このようにして自己署名ルート証明書が必要になります。 +.RE +.PP +\fBcacerts\fRキーストア・ファイルは、いくつかのVeriSignルートCA証明書を含んだ状態で出荷されているので、VeriSignの証明書を、信頼できる証明書としてキーストア内にインポートする必要がない場合があります。ただし、他のCAに対して署名付き証明書をリクエストしていて、このCAの公開鍵を認証する証明書が、\fBcacerts\fRにまだ追加されていない場合は、該当するCAからの証明書を、「信頼できる証明書」としてインポートする必要があります。 +.PP +通常、CAからの証明書は、自己署名証明書、または他のCAによって署名された証明書です(後者の場合は、該当する他のCAの公開鍵を認証する証明書が必要)。ABC, Inc\&.,がCAで、ABCから自己署名証明書であるA\fBBCCA\&.cer\fRという名前のファイルを取得したとします(この証明書はCAの公開鍵を認証します)。信頼できる証明書として証明書をインポートするときは、証明書が有効であることを確認する必要があります。まず、証明書の内容を表示し、\fBkeytool \-printcert\fRコマンドを使用するか、または\fB\-noprompt\fRオプションを指定しないで\fBkeytool \-importcert\fRコマンドを使用し、表示された証明書のフィンガープリントが、期待されるフィンガープリントと一致するかどうかを確認します。証明書を送信した人物に連絡し、この人物が提示した(または安全な公開鍵のリポジトリによって提示される)フィンガープリントと、上のコマンドで表示されたフィンガープリントとを比較します。フィンガープリントが一致すれば、送信途中で他の何者か(攻撃者など)による証明書のすり替えが行われていないことを確認できます。送信途中でこの種の攻撃が行われていた場合、チェックを行わずに証明書をインポートすると、攻撃者によって署名されたすべてのものを信頼することになります。 +.PP +証明書が有効であると信頼する場合は、次のコマンドでキーストアに追加できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-importcert \-alias abc \-file ABCCA\&.cer\fR + +.fi +.if n \{\ +.RE +.\} +.PP +ABCCA\&.cerファイルのデータを含む信頼できる証明書のエントリがキーストア内に作成され、該当するエントリに\fBabc\fRという別名が割り当てられます。 +.SS "CAからの証明書応答のインポート" +.PP +証明書署名リクエストの提出先のCAの公開鍵を認証する証明書をインポートした後は(または同種の証明書がすでにcacertsファイル内に存在している場合は)、証明応答をインポートし、自己署名証明書を証明書チェーンで置き換えることができます。このチェーンは、CAの応答がチェーンの場合に、リクエストに対するレスポンスとしてCAから送り返された証明書チェーンです。また、CAの応答が単一の証明書の場合は、この証明応答と、インポート先のキーストア内または\fBcacerts\fRキーストアファイル内にすでに存在する信頼できる証明書とを使用して構築した証明書チェーンです。 +.PP +たとえば、証明書署名リクエストをVeriSignに送信する場合、送り返された証明書の名前がVSMarkJ\&.cerだとすると、次のようにして応答をインポートできます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-importcert \-trustcacerts \-file VSMarkJ\&.cer\fR + +.fi +.if n \{\ +.RE +.\} +.SS "公開鍵を認証する証明書のエクスポート" +.PP +\fBjarsigner\fRコマンドを使用してJava Archive (JAR)ファイルに署名する場合、このファイルを使用するクライアントは署名を認証する必要があります。クライアントが署名を認証する方法の1つに、まず自分の公開鍵の証明書を信頼できるエントリとしてクライアントのキーストアにインポートする方法があります。 +.PP +そのためには、証明書をエクスポートして、クライアントに提供します。例として、次のコマンドを使用して、MJ\&.cerという名前のファイルに証明書をコピーできます。このコマンドでは、エントリに別名\fBmykey\fRがあると仮定しています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-exportcert \-alias mykey \-file MJ\&.cer\fR + +.fi +.if n \{\ +.RE +.\} +.PP +証明書と署名付きJARファイルを入手したクライアントは、\fBjarsigner\fRコマンドを使用して署名を認証できます。 +.SS "キーストアのインポート" +.PP +コマンド\fBimportkeystore\fRを使用すれば、あるキーストアの全体を別のキーストア内にインポートできます。これは、鍵や証明書といったソースキーストア内のすべてのエントリが、単一のコマンドを使用してターゲットキーストア内にインポートされることを意味します。このコマンドを使用すれば、異なるタイプのキーストア内に含まれるエントリをインポートすることができます。インポート時には、ターゲット・キーストア内の新しいエントリはすべて、元と同じ別名および(秘密鍵や秘密鍵の場合は)保護用パスワードを持ちます。ソースキーストア内の非公開/秘密鍵をリカバリできない場合、\fBkeytool\fRコマンドはユーザーにパスワードの入力を求めます。このコマンドは、別名の重複を検出すると、ユーザーに新しい別名の入力を求めます。ユーザーは、新しい別名を指定することも、単純に既存の別名の上書きを\fBkeytool\fRコマンドに許可することもできます。 +.PP +たとえば、通常のJKSタイプのキーストアkey\&.jks内のエントリをPKCS#11タイプのハードウェア・ベースのキーストア内にインポートするには、次のコマンドを使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-importkeystore\fR +\fB \-srckeystore key\&.jks \-destkeystore NONE\fR +\fB \-srcstoretype JKS \-deststoretype PKCS11\fR +\fB \-srcstorepass \fR +\fB \-deststorepass \fR + +.fi +.if n \{\ +.RE +.\} +.PP +また、\fBimportkeystore\fRコマンドを使用すれば、あるソース・キーストア内の単一のエントリをターゲット・キーストアにインポートすることもできます。この場合は、前例のオプションに加えて、インポートする別名を指定する必要があります。\fB\-srcalias\fRオプションを指定する場合には、ターゲット別名もコマンド行から指定できるほか、秘密/秘密鍵の保護用パスワードやターゲット保護用パスワードも指定できます。その方法を示すコマンドを次に示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-importkeystore\fR +\fB \-srckeystore key\&.jks \-destkeystore NONE\fR +\fB \-srcstoretype JKS \-deststoretype PKCS11\fR +\fB \-srcstorepass \fR +\fB \-deststorepass \fR +\fB \-srcalias myprivatekey \-destalias myoldprivatekey\fR +\fB \-srckeypass \fR +\fB \-destkeypass \fR +\fB \-noprompt\fR + +.fi +.if n \{\ +.RE +.\} +.SS "SSLサーバーの証明書の生成" +.PP +次に、3つのエンティティ、つまりルートCA(\fBroot\fR)、中間CA(\fBca\fR)およびSSLサーバー(\fBserver\fR)用の鍵ペアと証明書を生成する\fBkeytool\fRコマンドを示します。すべての証明書を同じキーストアに格納するようにしてください。これらの例では、RSAが推奨される鍵のアルゴリズムです。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-genkeypair \-keystore root\&.jks \-alias root \-ext bc:c\fR +\fBkeytool \-genkeypair \-keystore ca\&.jks \-alias ca \-ext bc:c\fR +\fBkeytool \-genkeypair \-keystore server\&.jks \-alias server\fR +\fB \fR +\fBkeytool \-keystore root\&.jks \-alias root \-exportcert \-rfc > root\&.pem\fR +\fB \fR +\fBkeytool \-storepass \-keystore ca\&.jks \-certreq \-alias ca |\fR +\fB keytool \-storepass \-keystore root\&.jks\fR +\fB \-gencert \-alias root \-ext BC=0 \-rfc > ca\&.pem\fR +\fBkeytool \-keystore ca\&.jks \-importcert \-alias ca \-file ca\&.pem\fR +\fB \fR +\fBkeytool \-storepass \-keystore server\&.jks \-certreq \-alias server |\fR +\fB keytool \-storepass \-keystore ca\&.jks \-gencert \-alias ca\fR +\fB \-ext ku:c=dig,kE \-rfc > server\&.pem\fR +\fBcat root\&.pem ca\&.pem server\&.pem |\fR +\fB keytool \-keystore server\&.jks \-importcert \-alias server\fR + +.fi +.if n \{\ +.RE +.\} +.SH "用語" +.PP +キーストア +.RS 4 +キーストアは、暗号化の鍵と証明書を格納するための機能です。 +.RE +.PP +キーストアのエントリ +.RS 4 +キーストアには異なるタイプのエントリを含めることができます。\fBkeytool\fRコマンドで最も適用範囲の広いエントリ・タイプは、次の2つです。 +.sp +\fB鍵のエントリ\fR +\- 各エントリは、非常に重要な暗号化の鍵の情報を保持します。この情報は、許可していないアクセスを防ぐために、保護された形で格納されます。一般に、この種のエントリとして格納される鍵は、秘密鍵か、対応する公開鍵の証明書チェーンを伴う秘密鍵です。証明書チェーンを参照してください。\fBkeytool\fRコマンドがこの両方のタイプのエントリを処理できるのに対し、\fBjarsigner\fRツールは後者のタイプのエントリ、つまり秘密鍵とそれに関連付けられた証明書チェーンのみを処理します。 +.sp +\fB信頼できる証明書のエントリ\fR: 各エントリは、第三者からの公開鍵証明書を1つ含んでいます。このエントリは、信頼できる証明書と呼ばれます。それは、証明書内の公開鍵が、証明書のSubject(所有者)によって特定されるアイデンティティに由来するものであることを、キーストアの所有者が信頼するからです。証明書の発行者は、証明書に署名を付けることによって、その内容を保証します。 +.RE +.PP +キーストアの別名 +.RS 4 +キーストアのすべてのエントリ(鍵および信頼できる証明書エントリ)は、一意の別名を介してアクセスされます。 +.sp +別名を指定するのは、\fB\-genseckey\fRコマンドを使用して秘密鍵を生成したり、\fB\-genkeypair\fRコマンドを使用して鍵ペア(公開鍵と秘密鍵)を生成したり、\fB\-importcert\fRコマンドを使用して証明書または証明書チェーンを信頼できる証明書のリストに追加するなど、特定のエンティティをキーストアに追加する場合です。これ以後、\fBkeytool\fRコマンドでエンティティを参照する場合は、このときに指定した別名を使用する必要があります。 +.sp +たとえば、\fBduke\fRという別名を使用して新しい公開鍵と秘密鍵のペアを生成し、公開鍵を自己署名証明書でラップするとします。この場合は、次のコマンドを実行します。証明書チェーンを参照してください。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-genkeypair \-alias duke \-keypass dukekeypasswd\fR + +.fi +.if n \{\ +.RE +.\} +この例では、初期パスワードとして\fBdukekeypasswd\fRを指定しています。以後、別名\fBduke\fRに関連付けられた秘密鍵にアクセスするコマンドを実行するときは、このパスワードが必要になります。Dukeの秘密鍵のパスワードをあとから変更するには、次のコマンドを実行します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-keypasswd \-alias duke \-keypass dukekeypasswd \-new newpass\fR + +.fi +.if n \{\ +.RE +.\} +パスワードが、\fBdukekeypasswd\fRから\fBnewpass\fRに変更されます。テスト目的の場合、またはセキュアなシステムを使用している場合以外は、コマンド行やスクリプトでパスワードを指定しないでください。必要なパスワードのオプションをコマンド行で指定しなかった場合は、パスワードの入力を求められます。 +.RE +.PP +キーストアの実装 +.RS 4 +\fBjava\&.security\fRパッケージで提供されている\fBKeyStore\fRクラスは、キーストア内の情報へのアクセスおよび情報の変更を行うための、明確に定義されたインタフェースを提供します。キーストアの固定実装としては、それぞれが特定のタイプのキーストアを対象とする複数の異なる実装が存在可能です。 +.sp +現在、\fBkeytool\fRと\fBjarsigner\fRの2つのコマンド行ツールと、Policy Toolという名前のGUIベースのツールが、キーストアの実装を使用しています。\fBKeyStore\fRクラスは\fBpublic\fRであるため、ユーザーはKeyStoreを使用した他のセキュリティ・アプリケーションも作成できます。 +.sp +キーストアには、Oracleが提供する組込みのデフォルトの実装があります。これは、JKSという名前の独自のキーストア・タイプ(形式)を利用するもので、キーストアをファイルとして実装しています。この実装では、個々の秘密鍵は個別のパスワードによって保護され、キーストア全体の整合性も(秘密鍵とは別の)パスワードによって保護されます。 +.sp +キーストアの実装は、プロバイダベースです。具体的には、\fBKeyStore\fRによって提供されるアプリケーション・インタフェースがサービス・プロバイダ・インタフェース(SPI)に基づいて実装されます。つまり、対応する\fBKeystoreSpi\fR抽象クラス(これも\fBjava\&.security\fRパッケージに含まれています)があり、このクラスが、プロバイダが実装する必要のあるService Provider Interfaceのメソッドを定義しています。ここで、\fIプロバイダ\fRとは、Java Security APIによってアクセス可能なサービスのサブセットに対し、その固定実装を提供するパッケージまたはパッケージの集合のことです。キーストアの実装を提供するには、http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider\&.htmlにある +Java暗号化アーキテクチャのプロバイダの実装方法で説明しているように、クライアントはプロバイダを実装し、\fBKeystoreSpi\fRサブクラスの実装を提供する必要があります。 +.sp +アプリケーションでは、\fBKeyStore\fRクラスが提供する\fBgetInstance\fRファクトリ・メソッドを使用することで、様々なプロバイダから異なるタイプのキーストアの実装を選択できます。キーストアのタイプは、キーストア情報の格納形式とデータ形式を定義するとともに、キーストア内の非公開/秘密鍵とキーストアの整合性を保護するために使用されるアルゴリズムを定義します。異なるタイプのキーストアの実装には、互換性はありません。 +.sp +\fBkeytool\fRコマンドは、任意のファイルベースのキーストア実装で動作します。コマンド行で渡されたキーストアの場所をファイル名として扱って、\fBFileInputStream\fRに変換し、ここからキーストア情報をロードします。\fBjarsigner\fRおよび\fBpolicytool\fRコマンドは、URLで指定できる任意の場所からキーストアを読み取ることができます。 +.sp +\fBkeytool\fRと\fBjarsigner\fRの場合、\fB\-storetype\fRオプションを使用してコマンド行でキーストアのタイプを指定できます。Policy Toolの場合は、「キーストア」メニューによってキーストアのタイプを指定できます。 +.sp +ユーザーがキーストアのタイプを明示的に指定しなかった場合、セキュリティ・プロパティ・ファイルで指定された\fBkeystore\&.type\fRプロパティの値に基づいて、ツールによってキーストアの実装が選択されます。このセキュリティ・プロパティ・ファイルは\fBjava\&.security\fRと呼ばれ、Windowsではセキュリティ・プロパティ・ディレクトリ\fBjava\&.home\elib\esecurity\fR、Oracle Solarisでは\fBjava\&.home/lib/security\fRにあります。\fBjava\&.home\fRは、実行時環境のディレクトリです。\fBjre\fRディレクトリは、SDKまたはJava Runtime Environment (JRE)の最上位のディレクトリにあります。 +.sp +各ツールは、\fBkeystore\&.type\fRの値を取得し、この値で指定されたタイプのキーストアを実装しているプロバイダが見つかるまで、現在インストールされているすべてのプロバイダを調べます。そのプロバイダからのキーストアの実装を使用します。\fBKeyStore\fRクラスに定義されているstaticメソッド\fBgetDefaultType\fRを使用すると、アプリケーションやアプレットから\fBkeystore\&.type\fRプロパティの値を取得できます。次のコードは、デフォルトのキーストア・タイプ(\fBkeystore\&.type\fRプロパティで指定されたタイプ)のインスタンスを生成します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBKeyStore keyStore = KeyStore\&.getInstance(KeyStore\&.getDefaultType());\fR + +.fi +.if n \{\ +.RE +.\} +デフォルトのキーストア・タイプは\fBjks\fRで、これはOracleが提供する独自のタイプのキーストアの実装です。これは、セキュリティ・プロパティ・ファイル内の次の行によって指定されています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeystore\&.type=jks\fR + +.fi +.if n \{\ +.RE +.\} +各ツールでデフォルト以外のキーストアの実装を使用するには、上の行を変更して別のキーストアのタイプを指定します。たとえば、\fBpkcs12\fRと呼ばれるキーストアのタイプのキーストアの実装を提供するプロバイダ・パッケージがある場合、行を次のように変更します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeystore\&.type=pkcs12\fR + +.fi +.if n \{\ +.RE +.\} +\fB注意:\fR +キーストアのタイプの指定では、大文字と小文字は区別されません。たとえば、JKSとjksは同じものとして扱われます。 +.RE +.PP +証明書 +.RS 4 +証明書(公開鍵証明書)とは、あるエンティティ(発行者)からのデジタル署名付きの文書のことです。証明書には、他のあるエンティティ(署名者)の公開鍵(およびその他の情報)が特別な値を持っていることが書かれています。次の用語は、証明書に関連しています。 +.sp +\fB公開鍵\fR: 公開鍵は、特定のエンティティに関連付けられた数です。公開鍵は、該当するエンティティとの間に信頼できる関係を持つ必要があるすべての人に対して公開することを意図したものです。公開鍵は、署名を検証するのに使用されます。 +.sp +\fBデジタル署名\fR: データがデジタル署名されると、そのデータは、エンティティのアイデンティティと、そのエンティティがデータの内容について知っていることを証明書する署名とともに格納されます。エンティティの秘密鍵を使用してデータに署名を付けると、データの偽造は不可能になります。 +.sp +\fBアイデンティティ\fR: エンティティをアドレス指定する既知の方法。システムによっては、公開鍵をアイデンティティにするものがあります。公開鍵の他にも、Oracle Solaris UIDや電子メール・アドレス、X\&.509識別名など、様々なものをアイデンティティとすることができます。 +.sp +\fB署名\fR: 署名は、なんらかのデータを基にエンティティの秘密鍵を使用して計算されます。署名者、証明書の場合は発行者とも呼ばれます。 +.sp +\fB秘密鍵\fR: 秘密鍵は特定のエンティティのみが知っている数のことで、この数のことを、そのエンティティの秘密鍵といいます。秘密鍵は、他に知られないように秘密にしておくことが前提になっています。秘密鍵と公開鍵は、すべての公開鍵暗号化システムで対になって存在しています。DSAなどの典型的な公開鍵暗号化システムの場合、1つの秘密鍵は正確に1つの公開鍵に対応します。秘密鍵は、署名を計算するのに使用されます。 +.sp +\fBエンティティ\fR: エンティティは、人、組織、プログラム、コンピュータ、企業、銀行など、一定の度合いで信頼の対象となる様々なものを指します。 +.sp +公開鍵暗号化では、ユーザーの公開鍵にアクセスする必要があります。大規模なネットワーク環境では、互いに通信しているエンティティ間で以前の関係が引続き確立されていると仮定したり、使用されているすべての公開鍵を収めた信頼できるリポジトリが存在すると仮定したりすることは不可能です。このような公開鍵の配布に関する問題を解決するために証明書が考案されました。現在では、認証局(CA)が信頼できる第三者として機能します。CAは、他のエンティティの証明書に署名する(発行する)行為を、信頼して任されているエンティティ(企業など)です。CAは法律上の契約に拘束されるので、有効かつ信頼できる証明書のみを作成するものとして扱われます。VeriSign、Thawte、Entrustをはじめ、多くの公的な認証局が存在します。 +.sp +Microsoftの認証サーバー、EntrustのCA製品などを所属組織内で利用すれば、独自の認証局を運営することも可能です。\fBkeytool\fRコマンドを使用すると、証明書の表示、インポートおよびエクスポートを行うことができます。また、自己署名証明書を生成することもできます。 +.sp +現在、\fBkeytool\fRコマンドはX\&.509証明書を対象にしています。 +.RE +.PP +X\&.509証明書 +.RS 4 +X\&.509規格では、証明書に含める情報が定義されており、この情報を証明書に書き込む方法(データ形式)についても記述されています。証明書のすべてのデータは、ASN\&.1/DERと呼ばれる2つの関連規格を使用して符号化されます。Abstract Syntax Notation 1はデータについて記述しています。Definite Encoding Rulesは、データの保存および転送の方法について記述しています。 +.sp +すべてのX\&.509証明書は、署名の他に次のデータを含んでいます。 +.sp +\fBバージョン\fR: 証明書に適用されるX\&.509規格のバージョンを特定します。証明書に指定できる情報は、バージョンによって異なります。今のところ、3つのバージョンが定義されています。\fBkeytool\fRコマンドでは、v1、v2、v3の証明書をインポートおよびエクスポートできます。v3の証明書を生成します。 +.sp +X\&.509 Version 1は、1988年から利用されて広く普及しており、最も一般的です。 +.sp +X\&.509 Version 2では、Subjectや発行者の名前をあとで再利用できるようにするために、Subjectと発行者の一意識別子の概念が導入されました。ほとんどの証明書プロファイル文書では、名前を再使用しないことと、証明書で一意の識別子を使用しないことが、強く推奨されています。Version 2の証明書は、広くは使用されていません。 +.sp +X\&.509 Version 3は最も新しい(1996年)規格で、エクステンションの概念をサポートしています。エクステンションは誰でも定義することができ、証明書に含めることができます。一般的なエクステンションとしては、KeyUsage(\fB署名専用\fRなど、鍵の使用を特定の目的に制限する)、AlternativeNames(DNS名、電子メール・アドレス、IPアドレスなど、他のアイデンティティを公開鍵に関連付けることができる)などがあります。エクステンションには、criticalというマークを付けて、そのエクステンションのチェックと使用を義務づけることができます。たとえば、criticalとマークされ、\fBkeyCertSign\fRが設定されたKeyUsageエクステンションが証明書に含まれている場合、この証明書をSSL通信中に提示すると、証明書が拒否されます。これは、証明書のエクステンションによって、関連する秘密鍵が証明書の署名専用として指定されており、SSLでは使用できないためです。 +.sp +\fBシリアル番号\fR: 証明書を作成したエンティティは、そのエンティティが発行する他の証明書と区別するために、証明書にシリアル番号を割り当てます。この情報は、様々な方法で使用されます。たとえば、証明書が取り消されると、シリアル番号が証明書失効リスト(CRL)に格納されます。 +.sp +\fB証明書アルゴリズム識別子\fR: 証明書に署名を付けるときにCAが使用したアルゴリズムを特定します。 +.sp +\fB発行者名\fR: 証明書に署名を付けたエンティティのX\&.500識別名です。X\&.500識別名を参照してください。通常はCAです。この証明書を使用することは、証明書に署名を付けたエンティティを信頼することを意味します。ルートつまりトップレベルのCAの証明書など、場合によっては発行者が自身の証明書に署名を付けることがあります。 +.sp +\fB有効期間\fR: 各証明書は限られた期間のみ有効です。この期間は開始の日時と終了の日時によって指定され、数秒の短い期間から100年という長期にわたることもあります。選択される有効期間は、証明書への署名に使用される秘密鍵の強度や証明書に支払う金額など、様々な要因で異なります。有効期間は、関連する秘密鍵が損われない場合に、エンティティが公開鍵を信頼できると期待される期間です。 +.sp +\fB主体名\fR: 証明書で公開鍵を認証するエンティティの名前。この名前はX\&.500標準を使用するので、インターネット全体で一意なものと想定されます。これは、エンティティのX\&.500識別名(DN)です。X\&.500識別名を参照してください。次に例を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCN=Java Duke, OU=Java Software Division, O=Oracle Corporation, C=US\fR + +.fi +.if n \{\ +.RE +.\} +これらはそれぞれ主体の通称(CN)、組織単位(OU)、組織(O)、国(C)を表します。 +.sp +\fB主体の公開鍵情報\fR: 名前を付けられたエンティティの公開鍵とアルゴリズム識別子です。アルゴリズム識別子では、公開鍵に対して使用されている公開鍵暗号化システムおよび関連する鍵パラメータが指定されています。 +.RE +.PP +証明書チェーン +.RS 4 +\fBkeytool\fRコマンドでは、秘密鍵および関連する証明書チェーンを含むキーストアの鍵エントリを作成し、管理することができます。このようなエントリでは、秘密鍵に対応する公開鍵は、チェーンの最初の証明書に含まれています。 +.sp +鍵を初めて作成すると、自己署名証明書という1つの要素のみを含むチェーンが開始されます。コマンドの\fB\-genkeypair\fRを参照してください。自己署名証明書は発行者(署名者)が主体と同じです。主体は、その公開鍵が証明書によって認証されるエンティティです。\fB\-genkeypair\fRコマンドを呼び出して新しい公開鍵と秘密鍵のペアを作成すると、公開鍵は常に自己署名証明書でラップされます。 +.sp +この後、証明書署名リクエスト(CSR)が\fB\-certreq\fRコマンドで生成されて、CSRが認証局(CA)に送信されると、CAからのレスポンスが\fB\-importcert\fRでインポートされ、元の自己署名証明書は証明書チェーンによって置き換えられます。\fBの\fR\-certreq\fBおよび\fR\-importcertコマンドオプションを参照してください。チェーンの最後にあるのは、Subjectの公開鍵を認証したCAが発行した証明書(応答)です。チェーン内のその前の証明書は、CAの公開鍵を認証する証明書です。 +.sp +CAの公開鍵を認証する証明書は、多くの場合、自己署名証明書(つまりCAが自身の公開鍵を認証した証明書)であり、これはチェーンの最初の証明書になります。場合によっては、CAが証明書のチェーンを返すこともあります。この場合、チェーン内の最後の証明書(CAによって署名され、鍵エントリの公開鍵を認証する証明書)に変わりはありませんが、チェーン内のその前の証明書は、CSRの送信先のCAとは別のCAによって署名され、CSRの送信先のCAの公開鍵を認証する証明書になります。チェーン内のその前の証明書は、次のCAの鍵を認証する証明書になります。以下同様に、自己署名された「ルート」証明書に達するまでチェーンが続きます。したがって、チェーン内の(最初の証明書以後の)各証明書では、チェーン内の次の証明書の署名者の公開鍵が認証されていることになります。 +.sp +多くのCAは、チェーンをサポートせずに発行済の証明書のみを返します。特に、中間のCAが存在しないフラットな階層構造の場合は、その傾向が顕著です。このような場合は、キーストアにすでに格納されている信頼できる証明書情報から、証明書チェーンを確立する必要があります。 +.sp +別の応答形式(PKCS#7で定義されている形式)では、発行済証明書に加え、証明書チェーンのサポートが含まれています。\fBkeytool\fRコマンドでは、どちらの応答形式も扱うことができます。 +.sp +トップレベル(ルート)CAの証明書は、自己署名証明書です。ただし、ルートの公開鍵への信頼は、ルート証明書自体からではなく、新聞など他のソースから取得されます。これは、VeriSignルートCAなどの識別名を使用して、誰でも自己署名型証明書を生成できるためです。ルートCAの公開鍵は広く知られています。ルートCAの公開鍵を証明書に格納する理由は、証明書という形式にすることで多くのツールから利用できるようになるからにすぎません。つまり、証明書は、ルートCAの公開鍵を運ぶ「媒体」として利用されるのみです。ルートCAの証明書をキーストアに追加するときは、\fB\-printcert\fRオプションを使用して、その前に証明書の内容を表示し、表示されたフィンガープリントと、新聞やルートCAのWebページなどから入手した既知のフィンガープリントとを比較する必要があります。 +.RE +.PP +cacerts証明書ファイル +.RS 4 +\fBcacerts\fRという名前の証明書ファイルは、Windowsではセキュリティ・プロパティ・ディレクトリ\fBjava\&.home\elib\esecurity\fR、Oracle Solarisでは\fBjava\&.home/lib/security\fRに置かれています。\fBjava\&.home\fRは、実行環境のディレクトリ(SDKの\fBjre\fRディレクトリまたはJREの最上位ディレクトリ)です。 +.sp +\fBcacerts\fRファイルは、CAの証明書を含む、システム全体のキーストアです。システム管理者は、キーストア・タイプに\fBjks\fRを指定することで、\fBkeytool\fRコマンドを使用してこのファイルの構成と管理を行うことができます。\fBcacerts\fRキーストア・ファイルは、ルートCA証明書のデフォルト・セットを含んだ状態で出荷されています。デフォルトの証明書を一覧表示するには、次のコマンドを使用します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-list \-keystore java\&.home/lib/security/cacerts\fR + +.fi +.if n \{\ +.RE +.\} +\fBcacerts\fRキーストア・ファイルの初期パスワードは、\fBchangeit\fRです。システム管理者は、SDKのインストール後、このファイルのパスワードとデフォルト・アクセス権を変更する必要があります。 +.sp +\fB注意:\fR +\fBcacerts\fRファイルを確認することが重要です。\fBcacerts\fRファイル内のCAは、署名および他のエンティティへの証明書発行のためのエンティティとして信頼されるため、\fBcacerts\fRファイルの管理は慎重に行う必要があります。\fBcacerts\fRファイルには、信頼するCAの証明書のみが含まれている必要があります。ユーザーは、自身の責任において、\fBcacerts\fRファイルにバンドルされている信頼できるルートCA証明書を検証し、信頼性に関する独自の決定を行います。 +.sp +信頼できないCA証明書を\fBcacerts\fRファイルから削除するには、\fBkeytool\fRコマンドの\fBdelete\fRオプションを使用します。\fBcacerts\fRファイルはJREのインストール・ディレクトリにあります。このファイルを編集するアクセス権がない場合は、システム管理者に連絡してください +.RE +.PP +インターネットRFC 1421証明書符号化規格 +.RS 4 +多くの場合、証明書は、バイナリ符号化ではなく、インターネットRFC 1421規格で定義されている出力可能符号化方式を使用して格納されます。Base 64符号化とも呼ばれるこの証明書形式では、電子メールやその他の機構を通じて、他のアプリケーションに証明書を容易にエクスポートできます。 +.sp +\fB\-importcert\fRと\fB\-printcert\fRコマンドでは、この形式の証明書とバイナリ符号化の証明書を読み込むことができます。\fB\-exportcert\fRコマンドでは、デフォルトでバイナリ符号化の証明書が出力されます。ただし、\fB\-rfc\fRオプションを指定した場合は、出力可能符号化方式の証明書が出力されます。 +.sp +\fB\-list\fRコマンドでは、デフォルトで証明書のSHA1フィンガープリントが出力されます。\fB\-v\fRオプションが指定されている場合、証明書は人が理解できる形式で出力されます。\fB\-rfc\fRオプションが指定されている場合、証明書は出力可能符号化方式で出力されます。 +.sp +出力可能符号化方式で符号化された証明書は、次のテキストで始まり、次のテキストで終了します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-\fR + +\fBencoded certificate goes here\&. \fR + +\fB\-\-\-\-\-END CERTIFICATE\-\-\-\-\-\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +X\&.500識別名 +.RS 4 +X\&.500識別名は、エンティティを特定するために使用されます。たとえば、X\&.509証明書の\fBsubject\fRフィールドと\fBissuer\fR(署名者)フィールドで指定される名前は、X\&.500識別名です。\fBkeytool\fRコマンドは、次のサブパートをサポートしています。 +.sp +\fBcommonName\fR: Susan Jonesなど、人の通称。 +.sp +\fBorganizationUnit\fR: 小さな組織(部、課など)の名称。Purchasingなどです。 +.sp +\fBlocalityName\fR: 地域(都市)名。Palo Altoなど。 +.sp +\fBstateName\fR: 州名または地方名。Californiaなど。 +.sp +\fBcountry\fR: 2文字の国コード。CHなど。 +.sp +識別名文字列を\fB\-dname\fRオプションの値として指定する場合(たとえば\fB\-genkeypair\fRコマンドに)、文字列は次の形式にする必要があります。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCN=cName, OU=orgUnit, O=org, L=city, S=state, C=countryCode\fR + +.fi +.if n \{\ +.RE +.\} +イタリックの項目は、実際に指定する値を表します。短縮形のキーワードの意味は、次のとおりです。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCN=commonName\fR +\fBOU=organizationUnit\fR +\fBO=organizationName\fR +\fBL=localityName\fR +\fBS=stateName\fR +\fBC=country\fR + +.fi +.if n \{\ +.RE +.\} +次に示すのは、識別名文字列の例です。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCN=Mark Smith, OU=Java, O=Oracle, L=Cupertino, S=California, C=US\fR + +.fi +.if n \{\ +.RE +.\} +この文字列を使用したコマンドの例です。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBkeytool \-genkeypair \-dname "CN=Mark Smith, OU=Java, O=Oracle, L=Cupertino,\fR +\fBS=California, C=US" \-alias mark\fR + +.fi +.if n \{\ +.RE +.\} +キーワードの短縮形では、大文字と小文字は区別されません。たとえば、CN、cnおよびCnは、どれも同じものとして扱われます。 +.sp +一方、キーワードの指定順序には意味があり、各サブコンポーネントは上に示した順序で指定する必要があります。ただし、サブコンポーネントをすべて指定する必要はありません。たとえば、次のように一部のサブコンポーネントのみを指定できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCN=Steve Meier, OU=Java, O=Oracle, C=US\fR + +.fi +.if n \{\ +.RE +.\} +識別名文字列の値にカンマが含まれる場合に、コマンド行で文字列を指定するときには、次のようにカンマをバックスラッシュ文字(\e)でエスケープする必要があります。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBcn=Peter Schuster, ou=Java\e, Product Development, o=Oracle, c=US\fR + +.fi +.if n \{\ +.RE +.\} +識別名文字列をコマンド行で指定する必要はありません。識別名を必要とするコマンドを実行するときに、コマンド行で識別名を指定しなかった場合は、各サブコンポーネントの入力を求められます。この場合は、カンマをバックスラッシュ(\e)でエスケープする必要はありません。 +.RE +.SH "警告" +.SS "信頼できる証明書のインポート警告" +.PP +\fB重要\fR: 信頼できる証明書として証明書をインポートする前に、証明書の内容を慎重に調べてください。 +.PP +Windowsの例: +.PP +まず、\fB\-noprompt\fRオプションを指定せずに\fB\-printcert\fRコマンドまたは\fB\-importcert\fRコマンドを使用して、証明書を表示します。表示された証明書のフィンガープリントが、期待されるフィンガープリントと一致することを確認します。たとえば、証明書が送られてきて、この証明書を\fB\etmp\ecert\fRという名前でファイルに格納しているとします。この場合は、信頼できる証明書のリストにこの証明書を追加する前に、\fB\-printcert\fRコマンドを実行してフィンガープリントを表示できます。たとえば、次のようにします。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB keytool \-printcert \-file \etmp\ecert\fR +\fB Owner: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fR +\fB Issuer: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fR +\fB Serial Number: 59092b34\fR +\fB Valid from: Thu Sep 25 18:01:13 PDT 1997 until: Wed Dec 24 17:01:13 PST 1997\fR +\fB Certificate Fingerprints:\fR +\fB MD5: 11:81:AD:92:C8:E5:0E:A2:01:2E:D4:7A:D7:5F:07:6F\fR +\fB SHA1: 20:B6:17:FA:EF:E5:55:8A:D0:71:1F:E8:D6:9D:C0:37:13:0E:5E:FE\fR +\fB SHA256: 90:7B:70:0A:EA:DC:16:79:92:99:41:FF:8A:FE:EB:90:\fR +\fB 17:75:E0:90:B2:24:4D:3A:2A:16:A6:E4:11:0F:67:A4\fR +.fi +.if n \{\ +.RE +.\} +.PP +Oracle Solarisの例: +.PP +まず、\fB\-noprompt\fRオプションを指定せずに\fB\-printcert\fRコマンドまたは\fB\-importcert\fRコマンドを使用して、証明書を表示します。表示された証明書のフィンガープリントが、期待されるフィンガープリントと一致することを確認します。たとえば、あるユーザーから証明書が送られてきて、この証明書を\fB/tmp/cert\fRという名前でファイルに格納しているとします。この場合は、信頼できる証明書のリストにこの証明書を追加する前に、\fB\-printcert\fRコマンドを実行してフィンガープリントを表示できます。たとえば、次のようにします。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB keytool \-printcert \-file /tmp/cert\fR +\fB Owner: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fR +\fB Issuer: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fR +\fB Serial Number: 59092b34\fR +\fB Valid from: Thu Sep 25 18:01:13 PDT 1997 until: Wed Dec 24 17:01:13 PST 1997\fR +\fB Certificate Fingerprints:\fR +\fB MD5: 11:81:AD:92:C8:E5:0E:A2:01:2E:D4:7A:D7:5F:07:6F\fR +\fB SHA1: 20:B6:17:FA:EF:E5:55:8A:D0:71:1F:E8:D6:9D:C0:37:13:0E:5E:FE\fR +\fB SHA256: 90:7B:70:0A:EA:DC:16:79:92:99:41:FF:8A:FE:EB:90:\fR +\fB 17:75:E0:90:B2:24:4D:3A:2A:16:A6:E4:11:0F:67:A4\fR + +.fi +.if n \{\ +.RE +.\} +.PP +次に、証明書を送信した人物に連絡し、この人物が提示したフィンガープリントと、上のコマンドで表示されたフィンガープリントとを比較します。フィンガープリントが一致すれば、送信途中で他の何者か(攻撃者など)による証明書のすり替えが行われていないことを確認できます。送信途中でこの種の攻撃が行われていた場合、チェックを行わずに証明書をインポートすると、攻撃者によって署名されたすべてのもの(攻撃的意図を持つクラス・ファイルを含んだJARファイルなど)を信頼することになります。 +.PP +\fB注意:\fR +証明書をインポートする前に\fB\-printcert\fRコマンドを実行する必要はありません。キーストア内の信頼できる証明書のリストに証明書を追加する前に、\fB\-importcert\fRコマンドによって証明書の情報が表示され、確認を求めるメッセージが表示されるためです。ユーザーはインポート操作を停止できます。ただし、これを実行できるのは、\fB\-noprompt\fRオプションを指定せずに\fB\-importcert\fRコマンドを呼び出す場合のみです。\fB\-noprompt\fRオプションが指定されている場合、ユーザーとの対話は行われません。 +.SS "パスワード警告" +.PP +キーストアに対する操作を行うほとんどのコマンドでは、ストアのパスワードが必要です。また、一部のコマンドでは、非公開/秘密鍵のパスワードが必要になることがあります。パスワードはコマンド行で指定できます(\fB\-storepass\fRオプションと\fB\-keypass\fRオプションを使用)。ただし、テスト目的の場合、またはセキュアなシステムを使用している場合以外は、コマンド行やスクリプトでパスワードを指定しないでください。必要なパスワードのオプションをコマンド行で指定しなかった場合は、パスワードの入力を求められます。 +.SS "証明書の準拠に関する警告" +.PP +インターネット標準RFC 5280では、X\&.509証明書の準拠に関するプロファイルが定義されており、証明書のフィールドおよびエクステンションに有効な値および値の組合せが記載されています。標準については、 +http://tools\&.ietf\&.org/rfc/rfc5280\&.txtを参照してください +.PP +\fBkeytool\fRコマンドでは、これらのルールすべてが適用されるわけではないため、標準に準拠しない証明書を生成できます。標準に準拠しない証明書は、JREや他のアプリケーションで拒否されることがあります。ユーザーは、\fB\-dname\fRや\fB\-ext\fRなどで適正なオプションを指定するようにしてください。 +.SH "注意" +.SS "新しい信頼できる証明書のインポート" +.PP +\fBkeytool\fRコマンドは、キーストアに証明書を追加する前に、キーストア内にすでに存在する信頼できる証明書を使用して、インポートする証明書から(ルートCAの)自己署名証明書に至るまでの信頼のチェーンの構築を試みます。 +.PP +\fB\-trustcacerts\fRオプションを指定した場合、追加の証明書は信頼できるすなわち\fBcacerts\fRという名前のファイルに含まれる証明書のチェーンとみなされます。 +.PP +\fBkeytool\fRコマンドが、インポートする証明書から自己署名証明書(キーストアまたは\fBcacerts\fRファイルに含まれている自己署名証明書)に至るまでの信頼のパスの構築に失敗した場合は、インポートする証明書の情報を表示し、ユーザーに確認を求めます。この場合は、表示された証明書のフィンガープリントと、他のなんらかの(信頼できる)情報源(証明書の所有者など)から入手したフィンガープリントとを比較します。信頼できる証明書として証明書をインポートするときは、証明書が有効であることを慎重に確認する必要があります。信頼できる証明書のインポート警告を参照してください。インポート操作は、証明書を確認する時点で中止できます。\fB\-noprompt\fRオプションが指定されている場合、ユーザーとの対話は行われません。 +.SS "証明書応答のインポート" +.PP +証明書応答をインポートするときは、キーストア内の信頼できる証明書、および(\fB\-trustcacert\fR\fBs\fRオプションが指定されている場合は)\fBcacerts\fRキーストア・ファイルで構成された証明書を使用して証明書応答が検査されます。cacerts証明書ファイルを参照してください。 +.PP +証明書応答が信頼できるかどうかを決定する方法は次のとおりです。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +証明書応答が単一のX\&.509証明書である場合、\fBkeytool\fRコマンドは、証明書応答から(ルートCAの)自己署名証明書に至るまでの信頼チェーンの確立を試みます。証明書応答と、証明書応答の認証に使用される証明書の階層構造は、aliasの新しい証明書チェーンを形成します。信頼チェーンが確立されない場合、証明書応答はインポートされません。この場合、\fBkeytool\fRコマンドは証明書を出力せず、ユーザーに検証を求めるプロンプトを表示します。ユーザーが証明書応答の信頼性を判断するのは非常に難しいためです。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +証明書応答がPKCS#7形式の証明書チェーンまたは一連のX\&.509証明書である場合、チェーンは、ユーザーの証明書が最初に、0以上のCA証明書がその次にくるように並べられます。チェーンが自己署名のルートCA証明書で終わり、\fB \-trustcacerts\fRオプションが指定されている場合、\fBkeytool\fRコマンドは、その証明書と、キーストア内または\fBcacerts\fRキーストア・ファイル内の信頼できるすべての証明書を照合しようとします。チェーンが自己署名のルートCA証明書で終わっておらず、\fB\-trustcacerts\fRオプションが指定されている場合、\fBkeytool\fRコマンドは、キーストア内または\fBcacerts\fRキーストア・ファイル内の信頼できる証明書から自己署名のルートCA証明書を見つけてそれをチェーンの末尾に追加しようとします。その証明書が見つからず、\fB\-noprompt\fRオプションが指定されていない場合は、チェーン内の最後の証明書の情報が出力され、ユーザーは確認を求められます。 +.RE +.PP +証明書応答内の公開鍵が\fBalias\fRですでに格納されているユーザーの公開鍵に一致した場合、古い証明書チェーンが応答内の新しい証明書チェーンで置き換えられます。以前の証明書チェーンを有効な\fBkeypass\fRで置き換えることができるのは、エントリの秘密鍵を保護するためのパスワードを指定した場合のみです。パスワードを指定しておらず、秘密鍵のパスワードがキーストアのパスワードと異なる場合は、秘密鍵のパスワードの入力を求められます。 +.PP +このコマンドは、以前のリリースでは\fB\-import\fRという名前でした。このリリースでは、引き続き古い名前がサポートされています。今後は、新しい名前\fB\-importcert\fRが優先されます。 +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jar(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jarsigner(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +http://docs\&.oracle\&.com/javase/tutorial/security/index\&.htmlにある +「コース: Java SEのセキュリティ機能」 +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/orbd.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/orbd.1 new file mode 100644 index 00000000..a4e9d365 --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/orbd.1 @@ -0,0 +1,285 @@ +'\" t +.\" Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: orbd +.\" Language: Japanese +.\" Date: 2013年11月21日 +.\" SectDesc: Java IDLおよびRMI-IIOPツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "orbd" "1" "2013年11月21日" "JDK 8" "Java IDLおよびRMI-IIOPツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +orbd \- CORBA環境のサーバーにある永続オブジェクトをクライアントから検索して呼び出せるようにします。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBorbd\fR [ \fIoptions\fR ] +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +コマンド行オプション。オプションを参照してください。 +.RE +.SH "説明" +.PP +\fBorbd\fRコマンドを使用すると、CORBA環境のサーバーにある永続オブジェクトをクライアントから透過的に検索して呼び出すことができます。orbdツールに含まれるサーバー・マネージャを使用すると、クライアントはCORBA環境でサーバー上にある永続オブジェクトを透過的に検索して呼び出すことができます。永続サーバーは、ネーミング・サービスに永続オブジェクト参照を発行する際、サーバーのポート番号のかわりにORBDのポート番号をオブジェクト参照に含めます。永続オブジェクト参照のオブジェクト参照にORBDポート番号を含めることには、次のような利点があります。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +ネーミング・サービスにあるオブジェクト参照が、サーバーのライフ・サイクルと無関係になります。たとえば、オブジェクト参照は、初めてインストールされたときはネーミング・サービスのサーバーによってネーミング・サービスに発行されますが、その後は、サーバーの開始またはシャットダウンの回数にかかわらず、呼び出したクライアントにORBDが正しいオブジェクト参照を返します。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +クライアントは一度のみネーミング・サービスのオブジェクト参照をルックアップする必要がありますが、その後はサーバーのライフ・サイクルによる変更とは無関係にこの参照を利用することができます。 +.RE +.PP +ORBDのサーバー・マネージャにアクセスするには、\fBservertool\fRを使用してサーバーを起動する必要があります。servertoolは、アプリケーション・プログラマが、永続サーバーの登録、登録解除、起動および停止を行うためのコマンド行インタフェースです。サーバー・マネージャの詳細は、サーバー・マネージャを参照してください。 +.PP +\fBorbd\fRを起動すると、ネーミング・サービスも起動されます。ネーミング・サービスの詳細。ネーミング・サービスの起動と停止を参照してください。 +.SH "オプション" +.PP +\-ORBInitialPort \fInameserverport\fR +.RS 4 +必須。ネーム・サーバーを起動するポートの番号を指定します。\fBorbd\fRは、起動されると、このポート上で着信リクエストをリスニングします。Oracle Solarisソフトウェアでは、1024より小さいポートでプロセスを開始する場合、rootユーザーになる必要があります。このため、1024以上のポート番号を使用することをお薦めします。 +.RE +.SS "必須でないオプション" +.PP +\-port \fIport\fR +.RS 4 +ORBDを起動するポートを指定します。このポートで、永続オブジェクトに対するリクエストをORBDが受け取ります。このポートのデフォルト値は1049です。このポート番号は、永続Interoperable Object References (IOR)のポート・フィールドに追加されます。 +.RE +.PP +\-defaultdb \fIdirectory\fR +.RS 4 +ORBD永続格納ディレクトリ\fBorb\&.db\fRが作成されるベース・ディレクトリを指定します。このオプションが指定されていない場合、デフォルト値は\fB\&./orb\&.db\fRになります。 +.RE +.PP +\-serverPollingTime \fImilliseconds\fR +.RS 4 +\fBservertool\fRを使用して登録された永続サーバーが正常に動作していることをORBDが確認する間隔を指定します。デフォルト値は1000ミリ秒です。\fBmilliseconds\fRに指定する値は、有効な正の整数にする必要があります。 +.RE +.PP +\-serverStartupDelay milliseconds +.RS 4 +\fBservertool\fRを使用して登録された永続サーバーを再起動してから、位置転送の例外を送信するまでのORBDの待機時間を指定します。デフォルト値は1000ミリ秒です。\fBmilliseconds\fRに指定する値は、有効な正の整数にする必要があります。 +.RE +.PP +\-J\fIoption\fR +.RS 4 +Java Virtual Machineに\fBoption\fRを渡します。\fBoption\fRには、Javaアプリケーション起動ツールのリファレンス・ページに記載されているオプションを1つ指定します。たとえば、\fB\-J\-Xms48m\fRと指定すると、スタートアップ・メモリーは48MBに設定されます。java(1)を参照してください。 +.RE +.SS "ネーミング・サービスの起動と停止" +.PP +ネーミング・サービスは、CORBAオブジェクトにネーミングを可能にするCORBAサービスです。ネーミングは名前をオブジェクト参照にバインドすることにより可能になります。ネーム・バインディングをネーミング・サービスに格納すれば、クライアントが名前を指定して目的のオブジェクト参照を取得できるようになります。 +.PP +クライアントまたはサーバーを実行する前に、ORBDを起動します。ORBDには、永続ネーミング・サービスおよび一時ネーミング・サービスが組み込まれています。これらはどちらもCOSネーミング・サービスの実装です。 +.PP +永続ネーミング・サービスは、ネーミング・コンテキストに対して永続性を提供します。つまり、この情報は、サービスの停止や起動後にも維持され、サービスに障害が発生した場合でも回復できます。ORBDを再起動すると、永続ネーミング・サービスはネーミング・コンテキストのグラフを復元し、すべてのクライアントとサーバーの名前のバインディングがそのまま(永続的に)保持されるようにします。 +.PP +後方互換性のため、旧リリースのJDKに同梱されていた一時ネーミング・サービス\fBtnameserv\fRが、今回のリリースのJava SEにも同梱されています。一時ネーム・サービスでは、ネーム・サービスの実行中にのみネーミング・コンテキストが保持されます。サービスが中断されると、ネーミング・コンテキスト・グラフは失われます。 +.PP +\fB\-ORBInitialPort\fR引数は、\fBorbd\fRの必須のコマンド行引数で、ネーミング・サービスが実行されるポートの番号を設定するために使用されます。次の手順では、Java IDL Object Request Broker Daemon用にポート1050を使用できることを前提としています。Oracle Solarisソフトウェアを使用する場合、1024より小さいポートでプロセスを開始するには、rootユーザーになる必要があります。このため、1024以上のポート番号を使用することをお薦めします。必要であれば別のポートに変更してください。 +.PP +Solaris、LinuxまたはOS Xコマンド・シェルから\fBorbd\fRを開始するには、次のように入力します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBorbd \-ORBInitialPort 1050&\fR + +.fi +.if n \{\ +.RE +.\} +.PP +WindowsのMS\-DOSシステム・プロンプトでは、次のように入力します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBstart orbd \-ORBInitialPort 1050\fR + +.fi +.if n \{\ +.RE +.\} +.PP +これでORBDが実行され、サーバーとクライアントのアプリケーションを実行できるようになります。クライアントとサーバーのアプリケーションは、実行時に、ネーミング・サービスが実行されているポートの番号(必要な場合はさらにマシン名)を認識している必要があります。これを実現する1つの方法は、次のコードをアプリケーションに追加することです。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBProperties props = new Properties();\fR +\fBprops\&.put("org\&.omg\&.CORBA\&.ORBInitialPort", "1050");\fR +\fBprops\&.put("org\&.omg\&.CORBA\&.ORBInitialHost", "MyHost");\fR +\fBORB orb = ORB\&.init(args, props);\fR + +.fi +.if n \{\ +.RE +.\} +.PP +この例では、ネーミング・サービスは、ホスト\fBMyHost\fRのポート1050上で実行されます。別の方法として、コマンド行からサーバーまたはクライアントのアプリケーションを実行するときに、ポート番号またはマシン名あるいはその両方を指定する方法もあります。たとえば、次のコマンド行オプションを使用して、\fBHelloApplication\fRを起動できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava HelloApplication \-ORBInitialPort 1050 \-ORBInitialHost MyHost\fR + +.fi +.if n \{\ +.RE +.\} +.PP +ネーミング・サービスを停止するには、適切なオペレーティング・システム・コマンドを使用します。たとえば、Oracle Solaris上で\fBpkill\fR +\fBorbd\fRを実行したり、\fBorbd\fRが動作中のDOSウィンドウで\fB[Ctrl]+[C]\fRキーを押します。一時ネーミング・サービスの場合は、サービスが終了されると、ネーミング・サービスに登録された名前が消去される場合があります。Java IDLネーム・サービスは、明示的に停止されるまで実行されます。 +.PP +ORBDに付属するネーミング・サービスの詳細は、 +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlNaming\&.htmlの「Naming Service」を参照してください +.SH "サーバー・マネージャ" +.PP +ORBDのサーバー・マネージャにアクセスして、永続サーバーを実行するには、\fBservertool\fRを使用してサーバーを起動する必要があります。servertoolは、アプリケーション・プログラマが、永続サーバーの登録、登録解除、起動および停止を行うためのコマンド行インタフェースです。\fBservertool\fRを使用してサーバーを起動する場合は、\fBorbd\fRが実行されている場所と同じポートとホストで起動する必要があります。サーバーを異なるポートで実行すると、ローカル・コンテキスト用にデータベースに保存されている情報が無効になり、サービスが正しく動作しません。 +.PP +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlExample\&.htmlの +「Java IDL: The "Hello World" Example」を参照してください +.PP +この例では、チュートリアルの手順に従って\fBidlj\fRコンパイラと\fBjavac\fRコンパイラを実行します。ORBDのサーバー・マネージャを実行するには、次の手順に従ってアプリケーションを実行します。 +.PP +\fBorbd\fRを起動します。 +.PP +Solaris、LinuxまたはOS Xコマンド・シェルで、次のように入力します: +\fBorbd \-ORBInitialPort 1050\fR +.PP +MS\-DOSシステム・プロンプト(Windows)で次のように入力します: +\fBs\fR\fBtart orbd \-ORBInitialPort 105\fR\fB0\fR +.PP +ポート1050はネーム・サーバーを実行するポートです。\fB\-ORBInitialPort\fRオプションは必須コマンド行引数です。Oracle Solarisソフトウェアを使用する場合、1024より小さいポートでプロセスを開始するには、rootユーザーになる必要があります。このため、1024以上のポート番号を使用することをお薦めします。 +.PP +\fBservertool\fRを起動します: +\fBservertool \-ORBInitialPort 1050\fR。 +.PP +前回の手順とネーム・サーバー(\fBorbd\fR)のポートが同じであることを確認します。たとえば\fB\-ORBInitialPort 1050\&.\fRのようになります。\fBservertool\fRは、ネーム・サーバーと同じポート上で起動する必要があります。 +.PP +\fBservertool\fRコマンド行インタフェースで、\fBservertool\fRプロンプトから\fBHello\fRサーバーを起動します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBservertool > register \-server HelloServer \-classpath \&. \-applicationName\fR +\fB HelloServerApName\fR + +.fi +.if n \{\ +.RE +.\} +.PP +\fBservertool\fRによってサーバーが登録されて、\fBHelloServerApName\fRという名前がサーバーに割り当てられ、登録されているすべてのサーバー一覧とともにサーバーIDが表示されます。他の端末ウィンドウまたはプロンプトからクライアント・アプリケーションを実行します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava HelloClient \-ORBInitialPort 1050 \-ORBInitialHost localhost\fR + +.fi +.if n \{\ +.RE +.\} +.PP +この例の\fB\-ORBInitialHost localhost\fRは省略することができます。ネーム・サーバーが\fBHello\fRクライアントとして同一ホスト上で動作しているからです。ネーム・サーバーが別のホストで実行されている場合は、\-\fBORBInitialHost nameserverhost\fRオプションを使用してIDLネーム・サーバーが実行されているホストを指定します。前の手順で行われたとおりにネーム・サーバー(\fBorbd\fR)ポートを指定します(例: +\fB\-ORBInitialPort 1050\fR)。ORBDのサーバー・マネージャの操作が終了したら、ネーム・サーバー(\fBorbd\fR)と\fBservertool\fRを停止するか終了してください。MS\-DOSプロンプトで\fBorbd\fRをシャットダウンするには、サーバーを実行しているウィンドウを選択して\fB[Ctrl]+[C]\fRキーを押します。 +.PP +Oracle Solarisから\fBorbd\fRをシャットダウンするには、プロセスを検索して、\fBkill\fRコマンドで終了します。サーバーを明示的に停止するまでは、呼出し待機状態が続きます。\fBservertool\fRをシャットダウンするには、\fBquit\fRと入力してキーボードの\fB[Enter]\fRキーを押します。 +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +servertool(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlNaming\&.htmlの +「Naming Service」 +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/pack200.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/pack200.1 new file mode 100644 index 00000000..153e0fb4 --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/pack200.1 @@ -0,0 +1,373 @@ +'\" t +.\" Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: pack200 +.\" Language: Japanese +.\" Date: 2013年11月21日 +.\" SectDesc: Javaデプロイメント・ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "pack200" "1" "2013年11月21日" "JDK 8" "Javaデプロイメント・ツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +pack200 \- WebデプロイメントのためにJARファイルをpack200圧縮ファイルにパッケージします。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpack200\fR [\fIoptions\fR] \fIoutput\-file\fR \fIJAR\-file\fR +.fi +.if n \{\ +.RE +.\} +.PP +オプションは任意の順序で指定できます。コマンド行またはpropertiesファイルに指定された最後のオプションが、それ以前に指定されたすべてのオプションより優先されます。 +.PP +\fIoptions\fR +.RS 4 +コマンド行オプション。オプションを参照してください。 +.RE +.PP +\fIoutput\-file\fR +.RS 4 +出力ファイルの名前。 +.RE +.PP +\fIJAR\-file\fR +.RS 4 +入力ファイルの名前。 +.RE +.SH "説明" +.PP +\fBpack200\fRコマンドは、Java gzipコンプレッサを使用してJARファイルをpack200圧縮ファイルに変換するJavaアプリケーションです。pack200ファイルは高圧縮のファイルで、直接デプロイでき、帯域幅の節約やダウンロード時間の短縮が可能です。 +.PP +\fBpack200\fRコマンドには、圧縮エンジンの設定や微調整を行うオプションがあります。一般的な使用方法を次の例に示します。\fBmyarchive\&.pack\&.gz\fRがデフォルトの\fBpack200\fRコマンド設定で作成されます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpack200 myarchive\&.pack\&.gz myarchive\&.jar\fR + +.fi +.if n \{\ +.RE +.\} +.SH "オプション" +.PP +\-r +.br +\-\-repack +.RS 4 +JARファイルをパックした後アンパックして、JARファイルを生成します。生成されたファイルは\fBjarsigner\fR(1)ツールの入力ファイルとして使用できます。次の例では、myarchive\&.jarファイルをパックした後、アンパックします。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpack200 \-\-repack myarchive\-packer\&.jar myarchive\&.jar\fR +\fBpack200 \-\-repack myarchive\&.jar\fR + +.fi +.if n \{\ +.RE +.\} +次の例では、入力ファイル内のファイルの順序を保持します。 +.RE +.PP +\-g +.br +\-\-no\-gzip +.RS 4 +\fBpack200\fRファイルを生成します。このオプションを指定するときは、適切な圧縮ツールを使用する必要があります。また、ターゲット・システムでは、対応する圧縮解除ツールを使用する必要があります。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpack200 \-\-no\-gzip myarchive\&.pack myarchive\&.jar\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-G +.br +\-\-strip\-debug +.RS 4 +出力からデバッグ属性を削除します。これには、\fBSourceFile\fR、\fBLineNumberTable\fR、\fBLocalVariableTable\fR、\fBLocalVariableTypeTable\fRが含まれます。これらの属性を削除すれば、ダウンロードとインストールのサイズは小さくなりますが、デバッガの機能は制限されます。 +.RE +.PP +\-\-keep\-file\-order +.RS 4 +入力ファイル内のファイルの順序を保持します。これは、デフォルトの動作です。 +.RE +.PP +\-O +.br +\-\-no\-keep\-file\-order +.RS 4 +パック・ツールは、すべての要素を並べ替えて送信します。パック・ツールは、JARディレクトリ名を削除してダウンロード・サイズを削減することもできます。ただし、インデックスなど、特定のJARファイルの最適化機能が正常に動作しなくなることがあります。 +.RE +.PP +\-S\fIvalue\fR +.br +\-\-segment\-limit=\fIvalue\fR +.RS 4 +この値は、各アーカイブ・セグメントの予想ターゲット・サイズ\fIN\fR +(バイト単位)です。単一の入力ファイルの必要サイズが\fIN\fRバイトを超えると、固有のアーカイブ・セグメントが提供されます。特殊なケースとして、値が\fB\-1\fRの場合は、すべての入力ファイルを含む大きな単一のセグメントが生成され、値が0の場合は、クラスごとにセグメントが1つずつ生成されます。アーカイブ・セグメントが大きくなると、断片化が少なくなり圧縮率が高くなりますが、その処理には多くのメモリーが必要です。 +.sp +各セグメントのサイズは、セグメントに変換されるそれぞれの入力ファイルのサイズのほか、その名前と他の送信されるプロパティのサイズを計算して推測されます。 +.sp +デフォルトは\-1です。つまり、パック・ツールは単一のセグメント出力ファイルを作成します。極端に大きな出力ファイルが生成される場合には、入力ファイルをセグメント化(分割)してより小さなJARにすることを強くお薦めします。 +.sp +この制限が課されていない10 MBのJARパック・ファイルは通常、約10%小さくパックされます。しかし、パック・ツールでより大きなJavaヒープ(セグメントの制限の約10倍)を必要とする場合もあります。 +.RE +.PP +\-E\fIvalue\fR +.br +\-\-effort=\fIvalue\fR +.RS 4 +単一の10進数値を指定した場合、パック・ツールは、指定された圧縮率でアーカイブを圧縮します。レベル1の場合は、比較的短い圧縮時間で多少大きめのファイルが生成されますが、レベル9の場合は、非常に長い時間がかかるものの、より圧縮率の高いファイルが生成されます。特殊な値0を指定した場合は、\fBpack200\fRコマンドは元のJARファイルを圧縮なしで直接コピーします。JSR 200標準では、すべての解凍プログラムが、この特別な場合をアーカイブ全体のパススルーと解釈するように規定しています。 +.sp +デフォルトは5です。この場合、標準的な時間で適切な圧縮が行われます。 +.RE +.PP +\-H\fIvalue\fR +.br +\-\-deflate\-hint=\fIvalue\fR +.RS 4 +入力情報を保存するというデフォルト値をオーバーライドします。転送されるアーカイブのサイズは大きくなる場合があります。指定可能な値は、\fBtrue\fR、\fBfalse\fRまたは\fBkeep\fRです。 +.sp +\fBvalue\fRが\fBtrue\fRまたはfalseの場合、\fBpacker200\fRコマンドは指定に従ってデフレーション・ヒントを出力アーカイブに設定します。アーカイブ要素の個々のデフレーション・ヒントは転送されません。 +.sp +\fBkeep\fR値は、入力JARで確認されたデフレーション・ヒントを保持します。これがデフォルトです。 +.RE +.PP +\-m\fIvalue\fR +.br +\-\-modification\-time=\fIvalue\fR +.RS 4 +指定可能な値は\fBlatest\fRと\fBkeep\fRです。 +.sp +値が最新の場合、パック・ツールは、元のアーカイブの使用可能なすべてのエントリのうちの最終更新時刻か、そのセグメントの使用可能なすべてのエントリの最終更新時刻を特定しようとします。この単一の値はセグメントの一部として転送され、各セグメントの全エントリに適用されます。この場合、すべてのインストール・ファイルに単一の日付が設定されるという問題はありますが、アーカイブの転送サイズを少し小さくすることができます。 +.sp +値が\fBkeep\fRの場合、入力JARで確認された変更時間が保持されます。これがデフォルトです。 +.RE +.PP +\-P\fIfile\fR +.br +\-\-pass\-file=\fIfile\fR +.RS 4 +ファイルを圧縮せず、バイト単位で転送するように指定します。このオプションを繰返し使用して、複数のファイルを指定できます。システム・ファイル・セパレータがJARファイル・セパレータのスラッシュ(/)に置き換えられる点を除き、パス名の変換は行われません。結果として得られるファイル名は、文字列として正確にJARファイルでの出現と一致している必要があります。\fBfile\fRにディレクトリ名を指定した場合、そのディレクトリ内のすべてのファイルが転送されます。 +.RE +.PP +\-U\fIaction\fR +.br +\-\-unknown\-attribute=\fIaction\fR +.RS 4 +デフォルトの動作をオーバーライドします。つまり、不明な属性を含むクラス・ファイルが、指定した\fBaction\fRによって渡されます。アクションとして指定可能な値は、\fBerror\fR、\fBstrip\fRまたは\fBpass\fRです。 +.sp +値が\fBerror\fRの場合、\fBpack200\fRコマンド操作全体が失敗して適切な説明が表示されます。 +.sp +値が\fBstrip\fRの場合、属性は削除されます。Java Virtual Machine (JVM)必須属性を削除すると、クラス・ローダーの障害が発生することがあります。 +.sp +値が\fBpass\fRの場合、クラス全体が1つのリソースとして転送されます。 +.RE +.PP +\-C\fIattribute\-name\fR=\fIlayout\fR +.br +\-\-class\-attribute=\fIattribute\-name\fR=\fIaction\fR +.RS 4 +次のオプションを参照してください。 +.RE +.PP +\-F\fIattribute\-name\fR=\fIlayout\fR +.br +\-\-field\-attribute=\fIattribute\-name\fR=\fIaction\fR +.RS 4 +次のオプションを参照してください。 +.RE +.PP +\-M\fIattribute\-name\fR=\fIlayout\fR +.br +\-\-method\-attribute=\fIattribute\-name\fR=\fIaction\fR +.RS 4 +次のオプションを参照してください。 +.RE +.PP +\-D\fIattribute\-name\fR=\fIlayout\fR +.br +\-\-code\-attribute=\fIattribute\-name\fR=\fIaction\fR +.RS 4 +前述の4つのオプションでは、クラス・エンティティに\fBclass\-attribute\fR、\fBfield\-attribute\fR、\fBmethod\-attribute\fRおよび\fBcode\-attribute\fRなどの属性のレイアウトを指定できます。\fIattribute\-name\fRには、これからレイアウトまたはアクションを定義する属性の名前を指定します。\fIaction\fRとして指定可能な値は、\fBsome\-layout\-string\fR、\fBerror\fR、\fBstrip\fR、\fBpass\fRです。 +.sp +\fBsome\-layout\-string\fR: レイアウト言語はJSR 200仕様で定義されています。例: +\fB\-\-class\-attribute=SourceFile=RUH\fR。 +.sp +値が\fBerror\fRの場合、\fBpack200\fR操作が失敗して説明が表示されます。 +.sp +値が\fBstrip\fRの場合、属性が出力から削除されます。VM必須属性を削除するとクラス・ローダーの障害が発生することがあります。たとえば、\fB\-\-class\-attribute=CompilationID=pass\fRというこの属性を含むクラス・ファイルを転送します。パック・ツールは、その他のアクションを行いません。 +.sp +値が\fBpass\fRの場合、クラス全体が1つのリソースとして転送されます。 +.RE +.PP +\-f \fIpack\&.properties\fR +.br +\-\-config\-file=\fIpack\&.properties\fR +.RS 4 +コマンド行に、パック・ツールを初期化するためのJavaプロパティが含まれている構成ファイルを指定できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpack200 \-f pack\&.properties myarchive\&.pack\&.gz myarchive\&.jar\fR +\fBmore pack\&.properties\fR +\fB# Generic properties for the packer\&.\fR +\fBmodification\&.time=latest\fR +\fBdeflate\&.hint=false\fR +\fBkeep\&.file\&.order=false\fR +\fB# This option will cause the files bearing new attributes to\fR +\fB# be reported as an error rather than passed uncompressed\&.\fR +\fBunknown\&.attribute=error\fR +\fB# Change the segment limit to be unlimited\&.\fR +\fBsegment\&.limit=\-1\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-v +.br +\-\-verbose +.RS 4 +最小限のメッセージを出力します。このオプションを複数指定すると、より長いメッセージが作成されます。 +.RE +.PP +\-q +.br +\-\-quiet +.RS 4 +メッセージを表示せずに動作するように指定します。 +.RE +.PP +\-l\fIfilename\fR +.br +\-\-log\-file=\fIfilename\fR +.RS 4 +出力メッセージのログ・ファイルを指定します。 +.RE +.PP +\-? +.br +\-h +.br +\-\-help +.RS 4 +このコマンドに関するヘルプ情報を出力します。 +.RE +.PP +\-V +.br +\-\-version +.RS 4 +このコマンドに関するバージョン情報を出力します。 +.RE +.PP +\-J\fIoption\fR +.RS 4 +指定されたオプションをJava Virtual Machineに渡します。詳細は、java(1)コマンドのリファレンス・ページを参照してください。たとえば、\fB\-J\-Xms48m\fRと指定すると、スタートアップ・メモリーは48MBに設定されます。 +.RE +.SH "終了ステータス" +.PP +次の終了値が返されます: 正常終了の場合は0、エラーが発生した場合は0より大きい値。 +.SH "注意" +.PP +このコマンドと\fBpack\fR(1)を混同しないでください。\fBpack\fRおよび\fBpack200\fRコマンドは、別々の製品です。 +.PP +JDKに付属するJava SE API仕様との相違が見つかった場合には、仕様を優先してください。 +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +unpack200(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jar(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jarsigner(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/policytool.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/policytool.1 new file mode 100644 index 00000000..a755d63c --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/policytool.1 @@ -0,0 +1,177 @@ +'\" t +.\" Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: policytool +.\" Language: Japanese +.\" Date: 2015年3月3日 +.\" SectDesc: セキュリティ・ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "policytool" "1" "2015年3月3日" "JDK 8" "セキュリティ・ツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +policytool \- ユーティリティGUI経由で取得したユーザー入力に基づいて、プレーン・テキストのポリシー・ファイルを読み書きします。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpolicytool\fR [ \fB\-file\fR ] [ \fIfilename\fR ] +.fi +.if n \{\ +.RE +.\} +.PP +\-file +.RS 4 +ポリシー・ファイルを読み込むように\fBpolicytool\fRに指示します。 +.RE +.PP +\fIfilename\fR +.RS 4 +ロードするファイルの名前。 +.RE +.PP +\fB例\fR: +.PP +ポリシー・ツール管理ユーティリティを実行します: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpolicytool\fR + +.fi +.if n \{\ +.RE +.\} +.PP +\fBpolicytool\fRコマンドを実行し、指定されたファイルをロードします: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpolicytool \-file \fR\fB\fImypolicyfile\fR\fR + +.fi +.if n \{\ +.RE +.\} +.SH "説明" +.PP +\fBpolicytool\fRコマンドは、管理者のGUIを呼び出します。これにより、システム管理者はローカル・ポリシー・ファイルの内容を管理できるようになります。ポリシー・ファイルは\fB\&.policy\fR拡張子を持つプレーンテキスト・ファイルで、ドメイン別にリモート・リクエスタを権限オブジェクトにマップします。詳細は、http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/PolicyFiles\&.htmlにある +「Default Policy Implementation and Policy File Syntax」を参照してください +.SH "オプション" +.PP +\-file +.RS 4 +ポリシー・ファイルを読み込むように\fBpolicytool\fRに指示します。 +.RE +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +「Default Policy Implementation and Policy File Syntax」 +(http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/PolicyFiles\&.html) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +「Policy File Creation and Management」 +(http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/PolicyGuide\&.html) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +「Permissions in Java SE Development Kit (JDK)」 +(http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/permissions\&.html) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +「Java Security Overview」 +(http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/overview/jsoverview\&.html) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +「Java Cryptography Architecture (JCA) Reference Guide」 +(http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec\&.html) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/rmid.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/rmid.1 new file mode 100644 index 00000000..3eb8cfa2 --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/rmid.1 @@ -0,0 +1,373 @@ +'\" t +.\" Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: rmid +.\" Language: Japanese +.\" Date: 2013年11月21日 +.\" SectDesc: Remote Method Invocation (RMI)ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "rmid" "1" "2013年11月21日" "JDK 8" "Remote Method Invocation (RMI)" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +rmid \- 起動システム・デーモンを開始すると、オブジェクトをJava Virtual Machine(VM)に登録してアクティブ化できるようになります。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmid\fR [\fIoptions\fR] +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +コマンド行オプション。オプションを参照してください。 +.RE +.SH "説明" +.PP +\fBrmid\fRコマンドは、起動システム・デーモンを開始します。起動システム・デーモンを開始してからでないと、アクティブ化可能オブジェクトをアクティブ化システムに登録したり、JVM内でアクティブ化したりすることができません。アクティブ化可能なオブジェクトを使用するプログラムの作成方法の詳細は、\fIアクティブ化の使用\fRに関するチュートリアル(http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/rmi/activation/overview\&.html)を参照してください +.PP +\fBrmid\fRコマンドを実行し、次のようにセキュリティ・ポリシー・ファイルを指定して、デーモンを起動します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmid \-J\-Djava\&.security\&.policy=rmid\&.policy\fR + +.fi +.if n \{\ +.RE +.\} +.PP +\fBrmid\fRコマンドのOracleの実装を実行する場合、デフォルトでは、セキュリティ・ポリシー・ファイルを指定する必要があります。それは、\fBrmid\fRコマンドが起動グループ用にJVMを起動するために各\fBActivationGroupDesc\fR内の情報を使用できるかどうかを検証できるようにするためです特に、\fBActivationGroupDesc\fRコンストラクタに渡される\fBCommandEnvironment\fRや任意のプロパティによって指定されるコマンドおよびオプションは、\fBrmid\fRコマンドのセキュリティ・ポリシー・ファイルの中で明示的に許可することが必要になりました。\fBsun\&.rmi\&.activation\&.execPolicy\fRプロパティの値は、起動グループ用にJVMを起動するために\fBActivationGroupDesc\fR内の情報を使用できるかどうかを判断するときに\fBrmid\fRコマンドが使用するポリシーを決定します。詳細は、\-J\-Dsun\&.rmi\&.activation\&.execPolicy=policyオプションの説明を参照してください。 +.PP +\fBrmid\fRコマンドを実行すると、デフォルト・ポート1098でアクティベータと内部レジストリが起動され、\fBActivationSystem\fRがこの内部レジストリ内の名前\fBjava\&.rmi\&.activation\&.ActivationSystem\fRにバインドされます。 +.PP +レジストリに他のポートを指定するには、\fBrmid\fRコマンドの実行時に\fB\-port\fRオプションを指定する必要があります。たとえば、次のコマンドは、レジストリのデフォルト・ポート1099で、起動システム・デーモンとレジストリを起動します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmid \-J\-Djava\&.security\&.policy=rmid\&.policy \-port 1099\fR + +.fi +.if n \{\ +.RE +.\} +.SH "必要に応じてRMIDを開始" +.PP +\fBrmid\fRをコマンド行から開始するには、\fBinetd\fR(Oracle Solarisの場合)、または\fBxinetd\fR(Linuxの場合)を構成して\fBrmid\fRを必要に応じて開始する方法もあります。 +.PP +RMIDを開始すると、\fBSystem\&.inheritedChannel\fRメソッドを呼び出して、継承されたチャンネル(\fBinetd\fR/\fBxinetd\fRから継承)を取得しようとします。継承されたチャンネルがnullであるか、\fBjava\&.nio\&.channels\&.ServerSocketChannel\fRのインスタンスでなかった場合、RMIDはそのチャンネルは\fBinetd\fR/\fBxinetd\fRによって起動されたものではないと判断し、前述のように起動します。 +.PP +継承されたチャンネルが\fBServerSocketChannel\fRインスタンスである場合は、RMIDはエクスポートするリモート・オブジェクト、つまり\fBjava\&.rmi\&.activation\&.ActivationSystem\fRがバインドされているレジストリと\fBjava\&.rmi\&.activation\&.Activator\fRリモート・オブジェクトに対するリクエストを受信するサーバー・ソケットとして、\fBServerSocketChannel\fRから取得した\fBjava\&.net\&.ServerSocket\fRを使用します。このモードでは、RMIDの動作は、次のことを除いて、コマンド行から起動した場合と同じです。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBSystem\&.err\fRに対する出力は、ファイルにリダイレクトされる。このファイルは\fBjava\&.io\&.tmpdir\fRシステム・プロパティで指定されるディレクトリ(通常は\fB/var/tmp\fRまたは\fB/tmp\fR)にある。ファイル名の接頭辞は\fBrmid\-err\fRで、接尾辞は\fBtmp\fRである。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB\-port\fRオプションは使用できません。このオプションが指定されている場合、RMIDはエラー・メッセージが表示されて終了します。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB\-log\fRオプションは必須。このオプションが指定されていない場合、RMIDはエラー・メッセージが表示されて終了します。 +.RE +.PP +必要に応じてサービスを開始するように構成する方法の詳細は、\fBinetd\fR +(Oracle Solarisの場合)、または\fBxinetd\fR +(Linux)のマニュアル・ページを参照してください。 +.SH "オプション" +.PP +\-C\fIオプション\fR +.RS 4 +\fBrmid\fRコマンドの子プロセス(起動グループ)が作成されたときに、それぞれの子プロセスにコマンド行引数として渡されるオプションを指定します。たとえば、次のように指定すると、起動システム・デーモンによって生成される各仮想マシンにプロパティを渡すことができます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmid \-C\-Dsome\&.property=value\fR + +.fi +.if n \{\ +.RE +.\} +コマンド行引数を子プロセスに渡す機能は、デバッグを行う場合に便利です。たとえば、次のコマンドでは、すべての子JVMでserver\-callロギングが可能です。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmid \-C\-Djava\&.rmi\&.server\&.logCalls=true\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-J\fIoption\fR +.RS 4 +RMIDを実行しているJavaインタプリタに渡すオプションを指定します。たとえば、\fBrmid\fRコマンドが\fBrmid\&.policy\fRという名前のポリシー・ファイルを使用するように指定するには、\fBrmid\fRのコマンド行で\fB\-J\fRオプションを使用して、\fBjava\&.security\&.policy\fRプロパティを定義します。次に例を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmid \-J\-Djava\&.security\&.policy\-rmid\&.policy\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-J\-Dsun\&.rmi\&.activation\&.execPolicy=\fIpolicy\fR +.RS 4 +起動グループが実行されることになるJVMの起動に使用するコマンドおよびコマンド行オプションをチェックするために、RMIDが採用するポリシーを指定します。このオプションは、Java RMI起動デーモンのOracleの実装のみに存在することに注意してください。コマンド行にこのプロパティを指定しない場合、結果は\fB\-J\-Dsun\&.rmi\&.activation\&.execPolicy=default\fRを指定した場合と同じになります。\fBpolicy\fRに指定可能な値は、\fBdefault\fR、\fBpolicyClassName\fRまたは\fBnone\fRです。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +デフォルト +.sp +\fBdefault\fRまたは未指定値の\fBexecPolicy\fRの場合、\fBrmid\fRコマンドが実行できるのは、\fBrmid\fRコマンドが使用するセキュリティ・ポリシー・ファイルの中で、実行する権限が\fBrmid\fRに与えられているコマンドおよびコマンド行オプションのみです。デフォルトの実行ポリシーで使用できるのは、デフォルトの起動グループ実装のみです。 +.sp +\fBrmid\fRコマンドは、起動グループ用のJVMを起動するときに、そのグループについて登録された起動グループ記述子である\fBActivationGroupDesc\fR内の情報を使用します。グループ記述子は、\fBActivationGroupDesc\&.CommandEnvironment\fRを指定します(省略可能)。これには、起動グループを開始するコマンドと、そのコマンド行に追加できるコマンド行オプションが含まれています。デフォルトでは、\fBrmid\fRコマンドは\fBjava\&.home\fRにある\fBjava\fRコマンドを使用します。グループ記述子には、コマンド行にオプションとして追加されるプロパティ・オーバーライドも含まれます(\fB\-D=\fRとして定義されます)。\fBcom\&.sun\&.rmi\&.rmid\&.ExecPermission\fR権限は\fBrmid\fRコマンドに、起動グループを開始するためにグループ記述子の\fBCommandEnvironment\fRで指定されたコマンドを実行する権限を付与します。\fBcom\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fR権限は起動グループの開始時に、グループ記述子でプロパティ・オーバーライドとして、または\fBCommandEnvironment\fRでオプションとして指定されたコマンド行オプションを、\fBrmid\fRコマンドが使用することを許可します。\fBrmid\fRコマンドに様々なコマンドおよびオプションを実行する権限を付与する場合、権限\fBExecPermission\fRおよび\fBExecOptionPermission\fRをすべてのコード・ソースに付与する必要があります。 +.sp +\fBExecPermission\fR +.sp +\fBExecPermission\fRクラスは、起動グループを開始するために\fBrmid\fRコマンドが特定のコマンドを実行する権限を表します。 +.sp +\fB構文\fR: +\fBExecPermission\fRの名前は、\fBrmid\fRコマンドに実行を許可するコマンドのパス名です。スラッシュ(/)およびアスタリスク(*)で終わるパス名は、そのディレクトリに含まれるすべてのファイルを示します。スラッシュはファイル区切り文字\fBFile\&.separatorChar\fRです。スラッシュ(/)およびマイナス符号(\-)で終わるパス名は、そのディレクトリに含まれるすべてのファイルとサブディレクトリ(再帰的に)を示します。特殊なトークン\fB<>\fRで構成されるパス名は、どのファイルとも一致します。 +.sp +パス名にアスタリスク(*)を指定した場合は、現在のディレクトリ内のすべてのファイルを示します。パス名にマイナス符号(\-)を指定した場合は、現在のディレクトリ内のすべてのファイルおよび(再帰的に)現在のディレクトリに含まれるすべてのファイルとサブディレクトリを示します。 +.sp +\fBExecOptionPermission\fR +.sp +\fBExecOptionPermission\fRクラスは、起動グループを開始するときに\fBrmid\fRコマンドで特定のコマンド行オプションを使用できる権限を表します。\fBExecOptionPermission\fRの名前は、コマンド行オプションの値です。 +.sp +\fB構文\fR: オプションでは、ワイルドカードが限定的にサポートされます。アスタリスクは、ワイルドカード・マッチを表します。アスタリスク(*)は、オプション名そのものとして使用できます。つまり、任意のオプションを表すことができます。また、オプション名の末尾に使用することもできます。ただし、ドット(\&.)か等号(=)の直後にアスタリスク(*)を指定する必要があります。 +.sp +例: +\fB*\fRや\fB\-Dmydir\&.*\fRや\fB\-Da\&.b\&.c=*\fRは有効ですが、\fB*mydir\fRや\fB\-Da*b\fRや\fBab*\fRは無効です。 +.sp +\fBrmidのポリシー・ファイル\fR +.sp +\fBrmid\fRコマンドに様々なコマンドおよびオプションを実行する権限を許可する場合は、権限\fBExecPermission\fRおよび\fBExecOptionPermission\fRをすべてのコード・ソースに付与する必要があります(汎用的に)。これらの権限をチェックするのは\fBrmid\fRコマンドのみなので、これらの権限を汎用的に付与しても安全です。 +.sp +\fBrmid\fRコマンドに各種の実行権限を付与するポリシー・ファイルの例を、次に示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBgrant {\fR +\fB permission com\&.sun\&.rmi\&.rmid\&.ExecPermission\fR +\fB "/files/apps/java/jdk1\&.7\&.0/solaris/bin/java";\fR +\fB \fR +\fB permission com\&.sun\&.rmi\&.rmid\&.ExecPermission\fR +\fB "/files/apps/rmidcmds/*";\fR +\fB \fR +\fB permission com\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fR +\fB "\-Djava\&.security\&.policy=/files/policies/group\&.policy";\fR +\fB \fR +\fB permission com\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fR +\fB "\-Djava\&.security\&.debug=*";\fR +\fB \fR +\fB permission com\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fR +\fB "\-Dsun\&.rmi\&.*";\fR +\fB};\fR + +.fi +.if n \{\ +.RE +.\} +最初に付与されている権限は、\fBrmid\fRコマンドに対し、パス名により明示的に指定される\fBjava\fRコマンドの1\&.7\&.0リリースの実行を許可します。デフォルトでは、\fBjava\&.home\fRにあるバージョンの\fBjava\fRコマンドを使用します。\fBrmid\fRコマンドが使用するのと同じバージョンが使用されるため、そのコマンドは、ポリシー・ファイルで指定する必要はありません。2番目の権限は、\fBrmid\fRコマンドに対して、ディレクトリ\fB/files/apps/rmidcmds\fR内の任意のコマンドの実行権限を許可します。 +.sp +3番目に付与されている権限\fBExecOptionPermission\fRは、\fBrmid\fRコマンドに対して、セキュリティ・ポリシー・ファイルを\fB/files/policies/group\&.policy\fRとして定義している起動グループの開始を許可します。次の権限は、起動グループが\fBjava\&.security\&.debug property\fRを使用することを許可しています。最後の権限は、起動グループが\fBsun\&.rmi property\fR名の階層内の任意のプロパティを使用することを許可しています。 +.sp +ポリシー・ファイルを指定して\fBrmid\fRコマンドを起動するには、\fBrmid\fRのコマンド行で\fBjava\&.security\&.policy\fRプロパティを指定する必要があります。次に例を示します。 +.sp +\fBrmid \-J\-Djava\&.security\&.policy=rmid\&.policy\fR\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} + +.sp +デフォルトの動作では十分な柔軟性が得られない場合、管理者は、\fBrmid\fRの起動時に、\fBcheckExecCommand\fRメソッドが所属するクラスの名前を指定して、\fBrmid\fRコマンドが実行するコマンドをチェックすることができます。 +.sp +\fBpolicyClassName\fRには、引数なしのコンストラクタを持ち、次のような\fBcheckExecCommand\fRメソッドを実装しているpublicクラスを指定します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB public void checkExecCommand(ActivationGroupDesc desc, String[] command)\fR +\fB throws SecurityException;\fR + +.fi +.if n \{\ +.RE +.\} +起動グループを開始する前に、\fBrmid\fRコマンドは、ポリシーの\fBcheckExecCommand\fRメソッドを呼び出します。このとき、起動グループの記述子と、起動グループを開始するための完全なコマンドを含む配列をそのメソッドに渡します。\fBcheckExecCommand\fRが\fBSecurityException\fRをスローすると、\fBrmid\fRコマンドはその起動グループを開始せず、オブジェクトの起動を試行している呼出し側には\fBActivationException\fRがスローされます。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +none +.sp +\fBsun\&.rmi\&.activation\&.execPolicy\fRプロパティの値が\fBnone\fRの場合、\fBrmid\fRコマンドは、起動グループを開始するコマンドをまったく検証しません。 +.RE +.RE +.PP +\-log \fIdir\fR +.RS 4 +起動システム・デーモンがデータベースおよび関連情報を書き込むのに使用するディレクトリの名前を指定します。デフォルトでは、\fBrmid\fRコマンドを実行したディレクトリに、logというログ・ディレクトリが作成されます。 +.RE +.PP +\-port \fIport\fR +.RS 4 +レジストリが使用するポートを指定します。起動システム・デーモンは、このレジストリの中で、\fBjava\&.rmi\&.activation\&.ActivationSystem\fRという名前で\fBActivationSystem\fRをバインドします。ローカル・マシン上の\fBActivationSystem\fRは、次のように\fBNaming\&.lookup\fRメソッドを呼び出すことによって取得できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBimport java\&.rmi\&.*; \fR +\fB import java\&.rmi\&.activation\&.*;\fR +\fB \fR +\fB ActivationSystem system; system = (ActivationSystem)\fR +\fB Naming\&.lookup("//:port/java\&.rmi\&.activation\&.ActivationSystem");\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-stop +.RS 4 +\fB\-port\fRオプションによって指定されたポートの、現在の\fBrmid\fRコマンドの呼出しを停止します。ポートが指定されていない場合は、このオプションはポート1098で実行されている\fBrmid\fRの呼出しを停止します。 +.RE +.SH "環境変数" +.PP +CLASSPATH +.RS 4 +ユーザー定義クラスへのパスをシステムに指定します。ディレクトリはコロンで区切られます。例: +\fB\&.:/usr/local/java/classes\fR +.RE +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +java(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +クラス・パスの設定 +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/rmiregistry.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/rmiregistry.1 new file mode 100644 index 00000000..9512dc5d --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/rmiregistry.1 @@ -0,0 +1,135 @@ +'\" t +.\" Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: rmiregistry +.\" Language: Japanese +.\" Date: 2013年11月21日 +.\" SectDesc: Remote Method Invocation (RMI)ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "rmiregistry" "1" "2013年11月21日" "JDK 8" "Remote Method Invocation (RMI)" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +rmiregistry \- 現在のホストの指定したポート上にリモート・オブジェクト・レジストリを開始します。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmiregistry\fR [ \fIport\fR ] +.fi +.if n \{\ +.RE +.\} +.PP +\fIport\fR +.RS 4 +リモート・オブジェクト・レジストリを開始する現在のホスト上の\fBport\fRの数。 +.RE +.SH "説明" +.PP +\fBrmiregistry\fRコマンドは、現在のホストの指定したポート上にリモート・オブジェクト・レジストリを作成し、開始します。portの指定を省略した場合、レジストリはポート1099で開始します。\fBrmiregistry\fRコマンドに、出力機能はありません。通常、これはバックグラウンドで実行されます。次に例を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBrmiregistry &\fR + +.fi +.if n \{\ +.RE +.\} +.PP +リモート・オブジェクト・レジストリは、ブートストラップのネーム・サービスです。同一ホストのRMIサーバーが、リモート・オブジェクトを名前にバインドするために使用されます。次に、ローカルおよびリモート・ホストのクライアントはリモート・オブジェクトを検索し、リモート・メソッドの呼出しを行います。 +.PP +レジストリは、一般的に、最初のリモート・オブジェクトの位置を指定します。そこで、アプリケーションはメソッドを呼び出す必要があります。その後、そのオブジェクトはアプリケーション指定のサポートを提供し、他のオブジェクトを探します。 +.PP +\fBjava\&.rmi\&.registry\&.LocateRegistry\fRクラスのメソッドは、ローカル・ホスト、またはローカル・ホストとポートで動作するレジストリを取得するために使用されます。 +.PP +\fBjava\&.rmi\&.Naming\fRクラスのURLベース・メソッドはレジストリに対して操作を実行し、任意のホストおよびローカル・ホストでのリモート・オブジェクトの検索に使用できます。単純名(文字列)をリモート・オブジェクトにバインドし、新しい名前をリモート・オブジェクトに再バインドし(古いバインドをオーバーライド)、リモート・オブジェクトをアンバインドし、レジストリにバインドされているURLをリスト表示します。 +.SH "オプション" +.PP +\-J +.RS 4 +Javaオプションとともに使用して、\fB\-J\fRの後ろに続くオプションをJavaインタプリタに引き渡します(\fB\-J\fRとオプションの間にスペースは入れません)。 +.RE +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +java(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB「java\&.rmi\&.registry\&.LocateRegistry」\fR(http://docs\&.oracle\&.com/javase/8/docs/api/java/rmi/registry/LocateRegistry\&.html) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB「java\&.rmi\&.Naming class description」\fR(http://docs\&.oracle\&.com/javase/8/docs/api/java/rmi/Naming\&.html) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/servertool.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/servertool.1 new file mode 100644 index 00000000..6456e743 --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/servertool.1 @@ -0,0 +1,201 @@ +'\" t +.\" Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: servertool +.\" Language: Japanese +.\" Date: 2013年11月21日 +.\" SectDesc: Java IDLおよびRMI-IIOPツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "servertool" "1" "2013年11月21日" "JDK 8" "Java IDLおよびRMI-IIOPツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +servertool \- 開発者が永続サーバーを登録、登録解除、起動、停止するための使いやすいユーザー・インタフェースを提供します。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBservertool\fR \-ORBInitialPort \fInameserverport\fR [ \fIoptions\fR ] [ \fIcommands \fR] +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +コマンド行オプション。オプションを参照してください。 +.RE +.PP +commands +.RS 4 +コマンド行コマンド。コマンドを参照してください。 +.RE +.PP +\fBservertool\fR\fBservertool >\fR\fBservertool >\fR +.SH "説明" +.PP +\fBservertool\fRコマンドは、開発者が永続サーバーを登録、登録解除、起動、停止するためのコマンド行インタフェースを提供します。コマンド行コマンドを使用すると、サーバーに関する様々な統計情報を取得できます。コマンドを参照してください。 +.SH "オプション" +.PP +\-ORBInitialHost \fInameserverhost\fR +.RS 4 +このオプションは必須です。ネーム・サーバーが実行され、着信リクエストをリスニングするホスト・マシンを指定します。\fBnameserverhost\fR値は、\fBorb\fRが実行され、リクエストをリスニングしているポートを指定する必要があります。このオプションを指定しない場合、値はデフォルトで\fBlocalhost\fRに設定されます。\fBorbd\fRと\fBservertool\fRが異なるマシン上で実行されている場合は、\fBorbd\fRが実行されているホストの名前またはIPアドレスを指定する必要があります。 +.sp +\fB注意:\fR +Oracle Solarisでは、1024より小さいポート上でプロセスを開始するには、rootユーザーになる必要があります。\fBnameserverport\fR値には、1024以上のポート番号を使用することをお薦めします。 +.RE +.PP +\-J\fIoption\fR +.RS 4 +Java Virtual Machineに\fBoption\fRを渡します。\fBoption\fRには、Javaアプリケーション起動ツールのリファレンス・ページに記載されているオプションを1つ指定します。たとえば、\fB\-J\-Xms48m\fRと指定すると、スタートアップ・メモリーは48MBに設定されます。java(1)を参照してください。 +.RE +.SH "コマンド" +.PP +\fBservertool\fRコマンドは、コマンド行コマンドを使用して、または使用せずに起動できます。 +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBservertool\fRの起動時にコマンドを指定しなかった場合、コマンド行ツールにコマンド入力を求める\fBservertool\fRプロンプトが表示されます: +\fBservertool >\fR。 +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBservertool\fRの起動時にコマンドを指定した場合、Java IDL Server Toolが起動してコマンドを実行し、終了します。 +.RE +.PP +register \-server \fIserver\-class\-name\fR \-classpath \fIclasspath\-to\-server\fR [ \-applicationName \fIapplication\-name\fR \-args \fIargs\-to\-server\fR \-vmargs \fIflags\-for\-JVM\fR ] +.RS 4 +Object Request Broker Daemon (ORBD)に新規永続サーバーを登録します。サーバーが未登録の場合、登録してアクティブ化します。このコマンドによって、\fB\-server\fRオプションで識別されるサーバーの\fBメイン\fR・クラス内でインストール・メソッドが呼び出されます。このインストール・メソッドは、\fBpublic static void install(org\&.omg\&.CORBA\&.ORB)\fRになっている必要があります。インストール・メソッドはオプションであり、開発者はデータベース・スキーマの作成など独自のサーバー・インストール動作を指定できます。 +.RE +.PP +unregister \-serverid \fIserver\-id\fR | \-applicationName \fIapplication\-name\fR +.RS 4 +サーバーIDまたはアプリケーション名で、サーバーをORBDから登録解除します。このコマンドによって、\fB\-server\fRオプションで識別されるサーバーの\fBメイン\fR・クラス内でアンインストール・メソッドが呼び出されます。 +\fBuninstall\fRメソッドは、\fBpublic static void uninstall(org\&.omg\&.CORBA\&.ORB)\fRになっている必要があります。\fBuninstall\fRメソッドはオプションであり、開発者は\fBinstall\fRメソッドの動作の取消など、独自のサーバー・アンインストール動作を指定できます。 +.RE +.PP +getserverid \-applicationName \fIapplication\-name\fR +.RS 4 +\fBapplication\-name\fR値に対応するサーバーIDを返します。 +.RE +.PP +list +.RS 4 +ORBDに登録されているすべての永続サーバーに関する情報を一覧表示します。 +.RE +.PP +listappnames +.RS 4 +現在ORBDに登録されているすべてのサーバーのアプリケーション名を一覧表示します。 +.RE +.PP +listactive +.RS 4 +ORBDによって起動され、現在実行されているすべての永続サーバーに関する情報を一覧表示します。 +.RE +.PP +locate \-serverid \fIserver\-id\fR | \-applicationName \fIapplication\-name\fR [ \-endpointType \fIendpointType\fR ] +.RS 4 +登録されたサーバーで作成したすべてのORBの特定のタイプについてエンドポイント(ポート)を検出します。サーバーが実行されていない場合、アクティブ化されます。\fBendpointType\fR値が指定されていない場合、サーバーのORBごとに関連付けられているplainタイプまたはnon\-protectedタイプのエンドポイントが返されます。 +.RE +.PP +locateperorb \-serverid \fIserver\-id\fR | \-applicationName \fIapplication\-name\fR [ \-orbid \fIORB\-name\fR ] +.RS 4 +登録されたサーバーの特定のObject Request Broker (ORB)で登録されたエンドポイント(ポート)を検出します。サーバーが実行されていない場合、アクティブ化されます。\fBorbid\fRが指定されていない場合、デフォルト値の\fB""\fRが\fBorbid\fRに割り当てられます。ORBが空文字列の\fBorbid\fRで作成されている場合、登録したポートがすべて返されます。 +.RE +.PP +orblist \-serverid \fIserver\-id\fR | \-applicationName \fIapplication\-name\fR +.RS 4 +サーバー上に定義されたORBの\fBORBId\fRを一覧表示します。\fBORBId\fRはサーバーで作成されたORBの文字列名です。サーバーが実行されていない場合、アクティブ化されます。 +.RE +.PP +shutdown \-serverid \fIserver\-id\fR | \-applicationName application\-name +.RS 4 +ORBDに登録されたアクティブなサーバーを停止します。このコマンドの実行中に、\fB\-serverid\fRパラメータまたは\fB\-applicationName\fRパラメータで指定されたクラス内に定義された\fBshutdown\fRメソッドも呼び出されてサーバー・プロセスを停止します。 +.RE +.PP +startup \-serverid \fIserver\-id\fR | \-applicationName application\-name +.RS 4 +ORBDに登録されたサーバーを起動またはアクティブ化します。サーバーが実行されていない場合、このコマンドがサーバーを起動します。サーバーがすでに実行されている場合は、エラー・メッセージが表示されます。 +.RE +.PP +help +.RS 4 +\fBservertool\fRコマンドを介してサーバーが利用できるすべてのコマンドをリストします。 +.RE +.PP +quit +.RS 4 +\fBservertool\fRコマンドを終了します。 +.RE +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +orbd(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/tnameserv.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/tnameserv.1 new file mode 100644 index 00000000..70d4ea4a --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/tnameserv.1 @@ -0,0 +1,446 @@ +'\" t +.\" Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: tnameserv +.\" Language: Japanese +.\" Date: 2013年11月21日 +.\" SectDesc: Java IDLおよびRMI-IIOPツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "tnameserv" "1" "2013年11月21日" "JDK 8" "Java IDLおよびRMI-IIOPツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +tnameserv \- インタフェース定義言語(IDL)。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBtnameserve\fR \fB\-ORBInitialPort\fR [ \fInameserverport\fR ] +.fi +.if n \{\ +.RE +.\} +.PP +\-ORBInitialPort \fInameserverport\fR +.RS 4 +ネーミング・サービスがORBの\fBresolve_initial_references\fRメソッドと\fBlist_initial_references\fRメソッドの実装に使用するブートストラップ・プロトコルをリスニングする初期ポートです。 +.RE +.SH "説明" +.PP +Java IDLには、Object Request Broker Daemon (ORBD)が含まれます。ORBDは、ブートストラップ・サービス、一時ネーミング・サービス、永続ネーミング・サービスおよびサーバー・マネージャを含むデーモン・プロセスです。Java IDLのすべてのチュートリアルではORBDを使用していますが、一時ネーミング・サービスを使用する例では、\fBorbd\fRのかわりに\fBtnameserv\fRを使用できます。 +.PP +orbd(1)http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlNaming\&.html +にあるまたは「Naming Service」を参照してください。 +.PP +CORBAのCOS (Common Object Services)ネーミング・サービスは、ファイル・システムがファイルに対してディレクトリ構造を提供しているのと同じように、オブジェクト参照に対してツリー構造のディレクトリを提供します。Java IDLの一時ネーム・サービスである\fBtnameserv\fRは、COSネーム・サービスの仕様を単純な形で実装したものです。 +.PP +オブジェクト参照はネームスペースに名前で格納され、オブジェクト参照と名前のペアは、それぞれネーム・バインディングと呼ばれます。ネーム・バインディングはネーミング・コンテキストに組み込むことができます。ネーミング・コンテキストはネーム・バインディングであり、ファイル・システムのサブディレクトリと同じ編成機能を持ちます。すべてのバインディングは初期ネーミング・コンテキストの下に格納されます。初期ネーミング・コンテキストは、ネームスペースの唯一の永続バインディングです。Java IDLネーミング・サービス・プロセスを停止して再起動すると、残りのネームスペースは失われます。 +.PP +アプレットまたはアプリケーションからCOSネーミング・サービスを使用するためには、そのORBがネーミング・サービスが動作しているホストのポートを知っているか、そのネーミング・サービスの初期ネーミング・コンテキスト文字列にアクセスできる必要があります。ネーム・サービスは、Java IDLのネーム・サービスでもその他のCOS準拠のネーム・サービスでもかまいません。 +.SS "ネーミング・サービスの起動" +.PP +Java IDLネーム・サービスは、ネーム・サービスを使用するアプリケーションまたはアプレットより前に起動しておく必要があります。Java IDL製品をインストールすると、Java IDLネーミング・サービスを起動するスクリプト(Oracle Solaris: +\fBtnameserv\fR)または実行可能ファイル(Windows: +\fBtnameserv\&.exe\fR)が作成されます。バックグラウンドで動作するように、ネーム・サービスを起動してください。 +.PP +特に指定しない場合、Java IDLネーミング・サービスは、ORBの\fBresolve_initial_references\fRメソッドと\fBlist_initial_references methods\fRメソッドの実装に使用するブートストラップ・プロトコルに対してポート900でリスニングします。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBtnameserv \-ORBInitialPort nameserverport&\fR + +.fi +.if n \{\ +.RE +.\} +.PP +ネーム・サーバー・ポートを指定しない場合、デフォルトでポート900が使用されます。Oracle Solarisソフトウェアの実行時、1024より小さいポートでプロセスを開始する場合は、rootユーザーになる必要があります。このため、1024以上のポート番号を使用することをお薦めします。1050のように別のポートを指定し、ネーム・サービスをバックグラウンドで実行するには、Solaris、LinuxまたはOS Xコマンド・シェルで次のように入力します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBtnameserv \-ORBInitialPort 1050&\fR + +.fi +.if n \{\ +.RE +.\} +.PP +WindowsのMS\-DOSシステム・プロンプトでは、次のように入力します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBstart tnameserv \-ORBInitialPort 1050\fR + +.fi +.if n \{\ +.RE +.\} +.PP +ネーム・サーバーのクライアントには、新しいポート番号を知らせる必要があります。これを行うには、ORBオブジェクトの作成時に\fBorg\&.omg\&.CORBA\&.ORBInitialPort\fRプロパティに新しいポート番号を設定します。 +.SS "異なるホスト上でのサーバーとクライアントの実行" +.PP +Java IDLとRMI\-IIOPのほとんどのチュートリアルでは、ネーミング・サービス、サーバーおよびクライアントはすべて開発用のマシン上で実行されます。実際にデプロイメントする場合には、クライアントとサーバーを、ネーミング・サービスとは異なるホスト・マシン上で実行することが多くなります。 +.PP +クライアントとサーバーがネーム・サービスを見つけるには、クライアントとサーバーが、ネーム・サービスが実行されているポートの番号とホストを認識している必要があります。そのためには、クライアントとサーバーのファイル内の\fBorg\&.omg\&.CORBA\&.ORBInitialPort\fRプロパティと\fBorg\&.omg\&.CORBA\&.ORBInitialHost\fRプロパティをネーム・サービスが実行されているポートの番号とマシンの名前に設定します。この例は、「Getting Started Using RMI\-IIOP」 +(http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/rmi\-iiop/rmiiiopexample\&.html)に示されています +.PP +コマンド行オプション\fB\-ORBInitialPort nameserverport#\fRと\fB\-ORBInitialHost nameserverhostname\fRを使用して、クライアントとサーバーに対してネーミング・サービスを探す場所を指定することもできます。コマンド行オプションを使用してこれを行う方法の1つの例は、http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/tutorial/jidl2machines\&.htmlの「Java IDL: The Hello World Example on Two Machines」 +を参照してください +.PP +たとえば、一時ネーム・サービス\fBtnameserv\fRが、ホスト\fBnameserverhost\fRのポート1050上で実行されているとします。さらに、クライアントがホスト\fBclienthost\fR上で実行され、サーバーはホスト\fBserverhost\fR上で実行されているとします。 +.PP +ホスト\fBnameserverhost\fR上で\fBtnameserv\fRを起動します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBtnameserv \-ORBInitialPort 1050\fR + +.fi +.if n \{\ +.RE +.\} +.PP +\fBserverhost\fR上でサーバーを起動します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava Server \-ORBInitialPort 1050 \-ORBInitialHost nameserverhost\fR + +.fi +.if n \{\ +.RE +.\} +.PP + +\fBclienthost\fR上でクライアントを起動します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava Client \-ORBInitialPort 1050 \-ORBInitialHost nameserverhost\fR + +.fi +.if n \{\ +.RE +.\} +.SS "ネーミング・サービスの停止" +.PP +Java IDLネーミング・サービスを停止するには、Solaris、LinuxまたはOS Xの場合は、\fBkill\fRなどのオペレーティング・システムのコマンドを使用し、Windowsの場合は、\fB[Ctrl]+[C]\fRキーを使用します。ネーミング・サービスを明示的に停止するまでは、呼出し待機状態が続きます。サービスを終了させると、Java IDLネーム・サービスに登録されている名前は失われます。 +.SH "オプション" +.PP +\-J\fIoption\fR +.RS 4 +Java Virtual Machineに\fBoption\fRを渡します。\fBoption\fRには、Javaアプリケーション起動ツールのリファレンス・ページに記載されているオプションを1つ指定します。たとえば、\fB\-J\-Xms48m\fRと指定すると、スタートアップ・メモリーは48MBに設定されます。java(1)を参照してください。 +.RE +.SH "例" +.SS "ネームスペースへのオブジェクトの追加" +.PP +次の例では、ネームスペースに名前を追加する方法を示します。このサンプル・プログラムは、自己完結型の一時ネーム・サービス・クライアントで、次のような単純なツリーを作成するものです。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBInitial Naming Context\fR +\fB plans\fR +\fB Personal\fR +\fB calendar\fR +\fB schedule\fR + +.fi +.if n \{\ +.RE +.\} +.PP +この例で、\fBplans\fRはオブジェクト参照、\fBPersonal\fRは\fBcalendar\fRと\fBschedule\fRの2つのオブジェクト参照を含むネーミング・コンテキストです。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBimport java\&.util\&.Properties;\fR +\fBimport org\&.omg\&.CORBA\&.*;\fR +\fBimport org\&.omg\&.CosNaming\&.*;\fR +\fB \fR +\fBpublic class NameClient {\fR + +\fB public static void main(String args[]) {\fR + +\fB try {\fR + +.fi +.if n \{\ +.RE +.\} +.PP +ネーミング・サービスの起動で、\fBnameserver\fRはポート1050で起動されました。次のコードで、このポート番号をクライアント・システムに知らせます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB Properties props = new Properties();\fR +\fB props\&.put("org\&.omg\&.CORBA\&.ORBInitialPort", "1050");\fR +\fB ORB orb = ORB\&.init(args, props);\fR + +.fi +.if n \{\ +.RE +.\} +.PP +次のコードでは、初期ネーミング・コンテキストを取得し、それを\fBctx\fRに代入します。2行目では、\fBctx\fRをダミーのオブジェクト参照\fBobjref\fRにコピーします。このobjrefには、あとで様々な名前を割り当ててネームスペースに追加します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB NamingContext ctx =\fR +\fB NamingContextHelper\&.narrow(\fR +\fB orb\&.resolve_initial_references("NameService"));\fR +\fB NamingContext objref = ctx;\fR + +.fi +.if n \{\ +.RE +.\} +.PP +次のコードでは、\fBtext\fRタイプの名前\fBplans\fRを作成し、それをダミーのオブジェクト参照にバインドします。その後、\fBrebind\fRメソッドを使用して初期ネーミング・コンテキストの下に\fBplans\fRを追加しています。\fBrebind\fRメソッドを使用すれば、\fBbind\fRメソッドを使用した場合に発生する例外を発生させずに、このプログラムを何度も繰返し実行できます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB NameComponent nc1 = new NameComponent("plans", "text");\fR +\fB NameComponent[] name1 = {nc1};\fR +\fB ctx\&.rebind(name1, objref);\fR +\fB System\&.out\&.println("plans rebind successful!");\fR + +.fi +.if n \{\ +.RE +.\} +.PP +次のコードでは、\fBdirectory\fRタイプの\fBPersonal\fRというネーミング・コンテキストを作成します。その結果得られるオブジェクト参照\fBctx2\fRを\fBname\fRにバインドし、初期ネーミング・コンテキストに追加します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB NameComponent nc2 = new NameComponent("Personal", "directory");\fR +\fB NameComponent[] name2 = {nc2};\fR +\fB NamingContext ctx2 = ctx\&.bind_new_context(name2);\fR +\fB System\&.out\&.println("new naming context added\&.\&.");\fR + +.fi +.if n \{\ +.RE +.\} +.PP +残りのコードでは、ダミーのオブジェクト参照を\fBschedule\fRと\fBcalendar\fRという名前でネーミング・コンテキスト\fBPersonal\fR(\fBctx2\fR)にバインドします。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB NameComponent nc3 = new NameComponent("schedule", "text");\fR +\fB NameComponent[] name3 = {nc3};\fR +\fB ctx2\&.rebind(name3, objref);\fR +\fB System\&.out\&.println("schedule rebind successful!");\fR +\fB \fR +\fB NameComponent nc4 = new NameComponent("calender", "text");\fR +\fB NameComponent[] name4 = {nc4};\fR +\fB ctx2\&.rebind(name4, objref);\fR +\fB System\&.out\&.println("calender rebind successful!");\fR +\fB } catch (Exception e) {\fR +\fB e\&.printStackTrace(System\&.err);\fR +\fB }\fR +\fB }\fR +\fB}\fR + +.fi +.if n \{\ +.RE +.\} +.SS "ネームスペースの参照" +.PP +次のサンプル・プログラムでは、ネームスペースをブラウズする方法を示します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBimport java\&.util\&.Properties;\fR +\fBimport org\&.omg\&.CORBA\&.*;\fR +\fBimport org\&.omg\&.CosNaming\&.*;\fR +\fB \fR +\fBpublic class NameClientList {\fR + +\fB public static void main(String args[]) {\fR + +\fB try {\fR + +.fi +.if n \{\ +.RE +.\} +.PP +ネーミング・サービスの起動で、\fBnameserver\fRはポート1050で起動されました。次のコードで、このポート番号をクライアント・システムに知らせます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB Properties props = new Properties();\fR +\fB props\&.put("org\&.omg\&.CORBA\&.ORBInitialPort", "1050");\fR +\fB ORB orb = ORB\&.init(args, props);\fR + +.fi +.if n \{\ +.RE +.\} +.PP +次のコードでは、初期ネーミング・コンテキストを取得しています。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB NamingContext nc =\fR +\fB NamingContextHelper\&.narrow(\fR +\fB orb\&.resolve_initial_references("NameService"));\fR + +.fi +.if n \{\ +.RE +.\} +.PP +\fBlist\fRメソッドは、ネーミング・コンテキストのバインディングをリストします。この場合、最大1000個までのバインディングが初期ネーミング・コンテキストから\fBBindingListHolder\fRに返されます。残りのバインディングは、\fBBindingIteratorHolder\fRに返されます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB BindingListHolder bl = new BindingListHolder();\fR +\fB BindingIteratorHolder blIt= new BindingIteratorHolder();\fR +\fB nc\&.list(1000, bl, blIt);\fR + +.fi +.if n \{\ +.RE +.\} +.PP +次のコードでは、返された\fBBindingListHolder\fRからバインディングの配列を取得します。バインディングがない場合は、プログラムが終了します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB Binding bindings[] = bl\&.value;\fR +\fB if (bindings\&.length == 0) return;\fR + +.fi +.if n \{\ +.RE +.\} +.PP +残りのコードでは、バインディングに対してループ処理を行い、名前を出力します。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB for (int i=0; i < bindings\&.length; i++) {\fR +\fB \fR +\fB // get the object reference for each binding\fR +\fB org\&.omg\&.CORBA\&.Object obj = nc\&.resolve(bindings[i]\&.binding_name);\fR +\fB String objStr = orb\&.object_to_string(obj);\fR +\fB int lastIx = bindings[i]\&.binding_name\&.length\-1;\fR +\fB \fR +\fB // check to see if this is a naming context\fR +\fB if (bindings[i]\&.binding_type == BindingType\&.ncontext) {\fR +\fB System\&.out\&.println("Context: " +\fR +\fB bindings[i]\&.binding_name[lastIx]\&.id);\fR +\fB } else {\fR +\fB System\&.out\&.println("Object: " +\fR +\fB bindings[i]\&.binding_name[lastIx]\&.id);\fR +\fB }\fR +\fB }\fR +\fB } catch (Exception e) {\fR +\fB e\&.printStackTrace(System\&.err)\fR +\fB }\fR +\fB }\fR +\fB}\fR + +.fi +.if n \{\ +.RE +.\} +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +orbd(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/unpack200.1 b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/unpack200.1 new file mode 100644 index 00000000..f35b5d46 --- /dev/null +++ b/FCL/src/main/assets/java/man/ja_JP.UTF-8/man1/unpack200.1 @@ -0,0 +1,205 @@ +'\" t +.\" Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: unpack200 +.\" Language: Japanese +.\" Date: 2013年11月21日 +.\" SectDesc: Javaデプロイメント・ツール +.\" Software: JDK 8 +.\" Arch: 汎用 +.\" Part Number: E58103-01 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "unpack200" "1" "2013年11月21日" "JDK 8" "Javaデプロイメント・ツール" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "名前" +unpack200 \- \fBpack200\fR(1)で作成されたパック・ファイルを、WebデプロイメントのためにJARファイルに変換します。 +.SH "概要" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBunpack200\fR [ \fIoptions\fR ] input\-file \fIJAR\-file\fR +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +コマンド行オプション。オプションを参照してください。 +.RE +.PP +\fIinput\-file\fR +.RS 4 +入力ファイルの名前。pack200 gzipファイルかpack200ファイルを指定できます。入力ファイルには、\fBpack200\fR(1)で作成されたJARファイルを指定することもできます(手間は\fB0\fRです)。この場合、入力ファイルの内容はPack2000マーカーで出力JARファイルにコピーされます。 +.RE +.PP +\fIJAR\-file\fR +.RS 4 +出力JARファイル名。 +.RE +.SH "説明" +.PP +\fBunpack200\fRコマンドは、\fBpack200\fR\fB(1)\fRで作成されたパック・ファイルをJARファイルに変換するネイティブ実装です。一般的な使用方法は次のとおりです。次の例では、デフォルトの\fBunpack200\fRコマンド設定で、\fBmyarchive\&.jar\fRファイルが\fBmyarchive\&.pack\&.gz\fRから作成されます。 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBunpack200 myarchive\&.pack\&.gz myarchive\&.jar\fR + +.fi +.if n \{\ +.RE +.\} +.SH "オプション" +.PP +\-Hvalue \-\-deflate\-hint=\fIvalue\fR +.RS 4 +JARファイル内のすべてのエントリに\fBtrue\fR、\fBfalse\fRまたは\fBkeep\fRのデフレーションを設定します。デフォルト・モードは\fBkeep\fRです。値が\fBtrue\fRまたは\fBfalse\fR場合、\fB\-\-deflate=hint\fRオプションはデフォルトの動作をオーバーライドして、出力JARファイル内のすべてのエントリのデフレーション・モードが設定されます。 +.RE +.PP +\-r \-\-remove\-pack\-file +.RS 4 +入力パック・ファイルを削除します。 +.RE +.PP +\-v \-\-verbose +.RS 4 +最小限のメッセージが表示されます。このオプションの複数の仕様には、より詳細なメッセージが表示されます。 +.RE +.PP +\-q \-\-quiet +.RS 4 +メッセージを表示せずに動作するように指定します。 +.RE +.PP +\-lfilename \-\-log\-file=\fIfilename\fR +.RS 4 +出力メッセージが記録されるログ・ファイルを指定します。 +.RE +.PP +\-? \-h \-\-help +.RS 4 +\fBunpack200\fRコマンドに関するヘルプ情報を出力します。 +.RE +.PP +\-V \-\-version +.RS 4 +\fBunpack200\fRコマンドに関するバージョン情報を出力します。 +.RE +.PP +\-J\fIoption\fR +.RS 4 +Java Virtual Machineにoptionを渡します。\fBoption\fRには、Javaアプリケーション起動ツールのリファレンス・ページに記載されているオプションを1つ指定します。たとえば、\fB\-J\-Xms48m\fRと指定すると、スタートアップ・メモリーは48MBに設定されます。java(1)を参照してください。 +.RE +.SH "注意" +.PP +このコマンドと\fBunpack\fRコマンドを混同しないでください。これらは別製品です。 +.PP +JDKに付属するJava SE API仕様との相違が見つかった場合には、仕様を優先してください。 +.SH "終了ステータス" +.PP +次の終了値が返されます: 正常終了の場合は0、エラーが発生した場合は0より大きい値。 +.SH "関連項目" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +pack200(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jar(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jarsigner(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +「Compression Formats for Network Deployment」 + +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/jweb/networking/compression_formats\&.html +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +「Java SE Technical Documentation」 +(http://docs\&.oracle\&.com/javase/) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/java.1 b/FCL/src/main/assets/java/man/man1/java.1 new file mode 100644 index 00000000..4615bad5 --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/java.1 @@ -0,0 +1,3801 @@ +'\" t +.\" Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: java +.\" Language: English +.\" Date: 03 March 2015 +.\" SectDesc: Basic Tools +.\" Software: JDK 8 +.\" Arch: generic +.\" Part Number: E38207-04 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "java" "1" "03 March 2015" "JDK 8" "Basic Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +java \- Launches a Java application\&. +.SH "SYNOPSIS" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava\fR [\fIoptions\fR] \fIclassname\fR [\fIargs\fR] +.fi +.if n \{\ +.RE +.\} +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava\fR [\fIoptions\fR] \fB\-jar\fR \fIfilename\fR [\fIargs\fR] +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +Command\-line options separated by spaces\&. See Options\&. +.RE +.PP +\fIclassname\fR +.RS 4 +The name of the class to be launched\&. +.RE +.PP +\fIfilename\fR +.RS 4 +The name of the Java Archive (JAR) file to be called\&. Used only with the +\fB\-jar\fR +option\&. +.RE +.PP +\fIargs\fR +.RS 4 +The arguments passed to the +\fBmain()\fR +method separated by spaces\&. +.RE +.SH "DESCRIPTION" +.PP +The +\fBjava\fR +command starts a Java application\&. It does this by starting the Java Runtime Environment (JRE), loading the specified class, and calling that class\*(Aqs +\fBmain()\fR +method\&. The method must be declared +\fIpublic\fR +and +\fIstatic\fR, it must not return any value, and it must accept a +\fBString\fR +array as a parameter\&. The method declaration has the following form: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBpublic static void main(String[] args)\fR + +.fi +.if n \{\ +.RE +.\} +.PP +The +\fBjava\fR +command can be used to launch a JavaFX application by loading a class that either has a +\fBmain()\fR +method or that extends +\fBjavafx\&.application\&.Application\fR\&. In the latter case, the launcher constructs an instance of the +\fBApplication\fR +class, calls its +\fBinit()\fR +method, and then calls the +\fBstart(javafx\&.stage\&.Stage)\fR +method\&. +.PP +By default, the first argument that is not an option of the +\fBjava\fR +command is the fully qualified name of the class to be called\&. If the +\fB\-jar\fR +option is specified, its argument is the name of the JAR file containing class and resource files for the application\&. The startup class must be indicated by the +\fBMain\-Class\fR +manifest header in its source code\&. +.PP +The JRE searches for the startup class (and other classes used by the application) in three sets of locations: the bootstrap class path, the installed extensions, and the user\(cqs class path\&. +.PP +Arguments after the class file name or the JAR file name are passed to the +\fBmain()\fR +method\&. +.SH "OPTIONS" +.PP +The +\fBjava\fR +command supports a wide range of options that can be divided into the following categories: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Standard Options +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Non\-Standard Options +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Advanced Runtime Options +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Advanced JIT Compiler Options +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Advanced Serviceability Options +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Advanced Garbage Collection Options +.RE +.PP +Standard options are guaranteed to be supported by all implementations of the Java Virtual Machine (JVM)\&. They are used for common actions, such as checking the version of the JRE, setting the class path, enabling verbose output, and so on\&. +.PP +Non\-standard options are general purpose options that are specific to the Java HotSpot Virtual Machine, so they are not guaranteed to be supported by all JVM implementations, and are subject to change\&. These options start with +\fB\-X\fR\&. +.PP +Advanced options are not recommended for casual use\&. These are developer options used for tuning specific areas of the Java HotSpot Virtual Machine operation that often have specific system requirements and may require privileged access to system configuration parameters\&. They are also not guaranteed to be supported by all JVM implementations, and are subject to change\&. Advanced options start with +\fB\-XX\fR\&. +.PP +To keep track of the options that were deprecated or removed in the latest release, there is a section named Deprecated and Removed Options at the end of the document\&. +.PP +Boolean options are used to either enable a feature that is disabled by default or disable a feature that is enabled by default\&. Such options do not require a parameter\&. Boolean +\fB\-XX\fR +options are enabled using the plus sign (\fB\-XX:+\fR\fIOptionName\fR) and disabled using the minus sign (\fB\-XX:\-\fR\fIOptionName\fR)\&. +.PP +For options that require an argument, the argument may be separated from the option name by a space, a colon (:), or an equal sign (=), or the argument may directly follow the option (the exact syntax differs for each option)\&. If you are expected to specify the size in bytes, you can use no suffix, or use the suffix +\fBk\fR +or +\fBK\fR +for kilobytes (KB), +\fBm\fR +or +\fBM\fR +for megabytes (MB), +\fBg\fR +or +\fBG\fR +for gigabytes (GB)\&. For example, to set the size to 8 GB, you can specify either +\fB8g\fR, +\fB8192m\fR, +\fB8388608k\fR, or +\fB8589934592\fR +as the argument\&. If you are expected to specify the percentage, use a number from 0 to 1 (for example, specify +\fB0\&.25\fR +for 25%)\&. +.SS "Standard Options" +.PP +These are the most commonly used options that are supported by all implementations of the JVM\&. +.PP +\-agentlib:\fIlibname\fR[=\fIoptions\fR] +.RS 4 +Loads the specified native agent library\&. After the library name, a comma\-separated list of options specific to the library can be used\&. +.sp +If the option +\fB\-agentlib:foo\fR +is specified, then the JVM attempts to load the library named +\fBlibfoo\&.so\fR +in the location specified by the +\fBLD_LIBRARY_PATH\fR +system variable (on OS X this variable is +\fBDYLD_LIBRARY_PATH\fR)\&. +.sp +The following example shows how to load the heap profiling tool (HPROF) library and get sample CPU information every 20 ms, with a stack depth of 3: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-agentlib:hprof=cpu=samples,interval=20,depth=3\fR + +.fi +.if n \{\ +.RE +.\} +The following example shows how to load the Java Debug Wire Protocol (JDWP) library and listen for the socket connection on port 8000, suspending the JVM before the main class loads: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-agentlib:jdwp=transport=dt_socket,server=y,address=8000\fR + +.fi +.if n \{\ +.RE +.\} +For more information about the native agent libraries, refer to the following: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The +\fBjava\&.lang\&.instrument\fR +package description at http://docs\&.oracle\&.com/javase/8/docs/api/java/lang/instrument/package\-summary\&.html +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Agent Command Line Options in the JVM Tools Interface guide at http://docs\&.oracle\&.com/javase/8/docs/platform/jvmti/jvmti\&.html#starting +.RE +.RE +.PP +\-agentpath:\fIpathname\fR[=\fIoptions\fR] +.RS 4 +Loads the native agent library specified by the absolute path name\&. This option is equivalent to +\fB\-agentlib\fR +but uses the full path and file name of the library\&. +.RE +.PP +\-client +.RS 4 +Selects the Java HotSpot Client VM\&. The 64\-bit version of the Java SE Development Kit (JDK) currently ignores this option and instead uses the Server JVM\&. +.sp +For default JVM selection, see Server\-Class Machine Detection at +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/server\-class\&.html +.RE +.PP +\-D\fIproperty\fR=\fIvalue\fR +.RS 4 +Sets a system property value\&. The +\fIproperty\fR +variable is a string with no spaces that represents the name of the property\&. The +\fIvalue\fR +variable is a string that represents the value of the property\&. If +\fIvalue\fR +is a string with spaces, then enclose it in quotation marks (for example +\fB\-Dfoo="foo bar"\fR)\&. +.RE +.PP +\-d32 +.RS 4 +Runs the application in a 32\-bit environment\&. If a 32\-bit environment is not installed or is not supported, then an error will be reported\&. By default, the application is run in a 32\-bit environment unless a 64\-bit system is used\&. +.RE +.PP +\-d64 +.RS 4 +Runs the application in a 64\-bit environment\&. If a 64\-bit environment is not installed or is not supported, then an error will be reported\&. By default, the application is run in a 32\-bit environment unless a 64\-bit system is used\&. +.sp +Currently only the Java HotSpot Server VM supports 64\-bit operation, and the +\fB\-server\fR +option is implicit with the use of +\fB\-d64\fR\&. The +\fB\-client\fR +option is ignored with the use of +\fB\-d64\fR\&. This is subject to change in a future release\&. +.RE +.PP +\-disableassertions[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.br +\-da[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.RS 4 +Disables assertions\&. By default, assertions are disabled in all packages and classes\&. +.sp +With no arguments, +\fB\-disableassertions\fR +(\fB\-da\fR) disables assertions in all packages and classes\&. With the +\fIpackagename\fR +argument ending in +\fB\&.\&.\&.\fR, the switch disables assertions in the specified package and any subpackages\&. If the argument is simply +\fB\&.\&.\&.\fR, then the switch disables assertions in the unnamed package in the current working directory\&. With the +\fIclassname\fR +argument, the switch disables assertions in the specified class\&. +.sp +The +\fB\-disableassertions\fR +(\fB\-da\fR) option applies to all class loaders and to system classes (which do not have a class loader)\&. There is one exception to this rule: if the option is provided with no arguments, then it does not apply to system classes\&. This makes it easy to disable assertions in all classes except for system classes\&. The +\fB\-disablesystemassertions\fR +option enables you to disable assertions in all system classes\&. +.sp +To explicitly enable assertions in specific packages or classes, use the +\fB\-enableassertions\fR +(\fB\-ea\fR) option\&. Both options can be used at the same time\&. For example, to run the +\fBMyClass\fR +application with assertions enabled in package +\fBcom\&.wombat\&.fruitbat\fR +(and any subpackages) but disabled in class +\fBcom\&.wombat\&.fruitbat\&.Brickbat\fR, use the following command: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-ea:com\&.wombat\&.fruitbat\&.\&.\&. \-da:com\&.wombat\&.fruitbat\&.Brickbat MyClass\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-disablesystemassertions +.br +\-dsa +.RS 4 +Disables assertions in all system classes\&. +.RE +.PP +\-enableassertions[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.br +\-ea[:[\fIpackagename\fR]\&.\&.\&.|:\fIclassname\fR] +.RS 4 +Enables assertions\&. By default, assertions are disabled in all packages and classes\&. +.sp +With no arguments, +\fB\-enableassertions\fR +(\fB\-ea\fR) enables assertions in all packages and classes\&. With the +\fIpackagename\fR +argument ending in +\fB\&.\&.\&.\fR, the switch enables assertions in the specified package and any subpackages\&. If the argument is simply +\fB\&.\&.\&.\fR, then the switch enables assertions in the unnamed package in the current working directory\&. With the +\fIclassname\fR +argument, the switch enables assertions in the specified class\&. +.sp +The +\fB\-enableassertions\fR +(\fB\-ea\fR) option applies to all class loaders and to system classes (which do not have a class loader)\&. There is one exception to this rule: if the option is provided with no arguments, then it does not apply to system classes\&. This makes it easy to enable assertions in all classes except for system classes\&. The +\fB\-enablesystemassertions\fR +option provides a separate switch to enable assertions in all system classes\&. +.sp +To explicitly disable assertions in specific packages or classes, use the +\fB\-disableassertions\fR +(\fB\-da\fR) option\&. If a single command contains multiple instances of these switches, then they are processed in order before loading any classes\&. For example, to run the +\fBMyClass\fR +application with assertions enabled only in package +\fBcom\&.wombat\&.fruitbat\fR +(and any subpackages) but disabled in class +\fBcom\&.wombat\&.fruitbat\&.Brickbat\fR, use the following command: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-ea:com\&.wombat\&.fruitbat\&.\&.\&. \-da:com\&.wombat\&.fruitbat\&.Brickbat MyClass\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-enablesystemassertions +.br +\-esa +.RS 4 +Enables assertions in all system classes\&. +.RE +.PP +\-help +.br +\-? +.RS 4 +Displays usage information for the +\fBjava\fR +command without actually running the JVM\&. +.RE +.PP +\-jar \fIfilename\fR +.RS 4 +Executes a program encapsulated in a JAR file\&. The +\fIfilename\fR +argument is the name of a JAR file with a manifest that contains a line in the form +\fBMain\-Class:\fR\fIclassname\fR +that defines the class with the +\fBpublic static void main(String[] args)\fR +method that serves as your application\*(Aqs starting point\&. +.sp +When you use the +\fB\-jar\fR +option, the specified JAR file is the source of all user classes, and other class path settings are ignored\&. +.sp +For more information about JAR files, see the following resources: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jar(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The Java Archive (JAR) Files guide at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/jar/index\&.html +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Lesson: Packaging Programs in JAR Files at + +http://docs\&.oracle\&.com/javase/tutorial/deployment/jar/index\&.html +.RE +.RE +.PP +\-javaagent:\fIjarpath\fR[=\fIoptions\fR] +.RS 4 +Loads the specified Java programming language agent\&. For more information about instrumenting Java applications, see the +\fBjava\&.lang\&.instrument\fR +package description in the Java API documentation at http://docs\&.oracle\&.com/javase/8/docs/api/java/lang/instrument/package\-summary\&.html +.RE +.PP +\-jre\-restrict\-search +.RS 4 +Includes user\-private JREs in the version search\&. +.RE +.PP +\-no\-jre\-restrict\-search +.RS 4 +Excludes user\-private JREs from the version search\&. +.RE +.PP +\-server +.RS 4 +Selects the Java HotSpot Server VM\&. The 64\-bit version of the JDK supports only the Server VM, so in that case the option is implicit\&. +.sp +For default JVM selection, see Server\-Class Machine Detection at +http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/server\-class\&.html +.RE +.PP +\-showversion +.RS 4 +Displays version information and continues execution of the application\&. This option is equivalent to the +\fB\-version\fR +option except that the latter instructs the JVM to exit after displaying version information\&. +.RE +.PP +\-splash:\fIimgname\fR +.RS 4 +Shows the splash screen with the image specified by +\fIimgname\fR\&. For example, to show the +\fBsplash\&.gif\fR +file from the +\fBimages\fR +directory when starting your application, use the following option: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-splash:images/splash\&.gif\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-verbose:class +.RS 4 +Displays information about each loaded class\&. +.RE +.PP +\-verbose:gc +.RS 4 +Displays information about each garbage collection (GC) event\&. +.RE +.PP +\-verbose:jni +.RS 4 +Displays information about the use of native methods and other Java Native Interface (JNI) activity\&. +.RE +.PP +\-version +.RS 4 +Displays version information and then exits\&. This option is equivalent to the +\fB\-showversion\fR +option except that the latter does not instruct the JVM to exit after displaying version information\&. +.RE +.PP +\-version:\fIrelease\fR +.RS 4 +Specifies the release version to be used for running the application\&. If the version of the +\fBjava\fR +command called does not meet this specification and an appropriate implementation is found on the system, then the appropriate implementation will be used\&. +.sp +The +\fIrelease\fR +argument specifies either the exact version string, or a list of version strings and ranges separated by spaces\&. A +\fIversion string\fR +is the developer designation of the version number in the following form: +\fB1\&.\fR\fIx\fR\fB\&.0_\fR\fIu\fR +(where +\fIx\fR +is the major version number, and +\fIu\fR +is the update version number)\&. A +\fIversion range\fR +is made up of a version string followed by a plus sign (\fB+\fR) to designate this version or later, or a part of a version string followed by an asterisk (\fB*\fR) to designate any version string with a matching prefix\&. Version strings and ranges can be combined using a space for a logical +\fIOR\fR +combination, or an ampersand (\fB&\fR) for a logical +\fIAND\fR +combination of two version strings/ranges\&. For example, if running the class or JAR file requires either JRE 6u13 (1\&.6\&.0_13), or any JRE 6 starting from 6u10 (1\&.6\&.0_10), specify the following: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-version:"1\&.6\&.0_13 1\&.6* & 1\&.6\&.0_10+"\fR + +.fi +.if n \{\ +.RE +.\} +Quotation marks are necessary only if there are spaces in the +\fIrelease\fR +parameter\&. +.sp +For JAR files, the preference is to specify version requirements in the JAR file manifest rather than on the command line\&. +.RE +.SS "Non\-Standard Options" +.PP +These options are general purpose options that are specific to the Java HotSpot Virtual Machine\&. +.PP +\-X +.RS 4 +Displays help for all available +\fB\-X\fR +options\&. +.RE +.PP +\-Xbatch +.RS 4 +Disables background compilation\&. By default, the JVM compiles the method as a background task, running the method in interpreter mode until the background compilation is finished\&. The +\fB\-Xbatch\fR +flag disables background compilation so that compilation of all methods proceeds as a foreground task until completed\&. +.sp +This option is equivalent to +\fB\-XX:\-BackgroundCompilation\fR\&. +.RE +.PP +\-Xbootclasspath:\fIpath\fR +.RS 4 +Specifies a list of directories, JAR files, and ZIP archives separated by colons (:) to search for boot class files\&. These are used in place of the boot class files included in the JDK\&. +.sp +Do not deploy applications that use this option to override a class in +\fBrt\&.jar\fR, because this violates the JRE binary code license\&. +.RE +.PP +\-Xbootclasspath/a:\fIpath\fR +.RS 4 +Specifies a list of directories, JAR files, and ZIP archives separated by colons (:) to append to the end of the default bootstrap class path\&. +.sp +Do not deploy applications that use this option to override a class in +\fBrt\&.jar\fR, because this violates the JRE binary code license\&. +.RE +.PP +\-Xbootclasspath/p:\fIpath\fR +.RS 4 +Specifies a list of directories, JAR files, and ZIP archives separated by colons (:) to prepend to the front of the default bootstrap class path\&. +.sp +Do not deploy applications that use this option to override a class in +\fBrt\&.jar\fR, because this violates the JRE binary code license\&. +.RE +.PP +\-Xcheck:jni +.RS 4 +Performs additional checks for Java Native Interface (JNI) functions\&. Specifically, it validates the parameters passed to the JNI function and the runtime environment data before processing the JNI request\&. Any invalid data encountered indicates a problem in the native code, and the JVM will terminate with an irrecoverable error in such cases\&. Expect a performance degradation when this option is used\&. +.RE +.PP +\-Xcomp +.RS 4 +Forces compilation of methods on first invocation\&. By default, the Client VM (\fB\-client\fR) performs 1,000 interpreted method invocations and the Server VM (\fB\-server\fR) performs 10,000 interpreted method invocations to gather information for efficient compilation\&. Specifying the +\fB\-Xcomp\fR +option disables interpreted method invocations to increase compilation performance at the expense of efficiency\&. +.sp +You can also change the number of interpreted method invocations before compilation using the +\fB\-XX:CompileThreshold\fR +option\&. +.RE +.PP +\-Xdebug +.RS 4 +Does nothing\&. Provided for backward compatibility\&. +.RE +.PP +\-Xdiag +.RS 4 +Shows additional diagnostic messages\&. +.RE +.PP +\-Xfuture +.RS 4 +Enables strict class\-file format checks that enforce close conformance to the class\-file format specification\&. Developers are encouraged to use this flag when developing new code because the stricter checks will become the default in future releases\&. +.RE +.PP +\-Xint +.RS 4 +Runs the application in interpreted\-only mode\&. Compilation to native code is disabled, and all bytecode is executed by the interpreter\&. The performance benefits offered by the just in time (JIT) compiler are not present in this mode\&. +.RE +.PP +\-Xinternalversion +.RS 4 +Displays more detailed JVM version information than the +\fB\-version\fR +option, and then exits\&. +.RE +.PP +\-Xloggc:\fIfilename\fR +.RS 4 +Sets the file to which verbose GC events information should be redirected for logging\&. The information written to this file is similar to the output of +\fB\-verbose:gc\fR +with the time elapsed since the first GC event preceding each logged event\&. The +\fB\-Xloggc\fR +option overrides +\fB\-verbose:gc\fR +if both are given with the same +\fBjava\fR +command\&. +.sp +Example: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xloggc:garbage\-collection\&.log\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-Xmaxjitcodesize=\fIsize\fR +.RS 4 +Specifies the maximum code cache size (in bytes) for JIT\-compiled code\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default maximum code cache size is 240 MB; if you disable tiered compilation with the option +\fB\-XX:\-TieredCompilation\fR, then the default size is 48 MB: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xmaxjitcodesize=240m\fR + +.fi +.if n \{\ +.RE +.\} +This option is equivalent to +\fB\-XX:ReservedCodeCacheSize\fR\&. +.RE +.PP +\-Xmixed +.RS 4 +Executes all bytecode by the interpreter except for hot methods, which are compiled to native code\&. +.RE +.PP +\-Xmn\fIsize\fR +.RS 4 +Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery)\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. +.sp +The young generation region of the heap is used for new objects\&. GC is performed in this region more often than in other regions\&. If the size for the young generation is too small, then a lot of minor garbage collections will be performed\&. If the size is too large, then only full garbage collections will be performed, which can take a long time to complete\&. Oracle recommends that you keep the size for the young generation between a half and a quarter of the overall heap size\&. +.sp +The following examples show how to set the initial and maximum size of young generation to 256 MB using various units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xmn256m\fR +\fB\-Xmn262144k\fR +\fB\-Xmn268435456\fR + +.fi +.if n \{\ +.RE +.\} +Instead of the +\fB\-Xmn\fR +option to set both the initial and maximum size of the heap for the young generation, you can use +\fB\-XX:NewSize\fR +to set the initial size and +\fB\-XX:MaxNewSize\fR +to set the maximum size\&. +.RE +.PP +\-Xms\fIsize\fR +.RS 4 +Sets the initial size (in bytes) of the heap\&. This value must be a multiple of 1024 and greater than 1 MB\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. +.sp +The following examples show how to set the size of allocated memory to 6 MB using various units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xms6291456\fR +\fB\-Xms6144k\fR +\fB\-Xms6m\fR + +.fi +.if n \{\ +.RE +.\} +If you do not set this option, then the initial size will be set as the sum of the sizes allocated for the old generation and the young generation\&. The initial size of the heap for the young generation can be set using the +\fB\-Xmn\fR +option or the +\fB\-XX:NewSize\fR +option\&. +.RE +.PP +\-Xmx\fIsize\fR +.RS 4 +Specifies the maximum size (in bytes) of the memory allocation pool in bytes\&. This value must be a multiple of 1024 and greater than 2 MB\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default value is chosen at runtime based on system configuration\&. For server deployments, +\fB\-Xms\fR +and +\fB\-Xmx\fR +are often set to the same value\&. See the section "Ergonomics" in +\fIJava SE HotSpot Virtual Machine Garbage Collection Tuning Guide\fR +at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/gctuning/index\&.html\&. +.sp +The following examples show how to set the maximum allowed size of allocated memory to 80 MB using various units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xmx83886080\fR +\fB\-Xmx81920k\fR +\fB\-Xmx80m\fR + +.fi +.if n \{\ +.RE +.\} +The +\fB\-Xmx\fR +option is equivalent to +\fB\-XX:MaxHeapSize\fR\&. +.RE +.PP +\-Xnoclassgc +.RS 4 +Disables garbage collection (GC) of classes\&. This can save some GC time, which shortens interruptions during the application run\&. +.sp +When you specify +\fB\-Xnoclassgc\fR +at startup, the class objects in the application will be left untouched during GC and will always be considered live\&. This can result in more memory being permanently occupied which, if not used carefully, will throw an out of memory exception\&. +.RE +.PP +\-Xprof +.RS 4 +Profiles the running program and sends profiling data to standard output\&. This option is provided as a utility that is useful in program development and is not intended to be used in production systems\&. +.RE +.PP +\-Xrs +.RS 4 +Reduces the use of operating system signals by the JVM\&. +.sp +Shutdown hooks enable orderly shutdown of a Java application by running user cleanup code (such as closing database connections) at shutdown, even if the JVM terminates abruptly\&. +.sp +The JVM catches signals to implement shutdown hooks for unexpected termination\&. The JVM uses +\fBSIGHUP\fR, +\fBSIGINT\fR, and +\fBSIGTERM\fR +to initiate the running of shutdown hooks\&. +.sp +The JVM uses a similar mechanism to implement the feature of dumping thread stacks for debugging purposes\&. The JVM uses +\fBSIGQUIT\fR +to perform thread dumps\&. +.sp +Applications embedding the JVM frequently need to trap signals such as +\fBSIGINT\fR +or +\fBSIGTERM\fR, which can lead to interference with the JVM signal handlers\&. The +\fB\-Xrs\fR +option is available to address this issue\&. When +\fB\-Xrs\fR +is used, the signal masks for +\fBSIGINT\fR, +\fBSIGTERM\fR, +\fBSIGHUP\fR, and +\fBSIGQUIT\fR +are not changed by the JVM, and signal handlers for these signals are not installed\&. +.sp +There are two consequences of specifying +\fB\-Xrs\fR: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fBSIGQUIT\fR +thread dumps are not available\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +User code is responsible for causing shutdown hooks to run, for example, by calling +\fBSystem\&.exit()\fR +when the JVM is to be terminated\&. +.RE +.RE +.PP +\-Xshare:\fImode\fR +.RS 4 +Sets the class data sharing (CDS) mode\&. Possible +\fImode\fR +arguments for this option include the following: +.PP +auto +.RS 4 +Use CDS if possible\&. This is the default value for Java HotSpot 32\-Bit Client VM\&. +.RE +.PP +on +.RS 4 +Require the use of CDS\&. Print an error message and exit if class data sharing cannot be used\&. +.RE +.PP +off +.RS 4 +Do not use CDS\&. This is the default value for Java HotSpot 32\-Bit Server VM, Java HotSpot 64\-Bit Client VM, and Java HotSpot 64\-Bit Server VM\&. +.RE +.PP +dump +.RS 4 +Manually generate the CDS archive\&. Specify the application class path as described in "Setting the Class Path "\&. +.sp +You should regenerate the CDS archive with each new JDK release\&. +.RE +.RE +.PP +\-XshowSettings:\fIcategory\fR +.RS 4 +Shows settings and continues\&. Possible +\fIcategory\fR +arguments for this option include the following: +.PP +all +.RS 4 +Shows all categories of settings\&. This is the default value\&. +.RE +.PP +locale +.RS 4 +Shows settings related to locale\&. +.RE +.PP +properties +.RS 4 +Shows settings related to system properties\&. +.RE +.PP +vm +.RS 4 +Shows the settings of the JVM\&. +.RE +.RE +.PP +\-Xss\fIsize\fR +.RS 4 +Sets the thread stack size (in bytes)\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate KB, +\fBm\fR +or +\fBM\fR +to indicate MB, +\fBg\fR +or +\fBG\fR +to indicate GB\&. The default value depends on the platform: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/ARM (32\-bit): 320 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/i386 (32\-bit): 320 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/x64 (64\-bit): 1024 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +OS X (64\-bit): 1024 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/i386 (32\-bit): 320 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/x64 (64\-bit): 1024 KB +.RE +.sp +The following examples set the thread stack size to 1024 KB in different units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-Xss1m\fR +\fB\-Xss1024k\fR +\fB\-Xss1048576\fR + +.fi +.if n \{\ +.RE +.\} +This option is equivalent to +\fB\-XX:ThreadStackSize\fR\&. +.RE +.PP +\-Xusealtsigs +.RS 4 +Use alternative signals instead of +\fBSIGUSR1\fR +and +\fBSIGUSR2\fR +for JVM internal signals\&. This option is equivalent to +\fB\-XX:+UseAltSigs\fR\&. +.RE +.PP +\-Xverify:\fImode\fR +.RS 4 +Sets the mode of the bytecode verifier\&. Bytecode verification helps to troubleshoot some problems, but it also adds overhead to the running application\&. Possible +\fImode\fR +arguments for this option include the following: +.PP +none +.RS 4 +Do not verify the bytecode\&. This reduces startup time and also reduces the protection provided by Java\&. +.RE +.PP +remote +.RS 4 +Verify those classes that are not loaded by the bootstrap class loader\&. This is the default behavior if you do not specify the +\fB\-Xverify\fR +option\&. +.RE +.PP +all +.RS 4 +Verify all classes\&. +.RE +.RE +.SS "Advanced Runtime Options" +.PP +These options control the runtime behavior of the Java HotSpot VM\&. +.PP +\-XX:+CheckEndorsedAndExtDirs +.RS 4 +Enables the option to prevent the +\fBjava\fR +command from running a Java application if it uses the endorsed\-standards override mechanism or the extension mechanism\&. This option checks if an application is using one of these mechanisms by checking the following: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The +\fBjava\&.ext\&.dirs\fR +or +\fBjava\&.endorsed\&.dirs\fR +system property is set\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The +\fBlib/endorsed\fR +directory exists and is not empty\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The +\fBlib/ext\fR +directory contains any JAR files other than those of the JDK\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The system\-wide platform\-specific extension directory contains any JAR files\&. +.RE +.RE +.PP +\-XX:+DisableAttachMechanism +.RS 4 +Enables the option that disables the mechanism that lets tools attach to the JVM\&. By default, this option is disabled, meaning that the attach mechanism is enabled and you can use tools such as +\fBjcmd\fR, +\fBjstack\fR, +\fBjmap\fR, and +\fBjinfo\fR\&. +.RE +.PP +\-XX:ErrorFile=\fIfilename\fR +.RS 4 +Specifies the path and file name to which error data is written when an irrecoverable error occurs\&. By default, this file is created in the current working directory and named +\fBhs_err_pid\fR\fIpid\fR\fB\&.log\fR +where +\fIpid\fR +is the identifier of the process that caused the error\&. The following example shows how to set the default log file (note that the identifier of the process is specified as +\fB%p\fR): +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ErrorFile=\&./hs_err_pid%p\&.log\fR + +.fi +.if n \{\ +.RE +.\} +The following example shows how to set the error log to +\fB/var/log/java/java_error\&.log\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ErrorFile=/var/log/java/java_error\&.log\fR + +.fi +.if n \{\ +.RE +.\} +If the file cannot be created in the specified directory (due to insufficient space, permission problem, or another issue), then the file is created in the temporary directory for the operating system\&. The temporary directory is +\fB/tmp\fR\&. +.RE +.PP +\-XX:+FailOverToOldVerifier +.RS 4 +Enables automatic failover to the old verifier when the new type checker fails\&. By default, this option is disabled and it is ignored (that is, treated as disabled) for classes with a recent bytecode version\&. You can enable it for classes with older versions of the bytecode\&. +.RE +.PP +\-XX:LargePageSizeInBytes=\fIsize\fR +.RS 4 +On Solaris, sets the maximum size (in bytes) for large pages used for Java heap\&. The +\fIsize\fR +argument must be a power of 2 (2, 4, 8, 16, \&.\&.\&.)\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. By default, the size is set to 0, meaning that the JVM chooses the size for large pages automatically\&. +.sp +The following example illustrates how to set the large page size to 4 megabytes (MB): +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:LargePageSizeInBytes=4m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxDirectMemorySize=\fIsize\fR +.RS 4 +Sets the maximum total size (in bytes) of the New I/O (the +\fBjava\&.nio\fR +package) direct\-buffer allocations\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. By default, the size is set to 0, meaning that the JVM chooses the size for NIO direct\-buffer allocations automatically\&. +.sp +The following examples illustrate how to set the NIO size to 1024 KB in different units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxDirectMemorySize=1m\fR +\fB\-XX:MaxDirectMemorySize=1024k\fR +\fB\-XX:MaxDirectMemorySize=1048576\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:NativeMemoryTracking=\fImode\fR +.RS 4 +Specifies the mode for tracking JVM native memory usage\&. Possible +\fImode\fR +arguments for this option include the following: +.PP +off +.RS 4 +Do not track JVM native memory usage\&. This is the default behavior if you do not specify the +\fB\-XX:NativeMemoryTracking\fR +option\&. +.RE +.PP +summary +.RS 4 +Only track memory usage by JVM subsystems, such as Java heap, class, code, and thread\&. +.RE +.PP +detail +.RS 4 +In addition to tracking memory usage by JVM subsystems, track memory usage by individual +\fBCallSite\fR, individual virtual memory region and its committed regions\&. +.RE +.RE +.PP +\-XX:ObjectAlignmentInBytes=\fIalignment\fR +.RS 4 +Sets the memory alignment of Java objects (in bytes)\&. By default, the value is set to 8 bytes\&. The specified value should be a power of two, and must be within the range of 8 and 256 (inclusive)\&. This option makes it possible to use compressed pointers with large Java heap sizes\&. +.sp +The heap size limit in bytes is calculated as: +.sp +\fB4GB * ObjectAlignmentInBytes\fR +.sp +Note: As the alignment value increases, the unused space between objects will also increase\&. As a result, you may not realize any benefits from using compressed pointers with large Java heap sizes\&. +.RE +.PP +\-XX:OnError=\fIstring\fR +.RS 4 +Sets a custom command or a series of semicolon\-separated commands to run when an irrecoverable error occurs\&. If the string contains spaces, then it must be enclosed in quotation marks\&. +.sp +The following example shows how the +\fB\-XX:OnError\fR +option can be used to run the +\fBgcore\fR +command to create the core image, and the debugger is started to attach to the process in case of an irrecoverable error (the +\fB%p\fR +designates the current process): +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:OnError="gcore %p;dbx \- %p"\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:OnOutOfMemoryError=\fIstring\fR +.RS 4 +Sets a custom command or a series of semicolon\-separated commands to run when an +\fBOutOfMemoryError\fR +exception is first thrown\&. If the string contains spaces, then it must be enclosed in quotation marks\&. For an example of a command string, see the description of the +\fB\-XX:OnError\fR +option\&. +.RE +.PP +\-XX:+PerfDataSaveToFile +.RS 4 +If enabled, saves +jstat(1) binary data when the Java application exits\&. This binary data is saved in a file named +\fBhsperfdata_\fR\fI\fR, where +\fI\fR +is the process identifier of the Java application you ran\&. Use +\fBjstat\fR +to display the performance data contained in this file as follows: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjstat \-class file:///\fR\fB\fI\fR\fR\fB/hsperfdata_\fR\fB\fI\fR\fR +\fBjstat \-gc file:///\fR\fB\fI\fR\fR\fB/hsperfdata_\fR\fB\fI\fR\fR +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+PrintCommandLineFlags +.RS 4 +Enables printing of ergonomically selected JVM flags that appeared on the command line\&. It can be useful to know the ergonomic values set by the JVM, such as the heap space size and the selected garbage collector\&. By default, this option is disabled and flags are not printed\&. +.RE +.PP +\-XX:+PrintNMTStatistics +.RS 4 +Enables printing of collected native memory tracking data at JVM exit when native memory tracking is enabled (see +\fB\-XX:NativeMemoryTracking\fR)\&. By default, this option is disabled and native memory tracking data is not printed\&. +.RE +.PP +\-XX:+RelaxAccessControlCheck +.RS 4 +Decreases the amount of access control checks in the verifier\&. By default, this option is disabled, and it is ignored (that is, treated as disabled) for classes with a recent bytecode version\&. You can enable it for classes with older versions of the bytecode\&. +.RE +.PP +\-XX:+ShowMessageBoxOnError +.RS 4 +Enables displaying of a dialog box when the JVM experiences an irrecoverable error\&. This prevents the JVM from exiting and keeps the process active so that you can attach a debugger to it to investigate the cause of the error\&. By default, this option is disabled\&. +.RE +.PP +\-XX:ThreadStackSize=\fIsize\fR +.RS 4 +Sets the thread stack size (in bytes)\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default value depends on the platform: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/ARM (32\-bit): 320 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/i386 (32\-bit): 320 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Linux/x64 (64\-bit): 1024 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +OS X (64\-bit): 1024 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/i386 (32\-bit): 320 KB +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Oracle Solaris/x64 (64\-bit): 1024 KB +.RE +.sp +The following examples show how to set the thread stack size to 1024 KB in different units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ThreadStackSize=1m\fR +\fB\-XX:ThreadStackSize=1024k\fR +\fB\-XX:ThreadStackSize=1048576\fR + +.fi +.if n \{\ +.RE +.\} +This option is equivalent to +\fB\-Xss\fR\&. +.RE +.PP +\-XX:+TraceClassLoading +.RS 4 +Enables tracing of classes as they are loaded\&. By default, this option is disabled and classes are not traced\&. +.RE +.PP +\-XX:+TraceClassLoadingPreorder +.RS 4 +Enables tracing of all loaded classes in the order in which they are referenced\&. By default, this option is disabled and classes are not traced\&. +.RE +.PP +\-XX:+TraceClassResolution +.RS 4 +Enables tracing of constant pool resolutions\&. By default, this option is disabled and constant pool resolutions are not traced\&. +.RE +.PP +\-XX:+TraceClassUnloading +.RS 4 +Enables tracing of classes as they are unloaded\&. By default, this option is disabled and classes are not traced\&. +.RE +.PP +\-XX:+TraceLoaderConstraints +.RS 4 +Enables tracing of the loader constraints recording\&. By default, this option is disabled and loader constraints recording is not traced\&. +.RE +.PP +\-XX:+UseAltSigs +.RS 4 +Enables the use of alternative signals instead of +\fBSIGUSR1\fR +and +\fBSIGUSR2\fR +for JVM internal signals\&. By default, this option is disabled and alternative signals are not used\&. This option is equivalent to +\fB\-Xusealtsigs\fR\&. +.RE +.PP +\-XX:\-UseBiasedLocking +.RS 4 +Disables the use of biased locking\&. Some applications with significant amounts of uncontended synchronization may attain significant speedups with this flag enabled, whereas applications with certain patterns of locking may see slowdowns\&. For more information about the biased locking technique, see the example in Java Tuning White Paper at http://www\&.oracle\&.com/technetwork/java/tuning\-139912\&.html#section4\&.2\&.5 +.sp +By default, this option is enabled\&. +.RE +.PP +\-XX:\-UseCompressedOops +.RS 4 +Disables the use of compressed pointers\&. By default, this option is enabled, and compressed pointers are used when Java heap sizes are less than 32 GB\&. When this option is enabled, object references are represented as 32\-bit offsets instead of 64\-bit pointers, which typically increases performance when running the application with Java heap sizes less than 32 GB\&. This option works only for 64\-bit JVMs\&. +.sp +It is also possible to use compressed pointers when Java heap sizes are greater than 32GB\&. See the +\fB\-XX:ObjectAlignmentInBytes\fR +option\&. +.RE +.PP +\-XX:+UseHugeTLBFS +.RS 4 +This option for Linux is the equivalent of specifying +\fB\-XX:+UseLargePages\fR\&. This option is disabled by default\&. This option pre\-allocates all large pages up\-front, when memory is reserved; consequently the JVM cannot dynamically grow or shrink large pages memory areas; see +\fB\-XX:UseTransparentHugePages\fR +if you want this behavior\&. +.sp +For more information, see "Large Pages"\&. +.RE +.PP +\-XX:+UseLargePages +.RS 4 +Enables the use of large page memory\&. By default, this option is disabled and large page memory is not used\&. +.sp +For more information, see "Large Pages"\&. +.RE +.PP +\-XX:+UseMembar +.RS 4 +Enables issuing of membars on thread state transitions\&. This option is disabled by default on all platforms except ARM servers, where it is enabled\&. (It is recommended that you do not disable this option on ARM servers\&.) +.RE +.PP +\-XX:+UsePerfData +.RS 4 +Enables the +\fBperfdata\fR +feature\&. This option is enabled by default to allow JVM monitoring and performance testing\&. Disabling it suppresses the creation of the +\fBhsperfdata_userid\fR +directories\&. To disable the +\fBperfdata\fR +feature, specify +\fB\-XX:\-UsePerfData\fR\&. +.RE +.PP +\-XX:+UseTransparentHugePages +.RS 4 +On Linux, enables the use of large pages that can dynamically grow or shrink\&. This option is disabled by default\&. You may encounter performance problems with transparent huge pages as the OS moves other pages around to create huge pages; this option is made available for experimentation\&. +.sp +For more information, see "Large Pages"\&. +.RE +.PP +\-XX:+AllowUserSignalHandlers +.RS 4 +Enables installation of signal handlers by the application\&. By default, this option is disabled and the application is not allowed to install signal handlers\&. +.RE +.SS "Advanced JIT Compiler Options" +.PP +These options control the dynamic just\-in\-time (JIT) compilation performed by the Java HotSpot VM\&. +.PP +\-XX:+AggressiveOpts +.RS 4 +Enables the use of aggressive performance optimization features, which are expected to become default in upcoming releases\&. By default, this option is disabled and experimental performance features are not used\&. +.RE +.PP +\-XX:AllocateInstancePrefetchLines=\fIlines\fR +.RS 4 +Sets the number of lines to prefetch ahead of the instance allocation pointer\&. By default, the number of lines to prefetch is set to 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocateInstancePrefetchLines=1\fR + +.fi +.if n \{\ +.RE +.\} +Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:AllocatePrefetchDistance=\fIsize\fR +.RS 4 +Sets the size (in bytes) of the prefetch distance for object allocation\&. Memory about to be written with the value of new objects is prefetched up to this distance starting from the address of the last allocated object\&. Each Java thread has its own allocation point\&. +.sp +Negative values denote that prefetch distance is chosen based on the platform\&. Positive values are bytes to prefetch\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default value is set to \-1\&. +.sp +The following example shows how to set the prefetch distance to 1024 bytes: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchDistance=1024\fR + +.fi +.if n \{\ +.RE +.\} +Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:AllocatePrefetchInstr=\fIinstruction\fR +.RS 4 +Sets the prefetch instruction to prefetch ahead of the allocation pointer\&. Only the Java HotSpot Server VM supports this option\&. Possible values are from 0 to 3\&. The actual instructions behind the values depend on the platform\&. By default, the prefetch instruction is set to 0: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchInstr=0\fR + +.fi +.if n \{\ +.RE +.\} +Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:AllocatePrefetchLines=\fIlines\fR +.RS 4 +Sets the number of cache lines to load after the last object allocation by using the prefetch instructions generated in compiled code\&. The default value is 1 if the last allocated object was an instance, and 3 if it was an array\&. +.sp +The following example shows how to set the number of loaded cache lines to 5: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchLines=5\fR + +.fi +.if n \{\ +.RE +.\} +Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:AllocatePrefetchStepSize=\fIsize\fR +.RS 4 +Sets the step size (in bytes) for sequential prefetch instructions\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. By default, the step size is set to 16 bytes: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:AllocatePrefetchStepSize=16\fR + +.fi +.if n \{\ +.RE +.\} +Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:AllocatePrefetchStyle=\fIstyle\fR +.RS 4 +Sets the generated code style for prefetch instructions\&. The +\fIstyle\fR +argument is an integer from 0 to 3: +.PP +0 +.RS 4 +Do not generate prefetch instructions\&. +.RE +.PP +1 +.RS 4 +Execute prefetch instructions after each allocation\&. This is the default parameter\&. +.RE +.PP +2 +.RS 4 +Use the thread\-local allocation block (TLAB) watermark pointer to determine when prefetch instructions are executed\&. +.RE +.PP +3 +.RS 4 +Use BIS instruction on SPARC for allocation prefetch\&. +.RE +.sp +Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:+BackgroundCompilation +.RS 4 +Enables background compilation\&. This option is enabled by default\&. To disable background compilation, specify +\fB\-XX:\-BackgroundCompilation\fR +(this is equivalent to specifying +\fB\-Xbatch\fR)\&. +.RE +.PP +\-XX:CICompilerCount=\fIthreads\fR +.RS 4 +Sets the number of compiler threads to use for compilation\&. By default, the number of threads is set to 2 for the server JVM, to 1 for the client JVM, and it scales to the number of cores if tiered compilation is used\&. The following example shows how to set the number of threads to 2: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CICompilerCount=2\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CodeCacheMinimumFreeSpace=\fIsize\fR +.RS 4 +Sets the minimum free space (in bytes) required for compilation\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. When less than the minimum free space remains, compiling stops\&. By default, this option is set to 500 KB\&. The following example shows how to set the minimum free space to 1024 MB: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CodeCacheMinimumFreeSpace=1024m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CompileCommand=\fIcommand\fR,\fImethod\fR[,\fIoption\fR] +.RS 4 +Specifies a command to perform on a method\&. For example, to exclude the +\fBindexOf()\fR +method of the +\fBString\fR +class from being compiled, use the following: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=exclude,java/lang/String\&.indexOf\fR + +.fi +.if n \{\ +.RE +.\} +Note that the full class name is specified, including all packages and subpackages separated by a slash (\fB/\fR)\&. For easier cut and paste operations, it is also possible to use the method name format produced by the +\fB\-XX:+PrintCompilation\fR +and +\fB\-XX:+LogCompilation\fR +options: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=exclude,java\&.lang\&.String::indexOf\fR + +.fi +.if n \{\ +.RE +.\} +If the method is specified without the signature, the command will be applied to all methods with the specified name\&. However, you can also specify the signature of the method in the class file format\&. In this case, you should enclose the arguments in quotation marks, because otherwise the shell treats the semicolon as command end\&. For example, if you want to exclude only the +\fBindexOf(String)\fR +method of the +\fBString\fR +class from being compiled, use the following: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand="exclude,java/lang/String\&.indexOf,(Ljava/lang/String;)I"\fR + +.fi +.if n \{\ +.RE +.\} +You can also use the asterisk (*) as a wildcard for class and method names\&. For example, to exclude all +\fBindexOf()\fR +methods in all classes from being compiled, use the following: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=exclude,*\&.indexOf\fR + +.fi +.if n \{\ +.RE +.\} +The commas and periods are aliases for spaces, making it easier to pass compiler commands through a shell\&. You can pass arguments to +\fB\-XX:CompileCommand\fR +using spaces as separators by enclosing the argument in quotation marks: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand="exclude java/lang/String indexOf"\fR + +.fi +.if n \{\ +.RE +.\} +Note that after parsing the commands passed on the command line using the +\fB\-XX:CompileCommand\fR +options, the JIT compiler then reads commands from the +\fB\&.hotspot_compiler\fR +file\&. You can add commands to this file or specify a different file using the +\fB\-XX:CompileCommandFile\fR +option\&. +.sp +To add several commands, either specify the +\fB\-XX:CompileCommand\fR +option multiple times, or separate each argument with the newline separator (\fB\en\fR)\&. The following commands are available: +.PP +break +.RS 4 +Set a breakpoint when debugging the JVM to stop at the beginning of compilation of the specified method\&. +.RE +.PP +compileonly +.RS 4 +Exclude all methods from compilation except for the specified method\&. As an alternative, you can use the +\fB\-XX:CompileOnly\fR +option, which allows to specify several methods\&. +.RE +.PP +dontinline +.RS 4 +Prevent inlining of the specified method\&. +.RE +.PP +exclude +.RS 4 +Exclude the specified method from compilation\&. +.RE +.PP +help +.RS 4 +Print a help message for the +\fB\-XX:CompileCommand\fR +option\&. +.RE +.PP +inline +.RS 4 +Attempt to inline the specified method\&. +.RE +.PP +log +.RS 4 +Exclude compilation logging (with the +\fB\-XX:+LogCompilation\fR +option) for all methods except for the specified method\&. By default, logging is performed for all compiled methods\&. +.RE +.PP +option +.RS 4 +This command can be used to pass a JIT compilation option to the specified method in place of the last argument (\fIoption\fR)\&. The compilation option is set at the end, after the method name\&. For example, to enable the +\fBBlockLayoutByFrequency\fR +option for the +\fBappend()\fR +method of the +\fBStringBuffer\fR +class, use the following: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileCommand=option,java/lang/StringBuffer\&.append,BlockLayoutByFrequency\fR + +.fi +.if n \{\ +.RE +.\} +You can specify multiple compilation options, separated by commas or spaces\&. +.RE +.PP +print +.RS 4 +Print generated assembler code after compilation of the specified method\&. +.RE +.PP +quiet +.RS 4 +Do not print the compile commands\&. By default, the commands that you specify with the \-\fBXX:CompileCommand\fR +option are printed; for example, if you exclude from compilation the +\fBindexOf()\fR +method of the +\fBString\fR +class, then the following will be printed to standard output: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBCompilerOracle: exclude java/lang/String\&.indexOf\fR + +.fi +.if n \{\ +.RE +.\} +You can suppress this by specifying the +\fB\-XX:CompileCommand=quiet\fR +option before other +\fB\-XX:CompileCommand\fR +options\&. +.RE +.RE +.PP +\-XX:CompileCommandFile=\fIfilename\fR +.RS 4 +Sets the file from which JIT compiler commands are read\&. By default, the +\fB\&.hotspot_compiler\fR +file is used to store commands performed by the JIT compiler\&. +.sp +Each line in the command file represents a command, a class name, and a method name for which the command is used\&. For example, this line prints assembly code for the +\fBtoString()\fR +method of the +\fBString\fR +class: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBprint java/lang/String toString\fR + +.fi +.if n \{\ +.RE +.\} +For more information about specifying the commands for the JIT compiler to perform on methods, see the +\fB\-XX:CompileCommand\fR +option\&. +.RE +.PP +\-XX:CompileOnly=\fImethods\fR +.RS 4 +Sets the list of methods (separated by commas) to which compilation should be restricted\&. Only the specified methods will be compiled\&. Specify each method with the full class name (including the packages and subpackages)\&. For example, to compile only the +\fBlength()\fR +method of the +\fBString\fR +class and the +\fBsize()\fR +method of the +\fBList\fR +class, use the following: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileOnly=java/lang/String\&.length,java/util/List\&.size\fR + +.fi +.if n \{\ +.RE +.\} +Note that the full class name is specified, including all packages and subpackages separated by a slash (\fB/\fR)\&. For easier cut and paste operations, it is also possible to use the method name format produced by the +\fB\-XX:+PrintCompilation\fR +and +\fB\-XX:+LogCompilation\fR +options: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileOnly=java\&.lang\&.String::length,java\&.util\&.List::size\fR + +.fi +.if n \{\ +.RE +.\} +Although wildcards are not supported, you can specify only the class or package name to compile all methods in that class or package, as well as specify just the method to compile methods with this name in any class: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileOnly=java/lang/String\fR +\fB\-XX:CompileOnly=java/lang\fR +\fB\-XX:CompileOnly=\&.length\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CompileThreshold=\fIinvocations\fR +.RS 4 +Sets the number of interpreted method invocations before compilation\&. By default, in the server JVM, the JIT compiler performs 10,000 interpreted method invocations to gather information for efficient compilation\&. For the client JVM, the default setting is 1,500 invocations\&. This option is ignored when tiered compilation is enabled; see the option +\fB\-XX:+TieredCompilation\fR\&. The following example shows how to set the number of interpreted method invocations to 5,000: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CompileThreshold=5000\fR + +.fi +.if n \{\ +.RE +.\} +You can completely disable interpretation of Java methods before compilation by specifying the +\fB\-Xcomp\fR +option\&. +.RE +.PP +\-XX:+DoEscapeAnalysis +.RS 4 +Enables the use of escape analysis\&. This option is enabled by default\&. To disable the use of escape analysis, specify +\fB\-XX:\-DoEscapeAnalysis\fR\&. Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:InitialCodeCacheSize=\fIsize\fR +.RS 4 +Sets the initial code cache size (in bytes)\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default value is set to 500 KB\&. The initial code cache size should be not less than the system\*(Aqs minimal memory page size\&. The following example shows how to set the initial code cache size to 32 KB: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitialCodeCacheSize=32k\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+Inline +.RS 4 +Enables method inlining\&. This option is enabled by default to increase performance\&. To disable method inlining, specify +\fB\-XX:\-Inline\fR\&. +.RE +.PP +\-XX:InlineSmallCode=\fIsize\fR +.RS 4 +Sets the maximum code size (in bytes) for compiled methods that should be inlined\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. Only compiled methods with the size smaller than the specified size will be inlined\&. By default, the maximum code size is set to 1000 bytes: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InlineSmallCode=1000\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+LogCompilation +.RS 4 +Enables logging of compilation activity to a file named +\fBhotspot\&.log\fR +in the current working directory\&. You can specify a different log file path and name using the +\fB\-XX:LogFile\fR +option\&. +.sp +By default, this option is disabled and compilation activity is not logged\&. The +\fB\-XX:+LogCompilation\fR +option has to be used together with the +\fB\-XX:UnlockDiagnosticVMOptions\fR +option that unlocks diagnostic JVM options\&. +.sp +You can enable verbose diagnostic output with a message printed to the console every time a method is compiled by using the +\fB\-XX:+PrintCompilation\fR +option\&. +.RE +.PP +\-XX:MaxInlineSize=\fIsize\fR +.RS 4 +Sets the maximum bytecode size (in bytes) of a method to be inlined\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. By default, the maximum bytecode size is set to 35 bytes: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxInlineSize=35\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxNodeLimit=\fInodes\fR +.RS 4 +Sets the maximum number of nodes to be used during single method compilation\&. By default, the maximum number of nodes is set to 65,000: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxNodeLimit=65000\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxTrivialSize=\fIsize\fR +.RS 4 +Sets the maximum bytecode size (in bytes) of a trivial method to be inlined\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. By default, the maximum bytecode size of a trivial method is set to 6 bytes: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxTrivialSize=6\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+OptimizeStringConcat +.RS 4 +Enables the optimization of +\fBString\fR +concatenation operations\&. This option is enabled by default\&. To disable the optimization of +\fBString\fR +concatenation operations, specify +\fB\-XX:\-OptimizeStringConcat\fR\&. Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:+PrintAssembly +.RS 4 +Enables printing of assembly code for bytecoded and native methods by using the external +\fBdisassembler\&.so\fR +library\&. This enables you to see the generated code, which may help you to diagnose performance issues\&. +.sp +By default, this option is disabled and assembly code is not printed\&. The +\fB\-XX:+PrintAssembly\fR +option has to be used together with the +\fB\-XX:UnlockDiagnosticVMOptions\fR +option that unlocks diagnostic JVM options\&. +.RE +.PP +\-XX:+PrintCompilation +.RS 4 +Enables verbose diagnostic output from the JVM by printing a message to the console every time a method is compiled\&. This enables you to see which methods actually get compiled\&. By default, this option is disabled and diagnostic output is not printed\&. +.sp +You can also log compilation activity to a file by using the +\fB\-XX:+LogCompilation\fR +option\&. +.RE +.PP +\-XX:+PrintInlining +.RS 4 +Enables printing of inlining decisions\&. This enables you to see which methods are getting inlined\&. +.sp +By default, this option is disabled and inlining information is not printed\&. The +\fB\-XX:+PrintInlining\fR +option has to be used together with the +\fB\-XX:+UnlockDiagnosticVMOptions\fR +option that unlocks diagnostic JVM options\&. +.RE +.PP +\-XX:ReservedCodeCacheSize=\fIsize\fR +.RS 4 +Sets the maximum code cache size (in bytes) for JIT\-compiled code\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default maximum code cache size is 240 MB; if you disable tiered compilation with the option +\fB\-XX:\-TieredCompilation\fR, then the default size is 48 MB\&. This option has a limit of 2 GB; otherwise, an error is generated\&. The maximum code cache size should not be less than the initial code cache size; see the option +\fB\-XX:InitialCodeCacheSize\fR\&. This option is equivalent to +\fB\-Xmaxjitcodesize\fR\&. +.RE +.PP +\-XX:RTMAbortRatio=\fIabort_ratio\fR +.RS 4 +The RTM abort ratio is specified as a percentage (%) of all executed RTM transactions\&. If a number of aborted transactions becomes greater than this ratio, then the compiled code will be deoptimized\&. This ratio is used when the +\fB\-XX:+UseRTMDeopt\fR +option is enabled\&. The default value of this option is 50\&. This means that the compiled code will be deoptimized if 50% of all transactions are aborted\&. +.RE +.PP +\-XX:RTMRetryCount=\fInumber_of_retries\fR +.RS 4 +RTM locking code will be retried, when it is aborted or busy, the number of times specified by this option before falling back to the normal locking mechanism\&. The default value for this option is 5\&. The +\fB\-XX:UseRTMLocking\fR +option must be enabled\&. +.RE +.PP +\-XX:\-TieredCompilation +.RS 4 +Disables the use of tiered compilation\&. By default, this option is enabled\&. Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:+UseAES +.RS 4 +Enables hardware\-based AES intrinsics for Intel, AMD, and SPARC hardware\&. Intel Westmere (2010 and newer), AMD Bulldozer (2011 and newer), and SPARC (T4 and newer) are the supported hardware\&. UseAES is used in conjunction with UseAESIntrinsics\&. +.RE +.PP +\-XX:+UseAESIntrinsics +.RS 4 +UseAES and UseAESIntrinsics flags are enabled by default and are supported only for Java HotSpot Server VM 32\-bit and 64\-bit\&. To disable hardware\-based AES intrinsics, specify +\fB\-XX:\-UseAES \-XX:\-UseAESIntrinsics\fR\&. For example, to enable hardware AES, use the following flags: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:+UseAES \-XX:+UseAESIntrinsics\fR + +.fi +.if n \{\ +.RE +.\} +To support UseAES and UseAESIntrinsics flags for 32\-bit and 64\-bit use +\fB\-server\fR +option to choose Java HotSpot Server VM\&. These flags are not supported on Client VM\&. +.RE +.PP +\-XX:+UseCodeCacheFlushing +.RS 4 +Enables flushing of the code cache before shutting down the compiler\&. This option is enabled by default\&. To disable flushing of the code cache before shutting down the compiler, specify +\fB\-XX:\-UseCodeCacheFlushing\fR\&. +.RE +.PP +\-XX:+UseCondCardMark +.RS 4 +Enables checking of whether the card is already marked before updating the card table\&. This option is disabled by default and should only be used on machines with multiple sockets, where it will increase performance of Java applications that rely heavily on concurrent operations\&. Only the Java HotSpot Server VM supports this option\&. +.RE +.PP +\-XX:+UseRTMDeopt +.RS 4 +Auto\-tunes RTM locking depending on the abort ratio\&. This ratio is specified by +\fB\-XX:RTMAbortRatio\fR +option\&. If the number of aborted transactions exceeds the abort ratio, then the method containing the lock will be deoptimized and recompiled with all locks as normal locks\&. This option is disabled by default\&. The +\fB\-XX:+UseRTMLocking\fR +option must be enabled\&. +.RE +.PP +\-XX:+UseRTMLocking +.RS 4 +Generate Restricted Transactional Memory (RTM) locking code for all inflated locks, with the normal locking mechanism as the fallback handler\&. This option is disabled by default\&. Options related to RTM are only available for the Java HotSpot Server VM on x86 CPUs that support Transactional Synchronization Extensions (TSX)\&. +.sp +RTM is part of Intel\*(Aqs TSX, which is an x86 instruction set extension and facilitates the creation of multithreaded applications\&. RTM introduces the new instructions +\fBXBEGIN\fR, +\fBXABORT\fR, +\fBXEND\fR, and +\fBXTEST\fR\&. The +\fBXBEGIN\fR +and +\fBXEND\fR +instructions enclose a set of instructions to run as a transaction\&. If no conflict is found when running the transaction, the memory and register modifications are committed together at the +\fBXEND\fR +instruction\&. The +\fBXABORT\fR +instruction can be used to explicitly abort a transaction and the +\fBXEND\fR +instruction to check if a set of instructions are being run in a transaction\&. +.sp +A lock on a transaction is inflated when another thread tries to access the same transaction, thereby blocking the thread that did not originally request access to the transaction\&. RTM requires that a fallback set of operations be specified in case a transaction aborts or fails\&. An RTM lock is a lock that has been delegated to the TSX\*(Aqs system\&. +.sp +RTM improves performance for highly contended locks with low conflict in a critical region (which is code that must not be accessed by more than one thread concurrently)\&. RTM also improves the performance of coarse\-grain locking, which typically does not perform well in multithreaded applications\&. (Coarse\-grain locking is the strategy of holding locks for long periods to minimize the overhead of taking and releasing locks, while fine\-grained locking is the strategy of trying to achieve maximum parallelism by locking only when necessary and unlocking as soon as possible\&.) Also, for lightly contended locks that are used by different threads, RTM can reduce false cache line sharing, also known as cache line ping\-pong\&. This occurs when multiple threads from different processors are accessing different resources, but the resources share the same cache line\&. As a result, the processors repeatedly invalidate the cache lines of other processors, which forces them to read from main memory instead of their cache\&. +.RE +.PP +\-XX:+UseSHA +.RS 4 +Enables hardware\-based intrinsics for SHA crypto hash functions for SPARC hardware\&. +\fBUseSHA\fR +is used in conjunction with the +\fBUseSHA1Intrinsics\fR, +\fBUseSHA256Intrinsics\fR, and +\fBUseSHA512Intrinsics\fR +options\&. +.sp +The +\fBUseSHA\fR +and +\fBUseSHA*Intrinsics\fR +flags are enabled by default, and are supported only for Java HotSpot Server VM 64\-bit on SPARC T4 and newer\&. +.sp +This feature is only applicable when using the +\fBsun\&.security\&.provider\&.Sun\fR +provider for SHA operations\&. +.sp +To disable all hardware\-based SHA intrinsics, specify +\fB\-XX:\-UseSHA\fR\&. To disable only a particular SHA intrinsic, use the appropriate corresponding option\&. For example: +\fB\-XX:\-UseSHA256Intrinsics\fR\&. +.RE +.PP +\-XX:+UseSHA1Intrinsics +.RS 4 +Enables intrinsics for SHA\-1 crypto hash function\&. +.RE +.PP +\-XX:+UseSHA256Intrinsics +.RS 4 +Enables intrinsics for SHA\-224 and SHA\-256 crypto hash functions\&. +.RE +.PP +\-XX:+UseSHA512Intrinsics +.RS 4 +Enables intrinsics for SHA\-384 and SHA\-512 crypto hash functions\&. +.RE +.PP +\-XX:+UseSuperWord +.RS 4 +Enables the transformation of scalar operations into superword operations\&. This option is enabled by default\&. To disable the transformation of scalar operations into superword operations, specify +\fB\-XX:\-UseSuperWord\fR\&. Only the Java HotSpot Server VM supports this option\&. +.RE +.SS "Advanced Serviceability Options" +.PP +These options provide the ability to gather system information and perform extensive debugging\&. +.PP +\-XX:+ExtendedDTraceProbes +.RS 4 +Enables additional +\fBdtrace\fR +tool probes that impact the performance\&. By default, this option is disabled and +\fBdtrace\fR +performs only standard probes\&. +.RE +.PP +\-XX:+HeapDumpOnOutOfMemory +.RS 4 +Enables the dumping of the Java heap to a file in the current directory by using the heap profiler (HPROF) when a +\fBjava\&.lang\&.OutOfMemoryError\fR +exception is thrown\&. You can explicitly set the heap dump file path and name using the +\fB\-XX:HeapDumpPath\fR +option\&. By default, this option is disabled and the heap is not dumped when an +\fBOutOfMemoryError\fR +exception is thrown\&. +.RE +.PP +\-XX:HeapDumpPath=\fIpath\fR +.RS 4 +Sets the path and file name for writing the heap dump provided by the heap profiler (HPROF) when the +\fB\-XX:+HeapDumpOnOutOfMemoryError\fR +option is set\&. By default, the file is created in the current working directory, and it is named +\fBjava_pid\fR\fIpid\fR\fB\&.hprof\fR +where +\fIpid\fR +is the identifier of the process that caused the error\&. The following example shows how to set the default file explicitly (\fB%p\fR +represents the current process identificator): +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:HeapDumpPath=\&./java_pid%p\&.hprof\fR + +.fi +.if n \{\ +.RE +.\} +The following example shows how to set the heap dump file to +\fB/var/log/java/java_heapdump\&.hprof\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:HeapDumpPath=/var/log/java/java_heapdump\&.hprof\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:LogFile=\fIpath\fR +.RS 4 +Sets the path and file name where log data is written\&. By default, the file is created in the current working directory, and it is named +\fBhotspot\&.log\fR\&. +.sp +The following example shows how to set the log file to +\fB/var/log/java/hotspot\&.log\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:LogFile=/var/log/java/hotspot\&.log\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+PrintClassHistogram +.RS 4 +Enables printing of a class instance histogram after a +\fBControl+C\fR +event (\fBSIGTERM\fR)\&. By default, this option is disabled\&. +.sp +Setting this option is equivalent to running the +\fBjmap \-histo\fR +command, or the +\fBjcmd \fR\fIpid\fR\fB GC\&.class_histogram\fR +command, where +\fIpid\fR +is the current Java process identifier\&. +.RE +.PP +\-XX:+PrintConcurrentLocks +.RS 4 +Enables printing of locks after a event\&. By default, this option is disabled\&. +.sp +Enables printing of +\fBjava\&.util\&.concurrent\fR +locks after a +\fBControl+C\fR +event (\fBSIGTERM\fR)\&. By default, this option is disabled\&. +.sp +Setting this option is equivalent to running the +\fBjstack \-l\fR +command or the +\fBjcmd \fR\fIpid\fR\fB Thread\&.print \-l\fR +command, where +\fIpid\fR +is the current Java process identifier\&. +.RE +.PP +\-XX:+UnlockDiagnosticVMOptions +.RS 4 +Unlocks the options intended for diagnosing the JVM\&. By default, this option is disabled and diagnostic options are not available\&. +.RE +.SS "Advanced Garbage Collection Options" +.PP +These options control how garbage collection (GC) is performed by the Java HotSpot VM\&. +.PP +\-XX:+AggressiveHeap +.RS 4 +Enables Java heap optimization\&. This sets various parameters to be optimal for long\-running jobs with intensive memory allocation, based on the configuration of the computer (RAM and CPU)\&. By default, the option is disabled and the heap is not optimized\&. +.RE +.PP +\-XX:+AlwaysPreTouch +.RS 4 +Enables touching of every page on the Java heap during JVM initialization\&. This gets all pages into the memory before entering the +\fBmain()\fR +method\&. The option can be used in testing to simulate a long\-running system with all virtual memory mapped to physical memory\&. By default, this option is disabled and all pages are committed as JVM heap space fills\&. +.RE +.PP +\-XX:+CMSClassUnloadingEnabled +.RS 4 +Enables class unloading when using the concurrent mark\-sweep (CMS) garbage collector\&. This option is enabled by default\&. To disable class unloading for the CMS garbage collector, specify +\fB\-XX:\-CMSClassUnloadingEnabled\fR\&. +.RE +.PP +\-XX:CMSExpAvgFactor=\fIpercent\fR +.RS 4 +Sets the percentage of time (0 to 100) used to weight the current sample when computing exponential averages for the concurrent collection statistics\&. By default, the exponential averages factor is set to 25%\&. The following example shows how to set the factor to 15%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CMSExpAvgFactor=15\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:CMSInitiatingOccupancyFraction=\fIpercent\fR +.RS 4 +Sets the percentage of the old generation occupancy (0 to 100) at which to start a CMS collection cycle\&. The default value is set to \-1\&. Any negative value (including the default) implies that +\fB\-XX:CMSTriggerRatio\fR +is used to define the value of the initiating occupancy fraction\&. +.sp +The following example shows how to set the occupancy fraction to 20%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CMSInitiatingOccupancyFraction=20\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+CMSScavengeBeforeRemark +.RS 4 +Enables scavenging attempts before the CMS remark step\&. By default, this option is disabled\&. +.RE +.PP +\-XX:CMSTriggerRatio=\fIpercent\fR +.RS 4 +Sets the percentage (0 to 100) of the value specified by +\fB\-XX:MinHeapFreeRatio\fR +that is allocated before a CMS collection cycle commences\&. The default value is set to 80%\&. +.sp +The following example shows how to set the occupancy fraction to 75%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:CMSTriggerRatio=75\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:ConcGCThreads=\fIthreads\fR +.RS 4 +Sets the number of threads used for concurrent GC\&. The default value depends on the number of CPUs available to the JVM\&. +.sp +For example, to set the number of threads for concurrent GC to 2, specify the following option: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ConcGCThreads=2\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+DisableExplicitGC +.RS 4 +Enables the option that disables processing of calls to +\fBSystem\&.gc()\fR\&. This option is disabled by default, meaning that calls to +\fBSystem\&.gc()\fR +are processed\&. If processing of calls to +\fBSystem\&.gc()\fR +is disabled, the JVM still performs GC when necessary\&. +.RE +.PP +\-XX:+ExplicitGCInvokesConcurrent +.RS 4 +Enables invoking of concurrent GC by using the +\fBSystem\&.gc()\fR +request\&. This option is disabled by default and can be enabled only together with the +\fB\-XX:+UseConcMarkSweepGC\fR +option\&. +.RE +.PP +\-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses +.RS 4 +Enables invoking of concurrent GC by using the +\fBSystem\&.gc()\fR +request and unloading of classes during the concurrent GC cycle\&. This option is disabled by default and can be enabled only together with the +\fB\-XX:+UseConcMarkSweepGC\fR +option\&. +.RE +.PP +\-XX:G1HeapRegionSize=\fIsize\fR +.RS 4 +Sets the size of the regions into which the Java heap is subdivided when using the garbage\-first (G1) collector\&. The value can be between 1 MB and 32 MB\&. The default region size is determined ergonomically based on the heap size\&. +.sp +The following example shows how to set the size of the subdivisions to 16 MB: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:G1HeapRegionSize=16m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+G1PrintHeapRegions +.RS 4 +Enables the printing of information about which regions are allocated and which are reclaimed by the G1 collector\&. By default, this option is disabled\&. +.RE +.PP +\-XX:G1ReservePercent=\fIpercent\fR +.RS 4 +Sets the percentage of the heap (0 to 50) that is reserved as a false ceiling to reduce the possibility of promotion failure for the G1 collector\&. By default, this option is set to 10%\&. +.sp +The following example shows how to set the reserved heap to 20%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:G1ReservePercent=20\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:InitialHeapSize=\fIsize\fR +.RS 4 +Sets the initial size (in bytes) of the memory allocation pool\&. This value must be either 0, or a multiple of 1024 and greater than 1 MB\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default value is chosen at runtime based on system configuration\&. See the section "Ergonomics" in +\fIJava SE HotSpot Virtual Machine Garbage Collection Tuning Guide\fR +at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/gctuning/index\&.html\&. +.sp +The following examples show how to set the size of allocated memory to 6 MB using various units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitialHeapSize=6291456\fR +\fB\-XX:InitialHeapSize=6144k\fR +\fB\-XX:InitialHeapSize=6m\fR + +.fi +.if n \{\ +.RE +.\} +If you set this option to 0, then the initial size will be set as the sum of the sizes allocated for the old generation and the young generation\&. The size of the heap for the young generation can be set using the +\fB\-XX:NewSize\fR +option\&. +.RE +.PP +\-XX:InitialSurvivorRatio=\fIratio\fR +.RS 4 +Sets the initial survivor space ratio used by the throughput garbage collector (which is enabled by the +\fB\-XX:+UseParallelGC\fR +and/or \-\fBXX:+UseParallelOldGC\fR +options)\&. Adaptive sizing is enabled by default with the throughput garbage collector by using the +\fB\-XX:+UseParallelGC\fR +and +\fB\-XX:+UseParallelOldGC\fR +options, and survivor space is resized according to the application behavior, starting with the initial value\&. If adaptive sizing is disabled (using the +\fB\-XX:\-UseAdaptiveSizePolicy\fR +option), then the +\fB\-XX:SurvivorRatio\fR +option should be used to set the size of the survivor space for the entire execution of the application\&. +.sp +The following formula can be used to calculate the initial size of survivor space (S) based on the size of the young generation (Y), and the initial survivor space ratio (R): +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBS=Y/(R+2)\fR + +.fi +.if n \{\ +.RE +.\} +The 2 in the equation denotes two survivor spaces\&. The larger the value specified as the initial survivor space ratio, the smaller the initial survivor space size\&. +.sp +By default, the initial survivor space ratio is set to 8\&. If the default value for the young generation space size is used (2 MB), the initial size of the survivor space will be 0\&.2 MB\&. +.sp +The following example shows how to set the initial survivor space ratio to 4: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitialSurvivorRatio=4\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:InitiatingHeapOccupancyPercent=\fIpercent\fR +.RS 4 +Sets the percentage of the heap occupancy (0 to 100) at which to start a concurrent GC cycle\&. It is used by garbage collectors that trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations (for example, the G1 garbage collector)\&. +.sp +By default, the initiating value is set to 45%\&. A value of 0 implies nonstop GC cycles\&. The following example shows how to set the initiating heap occupancy to 75%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:InitiatingHeapOccupancyPercent=75\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxGCPauseMillis=\fItime\fR +.RS 4 +Sets a target for the maximum GC pause time (in milliseconds)\&. This is a soft goal, and the JVM will make its best effort to achieve it\&. By default, there is no maximum pause time value\&. +.sp +The following example shows how to set the maximum target pause time to 500 ms: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxGCPauseMillis=500\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxHeapSize=\fIsize\fR +.RS 4 +Sets the maximum size (in byes) of the memory allocation pool\&. This value must be a multiple of 1024 and greater than 2 MB\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. The default value is chosen at runtime based on system configuration\&. For server deployments, +\fB\-XX:InitialHeapSize\fR +and +\fB\-XX:MaxHeapSize\fR +are often set to the same value\&. See the section "Ergonomics" in +\fIJava SE HotSpot Virtual Machine Garbage Collection Tuning Guide\fR +at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/vm/gctuning/index\&.html\&. +.sp +The following examples show how to set the maximum allowed size of allocated memory to 80 MB using various units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxHeapSize=83886080\fR +\fB\-XX:MaxHeapSize=81920k\fR +\fB\-XX:MaxHeapSize=80m\fR + +.fi +.if n \{\ +.RE +.\} +On Oracle Solaris 7 and Oracle Solaris 8 SPARC platforms, the upper limit for this value is approximately 4,000 MB minus overhead amounts\&. On Oracle Solaris 2\&.6 and x86 platforms, the upper limit is approximately 2,000 MB minus overhead amounts\&. On Linux platforms, the upper limit is approximately 2,000 MB minus overhead amounts\&. +.sp +The +\fB\-XX:MaxHeapSize\fR +option is equivalent to +\fB\-Xmx\fR\&. +.RE +.PP +\-XX:MaxHeapFreeRatio=\fIpercent\fR +.RS 4 +Sets the maximum allowed percentage of free heap space (0 to 100) after a GC event\&. If free heap space expands above this value, then the heap will be shrunk\&. By default, this value is set to 70%\&. +.sp +The following example shows how to set the maximum free heap ratio to 75%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxHeapFreeRatio=75\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxMetaspaceSize=\fIsize\fR +.RS 4 +Sets the maximum amount of native memory that can be allocated for class metadata\&. By default, the size is not limited\&. The amount of metadata for an application depends on the application itself, other running applications, and the amount of memory available on the system\&. +.sp +The following example shows how to set the maximum class metadata size to 256 MB: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxMetaspaceSize=256m\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MaxNewSize=\fIsize\fR +.RS 4 +Sets the maximum size (in bytes) of the heap for the young generation (nursery)\&. The default value is set ergonomically\&. +.RE +.PP +\-XX:MaxTenuringThreshold=\fIthreshold\fR +.RS 4 +Sets the maximum tenuring threshold for use in adaptive GC sizing\&. The largest value is 15\&. The default value is 15 for the parallel (throughput) collector, and 6 for the CMS collector\&. +.sp +The following example shows how to set the maximum tenuring threshold to 10: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MaxTenuringThreshold=10\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:MetaspaceSize=\fIsize\fR +.RS 4 +Sets the size of the allocated class metadata space that will trigger a garbage collection the first time it is exceeded\&. This threshold for a garbage collection is increased or decreased depending on the amount of metadata used\&. The default size depends on the platform\&. +.RE +.PP +\-XX:MinHeapFreeRatio=\fIpercent\fR +.RS 4 +Sets the minimum allowed percentage of free heap space (0 to 100) after a GC event\&. If free heap space falls below this value, then the heap will be expanded\&. By default, this value is set to 40%\&. +.sp +The following example shows how to set the minimum free heap ratio to 25%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:MinHeapFreeRatio=25\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:NewRatio=\fIratio\fR +.RS 4 +Sets the ratio between young and old generation sizes\&. By default, this option is set to 2\&. The following example shows how to set the young/old ratio to 1: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:NewRatio=1\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:NewSize=\fIsize\fR +.RS 4 +Sets the initial size (in bytes) of the heap for the young generation (nursery)\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. +.sp +The young generation region of the heap is used for new objects\&. GC is performed in this region more often than in other regions\&. If the size for the young generation is too low, then a large number of minor GCs will be performed\&. If the size is too high, then only full GCs will be performed, which can take a long time to complete\&. Oracle recommends that you keep the size for the young generation between a half and a quarter of the overall heap size\&. +.sp +The following examples show how to set the initial size of young generation to 256 MB using various units: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:NewSize=256m\fR +\fB\-XX:NewSize=262144k\fR +\fB\-XX:NewSize=268435456\fR + +.fi +.if n \{\ +.RE +.\} +The +\fB\-XX:NewSize\fR +option is equivalent to +\fB\-Xmn\fR\&. +.RE +.PP +\-XX:ParallelGCThreads=\fIthreads\fR +.RS 4 +Sets the number of threads used for parallel garbage collection in the young and old generations\&. The default value depends on the number of CPUs available to the JVM\&. +.sp +For example, to set the number of threads for parallel GC to 2, specify the following option: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:ParallelGCThreads=2\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+ParallelRefProcEnabled +.RS 4 +Enables parallel reference processing\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintAdaptiveSizePolicy +.RS 4 +Enables printing of information about adaptive generation sizing\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintGC +.RS 4 +Enables printing of messages at every GC\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintGCApplicationConcurrentTime +.RS 4 +Enables printing of how much time elapsed since the last pause (for example, a GC pause)\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintGCApplicationStoppedTime +.RS 4 +Enables printing of how much time the pause (for example, a GC pause) lasted\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintGCDateStamps +.RS 4 +Enables printing of a date stamp at every GC\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintGCDetails +.RS 4 +Enables printing of detailed messages at every GC\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintGCTaskTimeStamps +.RS 4 +Enables printing of time stamps for every individual GC worker thread task\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintGCTimeStamps +.RS 4 +Enables printing of time stamps at every GC\&. By default, this option is disabled\&. +.RE +.PP +\-XX:+PrintStringDeduplicationStatistics +.RS 4 +Prints detailed deduplication statistics\&. By default, this option is disabled\&. See the +\fB\-XX:+UseStringDeduplication\fR +option\&. +.RE +.PP +\-XX:+PrintTenuringDistribution +.RS 4 +Enables printing of tenuring age information\&. The following is an example of the output: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBDesired survivor size 48286924 bytes, new threshold 10 (max 10)\fR +\fB\- age 1: 28992024 bytes, 28992024 total\fR +\fB\- age 2: 1366864 bytes, 30358888 total\fR +\fB\- age 3: 1425912 bytes, 31784800 total\fR +\fB\&.\&.\&.\fR + +.fi +.if n \{\ +.RE +.\} +Age 1 objects are the youngest survivors (they were created after the previous scavenge, survived the latest scavenge, and moved from eden to survivor space)\&. Age 2 objects have survived two scavenges (during the second scavenge they were copied from one survivor space to the next)\&. And so on\&. +.sp +In the preceding example, 28 992 024 bytes survived one scavenge and were copied from eden to survivor space, 1 366 864 bytes are occupied by age 2 objects, etc\&. The third value in each row is the cumulative size of objects of age n or less\&. +.sp +By default, this option is disabled\&. +.RE +.PP +\-XX:+ScavengeBeforeFullGC +.RS 4 +Enables GC of the young generation before each full GC\&. This option is enabled by default\&. Oracle recommends that you +\fIdo not\fR +disable it, because scavenging the young generation before a full GC can reduce the number of objects reachable from the old generation space into the young generation space\&. To disable GC of the young generation before each full GC, specify +\fB\-XX:\-ScavengeBeforeFullGC\fR\&. +.RE +.PP +\-XX:SoftRefLRUPolicyMSPerMB=\fItime\fR +.RS 4 +Sets the amount of time (in milliseconds) a softly reachable object is kept active on the heap after the last time it was referenced\&. The default value is one second of lifetime per free megabyte in the heap\&. The +\fB\-XX:SoftRefLRUPolicyMSPerMB\fR +option accepts integer values representing milliseconds per one megabyte of the current heap size (for Java HotSpot Client VM) or the maximum possible heap size (for Java HotSpot Server VM)\&. This difference means that the Client VM tends to flush soft references rather than grow the heap, whereas the Server VM tends to grow the heap rather than flush soft references\&. In the latter case, the value of the +\fB\-Xmx\fR +option has a significant effect on how quickly soft references are garbage collected\&. +.sp +The following example shows how to set the value to 2\&.5 seconds: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:SoftRefLRUPolicyMSPerMB=2500\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:StringDeduplicationAgeThreshold=\fIthreshold\fR +.RS 4 +\fBString\fR +objects reaching the specified age are considered candidates for deduplication\&. An object\*(Aqs age is a measure of how many times it has survived garbage collection\&. This is sometimes referred to as tenuring; see the +\fB\-XX:+PrintTenuringDistribution\fR +option\&. Note that +\fBString\fR +objects that are promoted to an old heap region before this age has been reached are always considered candidates for deduplication\&. The default value for this option is +\fB3\fR\&. See the +\fB\-XX:+UseStringDeduplication\fR +option\&. +.RE +.PP +\-XX:SurvivorRatio=\fIratio\fR +.RS 4 +Sets the ratio between eden space size and survivor space size\&. By default, this option is set to 8\&. The following example shows how to set the eden/survivor space ratio to 4: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:SurvivorRatio=4\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:TargetSurvivorRatio=\fIpercent\fR +.RS 4 +Sets the desired percentage of survivor space (0 to 100) used after young garbage collection\&. By default, this option is set to 50%\&. +.sp +The following example shows how to set the target survivor space ratio to 30%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:TargetSurvivorRatio=30\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:TLABSize=\fIsize\fR +.RS 4 +Sets the initial size (in bytes) of a thread\-local allocation buffer (TLAB)\&. Append the letter +\fBk\fR +or +\fBK\fR +to indicate kilobytes, +\fBm\fR +or +\fBM\fR +to indicate megabytes, +\fBg\fR +or +\fBG\fR +to indicate gigabytes\&. If this option is set to 0, then the JVM chooses the initial size automatically\&. +.sp +The following example shows how to set the initial TLAB size to 512 KB: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\-XX:TLABSize=512k\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\-XX:+UseAdaptiveSizePolicy +.RS 4 +Enables the use of adaptive generation sizing\&. This option is enabled by default\&. To disable adaptive generation sizing, specify +\fB\-XX:\-UseAdaptiveSizePolicy\fR +and set the size of the memory allocation pool explicitly (see the +\fB\-XX:SurvivorRatio\fR +option)\&. +.RE +.PP +\-XX:+UseCMSInitiatingOccupancyOnly +.RS 4 +Enables the use of the occupancy value as the only criterion for initiating the CMS collector\&. By default, this option is disabled and other criteria may be used\&. +.RE +.PP +\-XX:+UseConcMarkSweepGC +.RS 4 +Enables the use of the CMS garbage collector for the old generation\&. Oracle recommends that you use the CMS garbage collector when application latency requirements cannot be met by the throughput (\fB\-XX:+UseParallelGC\fR) garbage collector\&. The G1 garbage collector (\fB\-XX:+UseG1GC\fR) is another alternative\&. +.sp +By default, this option is disabled and the collector is chosen automatically based on the configuration of the machine and type of the JVM\&. When this option is enabled, the +\fB\-XX:+UseParNewGC\fR +option is automatically set and you should not disable it, because the following combination of options has been deprecated in JDK 8: +\fB\-XX:+UseConcMarkSweepGC \-XX:\-UseParNewGC\fR\&. +.RE +.PP +\-XX:+UseG1GC +.RS 4 +Enables the use of the garbage\-first (G1) garbage collector\&. It is a server\-style garbage collector, targeted for multiprocessor machines with a large amount of RAM\&. It meets GC pause time goals with high probability, while maintaining good throughput\&. The G1 collector is recommended for applications requiring large heaps (sizes of around 6 GB or larger) with limited GC latency requirements (stable and predictable pause time below 0\&.5 seconds)\&. +.sp +By default, this option is disabled and the collector is chosen automatically based on the configuration of the machine and type of the JVM\&. +.RE +.PP +\-XX:+UseGCOverheadLimit +.RS 4 +Enables the use of a policy that limits the proportion of time spent by the JVM on GC before an +\fBOutOfMemoryError\fR +exception is thrown\&. This option is enabled, by default and the parallel GC will throw an +\fBOutOfMemoryError\fR +if more than 98% of the total time is spent on garbage collection and less than 2% of the heap is recovered\&. When the heap is small, this feature can be used to prevent applications from running for long periods of time with little or no progress\&. To disable this option, specify +\fB\-XX:\-UseGCOverheadLimit\fR\&. +.RE +.PP +\-XX:+UseNUMA +.RS 4 +Enables performance optimization of an application on a machine with nonuniform memory architecture (NUMA) by increasing the application\*(Aqs use of lower latency memory\&. By default, this option is disabled and no optimization for NUMA is made\&. The option is only available when the parallel garbage collector is used (\fB\-XX:+UseParallelGC\fR)\&. +.RE +.PP +\-XX:+UseParallelGC +.RS 4 +Enables the use of the parallel scavenge garbage collector (also known as the throughput collector) to improve the performance of your application by leveraging multiple processors\&. +.sp +By default, this option is disabled and the collector is chosen automatically based on the configuration of the machine and type of the JVM\&. If it is enabled, then the +\fB\-XX:+UseParallelOldGC\fR +option is automatically enabled, unless you explicitly disable it\&. +.RE +.PP +\-XX:+UseParallelOldGC +.RS 4 +Enables the use of the parallel garbage collector for full GCs\&. By default, this option is disabled\&. Enabling it automatically enables the +\fB\-XX:+UseParallelGC\fR +option\&. +.RE +.PP +\-XX:+UseParNewGC +.RS 4 +Enables the use of parallel threads for collection in the young generation\&. By default, this option is disabled\&. It is automatically enabled when you set the +\fB\-XX:+UseConcMarkSweepGC\fR +option\&. Using the +\fB\-XX:+UseParNewGC\fR +option without the +\fB\-XX:+UseConcMarkSweepGC\fR +option was deprecated in JDK 8\&. +.RE +.PP +\-XX:+UseSerialGC +.RS 4 +Enables the use of the serial garbage collector\&. This is generally the best choice for small and simple applications that do not require any special functionality from garbage collection\&. By default, this option is disabled and the collector is chosen automatically based on the configuration of the machine and type of the JVM\&. +.RE +.PP +\-XX:+UseSHM +.RS 4 +On Linux, enables the JVM to use shared memory to setup large pages\&. +.sp +For more information, see "Large Pages"\&. +.RE +.PP +\-XX:+UseStringDeduplication +.RS 4 +Enables string deduplication\&. By default, this option is disabled\&. To use this option, you must enable the garbage\-first (G1) garbage collector\&. See the +\fB\-XX:+UseG1GC\fR +option\&. +.sp +\fIString deduplication\fR +reduces the memory footprint of +\fBString\fR +objects on the Java heap by taking advantage of the fact that many +\fBString\fR +objects are identical\&. Instead of each +\fBString\fR +object pointing to its own character array, identical +\fBString\fR +objects can point to and share the same character array\&. +.RE +.PP +\-XX:+UseTLAB +.RS 4 +Enables the use of thread\-local allocation blocks (TLABs) in the young generation space\&. This option is enabled by default\&. To disable the use of TLABs, specify +\fB\-XX:\-UseTLAB\fR\&. +.RE +.SS "Deprecated and Removed Options" +.PP +These options were included in the previous release, but have since been considered unnecessary\&. +.PP +\-Xincgc +.RS 4 +Enables incremental garbage collection\&. This option was deprecated in JDK 8 with no replacement\&. +.RE +.PP +\-Xrun\fIlibname\fR +.RS 4 +Loads the specified debugging/profiling library\&. This option was superseded by the +\fB\-agentlib\fR +option\&. +.RE +.PP +\-XX:CMSIncrementalDutyCycle=\fIpercent\fR +.RS 4 +Sets the percentage of time (0 to 100) between minor collections that the concurrent collector is allowed to run\&. This option was deprecated in JDK 8 with no replacement, following the deprecation of the +\fB\-XX:+CMSIncrementalMode\fR +option\&. +.RE +.PP +\-XX:CMSIncrementalDutyCycleMin=\fIpercent\fR +.RS 4 +Sets the percentage of time (0 to 100) between minor collections that is the lower bound for the duty cycle when +\fB\-XX:+CMSIncrementalPacing\fR +is enabled\&. This option was deprecated in JDK 8 with no replacement, following the deprecation of the +\fB\-XX:+CMSIncrementalMode\fR +option\&. +.RE +.PP +\-XX:+CMSIncrementalMode +.RS 4 +Enables the incremental mode for the CMS collector\&. This option was deprecated in JDK 8 with no replacement, along with other options that start with +\fBCMSIncremental\fR\&. +.RE +.PP +\-XX:CMSIncrementalOffset=\fIpercent\fR +.RS 4 +Sets the percentage of time (0 to 100) by which the incremental mode duty cycle is shifted to the right within the period between minor collections\&. This option was deprecated in JDK 8 with no replacement, following the deprecation of the +\fB\-XX:+CMSIncrementalMode\fR +option\&. +.RE +.PP +\-XX:+CMSIncrementalPacing +.RS 4 +Enables automatic adjustment of the incremental mode duty cycle based on statistics collected while the JVM is running\&. This option was deprecated in JDK 8 with no replacement, following the deprecation of the +\fB\-XX:+CMSIncrementalMode\fR +option\&. +.RE +.PP +\-XX:CMSIncrementalSafetyFactor=\fIpercent\fR +.RS 4 +Sets the percentage of time (0 to 100) used to add conservatism when computing the duty cycle\&. This option was deprecated in JDK 8 with no replacement, following the deprecation of the +\fB\-XX:+CMSIncrementalMode\fR +option\&. +.RE +.PP +\-XX:CMSInitiatingPermOccupancyFraction=\fIpercent\fR +.RS 4 +Sets the percentage of the permanent generation occupancy (0 to 100) at which to start a GC\&. This option was deprecated in JDK 8 with no replacement\&. +.RE +.PP +\-XX:MaxPermSize=\fIsize\fR +.RS 4 +Sets the maximum permanent generation space size (in bytes)\&. This option was deprecated in JDK 8, and superseded by the +\fB\-XX:MaxMetaspaceSize\fR +option\&. +.RE +.PP +\-XX:PermSize=\fIsize\fR +.RS 4 +Sets the space (in bytes) allocated to the permanent generation that triggers a garbage collection if it is exceeded\&. This option was deprecated un JDK 8, and superseded by the +\fB\-XX:MetaspaceSize\fR +option\&. +.RE +.PP +\-XX:+UseSplitVerifier +.RS 4 +Enables splitting of the verification process\&. By default, this option was enabled in the previous releases, and verification was split into two phases: type referencing (performed by the compiler) and type checking (performed by the JVM runtime)\&. This option was deprecated in JDK 8, and verification is now split by default without a way to disable it\&. +.RE +.PP +\-XX:+UseStringCache +.RS 4 +Enables caching of commonly allocated strings\&. This option was removed from JDK 8 with no replacement\&. +.RE +.SH "PERFORMANCE TUNING EXAMPLES" +.PP +The following examples show how to use experimental tuning flags to either optimize throughput or to provide lower response time\&. +.PP +\fBExample 1 \fRTuning for Higher Throughput +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-d64 \-server \-XX:+AggressiveOpts \-XX:+UseLargePages \-Xmn10g \-Xms26g \-Xmx26g\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBExample 2 \fRTuning for Lower Response Time +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjava \-d64 \-XX:+UseG1GC \-Xms26g Xmx26g \-XX:MaxGCPauseMillis=500 \-XX:+PrintGCTimeStamp\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.SH "LARGE PAGES" +.PP +Also known as huge pages, large pages are memory pages that are significantly larger than the standard memory page size (which varies depending on the processor and operating system)\&. Large pages optimize processor Translation\-Lookaside Buffers\&. +.PP +A Translation\-Lookaside Buffer (TLB) is a page translation cache that holds the most\-recently used virtual\-to\-physical address translations\&. TLB is a scarce system resource\&. A TLB miss can be costly as the processor must then read from the hierarchical page table, which may require multiple memory accesses\&. By using a larger memory page size, a single TLB entry can represent a larger memory range\&. There will be less pressure on TLB, and memory\-intensive applications may have better performance\&. +.PP +However, large pages page memory can negatively affect system performance\&. For example, when a large mount of memory is pinned by an application, it may create a shortage of regular memory and cause excessive paging in other applications and slow down the entire system\&. Also, a system that has been up for a long time could produce excessive fragmentation, which could make it impossible to reserve enough large page memory\&. When this happens, either the OS or JVM reverts to using regular pages\&. +.SS "Large Pages Support" +.PP +Solaris and Linux support large pages\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBSolaris\fR +.RS 4 +.PP +Solaris 9 and later include Multiple Page Size Support (MPSS); no additional configuration is necessary\&. See http://www\&.oracle\&.com/technetwork/server\-storage/solaris10/overview/solaris9\-features\-scalability\-135663\&.html\&. +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBLinux\fR +.RS 4 +.PP +The 2\&.6 kernel supports large pages\&. Some vendors have backported the code to their 2\&.4\-based releases\&. To check if your system can support large page memory, try the following: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB# cat /proc/meminfo | grep Huge\fR +\fBHugePages_Total: 0\fR +\fBHugePages_Free: 0\fR +\fBHugepagesize: 2048 kB\fR + +.fi +.if n \{\ +.RE +.\} +.PP +If the output shows the three "Huge" variables, then your system can support large page memory but it needs to be configured\&. If the command prints nothing, then your system does not support large pages\&. To configure the system to use large page memory, login as +\fBroot\fR, and then follow these steps: +.sp +.RS 4 +.ie n \{\ +\h'-04' 1.\h'+01'\c +.\} +.el \{\ +.sp -1 +.IP " 1." 4.2 +.\} +If you are using the option +\fB\-XX:+UseSHM\fR +(instead of +\fB\-XX:+UseHugeTLBFS\fR), then increase the +\fBSHMMAX\fR +value\&. It must be larger than the Java heap size\&. On a system with 4 GB of physical RAM (or less), the following will make all the memory sharable: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB# echo 4294967295 > /proc/sys/kernel/shmmax\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04' 2.\h'+01'\c +.\} +.el \{\ +.sp -1 +.IP " 2." 4.2 +.\} +If you are using the option +\fB\-XX:+UseSHM\fR +or +\fB\-XX:+UseHugeTLBFS\fR, then specify the number of large pages\&. In the following example, 3 GB of a 4 GB system are reserved for large pages (assuming a large page size of 2048kB, then 3 GB = 3 * 1024 MB = 3072 MB = 3072 * 1024 kB = 3145728 kB and 3145728 kB / 2048 kB = 1536): +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB# echo 1536 > /proc/sys/vm/nr_hugepages\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.TS +allbox tab(:); +l. +T{ +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Note that the values contained in +\fB/proc\fR +will reset after you reboot your system, so may want to set them in an initialization script (for example, +\fBrc\&.local\fR +or +\fBsysctl\&.conf\fR)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +If you configure (or resize) the OS kernel parameters +\fB/proc/sys/kernel/shmmax\fR +or +\fB/proc/sys/vm/nr_hugepages\fR, Java processes may allocate large pages for areas in addition to the Java heap\&. These steps can allocate large pages for the following areas: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Java heap +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Code cache +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +The marking bitmap data structure for the parallel GC +.RE +.sp +Consequently, if you configure the +\fBnr_hugepages\fR +parameter to the size of the Java heap, then the JVM can fail in allocating the code cache areas on large pages because these areas are quite large in size\&. +.RE +T} +.TE +.sp 1 +.sp .5v +.RE +.RE +.SH "EXIT STATUS" +.PP +The following exit values are typically returned by the launcher when the launcher is called with the wrong arguments, serious errors, or exceptions thrown by the JVM\&. However, a Java application may choose to return any value by using the API call +\fBSystem\&.exit(exitValue)\fR\&. The values are: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB0\fR: Successful completion +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fB>0\fR: An error occurred +.RE +.SH "SEE ALSO" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +javac(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jdb(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +javah(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jar(1) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +jstat(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/jjs.1 b/FCL/src/main/assets/java/man/man1/jjs.1 new file mode 100644 index 00000000..04f8028c --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/jjs.1 @@ -0,0 +1,247 @@ +'\" t +.\" Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Title: jjs +.\" Language: English +.\" Date: 03 March 2015 +.\" SectDesc: Basic Tools +.\" Software: JDK 8 +.\" Arch: generic +.\" Part Number: E38207-04 +.\" Doc ID: JSSON +.\" +.if n .pl 99999 +.TH "jjs" "1" "03 March 2015" "JDK 8" "Basic Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +jjs \- Invokes the Nashorn engine\&. +.SH "SYNOPSIS" +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB\fBjjs\fR\fR\fB [\fR\fB\fIoptions\fR\fR\fB] [\fR\fB\fIscript\-files\fR\fR\fB] [\-\- \fR\fB\fIarguments\fR\fR\fB]\fR +.fi +.if n \{\ +.RE +.\} +.PP +\fIoptions\fR +.RS 4 +One or more options of the +\fBjjs\fR +command, separated by spaces\&. For more information, see Options\&. +.RE +.PP +\fIscript\-files\fR +.RS 4 +One or more script files which you want to interpret using Nashorn, separated by spaces\&. If no files are specified, an interactive shell is started\&. +.RE +.PP +\fIarguments\fR +.RS 4 +All values after the double hyphen marker (\fB\-\-\fR) are passed through to the script or the interactive shell as arguments\&. These values can be accessed by using the +\fBarguments\fR +property (see Example 3)\&. +.RE +.SH "DESCRIPTION" +.PP +The +\fBjjs\fR +command\-line tool is used to invoke the Nashorn engine\&. You can use it to interpret one or several script files, or to run an interactive shell\&. +.SH "OPTIONS" +.PP +The options of the +\fBjjs\fR +command control the conditions under which scripts are interpreted by Nashorn\&. +.PP +\-cp \fIpath\fR +.br +\-classpath \fIpath\fR +.RS 4 +Specifies the path to the supporting class files To set multiple paths, the option can be repeated, or you can separate each path with a colon (:)\&. +.RE +.PP +\-D\fIname\fR=\fIvalue\fR +.RS 4 +Sets a system property to be passed to the script by assigning a value to a property name\&. The following example shows how to invoke Nashorn in interactive mode and assign +\fBmyValue\fR +to the property named +\fBmyKey\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB>> \fR\fB\fBjjs \-DmyKey=myValue\fR\fR +\fBjjs> \fR\fB\fBjava\&.lang\&.System\&.getProperty("myKey")\fR\fR +\fBmyValue\fR +\fBjjs>\fR + +.fi +.if n \{\ +.RE +.\} +This option can be repeated to set multiple properties\&. +.RE +.PP +\-doe +.br +\-\-dump\-on\-error +.RS 4 +Provides a full stack trace when an error occurs\&. By default, only a brief error message is printed\&. +.RE +.PP +\-fv +.br +\-\-fullversion +.RS 4 +Prints the full Nashorn version string\&. +.RE +.PP +\-fx +.RS 4 +Launches the script as a JavaFX application\&. +.RE +.PP +\-h +.br +\-help +.RS 4 +Prints the list of options and their descriptions\&. +.RE +.PP +\-\-language=[es5] +.RS 4 +Specifies the ECMAScript language version\&. The default version is ES5\&. +.RE +.PP +\-ot +.br +\-\-optimistic\-types=[true|false] +.RS 4 +Enables or disables optimistic type assumptions with deoptimizing recompilation\&. Running with optimistic types will yield higher final speed, but may increase warmup time\&. +.RE +.PP +\-scripting +.RS 4 +Enables shell scripting features\&. +.RE +.PP +\-strict +.RS 4 +Enables strict mode, which enforces stronger adherence to the standard (ECMAScript Edition 5\&.1), making it easier to detect common coding errors\&. +.RE +.PP +\-t=\fIzone\fR +.br +\-timezone=\fIzone\fR +.RS 4 +Sets the specified time zone for script execution\&. It overrides the time zone set in the OS and used by the +\fBDate\fR +object\&. +.RE +.PP +\-v +.br +\-version +.RS 4 +Prints the Nashorn version string\&. +.RE +.SH "EXAMPLES" +.PP +\fBExample 1 \fRRunning a Script with Nashorn +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fBjjs script\&.js\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBExample 2 \fRRunning Nashorn in Interactive Mode +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB>> \fR\fB\fBjjs\fR\fR +\fBjjs> \fR\fB\fBprintln("Hello, World!")\fR\fR +\fBHello, World!\fR +\fBjjs> \fR\fB\fBquit()\fR\fR +\fB>>\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBExample 3 \fRPassing Arguments to Nashorn +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +\fB>> \fR\fB\fBjjs \-\- a b c\fR\fR +\fBjjs> \fR\fB\fBarguments\&.join(", ")\fR\fR +\fBa, b, c\fR +\fBjjs>\fR + +.fi +.if n \{\ +.RE +.\} +.RE +.SH "SEE ALSO" +.PP +\fBjrunscript\fR +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/keytool.1 b/FCL/src/main/assets/java/man/man1/keytool.1 new file mode 100644 index 00000000..a29e1ffe --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/keytool.1 @@ -0,0 +1,1619 @@ +'\" t +.\" Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 03 March 2015 +.\" SectDesc: Security Tools +.\" Title: keytool.1 +.\" +.if n .pl 99999 +.TH keytool 1 "03 March 2015" "JDK 8" "Security Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +keytool \- Manages a keystore (database) of cryptographic keys, X\&.509 certificate chains, and trusted certificates\&. +.SH SYNOPSIS +.sp +.nf + +\fBkeytool\fR [\fIcommands\fR] +.fi +.sp +.TP +\fIcommands\fR +See Commands\&. These commands are categorized by task as follows: +.RS +.TP 0.2i +\(bu +Create or Add Data to the Keystore +.RS +.TP 0.2i +\(bu +-gencert +.TP 0.2i +\(bu +-genkeypair +.TP 0.2i +\(bu +-genseckey +.TP 0.2i +\(bu +-importcert +.TP 0.2i +\(bu +-importpassword +.RE + +.TP 0.2i +\(bu +Import Contents From Another Keystore +.RS +.TP 0.2i +\(bu +-importkeystore +.RE + +.TP 0.2i +\(bu +Generate Certificate Request +.RS +.TP 0.2i +\(bu +-certreq +.RE + +.TP 0.2i +\(bu +Export Data +.RS +.TP 0.2i +\(bu +-exportcert +.RE + +.TP 0.2i +\(bu +Display Data +.RS +.TP 0.2i +\(bu +-list +.TP 0.2i +\(bu +-printcert +.TP 0.2i +\(bu +-printcertreq +.TP 0.2i +\(bu +-printcrl +.RE + +.TP 0.2i +\(bu +Manage the Keystore +.RS +.TP 0.2i +\(bu +-storepasswd +.TP 0.2i +\(bu +-keypasswd +.TP 0.2i +\(bu +-delete +.TP 0.2i +\(bu +-changealias +.RE + +.TP 0.2i +\(bu +Get Help +.RS +.TP 0.2i +\(bu +-help +.RE + +.RE + +.SH DESCRIPTION +The \f3keytool\fR command is a key and certificate management utility\&. It enables users to administer their own public/private key pairs and associated certificates for use in self-authentication (where the user authenticates himself or herself to other users and services) or data integrity and authentication services, using digital signatures\&. The \f3keytool\fR command also enables users to cache the public keys (in the form of certificates) of their communicating peers\&. +.PP +A certificate is a digitally signed statement from one entity (person, company, and so on\&.), that says that the public key (and some other information) of some other entity has a particular value\&. (See Certificate\&.) When data is digitally signed, the signature can be verified to check the data integrity and authenticity\&. Integrity means that the data has not been modified or tampered with, and authenticity means the data comes from whoever claims to have created and signed it\&. +.PP +The \f3keytool\fR command also enables users to administer secret keys and passphrases used in symmetric encryption and decryption (DES)\&. +.PP +The \f3keytool\fR command stores the keys and certificates in a keystore\&. See KeyStore aliases\&. +.SH COMMAND\ AND\ OPTION\ NOTES +See Commands for a listing and description of the various commands\&. +.TP 0.2i +\(bu +All command and option names are preceded by a minus sign (-)\&. +.TP 0.2i +\(bu +The options for each command can be provided in any order\&. +.TP 0.2i +\(bu +All items not italicized or in braces or brackets are required to appear as is\&. +.TP 0.2i +\(bu +Braces surrounding an option signify that a default value will be used when the option is not specified on the command line\&. See Option Defaults\&. Braces are also used around the \f3-v\fR, \f3-rfc\fR, and \f3-J\fR options, which only have meaning when they appear on the command line\&. They do not have any default values other than not existing\&. +.TP 0.2i +\(bu +Brackets surrounding an option signify that the user is prompted for the values when the option is not specified on the command line\&. For the \f3-keypass\fR option, if you do not specify the option on the command line, then the \f3keytool\fR command first attempts to use the keystore password to recover the private/secret key\&. If this attempt fails, then the \f3keytool\fR command prompts you for the private/secret key password\&. +.TP 0.2i +\(bu +Items in italics (option values) represent the actual values that must be supplied\&. For example, here is the format of the \f3-printcert\fR command: +.sp +.nf +\f3keytool \-printcert {\-file \fIcert_file\fR} {\-v}\fP +.fi +.sp + + + + +When you specify a \f3-printcert\fR command, replace \fIcert_file\fR with the actual file name, as follows: \f3keytool -printcert -file VScert\&.cer\fR +.TP 0.2i +\(bu +Option values must be put in quotation marks when they contain a blank (space)\&. +.TP 0.2i +\(bu +The \f3-help\fR option is the default\&. The \f3keytool\fR command is the same as \f3keytool -help\fR\&. +.SH OPTION\ DEFAULTS +The following examples show the defaults for various option values\&. +.sp +.nf +\f3\-alias "mykey"\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-keyalg\fP +.fi +.nf +\f3 "DSA" (when using \-genkeypair)\fP +.fi +.nf +\f3 "DES" (when using \-genseckey)\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-keysize\fP +.fi +.nf +\f3 2048 (when using \-genkeypair and \-keyalg is "RSA")\fP +.fi +.nf +\f3 1024 (when using \-genkeypair and \-keyalg is "DSA")\fP +.fi +.nf +\f3 256 (when using \-genkeypair and \-keyalg is "EC")\fP +.fi +.nf +\f3 56 (when using \-genseckey and \-keyalg is "DES")\fP +.fi +.nf +\f3 168 (when using \-genseckey and \-keyalg is "DESede")\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-validity 90\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-keystore \fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-storetype \fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-file\fP +.fi +.nf +\f3 stdin (if reading)\fP +.fi +.nf +\f3 stdout (if writing)\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-protected false\fP +.fi +.nf +\f3\fP +.fi +.sp +In generating a public/private key pair, the signature algorithm (\f3-sigalg\fR option) is derived from the algorithm of the underlying private key: +.TP 0.2i +\(bu +If the underlying private key is of type DSA, then the \f3-sigalg\fR option defaults to SHA1withDSA\&. +.TP 0.2i +\(bu +If the underlying private key is of type RSA, then the \f3-sigalg\fR option defaults to SHA256withRSA\&. +.TP 0.2i +\(bu +If the underlying private key is of type EC, then the \f3-sigalg\fR option defaults to SHA256withECDSA\&. +.PP +For a full list of \f3-keyalg\fR and \f3-sigalg\fR arguments, see Java Cryptography Architecture (JCA) Reference Guide at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec\&.html#AppA +.SH COMMON\ OPTIONS +The \f3-v\fR option can appear for all commands except \f3-help\fR\&. When the \f3-v\fR option appears, it signifies verbose mode, which means that more information is provided in the output\&. +.PP +There is also a \f3-Jjavaoption\fR argument that can appear for any command\&. When the \f3-Jjavaoption\fR appears, the specified \f3javaoption\fR string is passed directly to the Java interpreter\&. This option does not contain any spaces\&. It is useful for adjusting the execution environment or memory usage\&. For a list of possible interpreter options, type \f3java -h\fR or \f3java -X\fR at the command line\&. +.PP +These options can appear for all commands operating on a keystore: +.TP +-storetype \fIstoretype\fR +.br +This qualifier specifies the type of keystore to be instantiated\&. +.TP +-keystore \fIkeystore\fR +.br +The keystore location\&. + +If the JKS \f3storetype\fR is used and a keystore file does not yet exist, then certain \f3keytool\fR commands can result in a new keystore file being created\&. For example, if \f3keytool -genkeypair\fR is called and the \f3-keystore\fR option is not specified, the default keystore file named \f3\&.keystore\fR in the user\&'s home directory is created when it does not already exist\&. Similarly, if the \f3-keystore ks_file\fR option is specified but ks_file does not exist, then it is created\&. For more information on the JKS \f3storetype\fR, see the \fIKeyStore Implementation\fR section in KeyStore aliases\&. + +Note that the input stream from the \f3-keystore\fR option is passed to the \f3KeyStore\&.load\fR method\&. If \f3NONE\fR is specified as the URL, then a null stream is passed to the \f3KeyStore\&.load\fR method\&. \f3NONE\fR should be specified if the keystore is not file-based\&. For example, when it resides on a hardware token device\&. +.TP +-storepass[:\fIenv\fR| :\fIfile\fR] argument +.br +The password that is used to protect the integrity of the keystore\&. + +If the modifier \f3env\fR or \f3file\fR is not specified, then the password has the \f3value\fR argument, which must be at least 6 characters long\&. Otherwise, the password is retrieved as follows: +.RS +.TP 0.2i +\(bu +\f3env\fR: Retrieve the password from the environment variable named \f3argument\fR\&. +.TP 0.2i +\(bu +\f3file\fR: Retrieve the password from the file named argument\&. +.RE + + +\fINote:\fR All other options that require passwords, such as \f3-keypass\fR, \f3-srckeypass\fR, -\f3destkeypass\fR, \f3-srcstorepass\fR, and \f3-deststorepass\fR, accept the \fIenv\fR and \fIfile\fR modifiers\&. Remember to separate the password option and the modifier with a colon (:)\&. + +The password must be provided to all commands that access the keystore contents\&. For such commands, when the \f3-storepass\fR option is not provided at the command line, the user is prompted for it\&. + +When retrieving information from the keystore, the password is optional\&. If no password is specified, then the integrity of the retrieved information cannot be verified and a warning is displayed\&. +.TP +-providerName \fIprovider_name\fR +.br +Used to identify a cryptographic service provider\&'s name when listed in the security properties file\&. +.TP +-providerClass \fIprovider_class_name\fR +.br +Used to specify the name of a cryptographic service provider\&'s master class file when the service provider is not listed in the security properties file\&. +.TP +-providerArg \fIprovider_arg\fR +.br +Used with the \f3-providerClass\fR option to represent an optional string input argument for the constructor of \f3provider_class_name\fR\&. +.TP +-protected +.br +Either \f3true\fR or \f3false\fR\&. This value should be specified as \f3true\fR when a password must be specified by way of a protected authentication path such as a dedicated PIN reader\&.Because there are two keystores involved in the \f3-importkeystore\fR command, the following two options \f3-srcprotected\fR and -\f3destprotected\fR are provided for the source keystore and the destination keystore respectively\&. +.TP +-ext \fI{name{:critical} {=value}}\fR +.br +Denotes an X\&.509 certificate extension\&. The option can be used in \f3-genkeypair\fR and \f3-gencert\fR to embed extensions into the certificate generated, or in \f3-certreq\fR to show what extensions are requested in the certificate request\&. The option can appear multiple times\&. The \f3name\fR argument can be a supported extension name (see Named Extensions) or an arbitrary OID number\&. The \f3value\fR argument, when provided, denotes the argument for the extension\&. When \fIvalue\fR is omitted, that means that the default value of the extension or the extension requires no argument\&. The \f3:critical\fR modifier, when provided, means the extension\&'s \f3isCritical\fR attribute is \f3true\fR; otherwise, it is \f3false\fR\&. You can use \f3:c\fR in place of \f3:critical\fR\&. +.SH NAMED\ EXTENSIONS +The \f3keytool\fR command supports these named extensions\&. The names are not case-sensitive)\&. +.TP +BC or BasicContraints +\fIValues\fR: The full form is: \f3ca:{true|false}[,pathlen:]\fR or \f3\fR, which is short for \f3ca:true,pathlen:\fR\&. When <\f3len\fR> is omitted, you have \f3ca:true\fR\&. +.TP +KU or KeyUsage +\fIValues\fR: \f3usage\fR(,\f3usage\fR)*, where \fIusage\fR can be one of \f3digitalSignature\fR, \f3nonRepudiation\fR (contentCommitment), \f3keyEncipherment\fR, \f3dataEncipherment\fR, \f3keyAgreement\fR, \f3keyCertSign\fR, \f3cRLSign\fR, \f3encipherOnly\fR, \f3decipherOnly\fR\&. The \fIusage\fR argument can be abbreviated with the first few letters (\f3dig\fR for \f3digitalSignature\fR) or in camel-case style (\f3dS\fR for \f3digitalSignature\fR or \f3cRLS\fR for \f3cRLSign\fR), as long as no ambiguity is found\&. The \f3usage\fR values are case-sensitive\&. +.TP +EKU or ExtendedKeyUsage +\fIValues\fR: \f3usage\fR(,\f3usage\fR)*, where \fIusage\fR can be one of \f3anyExtendedKeyUsage\fR, \f3serverAuth\fR, \f3clientAuth\fR, \f3codeSigning\fR, \f3emailProtection\fR, \f3timeStamping\fR, \f3OCSPSigning\fR, or any \fIOID string\fR\&. The \fIusage\fR argument can be abbreviated with the first few letters or in camel-case style, as long as no ambiguity is found\&. The \f3usage\fR values are case-sensitive\&. +.TP +SAN or SubjectAlternativeName +\fIValues\fR: \f3type\fR:\f3value\fR(,t\f3ype:value\fR)*, where \f3type\fR can be \f3EMAIL\fR, \f3URI\fR, \f3DNS\fR, \f3IP\fR, or \f3OID\fR\&. The \f3value\fR argument is the string format value for the \f3type\fR\&. +.TP +IAN or IssuerAlternativeName +\fIValues\fR: Same as \f3SubjectAlternativeName\fR\&. +.TP +SIA or SubjectInfoAccess +\fIValues\fR: \f3method\fR:\f3location-type\fR:\f3location-value\fR (,\f3method:location-type\fR:\f3location-value\fR)*, where \f3method\fR can be \f3timeStamping\fR, \f3caRepository\fR or any OID\&. The \f3location-type\fR and \f3location-value\fR arguments can be any \f3type\fR:\f3value\fR supported by the \f3SubjectAlternativeName\fR extension\&. +.TP +AIA or AuthorityInfoAccess +\fIValues\fR: Same as \f3SubjectInfoAccess\fR\&. The \f3method\fR argument can be \f3ocsp\fR,\f3caIssuers\fR, or any OID\&. +.PP +When \f3name\fR is OID, the value is the hexadecimal dumped DER encoding of the \f3extnValue\fR for the extension excluding the OCTET STRING type and length bytes\&. Any extra character other than standard hexadecimal numbers (0-9, a-f, A-F) are ignored in the HEX string\&. Therefore, both 01:02:03:04 and 01020304 are accepted as identical values\&. When there is no value, the extension has an empty value field\&. +.PP +A special name \f3honored\fR, used in \f3-gencert\fR only, denotes how the extensions included in the certificate request should be honored\&. The value for this name is a comma separated list of \f3all\fR (all requested extensions are honored), \f3name{:[critical|non-critical]}\fR (the named extension is honored, but using a different \f3isCritical\fR attribute) and \f3-name\fR (used with \f3all\fR, denotes an exception)\&. Requested extensions are not honored by default\&. +.PP +If, besides the\f3-ext honored\fR option, another named or OID \f3-ext\fR option is provided, this extension is added to those already honored\&. However, if this name (or OID) also appears in the honored value, then its value and criticality overrides the one in the request\&. +.PP +The \f3subjectKeyIdentifier\fR extension is always created\&. For non-self-signed certificates, the \f3authorityKeyIdentifier\fR is created\&. +.PP +\fINote:\fR Users should be aware that some combinations of extensions (and other certificate fields) may not conform to the Internet standard\&. See Certificate Conformance Warning\&. +.SH COMMANDS +.TP +-gencert +.sp +.nf +\f3{\-rfc} {\-infile \fIinfile\fR} {\-outfile \fIoutfile\fR} {\-alias \fIalias\fR} {\-sigalg \fIsigalg\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-dname \fIdname\fR} {\-startdate \fIstartdate\fR {\-ext \fIext\fR}* {\-validity \fIvalDays\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-keypass \fIkeypass\fR] {\-keystore \fIkeystore\fR} [\-storepass \fIstorepass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-storetype \fIstoretype\fR} {\-providername \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Generates a certificate as a response to a certificate request file (which can be created by the \f3keytool\fR\f3-certreq\fR command)\&. The command reads the request from \fIinfile\fR (if omitted, from the standard input), signs it using alias\&'s private key, and outputs the X\&.509 certificate into \fIoutfile\fR (if omitted, to the standard output)\&. When\f3-rfc\fR is specified, the output format is Base64-encoded PEM; otherwise, a binary DER is created\&. + +The \f3sigalg\fR value specifies the algorithm that should be used to sign the certificate\&. The \f3startdate\fR argument is the start time and date that the certificate is valid\&. The \f3valDays\fR argument tells the number of days for which the certificate should be considered valid\&. + +When \f3dname\fR is provided, it is used as the subject of the generated certificate\&. Otherwise, the one from the certificate request is used\&. + +The \f3ext\fR value shows what X\&.509 extensions will be embedded in the certificate\&. Read Common Options for the grammar of \f3-ext\fR\&. + +The \f3-gencert\fR option enables you to create certificate chains\&. The following example creates a certificate, \f3e1\fR, that contains three certificates in its certificate chain\&. + +The following commands creates four key pairs named \f3ca\fR, \f3ca1\fR, \f3ca2\fR, and \f3e1\fR: +.sp +.nf +\f3keytool \-alias ca \-dname CN=CA \-genkeypair\fP +.fi +.nf +\f3keytool \-alias ca1 \-dname CN=CA \-genkeypair\fP +.fi +.nf +\f3keytool \-alias ca2 \-dname CN=CA \-genkeypair\fP +.fi +.nf +\f3keytool \-alias e1 \-dname CN=E1 \-genkeypair\fP +.fi +.nf +\f3\fP +.fi +.sp + + +The following two commands create a chain of signed certificates; \f3ca\fR signs \f3ca1\fR and \f3ca1\fR signs \f3ca2\fR, all of which are self-issued: +.sp +.nf +\f3keytool \-alias ca1 \-certreq |\fP +.fi +.nf +\f3 keytool \-alias ca \-gencert \-ext san=dns:ca1 |\fP +.fi +.nf +\f3 keytool \-alias ca1 \-importcert\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3keytool \-alias ca2 \-certreq |\fP +.fi +.nf +\f3 $KT \-alias ca1 \-gencert \-ext san=dns:ca2 |\fP +.fi +.nf +\f3 $KT \-alias ca2 \-importcert\fP +.fi +.nf +\f3\fP +.fi +.sp + + +The following command creates the certificate \f3e1\fR and stores it in the file \f3e1\&.cert\fR, which is signed by \f3ca2\fR\&. As a result, \f3e1\fR should contain \f3ca\fR, \f3ca1\fR, and \f3ca2\fR in its certificate chain: +.sp +.nf +\f3keytool \-alias e1 \-certreq | keytool \-alias ca2 \-gencert > e1\&.cert\fP +.fi +.nf +\f3\fP +.fi +.sp + +.TP +-genkeypair +.sp +.nf +\f3{\-alias \fIalias\fR} {\-keyalg \fIkeyalg\fR} {\-keysize \fIkeysize\fR} {\-sigalg \fIsigalg\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-dname \fIdname\fR] [\-keypass \fIkeypass\fR] {\-startdate \fIvalue\fR} {\-ext \fIext\fR}*\fP +.fi +.sp +.sp +.nf +\f3{\-validity \fIvalDays\fR} {\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-storepass \fIstorepass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Generates a key pair (a public key and associated private key)\&. Wraps the public key into an X\&.509 v3 self-signed certificate, which is stored as a single-element certificate chain\&. This certificate chain and the private key are stored in a new keystore entry identified by alias\&. + +The \f3keyalg\fR value specifies the algorithm to be used to generate the key pair, and the \f3keysize\fR value specifies the size of each key to be generated\&. The \f3sigalg\fR value specifies the algorithm that should be used to sign the self-signed certificate\&. This algorithm must be compatible with the \f3keyalg\fR value\&. + +The \f3dname\fR value specifies the X\&.500 Distinguished Name to be associated with the value of \f3alias\fR, and is used as the issuer and subject fields in the self-signed certificate\&. If no distinguished name is provided at the command line, then the user is prompted for one\&. + +The value of \f3keypass\fR is a password used to protect the private key of the generated key pair\&. If no password is provided, then the user is prompted for it\&. If you press \fIthe Return key\fR at the prompt, then the key password is set to the same password as the keystore password\&. The \f3keypass\fR value must be at least 6 characters\&. + +The value of \f3startdate\fR specifies the issue time of the certificate, also known as the "Not Before" value of the X\&.509 certificate\&'s Validity field\&. + +The option value can be set in one of these two forms: + +\f3([+-]nnn[ymdHMS])+\fR + +\f3[yyyy/mm/dd] [HH:MM:SS]\fR + +With the first form, the issue time is shifted by the specified value from the current time\&. The value is a concatenation of a sequence of subvalues\&. Inside each subvalue, the plus sign (+) means shift forward, and the minus sign (-) means shift backward\&. The time to be shifted is \f3nnn\fR units of years, months, days, hours, minutes, or seconds (denoted by a single character of \f3y\fR, \f3m\fR, \f3d\fR, \f3H\fR, \f3M\fR, or \f3S\fR respectively)\&. The exact value of the issue time is calculated using the \f3java\&.util\&.GregorianCalendar\&.add(int field, int amount)\fR method on each subvalue, from left to right\&. For example, by specifying, the issue time will be: +.sp +.nf +\f3Calendar c = new GregorianCalendar();\fP +.fi +.nf +\f3c\&.add(Calendar\&.YEAR, \-1);\fP +.fi +.nf +\f3c\&.add(Calendar\&.MONTH, 1);\fP +.fi +.nf +\f3c\&.add(Calendar\&.DATE, \-1);\fP +.fi +.nf +\f3return c\&.getTime()\fP +.fi +.nf +\f3\fP +.fi +.sp + + +With the second form, the user sets the exact issue time in two parts, year/month/day and hour:minute:second (using the local time zone)\&. The user can provide only one part, which means the other part is the same as the current date (or time)\&. The user must provide the exact number of digits as shown in the format definition (padding with 0 when shorter)\&. When both the date and time are provided, there is one (and only one) space character between the two parts\&. The hour should always be provided in 24 hour format\&. + +When the option is not provided, the start date is the current time\&. The option can be provided at most once\&. + +The value of \f3valDays\fR specifies the number of days (starting at the date specified by \f3-startdate\fR, or the current date when \f3-startdate\fR is not specified) for which the certificate should be considered valid\&. + +This command was named \f3-genkey\fR in earlier releases\&. The old name is still supported in this release\&. The new name, \f3-genkeypair\fR, is preferred going forward\&. +.TP +-genseckey +.sp +.nf +\f3{\-alias \fIalias\fR} {\-keyalg \fIkeyalg\fR} {\-keysize \fIkeysize\fR} [\-keypass \fIkeypass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR} [\-storepass \fIstorepass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}} {\-v}\fP +.fi +.sp +.sp +.nf +\f3{\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Generates a secret key and stores it in a new \f3KeyStore\&.SecretKeyEntry\fR identified by \f3alias\fR\&. + +The value of \f3keyalg\fR specifies the algorithm to be used to generate the secret key, and the value of \f3keysize\fR specifies the size of the key to be generated\&. The \f3keypass\fR value is a password that protects the secret key\&. If no password is provided, then the user is prompted for it\&. If you press the Return key at the prompt, then the key password is set to the same password that is used for the \f3keystore\fR\&. The \f3keypass\fR value must be at least 6 characters\&. +.TP +-importcert +.sp +.nf +\f3{\-alias \fIalias\fR} {\-file \fIcert_file\fR} [\-keypass \fIkeypass\fR] {\-noprompt} {\-trustcacerts}\fP +.fi +.sp +.sp +.nf +\f3{\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR} [\-storepass \fIstorepass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-providerName \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Reads the certificate or certificate chain (where the latter is supplied in a PKCS#7 formatted reply or a sequence of X\&.509 certificates) from the file \f3cert_file\fR, and stores it in the \f3keystore\fR entry identified by \f3alias\fR\&. If no file is specified, then the certificate or certificate chain is read from \f3stdin\fR\&. + +The \f3keytool\fR command can import X\&.509 v1, v2, and v3 certificates, and PKCS#7 formatted certificate chains consisting of certificates of that type\&. The data to be imported must be provided either in binary encoding format or in printable encoding format (also known as Base64 encoding) as defined by the Internet RFC 1421 standard\&. In the latter case, the encoding must be bounded at the beginning by a string that starts with \f3-\fR\f3----BEGIN\fR, and bounded at the end by a string that starts with \f3-----END\fR\&. + +You import a certificate for two reasons: To add it to the list of trusted certificates, and to import a certificate reply received from a certificate authority (CA) as the result of submitting a Certificate Signing Request to that CA (see the \f3-certreq\fR option in Commands)\&. + +Which type of import is intended is indicated by the value of the \f3-alias\fR option\&. If the alias does not point to a key entry, then the \f3keytool\fR command assumes you are adding a trusted certificate entry\&. In this case, the alias should not already exist in the keystore\&. If the alias does already exist, then the \f3keytool\fR command outputs an error because there is already a trusted certificate for that alias, and does not import the certificate\&. If the alias points to a key entry, then the \f3keytool\fR command assumes you are importing a certificate reply\&. +.TP +-importpassword +.sp +.nf +\f3{\-alias \fIalias\fR} [\-keypass \fIkeypass\fR] {\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-storepass \fIstorepass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Imports a passphrase and stores it in a new \f3KeyStore\&.SecretKeyEntry\fR identified by \f3alias\fR\&. The passphrase may be supplied via the standard input stream; otherwise the user is prompted for it\&. \f3keypass\fR is a password used to protect the imported passphrase\&. If no password is provided, the user is prompted for it\&. If you press the Return key at the prompt, the key password is set to the same password as that used for the \f3keystore\fR\&. \f3keypass\fR must be at least 6 characters long\&. +.TP +-importkeystore +.sp +.nf +\f3{\-srcstoretype \fIsrcstoretype\fR} {\-deststoretype \fIdeststoretype\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-srcstorepass \fIsrcstorepass\fR] [\-deststorepass \fIdeststorepass\fR] {\-srcprotected}\fP +.fi +.sp +.sp +.nf +\f3{\-destprotected} \fP +.fi +.sp +.sp +.nf +\f3{\-srcalias \fIsrcalias\fR {\-destalias \fIdestalias\fR} [\-srckeypass \fIsrckeypass\fR]} \fP +.fi +.sp +.sp +.nf +\f3[\-destkeypass \fIdestkeypass\fR] {\-noprompt}\fP +.fi +.sp +.sp +.nf +\f3{\-srcProviderName \fIsrc_provider_name\fR} {\-destProviderName \fIdest_provider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}} {\-v}\fP +.fi +.sp +.sp +.nf +\f3{\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Imports a single entry or all entries from a source keystore to a destination keystore\&. + +When the \f3-srcalias\fR option is provided, the command imports the single entry identified by the alias to the destination keystore\&. If a destination alias is not provided with \f3destalias\fR, then \f3srcalias\fR is used as the destination alias\&. If the source entry is protected by a password, then \f3srckeypass\fR is used to recover the entry\&. If \fIsrckeypass\fR is not provided, then the \f3keytool\fR command attempts to use \f3srcstorepass\fR to recover the entry\&. If \f3srcstorepass\fR is either not provided or is incorrect, then the user is prompted for a password\&. The destination entry is protected with \f3destkeypass\fR\&. If \f3destkeypass\fR is not provided, then the destination entry is protected with the source entry password\&. For example, most third-party tools require \f3storepass\fR and \f3keypass\fR in a PKCS #12 keystore to be the same\&. In order to create a PKCS #12 keystore for these tools, always specify a \f3-destkeypass\fR to be the same as \f3-deststorepass\fR\&. + +If the \f3-srcalias\fR option is not provided, then all entries in the source keystore are imported into the destination keystore\&. Each destination entry is stored under the alias from the source entry\&. If the source entry is protected by a password, then \f3srcstorepass\fR is used to recover the entry\&. If \f3srcstorepass\fR is either not provided or is incorrect, then the user is prompted for a password\&. If a source keystore entry type is not supported in the destination keystore, or if an error occurs while storing an entry into the destination keystore, then the user is prompted whether to skip the entry and continue or to quit\&. The destination entry is protected with the source entry password\&. + +If the destination alias already exists in the destination keystore, then the user is prompted to either overwrite the entry or to create a new entry under a different alias name\&. + +If the \f3-noprompt\fR option is provided, then the user is not prompted for a new destination alias\&. Existing entries are overwritten with the destination alias name\&. Entries that cannot be imported are skipped and a warning is displayed\&. +.TP +-printcertreq +.sp +.nf +\f3{\-file \fIfile\fR}\fP +.fi +.sp + + +Prints the content of a PKCS #10 format certificate request, which can be generated by the \f3keytool\fR\f3-certreq\fR command\&. The command reads the request from file\&. If there is no file, then the request is read from the standard input\&. +.TP +-certreq +.sp +.nf +\f3{\-alias \fIalias\fR} {\-dname \fIdname\fR} {\-sigalg \fIsigalg\fR} {\-file \fIcertreq_file\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-keypass \fIkeypass\fR] {\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-storepass \fIstorepass\fR] {\-providerName \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Generates a Certificate Signing Request (CSR) using the PKCS #10 format\&. + +A CSR is intended to be sent to a certificate authority (CA)\&. The CA authenticates the certificate requestor (usually off-line) and will return a certificate or certificate chain, used to replace the existing certificate chain (which initially consists of a self-signed certificate) in the keystore\&. + +The private key associated with alias is used to create the PKCS #10 certificate request\&. To access the private key, the correct password must be provided\&. If \f3keypass\fR is not provided at the command line and is different from the password used to protect the integrity of the keystore, then the user is prompted for it\&. If \f3dname\fR is provided, then it is used as the subject in the CSR\&. Otherwise, the X\&.500 Distinguished Name associated with alias is used\&. + +The \f3sigalg\fR value specifies the algorithm that should be used to sign the CSR\&. + +The CSR is stored in the file certreq_file\&. If no file is specified, then the CSR is output to \f3stdout\fR\&. + +Use the \f3importcert\fR command to import the response from the CA\&. +.TP +-exportcert +.sp +.nf +\f3{\-alias \fIalias\fR} {\-file \fIcert_file\fR} {\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-storepass \fIstorepass\fR] {\-providerName \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-rfc} {\-v} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Reads from the keystore the certificate associated with \fIalias\fR and stores it in the cert_file file\&. When no file is specified, the certificate is output to \f3stdout\fR\&. + +The certificate is by default output in binary encoding\&. If the \f3-rfc\fR option is specified, then the output in the printable encoding format defined by the Internet RFC 1421 Certificate Encoding Standard\&. + +If \f3alias\fR refers to a trusted certificate, then that certificate is output\&. Otherwise, \f3alias\fR refers to a key entry with an associated certificate chain\&. In that case, the first certificate in the chain is returned\&. This certificate authenticates the public key of the entity addressed by \f3alias\fR\&. + +This command was named \f3-export\fR in earlier releases\&. The old name is still supported in this release\&. The new name, \f3-exportcert\fR, is preferred going forward\&. +.TP +-list +.sp +.nf +\f3{\-alias \fIalias\fR} {\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR} [\-storepass \fIstorepass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-providerName \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v | \-rfc} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Prints to \f3stdout\fR the contents of the keystore entry identified by \f3alias\fR\&. If no \f3alias\fR is specified, then the contents of the entire keystore are printed\&. + +This command by default prints the SHA1 fingerprint of a certificate\&. If the \f3-v\fR option is specified, then the certificate is printed in human-readable format, with additional information such as the owner, issuer, serial number, and any extensions\&. If the \f3-rfc\fR option is specified, then the certificate contents are printed using the printable encoding format, as defined by the Internet RFC 1421 Certificate Encoding Standard\&. + +You cannot specify both \f3-v\fR and \f3-rfc\fR\&. +.TP +-printcert +.sp +.nf +\f3{\-file \fIcert_file\fR | \-sslserver \fIhost\fR[:\fIport\fR]} {\-jarfile \fIJAR_file\fR {\-rfc} {\-v}\fP +.fi +.sp +.sp +.nf +\f3{\-Jjavaoption}\fP +.fi +.sp + + +Reads the certificate from the file cert_file, the SSL server located at host:port, or the signed JAR file \f3JAR_file\fR (with the \f3-jarfile\fR option and prints its contents in a human-readable format\&. When no port is specified, the standard HTTPS port 443 is assumed\&. Note that \f3-sslserver\fR and -file options cannot be provided at the same time\&. Otherwise, an error is reported\&. If neither option is specified, then the certificate is read from \f3stdin\fR\&. + +When\f3-rfc\fR is specified, the \f3keytool\fR command prints the certificate in PEM mode as defined by the Internet RFC 1421 Certificate Encoding standard\&. See Internet RFC 1421 Certificate Encoding Standard\&. + +If the certificate is read from a file or \f3stdin\fR, then it might be either binary encoded or in printable encoding format, as defined by the RFC 1421 Certificate Encoding standard\&. + +If the SSL server is behind a firewall, then the \f3-J-Dhttps\&.proxyHost=proxyhost\fR and \f3-J-Dhttps\&.proxyPort=proxyport\fR options can be specified on the command line for proxy tunneling\&. See Java Secure Socket Extension (JSSE) Reference Guide at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide\&.html + +\fINote:\fR This option can be used independently of a keystore\&. +.TP +-printcrl +.sp +.nf +\f3\-file \fIcrl_\fR {\-v}\fP +.fi +.sp + + +Reads the Certificate Revocation List (CRL) from the file \f3crl_\fR\&. A CRL is a list of digital certificates that were revoked by the CA that issued them\&. The CA generates the \f3crl_\fR file\&. + +\fINote:\fR This option can be used independently of a keystore\&. +.TP +-storepasswd +.sp +.nf +\f3[\-new \fInew_storepass\fR] {\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR}\fP +.fi +.sp +.sp +.nf +\f3[\-storepass \fIstorepass\fR] {\-providerName \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v} {\-Jjavaoption}\fP +.fi +.sp + + +Changes the password used to protect the integrity of the keystore contents\&. The new password is \f3new_storepass\fR, which must be at least 6 characters\&. +.TP +-keypasswd +.sp +.nf +\f3{\-alias \fIalias\fR} [\-keypass \fIold_keypass\fR] [\-new \fInew_keypass\fR] {\-storetype \fIstoretype\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-keystore \fIkeystore\fR} [\-storepass \fIstorepass\fR] {\-providerName \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}} {\-v}\fP +.fi +.sp +.sp +.nf +\f3{\-Jjavaoption}\fP +.fi +.sp + + +Changes the password under which the private/secret key identified by \f3alias\fR is protected, from \f3old_keypass\fR to \f3new_keypass\fR, which must be at least 6 characters\&. + +If the \f3-keypass\fR option is not provided at the command line, and the key password is different from the keystore password, then the user is prompted for it\&. + +If the \f3-new\fR option is not provided at the command line, then the user is prompted for it +.TP +-delete +.sp +.nf +\f3[\-alias \fIalias\fR] {\-storetype \fIstoretype\fR} {\-keystore \fIkeystore\fR} [\-storepass \fIstorepass\fR]\fP +.fi +.sp +.sp +.nf +\f3{\-providerName \fIprovider_name\fR} \fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}}\fP +.fi +.sp +.sp +.nf +\f3{\-v} {\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Deletes from the keystore the entry identified by \f3alias\fR\&. The user is prompted for the alias, when no alias is provided at the command line\&. +.TP +-changealias +.sp +.nf +\f3{\-alias \fIalias\fR} [\-destalias \fIdestalias\fR] [\-keypass \fIkeypass\fR] {\-storetype \fIstoretype\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-keystore \fIkeystore\fR} [\-storepass \fIstorepass\fR] {\-providerName \fIprovider_name\fR}\fP +.fi +.sp +.sp +.nf +\f3{\-providerClass \fIprovider_class_name\fR {\-providerArg \fIprovider_arg\fR}} {\-v}\fP +.fi +.sp +.sp +.nf +\f3{\-protected} {\-Jjavaoption}\fP +.fi +.sp + + +Move an existing keystore entry from the specified \f3alias\fR to a new alias, \f3destalias\fR\&. If no destination alias is provided, then the command prompts for one\&. If the original entry is protected with an entry password, then the password can be supplied with the \f3-keypass\fR option\&. If no key password is provided, then the \f3storepass\fR (if provided) is attempted first\&. If the attempt fails, then the user is prompted for a password\&. +.TP +-help +.br +Lists the basic commands and their options\&. + +For more information about a specific command, enter the following, where \f3command_name\fR is the name of the command: \f3keytool -command_name -help\fR\&. +.SH EXAMPLES +This example walks through the sequence of steps to create a keystore for managing public/private key pair and certificates from trusted entities\&. +.SS GENERATE\ THE\ KEY\ PAIR +First, create a keystore and generate the key pair\&. You can use a command such as the following typed as a single line: +.sp +.nf +\f3keytool \-genkeypair \-dname "cn=Mark Jones, ou=Java, o=Oracle, c=US"\fP +.fi +.nf +\f3 \-alias business \-keypass \fP +.fi +.nf +\f3 \-keystore /working/mykeystore\fP +.fi +.nf +\f3 \-storepass \-validity 180\fP +.fi +.nf +\f3\fP +.fi +.sp +The command creates the keystore named \f3mykeystore\fR in the working directory (assuming it does not already exist), and assigns it the password specified by \f3\fR\&. It generates a public/private key pair for the entity whose distinguished name has a common name of Mark Jones, organizational unit of Java, organization of Oracle and two-letter country code of US\&. It uses the default DSA key generation algorithm to create the keys; both are 1024 bits\&. +.PP +The command uses the default SHA1withDSA signature algorithm to create a self-signed certificate that includes the public key and the distinguished name information\&. The certificate is valid for 180 days, and is associated with the private key in a keystore entry referred to by the alias \f3business\fR\&. The private key is assigned the password specified by \f3\fR\&. +.PP +The command is significantly shorter when the option defaults are accepted\&. In this case, no options are required, and the defaults are used for unspecified options that have default values\&. You are prompted for any required values\&. You could have the following: +.sp +.nf +\f3keytool \-genkeypair\fP +.fi +.nf +\f3\fP +.fi +.sp +In this case, a keystore entry with the alias \f3mykey\fR is created, with a newly generated key pair and a certificate that is valid for 90 days\&. This entry is placed in the keystore named \f3\&.keystore\fR in your home directory\&. The keystore is created when it does not already exist\&. You are prompted for the distinguished name information, the keystore password, and the private key password\&. +.PP +The rest of the examples assume you executed the \f3-genkeypair\fR command without options specified, and that you responded to the prompts with values equal to those specified in the first \f3-genkeypair\fR command\&. For example, a distinguished name of \f3cn=Mark Jones\fR, \f3ou=Java\fR, \f3o=Oracle\fR, \f3c=US\fR)\&. +.SS REQUEST\ A\ SIGNED\ CERTIFICATE\ FROM\ A\ CA +Generating the key pair created a self-signed certificate\&. A certificate is more likely to be trusted by others when it is signed by a Certification Authority (CA)\&. To get a CA signature, first generate a Certificate Signing Request (CSR), as follows: +.sp +.nf +\f3keytool \-certreq \-file MarkJ\&.csr\fP +.fi +.nf +\f3\fP +.fi +.sp +This creates a CSR for the entity identified by the default alias \f3mykey\fR and puts the request in the file named MarkJ\&.csr\&. Submit this file to a CA, such as VeriSign\&. The CA authenticates you, the requestor (usually off-line), and returns a certificate, signed by them, authenticating your public key\&. In some cases, the CA returns a chain of certificates, each one authenticating the public key of the signer of the previous certificate in the chain\&. +.SS IMPORT\ A\ CERTIFICATE\ FOR\ THE\ CA +You now need to replace the self-signed certificate with a certificate chain, where each certificate in the chain authenticates the public key of the signer of the previous certificate in the chain, up to a root CA\&. +.PP +Before you import the certificate reply from a CA, you need one or more trusted certificates in your keystore or in the \f3cacerts\fR keystore file\&. See \f3-importcert\fR in Commands\&. +.TP 0.2i +\(bu +If the certificate reply is a certificate chain, then you need the top certificate of the chain\&. The root CA certificate that authenticates the public key of the CA\&. +.TP 0.2i +\(bu +If the certificate reply is a single certificate, then you need a certificate for the issuing CA (the one that signed it)\&. If that certificate is not self-signed, then you need a certificate for its signer, and so on, up to a self-signed root CA certificate\&. +.PP +The \f3cacerts\fR keystore file ships with several VeriSign root CA certificates, so you probably will not need to import a VeriSign certificate as a trusted certificate in your keystore\&. But if you request a signed certificate from a different CA, and a certificate authenticating that CA\&'s public key was not added to \f3cacerts\fR, then you must import a certificate from the CA as a trusted certificate\&. +.PP +A certificate from a CA is usually either self-signed or signed by another CA, in which case you need a certificate that authenticates that CA\&'s public key\&. Suppose company ABC, Inc\&., is a CA, and you obtain a file named A\f3BCCA\&.cer\fR that is supposed to be a self-signed certificate from ABC, that authenticates that CA\&'s public key\&. Be careful to ensure the certificate is valid before you import it as a trusted certificate\&. View it first with the \f3keytool -printcert\fR command or the \f3keytool -importcert\fR command without the \f3-noprompt\fR option, and make sure that the displayed certificate fingerprints match the expected ones\&. You can call the person who sent the certificate, and compare the fingerprints that you see with the ones that they show or that a secure public key repository shows\&. Only when the fingerprints are equal is it guaranteed that the certificate was not replaced in transit with somebody else\&'s (for example, an attacker\&'s) certificate\&. If such an attack takes place, and you did not check the certificate before you imported it, then you would be trusting anything the attacker has signed\&. +.PP +If you trust that the certificate is valid, then you can add it to your keystore with the following command: +.sp +.nf +\f3keytool \-importcert \-alias abc \-file ABCCA\&.cer\fP +.fi +.nf +\f3\fP +.fi +.sp +This command creates a trusted certificate entry in the keystore, with the data from the file ABCCA\&.cer, and assigns the alias \f3abc\fR to the entry\&. +.SS IMPORT\ THE\ CERTIFICATE\ REPLY\ FROM\ THE\ CA +After you import a certificate that authenticates the public key of the CA you submitted your certificate signing request to (or there is already such a certificate in the cacerts file), you can import the certificate reply and replace your self-signed certificate with a certificate chain\&. This chain is the one returned by the CA in response to your request (when the CA reply is a chain), or one constructed (when the CA reply is a single certificate) using the certificate reply and trusted certificates that are already available in the keystore where you import the reply or in the \f3cacerts\fR keystore file\&. +.PP +For example, if you sent your certificate signing request to VeriSign, then you can import the reply with the following, which assumes the returned certificate is named VSMarkJ\&.cer: +.sp +.nf +\f3keytool \-importcert \-trustcacerts \-file VSMarkJ\&.cer\fP +.fi +.nf +\f3\fP +.fi +.sp +.SS EXPORT\ A\ CERTIFICATE\ THAT\ AUTHENTICATES\ THE\ PUBLIC\ KEY +If you used the \f3jarsigner\fR command to sign a Java Archive (JAR) file, then clients that want to use the file will want to authenticate your signature\&. One way the clients can authenticate you is by first importing your public key certificate into their keystore as a trusted entry\&. +.PP +You can export the certificate and supply it to your clients\&. As an example, you can copy your certificate to a file named MJ\&.cer with the following command that assumes the entry has an alias of \f3mykey\fR: +.sp +.nf +\f3keytool \-exportcert \-alias mykey \-file MJ\&.cer\fP +.fi +.nf +\f3\fP +.fi +.sp +With the certificate and the signed JAR file, a client can use the \f3jarsigner\fR command to authenticate your signature\&. +.SS IMPORT\ KEYSTORE +The command \f3importkeystore\fR is used to import an entire keystore into another keystore, which means all entries from the source keystore, including keys and certificates, are all imported to the destination keystore within a single command\&. You can use this command to import entries from a different type of keystore\&. During the import, all new entries in the destination keystore will have the same alias names and protection passwords (for secret keys and private keys)\&. If the \f3keytool\fR command cannot recover the private keys or secret keys from the source keystore, then it prompts you for a password\&. If it detects alias duplication, then it asks you for a new alias, and you can specify a new alias or simply allow the \f3keytool\fR command to overwrite the existing one\&. +.PP +For example, to import entries from a typical JKS type keystore key\&.jks into a PKCS #11 type hardware-based keystore, use the command: +.sp +.nf +\f3keytool \-importkeystore\fP +.fi +.nf +\f3 \-srckeystore key\&.jks \-destkeystore NONE\fP +.fi +.nf +\f3 \-srcstoretype JKS \-deststoretype PKCS11\fP +.fi +.nf +\f3 \-srcstorepass \fP +.fi +.nf +\f3 \-deststorepass \fP +.fi +.nf +\f3\fP +.fi +.sp +The \f3importkeystore\fR command can also be used to import a single entry from a source keystore to a destination keystore\&. In this case, besides the options you see in the previous example, you need to specify the alias you want to import\&. With the \f3-srcalias\fR option specified, you can also specify the destination alias name in the command line, as well as protection password for a secret/private key and the destination protection password you want\&. The following command demonstrates this: +.sp +.nf +\f3keytool \-importkeystore\fP +.fi +.nf +\f3 \-srckeystore key\&.jks \-destkeystore NONE\fP +.fi +.nf +\f3 \-srcstoretype JKS \-deststoretype PKCS11\fP +.fi +.nf +\f3 \-srcstorepass \fP +.fi +.nf +\f3 \-deststorepass \fP +.fi +.nf +\f3 \-srcalias myprivatekey \-destalias myoldprivatekey\fP +.fi +.nf +\f3 \-srckeypass \fP +.fi +.nf +\f3 \-destkeypass \fP +.fi +.nf +\f3 \-noprompt\fP +.fi +.nf +\f3\fP +.fi +.sp +.SS GENERATE\ CERTIFICATES\ FOR\ AN\ SSL\ SERVER +The following are \f3keytool\fR commands to generate key pairs and certificates for three entities: Root CA (\f3root\fR), Intermediate CA (\f3ca\fR), and SSL server (\f3server\fR)\&. Ensure that you store all the certificates in the same keystore\&. In these examples, RSA is the recommended the key algorithm\&. +.sp +.nf +\f3keytool \-genkeypair \-keystore root\&.jks \-alias root \-ext bc:c\fP +.fi +.nf +\f3keytool \-genkeypair \-keystore ca\&.jks \-alias ca \-ext bc:c\fP +.fi +.nf +\f3keytool \-genkeypair \-keystore server\&.jks \-alias server\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3keytool \-keystore root\&.jks \-alias root \-exportcert \-rfc > root\&.pem\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3keytool \-storepass \-keystore ca\&.jks \-certreq \-alias ca |\fP +.fi +.nf +\f3 keytool \-storepass \-keystore root\&.jks\fP +.fi +.nf +\f3 \-gencert \-alias root \-ext BC=0 \-rfc > ca\&.pem\fP +.fi +.nf +\f3keytool \-keystore ca\&.jks \-importcert \-alias ca \-file ca\&.pem\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3keytool \-storepass \-keystore server\&.jks \-certreq \-alias server |\fP +.fi +.nf +\f3 keytool \-storepass \-keystore ca\&.jks \-gencert \-alias ca\fP +.fi +.nf +\f3 \-ext ku:c=dig,kE \-rfc > server\&.pem\fP +.fi +.nf +\f3cat root\&.pem ca\&.pem server\&.pem |\fP +.fi +.nf +\f3 keytool \-keystore server\&.jks \-importcert \-alias server\fP +.fi +.nf +\f3\fP +.fi +.sp +.SH TERMS +.TP +Keystore +A keystore is a storage facility for cryptographic keys and certificates\&. +.TP +Keystore entries +Keystores can have different types of entries\&. The two most applicable entry types for the \f3keytool\fR command include the following: + +\fIKey entries\fR: Each entry holds very sensitive cryptographic key information, which is stored in a protected format to prevent unauthorized access\&. Typically, a key stored in this type of entry is a secret key, or a private key accompanied by the certificate chain for the corresponding public key\&. See Certificate Chains\&. The \f3keytool\fR command can handle both types of entries, while the \f3jarsigner\fR tool only handles the latter type of entry, that is private keys and their associated certificate chains\&. + +\fITrusted certificate entries\fR: Each entry contains a single public key certificate that belongs to another party\&. The entry is called a trusted certificate because the keystore owner trusts that the public key in the certificate belongs to the identity identified by the subject (owner) of the certificate\&. The issuer of the certificate vouches for this, by signing the certificate\&. +.TP +KeyStore aliases +All keystore entries (key and trusted certificate entries) are accessed by way of unique aliases\&. + +An alias is specified when you add an entity to the keystore with the \f3-genseckey\fR command to generate a secret key, the \f3-genkeypair\fR command to generate a key pair (public and private key), or the \f3-importcert\fR command to add a certificate or certificate chain to the list of trusted certificates\&. Subsequent \f3keytool\fR commands must use this same alias to refer to the entity\&. + +For example, you can use the alias \f3duke\fR to generate a new public/private key pair and wrap the public key into a self-signed certificate with the following command\&. See Certificate Chains\&. +.sp +.nf +\f3keytool \-genkeypair \-alias duke \-keypass dukekeypasswd\fP +.fi +.nf +\f3\fP +.fi +.sp + + +This example specifies an initial password of \f3dukekeypasswd\fR required by subsequent commands to access the private key associated with the alias \f3duke\fR\&. If you later want to change Duke\&'s private key password, use a command such as the following: +.sp +.nf +\f3keytool \-keypasswd \-alias duke \-keypass dukekeypasswd \-new newpass\fP +.fi +.nf +\f3\fP +.fi +.sp + + +This changes the password from \f3dukekeypasswd\fR to \f3newpass\fR\&. A password should not be specified on a command line or in a script unless it is for testing purposes, or you are on a secure system\&. If you do not specify a required password option on a command line, then you are prompted for it\&. +.TP +KeyStore implementation +The \f3KeyStore\fR class provided in the \f3java\&.security\fR package supplies well-defined interfaces to access and modify the information in a keystore\&. It is possible for there to be multiple different concrete implementations, where each implementation is that for a particular type of keystore\&. + +Currently, two command-line tools (\f3keytool\fR and \f3jarsigner\fR) and a GUI-based tool named Policy Tool make use of keystore implementations\&. Because the \f3KeyStore\fR class is \f3public\fR, users can write additional security applications that use it\&. + +There is a built-in default implementation, provided by Oracle\&. It implements the keystore as a file with a proprietary keystore type (format) named JKS\&. It protects each private key with its individual password, and also protects the integrity of the entire keystore with a (possibly different) password\&. + +Keystore implementations are provider-based\&. More specifically, the application interfaces supplied by \f3KeyStore\fR are implemented in terms of a Service Provider Interface (SPI)\&. That is, there is a corresponding abstract \f3KeystoreSpi\fR class, also in the \f3java\&.security package\fR, which defines the Service Provider Interface methods that providers must implement\&. The term \fIprovider\fR refers to a package or a set of packages that supply a concrete implementation of a subset of services that can be accessed by the Java Security API\&. To provide a keystore implementation, clients must implement a provider and supply a \f3KeystoreSpi\fR subclass implementation, as described in How to Implement a Provider in the Java Cryptography Architecture at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider\&.html + +Applications can choose different types of keystore implementations from different providers, using the \f3getInstance\fR factory method supplied in the \f3KeyStore\fR class\&. A keystore type defines the storage and data format of the keystore information, and the algorithms used to protect private/secret keys in the keystore and the integrity of the keystore\&. Keystore implementations of different types are not compatible\&. + +The \f3keytool\fR command works on any file-based keystore implementation\&. It treats the keystore location that is passed to it at the command line as a file name and converts it to a \f3FileInputStream\fR, from which it loads the keystore information\&.)The \f3jarsigner\fR and \f3policytool\fR commands can read a keystore from any location that can be specified with a URL\&. + +For \f3keytool\fR and \f3jarsigner\fR, you can specify a keystore type at the command line, with the \f3-storetype\fR option\&. For Policy Tool, you can specify a keystore type with the \fIKeystore\fR menu\&. + +If you do not explicitly specify a keystore type, then the tools choose a keystore implementation based on the value of the \f3keystore\&.type\fR property specified in the security properties file\&. The security properties file is called \f3java\&.security\fR, and resides in the security properties directory, \f3java\&.home\elib\esecurity\fR on Windows and \f3java\&.home/lib/security\fR on Oracle Solaris, where \f3java\&.home\fR is the runtime environment directory\&. The \f3jre\fR directory in the SDK or the top-level directory of the Java Runtime Environment (JRE)\&. + +Each tool gets the \f3keystore\&.type\fR value and then examines all the currently installed providers until it finds one that implements a keystores of that type\&. It then uses the keystore implementation from that provider\&.The \f3KeyStore\fR class defines a static method named \f3getDefaultType\fR that lets applications and applets retrieve the value of the \f3keystore\&.type\fR property\&. The following line of code creates an instance of the default keystore type as specified in the \f3keystore\&.type\fR property: +.sp +.nf +\f3KeyStore keyStore = KeyStore\&.getInstance(KeyStore\&.getDefaultType());\fP +.fi +.nf +\f3\fP +.fi +.sp + + +The default keystore type is \f3jks\fR, which is the proprietary type of the keystore implementation provided by Oracle\&. This is specified by the following line in the security properties file: +.sp +.nf +\f3keystore\&.type=jks\fP +.fi +.nf +\f3\fP +.fi +.sp + + +To have the tools utilize a keystore implementation other than the default, you can change that line to specify a different keystore type\&. For example, if you have a provider package that supplies a keystore implementation for a keystore type called \f3pkcs12\fR, then change the line to the following: +.sp +.nf +\f3keystore\&.type=pkcs12\fP +.fi +.nf +\f3\fP +.fi +.sp + + +\fINote:\fR Case does not matter in keystore type designations\&. For example, JKS would be considered the same as jks\&. +.TP +Certificate +A certificate (or public-key certificate) is a digitally signed statement from one entity (the issuer), saying that the public key and some other information of another entity (the subject) has some specific value\&. The following terms are related to certificates: + +\fIPublic Keys\fR: These are numbers associated with a particular entity, and are intended to be known to everyone who needs to have trusted interactions with that entity\&. Public keys are used to verify signatures\&. + +\fIDigitally Signed\fR: If some data is digitally signed, then it is stored with the identity of an entity and a signature that proves that entity knows about the data\&. The data is rendered unforgeable by signing with the entity\&'s private key\&. + +\fIIdentity\fR: A known way of addressing an entity\&. In some systems, the identity is the public key, and in others it can be anything from an Oracle Solaris UID to an email address to an X\&.509 distinguished name\&. + +\fISignature\fR: A signature is computed over some data using the private key of an entity\&. The signer, which in the case of a certificate is also known as the issuer\&. + +\fIPrivate Keys\fR: These are numbers, each of which is supposed to be known only to the particular entity whose private key it is (that is, it is supposed to be kept secret)\&. Private and public keys exist in pairs in all public key cryptography systems (also referred to as public key crypto systems)\&. In a typical public key crypto system, such as DSA, a private key corresponds to exactly one public key\&. Private keys are used to compute signatures\&. + +\fIEntity\fR: An entity is a person, organization, program, computer, business, bank, or something else you are trusting to some degree\&. + +Public key cryptography requires access to users\&' public keys\&. In a large-scale networked environment, it is impossible to guarantee that prior relationships between communicating entities were established or that a trusted repository exists with all used public keys\&. Certificates were invented as a solution to this public key distribution problem\&. Now a Certification Authority (CA) can act as a trusted third party\&. CAs are entities such as businesses that are trusted to sign (issue) certificates for other entities\&. It is assumed that CAs only create valid and reliable certificates because they are bound by legal agreements\&. There are many public Certification Authorities, such as VeriSign, Thawte, Entrust, and so on\&. + +You can also run your own Certification Authority using products such as Microsoft Certificate Server or the Entrust CA product for your organization\&. With the \f3keytool\fR command, it is possible to display, import, and export certificates\&. It is also possible to generate self-signed certificates\&. + +The \f3keytool\fR command currently handles X\&.509 certificates\&. +.TP +X\&.509 Certificates +The X\&.509 standard defines what information can go into a certificate and describes how to write it down (the data format)\&. All the data in a certificate is encoded with two related standards called ASN\&.1/DER\&. Abstract Syntax Notation 1 describes data\&. The Definite Encoding Rules describe a single way to store and transfer that data\&. + +All X\&.509 certificates have the following data, in addition to the signature: + +\fIVersion\fR: This identifies which version of the X\&.509 standard applies to this certificate, which affects what information can be specified in it\&. Thus far, three versions are defined\&. The \f3keytool\fR command can import and export v1, v2, and v3 certificates\&. It generates v3 certificates\&. + +X\&.509 Version 1 has been available since 1988, is widely deployed, and is the most generic\&. + +X\&.509 Version 2 introduced the concept of subject and issuer unique identifiers to handle the possibility of reuse of subject or issuer names over time\&. Most certificate profile documents strongly recommend that names not be reused and that certificates should not make use of unique identifiers\&. Version 2 certificates are not widely used\&. + +X\&.509 Version 3 is the most recent (1996) and supports the notion of extensions where anyone can define an extension and include it in the certificate\&. Some common extensions are: KeyUsage (limits the use of the keys to particular purposes such as \f3signing-only\fR) and AlternativeNames (allows other identities to also be associated with this public key, for example\&. DNS names, email addresses, IP addresses)\&. Extensions can be marked critical to indicate that the extension should be checked and enforced or used\&. For example, if a certificate has the KeyUsage extension marked critical and set to \f3keyCertSign\fR, then when this certificate is presented during SSL communication, it should be rejected because the certificate extension indicates that the associated private key should only be used for signing certificates and not for SSL use\&. + +\fISerial number\fR: The entity that created the certificate is responsible for assigning it a serial number to distinguish it from other certificates it issues\&. This information is used in numerous ways\&. For example, when a certificate is revoked its serial number is placed in a Certificate Revocation List (CRL)\&. + +\fISignature algorithm identifier\fR: This identifies the algorithm used by the CA to sign the certificate\&. + +\fIIssuer name\fR: The X\&.500 Distinguished Name of the entity that signed the certificate\&. See X\&.500 Distinguished Names\&. This is typically a CA\&. Using this certificate implies trusting the entity that signed this certificate\&. In some cases, such as root or top-level CA certificates, the issuer signs its own certificate\&. + +\fIValidity period\fR: Each certificate is valid only for a limited amount of time\&. This period is described by a start date and time and an end date and time, and can be as short as a few seconds or almost as long as a century\&. The validity period chosen depends on a number of factors, such as the strength of the private key used to sign the certificate, or the amount one is willing to pay for a certificate\&. This is the expected period that entities can rely on the public value, when the associated private key has not been compromised\&. + +\fISubject name\fR: The name of the entity whose public key the certificate identifies\&. This name uses the X\&.500 standard, so it is intended to be unique across the Internet\&. This is the X\&.500 Distinguished Name (DN) of the entity\&. See X\&.500 Distinguished Names\&. For example, +.sp +.nf +\f3CN=Java Duke, OU=Java Software Division, O=Oracle Corporation, C=US\fP +.fi +.nf +\f3\fP +.fi +.sp + + +These refer to the subject\&'s common name (CN), organizational unit (OU), organization (O), and country (C)\&. + +\fISubject public key information\fR: This is the public key of the entity being named with an algorithm identifier that specifies which public key crypto system this key belongs to and any associated key parameters\&. +.TP +Certificate Chains +The \f3keytool\fR command can create and manage keystore key entries that each contain a private key and an associated certificate chain\&. The first certificate in the chain contains the public key that corresponds to the private key\&. + +When keys are first generated, the chain starts off containing a single element, a self-signed certificate\&. See \f3-genkeypair\fR in Commands\&. A self-signed certificate is one for which the issuer (signer) is the same as the subject\&. The subject is the entity whose public key is being authenticated by the certificate\&. Whenever the \f3-genkeypair\fR command is called to generate a new public/private key pair, it also wraps the public key into a self-signed certificate\&. + +Later, after a Certificate Signing Request (CSR) was generated with the \f3-certreq\fR command and sent to a Certification Authority (CA), the response from the CA is imported with \f3-importcert\fR, and the self-signed certificate is replaced by a chain of certificates\&. See the \f3-certreq\fR and \f3-importcert\fR options in Commands\&. At the bottom of the chain is the certificate (reply) issued by the CA authenticating the subject\&'s public key\&. The next certificate in the chain is one that authenticates the CA\&'s public key\&. + +In many cases, this is a self-signed certificate, which is a certificate from the CA authenticating its own public key, and the last certificate in the chain\&. In other cases, the CA might return a chain of certificates\&. In this case, the bottom certificate in the chain is the same (a certificate signed by the CA, authenticating the public key of the key entry), but the second certificate in the chain is a certificate signed by a different CA that authenticates the public key of the CA you sent the CSR to\&. The next certificate in the chain is a certificate that authenticates the second CA\&'s key, and so on, until a self-signed root certificate is reached\&. Each certificate in the chain (after the first) authenticates the public key of the signer of the previous certificate in the chain\&. + +Many CAs only return the issued certificate, with no supporting chain, especially when there is a flat hierarchy (no intermediates CAs)\&. In this case, the certificate chain must be established from trusted certificate information already stored in the keystore\&. + +A different reply format (defined by the PKCS #7 standard) includes the supporting certificate chain in addition to the issued certificate\&. Both reply formats can be handled by the \f3keytool\fR command\&. + +The top-level (root) CA certificate is self-signed\&. However, the trust into the root\&'s public key does not come from the root certificate itself, but from other sources such as a newspaper\&. This is because anybody could generate a self-signed certificate with the distinguished name of, for example, the VeriSign root CA\&. The root CA public key is widely known\&. The only reason it is stored in a certificate is because this is the format understood by most tools, so the certificate in this case is only used as a vehicle to transport the root CA\&'s public key\&. Before you add the root CA certificate to your keystore, you should view it with the \f3-printcert\fR option and compare the displayed fingerprint with the well-known fingerprint obtained from a newspaper, the root CA\&'s Web page, and so on\&. +.TP +The cacerts Certificates File +A certificates file named \f3cacerts\fR resides in the security properties directory, \f3java\&.home\elib\esecurity\fR on Windows and \f3java\&.home/lib/security\fR on Oracle Solaris, where \f3java\&.home\fR is the runtime environment\&'s directory, which would be the \f3jre\fR directory in the SDK or the top-level directory of the JRE\&. + +The \f3cacerts\fR file represents a system-wide keystore with CA certificates\&. System administrators can configure and manage that file with the \f3keytool\fR command by specifying \f3jks\fR as the keystore type\&. The \f3cacerts\fR keystore file ships with a default set of root CA certificates\&. You can list the default certificates with the following command: +.sp +.nf +\f3keytool \-list \-keystore java\&.home/lib/security/cacerts\fP +.fi +.nf +\f3\fP +.fi +.sp + + +The initial password of the \f3cacerts\fR keystore file is \f3changeit\fR\&. System administrators should change that password and the default access permission of that file upon installing the SDK\&. + +\fINote:\fR It is important to verify your \f3cacerts\fR file\&. Because you trust the CAs in the \f3cacerts\fR file as entities for signing and issuing certificates to other entities, you must manage the \f3cacerts\fR file carefully\&. The \f3cacerts\fR file should contain only certificates of the CAs you trust\&. It is your responsibility to verify the trusted root CA certificates bundled in the \f3cacerts\fR file and make your own trust decisions\&. + +To remove an untrusted CA certificate from the \f3cacerts\fR file, use the \f3delete\fR option of the \f3keytool\fR command\&. You can find the \f3cacerts\fR file in the JRE installation directory\&. Contact your system administrator if you do not have permission to edit this file +.TP +Internet RFC 1421 Certificate Encoding Standard +Certificates are often stored using the printable encoding format defined by the Internet RFC 1421 standard, instead of their binary encoding\&. This certificate format, also known as Base64 encoding, makes it easy to export certificates to other applications by email or through some other mechanism\&. + +Certificates read by the \f3-importcert\fR and \f3-printcert\fR commands can be in either this format or binary encoded\&. The \f3-exportcert\fR command by default outputs a certificate in binary encoding, but will instead output a certificate in the printable encoding format, when the \f3-rfc\fR option is specified\&. + +The \f3-list\fR command by default prints the SHA1 fingerprint of a certificate\&. If the \f3-v\fR option is specified, then the certificate is printed in human-readable format\&. If the \f3-rfc\fR option is specified, then the certificate is output in the printable encoding format\&. + +In its printable encoding format, the encoded certificate is bounded at the beginning and end by the following text: +.sp +.nf +\f3\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3encoded certificate goes here\&. \fP +.fi +.nf +\f3\fP +.fi +.nf +\f3\-\-\-\-\-END CERTIFICATE\-\-\-\-\-\fP +.fi +.nf +\f3\fP +.fi +.sp + +.TP +X\&.500 Distinguished Names +X\&.500 Distinguished Names are used to identify entities, such as those that are named by the \f3subject\fR and \f3issuer\fR (signer) fields of X\&.509 certificates\&. The \f3keytool\fR command supports the following subparts: + +\fIcommonName\fR: The common name of a person such as Susan Jones\&. + +\fIorganizationUnit\fR: The small organization (such as department or division) name\&. For example, Purchasing\&. + +\fIlocalityName\fR: The locality (city) name, for example, Palo Alto\&. + +\fIstateName\fR: State or province name, for example, California\&. + +\fIcountry\fR: Two-letter country code, for example, CH\&. + +When you supply a distinguished name string as the value of a \f3-dname\fR option, such as for the \f3-genkeypair\fR command, the string must be in the following format: +.sp +.nf +\f3CN=cName, OU=orgUnit, O=org, L=city, S=state, C=countryCode\fP +.fi +.nf +\f3\fP +.fi +.sp + + +All the italicized items represent actual values and the previous keywords are abbreviations for the following: +.sp +.nf +\f3CN=commonName\fP +.fi +.nf +\f3OU=organizationUnit\fP +.fi +.nf +\f3O=organizationName\fP +.fi +.nf +\f3L=localityName\fP +.fi +.nf +\f3S=stateName\fP +.fi +.nf +\f3C=country\fP +.fi +.nf +\f3\fP +.fi +.sp + + +A sample distinguished name string is: +.sp +.nf +\f3CN=Mark Smith, OU=Java, O=Oracle, L=Cupertino, S=California, C=US\fP +.fi +.nf +\f3\fP +.fi +.sp + + +A sample command using such a string is: +.sp +.nf +\f3keytool \-genkeypair \-dname "CN=Mark Smith, OU=Java, O=Oracle, L=Cupertino,\fP +.fi +.nf +\f3S=California, C=US" \-alias mark\fP +.fi +.nf +\f3\fP +.fi +.sp + + +Case does not matter for the keyword abbreviations\&. For example, CN, cn, and Cn are all treated the same\&. + +Order matters; each subcomponent must appear in the designated order\&. However, it is not necessary to have all the subcomponents\&. You can use a subset, for example: +.sp +.nf +\f3CN=Steve Meier, OU=Java, O=Oracle, C=US\fP +.fi +.nf +\f3\fP +.fi +.sp + + +If a distinguished name string value contains a comma, then the comma must be escaped by a backslash (\e) character when you specify the string on a command line, as in: +.sp +.nf +\f3cn=Peter Schuster, ou=Java\e, Product Development, o=Oracle, c=US\fP +.fi +.nf +\f3\fP +.fi +.sp + + +It is never necessary to specify a distinguished name string on a command line\&. When the distinguished name is needed for a command, but not supplied on the command line, the user is prompted for each of the subcomponents\&. In this case, a comma does not need to be escaped by a backslash (\e)\&. +.SH WARNINGS +.SS IMPORTING\ TRUSTED\ CERTIFICATES\ WARNING +\fIImportant\fR: Be sure to check a certificate very carefully before importing it as a trusted certificate\&. +.PP +Windows Example: + +View the certificate first with the \f3-printcert\fR command or the \f3-importcert\fR command without the \f3-noprompt\fR option\&. Ensure that the displayed certificate fingerprints match the expected ones\&. For example, suppose sends or emails you a certificate that you put it in a file named \f3\etmp\ecert\fR\&. Before you consider adding the certificate to your list of trusted certificates, you can execute a \f3-printcert\fR command to view its fingerprints, as follows: +.sp +.nf +\f3 keytool \-printcert \-file \etmp\ecert\fP +.fi +.nf +\f3 Owner: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fP +.fi +.nf +\f3 Issuer: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fP +.fi +.nf +\f3 Serial Number: 59092b34\fP +.fi +.nf +\f3 Valid from: Thu Sep 25 18:01:13 PDT 1997 until: Wed Dec 24 17:01:13 PST 1997\fP +.fi +.nf +\f3 Certificate Fingerprints:\fP +.fi +.nf +\f3 MD5: 11:81:AD:92:C8:E5:0E:A2:01:2E:D4:7A:D7:5F:07:6F\fP +.fi +.nf +\f3 SHA1: 20:B6:17:FA:EF:E5:55:8A:D0:71:1F:E8:D6:9D:C0:37:13:0E:5E:FE\fP +.fi +.nf +\f3 SHA256: 90:7B:70:0A:EA:DC:16:79:92:99:41:FF:8A:FE:EB:90:\fP +.fi +.nf +\f3 17:75:E0:90:B2:24:4D:3A:2A:16:A6:E4:11:0F:67:A4\fP +.fi +.sp + +.PP +Oracle Solaris Example: + +View the certificate first with the \f3-printcert\fR command or the \f3-importcert\fR command without the \f3-noprompt\fR option\&. Ensure that the displayed certificate fingerprints match the expected ones\&. For example, suppose someone sends or emails you a certificate that you put it in a file named \f3/tmp/cert\fR\&. Before you consider adding the certificate to your list of trusted certificates, you can execute a \f3-printcert\fR command to view its fingerprints, as follows: +.sp +.nf +\f3 keytool \-printcert \-file /tmp/cert\fP +.fi +.nf +\f3 Owner: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fP +.fi +.nf +\f3 Issuer: CN=ll, OU=ll, O=ll, L=ll, S=ll, C=ll\fP +.fi +.nf +\f3 Serial Number: 59092b34\fP +.fi +.nf +\f3 Valid from: Thu Sep 25 18:01:13 PDT 1997 until: Wed Dec 24 17:01:13 PST 1997\fP +.fi +.nf +\f3 Certificate Fingerprints:\fP +.fi +.nf +\f3 MD5: 11:81:AD:92:C8:E5:0E:A2:01:2E:D4:7A:D7:5F:07:6F\fP +.fi +.nf +\f3 SHA1: 20:B6:17:FA:EF:E5:55:8A:D0:71:1F:E8:D6:9D:C0:37:13:0E:5E:FE\fP +.fi +.nf +\f3 SHA256: 90:7B:70:0A:EA:DC:16:79:92:99:41:FF:8A:FE:EB:90:\fP +.fi +.nf +\f3 17:75:E0:90:B2:24:4D:3A:2A:16:A6:E4:11:0F:67:A4\fP +.fi +.nf +\f3\fP +.fi +.sp +Then call or otherwise contact the person who sent the certificate and compare the fingerprints that you see with the ones that they show\&. Only when the fingerprints are equal is it guaranteed that the certificate was not replaced in transit with somebody else\&'s certificate such as an attacker\&'s certificate\&. If such an attack took place, and you did not check the certificate before you imported it, then you would be trusting anything the attacker signed, for example, a JAR file with malicious class files inside\&. +.PP +\fINote:\fR It is not required that you execute a \f3-printcert\fR command before importing a certificate\&. This is because before you add a certificate to the list of trusted certificates in the keystore, the \f3-importcert\fR command prints out the certificate information and prompts you to verify it\&. You can then stop the import operation\&. However, you can do this only when you call the \f3-importcert\fR command without the \f3-noprompt\fR option\&. If the \f3-noprompt\fR option is specified, then there is no interaction with the user\&. +.SS PASSWORDS\ WARNING +Most commands that operate on a keystore require the store password\&. Some commands require a private/secret key password\&. Passwords can be specified on the command line in the \f3-storepass\fR and \f3-keypass\fR options\&. However, a password should not be specified on a command line or in a script unless it is for testing, or you are on a secure system\&. When you do not specify a required password option on a command line, you are prompted for it\&. +.SS CERTIFICATE\ CONFORMANCE\ WARNING +The Internet standard RFC 5280 has defined a profile on conforming X\&.509 certificates, which includes what values and value combinations are valid for certificate fields and extensions\&. See the standard at http://tools\&.ietf\&.org/rfc/rfc5280\&.txt +.PP +The \f3keytool\fR command does not enforce all of these rules so it can generate certificates that do not conform to the standard\&. Certificates that do not conform to the standard might be rejected by JRE or other applications\&. Users should ensure that they provide the correct options for \f3-dname\fR, \f3-ext\fR, and so on\&. +.SH NOTES +.SS IMPORT\ A\ NEW\ TRUSTED\ CERTIFICATE +Before you add the certificate to the keystore, the \f3keytool\fR command verifies it by attempting to construct a chain of trust from that certificate to a self-signed certificate (belonging to a root CA), using trusted certificates that are already available in the keystore\&. +.PP +If the \f3-trustcacerts\fR option was specified, then additional certificates are considered for the chain of trust, namely the certificates in a file named \f3cacerts\fR\&. +.PP +If the \f3keytool\fR command fails to establish a trust path from the certificate to be imported up to a self-signed certificate (either from the keystore or the \f3cacerts\fR file), then the certificate information is printed, and the user is prompted to verify it by comparing the displayed certificate fingerprints with the fingerprints obtained from some other (trusted) source of information, which might be the certificate owner\&. Be very careful to ensure the certificate is valid before importing it as a trusted certificate\&. See Importing Trusted Certificates Warning\&. The user then has the option of stopping the import operation\&. If the \f3-noprompt\fR option is specified, then there is no interaction with the user\&. +.SS IMPORT\ A\ CERTIFICATE\ REPLY +When you import a certificate reply, the certificate reply is validated with trusted certificates from the keystore, and optionally, the certificates configured in the \f3cacerts\fR keystore file when the \f3-trustcacert\fR\f3s\fR option is specified\&. See The cacerts Certificates File\&. +.PP +The methods of determining whether the certificate reply is trusted are as follows: +.TP 0.2i +\(bu +If the reply is a single X\&.509 certificate, then the \f3keytool\fR command attempts to establish a trust chain, starting at the certificate reply and ending at a self-signed certificate (belonging to a root CA)\&. The certificate reply and the hierarchy of certificates is used to authenticate the certificate reply from the new certificate chain of aliases\&. If a trust chain cannot be established, then the certificate reply is not imported\&. In this case, the \f3keytool\fR command does not print the certificate and prompt the user to verify it, because it is very difficult for a user to determine the authenticity of the certificate reply\&. +.TP 0.2i +\(bu +If the reply is a PKCS #7 formatted certificate chain or a sequence of X\&.509 certificates, then the chain is ordered with the user certificate first followed by zero or more CA certificates\&. If the chain ends with a self-signed root CA certificate and the\f3-trustcacerts\fR option was specified, the \f3keytool\fR command attempts to match it with any of the trusted certificates in the keystore or the \f3cacerts\fR keystore file\&. If the chain does not end with a self-signed root CA certificate and the \f3-trustcacerts\fR option was specified, the \f3keytool\fR command tries to find one from the trusted certificates in the keystore or the \f3cacerts\fR keystore file and add it to the end of the chain\&. If the certificate is not found and the \f3-noprompt\fR option is not specified, the information of the last certificate in the chain is printed, and the user is prompted to verify it\&. +.PP +If the public key in the certificate reply matches the user\&'s public key already stored with \f3alias\fR, then the old certificate chain is replaced with the new certificate chain in the reply\&. The old chain can only be replaced with a valid \f3keypass\fR, and so the password used to protect the private key of the entry is supplied\&. If no password is provided, and the private key password is different from the keystore password, the user is prompted for it\&. +.PP +This command was named \f3-import\fR in earlier releases\&. This old name is still supported in this release\&. The new name, \f3-importcert\fR, is preferred going forward\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +jar(1) +.TP 0.2i +\(bu +jarsigner(1) +.TP 0.2i +\(bu +Trail: Security Features in Java SE at http://docs\&.oracle\&.com/javase/tutorial/security/index\&.html +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/orbd.1 b/FCL/src/main/assets/java/man/man1/orbd.1 new file mode 100644 index 00000000..f4478eca --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/orbd.1 @@ -0,0 +1,213 @@ +'\" t +.\" Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 21 November 2013 +.\" SectDesc: Java IDL and RMI-IIOP Tools +.\" Title: orbd.1 +.\" +.if n .pl 99999 +.TH orbd 1 "21 November 2013" "JDK 8" "Java IDL and RMI-IIOP Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +orbd \- Enables clients to locate and call persistent objects on servers in the CORBA environment\&. +.SH SYNOPSIS +.sp +.nf + +\fBorbd\fR [ \fIoptions\fR ] +.fi +.sp +.TP +\fIoptions\fR +Command-line options\&. See Options\&. +.SH DESCRIPTION +The \f3orbd\fR command enables clients to transparently locate and call persistent objects on servers in the CORBA environment\&. The Server Manager included with the orbd tool is used to enable clients to transparently locate and call persistent objects on servers in the CORBA environment\&. The persistent servers, while publishing the persistent object references in the naming service, include the port number of the ORBD in the object reference instead of the port number of the server\&. The inclusion of an ORBD port number in the object reference for persistent object references has the following advantages: +.TP 0.2i +\(bu +The object reference in the naming service remains independent of the server life cycle\&. For example, the object reference could be published by the server in the Naming Service when it is first installed, and then, independent of how many times the server is started or shut down, the ORBD returns the correct object reference to the calling client\&. +.TP 0.2i +\(bu +The client needs to look up the object reference in the naming service only once, and can keep reusing this reference independent of the changes introduced due to server life cycle\&. +.PP +To access the ORBD Server Manager, the server must be started using \f3servertool\fR, which is a command-line interface for application programmers to register, unregister, start up, and shut down a persistent server\&. For more information on the Server Manager, see Server Manager\&. +.PP +When \f3orbd\fR starts, it also starts a naming service\&. For more information about the naming service\&. See Start and Stop the Naming Service\&. +.SH OPTIONS +.TP +-ORBInitialPort \fInameserverport\fR +.br +Required\&. Specifies the port on which the name server should be started\&. After it is started, \f3orbd\fR listens for incoming requests on this port\&. On Oracle Solaris software, you must become the root user to start a process on a port below 1024\&. For this reason, Oracle recommends that you use a port number above or equal to 1024\&. +.SS NONREQUIRED\ OPTIONS +.TP +-port \fIport\fR +.br +Specifies the activation port where ORBD should be started, and where ORBD will be accepting requests for persistent objects\&. The default value for this port is 1049\&. This port number is added to the port field of the persistent Interoperable Object References (IOR)\&. +.TP +-defaultdb \fIdirectory\fR +.br +Specifies the base where the ORBD persistent storage directory, \f3orb\&.db\fR, is created\&. If this option is not specified, then the default value is \f3\&./orb\&.db\fR\&. +.TP +-serverPollingTime \fImilliseconds\fR +.br +Specifies how often ORBD checks for the health of persistent servers registered through \f3servertool\fR\&. The default value is 1000 ms\&. The value specified for \f3milliseconds\fR must be a valid positive integer\&. +.TP +-serverStartupDelay milliseconds +.br +Specifies how long ORBD waits before sending a location forward exception after a persistent server that is registered through \f3servertool\fR is restarted\&. The default value is 1000 ms\&. The value specified for \f3milliseconds\fR must be a valid positive integer\&. +.TP +-J\fIoption\fR +.br +Passes \f3option\fR to the Java Virtual Machine, where \f3option\fR is one of the options described on the reference page for the Java application launcher\&. For example, \f3-J-Xms48m\fR sets the startup memory to 48 MB\&. See java(1)\&. +.SS START\ AND\ STOP\ THE\ NAMING\ SERVICE +A naming service is a CORBA service that allows CORBA objects to be named by means of binding a name to an object reference\&. The name binding can be stored in the naming service, and a client can supply the name to obtain the desired object reference\&. +.PP +Before running a client or a server, you will start ORBD\&. ORBD includes a persistent naming service and a transient naming service, both of which are an implementation of the COS Naming Service\&. +.PP +The Persistent Naming Service provides persistence for naming contexts\&. This means that this information is persistent across service shutdowns and startups, and is recoverable in the event of a service failure\&. If ORBD is restarted, then the Persistent Naming Service restores the naming context graph, so that the binding of all clients\&' and servers\&' names remains intact (persistent)\&. +.PP +For backward compatibility, \f3tnameserv\fR, a Transient Naming Service that shipped with earlier releases of the JDK, is also included in this release of Java SE\&. A transient naming service retains naming contexts as long as it is running\&. If there is a service interruption, then the naming context graph is lost\&. +.PP +The \f3-ORBInitialPort\fR argument is a required command-line argument for \f3orbd\fR, and is used to set the port number on which the naming service runs\&. The following instructions assume you can use port 1050 for the Java IDL Object Request Broker Daemon\&. When using Oracle Solaris software, you must become a root user to start a process on a port lower than 1024\&. For this reason, it is recommended that you use a port number above or equal to 1024\&. You can substitute a different port when necessary\&. +.PP +To start \f3orbd\fR from a UNIX command shell, enter: +.sp +.nf +\f3orbd \-ORBInitialPort 1050&\fP +.fi +.nf +\f3\fP +.fi +.sp +From an MS-DOS system prompt (Windows), enter: +.sp +.nf +\f3start orbd \-ORBInitialPort 1050\fP +.fi +.nf +\f3\fP +.fi +.sp +Now that ORBD is running, you can run your server and client applications\&. When running the client and server applications, they must be made aware of the port number (and machine name, when applicable) where the Naming Service is running\&. One way to do this is to add the following code to your application: +.sp +.nf +\f3Properties props = new Properties();\fP +.fi +.nf +\f3props\&.put("org\&.omg\&.CORBA\&.ORBInitialPort", "1050");\fP +.fi +.nf +\f3props\&.put("org\&.omg\&.CORBA\&.ORBInitialHost", "MyHost");\fP +.fi +.nf +\f3ORB orb = ORB\&.init(args, props);\fP +.fi +.nf +\f3\fP +.fi +.sp +In this example, the naming service is running on port 1050 on host \f3MyHost\fR\&. Another way is to specify the port number and/or machine name when running the server or client application from the command line\&. For example, you would start your \f3HelloApplication\fR with the following command line: +.sp +.nf +\f3java HelloApplication \-ORBInitialPort 1050 \-ORBInitialHost MyHost\fP +.fi +.nf +\f3\fP +.fi +.sp +To stop the naming service, use the relevant operating system command, such as \f3pkill\fR\f3orbd\fR on Oracle Solaris, or \fICtrl+C\fR in the DOS window in which \f3orbd\fR is running\&. Note that names registered with the naming service can disappear when the service is terminated because of a transient naming service\&. The Java IDL naming service will run until it is explicitly stopped\&. +.PP +For more information about the naming service included with ORBD, see Naming Service at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlNaming\&.html +.SH SERVER\ MANAGER +To access the ORBD Server Manager and run a persistent server, the server must be started with \f3servertool\fR, which is a command-line interface for application programmers to register, unregister, start up, and shut down a persistent server\&. When a server is started using \f3servertool\fR, it must be started on the same host and port on which \f3orbd\fR is executing\&. If the server is run on a different port, then the information stored in the database for local contexts will be invalid and the service will not work properly\&. +.PP +See Java IDL: The "Hello World" Example at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlExample\&.html +.PP +In this example, you run the \f3idlj\fR compiler and \f3javac\fR compiler as shown in the tutorial\&. To run the ORBD Server Manager, follow these steps for running the application: +.PP +Start \f3orbd\fR\&. +.PP +UNIX command shell, enter: \f3orbd -ORBInitialPort 1050\fR\&. +.PP +MS-DOS system prompt (Windows), enter: \f3s\fR\f3tart orbd -ORBInitialPort 105\fR\f30\fR\&. +.PP +Port 1050 is the port on which you want the name server to run\&. The \f3-ORBInitialPort\fR option is a required command-line argument\&. When using Oracle Solaris software, you must become a root user to start a process on a port below 1024\&. For this reason, it is recommended that you use a port number above or equal to 1024\&. +.PP +Start the \f3servertool\fR: \f3servertool -ORBInitialPort 1050\fR\&. +.PP +Make sure the name server (\f3orbd\fR) port is the same as in the previous step, for example, \f3-ORBInitialPort 1050\&.\fR The \f3servertool\fR must be started on the same port as the name server\&. +.PP +In the \f3servertool\fR command line interface, start the \f3Hello\fR server from the \f3servertool\fR prompt: +.sp +.nf +\f3servertool > register \-server HelloServer \-classpath \&. \-applicationName\fP +.fi +.nf +\f3 HelloServerApName\fP +.fi +.nf +\f3\fP +.fi +.sp +The \f3servertool\fR registers the server, assigns it the name \f3HelloServerApName\fR, and displays its server ID with a listing of all registered servers\&.Run the client application from another terminal window or prompt: +.sp +.nf +\f3java HelloClient \-ORBInitialPort 1050 \-ORBInitialHost localhost\fP +.fi +.nf +\f3\fP +.fi +.sp +For this example, you can omit \f3-ORBInitialHost localhost\fR because the name server is running on the same host as the \f3Hello\fR client\&. If the name server is running on a different host, then use the -\f3ORBInitialHost nameserverhost\fR option to specify the host on which the IDL name server is running\&.Specify the name server (\f3orbd\fR) port as done in the previous step, for example, \f3-ORBInitialPort 1050\fR\&. When you finish experimenting with the ORBD Server Manager, be sure to shut down or terminate the name server (\f3orbd\fR) and \f3servertool\fR\&. To shut down \f3orbd\fR from am MS-DOS prompt, select the window that is running the server and enter \fICtrl+C\fR to shut it down\&. +.PP +To shut down \f3orbd\fR from an Oracle Solaris shell, find the process, and terminate with the \f3kill\fR command\&. The server continues to wait for invocations until it is explicitly stopped\&. To shut down the \f3servertool\fR, type \fIquit\fR and press the \fIEnter\fR key\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +servertool(1) +.TP 0.2i +\(bu +Naming Service at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlNaming\&.html +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/pack200.1 b/FCL/src/main/assets/java/man/man1/pack200.1 new file mode 100644 index 00000000..667d367b --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/pack200.1 @@ -0,0 +1,290 @@ +'\" t +.\" Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 21 November 2013 +.\" SectDesc: Java Deployment Tools +.\" Title: pack200.1 +.\" +.if n .pl 99999 +.TH pack200 1 "21 November 2013" "JDK 8" "Java Deployment Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +pack200 \- Packages a JAR file into a compressed pack200 file for web deployment\&. +.SH SYNOPSIS +.sp +.nf + +\fBpack200\fR [\fIoptions\fR] \fIoutput\-file\fR \fIJAR\-file\fR +.fi +.sp +Options can be in any order\&. The last option on the command line or in a properties file supersedes all previously specified options\&. +.TP +\fIoptions\fR +The command-line options\&. See Options\&. +.TP +\fIoutput-file\fR +Name of the output file\&. +.TP +\fIJAR-file\fR +Name of the input file\&. +.SH DESCRIPTION +The \f3pack200\fR command is a Java application that transforms a JAR file into a compressed pack200 file with the Java gzip compressor\&. The pack200 files are highly compressed files that can be directly deployed to save bandwidth and reduce download time\&. +.PP +The \f3pack200\fR command has several options to fine-tune and set the compression engine\&. The typical usage is shown in the following example, where \f3myarchive\&.pack\&.gz\fR is produced with the default \f3pack200\fR command settings: +.sp +.nf +\f3pack200 myarchive\&.pack\&.gz myarchive\&.jar\fP +.fi +.nf +\f3\fP +.fi +.sp +.SH OPTIONS +.TP +-r, --repack +.br +Produces a JAR file by packing and unpacking a JAR file\&. The resulting file can be used as an input to the \f3jarsigner\fR(1) tool\&. The following example packs and unpacks the myarchive\&.jar file: +.sp +.nf +\f3pack200 \-\-repack myarchive\-packer\&.jar myarchive\&.jar\fP +.fi +.nf +\f3pack200 \-\-repack myarchive\&.jar\fP +.fi +.nf +\f3\fP +.fi +.sp + + +The following example preserves the order of files in the input file\&. +.TP +-g, --no-gzip +.br +Produces a \f3pack200\fR file\&. With this option, a suitable compressor must be used, and the target system must use a corresponding decompresser\&. +.sp +.nf +\f3pack200 \-\-no\-gzip myarchive\&.pack myarchive\&.jar\fP +.fi +.nf +\f3\fP +.fi +.sp + +.TP +-G, --strip-debug +.br +Strips debugging attributes from the output\&. These include \f3SourceFile\fR, \f3LineNumberTable\fR, \f3LocalVariableTable\fR and \f3LocalVariableTypeTable\fR\&. Removing these attributes reduces the size of both downloads and installations, but reduces the usefulness of debuggers\&. +.TP +--keep-file-order +.br +Preserve the order of files in the input file\&. This is the default behavior\&. +.TP +-O, --no-keep-file-order +.br +The packer reorders and transmits all elements\&. The packer can also remove JAR directory names to reduce the download size\&. However, certain JAR file optimizations, such as indexing, might not work correctly\&. +.TP +-S\fIvalue\fR , --segment-limit=\fIvalue\fR +.br +The value is the estimated target size \fIN\fR (in bytes) of each archive segment\&. If a single input file requires more than \fIN\fR bytes, then its own archive segment is provided\&. As a special case, a value of \f3-1\fR produces a single large segment with all input files, while a value of 0 produces one segment for each class\&. Larger archive segments result in less fragmentation and better compression, but processing them requires more memory\&. + +The size of each segment is estimated by counting the size of each input file to be transmitted in the segment with the size of its name and other transmitted properties\&. + +The default is -1, which means that the packer creates a single segment output file\&. In cases where extremely large output files are generated, users are strongly encouraged to use segmenting or break up the input file into smaller JARs\&. + +A 10 MB JAR packed without this limit typically packs about 10 percent smaller, but the packer might require a larger Java heap (about 10 times the segment limit)\&. +.TP +-E\fIvalue\fR , --effort=\fIvalue\fR +.br +If the value is set to a single decimal digit, then the packer uses the indicated amount of effort in compressing the archive\&. Level 1 might produce somewhat larger size and faster compression speed, while level 9 takes much longer, but can produce better compression\&. The special value 0 instructs the \f3pack200\fR command to copy through the original JAR file directly with no compression\&. The JSR 200 standard requires any unpacker to understand this special case as a pass-through of the entire archive\&. + +The default is 5, to invest a modest amount of time to produce reasonable compression\&. +.TP +-H\fIvalue\fR , --deflate-hint=\fIvalue\fR +.br +Overrides the default, which preserves the input information, but can cause the transmitted archive to be larger\&. The possible values are: \f3true\fR, \f3false\fR, or \f3keep\fR\&. + +If the \f3value\fR is \f3true\fR or false, then the \f3packer200\fR command sets the deflation hint accordingly in the output archive and does not transmit the individual deflation hints of archive elements\&. + +The \f3keep\fR value preserves deflation hints observed in the input JAR\&. This is the default\&. +.TP +-m\fIvalue\fR , --modification-time=\fIvalue\fR +.br +The possible values are \f3latest\fR and \f3keep\fR\&. + +If the value is latest, then the packer attempts to determine the latest modification time, among all the available entries in the original archive, or the latest modification time of all the available entries in that segment\&. This single value is transmitted as part of the segment and applied to all the entries in each segment\&. This can marginally decrease the transmitted size of the archive at the expense of setting all installed files to a single date\&. + +If the value is \f3keep\fR, then modification times observed in the input JAR are preserved\&. This is the default\&. +.TP +-P\fIfile\fR , --pass-file=\fIfile\fR +.br +Indicates that a file should be passed through bytewise with no compression\&. By repeating the option, multiple files can be specified\&. There is no pathname transformation, except that the system file separator is replaced by the JAR file separator forward slash (/)\&. The resulting file names must match exactly as strings with their occurrences in the JAR file\&. If \f3file\fR is a directory name, then all files under that directory are passed\&. +.TP +-U\fIaction\fR , --unknown-attribute=\fIaction\fR +.br +Overrides the default behavior, which means that the class file that contains the unknown attribute is passed through with the specified \f3action\fR\&. The possible values for actions are \f3error\fR, \f3strip\fR, or \f3pass\fR\&. + +If the value is \f3error\fR, then the entire \f3pack200\fR command operation fails with a suitable explanation\&. + +If the value is \f3strip\fR, then the attribute is dropped\&. Removing the required Java Virtual Machine (JVM) attributes can cause class loader failures\&. + +If the value is \f3pass\fR, then the entire class is transmitted as though it is a resource\&. +.TP +.nf +-C\fIattribute-name\fR=\fIlayout\fR , --class-attribute=\fIattribute-name\fR=\fIaction\fR +.br +.fi +See next option\&. +.TP +.nf +-F\fIattribute-name\fR=\fIlayout\fR , --field-attribute=\fIattribute-name\fR=\fIaction\fR +.br +.fi +See next option\&. +.TP +.nf +-M\fIattribute-name\fR=\fIlayout\fR , --method-attribute=\fIattribute-name\fR=\fIaction\fR +.br +.fi +See next option\&. +.TP +.nf +-D\fIattribute-name\fR=\fIlayout\fR , --code-attribute=\fIattribute-name\fR=\fIaction\fR +.br +.fi +With the previous four options, the attribute layout can be specified for a class entity, such as \f3class-attribute\fR, \f3field-attribute\fR, \f3method-attribute\fR, and \f3code-attribute\fR\&. The \fIattribute-name\fR is the name of the attribute for which the layout or action is being defined\&. The possible values for \fIaction\fR are \f3some-layout-string\fR, \f3error\fR, \f3strip\fR, \f3pass\fR\&. + +\f3some-layout-string\fR: The layout language is defined in the JSR 200 specification, for example: \f3--class-attribute=SourceFile=RUH\fR\&. + +If the value is \f3error\fR, then the \f3pack200\fR operation fails with an explanation\&. + +If the value is \f3strip\fR, then the attribute is removed from the output\&. Removing JVM-required attributes can cause class loader failures\&. For example, \f3--class-attribute=CompilationID=pass\fR causes the class file that contains this attribute to be passed through without further action by the packer\&. + +If the value is \f3pass\fR, then the entire class is transmitted as though it is a resource\&. +.TP +-f \fIpack\&.properties\fR , --config-file=\fIpack\&.properties\fR +.br +A configuration file, containing Java properties to initialize the packer, can be specified on the command line\&. +.sp +.nf +\f3pack200 \-f pack\&.properties myarchive\&.pack\&.gz myarchive\&.jar\fP +.fi +.nf +\f3more pack\&.properties\fP +.fi +.nf +\f3# Generic properties for the packer\&.\fP +.fi +.nf +\f3modification\&.time=latest\fP +.fi +.nf +\f3deflate\&.hint=false\fP +.fi +.nf +\f3keep\&.file\&.order=false\fP +.fi +.nf +\f3# This option will cause the files bearing new attributes to\fP +.fi +.nf +\f3# be reported as an error rather than passed uncompressed\&.\fP +.fi +.nf +\f3unknown\&.attribute=error\fP +.fi +.nf +\f3# Change the segment limit to be unlimited\&.\fP +.fi +.nf +\f3segment\&.limit=\-1\fP +.fi +.nf +\f3\fP +.fi +.sp + +.TP +-v, --verbose +.br +Outputs minimal messages\&. Multiple specification of this option will create more verbose messages\&. +.TP +-q, --quiet +.br +Specifies quiet operation with no messages\&. +.TP +-l\fIfilename\fR , --log-file=\fIfilename\fR +.br +Specifies a log file to output messages\&. +.TP +-?, -h, --help +.br +Prints help information about this command\&. +.TP +-V, --version +.br +Prints version information about this command\&. +.TP +-J\fIoption\fR +.br +Passes the specified option to the Java Virtual Machine\&. For more information, see the reference page for the java(1) command\&. For example, \f3-J-Xms48m\fR sets the startup memory to 48 MB\&. +.SH EXIT\ STATUS +The following exit values are returned: 0 for successful completion and a number greater than 0 when an error occurs\&. +.SH NOTES +This command should not be confused with \f3pack\fR(1)\&. The \f3pack\fR and \f3pack200\fR commands are separate products\&. +.PP +The Java SE API Specification provided with the JDK is the superseding authority, when there are discrepancies\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +unpack200(1) +.TP 0.2i +\(bu +jar(1) +.TP 0.2i +\(bu +jarsigner(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/policytool.1 b/FCL/src/main/assets/java/man/man1/policytool.1 new file mode 100644 index 00000000..5af5919b --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/policytool.1 @@ -0,0 +1,114 @@ +'\" t +.\" Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 03 March 2015 +.\" SectDesc: Security Tools +.\" Title: policytool.1 +.\" +.if n .pl 99999 +.TH policytool 1 "03 March 2015" "JDK 8" "Security Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +policytool \- Reads and writes a plain text policy file based on user input through the utility GUI\&. +.SH SYNOPSIS +.sp +.nf + +\fBpolicytool\fR [ \fB\-file\fR ] [ \fIfilename\fR ] +.fi +.sp +.TP +-file +.br +Directs the \f3policytool\fR command to load a policy file\&. +.TP +\fIfilename\fR +The name of the file to be loaded\&. +.PP +\fIExamples\fR: +.PP +Run the policy tool administrator utility: +.sp +.nf +\f3policytool\fP +.fi +.nf +\f3\fP +.fi +.sp +Run the \f3policytool\fR command and load the specified file: +.sp +.nf +\f3policytool \-file \fImypolicyfile\fR\fP +.fi +.nf +\f3\fP +.fi +.sp +.SH DESCRIPTION +The \f3policytool\fR command calls an administrator\&'s GUI that enables system administrators to manage the contents of local policy files\&. A policy file is a plain-text file with a \f3\&.policy\fR extension, that maps remote requestors by domain, to permission objects\&. For details, see Default Policy Implementation and Policy File Syntax at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/PolicyFiles\&.html +.SH OPTIONS +.TP +-file +.br +Directs the \f3policytool\fR command to load a policy file\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +Default Policy Implementation and Policy File Syntax at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/PolicyFiles\&.html +.TP 0.2i +\(bu +Policy File Creation and Management at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/PolicyGuide\&.html +.TP 0.2i +\(bu +Permissions in Java SE Development Kit (JDK) at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/permissions\&.html +.TP 0.2i +\(bu +Java Security Overview at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/overview/jsoverview\&.html +.TP 0.2i +\(bu +Java Cryptography Architecture (JCA) Reference Guide at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec\&.html +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/rmid.1 b/FCL/src/main/assets/java/man/man1/rmid.1 new file mode 100644 index 00000000..4d8e3ddb --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/rmid.1 @@ -0,0 +1,314 @@ +'\" t +.\" Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 21 November 2013 +.\" SectDesc: Remote Method Invocation (RMI) Tools +.\" Title: rmid.1 +.\" +.if n .pl 99999 +.TH rmid 1 "21 November 2013" "JDK 8" "Remote Method Invocation (RMI) Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +rmid \- Starts the activation system daemon that enables objects to be registered and activated in a Java Virtual Machine (JVM)\&. +.SH SYNOPSIS +.sp +.nf + +\fBrmid\fR [\fIoptions\fR] +.fi +.sp +.TP +\fIoptions\fR +The command-line options\&. See Options\&. +.SH DESCRIPTION +The \f3rmid\fR command starts the activation system daemon\&. The activation system daemon must be started before activatable objects can be either registered with the activation system or activated in a JVM\&. For details on how to write programs that use activatable objects, the \fIUsing Activation\fR tutorial at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/rmi/activation/overview\&.html +.PP +Start the daemon by executing the \f3rmid\fR command and specifying a security policy file, as follows: +.sp +.nf +\f3rmid \-J\-Djava\&.security\&.policy=rmid\&.policy\fP +.fi +.nf +\f3\fP +.fi +.sp +When you run Oracle\(cqs implementation of the \f3rmid\fR command, by default you must specify a security policy file so that the \f3rmid\fR command can verify whether or not the information in each \f3ActivationGroupDesc\fR is allowed to be used to start a JVM for an activation group\&. Specifically, the command and options specified by the \f3CommandEnvironment\fR and any properties passed to an \f3ActivationGroupDesc\fR constructor must now be explicitly allowed in the security policy file for the \f3rmid\fR command\&. The value of the \f3sun\&.rmi\&.activation\&.execPolicy\fR property dictates the policy that the \f3rmid\fR command uses to determine whether or not the information in an \f3ActivationGroupDesc\fR can be used to start a JVM for an activation group\&. For more information see the description of the -J-Dsun\&.rmi\&.activation\&.execPolicy=policy option\&. +.PP +Executing the \f3rmid\fR command starts the Activator and an internal registry on the default port1098 and binds an \f3ActivationSystem\fR to the name \f3java\&.rmi\&.activation\&.ActivationSystem\fR in this internal registry\&. +.PP +To specify an alternate port for the registry, you must specify the \f3-port\fR option when you execute the \f3rmid\fR command\&. For example, the following command starts the activation system daemon and a registry on the registry\&'s default port, 1099\&. +.sp +.nf +\f3rmid \-J\-Djava\&.security\&.policy=rmid\&.policy \-port 1099\fP +.fi +.nf +\f3\fP +.fi +.sp +.SH START\ RMID\ ON\ DEMAND +An alternative to starting \f3rmid\fR from the command line is to configure \f3inetd\fR (Oracle Solaris) or \f3xinetd\fR (Linux) to start \f3rmid\fR on demand\&. +.PP +When RMID starts, it attempts to obtain an inherited channel (inherited from \f3inetd\fR/\f3xinetd\fR) by calling the \f3System\&.inheritedChannel\fR method\&. If the inherited channel is null or not an instance of \f3java\&.nio\&.channels\&.ServerSocketChannel\fR, then RMID assumes that it was not started by \f3inetd\fR/\f3xinetd\fR, and it starts as previously described\&. +.PP +If the inherited channel is a \f3ServerSocketChannel\fR instance, then RMID uses the \f3java\&.net\&.ServerSocket\fR obtained from the \f3ServerSocketChannel\fR as the server socket that accepts requests for the remote objects it exports: The registry in which the \f3java\&.rmi\&.activation\&.ActivationSystem\fR is bound and the \f3java\&.rmi\&.activation\&.Activator\fR remote object\&. In this mode, RMID behaves the same as when it is started from the command line, except in the following cases: +.TP 0.2i +\(bu +Output printed to \f3System\&.err\fR is redirected to a file\&. This file is located in the directory specified by the \f3java\&.io\&.tmpdir\fR system property (typically \f3/var/tmp\fR or \f3/tmp\fR) with the prefix \f3rmid-err\fR and the suffix \f3tmp\fR\&. +.TP 0.2i +\(bu +The \f3-port\fR option is not allowed\&. If this option is specified, then RMID exits with an error message\&. +.TP 0.2i +\(bu +The \f3-log\fR option is required\&. If this option is not specified, then RMID exits with an error message +.PP +See the man pages for \f3inetd\fR (Oracle Solaris) or \f3xinetd\fR (Linux) for details on how to configure services to be started on demand\&. +.SH OPTIONS +.TP +-C\fIoption\fR +.br +Specifies an option that is passed as a command-line argument to each child process (activation group) of the \f3rmid\fR command when that process is created\&. For example, you could pass a property to each virtual machine spawned by the activation system daemon: +.sp +.nf +\f3rmid \-C\-Dsome\&.property=value\fP +.fi +.nf +\f3\fP +.fi +.sp + + +This ability to pass command-line arguments to child processes can be useful for debugging\&. For example, the following command enables server-call logging in all child JVMs\&. +.sp +.nf +\f3rmid \-C\-Djava\&.rmi\&.server\&.logCalls=true\fP +.fi +.nf +\f3\fP +.fi +.sp + +.TP +-J\fIoption\fR +.br +Specifies an option that is passed to the Java interpreter running RMID\&. For example, to specify that the \f3rmid\fR command use a policy file named \f3rmid\&.policy\fR, the \f3-J\fR option can be used to define the \f3java\&.security\&.policy\fR property on the \f3rmid\fR command line, for example: +.sp +.nf +\f3rmid \-J\-Djava\&.security\&.policy\-rmid\&.policy\fP +.fi +.nf +\f3\fP +.fi +.sp + +.TP +-J-Dsun\&.rmi\&.activation\&.execPolicy=\fIpolicy\fR +.br +Specifies the policy that RMID employs to check commands and command-line options used to start the JVM in which an activation group runs\&. Please note that this option exists only in Oracle\&'s implementation of the Java RMI activation daemon\&. If this property is not specified on the command line, then the result is the same as though \f3-J-Dsun\&.rmi\&.activation\&.execPolicy=default\fR were specified\&. The possible values of \f3policy\fR can be \f3default\fR, \f3policyClassName\fR, or \f3none\fR\&. +.RS +.TP 0.2i +\(bu +default + +The \f3default\fR or unspecified value \f3execPolicy\fR allows the \f3rmid\fR command to execute commands with specific command-line options only when the \f3rmid\fR command was granted permission to execute those commands and options in the security policy file that the \f3rmid\fR command uses\&. Only the default activation group implementation can be used with the default execution policy\&. + +The \f3rmid\fR command starts a JVM for an activation group with the information in the group\&'s registered activation group descriptor, an \f3ActivationGroupDesc\fR\&. The group descriptor specifies an optional \f3ActivationGroupDesc\&.CommandEnvironment\fR that includes the command to execute to start the activation group and any command-line options to be added to the command line\&. By default, the \f3rmid\fR command uses the \f3java\fR command found in \f3java\&.home\fR\&. The group descriptor also contains properties overrides that are added to the command line as options defined as: \f3-D=\fR\&.The \f3com\&.sun\&.rmi\&.rmid\&.ExecPermission\fR permission grants the \f3rmid\fR command permission to execute a command that is specified in the group descriptor\&'s \f3CommandEnvironment\fR to start an activation group\&. The \f3com\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fR permission enables the \f3rmid\fR command to use command-line options, specified as properties overrides in the group descriptor or as options in the \f3CommandEnvironment\fR when starting the activation group\&.When granting the \f3rmid\fR command permission to execute various commands and options, the permissions \f3ExecPermission\fR and \f3ExecOptionPermission\fR must be granted to all code sources\&. + +\fIExecPermission\fR + +The \f3ExecPermission\fR class represents permission for the \f3rmid\fR command to execute a specific command to start an activation group\&. + +\fISyntax\fR: The name of an \f3ExecPermission\fR is the path name of a command to grant the \f3rmid\fR command permission to execute\&. A path name that ends in a slash (/) and an asterisk (*) indicates that all of the files contained in that directory where slash is the file-separator character, \f3File\&.separatorChar\fR\&. A path name that ends in a slash (/) and a minus sign (-) indicates all files and subdirectories contained in that directory (recursively)\&. A path name that consists of the special token \f3<>\fR matches any file\&. + +A path name that consists of an asterisk (*) indicates all the files in the current directory\&. A path name that consists of a minus sign (-) indicates all the files in the current directory and (recursively) all files and subdirectories contained in the current directory\&. + +\fIExecOptionPermission\fR + +The \f3ExecOptionPermission\fR class represents permission for the \f3rmid\fR command to use a specific command-line option when starting an activation group\&. The name of an \f3ExecOptionPermission\fR is the value of a command-line option\&. + +\fISyntax\fR: Options support a limited wild card scheme\&. An asterisk signifies a wild card match, and it can appear as the option name itself (matches any option), or an asterisk (*) can appear at the end of the option name only when the asterisk (*) follows a dot (\&.) or an equals sign (=)\&. + +For example: \f3*\fR or \f3-Dmydir\&.*\fR or \f3-Da\&.b\&.c=*\fR is valid, but \f3*mydir\fR or \f3-Da*b\fR or \f3ab*\fR is not\&. + +\fIPolicy file for rmid\fR + +When you grant the \f3rmid\fR command permission to execute various commands and options, the permissions \f3ExecPermission\fR and \f3ExecOptionPermission\fR must be granted to all code sources (universally)\&. It is safe to grant these permissions universally because only the \f3rmid\fR command checks these permissions\&. + +An example policy file that grants various execute permissions to the \f3rmid\fR command is: +.sp +.nf +\f3grant {\fP +.fi +.nf +\f3 permission com\&.sun\&.rmi\&.rmid\&.ExecPermission\fP +.fi +.nf +\f3 "/files/apps/java/jdk1\&.7\&.0/solaris/bin/java";\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 permission com\&.sun\&.rmi\&.rmid\&.ExecPermission\fP +.fi +.nf +\f3 "/files/apps/rmidcmds/*";\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 permission com\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fP +.fi +.nf +\f3 "\-Djava\&.security\&.policy=/files/policies/group\&.policy";\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 permission com\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fP +.fi +.nf +\f3 "\-Djava\&.security\&.debug=*";\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 permission com\&.sun\&.rmi\&.rmid\&.ExecOptionPermission\fP +.fi +.nf +\f3 "\-Dsun\&.rmi\&.*";\fP +.fi +.nf +\f3};\fP +.fi +.nf +\f3\fP +.fi +.sp + + +The first permission granted allows the \f3rmid\fR tcommand o execute the 1\&.7\&.0 release of the \f3java\fR command, specified by its explicit path name\&. By default, the version of the \f3java\fR command found in \f3java\&.home\fR is used (the same one that the \f3rmid\fR command uses), and does not need to be specified in the policy file\&. The second permission allows the \f3rmid\fR command to execute any command in the directory \f3/files/apps/rmidcmds\fR\&. + +The third permission granted, an \f3ExecOptionPermission\fR, allows the \f3rmid\fR command to start an activation group that defines the security policy file to be \f3/files/policies/group\&.policy\fR\&. The next permission allows the \f3java\&.security\&.debug property\fR to be used by an activation group\&. The last permission allows any property in the \f3sun\&.rmi property\fR name hierarchy to be used by activation groups\&. + +To start the \f3rmid\fR command with a policy file, the \f3java\&.security\&.policy\fR property needs to be specified on the \f3rmid\fR command line, for example: + +\f3rmid -J-Djava\&.security\&.policy=rmid\&.policy\fR\&. +.TP 0.2i +\(bu + + +If the default behavior is not flexible enough, then an administrator can provide, when starting the \f3rmid\fR command, the name of a class whose \f3checkExecCommand\fR method is executed to check commands to be executed by the \f3rmid\fR command\&. + +The \f3policyClassName\fR specifies a public class with a public, no-argument constructor and an implementation of the following \f3checkExecCommand\fR method: +.sp +.nf +\f3 public void checkExecCommand(ActivationGroupDesc desc, String[] command)\fP +.fi +.nf +\f3 throws SecurityException;\fP +.fi +.nf +\f3\fP +.fi +.sp + + +Before starting an activation group, the \f3rmid\fR command calls the policy\&'s \f3checkExecCommand\fR method and passes to it the activation group descriptor and an array that contains the complete command to start the activation group\&. If the \f3checkExecCommand\fR throws a \f3SecurityException\fR, then the \f3rmid\fR command does not start the activation group and an \f3ActivationException\fR is thrown to the caller attempting to activate the object\&. +.TP 0.2i +\(bu +none + +If the \f3sun\&.rmi\&.activation\&.execPolicy\fR property value is \f3none\fR, then the \f3rmid\fR command does not perform any validation of commands to start activation groups\&. +.RE + +.TP +-log \fIdir\fR +.br +Specifies the name of the directory the activation system daemon uses to write its database and associated information\&. The log directory defaults to creating a log, in the directory in which the \f3rmid\fR command was executed\&. +.TP +-port \fIport\fR +.br +Specifies the port the registry uses\&. The activation system daemon binds the \f3ActivationSystem\fR, with the name \f3java\&.rmi\&.activation\&.ActivationSystem\fR, in this registry\&. The \f3ActivationSystem\fR on the local machine can be obtained using the following \f3Naming\&.lookup\fR method call: +.sp +.nf +\f3import java\&.rmi\&.*; \fP +.fi +.nf +\f3 import java\&.rmi\&.activation\&.*;\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 ActivationSystem system; system = (ActivationSystem)\fP +.fi +.nf +\f3 Naming\&.lookup("//:port/java\&.rmi\&.activation\&.ActivationSystem");\fP +.fi +.nf +\f3\fP +.fi +.sp + +.TP +-stop +.br +Stops the current invocation of the \f3rmid\fR command for a port specified by the \f3-port\fR option\&. If no port is specified, then this option stops the \f3rmid\fR invocation running on port 1098\&. +.SH ENVIRONMENT\ VARIABLES +.TP +CLASSPATH +Used to provide the system a path to user-defined classes\&. Directories are separated by colons, for example: \f3\&.:/usr/local/java/classes\fR\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +java(1) +.TP 0.2i +\(bu +Setting the Class Path +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/rmiregistry.1 b/FCL/src/main/assets/java/man/man1/rmiregistry.1 new file mode 100644 index 00000000..822d3766 --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/rmiregistry.1 @@ -0,0 +1,98 @@ +'\" t +.\" Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 21 November 2013 +.\" SectDesc: Remote Method Invocation (RMI) Tools +.\" Title: rmiregistry.1 +.\" +.if n .pl 99999 +.TH rmiregistry 1 "21 November 2013" "JDK 8" "Remote Method Invocation (RMI) Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +rmiregistry \- Starts a remote object registry on the specified port on the current host\&. +.SH SYNOPSIS +.sp +.nf + +\fBrmiregistry\fR [ \fIport\fR ] +.fi +.sp +.TP +\fIport\fR +The number of a \f3port\fR on the current host at which to start the remote object registry\&. +.SH DESCRIPTION +The \f3rmiregistry\fR command creates and starts a remote object registry on the specified port on the current host\&. If the port is omitted, then the registry is started on port 1099\&. The \f3rmiregistry\fR command produces no output and is typically run in the background, for example: +.sp +.nf +\f3rmiregistry &\fP +.fi +.nf +\f3\fP +.fi +.sp +A remote object registry is a bootstrap naming service that is used by RMI servers on the same host to bind remote objects to names\&. Clients on local and remote hosts can then look up remote objects and make remote method invocations\&. +.PP +The registry is typically used to locate the first remote object on which an application needs to call methods\&. That object then provides application-specific support for finding other objects\&. +.PP +The methods of the \f3java\&.rmi\&.registry\&.LocateRegistry\fR class are used to get a registry operating on the local host or local host and port\&. +.PP +The URL-based methods of the \f3java\&.rmi\&.Naming\fR class operate on a registry and can be used to look up a remote object on any host and on the local host\&. Bind a simple name (string) to a remote object, rebind a new name to a remote object (overriding the old binding), unbind a remote object, and list the URL bound in the registry\&. +.SH OPTIONS +.TP +-J +.br +Used with any Java option to pass the option following the \f3-J\fR (no spaces between the \f3-J\fR and the option) to the Java interpreter\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +java(1) +.TP 0.2i +\(bu +\f3java\&.rmi\&.registry\&.LocateRegistry\fR class description at http://docs\&.oracle\&.com/javase/8/docs/api/java/rmi/registry/LocateRegistry\&.html +.TP 0.2i +\(bu +\f3java\&.rmi\&.Naming class description\fR at http://docs\&.oracle\&.com/javase/8/docs/api/java/rmi/Naming\&.html +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/servertool.1 b/FCL/src/main/assets/java/man/man1/servertool.1 new file mode 100644 index 00000000..3204a2e2 --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/servertool.1 @@ -0,0 +1,137 @@ +'\" t +.\" Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 21 November 2013 +.\" SectDesc: Java IDL and RMI-IIOP Tools +.\" Title: servertool.1 +.\" +.if n .pl 99999 +.TH servertool 1 "21 November 2013" "JDK 8" "Java IDL and RMI-IIOP Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +servertool \- Provides an easy-to-use interface for developers to register, unregister, start up, and shut down a persistent server\&. +.SH SYNOPSIS +.sp +.nf + +\fBservertool\fR \-ORBInitialPort \fInameserverport\fR [ \fIoptions\fR ] [ \fIcommands \fR] +.fi +.sp +.TP +\fIoptions\fR +The command-line options\&. See Options\&. +.TP +commands +The command-line commands\&. See Commands\&. +.SH DESCRIPTION +The \f3servertool\fR command provides the command-line interface for developers to register, unregister, start up, and shut down a persistent server\&. Command-line commands let you obtain various statistical information about the server\&. See Commands\&. +.SH OPTIONS +.TP +-ORBInitialHost \fInameserverhost\fR +.br +This options is required\&. It specifies the host machine on which the name server runs and listens for incoming requests\&. The \f3nameserverhost\fR value must specify the port on which the \f3orb\fR is running and listening for requests\&. The value defaults to \f3localhost\fR when this option is not specified\&. If \f3orbd\fR and \f3servertool\fR are running on different machines, then you must specify the name or IP address of the host on which \f3orbd\fR is running\&. + +\fINote:\fR On Oracle Solaris, you must become a root user to start a process on a port below 1024\&. Oracle recommends that you use a port number above or equal to 1024 for the \f3nameserverport\fR value\&. +.TP +-J\fIoption\fR +.br +Passes \f3option\fR to the Java Virtual Machine, where \f3option\fR is one of the options described on the reference page for the Java application launcher\&. For example, \f3-J-Xms48m\fR sets the startup memory to 48 MB\&. See java(1)\&. +.SH COMMANDS +You can start the \f3servertool\fR command with or without a command-line command\&. +.TP 0.2i +\(bu +If you did not specify a command when you started \f3servertool\fR, then the command-line tool displays the \f3servertool\fR prompt where you can enter commands: \f3servertool >\fR\&. +.TP 0.2i +\(bu +If you specify a command when you start \f3servertool\fR, then the Java IDL Server Tool starts, executes the command, and exits\&. +.TP +.ll 180 +register -server \fIserver-class-name\fR -classpath \fIclasspath-to-server\fR [ -applicationName \fIapplication-name\fR -args \fIargs-to-server\fR -vmargs \fIflags-for-JVM\fR ] +Registers a new persistent server with the Object Request Broker Daemon (ORBD)\&. If the server is not already registered, then it is registered and activated\&. This command causes an installation method to be called in the \f3main\fR class of the server identified by the \f3-server\fR option\&. The installation method must be \f3public static void install(org\&.omg\&.CORBA\&.ORB)\fR\&. The install method is optional and lets developers provide their own server installation behavior, such as creating a database schema\&. +.TP +.ll 180 +unregister -serverid \fIserver-id\fR | -applicationName \fIapplication-name\fR +Unregisters a server from the ORBD with either its server ID or its application name\&. This command causes an uninstallation method to be called in the \f3main\fR class of the server identified by the \f3-server\fR option\&. The \f3uninstall\fR method must be \f3public static void uninstall(org\&.omg\&.CORBA\&.ORB)\fR\&. The \f3uninstall\fR method is optional and lets developers provide their own server uninstallation behavior, such as undoing the behavior of the \f3install\fR method\&. +.TP +getserverid -applicationName \fIapplication-name\fR +Returns the server ID that corresponds to the \f3application-name\fR value\&. +.TP +list +Lists information about all persistent servers registered with the ORBD\&. +.TP +listappnames +Lists the application names for all servers currently registered with the ORBD\&. +.TP +listactive +Lists information about all persistent servers that were started by the ORBD and are currently running\&. +.TP +.ll 180 +locate -serverid \fIserver-id\fR | -applicationName \fIapplication-name\fR [ -endpointType \fIendpointType\fR ] +Locates the endpoints (ports) of a specific type for all ORBs created by a registered server\&. If a server is not already running, then it is activated\&. If an \f3endpointType\fR value is not specified, then the plain/non-protected endpoint associated with each ORB in a server is returned\&. +.TP +.ll 180 +locateperorb -serverid \fIserver-id\fR | -applicationName \fIapplication-name\fR [ -orbid \fIORB-name\fR ] +Locates all the endpoints (ports) registered by a specific Object Request Broker (ORB) of registered server\&. If a server is not already running, then it is activated\&. If an \f3orbid\fR is not specified, then the default value of \f3""\fR is assigned to the \f3orbid\fR\&. If any ORBs are created with an \f3orbid\fR of empty string, then all ports registered by it are returned\&. +.TP +orblist -serverid \fIserver-id\fR | -applicationName \fIapplication-name\fR +Lists the \f3ORBId\fR of the ORBs defined on a server\&. An \f3ORBId\fR is the string name for the ORB created by the server\&. If the server is not already running, then it is activated\&. +.TP +shutdown -serverid \fIserver-id\fR | -applicationName application-name +Shut down an active server that is registered with ORBD\&. During execution of this command, the \f3shutdown\fR method defined in the class specified by either the \f3-serverid\fR or \f3-applicationName\fR parameter is also called to shut down the server process\&. +.TP +startup -serverid \fIserver-id\fR | -applicationName application-name +Starts up or activate a server that is registered with ORBD\&. If the server is not running, then this command starts the server\&. If the server is already running, then an error message is displayed\&. +.TP +help +Lists all the commands available to the server through the \f3servertool\fR command\&. +.TP +quit +Exits the \f3servertool\fR command\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +orbd(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/tnameserv.1 b/FCL/src/main/assets/java/man/man1/tnameserv.1 new file mode 100644 index 00000000..446183b2 --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/tnameserv.1 @@ -0,0 +1,488 @@ +'\" t +.\" Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 21 November 2013 +.\" SectDesc: Java IDL and RMI-IIOP Tools +.\" Title: tnameserv.1 +.\" +.if n .pl 99999 +.TH tnameserv 1 "21 November 2013" "JDK 8" "Java IDL and RMI-IIOP Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +tnameserv \- Interface Definition Language (IDL)\&. +.SH SYNOPSIS +.sp +.nf + +\fBtnameserve\fR \fB\-ORBInitialPort\fR [ \fInameserverport\fR ] +.fi +.sp +.TP +-ORBInitialPort \fInameserverport\fR +.br +The initial port where the naming service listens for the bootstrap protocol used to implement the ORB \f3resolve_initial_references\fR and \f3list_initial_references\fR methods\&. +.SH DESCRIPTION +Java IDL includes the Object Request Broker Daemon (ORBD)\&. ORBD is a daemon process that contains a Bootstrap Service, a Transient Naming Service, a Persistent Naming Service, and a Server Manager\&. The Java IDL tutorials all use ORBD, but you can substitute the \f3tnameserv\fR command for the \f3orbd\fR command in any of the examples that use a Transient Naming Service\&. +.PP +See orbd(1) or Naming Service at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/jidlNaming\&.html +.PP +The CORBA Common Object Services (COS) Naming Service provides a tree-structure directory for object references similar to a file system that provides a directory structure for files\&. The Transient Naming Service provided with Java IDL, \f3tnameserv\fR, is a simple implementation of the COS Naming Service specification\&. +.PP +Object references are stored in the name space by name and each object reference-name pair is called a name binding\&. Name bindings can be organized under naming contexts\&. Naming contexts are name bindings and serve the same organizational function as a file system subdirectory\&. All bindings are stored under the initial naming context\&. The initial naming context is the only persistent binding in the name space\&. The rest of the name space is lost when the Java IDL naming service process stops and restarts\&. +.PP +For an applet or application to use COS naming, its ORB must know the port of a host running a naming service or have access to an initial naming context string for that naming service\&. The naming service can either be the Java IDL naming service or another COS-compliant naming service\&. +.SS START\ THE\ NAMING\ SERVICE +You must start the Java IDL naming service before an application or applet that uses its naming service\&. Installation of the Java IDL product creates a script (Oracle Solaris: \f3tnameserv\fR) or executable file (Windows: \f3tnameserv\&.exe\fR) that starts the Java IDL naming service\&. Start the naming service so it runs in the background\&. +.PP +If you do not specify otherwise, then the Java IDL naming service listens on port 900 for the bootstrap protocol used to implement the ORB \f3resolve_initial_references\fR and \f3list_initial_references methods\fR, as follows: +.sp +.nf +\f3tnameserv \-ORBInitialPort nameserverport&\fP +.fi +.nf +\f3\fP +.fi +.sp +If you do not specify the name server port, then port 900 is used by default\&. When running Oracle Solaris software, you must become the root user to start a process on a port below 1024\&. For this reason, it is recommended that you use a port number greater than or equal to 1024\&. To specify a different port, for example, 1050, and to run the naming service in the background, from a UNIX command shell, enter: +.sp +.nf +\f3tnameserv \-ORBInitialPort 1050&\fP +.fi +.nf +\f3\fP +.fi +.sp +From an MS-DOS system prompt (Windows), enter: +.sp +.nf +\f3start tnameserv \-ORBInitialPort 1050\fP +.fi +.nf +\f3\fP +.fi +.sp +Clients of the name server must be made aware of the new port number\&. Do this by setting the \f3org\&.omg\&.CORBA\&.ORBInitialPort\fR property to the new port number when you create the ORB object\&. +.SS RUN\ THE\ SERVER\ AND\ CLIENT\ ON\ DIFFERENT\ HOSTS +In most of the Java IDL and RMI-IIOP tutorials, the naming service, server, and client are all running on the development machine\&. In real-world deployment, the client and server probably run on different host machines from the Naming Service\&. +.PP +For the client and server to find the Naming Service, they must be made aware of the port number and host on which the naming service is running\&. Do this by setting the \f3org\&.omg\&.CORBA\&.ORBInitialPort\fR and \f3org\&.omg\&.CORBA\&.ORBInitialHost\fR properties in the client and server files to the machine name and port number on which the Naming Service is running\&. An example of this is shown in Getting Started Using RMI-IIOP at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/rmi-iiop/rmiiiopexample\&.html +.PP +You could also use the command-line options \f3-ORBInitialPort nameserverport#\fR and \f3-ORBInitialHost nameserverhostname\fR to tell the client and server where to find the naming service\&. For one example of doing this using the command-line option, see Java IDL: The Hello World Example on Two Machines at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/idl/tutorial/jidl2machines\&.html +.PP +For example, suppose the Transient Naming Service, \f3tnameserv\fR is running on port 1050 on host \f3nameserverhost\fR\&. The client is running on host \f3clienthost,\fR and the server is running on host \f3serverhost\fR\&. +.PP +Start \f3tnameserv\fR on the host \f3nameserverhost\fR: +.sp +.nf +\f3tnameserv \-ORBInitialPort 1050\fP +.fi +.nf +\f3\fP +.fi +.sp +Start the server on the \f3serverhost\fR: +.sp +.nf +\f3java Server \-ORBInitialPort 1050 \-ORBInitialHost nameserverhost\fP +.fi +.nf +\f3\fP +.fi +.sp +Start the client on the \f3clienthost\fR: +.sp +.nf +\f3java Client \-ORBInitialPort 1050 \-ORBInitialHost nameserverhost\fP +.fi +.nf +\f3\fP +.fi +.sp +.SS STOP\ THE\ NAMING\ SERVICE +To stop the Java IDL naming service, use the relevant operating system command, such as \f3kill\fR for a Unix process or \f3Ctrl+C\fR for a Windows process\&. The naming service continues to wait for invocations until it is explicitly shut down\&. Note that names registered with the Java IDL naming service disappear when the service is terminated\&. +.SH OPTIONS +.TP +-J\fIoption\fR +.br +Passes \f3option\fR to the Java Virtual Machine, where \f3option\fR is one of the options described on the reference page for the Java application launcher\&. For example, \f3-J-Xms48m\fR sets the startup memory to 48 MB\&. See java(1)\&. +.SH EXAMPLES +.SS ADD\ OBJECTS\ TO\ THE\ NAME\ SPACE +The following example shows how to add names to the name space\&. It is a self-contained Transient Naming Service client that creates the following simple tree\&. +.sp +.nf +\f3Initial Naming Context\fP +.fi +.nf +\f3 plans\fP +.fi +.nf +\f3 Personal\fP +.fi +.nf +\f3 calendar\fP +.fi +.nf +\f3 schedule\fP +.fi +.nf +\f3\fP +.fi +.sp +In this example, \f3plans\fR is an object reference and \f3Personal\fR is a naming context that contains two object references: \f3calendar\fR and \f3schedule\fR\&. +.sp +.nf +\f3import java\&.util\&.Properties;\fP +.fi +.nf +\f3import org\&.omg\&.CORBA\&.*;\fP +.fi +.nf +\f3import org\&.omg\&.CosNaming\&.*;\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3public class NameClient {\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 public static void main(String args[]) {\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 try {\fP +.fi +.nf +\f3\fP +.fi +.sp +In Start the Naming Service, the \f3nameserver\fR was started on port 1050\&. The following code ensures that the client program is aware of this port number\&. +.sp +.nf +\f3 Properties props = new Properties();\fP +.fi +.nf +\f3 props\&.put("org\&.omg\&.CORBA\&.ORBInitialPort", "1050");\fP +.fi +.nf +\f3 ORB orb = ORB\&.init(args, props);\fP +.fi +.nf +\f3\fP +.fi +.sp +This code obtains the initial naming context and assigns it to \f3ctx\fR\&. The second line copies \f3ctx\fR into a dummy object reference \f3objref\fR that is attached to various names and added into the name space\&. +.sp +.nf +\f3 NamingContext ctx =\fP +.fi +.nf +\f3 NamingContextHelper\&.narrow(\fP +.fi +.nf +\f3 orb\&.resolve_initial_references("NameService"));\fP +.fi +.nf +\f3 NamingContext objref = ctx;\fP +.fi +.nf +\f3\fP +.fi +.sp +This code creates a name \f3plans\fR of type \f3text\fR and binds it to the dummy object reference\&. \f3plans\fR is then added under the initial naming context using the \f3rebind\fR method\&. The \f3rebind\fR method enables you to run this program over and over again without getting the exceptions from using the \f3bind\fR method\&. +.sp +.nf +\f3 NameComponent nc1 = new NameComponent("plans", "text");\fP +.fi +.nf +\f3 NameComponent[] name1 = {nc1};\fP +.fi +.nf +\f3 ctx\&.rebind(name1, objref);\fP +.fi +.nf +\f3 System\&.out\&.println("plans rebind successful!");\fP +.fi +.nf +\f3\fP +.fi +.sp +This code creates a naming context called \f3Personal\fR of type \f3directory\fR\&. The resulting object reference, \f3ctx2\fR, is bound to the \f3name\fR and added under the initial naming context\&. +.sp +.nf +\f3 NameComponent nc2 = new NameComponent("Personal", "directory");\fP +.fi +.nf +\f3 NameComponent[] name2 = {nc2};\fP +.fi +.nf +\f3 NamingContext ctx2 = ctx\&.bind_new_context(name2);\fP +.fi +.nf +\f3 System\&.out\&.println("new naming context added\&.\&.");\fP +.fi +.nf +\f3\fP +.fi +.sp +The remainder of the code binds the dummy object reference using the names \f3schedule\fR and \f3calendar\fR under the \f3Personal\fR naming context (\f3ctx2\fR)\&. +.sp +.nf +\f3 NameComponent nc3 = new NameComponent("schedule", "text");\fP +.fi +.nf +\f3 NameComponent[] name3 = {nc3};\fP +.fi +.nf +\f3 ctx2\&.rebind(name3, objref);\fP +.fi +.nf +\f3 System\&.out\&.println("schedule rebind successful!");\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 NameComponent nc4 = new NameComponent("calender", "text");\fP +.fi +.nf +\f3 NameComponent[] name4 = {nc4};\fP +.fi +.nf +\f3 ctx2\&.rebind(name4, objref);\fP +.fi +.nf +\f3 System\&.out\&.println("calender rebind successful!");\fP +.fi +.nf +\f3 } catch (Exception e) {\fP +.fi +.nf +\f3 e\&.printStackTrace(System\&.err);\fP +.fi +.nf +\f3 }\fP +.fi +.nf +\f3 }\fP +.fi +.nf +\f3}\fP +.fi +.nf +\f3\fP +.fi +.sp +.SS BROWSING\ THE\ NAME\ SPACE +The following sample program shoes how to browse the name space\&. +.sp +.nf +\f3import java\&.util\&.Properties;\fP +.fi +.nf +\f3import org\&.omg\&.CORBA\&.*;\fP +.fi +.nf +\f3import org\&.omg\&.CosNaming\&.*;\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3public class NameClientList {\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 public static void main(String args[]) {\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 try {\fP +.fi +.nf +\f3\fP +.fi +.sp +In Start the Naming Service, the \f3nameserver\fR was started on port 1050\&. The following code ensures that the client program is aware of this port number\&. +.sp +.nf +\f3 Properties props = new Properties();\fP +.fi +.nf +\f3 props\&.put("org\&.omg\&.CORBA\&.ORBInitialPort", "1050");\fP +.fi +.nf +\f3 ORB orb = ORB\&.init(args, props);\fP +.fi +.nf +\f3\fP +.fi +.sp +The following code obtains the initial naming context\&. +.sp +.nf +\f3 NamingContext nc =\fP +.fi +.nf +\f3 NamingContextHelper\&.narrow(\fP +.fi +.nf +\f3 orb\&.resolve_initial_references("NameService"));\fP +.fi +.nf +\f3\fP +.fi +.sp +The \f3list\fR method lists the bindings in the naming context\&. In this case, up to 1000 bindings from the initial naming context will be returned in the \f3BindingListHolder\fR; any remaining bindings are returned in the \f3BindingIteratorHolder\fR\&. +.sp +.nf +\f3 BindingListHolder bl = new BindingListHolder();\fP +.fi +.nf +\f3 BindingIteratorHolder blIt= new BindingIteratorHolder();\fP +.fi +.nf +\f3 nc\&.list(1000, bl, blIt);\fP +.fi +.nf +\f3\fP +.fi +.sp +This code gets the array of bindings out of the returned \f3BindingListHolder\fR\&. If there are no bindings, then the program ends\&. +.sp +.nf +\f3 Binding bindings[] = bl\&.value;\fP +.fi +.nf +\f3 if (bindings\&.length == 0) return;\fP +.fi +.nf +\f3\fP +.fi +.sp +The remainder of the code loops through the bindings and prints outs the names\&. +.sp +.nf +\f3 for (int i=0; i < bindings\&.length; i++) {\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 // get the object reference for each binding\fP +.fi +.nf +\f3 org\&.omg\&.CORBA\&.Object obj = nc\&.resolve(bindings[i]\&.binding_name);\fP +.fi +.nf +\f3 String objStr = orb\&.object_to_string(obj);\fP +.fi +.nf +\f3 int lastIx = bindings[i]\&.binding_name\&.length\-1;\fP +.fi +.nf +\f3\fP +.fi +.nf +\f3 // check to see if this is a naming context\fP +.fi +.nf +\f3 if (bindings[i]\&.binding_type == BindingType\&.ncontext) {\fP +.fi +.nf +\f3 System\&.out\&.println("Context: " +\fP +.fi +.nf +\f3 bindings[i]\&.binding_name[lastIx]\&.id);\fP +.fi +.nf +\f3 } else {\fP +.fi +.nf +\f3 System\&.out\&.println("Object: " +\fP +.fi +.nf +\f3 bindings[i]\&.binding_name[lastIx]\&.id);\fP +.fi +.nf +\f3 }\fP +.fi +.nf +\f3 }\fP +.fi +.nf +\f3 } catch (Exception e) {\fP +.fi +.nf +\f3 e\&.printStackTrace(System\&.err)\fP +.fi +.nf +\f3 }\fP +.fi +.nf +\f3 }\fP +.fi +.nf +\f3}\fP +.fi +.nf +\f3\fP +.fi +.sp +.SH SEE\ ALSO +.TP 0.2i +\(bu +orbd(1) +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/man/man1/unpack200.1 b/FCL/src/main/assets/java/man/man1/unpack200.1 new file mode 100644 index 00000000..22d63c0c --- /dev/null +++ b/FCL/src/main/assets/java/man/man1/unpack200.1 @@ -0,0 +1,137 @@ +'\" t +.\" Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. +.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +.\" +.\" This code is free software; you can redistribute it and/or modify it +.\" under the terms of the GNU General Public License version 2 only, as +.\" published by the Free Software Foundation. +.\" +.\" This code is distributed in the hope that it will be useful, but WITHOUT +.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +.\" version 2 for more details (a copy is included in the LICENSE file that +.\" accompanied this code). +.\" +.\" You should have received a copy of the GNU General Public License version +.\" 2 along with this work; if not, write to the Free Software Foundation, +.\" Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.\" Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +.\" or visit www.oracle.com if you need additional information or have any +.\" questions. +.\" +.\" Arch: generic +.\" Software: JDK 8 +.\" Date: 21 November 2013 +.\" SectDesc: Java Deployment Tools +.\" Title: unpack200.1 +.\" +.if n .pl 99999 +.TH unpack200 1 "21 November 2013" "JDK 8" "Java Deployment Tools" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- + +.SH NAME +unpack200 \- Transforms a packed file produced by pack200(1) into a JAR file for web deployment\&. +.SH SYNOPSIS +.sp +.nf + +\fBunpack200\fR [ \fIoptions\fR ] input\-file \fIJAR\-file\fR +.fi +.sp +.TP +\fIoptions\fR +The command-line options\&. See Options\&. +.TP +\fIinput-file\fR +Name of the input file, which can be a pack200 gzip file or a pack200 file\&. The input can also be JAR file produced by \f3pack200\fR(1) with an effort of \f30\fR, in which case the contents of the input file are copied to the output JAR file with the Pack200 marker\&. +.TP +\fIJAR-file\fR +Name of the output JAR file\&. +.SH DESCRIPTION +The \f3unpack200\fR command is a native implementation that transforms a packed file produced by \f3pack200\fR\f3(1)\fR into a JAR file\&. A typical usage follows\&. In the following example, the \f3myarchive\&.jar\fR file is produced from \f3myarchive\&.pack\&.gz\fR with the default \f3unpack200\fR command settings\&. +.sp +.nf +\f3unpack200 myarchive\&.pack\&.gz myarchive\&.jar\fP +.fi +.nf +\f3\fP +.fi +.sp +.SH OPTIONS +.TP +-Hvalue --deflate-hint=\fIvalue\fR +.br +Sets the deflation to be \f3true\fR, \f3false\fR, or \f3keep\fR on all entries within a JAR file\&. The default mode is \f3keep\fR\&. If the value is \f3true\fR or \f3false\fR, then the \f3--deflate=hint\fR option overrides the default behavior and sets the deflation mode on all entries within the output JAR file\&. +.TP +-r --remove-pack-file +.br +Removes the input pack file\&. +.TP +-v --verbose +.br +Displays minimal messages\&. Multiple specifications of this option displays more verbose messages\&. +.TP +-q --quiet +.br +Specifies quiet operation with no messages\&. +.TP +-lfilename --log-file=\fIfilename\fR +.br +Specifies a log file where output messages are logged\&. +.TP +-? -h --help +.br +Prints help information about the \f3unpack200\fR command\&. +.TP +-V --version +.br +Prints version information about the \f3unpack200\fR command\&. +.TP +-J\fIoption\fR +.br +Passes option to the Java Virtual Machine, where \f3option\fR is one of the options described on the reference page for the Java application launcher\&. For example, \f3-J-Xms48m\fR sets the startup memory to 48 MB\&. See java(1)\&. +.SH NOTES +This command should not be confused with the \f3unpack\fR command\&. They are distinctly separate products\&. +.PP +The Java SE API Specification provided with the JDK is the superseding authority in case of discrepancies\&. +.SH EXIT\ STATUS +The following exit values are returned: 0 for successful completion, and a value that is greater than 0 when an error occurred\&. +.SH SEE\ ALSO +.TP 0.2i +\(bu +pack200(1) +.TP 0.2i +\(bu +jar(1) +.TP 0.2i +\(bu +jarsigner(1) +.TP 0.2i +\(bu +Pack200 and Compression at http://docs\&.oracle\&.com/javase/8/docs/technotes/guides/deployment/deployment-guide/pack200\&.html +.TP 0.2i +\(bu +The Java SE Technical Documentation page at http://docs\&.oracle\&.com/javase/ +.RE +.br +'pl 8.5i +'bp diff --git a/FCL/src/main/assets/java/release b/FCL/src/main/assets/java/release new file mode 100644 index 00000000..b7906963 --- /dev/null +++ b/FCL/src/main/assets/java/release @@ -0,0 +1,5 @@ +JAVA_VERSION="1.8.0" +OS_NAME="Linux" +OS_VERSION="2.6" +OS_ARCH="aarch64" +SOURCE="" diff --git a/FCL/src/main/assets/java/version b/FCL/src/main/assets/java/version new file mode 100644 index 00000000..62f94575 --- /dev/null +++ b/FCL/src/main/assets/java/version @@ -0,0 +1 @@ +6 \ No newline at end of file diff --git a/FCL/src/main/java/com/tungsten/fcl/AssetsUtils.java b/FCL/src/main/java/com/tungsten/fcl/AssetsUtils.java new file mode 100644 index 00000000..df78dd25 --- /dev/null +++ b/FCL/src/main/java/com/tungsten/fcl/AssetsUtils.java @@ -0,0 +1,188 @@ +package com.tungsten.fcl; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class AssetsUtils { + + private static AssetsUtils instance; + private static final int SUCCESS = 1; + private static final int FAILED = 0; + private Context context; + private FileOperateCallback callback; + private ProgressCallback progressCallback; + private volatile boolean isSuccess; + private String errorStr; + + public static AssetsUtils getInstance(Context context) { + if (instance == null) + instance = new AssetsUtils(context); + return instance; + } + + private AssetsUtils(Context context) { + this.context = context; + } + + private Handler handler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + if (callback != null) { + if (msg.what == SUCCESS) { + callback.onSuccess(); + } + if (msg.what == FAILED) { + callback.onFailed(msg.obj.toString()); + } + } + } + }; + + public static String readAssetsTxt(Context context,String fileName){ + try { + InputStream is = context.getAssets().open(fileName); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + String text = new String(buffer, "utf-8"); + Log.e("latest",text); + return text; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public AssetsUtils copyAssetsToSD(final String srcPath, final String sdPath) { + currentPosition = 0; + try { + totalSize = getTotalSize(context,srcPath); + Log.e("assetsFileSize",Long.toString(totalSize)); + } catch (IOException e) { + e.printStackTrace(); + } + new Thread(() -> { + copyAssetsToDst(context, srcPath, sdPath); + if (isSuccess) + handler.obtainMessage(SUCCESS).sendToTarget(); + else + handler.obtainMessage(FAILED, errorStr).sendToTarget(); + }).start(); + return this; + } + + public AssetsUtils copyOnMainThread(final String srcPath, final String sdPath) { + currentPosition = 0; + try { + totalSize = getTotalSize(context,srcPath); + Log.e("assetsFileSize",Long.toString(totalSize)); + } catch (IOException e) { + e.printStackTrace(); + } + copyAssetsToDst(context, srcPath, sdPath); + if (isSuccess) { + handler.obtainMessage(SUCCESS).sendToTarget(); + } + else { + handler.obtainMessage(FAILED, errorStr).sendToTarget(); + } + return this; + } + + public AssetsUtils setProgressCallback(ProgressCallback callback) { + this.progressCallback = callback; + return instance; + } + + public void setFileOperateCallback(FileOperateCallback callback) { + this.callback = callback; + } + + long currentPosition = 0; + long totalSize = 0; + + int currentProgress = 0; + + private void copyAssetsToDst(Context context, String srcPath, String dstPath) { + try { + String fileNames[] = context.getAssets().list(srcPath); + if (fileNames.length > 0) { + File file = new File(dstPath); + if (!file.exists()) file.mkdirs(); + for (String fileName : fileNames) { + if (!srcPath.equals("")) { // assets 文件夹下的目录 + copyAssetsToDst(context, srcPath + File.separator + fileName, dstPath + File.separator + fileName); + } else { // assets 文件夹 + copyAssetsToDst(context, fileName, dstPath + File.separator + fileName); + } + } + } else { + File outFile = new File(dstPath); + InputStream is = context.getAssets().open(srcPath); + FileOutputStream fos = new FileOutputStream(outFile); + byte[] buffer = new byte[1024]; + int byteCount; + while ((byteCount = is.read(buffer)) != -1) { + currentPosition += byteCount; + fos.write(buffer, 0, byteCount); + if (progressCallback != null) { + long cur = 100L * currentPosition; + int progress = (int) (cur / totalSize); + if (progress != currentProgress) { + currentProgress = progress; + handler.post(() -> { + progressCallback.onProgress(progress); + }); + } + } + } + fos.flush(); + is.close(); + fos.close(); + } + isSuccess = true; + } catch (Exception e) { + e.printStackTrace(); + errorStr = e.getMessage(); + isSuccess = false; + } + } + + private long getTotalSize(Context context,String srcPath) throws IOException { + String fileNames[] = context.getAssets().list(srcPath); + long size = 0; + if (fileNames.length > 0) { + for (String fileName : fileNames) { + if (!srcPath.equals("")) { // assets 文件夹下的目录 + size += getTotalSize(context, srcPath + File.separator + fileName); + } else { // assets 文件夹 + size += getTotalSize(context, fileName); + } + } + } else { + InputStream is = context.getAssets().open(srcPath); + size += is.available(); + } + return size; + } + + public interface FileOperateCallback { + void onSuccess(); + void onFailed(String error); + } + + public interface ProgressCallback{ + void onProgress(int progress); + } + +} \ No newline at end of file diff --git a/FCL/src/main/java/com/tungsten/fcl/MainActivity.java b/FCL/src/main/java/com/tungsten/fcl/MainActivity.java new file mode 100644 index 00000000..d0392532 --- /dev/null +++ b/FCL/src/main/java/com/tungsten/fcl/MainActivity.java @@ -0,0 +1,220 @@ +package com.tungsten.fcl; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.SurfaceTexture; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.provider.Settings; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; + +import com.tungsten.fclauncher.FCLConfig; +import com.tungsten.fclauncher.FCLauncher; +import com.tungsten.fclauncher.bridge.FCLBridgeCallback; + +import java.io.File; +import java.io.IOException; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + new Thread(() -> { + //AssetsUtils.getInstance(MainActivity.this).copyOnMainThread("java",getFilesDir() + "/java"); + //AssetsUtils.getInstance(MainActivity.this).copyOnMainThread("app_runtime",getFilesDir().getParent() + "/app_runtime"); + }).start(); + + requestPermission(); + } + + private void requestPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // 先判断有没有权限 + if (Environment.isExternalStorageManager()) { + try { + init(); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); + intent.setData(Uri.parse("package:" + getPackageName())); + startActivityForResult(intent, 1000); + } + } else { + // 先判断有没有权限 + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + try { + init(); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1000); + } + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == 1000 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Environment.isExternalStorageManager()) { + try { + init(); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + new AlertDialog.Builder(this) + .setMessage("a") + .setPositiveButton("OK", (dialog1, which) -> + requestPermission()) + .setNegativeButton("Cancel", null) + .create() + .show(); + } + } + } + + private void init() throws IOException { + String[] args3 = new String[] { + getFilesDir() + "/java/bin/java", + "-cp", + "/data/user/0/com.tungsten.fcl/app_runtime/lwjgl3/lwjgl-jemalloc.jar:/data/user/0/com.tungsten.fcl/app_runtime/lwjgl3/lwjgl-tinyfd.jar:/data/user/0/com.tungsten.fcl/app_runtime/lwjgl3/lwjgl-opengl.jar:/data/user/0/com.tungsten.fcl/app_runtime/lwjgl3/lwjgl-openal.jar:/data/user/0/com.tungsten.fcl/app_runtime/lwjgl3/lwjgl-glfw.jar:/data/user/0/com.tungsten.fcl/app_runtime/lwjgl3/lwjgl-stb.jar:/data/user/0/com.tungsten.fcl/app_runtime/lwjgl3/lwjgl.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/patchy/1.3.9/patchy-1.3.9.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/oshi-project/oshi-core/1.1/oshi-core-1.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/java/dev/jna/jna/4.4.0/jna-4.4.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/java/dev/jna/platform/3.4.0/platform-3.4.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/ibm/icu/icu4j/66.1/icu4j-66.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/javabridge/1.0.22/javabridge-1.0.22.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/sf/jopt-simple/jopt-simple/5.0.3/jopt-simple-5.0.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/io/netty/netty-all/4.1.25.Final/netty-all-4.1.25.Final.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/google/guava/guava/21.0/guava-21.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/commons/commons-lang3/3.5/commons-lang3-3.5.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/commons-io/commons-io/2.5/commons-io-2.5.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/commons-codec/commons-codec/1.10/commons-codec-1.10.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/brigadier/1.0.17/brigadier-1.0.17.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/datafixerupper/4.0.26/datafixerupper-4.0.26.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/google/code/gson/gson/2.8.0/gson-2.8.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/authlib/2.1.28/authlib-2.1.28.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/it/unimi/dsi/fastutil/8.2.1/fastutil-8.2.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/logging/log4j/log4j-api/2.8.1/log4j-api-2.8.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/logging/log4j/log4j-core/2.8.1/log4j-core-2.8.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/ca/weblite/java-objc-bridge/1.0.0/java-objc-bridge-1.0.0.jar:/storage/emulated/0/HMCLPE/.minecraft/versions/1.16.5/1.16.5.jar", + "-Dfml.earlyprogresswindow=false", + "-Dorg.lwjgl.util.Debug=true", + "-Dorg.lwjgl.util.DebugFunctions=true", + "-Dorg.lwjgl.util.DebugLoader=true", + "-Dos.name=Linux", + "-Dlwjgl.platform=FCL", + "-Dorg.lwjgl.opengl.libname=${glLibName}", + "-Djava.io.tmpdir=/data/user/0/com.tungsten.fcl/cache", + "-Dminecraft.launcher.brand=FCL", + "-Dminecraft.launcher.version=1.0.0", + "-Xms1024M", + "-Xmx1024M", + "net.minecraft.client.main.Main", + "--username", + "Tunsgten", + "--version", + "1.16.5", + "--gameDir", + "/storage/emulated/0/HMCLPE/.minecraft/versions/1.16.5", + "--assetsDir", + "/storage/emulated/0/HMCLPE/.minecraft/assets", + "--assetIndex", + "1.16", + "--uuid", + "410efec2-313f-4346-aa2a-7708597ff92f", + "--accessToken", + "eyJhbGciOiJIUzI1NiJ9.eyJ4dWlkIjoiMjUzNTQyNTU3Mzk1OTc0MCIsImFnZyI6IkFkdWx0Iiwic3ViIjoiNzcyMDU1YzMtMjNhMi00NmE0LThjOTItODRiOTE5NDVkOTFhIiwibmJmIjoxNjY1NTc0MzExLCJhdXRoIjoiWEJPWCIsInJvbGVzIjpbXSwiaXNzIjoiYXV0aGVudGljYXRpb24iLCJleHAiOjE2NjU2NjA3MTEsImlhdCI6MTY2NTU3NDMxMSwicGxhdGZvcm0iOiJVTktOT1dOIiwieXVpZCI6IjFhYTRlNDM5MGVmMTgwZWE1M2Q2MjBkMzEyM2Y5YzE3In0.8jpLk9Wr_g-SxeJrHtGC7vSxu54I6gw-0g6hSdQiSYk", + "--userType", + "mojang", + "--versionType", + "release", + "--width", + "2000", + "--height", + "1200" + }; + + String[] args2 = new String[] { + getFilesDir() + "/java/bin/java", + "-cp", + "/data/user/0/com.tungsten.fcl/app_runtime/lwjgl2/lwjgl.jar:/data/user/0/com.tungsten.fcl/app_runtime/lwjgl2/lwjgl_util.jar:/storage/emulated/0/HMCLPE/.minecraft/versions/1.7.10/1.7.10.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/netty/1.8.8/netty-1.8.8.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/realms/1.3.5/realms-1.3.5.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/java3d/vecmath/1.3.1/vecmath-1.3.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/sf/trove4j/trove4j/3.0.3/trove4j-3.0.3.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/sf/jopt-simple/jopt-simple/4.5/jopt-simple-4.5.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/paulscode/codecwav/20101023/codecwav-20101023.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/paulscode/soundsystem/20120107/soundsystem-20120107.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/io/netty/netty-all/4.0.10.Final/netty-all-4.0.10.Final.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/google/guava/guava/15.0/guava-15.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/commons/commons-lang3/3.1/commons-lang3-3.1.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/commons-io/commons-io/2.4/commons-io-2.4.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/commons-codec/commons-codec/1.9/commons-codec-1.9.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/com/mojang/authlib/1.5.21/authlib-1.5.21.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar:/storage/emulated/0/HMCLPE/.minecraft/libraries/org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar", + "-Dfml.earlyprogresswindow=false", + "-Dorg.lwjgl.util.Debug=true", + "-Dorg.lwjgl.util.DebugFunctions=true", + "-Dorg.lwjgl.util.DebugLoader=true", + "-Dos.name=Linux", + "-Dlwjgl.platform=FCL", + "-Dorg.lwjgl.opengl.libname=%s", + "-Djava.io.tmpdir=/data/user/0/com.tungsten.fcl/cache", + "-Dminecraft.launcher.brand=FCL", + "-Dminecraft.launcher.version=1.0.0", + "-Xms1024M", + "-Xmx1024M", + "net.minecraft.client.main.Main", + "--username", + "Tunsgten", + "--version", + "1.7.10", + "--gameDir", + "/storage/emulated/0/HMCLPE/.minecraft/versions/1.7.10", + "--assetsDir", + "/storage/emulated/0/HMCLPE/.minecraft/assets", + "--assetIndex", + "1.7.10", + "--uuid", + "410efec2-313f-4346-aa2a-7708597ff92f", + "--accessToken", + "eyJhbGciOiJIUzI1NiJ9.eyJ4dWlkIjoiMjUzNTQyNTU3Mzk1OTc0MCIsImFnZyI6IkFkdWx0Iiwic3ViIjoiNzcyMDU1YzMtMjNhMi00NmE0LThjOTItODRiOTE5NDVkOTFhIiwibmJmIjoxNjY1NTc0MzExLCJhdXRoIjoiWEJPWCIsInJvbGVzIjpbXSwiaXNzIjoiYXV0aGVudGljYXRpb24iLCJleHAiOjE2NjU2NjA3MTEsImlhdCI6MTY2NTU3NDMxMSwicGxhdGZvcm0iOiJVTktOT1dOIiwieXVpZCI6IjFhYTRlNDM5MGVmMTgwZWE1M2Q2MjBkMzEyM2Y5YzE3In0.8jpLk9Wr_g-SxeJrHtGC7vSxu54I6gw-0g6hSdQiSYk", + "--userProperties", + "{}", + "--userType", + "mojang", + "--width", + "2000", + "--height", + "1200" + }; + + TextureView textureView = findViewById(R.id.main); + textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int i, int i1) { + FCLConfig config = new FCLConfig(MainActivity.this, + new Surface(surfaceTexture), + getExternalFilesDir("log").getAbsolutePath(), + getFilesDir() + "/java", + Environment.getExternalStorageDirectory() + "/HMCLPE/.minecraft", + FCLConfig.Renderer.RENDERER_GL4ES, + args3, + new FCLBridgeCallback() { + @Override + public void onCursorModeChange(int mode) { + + } + + @Override + public void onExit(int code) { + + } + }); + FCLauncher.launchMinecraft(config); + } + + @Override + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, int i, int i1) { + + } + + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) { + return false; + } + + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) { + + } + }); + } +} \ No newline at end of file diff --git a/FCL/src/main/res/drawable-v24/ic_launcher_foreground.xml b/FCL/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/FCL/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/FCL/src/main/res/drawable/ic_launcher_background.xml b/FCL/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/FCL/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FCL/src/main/res/layout/activity_main.xml b/FCL/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..3f9788c8 --- /dev/null +++ b/FCL/src/main/res/layout/activity_main.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/FCL/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/FCL/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/FCL/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/FCL/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/FCL/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/FCL/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/FCL/src/main/res/mipmap-hdpi/ic_launcher.webp b/FCL/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/FCL/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/FCL/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/FCL/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/FCL/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/FCL/src/main/res/mipmap-mdpi/ic_launcher.webp b/FCL/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/FCL/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/FCL/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/FCL/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/FCL/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/FCL/src/main/res/mipmap-xhdpi/ic_launcher.webp b/FCL/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/FCL/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/FCL/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/FCL/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/FCL/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/FCL/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/FCL/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/FCL/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/FCL/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/FCL/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/FCL/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/FCL/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/FCL/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/FCL/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/FCL/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/FCL/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/FCL/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/FCL/src/main/res/values-night/themes.xml b/FCL/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..e59b723a --- /dev/null +++ b/FCL/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/FCL/src/main/res/values/colors.xml b/FCL/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/FCL/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/FCL/src/main/res/values/strings.xml b/FCL/src/main/res/values/strings.xml new file mode 100644 index 00000000..80542811 --- /dev/null +++ b/FCL/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Fold Craft Launcher + \ No newline at end of file diff --git a/FCL/src/main/res/values/themes.xml b/FCL/src/main/res/values/themes.xml new file mode 100644 index 00000000..2734589a --- /dev/null +++ b/FCL/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/FCL/src/main/res/xml/backup_rules.xml b/FCL/src/main/res/xml/backup_rules.xml new file mode 100644 index 00000000..fa0f996d --- /dev/null +++ b/FCL/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/FCL/src/main/res/xml/data_extraction_rules.xml b/FCL/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..9ee9997b --- /dev/null +++ b/FCL/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/FCL/src/test/java/com/tungsten/fcl/ExampleUnitTest.java b/FCL/src/test/java/com/tungsten/fcl/ExampleUnitTest.java new file mode 100644 index 00000000..b4abb27e --- /dev/null +++ b/FCL/src/test/java/com/tungsten/fcl/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.tungsten.fcl; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/FCLCore/.gitignore b/FCLCore/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/FCLCore/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/FCLCore/build.gradle b/FCLCore/build.gradle new file mode 100644 index 00000000..7c213451 --- /dev/null +++ b/FCLCore/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'com.android.library' +} + +android { + namespace 'com.tungsten.fclcore' + compileSdk 32 + + defaultConfig { + minSdk 23 + targetSdk 32 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation 'org.apache.commons:commons-lang3:3.12.0' + implementation 'org.jenkins-ci:constant-pool-scanner:1.2' + implementation 'com.google.code.gson:gson:2.9.0' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.6.1' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/FCLCore/consumer-rules.pro b/FCLCore/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/FCLCore/proguard-rules.pro b/FCLCore/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/FCLCore/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/FCLCore/src/androidTest/java/com/tungsten/fclcore/ExampleInstrumentedTest.java b/FCLCore/src/androidTest/java/com/tungsten/fclcore/ExampleInstrumentedTest.java new file mode 100644 index 00000000..0ee0abab --- /dev/null +++ b/FCLCore/src/androidTest/java/com/tungsten/fclcore/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.tungsten.fclcore; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.tungsten.fclcore.test", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/FCLCore/src/main/AndroidManifest.xml b/FCLCore/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/FCLCore/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/constant/UrlConstants.java b/FCLCore/src/main/java/com/tungsten/fclcore/constant/UrlConstants.java new file mode 100644 index 00000000..304b56d3 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/constant/UrlConstants.java @@ -0,0 +1,9 @@ +package com.tungsten.fclcore.constant; + +public class UrlConstants { + + public static final String DEFAULT_LIBRARY_URL = "https://libraries.minecraft.net/"; + public static final String DEFAULT_VERSION_DOWNLOAD_URL = "https://s3.amazonaws.com/Minecraft.Download/versions/"; + public static final String DEFAULT_INDEX_URL = "https://s3.amazonaws.com/Minecraft.Download/indexes/"; + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/AbstractDependencyManager.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/AbstractDependencyManager.java new file mode 100644 index 00000000..1081f87f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/AbstractDependencyManager.java @@ -0,0 +1,14 @@ +package com.tungsten.fclcore.download; + +public abstract class AbstractDependencyManager implements DependencyManager { + + public abstract DownloadProvider getDownloadProvider(); + + @Override + public abstract DefaultCacheRepository getCacheRepository(); + + @Override + public VersionList getVersionList(String id) { + return getDownloadProvider().getVersionListById(id); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/AdaptedDownloadProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/AdaptedDownloadProvider.java new file mode 100644 index 00000000..5dc86da0 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/AdaptedDownloadProvider.java @@ -0,0 +1,72 @@ +package com.tungsten.fclcore.download; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The download provider that changes the real download source in need. + */ +public class AdaptedDownloadProvider implements DownloadProvider { + + private List downloadProviderCandidates; + + public void setDownloadProviderCandidates(List downloadProviderCandidates) { + this.downloadProviderCandidates = new ArrayList<>(downloadProviderCandidates); + } + + public DownloadProvider getPreferredDownloadProvider() { + List d = downloadProviderCandidates; + if (d == null || d.isEmpty()) { + throw new IllegalStateException("No download provider candidate"); + } + return d.get(0); + } + + @Override + public String getVersionListURL() { + return getPreferredDownloadProvider().getVersionListURL(); + } + + @Override + public String getAssetBaseURL() { + return getPreferredDownloadProvider().getAssetBaseURL(); + } + + @Override + public String injectURL(String baseURL) { + return getPreferredDownloadProvider().injectURL(baseURL); + } + + @Override + public List getAssetObjectCandidates(String assetObjectLocation) { + return downloadProviderCandidates.stream() + .flatMap(d -> d.getAssetObjectCandidates(assetObjectLocation).stream()) + .collect(Collectors.toList()); + } + + @Override + public List injectURLWithCandidates(String baseURL) { + return downloadProviderCandidates.stream() + .flatMap(d -> d.injectURLWithCandidates(baseURL).stream()) + .collect(Collectors.toList()); + } + + @Override + public List injectURLsWithCandidates(List urls) { + return downloadProviderCandidates.stream() + .flatMap(d -> d.injectURLsWithCandidates(urls).stream()) + .collect(Collectors.toList()); + } + + @Override + public VersionList getVersionListById(String id) { + return getPreferredDownloadProvider().getVersionListById(id); + } + + @Override + public int getConcurrency() { + return getPreferredDownloadProvider().getConcurrency(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/ArtifactMalformedException.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/ArtifactMalformedException.java new file mode 100644 index 00000000..4712a747 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/ArtifactMalformedException.java @@ -0,0 +1,14 @@ +package com.tungsten.fclcore.download; + +import java.io.IOException; + +public class ArtifactMalformedException extends IOException { + public ArtifactMalformedException(String message) { + super(message); + } + + public ArtifactMalformedException(String message, Throwable cause) { + super(message, cause); + } +} + diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/AutoDownloadProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/AutoDownloadProvider.java new file mode 100644 index 00000000..9b4ae513 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/AutoDownloadProvider.java @@ -0,0 +1,58 @@ +package com.tungsten.fclcore.download; + +import java.net.URL; +import java.util.List; + +/** + * Official Download Provider fetches version list from Mojang and + * download files from mcbbs. + */ +public class AutoDownloadProvider implements DownloadProvider { + private final DownloadProvider versionListProvider; + private final DownloadProvider fileProvider; + + public AutoDownloadProvider(DownloadProvider versionListProvider, DownloadProvider fileProvider) { + this.versionListProvider = versionListProvider; + this.fileProvider = fileProvider; + } + + @Override + public String getVersionListURL() { + return versionListProvider.getVersionListURL(); + } + + @Override + public String getAssetBaseURL() { + return fileProvider.getAssetBaseURL(); + } + + @Override + public String injectURL(String baseURL) { + return fileProvider.injectURL(baseURL); + } + + @Override + public List getAssetObjectCandidates(String assetObjectLocation) { + return fileProvider.getAssetObjectCandidates(assetObjectLocation); + } + + @Override + public List injectURLWithCandidates(String baseURL) { + return fileProvider.injectURLWithCandidates(baseURL); + } + + @Override + public List injectURLsWithCandidates(List urls) { + return fileProvider.injectURLsWithCandidates(urls); + } + + @Override + public VersionList getVersionListById(String id) { + return versionListProvider.getVersionListById(id); + } + + @Override + public int getConcurrency() { + return fileProvider.getConcurrency(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/BMCLAPIDownloadProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/BMCLAPIDownloadProvider.java new file mode 100644 index 00000000..6f6827b5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/BMCLAPIDownloadProvider.java @@ -0,0 +1,96 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.download.fabric.FabricAPIVersionList; +import com.tungsten.fclcore.download.fabric.FabricVersionList; +import com.tungsten.fclcore.download.forge.ForgeBMCLVersionList; +import com.tungsten.fclcore.download.game.GameVersionList; +import com.tungsten.fclcore.download.liteloader.LiteLoaderBMCLVersionList; +import com.tungsten.fclcore.download.optifine.OptiFineBMCLVersionList; +import com.tungsten.fclcore.download.quilt.QuiltAPIVersionList; +import com.tungsten.fclcore.download.quilt.QuiltVersionList; + +public class BMCLAPIDownloadProvider implements DownloadProvider { + private final String apiRoot; + private final GameVersionList game; + private final FabricVersionList fabric; + private final FabricAPIVersionList fabricApi; + private final ForgeBMCLVersionList forge; + private final LiteLoaderBMCLVersionList liteLoader; + private final OptiFineBMCLVersionList optifine; + private final QuiltVersionList quilt; + private final QuiltAPIVersionList quiltApi; + + public BMCLAPIDownloadProvider(String apiRoot) { + this.apiRoot = apiRoot; + this.game = new GameVersionList(this); + this.fabric = new FabricVersionList(this); + this.fabricApi = new FabricAPIVersionList(this); + this.forge = new ForgeBMCLVersionList(apiRoot); + this.liteLoader = new LiteLoaderBMCLVersionList(this); + this.optifine = new OptiFineBMCLVersionList(apiRoot); + this.quilt = new QuiltVersionList(this); + this.quiltApi = new QuiltAPIVersionList(this); + } + + public String getApiRoot() { + return apiRoot; + } + + @Override + public String getVersionListURL() { + return apiRoot + "/mc/game/version_manifest.json"; + } + + @Override + public String getAssetBaseURL() { + return apiRoot + "/assets/"; + } + + @Override + public VersionList getVersionListById(String id) { + switch (id) { + case "game": + return game; + case "fabric": + return fabric; + case "fabric-api": + return fabricApi; + case "forge": + return forge; + case "liteloader": + return liteLoader; + case "optifine": + return optifine; + case "quilt": + return quilt; + case "quilt-api": + return quiltApi; + default: + throw new IllegalArgumentException("Unrecognized version list id: " + id); + } + } + + @Override + public String injectURL(String baseURL) { + return baseURL + .replace("https://bmclapi2.bangbang93.com", apiRoot) + .replace("https://launchermeta.mojang.com", apiRoot) + .replace("https://piston-meta.mojang.com", apiRoot) + .replace("https://piston-data.mojang.com", apiRoot) + .replace("https://launcher.mojang.com", apiRoot) + .replace("https://libraries.minecraft.net", apiRoot + "/libraries") + .replaceFirst("https?://files\\.minecraftforge\\.net/maven", apiRoot + "/maven") + .replace("https://maven.minecraftforge.net", apiRoot + "/maven") + .replace("http://dl.liteloader.com/versions/versions.json", apiRoot + "/maven/com/mumfrey/liteloader/versions.json") + .replace("http://dl.liteloader.com/versions", apiRoot + "/maven") + .replace("https://meta.fabricmc.net", apiRoot + "/fabric-meta") + .replace("https://maven.fabricmc.net", apiRoot + "/maven") + .replace("https://authlib-injector.yushi.moe", apiRoot + "/mirrors/authlib-injector") + .replace("https://repo1.maven.org/maven2", "https://maven.aliyun.com/repository/central"); + } + + @Override + public int getConcurrency() { + return Math.max(Runtime.getRuntime().availableProcessors() * 2, 6); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/BalancedDownloadProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/BalancedDownloadProvider.java new file mode 100644 index 00000000..2a239e9e --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/BalancedDownloadProvider.java @@ -0,0 +1,51 @@ +package com.tungsten.fclcore.download; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Official Download Provider fetches version list from Mojang and + * download files from mcbbs. + */ +public class BalancedDownloadProvider implements DownloadProvider { + List candidates; + + Map> versionLists = new HashMap<>(); + + public BalancedDownloadProvider(List candidates) { + this.candidates = candidates; + } + + @Override + public String getVersionListURL() { + throw new UnsupportedOperationException(); + } + + @Override + public String getAssetBaseURL() { + throw new UnsupportedOperationException(); + } + + @Override + public String injectURL(String baseURL) { + throw new UnsupportedOperationException(); + } + + @Override + public VersionList getVersionListById(String id) { + if (!versionLists.containsKey(id)) { + versionLists.put(id, new MultipleSourceVersionList( + candidates.stream() + .map(downloadProvider -> downloadProvider.getVersionListById(id)) + .collect(Collectors.toList()))); + } + return versionLists.get(id); + } + + @Override + public int getConcurrency() { + throw new UnsupportedOperationException(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultCacheRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultCacheRepository.java new file mode 100644 index 00000000..dd578747 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultCacheRepository.java @@ -0,0 +1,283 @@ +package com.tungsten.fclcore.download; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.game.LibraryDownloadTask; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.LibraryDownloadInfo; +import com.tungsten.fclcore.util.CacheRepository; +import com.tungsten.fclcore.util.DigestUtils; +import com.tungsten.fclcore.util.Hex; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; +import com.tungsten.fclcore.util.io.FileUtils; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public class DefaultCacheRepository extends CacheRepository { + private Path librariesDir; + private Path indexFile; + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private Index index = null; + + public DefaultCacheRepository() { + this(OperatingSystem.getWorkingDirectory("minecraft")); + } + + public DefaultCacheRepository(Path commonDirectory) { + changeDirectory(commonDirectory); + } + + @Override + public void changeDirectory(Path commonDir) { + super.changeDirectory(commonDir); + + librariesDir = commonDir.resolve("libraries"); + indexFile = getCacheDirectory().resolve("index.json"); + + lock.writeLock().lock(); + try { + if (Files.isRegularFile(indexFile)) + index = JsonUtils.fromNonNullJson(FileUtils.readText(indexFile.toFile()), Index.class); + else + index = new Index(); + } catch (IOException | JsonParseException e) { + Logging.LOG.log(Level.WARNING, "Unable to read index file", e); + index = new Index(); + } finally { + lock.writeLock().unlock(); + } + } + + /** + * Try to cache the library given. + * This library will be cached only if it is verified. + * If cannot be verified, the library will not be cached. + * + * @param library the library being cached + * @param jar the file of library + */ + public void tryCacheLibrary(Library library, Path jar) { + lock.readLock().lock(); + try { + if (index.getLibraries().stream().anyMatch(it -> library.getName().equals(it.getName()))) + return; + } finally { + lock.readLock().unlock(); + } + + try { + LibraryDownloadInfo info = library.getDownload(); + String hash = info.getSha1(); + if (hash != null) { + String checksum = Hex.encodeHex(DigestUtils.digest("SHA-1", jar)); + if (hash.equalsIgnoreCase(checksum)) + cacheLibrary(library, jar, false); + } else if (library.getChecksums() != null && !library.getChecksums().isEmpty()) { + if (LibraryDownloadTask.checksumValid(jar.toFile(), library.getChecksums())) + cacheLibrary(library, jar, true); + } else { + // or we will not cache the library + } + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Unable to calc hash value of file " + jar, e); + } + } + + /** + * Get the path of cached library, empty if not cached + * + * @param library the library we check if cached. + * @return the cached path if exists, otherwise empty + */ + public Optional getLibrary(Library library) { + LibraryDownloadInfo info = library.getDownload(); + String hash = info.getSha1(); + + if (fileExists(SHA1, hash)) + return Optional.of(getFile(SHA1, hash)); + + Lock readLock = lock.readLock(); + readLock.lock(); + + try { + // check if this library is from Forge + List libraries = index.getLibraries().stream() + .filter(it -> it.getName().equals(library.getName())) + .collect(Collectors.toList()); + for (LibraryIndex libIndex : libraries) { + if (fileExists(SHA1, libIndex.getHash())) { + Path file = getFile(SHA1, libIndex.getHash()); + if (libIndex.getType().equalsIgnoreCase(LibraryIndex.TYPE_FORGE)) { + if (LibraryDownloadTask.checksumValid(file.toFile(), library.getChecksums())) + return Optional.of(file); + } + } + } + } finally { + readLock.unlock(); + } + + // check old common directory + Path jar = librariesDir.resolve(info.getPath()); + if (Files.exists(jar)) { + try { + if (hash != null) { + String checksum = Hex.encodeHex(DigestUtils.digest("SHA-1", jar)); + if (hash.equalsIgnoreCase(checksum)) + return Optional.of(restore(jar, () -> cacheLibrary(library, jar, false))); + } else if (library.getChecksums() != null && !library.getChecksums().isEmpty()) { + if (LibraryDownloadTask.checksumValid(jar.toFile(), library.getChecksums())) + return Optional.of(restore(jar, () -> cacheLibrary(library, jar, true))); + } else { + return Optional.of(jar); + } + } catch (IOException e) { + // we cannot check the hashcode or unable to move file. + } + } + + return Optional.empty(); + } + + /** + * Caches the library file to repository. + * + * @param library the library to cache + * @param path the file being cached, must be verified + * @param forge true if this library is provided by Forge + * @return cached file location + * @throws IOException if failed to calculate hash code of {@code path} or copy the file to cache + */ + public Path cacheLibrary(Library library, Path path, boolean forge) throws IOException { + String hash = library.getDownload().getSha1(); + if (hash == null) + hash = Hex.encodeHex(DigestUtils.digest(SHA1, path)); + + Path cache = getFile(SHA1, hash); + FileUtils.copyFile(path.toFile(), cache.toFile()); + + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + LibraryIndex libIndex = new LibraryIndex(library.getName(), hash, forge ? LibraryIndex.TYPE_FORGE : LibraryIndex.TYPE_JAR); + index.getLibraries().add(libIndex); + saveIndex(); + } finally { + writeLock.unlock(); + } + + return cache; + } + + private void saveIndex() { + if (indexFile == null || index == null) return; + try { + FileUtils.writeText(indexFile.toFile(), JsonUtils.GSON.toJson(index)); + } catch (IOException e) { + Logging.LOG.log(Level.SEVERE, "Unable to save index.json", e); + } + } + + /** + * { + * "libraries": { + * // allow a library has multiple hash code. + * [ + * "name": "net.minecraftforge:forge:1.11.2-13.20.0.2345", + * "hash": "blablabla", + * "type": "forge" + * ] + * } + * // assets and versions will not be included in index. + * } + */ + private class Index implements Validation { + private final Set libraries; + + public Index() { + this(new HashSet<>()); + } + + public Index(Set libraries) { + this.libraries = Objects.requireNonNull(libraries); + } + + @NotNull + public Set getLibraries() { + return libraries; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (libraries == null) + throw new JsonParseException("Index.libraries cannot be null"); + } + } + + private class LibraryIndex implements Validation { + private final String name; + private final String hash; + private final String type; + + public LibraryIndex() { + this("", "", ""); + } + + public LibraryIndex(String name, String hash, String type) { + this.name = name; + this.hash = hash; + this.type = type; + } + + @NotNull + public String getName() { + return name; + } + + @NotNull + public String getHash() { + return hash; + } + + @NotNull + public String getType() { + return type; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (name == null || hash == null || type == null) + throw new JsonParseException("Index.LibraryIndex.* cannot be null"); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LibraryIndex that = (LibraryIndex) o; + return Objects.equals(name, that.name) && + Objects.equals(hash, that.hash) && + Objects.equals(type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(name, hash, type); + } + + public static final String TYPE_FORGE = "forge"; + public static final String TYPE_JAR = "jar"; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultDependencyManager.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultDependencyManager.java new file mode 100644 index 00000000..dd2a8e1c --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultDependencyManager.java @@ -0,0 +1,200 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.download.forge.ForgeInstallTask; +import com.tungsten.fclcore.download.game.GameAssetDownloadTask; +import com.tungsten.fclcore.download.game.GameDownloadTask; +import com.tungsten.fclcore.download.game.GameLibrariesTask; +import com.tungsten.fclcore.download.optifine.OptiFineInstallTask; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Note: This class has no state. + */ +public class DefaultDependencyManager extends AbstractDependencyManager { + + private final DefaultGameRepository repository; + private final DownloadProvider downloadProvider; + private final DefaultCacheRepository cacheRepository; + + public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, DefaultCacheRepository cacheRepository) { + this.repository = repository; + this.downloadProvider = downloadProvider; + this.cacheRepository = cacheRepository; + } + + @Override + public DefaultGameRepository getGameRepository() { + return repository; + } + + @Override + public DownloadProvider getDownloadProvider() { + return downloadProvider; + } + + @Override + public DefaultCacheRepository getCacheRepository() { + return cacheRepository; + } + + @Override + public GameBuilder gameBuilder() { + return new DefaultGameBuilder(this); + } + + @Override + public Task checkGameCompletionAsync(Version version, boolean integrityCheck) { + return Task.allOf( + Task.composeAsync(() -> { + File versionJar = repository.getVersionJar(version); + if (!versionJar.exists() || versionJar.length() == 0) + return new GameDownloadTask(this, null, version); + else + return null; + }).thenComposeAsync(checkPatchCompletionAsync(version, integrityCheck)), + new GameAssetDownloadTask(this, version, GameAssetDownloadTask.DOWNLOAD_INDEX_IF_NECESSARY, integrityCheck), + new GameLibrariesTask(this, version, integrityCheck) + ); + } + + @Override + public Task checkLibraryCompletionAsync(Version version, boolean integrityCheck) { + return new GameLibrariesTask(this, version, integrityCheck, version.getLibraries()); + } + + @Override + public Task checkPatchCompletionAsync(Version version, boolean integrityCheck) { + return Task.composeAsync(() -> { + List> tasks = new ArrayList<>(0); + + String gameVersion = repository.getGameVersion(version).orElse(null); + if (gameVersion == null) return null; + + Version original = repository.getVersion(version.getId()); + Version resolved = original.resolvePreservingPatches(repository); + + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved); + for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) { + if (!analyzer.has(type)) + continue; + + if (type == LibraryAnalyzer.LibraryType.OPTIFINE) { + String optifinePatchVersion = analyzer.getVersion(type) + .map(optifineVersion -> { + Matcher matcher = Pattern.compile("^([0-9.]+)_(?HD_.+)$").matcher(optifineVersion); + return matcher.find() ? matcher.group("optifine") : optifineVersion; + }) + .orElseGet(() -> resolved.getPatches().stream() + .filter(patch -> "optifine".equals(patch.getId())) + .findAny() + .map(Version::getVersion) + .orElse(null)); + + boolean needsReInstallation = version.getLibraries().stream() + .anyMatch(library -> !library.hasDownloadURL() + && "optifine".equals(library.getGroupId()) + && GameLibrariesTask.shouldDownloadLibrary(repository, version, library, integrityCheck)); + + if (needsReInstallation) { + Library installer = new Library(new Artifact("optifine", "OptiFine", gameVersion + "_" + optifinePatchVersion, "installer")); + if (GameLibrariesTask.shouldDownloadLibrary(repository, version, installer, integrityCheck)) { + tasks.add(installLibraryAsync(gameVersion, original, "optifine", optifinePatchVersion)); + } else { + tasks.add(OptiFineInstallTask.install(this, original, repository.getLibraryFile(version, installer).toPath())); + } + } + } + } + + return Task.allOf(tasks); + }); + } + + @Override + public Task installLibraryAsync(String gameVersion, Version baseVersion, String libraryId, String libraryVersion) { + if (baseVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved"); + + VersionList versionList = getVersionList(libraryId); + return Task.fromCompletableFuture(versionList.loadAsync(gameVersion)) + .thenComposeAsync(() -> installLibraryAsync(baseVersion, versionList.getVersion(gameVersion, libraryVersion) + .orElseThrow(() -> new IOException("Remote library " + libraryId + " has no version " + libraryVersion)))) + .withStage(String.format("hmcl.install.%s:%s", libraryId, libraryVersion)); + } + + @Override + public Task installLibraryAsync(Version baseVersion, RemoteVersion libraryVersion) { + if (baseVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved"); + + AtomicReference removedLibraryVersion = new AtomicReference<>(); + + return removeLibraryAsync(baseVersion.resolvePreservingPatches(repository), libraryVersion.getLibraryId()) + .thenComposeAsync(version -> { + removedLibraryVersion.set(version); + return libraryVersion.getInstallTask(this, version); + }) + .thenApplyAsync(patch -> { + if (patch == null) { + return removedLibraryVersion.get(); + } else { + return removedLibraryVersion.get().addPatch(patch); + } + }) + .withStage(String.format("hmcl.install.%s:%s", libraryVersion.getLibraryId(), libraryVersion.getSelfVersion())); + } + + public Task installLibraryAsync(Version oldVersion, Path installer) { + if (oldVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved"); + + return Task + .composeAsync(() -> { + try { + return ForgeInstallTask.install(this, oldVersion, installer); + } catch (IOException ignore) { + } + + try { + return OptiFineInstallTask.install(this, oldVersion, installer); + } catch (IOException ignore) { + } + + throw new UnsupportedLibraryInstallerException(); + }) + .thenApplyAsync(oldVersion::addPatch); + } + + public static class UnsupportedLibraryInstallerException extends Exception { + } + + /** + * Remove installed library. + * Will try to remove libraries and patches. + * + * @param version not resolved version + * @param libraryId forge/liteloader/optifine/fabric + * @return task to remove the specified library + */ + public Task removeLibraryAsync(Version version, String libraryId) { + // MaintainTask requires version that does not inherits from any version. + // If we want to remove a library in dependent version, we should keep the dependents not changed + // So resolving this game version to preserve all information in this version.json is necessary. + if (version.isResolved()) + throw new IllegalArgumentException("removeLibraryWithoutSavingAsync requires non-resolved version"); + Version independentVersion = version.resolvePreservingPatches(repository); + + return Task.supplyAsync(() -> LibraryAnalyzer.analyze(independentVersion).removeLibrary(libraryId).build()); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultGameBuilder.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultGameBuilder.java new file mode 100644 index 00000000..0d4d3fd8 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/DefaultGameBuilder.java @@ -0,0 +1,51 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.function.ExceptionalFunction; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class DefaultGameBuilder extends GameBuilder { + + private final DefaultDependencyManager dependencyManager; + + public DefaultGameBuilder(DefaultDependencyManager dependencyManager) { + this.dependencyManager = dependencyManager; + } + + public DefaultDependencyManager getDependencyManager() { + return dependencyManager; + } + + @Override + public Task buildAsync() { + List stages = new ArrayList<>(); + + Task libraryTask = Task.supplyAsync(() -> new Version(name)); + libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, "game", gameVersion)); + stages.add("hmcl.install.game:" + gameVersion); + stages.add("hmcl.install.assets"); + + for (Map.Entry entry : toolVersions.entrySet()) { + libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, entry.getKey(), entry.getValue())); + stages.add(String.format("hmcl.install.%s:%s", entry.getKey(), entry.getValue())); + } + + for (RemoteVersion remoteVersion : remoteVersions) { + libraryTask = libraryTask.thenComposeAsync(version -> dependencyManager.installLibraryAsync(version, remoteVersion)); + stages.add(String.format("hmcl.install.%s:%s", remoteVersion.getLibraryId(), remoteVersion.getSelfVersion())); + } + + return libraryTask.thenComposeAsync(dependencyManager.getGameRepository()::saveAsync).whenComplete(exception -> { + if (exception != null) + dependencyManager.getGameRepository().removeVersionFromDisk(name); + }).withStagesHint(stages); + } + + private ExceptionalFunction, ?> libraryTaskHelper(String gameVersion, String libraryId, String libraryVersion) { + return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, libraryVersion); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/DependencyManager.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/DependencyManager.java new file mode 100644 index 00000000..dd342a75 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/DependencyManager.java @@ -0,0 +1,84 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.game.GameRepository; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.CacheRepository; + +/** + * Do everything that will connect to Internet. + * Downloading Minecraft files. + */ +public interface DependencyManager { + + /** + * The relied game repository. + */ + GameRepository getGameRepository(); + + /** + * The cache repository + */ + CacheRepository getCacheRepository(); + + /** + * Check if the game is complete. + * Check libraries, assets files and so on. + * + * @return the task to check game completion. + */ + Task checkGameCompletionAsync(Version version, boolean integrityCheck); + + /** + * Check if libraries of this version in complete. + * If not, download missing libraries if possible. + * + * @return the task to check game completion. + */ + Task checkLibraryCompletionAsync(Version version, boolean integrityCheck); + + /** + * Check if patches of this version in complete. + * If not, reinstall the patch if possible. + * + * @param version the version to be checked + * @param integrityCheck check if some libraries are corrupt. + * @return the task to check patches completion. + */ + Task checkPatchCompletionAsync(Version version, boolean integrityCheck); + + /** + * The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine. + */ + GameBuilder gameBuilder(); + + /** + * Install a library to a version. + * **Note**: Installing a library may change the version.json. + * + * @param gameVersion the Minecraft version that the library relies on. + * @param baseVersion the version.json. + * @param libraryId the type of being installed library. i.e. "forge", "liteloader", "optifine" + * @param libraryVersion the version of being installed library. + * @return the task to install the specific library. + */ + Task installLibraryAsync(String gameVersion, Version baseVersion, String libraryId, String libraryVersion); + + /** + * Install a library to a version. + * **Note**: Installing a library may change the version.json. + * + * @param baseVersion the version.json. + * @param libraryVersion the remote version of being installed library. + * @return the task to install the specific library. + */ + Task installLibraryAsync(Version baseVersion, RemoteVersion libraryVersion); + + /** + * Get registered version list. + * + * @param id the id of version list. i.e. game, forge, liteloader, optifine + * @throws IllegalArgumentException if the version list of specific id is not found. + */ + VersionList getVersionList(String id); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/DownloadProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/DownloadProvider.java new file mode 100644 index 00000000..7ed22583 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/DownloadProvider.java @@ -0,0 +1,67 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.util.io.NetworkUtils; + +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The service provider that provides Minecraft online file downloads. + * + * @author huangyuhui + */ +public interface DownloadProvider { + + String getVersionListURL(); + + String getAssetBaseURL(); + + default List getAssetObjectCandidates(String assetObjectLocation) { + return Collections.singletonList(NetworkUtils.toURL(getAssetBaseURL() + assetObjectLocation)); + } + + /** + * Inject into original URL provided by Mojang and Forge. + * + * Since there are many provided URLs that are written in JSONs and are unmodifiable, + * this method provides a way to change them. + * + * @param baseURL original URL provided by Mojang and Forge. + * @return the URL that is equivalent to [baseURL], but belongs to your own service provider. + */ + String injectURL(String baseURL); + + /** + * Inject into original URL provided by Mojang and Forge. + * + * Since there are many provided URLs that are written in JSONs and are unmodifiable, + * this method provides a way to change them. + * + * @param baseURL original URL provided by Mojang and Forge. + * @return the URL that is equivalent to [baseURL], but belongs to your own service provider. + */ + default List injectURLWithCandidates(String baseURL) { + return Collections.singletonList(NetworkUtils.toURL(injectURL(baseURL))); + } + + default List injectURLsWithCandidates(List urls) { + return urls.stream().flatMap(url -> injectURLWithCandidates(url).stream()).collect(Collectors.toList()); + } + + /** + * the specific version list that this download provider provides. i.e. "fabric", "forge", "liteloader", "game", "optifine" + * + * @param id the id of specific version list that this download provider provides. i.e. "fabric", "forge", "liteloader", "game", "optifine" + * @return the version list + * @throws IllegalArgumentException if the version list does not exist + */ + VersionList getVersionListById(String id); + + /** + * The maximum download concurrency that this download provider supports. + * @return the maximum download concurrency. + */ + int getConcurrency(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/GameBuilder.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/GameBuilder.java new file mode 100644 index 00000000..4a38fd8d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/GameBuilder.java @@ -0,0 +1,57 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.task.Task; + +import java.util.*; + +/** + * The builder which provide a task to build Minecraft environment. + */ +public abstract class GameBuilder { + + protected String name = ""; + protected String gameVersion = ""; + protected final Map toolVersions = new HashMap<>(); + protected final Set remoteVersions = new HashSet<>(); + + public String getName() { + return name; + } + + /** + * The new game version name, for .minecraft/<version name>. + * + * @param name the name of new game version. + */ + public GameBuilder name(String name) { + this.name = Objects.requireNonNull(name); + return this; + } + + public GameBuilder gameVersion(String version) { + this.gameVersion = Objects.requireNonNull(version); + return this; + } + + /** + * @param id the core library id. i.e. "forge", "liteloader", "optifine" + * @param version the version of the core library. For documents, you can first try [VersionList.versions] + */ + public GameBuilder version(String id, String version) { + if ("game".equals(id)) + gameVersion(version); + else + toolVersions.put(id, version); + return this; + } + + public GameBuilder version(RemoteVersion remoteVersion) { + remoteVersions.add(remoteVersion); + return this; + } + + /** + * @return the task that can build thw whole Minecraft environment + */ + public abstract Task buildAsync(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/LibraryAnalyzer.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/LibraryAnalyzer.java new file mode 100644 index 00000000..4f06ba31 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/LibraryAnalyzer.java @@ -0,0 +1,256 @@ +package com.tungsten.fclcore.download; + +import static com.tungsten.fclcore.util.Pair.pair; + +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.game.VersionProvider; +import com.tungsten.fclcore.util.Pair; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +// Todo : fix +public final class LibraryAnalyzer implements Iterable { + private Version version; + private final Map> libraries; + + private LibraryAnalyzer(Version version, Map> libraries) { + this.version = version; + this.libraries = libraries; + } + + public String getVersion(LibraryType type) { + return getVersion(type.getPatchId()); + } + + public String getVersion(String type) { + return Objects.requireNonNull(libraries.get(type)).getValue(); + } + + public Library getLibrary(LibraryType type) { + return Objects.requireNonNull(libraries.get(type.getPatchId())).getKey(); + } + + @NotNull + @Override + public Iterator iterator() { + return new Iterator() { + Iterator>> impl = libraries.entrySet().iterator(); + + @Override + public boolean hasNext() { + return impl.hasNext(); + } + + @Override + public LibraryMark next() { + Map.Entry> entry = impl.next(); + return new LibraryMark(entry.getKey(), entry.getValue().getValue()); + } + }; + } + + public boolean has(LibraryType type) { + return has(type.getPatchId()); + } + + public boolean has(String type) { + return libraries.containsKey(type); + } + + public boolean hasModLoader() { + for (String s : libraries.keySet()) { + if (Objects.requireNonNull(LibraryType.fromPatchId(s)).isModLoader()) { + return true; + } + } + return false; + } + + public boolean hasModLauncher() { + final String modLauncher = "cpw.mods.modlauncher.Launcher"; + boolean has = false; + for (Version patch : version.getPatches()) { + if (version.getMainClass().equals(modLauncher)) { + has = true; + break; + } + } + return modLauncher.equals(version.getMainClass()) || has; + } + + public boolean hasBootstrapLauncher() { + final String bootstrapLauncher = "cpw.mods.bootstraplauncher.BootstrapLauncher"; + boolean has = false; + for (Version patch : version.getPatches()) { + if (version.getMainClass().equals(bootstrapLauncher)) { + has = true; + break; + } + } + return bootstrapLauncher.equals(version.getMainClass()) || has; + } + + private Version removingMatchedLibrary(Version version, String libraryId) { + LibraryType type = LibraryType.fromPatchId(libraryId); + if (type == null) return version; + + List libraries = new ArrayList<>(); + for (Library library : version.getLibraries()) { + if (type.matchLibrary(library)) { + // skip + } else { + libraries.add(library); + } + } + return version.setLibraries(libraries); + } + + /** + * Remove library by library id + * @param libraryId patch id or "forge"/"optifine"/"liteloader"/"fabric" + * @return this + */ + public LibraryAnalyzer removeLibrary(String libraryId) { + if (!has(libraryId)) return this; + List newPatches = new ArrayList<>(); + for (Version p : version.getPatches()) { + removingMatchedLibrary(p, libraryId); + newPatches.add(p); + } + version = removingMatchedLibrary(version, libraryId).setPatches(newPatches); + return this; + } + + public Version build() { + return version; + } + + public static LibraryAnalyzer analyze(Version version) { + if (version.getInheritsFrom() != null) + throw new IllegalArgumentException("LibraryAnalyzer can only analyze independent game version"); + + Map> libraries = new HashMap<>(); + + for (Library library : version.resolve(null).getLibraries()) { + for (LibraryType type : LibraryType.values()) { + if (type.matchLibrary(library)) { + libraries.put(type.getPatchId(), pair(library, type.patchVersion(library.getVersion()))); + break; + } + } + } + + for (Version patch : version.getPatches()) { + if (patch.isHidden()) continue; + libraries.put(patch.getId(), pair(null, patch.getVersion())); + } + + return new LibraryAnalyzer(version, libraries); + } + + public static boolean isModded(VersionProvider provider, Version version) { + Version resolvedVersion = version.resolve(provider); + String mainClass = resolvedVersion.getMainClass(); + return mainClass != null && (LAUNCH_WRAPPER_MAIN.equals(mainClass) || mainClass.startsWith("net.fabricmc") || mainClass.startsWith("cpw.mods")); + } + + public enum LibraryType { + MINECRAFT(true, "game", Pattern.compile("^$"), Pattern.compile("^$")), + FABRIC(true, "fabric", Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-loader")), + FABRIC_API(true, "fabric-api", Pattern.compile("net\\.fabricmc"), Pattern.compile("fabric-api")), + FORGE(true, "forge", Pattern.compile("net\\.minecraftforge"), Pattern.compile("(forge|fmlloader)")) { + private final Pattern FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?[0-9.]+)(-([0-9.]+))?$"); + + @Override + public String patchVersion(String libraryVersion) { + Matcher matcher = FORGE_VERSION_MATCHER.matcher(libraryVersion); + if (matcher.find()) { + return matcher.group("forge"); + } + return super.patchVersion(libraryVersion); + } + }, + LITELOADER(true, "liteloader", Pattern.compile("com\\.mumfrey"), Pattern.compile("liteloader")), + OPTIFINE(false, "optifine", Pattern.compile("(net\\.)?optifine"), Pattern.compile("^(?!.*launchwrapper).*$")), + QUILT(true, "quilt", Pattern.compile("org\\.quiltmc"), Pattern.compile("quilt-loader")), + QUILT_API(true, "quilt-api", Pattern.compile("org\\.quiltmc"), Pattern.compile("quilt-api")), + BOOTSTRAP_LAUNCHER(false, "", Pattern.compile("cpw\\.mods"), Pattern.compile("bootstraplauncher")); + + private final boolean modLoader; + private final String patchId; + private final Pattern group, artifact; + + LibraryType(boolean modLoader, String patchId, Pattern group, Pattern artifact) { + this.modLoader = modLoader; + this.patchId = patchId; + this.group = group; + this.artifact = artifact; + } + + public boolean isModLoader() { + return modLoader; + } + + public String getPatchId() { + return patchId; + } + + public static LibraryType fromPatchId(String patchId) { + for (LibraryType type : values()) + if (type.getPatchId().equals(patchId)) + return type; + return null; + } + + public boolean matchLibrary(Library library) { + return group.matcher(library.getGroupId()).matches() && artifact.matcher(library.getArtifactId()).matches(); + } + + public String patchVersion(String libraryVersion) { + return libraryVersion; + } + } + + public static class LibraryMark { + private final String libraryId; + private final String libraryVersion; + + public LibraryMark(@NotNull String libraryId, @Nullable String libraryVersion) { + this.libraryId = libraryId; + this.libraryVersion = libraryVersion; + } + + @NotNull + public String getLibraryId() { + return libraryId; + } + + @Nullable + public String getLibraryVersion() { + return libraryVersion; + } + } + + public static final String VANILLA_MAIN = "net.minecraft.client.main.Main"; + public static final String LAUNCH_WRAPPER_MAIN = "net.minecraft.launchwrapper.Launch"; + public static final String MOD_LAUNCHER_MAIN = "cpw.mods.modlauncher.Launcher"; + public static final String BOOTSTRAP_LAUNCHER_MAIN = "cpw.mods.bootstraplauncher.BootstrapLauncher"; + + public static final String[] FORGE_TWEAKERS = new String[] { + "net.minecraftforge.legacy._1_5_2.LibraryFixerTweaker", // 1.5.2 + "cpw.mods.fml.common.launcher.FMLTweaker", // 1.6.1 ~ 1.7.10 + "net.minecraftforge.fml.common.launcher.FMLTweaker" // 1.8 ~ 1.12.2 + }; + public static final String[] OPTIFINE_TWEAKERS = new String[] { + "optifine.OptiFineTweaker", + "optifine.OptiFineForgeTweaker" + }; + public static final String LITELOADER_TWEAKER = "com.mumfrey.liteloader.launch.LiteLoaderTweaker"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/MaintainTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/MaintainTask.java new file mode 100644 index 00000000..5fb3a7c8 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/MaintainTask.java @@ -0,0 +1,339 @@ +package com.tungsten.fclcore.download; + +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.BOOTSTRAP_LAUNCHER; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.FORGE; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.LITELOADER; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.OPTIFINE; + +import com.tungsten.fclcore.game.Argument; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.CompatibilityRule; +import com.tungsten.fclcore.game.GameRepository; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.StringArgument; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.game.VersionLibraryBuilder; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.SimpleMultimap; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MaintainTask extends Task { + private final GameRepository repository; + private final Version version; + + public MaintainTask(GameRepository repository, Version version) { + this.repository = repository; + this.version = version; + + if (version.getInheritsFrom() != null) + throw new IllegalArgumentException("MaintainTask requires independent game version"); + } + + @Override + public void execute() { + setResult(maintain(repository, version)); + } + + public static Version maintain(GameRepository repository, Version version) { + if (version.getInheritsFrom() != null) + throw new IllegalArgumentException("MaintainTask requires independent game version"); + + String mainClass = version.resolve(null).getMainClass(); + + if (mainClass != null && mainClass.equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)) { + version = maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true), false); + } else if (mainClass != null && mainClass.equals(LibraryAnalyzer.MOD_LAUNCHER_MAIN)) { + // Forge 1.13 and OptiFine + version = maintainOptiFineLibrary(repository, maintainGameWithCpwModLauncher(repository, unique(version)), true); + } else if (mainClass != null && mainClass.equals(LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN)) { + // Forge 1.17 + version = maintainGameWithCpwBoostrapLauncher(repository, unique(version)); + } else { + // Vanilla Minecraft does not need maintain + // Fabric does not need maintain, nothing compatible with fabric now. + version = maintainOptiFineLibrary(repository, unique(version), false); + } + + List libraries = version.getLibraries(); + if (libraries.size() > 0) { + Library library = libraries.get(0); + if ("org.glavo".equals(library.getGroupId()) + && ("log4j-patch".equals(library.getArtifactId()) || "log4j-patch-beta9".equals(library.getArtifactId())) + && "1.0".equals(library.getVersion()) + && library.getDownload() == null) { + version = version.setLibraries(libraries.subList(1, libraries.size())); + } + } + + return version; + } + + public static Version maintainPreservingPatches(GameRepository repository, Version version) { + if (!version.isResolvedPreservingPatches()) + throw new IllegalArgumentException("MaintainTask requires independent game version"); + Version newVersion = maintain(repository, version.resolve(repository)); + return newVersion.setPatches(version.getPatches()).markAsUnresolved(); + } + + private static Version maintainGameWithLaunchWrapper(Version version, boolean reorderTweakClass) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version); + VersionLibraryBuilder builder = new VersionLibraryBuilder(version); + String mainClass = null; + + // Installing Forge will override the Minecraft arguments in json, so LiteLoader and OptiFine Tweaker are being re-added. + if (libraryAnalyzer.has(LITELOADER) && !libraryAnalyzer.hasModLauncher()) { + builder.replaceTweakClass(LibraryAnalyzer.LITELOADER_TWEAKER, LibraryAnalyzer.LITELOADER_TWEAKER, !reorderTweakClass, reorderTweakClass); + } else { + builder.removeTweakClass(LibraryAnalyzer.LITELOADER_TWEAKER); + } + + if (libraryAnalyzer.has(OPTIFINE)) { + if (!libraryAnalyzer.has(LITELOADER) && !libraryAnalyzer.has(FORGE)) { + if (builder.hasTweakClass(LibraryAnalyzer.OPTIFINE_TWEAKERS[1])) { + builder.replaceTweakClass(LibraryAnalyzer.OPTIFINE_TWEAKERS[1], LibraryAnalyzer.OPTIFINE_TWEAKERS[0], !reorderTweakClass, reorderTweakClass); + } + } else { + if (libraryAnalyzer.hasModLauncher()) { + // If ModLauncher installed, we use ModLauncher in place of LaunchWrapper. + mainClass = LibraryAnalyzer.MOD_LAUNCHER_MAIN; + for (String optiFineTweaker : LibraryAnalyzer.OPTIFINE_TWEAKERS) { + builder.removeTweakClass(optiFineTweaker); + } + } else if (builder.hasTweakClass(LibraryAnalyzer.OPTIFINE_TWEAKERS[0])) { + // If forge or LiteLoader installed, OptiFine Forge Tweaker is needed. + builder.replaceTweakClass(LibraryAnalyzer.OPTIFINE_TWEAKERS[0], LibraryAnalyzer.OPTIFINE_TWEAKERS[1], !reorderTweakClass, reorderTweakClass); + } + + } + } else { + for (String optiFineTweaker : LibraryAnalyzer.OPTIFINE_TWEAKERS) { + builder.removeTweakClass(optiFineTweaker); + } + } + + boolean hasForge = libraryAnalyzer.has(FORGE), hasModLauncher = libraryAnalyzer.hasModLauncher(); + for (String forgeTweaker : LibraryAnalyzer.FORGE_TWEAKERS) { + if (!hasForge) { + builder.removeTweakClass(forgeTweaker); + } else if (!hasModLauncher && builder.hasTweakClass(forgeTweaker)) { + builder.replaceTweakClass(forgeTweaker, forgeTweaker, !reorderTweakClass, reorderTweakClass); + } + } + + Version ret = builder.build(); + return mainClass == null ? ret : ret.setMainClass(mainClass); + } + + private static Version maintainGameWithCpwModLauncher(GameRepository repository, Version version) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version); + VersionLibraryBuilder builder = new VersionLibraryBuilder(version); + + if (!libraryAnalyzer.has(FORGE)) return version; + + if (libraryAnalyzer.has(OPTIFINE)) { + Library hmclTransformerDiscoveryService = new Library(new Artifact("org.jackhuang.hmcl", "transformer-discovery-service", "1.0")); + Optional optiFine = version.getLibraries().stream().filter(library -> library.is("optifine", "OptiFine")).findAny(); + boolean libraryExisting = version.getLibraries().stream().anyMatch(library -> library.is("org.jackhuang.hmcl", "transformer-discovery-service")); + optiFine.ifPresent(library -> { + builder.addJvmArgument("-Dhmcl.transformer.candidates=${library_directory}/" + library.getPath()); + if (!libraryExisting) builder.addLibrary(hmclTransformerDiscoveryService); + Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath(); + try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) { + Files.createDirectories(libraryPath.getParent()); + Files.copy(input, libraryPath, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Unable to unpack HMCLTransformerDiscoveryService", e); + } + }); + } + + return builder.build(); + } + + private static String updateIgnoreList(GameRepository repository, Version version, String ignoreList) { + String[] ignores = ignoreList.split(","); + List newIgnoreList = new ArrayList<>(); + + // To resolve the the problem that name of primary jar may conflict with the module naming convention, + // we need to manually ignore ${primary_jar}. + newIgnoreList.add("${primary_jar}"); + + Path libraryDirectory = repository.getLibrariesDirectory(version).toPath().toAbsolutePath(); + + // The default ignoreList is too loose and may cause some problems, we replace them with the absolute version. + // For example, if "client-extra" is in ignoreList, and game directory contains "client-extra" component, all + // libraries will be ignored, which is not expected. + for (String classpathName : repository.getClasspath(version)) { + Path classpathFile = Paths.get(classpathName).toAbsolutePath(); + String fileName = classpathFile.getFileName().toString(); + if (Stream.of(ignores).anyMatch(fileName::contains)) { + // This library should be ignored for Jigsaw module finding by Forge. + String absolutePath; + if (classpathFile.startsWith(libraryDirectory)) { + // Note: It's assumed using "/" instead of File.separator in classpath + absolutePath = "${library_directory}${file_separator}" + libraryDirectory.relativize(classpathFile).toString().replace(File.separator, "${file_separator}"); + } else { + absolutePath = classpathFile.toString(); + } + newIgnoreList.add(StringUtils.substringBefore(absolutePath, ",")); + } + } + return String.join(",", newIgnoreList); + } + + // Fix wrong configurations when launching 1.17+ with Forge. + private static Version maintainGameWithCpwBoostrapLauncher(GameRepository repository, Version version) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version); + VersionLibraryBuilder builder = new VersionLibraryBuilder(version); + + if (!libraryAnalyzer.has(FORGE)) return version; + + Optional bslVersion = libraryAnalyzer.getVersion(BOOTSTRAP_LAUNCHER); + + if (bslVersion.isPresent()) { + if (VersionNumber.VERSION_COMPARATOR.compare(bslVersion.get(), "0.1.17") < 0) { + // The default ignoreList will be applied to all components of libraries in classpath, + // so if game directory located in some directory like /Users/asm, all libraries will be ignored, + // which is not expected. We fix this here. + List jvm = builder.getMutableJvmArguments(); + for (int i = 0; i < jvm.size(); i++) { + Argument jvmArg = jvm.get(i); + if (jvmArg instanceof StringArgument) { + String jvmArgStr = jvmArg.toString(); + if (jvmArgStr.startsWith("-DignoreList=")) { + jvm.set(i, new StringArgument("-DignoreList=" + updateIgnoreList(repository, version, jvmArgStr.substring("-DignoreList=".length())))); + } + } + } + } else { + // bootstraplauncher 0.1.17 will only apply ignoreList to file name of libraries in classpath. + // So we only fixes name of primary jar. + List jvm = builder.getMutableJvmArguments(); + for (int i = 0; i < jvm.size(); i++) { + Argument jvmArg = jvm.get(i); + if (jvmArg instanceof StringArgument) { + String jvmArgStr = jvmArg.toString(); + if (jvmArgStr.startsWith("-DignoreList=")) { + jvm.set(i, new StringArgument(jvmArgStr + ",${primary_jar_name}")); + } + } + } + } + } + + return builder.build(); + } + + private static Version maintainOptiFineLibrary(GameRepository repository, Version version, boolean remove) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version); + List libraries = new ArrayList<>(version.getLibraries()); + + if (libraryAnalyzer.has(OPTIFINE)) { + if (libraryAnalyzer.has(LITELOADER) || libraryAnalyzer.has(FORGE)) { + // If forge or LiteLoader installed, OptiFine Forge Tweaker is needed. + // And we should load the installer jar instead of patch jar. + if (repository != null) { + for (int i = 0; i < version.getLibraries().size(); ++i) { + Library library = libraries.get(i); + if (library.is("optifine", "OptiFine")) { + Library newLibrary = new Library(new Artifact("optifine", "OptiFine", library.getVersion(), "installer")); + if (repository.getLibraryFile(version, newLibrary).exists()) { + libraries.set(i, null); + // OptiFine should be loaded after Forge in classpath. + // Although we have altered priority of OptiFine higher than Forge, + // there still exists a situation that Forge is installed without patch. + // Here we manually alter the position of OptiFine library in classpath. + if (!remove) libraries.add(newLibrary); + } + } + + if (library.is("optifine", "launchwrapper-of")) { + // With MinecraftForge installed, the custom launchwrapper installed by OptiFine will conflicts + // with the one installed by MinecraftForge or LiteLoader or ModLoader. + // Simply removing it works. + libraries.set(i, null); + } + } + } + } + } + + return version.setLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); + } + + public static boolean isPurePatched(Version version) { + if (!version.isResolvedPreservingPatches()) + throw new IllegalArgumentException("isPurePatched requires a version resolved preserving patches"); + + return version.hasPatch("game"); + } + + public static Version unique(Version version) { + List libraries = new ArrayList<>(); + + SimpleMultimap multimap = new SimpleMultimap(HashMap::new, ArrayList::new); + + for (Library library : version.getLibraries()) { + String id = library.getGroupId() + ":" + library.getArtifactId(); + VersionNumber number = VersionNumber.asVersion(library.getVersion()); + String serialized = JsonUtils.GSON.toJson(library); + + if (multimap.containsKey(id)) { + boolean duplicate = false; + for (int otherLibraryIndex : multimap.get(id)) { + Library otherLibrary = libraries.get(otherLibraryIndex); + VersionNumber otherNumber = VersionNumber.asVersion(otherLibrary.getVersion()); + if (CompatibilityRule.equals(library.getRules(), otherLibrary.getRules())) { // rules equal, ignore older version. + boolean flag = true; + if (number.compareTo(otherNumber) > 0) { // if this library is newer + // replace [otherLibrary] with [library] + libraries.set(otherLibraryIndex, library); + } else if (number.compareTo(otherNumber) == 0) { // same library id. + // prevent from duplicated libraries + if (library.equals(otherLibrary)) { + String otherSerialized = JsonUtils.GSON.toJson(otherLibrary); + // A trick, the library that has more information is better, which can be + // considered whose serialized JSON text will be longer. + if (serialized.length() > otherSerialized.length()) { + libraries.set(otherLibraryIndex, library); + } + } else { + // for text2speech, which have same library id as well as version number, + // but its library and native library does not equal + flag = false; + } + } + if (flag) { + duplicate = true; + break; + } + } + } + + if (!duplicate) { + multimap.put(id, libraries.size()); + libraries.add(library); + } + } else { + multimap.put(id, libraries.size()); + libraries.add(library); + } + } + + return version.setLibraries(libraries); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/MojangDownloadProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/MojangDownloadProvider.java new file mode 100644 index 00000000..3a787078 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/MojangDownloadProvider.java @@ -0,0 +1,81 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.download.fabric.FabricAPIVersionList; +import com.tungsten.fclcore.download.fabric.FabricVersionList; +import com.tungsten.fclcore.download.forge.ForgeBMCLVersionList; +import com.tungsten.fclcore.download.game.GameVersionList; +import com.tungsten.fclcore.download.liteloader.LiteLoaderVersionList; +import com.tungsten.fclcore.download.optifine.OptiFineBMCLVersionList; +import com.tungsten.fclcore.download.quilt.QuiltAPIVersionList; +import com.tungsten.fclcore.download.quilt.QuiltVersionList; + +/** + * @see http://wiki.vg + */ +public class MojangDownloadProvider implements DownloadProvider { + private final GameVersionList game; + private final FabricVersionList fabric; + private final FabricAPIVersionList fabricApi; + private final ForgeBMCLVersionList forge; + private final LiteLoaderVersionList liteLoader; + private final OptiFineBMCLVersionList optifine; + private final QuiltVersionList quilt; + private final QuiltAPIVersionList quiltApi; + + public MojangDownloadProvider() { + String apiRoot = "https://bmclapi2.bangbang93.com"; + + this.game = new GameVersionList(this); + this.fabric = new FabricVersionList(this); + this.fabricApi = new FabricAPIVersionList(this); + this.forge = new ForgeBMCLVersionList(apiRoot); + this.liteLoader = new LiteLoaderVersionList(this); + this.optifine = new OptiFineBMCLVersionList(apiRoot); + this.quilt = new QuiltVersionList(this); + this.quiltApi = new QuiltAPIVersionList(this); + } + + @Override + public String getVersionListURL() { + return "https://piston-meta.mojang.com/mc/game/version_manifest.json"; + } + + @Override + public String getAssetBaseURL() { + return "https://resources.download.minecraft.net/"; + } + + @Override + public VersionList getVersionListById(String id) { + switch (id) { + case "game": + return game; + case "fabric": + return fabric; + case "fabric-api": + return fabricApi; + case "forge": + return forge; + case "liteloader": + return liteLoader; + case "optifine": + return optifine; + case "quilt": + return quilt; + case "quilt-api": + return quiltApi; + default: + throw new IllegalArgumentException("Unrecognized version list id: " + id); + } + } + + @Override + public String injectURL(String baseURL) { + return baseURL; + } + + @Override + public int getConcurrency() { + return 6; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/MultipleSourceVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/MultipleSourceVersionList.java new file mode 100644 index 00000000..1c323ec6 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/MultipleSourceVersionList.java @@ -0,0 +1,49 @@ +package com.tungsten.fclcore.download; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class MultipleSourceVersionList extends VersionList { + + private final List> backends; + + MultipleSourceVersionList(List> backends) { + this.backends = backends; + + assert (backends.size() >= 1); + } + + @Override + public boolean hasType() { + boolean hasType = backends.get(0).hasType(); + assert (backends.stream().allMatch(versionList -> versionList.hasType() == hasType)); + return hasType; + } + + @Override + public CompletableFuture loadAsync() { + throw new UnsupportedOperationException("MultipleSourceVersionList does not support loading the entire remote version list."); + } + + @Override + public CompletableFuture refreshAsync() { + throw new UnsupportedOperationException("MultipleSourceVersionList does not support loading the entire remote version list."); + } + + @Override + public CompletableFuture refreshAsync(String gameVersion) { + versions.clear(gameVersion); + return CompletableFuture.anyOf(backends.stream() + .map(versionList -> versionList.refreshAsync(gameVersion) + .thenRunAsync(() -> { + lock.writeLock().lock(); + + try { + versions.putAll(gameVersion, versionList.getVersions(gameVersion)); + } finally { + lock.writeLock().unlock(); + } + })) + .toArray(CompletableFuture[]::new)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/RemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/RemoteVersion.java new file mode 100644 index 00000000..4b501063 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/RemoteVersion.java @@ -0,0 +1,113 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.ToStringBuilder; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * The remote version. + */ +public class RemoteVersion implements Comparable { + + private final String libraryId; + private final String gameVersion; + private final String selfVersion; + private final Date releaseDate; + private final List urls; + private final Type type; + + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param urls the installer or universal jar original URL. + */ + public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Date releaseDate, List urls) { + this(libraryId, gameVersion, selfVersion, releaseDate, Type.UNCATEGORIZED, urls); + } + + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param urls the installer or universal jar URL. + */ + public RemoteVersion(String libraryId, String gameVersion, String selfVersion, Date releaseDate, Type type, List urls) { + this.libraryId = Objects.requireNonNull(libraryId); + this.gameVersion = Objects.requireNonNull(gameVersion); + this.selfVersion = Objects.requireNonNull(selfVersion); + this.releaseDate = releaseDate; + this.urls = Objects.requireNonNull(urls); + this.type = Objects.requireNonNull(type); + } + + public String getLibraryId() { + return libraryId; + } + + public String getGameVersion() { + return gameVersion; + } + + public String getSelfVersion() { + return selfVersion; + } + + public String getFullVersion() { + return getSelfVersion(); + } + + public Date getReleaseDate() { + return releaseDate; + } + + public List getUrls() { + return urls; + } + + public Type getVersionType() { + return type; + } + + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + throw new UnsupportedOperationException(this + " cannot be installed yet"); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof RemoteVersion && Objects.equals(selfVersion, ((RemoteVersion) obj).selfVersion); + } + + @Override + public int hashCode() { + return selfVersion.hashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("selfVersion", selfVersion) + .append("gameVersion", gameVersion) + .toString(); + } + + @Override + public int compareTo(RemoteVersion o) { + // newer versions are smaller than older versions + return VersionNumber.asVersion(o.selfVersion).compareTo(VersionNumber.asVersion(selfVersion)); + } + + public enum Type { + UNCATEGORIZED, + RELEASE, + SNAPSHOT, + OLD + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/UnsupportedInstallationException.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/UnsupportedInstallationException.java new file mode 100644 index 00000000..a4649639 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/UnsupportedInstallationException.java @@ -0,0 +1,22 @@ +package com.tungsten.fclcore.download; + +public class UnsupportedInstallationException extends Exception { + + private final int reason; + + public UnsupportedInstallationException(int reason) { + this.reason = reason; + } + + public int getReason() { + return reason; + } + + // e.g. Forge is not compatible with fabric. + public static final int UNSUPPORTED_LAUNCH_WRAPPER = 1; + + // 1.17: OptiFine>=H1 Pre2 is compatible with Forge. + public static final int FORGE_1_17_OPTIFINE_H1_PRE2 = 2; + + public static final int FABRIC_NOT_COMPATIBLE_WITH_FORGE = 3; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/VersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/VersionList.java new file mode 100644 index 00000000..62155f0e --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/VersionList.java @@ -0,0 +1,123 @@ +package com.tungsten.fclcore.download; + +import com.tungsten.fclcore.util.SimpleMultimap; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * The remote version list. + * + * @param The subclass of {@code RemoteVersion}, the type of RemoteVersion. + */ +public abstract class VersionList { + + /** + * the remote version list. + * key: game version. + * values: corresponding remote versions. + */ + protected final SimpleMultimap versions = new SimpleMultimap(HashMap::new, TreeSet::new); + + /** + * True if the version list has been loaded. + */ + public boolean isLoaded() { + return !versions.isEmpty(); + } + + /** + * True if the version list that contains the remote versions which depends on the specific game version has been loaded. + * @param gameVersion the remote version depends on + */ + public boolean isLoaded(String gameVersion) { + return !versions.get(gameVersion).isEmpty(); + } + + public abstract boolean hasType(); + + protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + /** + * @return the task to reload the remote version list. + */ + public abstract CompletableFuture refreshAsync(); + + /** + * @param gameVersion the remote version depends on + * @return the task to reload the remote version list. + */ + public CompletableFuture refreshAsync(String gameVersion) { + return refreshAsync(); + } + + public CompletableFuture loadAsync() { + return CompletableFuture.completedFuture(null) + .thenComposeAsync(unused -> { + lock.readLock().lock(); + boolean loaded; + + try { + loaded = isLoaded(); + } finally { + lock.readLock().unlock(); + } + return loaded ? CompletableFuture.completedFuture(null) : refreshAsync(); + }); + } + + public CompletableFuture loadAsync(String gameVersion) { + return CompletableFuture.completedFuture(null) + .thenComposeAsync(unused -> { + lock.readLock().lock(); + boolean loaded; + + try { + loaded = isLoaded(gameVersion); + } finally { + lock.readLock().unlock(); + } + return loaded ? CompletableFuture.completedFuture(null) : refreshAsync(gameVersion); + }); + } + + protected Collection getVersionsImpl(String gameVersion) { + return versions.get(gameVersion); + } + + /** + * Get the remote versions that specifics Minecraft version. + * + * @param gameVersion the Minecraft version that remote versions belong to + * @return the collection of specific remote versions + */ + public final Collection getVersions(String gameVersion) { + lock.readLock().lock(); + try { + return Collections.unmodifiableCollection(new ArrayList<>(getVersionsImpl(gameVersion))); + } finally { + lock.readLock().unlock(); + } + } + + /** + * Get the specific remote version. + * + * @param gameVersion the Minecraft version that remote versions belong to + * @param remoteVersion the version of the remote version. + * @return the specific remote version, null if it is not found. + */ + public Optional getVersion(String gameVersion, String remoteVersion) { + lock.readLock().lock(); + try { + T result = null; + for (T it : versions.get(gameVersion)) + if (remoteVersion.equals(it.getSelfVersion())) + result = it; + return Optional.ofNullable(result); + } finally { + lock.readLock().unlock(); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/VersionMismatchException.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/VersionMismatchException.java new file mode 100644 index 00000000..6ac9de2f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/VersionMismatchException.java @@ -0,0 +1,20 @@ +package com.tungsten.fclcore.download; + +public class VersionMismatchException extends Exception { + + private final String expect, actual; + + public VersionMismatchException(String expect, String actual) { + super("Mismatched game version requirement, library requires game to be " + expect + ", but actual is " + actual); + this.expect = expect; + this.actual = actual; + } + + public String getExpect() { + return expect; + } + + public String getActual() { + return actual; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIInstallTask.java new file mode 100644 index 00000000..8e69c367 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIInstallTask.java @@ -0,0 +1,48 @@ +package com.tungsten.fclcore.download.fabric; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Note: Fabric should be installed first. + */ +public final class FabricAPIInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final FabricAPIRemoteVersion remote; + private final List> dependencies = new ArrayList<>(1); + + public FabricAPIInstallTask(DefaultDependencyManager dependencyManager, Version version, FabricAPIRemoteVersion remoteVersion) { + this.dependencyManager = dependencyManager; + this.version = version; + this.remote = remoteVersion; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() throws IOException { + dependencies.add(new FileDownloadTask( + new URL(remote.getVersion().getFile().getUrl()), + dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("fabric-api-" + remote.getVersion().getVersion() + ".jar").toFile(), + remote.getVersion().getFile().getIntegrityCheck()) + ); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIRemoteVersion.java new file mode 100644 index 00000000..18a3758d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIRemoteVersion.java @@ -0,0 +1,50 @@ +package com.tungsten.fclcore.download.fabric; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.task.Task; + +import java.util.Date; +import java.util.List; + +public class FabricAPIRemoteVersion extends RemoteVersion { + private final String fullVersion; + private final RemoteMod.Version version; + + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param urls the installer or universal jar original URL. + */ + FabricAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Date datePublished, RemoteMod.Version version, List urls) { + super(LibraryAnalyzer.LibraryType.FABRIC_API.getPatchId(), gameVersion, selfVersion, datePublished, urls); + + this.fullVersion = fullVersion; + this.version = version; + } + + @Override + public String getFullVersion() { + return fullVersion; + } + + public RemoteMod.Version getVersion() { + return version; + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new FabricAPIInstallTask(dependencyManager, baseVersion, this); + } + + @Override + public int compareTo(RemoteVersion o) { + if (!(o instanceof FabricAPIRemoteVersion)) return 0; + return -this.getReleaseDate().compareTo(o.getReleaseDate()); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIVersionList.java new file mode 100644 index 00000000..d9c2707b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricAPIVersionList.java @@ -0,0 +1,36 @@ +package com.tungsten.fclcore.download.fabric; + +import com.tungsten.fclcore.download.DownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.mod.modrinth.ModrinthRemoteModRepository; +import com.tungsten.fclcore.util.Lang; + +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +public class FabricAPIVersionList extends VersionList { + + private final DownloadProvider downloadProvider; + + public FabricAPIVersionList(DownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return false; + } + + @Override + public CompletableFuture refreshAsync() { + return CompletableFuture.runAsync(wrap(() -> { + for (RemoteMod.Version modVersion : Lang.toIterable(ModrinthRemoteModRepository.MODS.getRemoteVersionsById("P7dR8mSH"))) { + for (String gameVersion : modVersion.getGameVersions()) { + versions.put(gameVersion, new FabricAPIRemoteVersion(gameVersion, modVersion.getVersion(), modVersion.getName(), modVersion.getDatePublished(), modVersion, + Collections.singletonList(modVersion.getFile().getUrl()))); + } + } + })); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricInstallTask.java new file mode 100644 index 00000000..d7c1314a --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricInstallTask.java @@ -0,0 +1,189 @@ +package com.tungsten.fclcore.download.fabric; + +import static com.tungsten.fclcore.download.UnsupportedInstallationException.FABRIC_NOT_COMPATIBLE_WITH_FORGE; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.UnsupportedInstallationException; +import com.tungsten.fclcore.game.Arguments; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.GetTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.util.*; + +/** + * Note: Fabric should be installed first. + */ +public final class FabricInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final FabricRemoteVersion remote; + private final GetTask launchMetaTask; + private final List> dependencies = new ArrayList<>(1); + + public FabricInstallTask(DefaultDependencyManager dependencyManager, Version version, FabricRemoteVersion remoteVersion) { + this.dependencyManager = dependencyManager; + this.version = version; + this.remote = remoteVersion; + + launchMetaTask = new GetTask(dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls())); + launchMetaTask.setCacheRepository(dependencyManager.getCacheRepository()); + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() throws Exception { + if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).getMainClass())) + throw new UnsupportedInstallationException(FABRIC_NOT_COMPATIBLE_WITH_FORGE); + } + + @Override + public Collection> getDependents() { + return Collections.singleton(launchMetaTask); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() { + setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInfo.class), remote.getGameVersion(), remote.getSelfVersion())); + + dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + } + + private Version getPatch(FabricInfo fabricInfo, String gameVersion, String loaderVersion) { + JsonObject launcherMeta = fabricInfo.launcherMeta; + Arguments arguments = new Arguments(); + + String mainClass; + if (!launcherMeta.get("mainClass").isJsonObject()) { + mainClass = launcherMeta.get("mainClass").getAsString(); + } else { + mainClass = launcherMeta.get("mainClass").getAsJsonObject().get("client").getAsString(); + } + + if (launcherMeta.has("launchwrapper")) { + String clientTweaker = launcherMeta.get("launchwrapper").getAsJsonObject().get("tweakers").getAsJsonObject().get("client").getAsJsonArray().get(0).getAsString(); + arguments = arguments.addGameArguments("--tweakClass", clientTweaker); + } + + JsonObject librariesObject = launcherMeta.getAsJsonObject("libraries"); + List libraries = new ArrayList<>(); + + // "common, server" is hard coded in fabric installer. + // Don't know the purpose of ignoring client libraries. + for (String side : new String[]{"common", "server"}) { + for (JsonElement element : librariesObject.getAsJsonArray(side)) { + libraries.add(JsonUtils.GSON.fromJson(element, Library.class)); + } + } + + libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.intermediary.maven), "https://maven.fabricmc.net/", null)); + libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.loader.maven), "https://maven.fabricmc.net/", null)); + + return new Version(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), loaderVersion, 30000, arguments, mainClass, libraries); + } + + public static class FabricInfo { + private final LoaderInfo loader; + private final IntermediaryInfo intermediary; + private final JsonObject launcherMeta; + + public FabricInfo(LoaderInfo loader, IntermediaryInfo intermediary, JsonObject launcherMeta) { + this.loader = loader; + this.intermediary = intermediary; + this.launcherMeta = launcherMeta; + } + + public LoaderInfo getLoader() { + return loader; + } + + public IntermediaryInfo getIntermediary() { + return intermediary; + } + + public JsonObject getLauncherMeta() { + return launcherMeta; + } + } + + public static class LoaderInfo { + private final String separator; + private final int build; + private final String maven; + private final String version; + private final boolean stable; + + public LoaderInfo(String separator, int build, String maven, String version, boolean stable) { + this.separator = separator; + this.build = build; + this.maven = maven; + this.version = version; + this.stable = stable; + } + + public String getSeparator() { + return separator; + } + + public int getBuild() { + return build; + } + + public String getMaven() { + return maven; + } + + public String getVersion() { + return version; + } + + public boolean isStable() { + return stable; + } + } + + public static class IntermediaryInfo { + private final String maven; + private final String version; + private final boolean stable; + + public IntermediaryInfo(String maven, String version, boolean stable) { + this.maven = maven; + this.version = version; + this.stable = stable; + } + + public String getMaven() { + return maven; + } + + public String getVersion() { + return version; + } + + public boolean isStable() { + return stable; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricRemoteVersion.java new file mode 100644 index 00000000..a5019b70 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricRemoteVersion.java @@ -0,0 +1,27 @@ +package com.tungsten.fclcore.download.fabric; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; + +import java.util.List; + +public class FabricRemoteVersion extends RemoteVersion { + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param urls the installer or universal jar original URL. + */ + FabricRemoteVersion(String gameVersion, String selfVersion, List urls) { + super(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), gameVersion, selfVersion, null, urls); + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new FabricInstallTask(dependencyManager, baseVersion, this); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricVersionList.java new file mode 100644 index 00000000..5abaac76 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/fabric/FabricVersionList.java @@ -0,0 +1,90 @@ +package com.tungsten.fclcore.download.fabric; + +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public final class FabricVersionList extends VersionList { + private final DownloadProvider downloadProvider; + + public FabricVersionList(DownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return false; + } + + @Override + public CompletableFuture refreshAsync() { + return CompletableFuture.runAsync(wrap(() -> { + List gameVersions = getGameVersions(GAME_META_URL); + List loaderVersions = getGameVersions(LOADER_META_URL); + + lock.writeLock().lock(); + + try { + for (String gameVersion : gameVersions) + for (String loaderVersion : loaderVersions) + versions.put(gameVersion, new FabricRemoteVersion(gameVersion, loaderVersion, + Collections.singletonList(getLaunchMetaUrl(gameVersion, loaderVersion)))); + } finally { + lock.writeLock().unlock(); + } + })); + } + + private static final String LOADER_META_URL = "https://meta.fabricmc.net/v2/versions/loader"; + private static final String GAME_META_URL = "https://meta.fabricmc.net/v2/versions/game"; + + private List getGameVersions(String metaUrl) throws IOException { + String json = NetworkUtils.doGet(NetworkUtils.toURL(downloadProvider.injectURL(metaUrl))); + return JsonUtils.GSON.>fromJson(json, new TypeToken>() { + }.getType()).stream().map(GameVersion::getVersion).collect(Collectors.toList()); + } + + private static String getLaunchMetaUrl(String gameVersion, String loaderVersion) { + return String.format("https://meta.fabricmc.net/v2/versions/loader/%s/%s", gameVersion, loaderVersion); + } + + private static class GameVersion { + private final String version; + private final String maven; + private final boolean stable; + + public GameVersion() { + this("", null, false); + } + + public GameVersion(String version, String maven, boolean stable) { + this.version = version; + this.maven = maven; + this.stable = stable; + } + + public String getVersion() { + return version; + } + + @Nullable + public String getMaven() { + return maven; + } + + public boolean isStable() { + return stable; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeBMCLVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeBMCLVersionList.java new file mode 100644 index 00000000..3632ed26 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeBMCLVersionList.java @@ -0,0 +1,201 @@ +package com.tungsten.fclcore.download.forge; + +import static com.tungsten.fclcore.util.Logging.LOG; +import static com.tungsten.fclcore.util.Pair.pair; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.Validation; +import com.tungsten.fclcore.util.io.HttpRequest; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; + +public final class ForgeBMCLVersionList extends VersionList { + private final String apiRoot; + + /** + * @param apiRoot API Root of BMCLAPI implementations + */ + public ForgeBMCLVersionList(String apiRoot) { + this.apiRoot = apiRoot; + } + + @Override + public boolean hasType() { + return false; + } + + @Override + public CompletableFuture loadAsync() { + throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list."); + } + + @Override + public CompletableFuture refreshAsync() { + throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list."); + } + + @Override + public CompletableFuture refreshAsync(String gameVersion) { + return CompletableFuture.completedFuture(null) + .thenApplyAsync(wrap(unused -> HttpRequest.GET(apiRoot + "/forge/minecraft/" + gameVersion).>getJson(new TypeToken>() { + }.getType()))) + .thenAcceptAsync(forgeVersions -> { + lock.writeLock().lock(); + + try { + versions.clear(gameVersion); + if (forgeVersions == null) return; + for (ForgeVersion version : forgeVersions) { + if (version == null) + continue; + List urls = new ArrayList<>(); + for (ForgeVersion.File file : version.getFiles()) + if ("installer".equals(file.getCategory()) && "jar".equals(file.getFormat())) { + String classifier = gameVersion + "-" + version.getVersion() + + (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : ""); + String fileName1 = "forge-" + classifier + "-" + file.getCategory() + "." + file.getFormat(); + String fileName2 = "forge-" + classifier + "-" + gameVersion + "-" + file.getCategory() + "." + file.getFormat(); + urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "/" + fileName1); + urls.add("https://files.minecraftforge.net/maven/net/minecraftforge/forge/" + classifier + "-" + gameVersion + "/" + fileName2); + urls.add(NetworkUtils.withQuery("https://bmclapi2.bangbang93.com/forge/download", mapOf( + pair("mcversion", version.getGameVersion()), + pair("version", version.getVersion()), + pair("branch", version.getBranch()), + pair("category", file.getCategory()), + pair("format", file.getFormat()) + ))); + } + + if (urls.isEmpty()) + continue; + + Instant releaseDate = null; + if (version.getModified() != null) { + try { + releaseDate = Instant.parse(version.getModified()); + } catch (DateTimeParseException e) { + LOG.log(Level.WARNING, "Failed to parse instant " + version.getModified(), e); + } + } + + versions.put(gameVersion, new ForgeRemoteVersion( + version.getGameVersion(), version.getVersion(), releaseDate == null ? null : Date.from(releaseDate), urls)); + } + } finally { + lock.writeLock().unlock(); + } + }); + } + + @Override + public Optional getVersion(String gameVersion, String remoteVersion) { + remoteVersion = StringUtils.substringAfter(remoteVersion, "-", remoteVersion); + return super.getVersion(gameVersion, remoteVersion); + } + + public static final class ForgeVersion implements Validation { + + private final String branch; + private final int build; + private final String mcversion; + private final String modified; + private final String version; + private final List files; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public ForgeVersion() { + this(null, 0, "", null, "", Collections.emptyList()); + } + + public ForgeVersion(String branch, int build, String mcversion, String modified, String version, List files) { + this.branch = branch; + this.build = build; + this.mcversion = mcversion; + this.modified = modified; + this.version = version; + this.files = files; + } + + @Nullable + public String getBranch() { + return branch; + } + + public int getBuild() { + return build; + } + + @NotNull + public String getGameVersion() { + return mcversion; + } + + @Nullable + public String getModified() { + return modified; + } + + @NotNull + public String getVersion() { + return version; + } + + @NotNull + public List getFiles() { + return files; + } + + @Override + public void validate() throws JsonParseException { + if (files == null) + throw new JsonParseException("ForgeVersion files cannot be null"); + if (version == null) + throw new JsonParseException("ForgeVersion version cannot be null"); + if (mcversion == null) + throw new JsonParseException("ForgeVersion mcversion cannot be null"); + } + + @Immutable + public static final class File { + private final String format; + private final String category; + private final String hash; + + public File() { + this("", "", ""); + } + + public File(String format, String category, String hash) { + this.format = format; + this.category = category; + this.hash = hash; + } + + public String getFormat() { + return format; + } + + public String getCategory() { + return category; + } + + public String getHash() { + return hash; + } + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstall.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstall.java new file mode 100644 index 00000000..895b91bc --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstall.java @@ -0,0 +1,69 @@ +package com.tungsten.fclcore.download.forge; + +import com.tungsten.fclcore.game.Artifact; + +public final class ForgeInstall { + + private final String profileName; + private final String target; + private final Artifact path; + private final String version; + private final String filePath; + private final String welcome; + private final String minecraft; + private final String mirrorList; + private final String logo; + + public ForgeInstall() { + this(null, null, null, null, null, null, null, null, null); + } + + public ForgeInstall(String profileName, String target, Artifact path, String version, String filePath, String welcome, String minecraft, String mirrorList, String logo) { + this.profileName = profileName; + this.target = target; + this.path = path; + this.version = version; + this.filePath = filePath; + this.welcome = welcome; + this.minecraft = minecraft; + this.mirrorList = mirrorList; + this.logo = logo; + } + + public String getProfileName() { + return profileName; + } + + public String getTarget() { + return target; + } + + public Artifact getPath() { + return path; + } + + public String getVersion() { + return version; + } + + public String getFilePath() { + return filePath; + } + + public String getWelcome() { + return welcome; + } + + public String getMinecraft() { + return minecraft; + } + + public String getMirrorList() { + return mirrorList; + } + + public String getLogo() { + return logo; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstallProfile.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstallProfile.java new file mode 100644 index 00000000..3ce87296 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstallProfile.java @@ -0,0 +1,37 @@ +package com.tungsten.fclcore.download.forge; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.util.gson.Validation; + +public final class ForgeInstallProfile implements Validation { + + @SerializedName("install") + private final ForgeInstall install; + + @SerializedName("versionInfo") + private final Version versionInfo; + + public ForgeInstallProfile(ForgeInstall install, Version versionInfo) { + this.install = install; + this.versionInfo = versionInfo; + } + + public ForgeInstall getInstall() { + return install; + } + + public Version getVersionInfo() { + return versionInfo; + } + + @Override + public void validate() throws JsonParseException { + if (install == null) + throw new JsonParseException("InstallProfile install cannot be null"); + + if (versionInfo == null) + throw new JsonParseException("InstallProfile versionInfo cannot be null"); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstallTask.java new file mode 100644 index 00000000..fd766f1b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeInstallTask.java @@ -0,0 +1,173 @@ +package com.tungsten.fclcore.download.forge; + +import static com.tungsten.fclcore.download.UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER; +import static com.tungsten.fclcore.util.StringUtils.removePrefix; +import static com.tungsten.fclcore.util.StringUtils.removeSuffix; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.DependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.UnsupportedInstallationException; +import com.tungsten.fclcore.download.VersionMismatchException; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +public final class ForgeInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final Version version; + private Path installer; + private final ForgeRemoteVersion remote; + private FileDownloadTask dependent; + private Task dependency; + + public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) { + this.dependencyManager = dependencyManager; + this.version = version; + this.remote = remoteVersion; + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() throws Exception { + installer = Files.createTempFile("forge-installer", ".jar"); + + dependent = new FileDownloadTask( + dependencyManager.getDownloadProvider().injectURLsWithCandidates(remote.getUrls()), + installer.toFile(), null); + dependent.setCacheRepository(dependencyManager.getCacheRepository()); + dependent.setCaching(true); + dependent.addIntegrityCheckHandler(FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER); + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + Files.deleteIfExists(installer); + setResult(dependency.getResult()); + } + + @Override + public Collection> getDependents() { + return Collections.singleton(dependent); + } + + @Override + public Collection> getDependencies() { + return Collections.singleton(dependency); + } + + @Override + public void execute() throws IOException, VersionMismatchException, UnsupportedInstallationException { + String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); + if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0) { + // Forge 1.13 is not compatible with fabric. + if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) + && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) + && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) + && !LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass)) + throw new UnsupportedInstallationException(UNSUPPORTED_LAUNCH_WRAPPER); + } else { + // Forge 1.12 and older versions is compatible with vanilla and launchwrapper. + // if (!"net.minecraft.client.main.Main".equals(originalMainClass) && !"net.minecraft.launchwrapper.Launch".equals(originalMainClass)) + // throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException(); + } + + + if (detectForgeInstallerType(dependencyManager, version, installer)) + dependency = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer); + else + dependency = new ForgeOldInstallTask(dependencyManager, version, remote.getSelfVersion(), installer); + } + + /** + * Detect Forge installer type. + * + * @param dependencyManager game repository + * @param version version.json + * @param installer the Forge installer, either the new or old one. + * @return true for new, false for old + * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. + * @throws VersionMismatchException if required game version of installer does not match the actual one. + */ + public static boolean detectForgeInstallerType(DependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); + if (!gameVersion.isPresent()) throw new IOException(); + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { + String installProfileText = FileUtils.readText(fs.getPath("install_profile.json")); + Map installProfile = JsonUtils.fromNonNullJson(installProfileText, Map.class); + if (installProfile.containsKey("spec")) { + ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class); + if (!gameVersion.get().equals(profile.getMinecraft())) + throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get()); + return true; + } else if (installProfile.containsKey("install") && installProfile.containsKey("versionInfo")) { + ForgeInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeInstallProfile.class); + if (!gameVersion.get().equals(profile.getInstall().getMinecraft())) + throw new VersionMismatchException(profile.getInstall().getMinecraft(), gameVersion.get()); + return false; + } else { + throw new IOException(); + } + } + } + + /** + * Install Forge library from existing local file. + * This method will try to identify this installer whether it is in old or new format. + * + * @param dependencyManager game repository + * @param version version.json + * @param installer the Forge installer, either the new or old one. + * @return the task to install library + * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. + * @throws VersionMismatchException if required game version of installer does not match the actual one. + */ + public static Task install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); + if (!gameVersion.isPresent()) throw new IOException(); + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { + String installProfileText = FileUtils.readText(fs.getPath("install_profile.json")); + Map installProfile = JsonUtils.fromNonNullJson(installProfileText, Map.class); + if (installProfile.containsKey("spec")) { + ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class); + if (!gameVersion.get().equals(profile.getMinecraft())) + throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get()); + return new ForgeNewInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getVersion()), installer); + } else if (installProfile.containsKey("install") && installProfile.containsKey("versionInfo")) { + ForgeInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeInstallProfile.class); + if (!gameVersion.get().equals(profile.getInstall().getMinecraft())) + throw new VersionMismatchException(profile.getInstall().getMinecraft(), gameVersion.get()); + return new ForgeOldInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getInstall().getPath().getVersion().replaceAll("(?i)forge", "")), installer); + } else { + throw new IOException(); + } + } + } + + private static String modifyVersion(String gameVersion, String version) { + return removeSuffix(removePrefix(removeSuffix(removePrefix(version.replace(gameVersion, "").trim(), "-"), "-"), "_"), "_"); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeNewInstallProfile.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeNewInstallProfile.java new file mode 100644 index 00000000..77fa4fa5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeNewInstallProfile.java @@ -0,0 +1,197 @@ +package com.tungsten.fclcore.download.forge; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; + +import java.util.*; +import java.util.stream.Collectors; + +public class ForgeNewInstallProfile implements Validation { + + private final int spec; + private final String minecraft; + private final String json; + private final String version; + private final Artifact path; + private final List libraries; + private final List processors; + private final Map data; + + public ForgeNewInstallProfile(int spec, String minecraft, String json, String version, Artifact path, List libraries, List processors, Map data) { + this.spec = spec; + this.minecraft = minecraft; + this.json = json; + this.version = version; + this.path = path; + this.libraries = libraries; + this.processors = processors; + this.data = data; + } + + /** + * Specification for install_profile.json. + */ + public int getSpec() { + return spec; + } + + /** + * Vanilla game version that this installer supports. + */ + public String getMinecraft() { + return minecraft; + } + + /** + * Version json to be installed. + * @return path of the version json relative to the installer JAR file. + */ + public String getJson() { + return json; + } + + /** + * + * @return forge version. + */ + public String getVersion() { + return version; + } + + /** + * Maven artifact path for the main jar to install. + * @return artifact path of the main jar. + */ + public Optional getPath() { + return Optional.ofNullable(path); + } + + /** + * Libraries that processors depend on. + * @return the required dependencies. + */ + public List getLibraries() { + return libraries == null ? Collections.emptyList() : libraries; + } + + /** + * Tasks to be executed to setup modded environment. + */ + public List getProcessors() { + if (processors == null) return Collections.emptyList(); + return processors.stream().filter(p -> p.isSide("client")).collect(Collectors.toList()); + } + + /** + * Data for processors. + * + * @return a mutable data map for processors. + */ + public Map getData() { + if (data == null) + return new HashMap<>(); + + return data.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getClient())); + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (minecraft == null || json == null || version == null) + throw new JsonParseException("ForgeNewInstallProfile is malformed"); + } + + public static class Processor implements Validation { + private final List sides; + private final Artifact jar; + private final List classpath; + private final List args; + private final Map outputs; + + public Processor(List sides, Artifact jar, List classpath, List args, Map outputs) { + this.sides = sides; + this.jar = jar; + this.classpath = classpath; + this.args = args; + this.outputs = outputs; + } + + /** + * Check which side this processor should be run on. We only support client install currently. + * @param side can be one of "client", "server", "extract". + * @return true if the processor can run on the side. + */ + public boolean isSide(String side) { + return sides == null || sides.contains(side); + } + + /** + * The executable jar of this processor task. Will be executed in installation process. + * @return the artifact path of executable jar. + */ + public Artifact getJar() { + return jar; + } + + /** + * The dependencies of this processor task. + * @return the artifact path of dependencies. + */ + public List getClasspath() { + return classpath == null ? Collections.emptyList() : classpath; + } + + /** + * Arguments to pass to the processor jar. + * Each item can be in one of the following formats: + * [artifact]: An artifact path, used for locating files. + * {entry}: Get corresponding value of the entry in {@link ForgeNewInstallProfile#getData()} + * {MINECRAFT_JAR}: path of the Minecraft jar. + * {SIDE}: values other than "client" will be ignored. + * @return arguments to pass to the processor jar. + * @see ForgeNewInstallTask#parseLiteral(String, Map, ExceptionalFunction) + */ + public List getArgs() { + return args == null ? Collections.emptyList() : args; + } + + /** + * File-checksum pairs, used for verifying the output file is correct. + * Arguments to pass to the processor jar. + * Keys can be in one of [artifact] or {entry}. Should be file path. + * Values can be in one of {entry} or 'literal'. Should be SHA-1 checksum. + * @return files output from this processor. + * @see ForgeNewInstallTask#parseLiteral(String, Map, ExceptionalFunction) + */ + public Map getOutputs() { + return outputs == null ? Collections.emptyMap() : outputs; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (jar == null) + throw new JsonParseException("Processor::jar cannot be null"); + } + } + + public static class Datum { + private final String client; + + public Datum(String client) { + this.client = client; + } + + /** + * Can be in the following formats: + * [value]: An artifact path. + * 'value': A string literal. + * value: A file in the installer package, to be extracted to a temp folder, and then have the absolute path in replacements. + * @return Value to use for the client install + */ + public String getClient() { + return client; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeNewInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeNewInstallTask.java new file mode 100644 index 00000000..b28ae4e5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeNewInstallTask.java @@ -0,0 +1,407 @@ +package com.tungsten.fclcore.download.forge; + +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; +import static com.tungsten.fclcore.util.Logging.LOG; +import static com.tungsten.fclcore.util.gson.JsonUtils.fromNonNullJson; + +import com.tungsten.fclcore.download.ArtifactMalformedException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.game.GameLibrariesTask; +import com.tungsten.fclcore.download.game.VersionJsonDownloadTask; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.game.DownloadInfo; +import com.tungsten.fclcore.game.DownloadType; +import com.tungsten.fclcore.game.JavaVersion; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.function.ExceptionalFunction; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.ChecksumMismatchException; +import com.tungsten.fclcore.util.io.FileUtils; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.zip.ZipException; + +public class ForgeNewInstallTask extends Task { + + private class ProcessorTask extends Task { + + private Processor processor; + private Map vars; + + public ProcessorTask(@NotNull Processor processor, @NotNull Map vars) { + this.processor = processor; + this.vars = vars; + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public void execute() throws Exception { + Map outputs = new HashMap<>(); + boolean miss = false; + + for (Map.Entry entry : processor.getOutputs().entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + key = parseLiteral(key, vars); + value = parseLiteral(value, vars); + + if (key == null || value == null) { + throw new ArtifactMalformedException("Invalid forge installation configuration"); + } + + outputs.put(key, value); + + Path artifact = Paths.get(key); + if (Files.exists(artifact)) { + String code; + try (InputStream stream = Files.newInputStream(artifact)) { + code = encodeHex(digest("SHA-1", stream)); + } + + if (!Objects.equals(code, value)) { + Files.delete(artifact); + LOG.info("Found existing file is not valid: " + artifact); + + miss = true; + } + } else { + miss = true; + } + } + + if (!processor.getOutputs().isEmpty() && !miss) { + return; + } + + Path jar = gameRepository.getArtifactFile(version, processor.getJar()); + if (!Files.isRegularFile(jar)) + throw new FileNotFoundException("Game processor file not found, should be downloaded in preprocess"); + + String mainClass; + try (JarFile jarFile = new JarFile(jar.toFile())) { + mainClass = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS); + } + + if (StringUtils.isBlank(mainClass)) + throw new Exception("Game processor jar does not have main class " + jar); + + List command = new ArrayList<>(); + command.add(JavaVersion.fromCurrentEnvironment().getBinary().toString()); + command.add("-cp"); + + List classpath = new ArrayList<>(processor.getClasspath().size() + 1); + for (Artifact artifact : processor.getClasspath()) { + Path file = gameRepository.getArtifactFile(version, artifact); + if (!Files.isRegularFile(file)) + throw new Exception("Game processor dependency missing"); + classpath.add(file.toString()); + } + classpath.add(jar.toString()); + command.add(String.join(OperatingSystem.PATH_SEPARATOR, classpath)); + + command.add(mainClass); + + List args = new ArrayList<>(processor.getArgs().size()); + for (String arg : processor.getArgs()) { + String parsed = parseLiteral(arg, vars); + if (parsed == null) + throw new ArtifactMalformedException("Invalid forge installation configuration"); + args.add(parsed); + } + + command.addAll(args); + + LOG.info("Executing external processor " + processor.getJar().toString() + ", command line: " + new CommandBuilder().addAll(command).toString()); + int exitCode = SystemUtils.callExternalProcess(command); + if (exitCode != 0) + throw new IOException("Game processor exited abnormally with code " + exitCode); + + for (Map.Entry entry : outputs.entrySet()) { + Path artifact = Paths.get(entry.getKey()); + if (!Files.isRegularFile(artifact)) + throw new FileNotFoundException("File missing: " + artifact); + + String code; + try (InputStream stream = Files.newInputStream(artifact)) { + code = encodeHex(digest("SHA-1", stream)); + } + + if (!Objects.equals(code, entry.getValue())) { + Files.delete(artifact); + throw new ChecksumMismatchException("SHA-1", entry.getValue(), code); + } + } + } + } + + private final DefaultDependencyManager dependencyManager; + private final DefaultGameRepository gameRepository; + private final Version version; + private final Path installer; + private final List> dependents = new ArrayList<>(1); + private final List> dependencies = new ArrayList<>(1); + + private ForgeNewInstallProfile profile; + private List processors; + private Version forgeVersion; + private final String selfVersion; + + private Path tempDir; + private AtomicInteger processorDoneCount = new AtomicInteger(0); + + ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) { + this.dependencyManager = dependencyManager; + this.gameRepository = dependencyManager.getGameRepository(); + this.version = version; + this.installer = installer; + this.selfVersion = selfVersion; + + setSignificance(TaskSignificance.MAJOR); + } + + private static String replaceTokens(Map tokens, String value) { + StringBuilder buf = new StringBuilder(); + for (int x = 0; x < value.length(); x++) { + char c = value.charAt(x); + if (c == '\\') { + if (x == value.length() - 1) + throw new IllegalArgumentException("Illegal pattern (Bad escape): " + value); + buf.append(value.charAt(++x)); + } else if (c == '{' || c == '\'') { + StringBuilder key = new StringBuilder(); + for (int y = x + 1; y <= value.length(); y++) { + if (y == value.length()) + throw new IllegalArgumentException("Illegal pattern (Unclosed " + c + "): " + value); + char d = value.charAt(y); + if (d == '\\') { + if (y == value.length() - 1) + throw new IllegalArgumentException("Illegal pattern (Bad escape): " + value); + key.append(value.charAt(++y)); + } else { + if (c == '{' && d == '}') { + x = y; + break; + } + if (c == '\'' && d == '\'') { + x = y; + break; + } + key.append(d); + } + } + if (c == '\'') { + buf.append(key); + } else { + if (!tokens.containsKey(key.toString())) + throw new IllegalArgumentException("Illegal pattern: " + value + " Missing Key: " + key); + buf.append(tokens.get(key.toString())); + } + } else { + buf.append(c); + } + } + return buf.toString(); + } + + private String parseLiteral(String literal, Map var, ExceptionalFunction plainConverter) throws E { + if (StringUtils.isSurrounded(literal, "{", "}")) + return var.get(StringUtils.removeSurrounding(literal, "{", "}")); + else if (StringUtils.isSurrounded(literal, "'", "'")) + return StringUtils.removeSurrounding(literal, "'"); + else if (StringUtils.isSurrounded(literal, "[", "]")) + return gameRepository.getArtifactFile(version, Artifact.fromDescriptor(StringUtils.removeSurrounding(literal, "[", "]"))).toString(); + else + return plainConverter.apply(replaceTokens(var, literal)); + } + + private String parseLiteral(String literal, Map var) { + return parseLiteral(literal, var, ExceptionalFunction.identity()); + } + + @Override + public Collection> getDependents() { + return dependents; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() throws Exception { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { + profile = fromNonNullJson(FileUtils.readText(fs.getPath("install_profile.json")), ForgeNewInstallProfile.class); + processors = profile.getProcessors(); + forgeVersion = fromNonNullJson(FileUtils.readText(fs.getPath(profile.getJson())), Version.class); + + for (Library library : profile.getLibraries()) { + Path file = fs.getPath("maven").resolve(library.getPath()); + if (Files.exists(file)) { + Path dest = gameRepository.getLibraryFile(version, library).toPath(); + FileUtils.copyFile(file, dest); + } + } + + if (profile.getPath().isPresent()) { + Path mainJar = profile.getPath().get().getPath(fs.getPath("maven")); + if (Files.exists(mainJar)) { + Path dest = gameRepository.getArtifactFile(version, profile.getPath().get()); + FileUtils.copyFile(mainJar, dest); + } + } + } catch (ZipException ex) { + throw new ArtifactMalformedException("Malformed forge installer file", ex); + } + + dependents.add(new GameLibrariesTask(dependencyManager, version, true, profile.getLibraries())); + } + + private Map parseOptions(List args, Map vars) { + Map options = new LinkedHashMap<>(); + String optionName = null; + for (String arg : args) { + if (arg.startsWith("--")) { + if (optionName != null) { + options.put(optionName, ""); + } + optionName = arg.substring(2); + } else { + if (optionName == null) { + // ignore + } else { + options.put(optionName, parseLiteral(arg, vars)); + optionName = null; + } + } + } + if (optionName != null) { + options.put(optionName, ""); + } + return options; + } + + private Task patchDownloadMojangMappingsTask(Processor processor, Map vars) { + Map options = parseOptions(processor.getArgs(), vars); + if (!"DOWNLOAD_MOJMAPS".equals(options.get("task")) || !"client".equals(options.get("side"))) + return null; + String version = options.get("version"); + String output = options.get("output"); + if (version == null || output == null) + return null; + + LOG.info("Patching DOWNLOAD_MOJMAPS task"); + return new VersionJsonDownloadTask(version, dependencyManager) + .thenComposeAsync(json -> { + DownloadInfo mappings = fromNonNullJson(json, Version.class) + .getDownloads().get(DownloadType.CLIENT_MAPPINGS); + if (mappings == null) { + throw new Exception("client_mappings download info not found"); + } + + List mappingsUrl = dependencyManager.getDownloadProvider() + .injectURLWithCandidates(mappings.getUrl()); + FileDownloadTask mappingsTask = new FileDownloadTask( + mappingsUrl, + new File(output), + FileDownloadTask.IntegrityCheck.of("SHA-1", mappings.getSha1())); + mappingsTask.setCaching(true); + mappingsTask.setCacheRepository(dependencyManager.getCacheRepository()); + return mappingsTask; + }); + } + + private Task createProcessorTask(Processor processor, Map vars) { + Task task = patchDownloadMojangMappingsTask(processor, vars); + if (task == null) { + task = new ProcessorTask(processor, vars); + } + task.onDone().register( + () -> updateProgress(processorDoneCount.incrementAndGet(), processors.size())); + return task; + } + + @Override + public void execute() throws Exception { + tempDir = Files.createTempDirectory("forge_installer"); + + Map vars = new HashMap<>(); + + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { + for (Map.Entry entry : profile.getData().entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + vars.put(key, parseLiteral(value, + Collections.emptyMap(), + str -> { + Path dest = Files.createTempFile(tempDir, null, null); + FileUtils.copyFile(fs.getPath(str), dest); + return dest.toString(); + })); + } + } catch (ZipException ex) { + throw new ArtifactMalformedException("Malformed forge installer file", ex); + } + + vars.put("SIDE", "client"); + vars.put("MINECRAFT_JAR", gameRepository.getVersionJar(version).getAbsolutePath()); + vars.put("MINECRAFT_VERSION", gameRepository.getVersionJar(version).getAbsolutePath()); + vars.put("ROOT", gameRepository.getBaseDirectory().getAbsolutePath()); + vars.put("INSTALLER", installer.toAbsolutePath().toString()); + vars.put("LIBRARY_DIR", gameRepository.getLibrariesDirectory(version).getAbsolutePath()); + + updateProgress(0, processors.size()); + + Task processorsTask = Task.runSequentially( + processors.stream() + .map(processor -> createProcessorTask(processor, vars)) + .toArray(Task[]::new)); + + dependencies.add( + processorsTask.thenComposeAsync( + dependencyManager.checkLibraryCompletionAsync(forgeVersion, true))); + + setResult(forgeVersion + .setPriority(30000) + .setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) + .setVersion(selfVersion)); + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + FileUtils.deleteDirectory(tempDir.toFile()); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeOldInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeOldInstallTask.java new file mode 100644 index 00000000..113841f5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeOldInstallTask.java @@ -0,0 +1,77 @@ +package com.tungsten.fclcore.download.forge; + +import com.tungsten.fclcore.download.ArtifactMalformedException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.io.IOUtils; + +import java.io.*; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +public class ForgeOldInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final Path installer; + private final String selfVersion; + private final List> dependencies = new ArrayList<>(1); + + ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) { + this.dependencyManager = dependencyManager; + this.version = version; + this.installer = installer; + this.selfVersion = selfVersion; + + setSignificance(TaskSignificance.MAJOR); + } + + @Override + public List> getDependencies() { + return dependencies; + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void execute() throws Exception { + try (ZipFile zipFile = new ZipFile(installer.toFile())) { + InputStream stream = zipFile.getInputStream(zipFile.getEntry("install_profile.json")); + if (stream == null) + throw new ArtifactMalformedException("Malformed forge installer file, install_profile.json does not exist."); + String json = IOUtils.readFullyAsString(stream); + ForgeInstallProfile installProfile = JsonUtils.fromNonNullJson(json, ForgeInstallProfile.class); + + // unpack the universal jar in the installer file. + Library forgeLibrary = new Library(installProfile.getInstall().getPath()); + File forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary); + if (!FileUtils.makeFile(forgeFile)) + throw new IOException("Cannot make directory " + forgeFile.getParent()); + + ZipEntry forgeEntry = zipFile.getEntry(installProfile.getInstall().getFilePath()); + try (InputStream is = zipFile.getInputStream(forgeEntry); OutputStream os = new FileOutputStream(forgeFile)) { + IOUtils.copyTo(is, os); + } + + setResult(installProfile.getVersionInfo() + .setPriority(30000) + .setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) + .setVersion(selfVersion)); + dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo(), true)); + } catch (ZipException ex) { + throw new ArtifactMalformedException("Malformed forge installer file", ex); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeRemoteVersion.java new file mode 100644 index 00000000..9ddab6b9 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeRemoteVersion.java @@ -0,0 +1,28 @@ +package com.tungsten.fclcore.download.forge; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; + +import java.util.Date; +import java.util.List; + +public class ForgeRemoteVersion extends RemoteVersion { + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param url the installer or universal jar original URL. + */ + public ForgeRemoteVersion(String gameVersion, String selfVersion, Date releaseDate, List url) { + super(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), gameVersion, selfVersion, releaseDate, url); + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new ForgeInstallTask(dependencyManager, baseVersion, this); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersion.java new file mode 100644 index 00000000..d6c9615a --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersion.java @@ -0,0 +1,72 @@ +package com.tungsten.fclcore.download.forge; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.gson.Validation; + +public final class ForgeVersion implements Validation { + + private final String branch; + private final String mcversion; + private final String jobver; + private final String version; + private final int build; + private final double modified; + private final String[][] files; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public ForgeVersion() { + this(null, null, null, null, 0, 0, null); + } + + public ForgeVersion(String branch, String mcversion, String jobver, String version, int build, double modified, String[][] files) { + this.branch = branch; + this.mcversion = mcversion; + this.jobver = jobver; + this.version = version; + this.build = build; + this.modified = modified; + this.files = files; + } + + public String getBranch() { + return branch; + } + + public String getGameVersion() { + return mcversion; + } + + public String getJobver() { + return jobver; + } + + public String getVersion() { + return version; + } + + public int getBuild() { + return build; + } + + public double getModified() { + return modified; + } + + public String[][] getFiles() { + return files; + } + + @Override + public void validate() throws JsonParseException { + if (files == null) + throw new JsonParseException("ForgeVersion files cannot be null"); + if (version == null) + throw new JsonParseException("ForgeVersion version cannot be null"); + if (mcversion == null) + throw new JsonParseException("ForgeVersion mcversion cannot be null"); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersionList.java new file mode 100644 index 00000000..08e08b8b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersionList.java @@ -0,0 +1,65 @@ +package com.tungsten.fclcore.download.forge; + +import com.tungsten.fclcore.download.DownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.io.HttpRequest; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public final class ForgeVersionList extends VersionList { + private final DownloadProvider downloadProvider; + + public ForgeVersionList(DownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return false; + } + + @Override + public CompletableFuture refreshAsync() { + return HttpRequest.GET(downloadProvider.injectURL(FORGE_LIST)).getJsonAsync(ForgeVersionRoot.class) + .thenAcceptAsync(root -> { + lock.writeLock().lock(); + + try { + if (root == null) + return; + versions.clear(); + + for (Map.Entry entry : root.getGameVersions().entrySet()) { + String gameVersion = VersionNumber.normalize(entry.getKey()); + for (int v : entry.getValue()) { + ForgeVersion version = root.getNumber().get(v); + if (version == null) + continue; + String jar = null; + for (String[] file : version.getFiles()) + if (file.length > 1 && "installer".equals(file[1])) { + String classifier = version.getGameVersion() + "-" + version.getVersion() + + (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : ""); + String fileName = root.getArtifact() + "-" + classifier + "-" + file[1] + "." + file[0]; + jar = root.getWebPath() + classifier + "/" + fileName; + } + + if (jar == null) + continue; + versions.put(gameVersion, new ForgeRemoteVersion( + version.getGameVersion(), version.getVersion(), null, Collections.singletonList(jar) + )); + } + } + } finally { + lock.writeLock().unlock(); + } + }); + } + + public static final String FORGE_LIST = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/json"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersionRoot.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersionRoot.java new file mode 100644 index 00000000..11afcdef --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/forge/ForgeVersionRoot.java @@ -0,0 +1,84 @@ +package com.tungsten.fclcore.download.forge; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.gson.Validation; + +import java.util.Map; + +public final class ForgeVersionRoot implements Validation { + + private final String artifact; + private final String webpath; + private final String adfly; + private final String homepage; + private final String name; + private final Map branches; + private final Map mcversion; + private final Map promos; + private final Map number; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public ForgeVersionRoot() { + this(null, null, null, null, null, null, null, null, null); + } + + public ForgeVersionRoot(String artifact, String webpath, String adfly, String homepage, String name, Map branches, Map mcversion, Map promos, Map number) { + this.artifact = artifact; + this.webpath = webpath; + this.adfly = adfly; + this.homepage = homepage; + this.name = name; + this.branches = branches; + this.mcversion = mcversion; + this.promos = promos; + this.number = number; + } + + public String getArtifact() { + return artifact; + } + + public String getWebPath() { + return webpath; + } + + public String getAdfly() { + return adfly; + } + + public String getHomePage() { + return homepage; + } + + public String getName() { + return name; + } + + public Map getBranches() { + return branches; + } + + public Map getGameVersions() { + return mcversion; + } + + public Map getPromos() { + return promos; + } + + public Map getNumber() { + return number; + } + + @Override + public void validate() throws JsonParseException { + if (number == null) + throw new JsonParseException("ForgeVersionRoot number cannot be null"); + if (mcversion == null) + throw new JsonParseException("ForgeVersionRoot mcversion cannot be null"); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameAssetDownloadTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameAssetDownloadTask.java new file mode 100644 index 00000000..80073b97 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameAssetDownloadTask.java @@ -0,0 +1,109 @@ +package com.tungsten.fclcore.download.game; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.AbstractDependencyManager; +import com.tungsten.fclcore.game.AssetIndex; +import com.tungsten.fclcore.game.AssetIndexInfo; +import com.tungsten.fclcore.game.AssetObject; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.CacheRepository; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; + +public final class GameAssetDownloadTask extends Task { + + private final AbstractDependencyManager dependencyManager; + private final Version version; + private final AssetIndexInfo assetIndexInfo; + private final Path assetIndexFile; + private final boolean integrityCheck; + private final List> dependents = new ArrayList<>(1); + private final List> dependencies = new ArrayList<>(); + + /** + * Constructor. + * + * @param dependencyManager the dependency manager that can provides {@link com.tungsten.fclcore.game.GameRepository} + * @param version the game version + */ + public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloadingIndex, boolean integrityCheck) { + this.dependencyManager = dependencyManager; + this.version = version.resolve(dependencyManager.getGameRepository()); + this.assetIndexInfo = this.version.getAssetIndex(); + this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId()); + this.integrityCheck = integrityCheck; + + setStage("hmcl.install.assets"); + dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version, forceDownloadingIndex)); + } + + @Override + public Collection> getDependents() { + return dependents; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws Exception { + AssetIndex index; + try { + index = JsonUtils.fromNonNullJson(FileUtils.readText(assetIndexFile), AssetIndex.class); + } catch (IOException | JsonParseException e) { + throw new GameAssetIndexDownloadTask.GameAssetIndexMalformedException(); + } + + int progress = 0; + for (AssetObject assetObject : index.getObjects().values()) { + if (isCancelled()) + throw new InterruptedException(); + + Path file = dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject); + boolean download = !Files.isRegularFile(file); + try { + if (!download && integrityCheck && !assetObject.validateChecksum(file, true)) + download = true; + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Unable to calc hash value of file " + file, e); + } + if (download) { + List urls = dependencyManager.getDownloadProvider().getAssetObjectCandidates(assetObject.getLocation()); + + FileDownloadTask task = new FileDownloadTask(urls, file.toFile(), new FileDownloadTask.IntegrityCheck("SHA-1", assetObject.getHash())); + task.setName(assetObject.getHash()); + task.setCandidate(dependencyManager.getCacheRepository().getCommonDirectory() + .resolve("assets").resolve("objects").resolve(assetObject.getLocation())); + task.setCacheRepository(dependencyManager.getCacheRepository()); + task.setCaching(true); + dependencies.add(task.withCounter("hmcl.install.assets")); + } else { + dependencyManager.getCacheRepository().tryCacheFile(file, CacheRepository.SHA1, assetObject.getHash()); + } + + updateProgress(++progress, index.getObjects().size()); + } + + if (!dependencies.isEmpty()) { + getProperties().put("total", dependencies.size()); + notifyPropertiesChanged(); + } + } + + public static final boolean DOWNLOAD_INDEX_FORCIBLY = true; + public static final boolean DOWNLOAD_INDEX_IF_NECESSARY = false; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameAssetIndexDownloadTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameAssetIndexDownloadTask.java new file mode 100644 index 00000000..f544cd9d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameAssetIndexDownloadTask.java @@ -0,0 +1,90 @@ +package com.tungsten.fclcore.download.game; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.AbstractDependencyManager; +import com.tungsten.fclcore.game.AssetIndex; +import com.tungsten.fclcore.game.AssetIndexInfo; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.DigestUtils; +import com.tungsten.fclcore.util.Hex; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +/** + * This task is to download asset index file provided in minecraft.json. + */ +public final class GameAssetIndexDownloadTask extends Task { + + private final AbstractDependencyManager dependencyManager; + private final Version version; + private final boolean forceDownloading; + private final List> dependencies = new ArrayList<>(1); + + /** + * Constructor. + * + * @param dependencyManager the dependency manager that can provides {@link com.tungsten.fclcore.game.GameRepository} + * @param version the resolved version + */ + public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloading) { + this.dependencyManager = dependencyManager; + this.version = version; + this.forceDownloading = forceDownloading; + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public List> getDependencies() { + return dependencies; + } + + @Override + public void execute() { + AssetIndexInfo assetIndexInfo = version.getAssetIndex(); + Path assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId()); + boolean verifyHashCode = StringUtils.isNotBlank(assetIndexInfo.getSha1()) && assetIndexInfo.getUrl().contains(assetIndexInfo.getSha1()); + + if (Files.exists(assetIndexFile) && !forceDownloading) { + // verify correctness of file content + if (verifyHashCode) { + try { + String actualSum = Hex.encodeHex(DigestUtils.digest("SHA-1", assetIndexFile)); + if (actualSum.equalsIgnoreCase(assetIndexInfo.getSha1())) + return; + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to calculate sha1sum of file " + assetIndexInfo, e); + // continue downloading + } + } else { + try { + JsonUtils.fromNonNullJson(FileUtils.readText(assetIndexFile), AssetIndex.class); + return; + } catch (IOException | JsonParseException ignore) { + } + } + } + + // We should not check the hash code of asset index file since this file is not consistent + // And Mojang will modify this file anytime. So assetIndex.hash might be outdated. + FileDownloadTask task = new FileDownloadTask( + dependencyManager.getDownloadProvider().injectURLWithCandidates(assetIndexInfo.getUrl()), + assetIndexFile.toFile(), + verifyHashCode ? new FileDownloadTask.IntegrityCheck("SHA-1", assetIndexInfo.getSha1()) : null + ); + task.setCacheRepository(dependencyManager.getCacheRepository()); + dependencies.add(task); + } + + public static class GameAssetIndexMalformedException extends IOException { + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameDownloadTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameDownloadTask.java new file mode 100644 index 00000000..effb16ae --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameDownloadTask.java @@ -0,0 +1,53 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.CacheRepository; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Task to download Minecraft jar + */ +public final class GameDownloadTask extends Task { + private final DefaultDependencyManager dependencyManager; + private final String gameVersion; + private final Version version; + private final List> dependencies = new ArrayList<>(); + + public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) { + this.dependencyManager = dependencyManager; + this.gameVersion = gameVersion; + this.version = version.resolve(dependencyManager.getGameRepository()); + + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public void execute() { + File jar = dependencyManager.getGameRepository().getVersionJar(version); + + FileDownloadTask task = new FileDownloadTask( + dependencyManager.getDownloadProvider().injectURLWithCandidates(version.getDownloadInfo().getUrl()), + jar, + FileDownloadTask.IntegrityCheck.of(CacheRepository.SHA1, version.getDownloadInfo().getSha1())); + task.setCaching(true); + task.setCacheRepository(dependencyManager.getCacheRepository()); + + if (gameVersion != null) + task.setCandidate(dependencyManager.getCacheRepository().getCommonDirectory().resolve("jars").resolve(gameVersion + ".jar")); + + dependencies.add(task); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameInstallTask.java new file mode 100644 index 00000000..b9b7529b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameInstallTask.java @@ -0,0 +1,83 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2020 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.tungsten.fclcore.download.game; + +import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.util.gson.JsonUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT; + +public class GameInstallTask extends Task { + + private final DefaultGameRepository gameRepository; + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final GameRemoteVersion remote; + private final VersionJsonDownloadTask downloadTask; + private final List> dependencies = new ArrayList<>(1); + + public GameInstallTask(DefaultDependencyManager dependencyManager, Version version, GameRemoteVersion remoteVersion) { + this.dependencyManager = dependencyManager; + this.gameRepository = dependencyManager.getGameRepository(); + this.version = version; + this.remote = remoteVersion; + this.downloadTask = new VersionJsonDownloadTask(remoteVersion.getGameVersion(), dependencyManager); + } + + @Override + public Collection> getDependents() { + return Collections.singleton(downloadTask); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() throws Exception { + Version patch = JsonUtils.fromNonNullJson(downloadTask.getResult(), Version.class) + .setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(0); + setResult(patch); + + Version version = new Version(this.version.getId()).addPatch(patch); + dependencies.add(Task.allOf( + new GameDownloadTask(dependencyManager, remote.getGameVersion(), version), + Task.allOf( + new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true), + new GameLibrariesTask(dependencyManager, version, true) + ).withRunAsync(() -> { + // ignore failure + }) + ).thenComposeAsync(gameRepository.saveAsync(version))); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameLibrariesTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameLibrariesTask.java new file mode 100644 index 00000000..a94338ae --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameLibrariesTask.java @@ -0,0 +1,101 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.download.AbstractDependencyManager; +import com.tungsten.fclcore.game.GameRepository; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +/** + * This task is to download game libraries. + * This task should be executed last(especially after game downloading, Forge, LiteLoader and OptiFine install task). + */ +public final class GameLibrariesTask extends Task { + + private final AbstractDependencyManager dependencyManager; + private final Version version; + private final boolean integrityCheck; + private final List libraries; + private final List> dependencies = new ArrayList<>(); + + /** + * Constructor. + * + * @param dependencyManager the dependency manager that can provides {@link com.tungsten.fclcore.game.GameRepository} + * @param version the game version + */ + public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, boolean integrityCheck) { + this(dependencyManager, version, integrityCheck, version.resolve(dependencyManager.getGameRepository()).getLibraries()); + } + + /** + * Constructor. + * + * @param dependencyManager the dependency manager that can provides {@link com.tungsten.fclcore.game.GameRepository} + * @param version the game version + */ + public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, boolean integrityCheck, List libraries) { + this.dependencyManager = dependencyManager; + this.version = version; + this.integrityCheck = integrityCheck; + this.libraries = libraries; + + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public List> getDependencies() { + return dependencies; + } + + public static boolean shouldDownloadLibrary(GameRepository gameRepository, Version version, Library library, boolean integrityCheck) { + File file = gameRepository.getLibraryFile(version, library); + Path jar = file.toPath(); + if (!file.isFile()) return true; + try { + if (integrityCheck && !library.getDownload().validateChecksum(jar, true)) return true; + if (integrityCheck && + library.getChecksums() != null && !library.getChecksums().isEmpty() && + !LibraryDownloadTask.checksumValid(file, library.getChecksums())) return true; + if (integrityCheck) { + String ext = FileUtils.getExtension(file); + if (ext.equals("jar")) { + try { + FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar); + } catch (IOException ignored) { + // the Jar file is malformed, so re-download it. + return true; + } + } + } + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Unable to calc hash value of file " + jar, e); + } + + return false; + } + + @Override + public void execute() { + libraries.stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> { + File file = dependencyManager.getGameRepository().getLibraryFile(version, library); + if (shouldDownloadLibrary(dependencyManager.getGameRepository(), version, library, integrityCheck)) { + if (library.hasDownloadURL() || !"optifine".equals(library.getGroupId())) + dependencies.add(new LibraryDownloadTask(dependencyManager, file, library)); + } else { + dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath()); + } + }); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteLatestVersions.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteLatestVersions.java new file mode 100644 index 00000000..498684f6 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteLatestVersions.java @@ -0,0 +1,29 @@ +package com.tungsten.fclcore.download.game; + +import com.google.gson.annotations.SerializedName; + +public final class GameRemoteLatestVersions { + + @SerializedName("snapshot") + private final String snapshot; + + @SerializedName("release") + private final String release; + + public GameRemoteLatestVersions() { + this(null, null); + } + + public GameRemoteLatestVersions(String snapshot, String release) { + this.snapshot = snapshot; + this.release = release; + } + + public String getRelease() { + return release; + } + + public String getSnapshot() { + return snapshot; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersion.java new file mode 100644 index 00000000..2ff9878d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersion.java @@ -0,0 +1,52 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.ReleaseType; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; + +import java.util.Date; +import java.util.List; + +public final class GameRemoteVersion extends RemoteVersion { + + private final ReleaseType type; + + public GameRemoteVersion(String gameVersion, String selfVersion, List url, ReleaseType type, Date releaseDate) { + super(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId(), gameVersion, selfVersion, releaseDate, getReleaseType(type), url); + this.type = type; + } + + public ReleaseType getType() { + return type; + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new GameInstallTask(dependencyManager, baseVersion, this); + } + + @Override + public int compareTo(RemoteVersion o) { + if (!(o instanceof GameRemoteVersion)) + return 0; + + return o.getReleaseDate().compareTo(getReleaseDate()); + } + + private static Type getReleaseType(ReleaseType type) { + if (type == null) return Type.UNCATEGORIZED; + switch (type) { + case RELEASE: + return Type.RELEASE; + case SNAPSHOT: + return Type.SNAPSHOT; + case UNKNOWN: + return Type.UNCATEGORIZED; + default: + return Type.OLD; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersionInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersionInfo.java new file mode 100644 index 00000000..4c701485 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersionInfo.java @@ -0,0 +1,72 @@ +package com.tungsten.fclcore.download.game; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.constant.UrlConstants; +import com.tungsten.fclcore.game.ReleaseType; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.Validation; + +import java.util.Date; + +public final class GameRemoteVersionInfo implements Validation { + + @SerializedName("id") + private final String gameVersion; + + @SerializedName("time") + private final Date time; + + @SerializedName("releaseTime") + private final Date releaseTime; + + @SerializedName("type") + private final ReleaseType type; + + @SerializedName("url") + private final String url; + + public GameRemoteVersionInfo() { + this("", new Date(), new Date(), ReleaseType.UNKNOWN); + } + + public GameRemoteVersionInfo(String gameVersion, Date time, Date releaseTime, ReleaseType type) { + this(gameVersion, time, releaseTime, type, UrlConstants.DEFAULT_LIBRARY_URL + gameVersion + "/" + gameVersion + ".json"); + } + + public GameRemoteVersionInfo(String gameVersion, Date time, Date releaseTime, ReleaseType type, String url) { + this.gameVersion = gameVersion; + this.time = time; + this.releaseTime = releaseTime; + this.type = type; + this.url = url; + } + + public String getGameVersion() { + return gameVersion; + } + + public Date getTime() { + return time; + } + + public Date getReleaseTime() { + return releaseTime; + } + + public ReleaseType getType() { + return type; + } + + public String getUrl() { + return url; + } + + @Override + public void validate() throws JsonParseException { + if (StringUtils.isBlank(gameVersion)) + throw new JsonParseException("GameRemoteVersion id cannot be blank"); + if (StringUtils.isBlank(url)) + throw new JsonParseException("GameRemoteVersion url cannot be blank"); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersions.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersions.java new file mode 100644 index 00000000..0d0544be --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameRemoteVersions.java @@ -0,0 +1,44 @@ +package com.tungsten.fclcore.download.game; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.gson.Validation; + +import java.util.Collections; +import java.util.List; + +public final class GameRemoteVersions implements Validation { + + @SerializedName("versions") + private final List versions; + + @SerializedName("latest") + private final GameRemoteLatestVersions latest; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public GameRemoteVersions() { + this(Collections.emptyList(), null); + } + + public GameRemoteVersions(List versions, GameRemoteLatestVersions latest) { + this.versions = versions; + this.latest = latest; + } + + public GameRemoteLatestVersions getLatest() { + return latest; + } + + public List getVersions() { + return versions; + } + + @Override + public void validate() throws JsonParseException { + if (versions == null) + throw new JsonParseException("GameRemoteVersions.versions cannot be null"); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameVerificationFixTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameVerificationFixTask.java new file mode 100644 index 00000000..0f4da086 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameVerificationFixTask.java @@ -0,0 +1,57 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Remove class digital verification file in game jar + */ +public final class GameVerificationFixTask extends Task { + private final DefaultDependencyManager dependencyManager; + private final String gameVersion; + private final Version version; + private final List> dependencies = new ArrayList<>(); + + public GameVerificationFixTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) { + this.dependencyManager = dependencyManager; + this.gameVersion = gameVersion; + this.version = version; + + if (!version.isResolved()) { + throw new IllegalArgumentException("GameVerificationFixTask requires a resolved game version"); + } + + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws IOException { + File jar = dependencyManager.getGameRepository().getVersionJar(version); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version); + + if (jar.exists() && VersionNumber.VERSION_COMPARATOR.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) { + try (FileSystem fs = CompressingUtils.createWritableZipFileSystem(jar.toPath(), StandardCharsets.UTF_8)) { + Files.deleteIfExists(fs.getPath("META-INF/MOJANG_C.DSA")); + Files.deleteIfExists(fs.getPath("META-INF/MOJANG_C.SF")); + } + } + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameVersionList.java new file mode 100644 index 00000000..e0438f47 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/GameVersionList.java @@ -0,0 +1,48 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.download.DownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.io.HttpRequest; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +public final class GameVersionList extends VersionList { + private final DownloadProvider downloadProvider; + + public GameVersionList(DownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return true; + } + + @Override + protected Collection getVersionsImpl(String gameVersion) { + return versions.values(); + } + + @Override + public CompletableFuture refreshAsync() { + return HttpRequest.GET(downloadProvider.getVersionListURL()).getJsonAsync(GameRemoteVersions.class) + .thenAcceptAsync(root -> { + lock.writeLock().lock(); + try { + versions.clear(); + + for (GameRemoteVersionInfo remoteVersion : root.getVersions()) { + versions.put(remoteVersion.getGameVersion(), new GameRemoteVersion( + remoteVersion.getGameVersion(), + remoteVersion.getGameVersion(), + Collections.singletonList(remoteVersion.getUrl()), + remoteVersion.getType(), remoteVersion.getReleaseTime())); + } + } finally { + lock.writeLock().unlock(); + } + }); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/LibraryDownloadException.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/LibraryDownloadException.java new file mode 100644 index 00000000..50c10693 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/LibraryDownloadException.java @@ -0,0 +1,19 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.game.Library; + +import org.jetbrains.annotations.NotNull; + +public class LibraryDownloadException extends Exception { + private final Library library; + + public LibraryDownloadException(Library library, @NotNull Throwable cause) { + super("Unable to download library " + library, cause); + + this.library = library; + } + + public Library getLibrary() { + return library; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/LibraryDownloadTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/LibraryDownloadTask.java new file mode 100644 index 00000000..2df3fca4 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/LibraryDownloadTask.java @@ -0,0 +1,256 @@ +package com.tungsten.fclcore.download.game; + +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; +import static com.tungsten.fclcore.util.Logging.LOG; + +import com.tungsten.fclcore.download.AbstractDependencyManager; +import com.tungsten.fclcore.download.ArtifactMalformedException; +import com.tungsten.fclcore.download.DefaultCacheRepository; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.task.DownloadException; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.io.IOUtils; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import java.io.*; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.logging.Level; + +public class LibraryDownloadTask extends Task { + private FileDownloadTask task; + protected final File jar; + protected final DefaultCacheRepository cacheRepository; + protected final AbstractDependencyManager dependencyManager; + private final File xzFile; + protected final Library library; + protected final String url; + protected boolean xz; + private final Library originalLibrary; + private boolean cached = false; + + public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) { + this.dependencyManager = dependencyManager; + this.originalLibrary = library; + + setSignificance(TaskSignificance.MODERATE); + + if (library.is("net.minecraftforge", "forge")) + library = library.setClassifier("universal"); + + this.library = library; + this.cacheRepository = dependencyManager.getCacheRepository(); + + url = library.getDownload().getUrl(); + jar = file; + + xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz"); + } + + @Override + public Collection> getDependents() { + if (cached) return Collections.emptyList(); + else return Collections.singleton(task); + } + + @Override + public boolean isRelyingOnDependents() { + return false; + } + + @Override + public void execute() throws Exception { + if (cached) return; + + if (!isDependentsSucceeded()) { + // Since FileDownloadTask wraps the actual exception with DownloadException. + // We should extract it letting the error message clearer. + Exception t = task.getException(); + if (t instanceof DownloadException) + throw new LibraryDownloadException(library, t.getCause()); + else if (t instanceof CancellationException) + throw new CancellationException(); + else + throw new LibraryDownloadException(library, t); + } else { + if (xz) unpackLibrary(jar, Files.readAllBytes(xzFile.toPath())); + } + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() { + Optional libPath = cacheRepository.getLibrary(originalLibrary); + if (libPath.isPresent()) { + try { + FileUtils.copyFile(libPath.get().toFile(), jar); + cached = true; + return; + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to copy file from cache", e); + // We cannot copy cached file to current location + // so we try to download a new one. + } + } + + if (Pack200Utils.isSupported() && testURLExistence(url)) { + List urls = dependencyManager.getDownloadProvider().injectURLWithCandidates(url + ".pack.xz"); + task = new FileDownloadTask(urls, xzFile, null); + task.setCacheRepository(cacheRepository); + task.setCaching(true); + xz = true; + } else { + List urls = dependencyManager.getDownloadProvider().injectURLWithCandidates(url); + task = new FileDownloadTask(urls, jar, + library.getDownload().getSha1() != null ? new FileDownloadTask.IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null); + task.setCacheRepository(cacheRepository); + task.setCaching(true); + task.addIntegrityCheckHandler(FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER); + xz = false; + } + } + + private boolean testURLExistence(String rawUrl) { + List urls = dependencyManager.getDownloadProvider().injectURLWithCandidates(rawUrl); + for (URL url : urls) { + URL xzURL = NetworkUtils.toURL(url.toString() + ".pack.xz"); + for (int retry = 0; retry < 3; retry++) { + try { + return NetworkUtils.urlExists(xzURL); + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to test for url existence: " + url + ".pack.xz", e); + } + } + } + return false; // maybe some ugly implementation will give timeout for not existent url. + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + if (!cached) { + try { + cacheRepository.cacheLibrary(library, jar.toPath(), xz); + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to cache downloaded library " + library, e); + } + } + } + + public static boolean checksumValid(File libPath, List checksums) { + try { + if (checksums == null || checksums.isEmpty()) { + return true; + } + byte[] fileData = Files.readAllBytes(libPath.toPath()); + boolean valid = checksums.contains(encodeHex(digest("SHA-1", fileData))); + if (!valid && libPath.getName().endsWith(".jar")) { + valid = validateJar(fileData, checksums); + } + return valid; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + private static boolean validateJar(byte[] data, List checksums) throws IOException { + HashMap files = new HashMap<>(); + String[] hashes = null; + JarInputStream jar = new JarInputStream(new ByteArrayInputStream(data)); + JarEntry entry = jar.getNextJarEntry(); + while (entry != null) { + byte[] eData = IOUtils.readFullyWithoutClosing(jar); + if (entry.getName().equals("checksums.sha1")) { + hashes = new String(eData, StandardCharsets.UTF_8).split("\n"); + } + if (!entry.isDirectory()) { + files.put(entry.getName(), encodeHex(digest("SHA-1", eData))); + } + entry = jar.getNextJarEntry(); + } + jar.close(); + if (hashes != null) { + boolean failed = !checksums.contains(files.get("checksums.sha1")); + if (!failed) { + for (String hash : hashes) { + if ((!hash.trim().equals("")) && (hash.contains(" "))) { + String[] e = hash.split(" "); + String validChecksum = e[0]; + String target = hash.substring(validChecksum.length() + 1); + String checksum = files.get(target); + if ((!files.containsKey(target)) || (checksum == null)) { + LOG.warning(" " + target + " : missing"); + failed = true; + break; + } else if (!checksum.equals(validChecksum)) { + LOG.warning(" " + target + " : failed (" + checksum + ", " + validChecksum + ")"); + failed = true; + break; + } + } + } + } + return !failed; + } + return false; + } + + private static void unpackLibrary(File dest, byte[] src) throws IOException { + if (dest.exists()) + if (!dest.delete()) + throw new IOException("Unable to delete file " + dest); + + byte[] decompressed; + try { + decompressed = IOUtils.readFullyAsByteArray(new XZInputStream(new ByteArrayInputStream(src))); + } catch (IOException e) { + throw new ArtifactMalformedException("Library " + dest + " is malformed"); + } + + String end = new String(decompressed, decompressed.length - 4, 4); + if (!end.equals("SIGN")) + throw new IOException("Unpacking failed, signature missing " + end); + + int x = decompressed.length; + int len = decompressed[(x - 8)] & 0xFF | (decompressed[(x - 7)] & 0xFF) << 8 | (decompressed[(x - 6)] & 0xFF) << 16 | (decompressed[(x - 5)] & 0xFF) << 24; + + Path temp = Files.createTempFile("minecraft", ".pack"); + + byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8); + + try (OutputStream out = Files.newOutputStream(temp)) { + out.write(decompressed, 0, decompressed.length - len - 8); + } + + try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) { + Pack200Utils.unpack(temp.toFile(), jos); + + JarEntry checksumsFile = new JarEntry("checksums.sha1"); + checksumsFile.setTime(0L); + jos.putNextEntry(checksumsFile); + jos.write(checksums); + jos.closeEntry(); + } + + Files.delete(temp); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/VersionJsonDownloadTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/VersionJsonDownloadTask.java new file mode 100644 index 00000000..c7b43474 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/VersionJsonDownloadTask.java @@ -0,0 +1,47 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.task.GetTask; +import com.tungsten.fclcore.task.Task; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public final class VersionJsonDownloadTask extends Task { + private final String gameVersion; + private final DefaultDependencyManager dependencyManager; + private final List> dependents = new ArrayList<>(1); + private final List> dependencies = new ArrayList<>(1); + private final VersionList gameVersionList; + + public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) { + this.gameVersion = gameVersion; + this.dependencyManager = dependencyManager; + this.gameVersionList = dependencyManager.getVersionList("game"); + + dependents.add(Task.fromCompletableFuture(gameVersionList.loadAsync(gameVersion))); + + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public Collection> getDependents() { + return dependents; + } + + @Override + public void execute() throws IOException { + RemoteVersion remoteVersion = gameVersionList.getVersion(gameVersion, gameVersion) + .orElseThrow(() -> new IOException("Cannot find specific version " + gameVersion + " in remote repository")); + dependencies.add(new GetTask(dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls())).storeTo(this::setResult)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/game/VersionJsonSaveTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/VersionJsonSaveTask.java new file mode 100644 index 00000000..ee227963 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/game/VersionJsonSaveTask.java @@ -0,0 +1,38 @@ +package com.tungsten.fclcore.download.game; + +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; + +/** + * This task is to save the version json. + */ +public final class VersionJsonSaveTask extends Task { + + private final DefaultGameRepository repository; + private final Version version; + + /** + * Constructor. + * + * @param repository the game repository + * @param version the game version + */ + public VersionJsonSaveTask(DefaultGameRepository repository, Version version) { + this.repository = repository; + this.version = version; + + setSignificance(TaskSignificance.MODERATE); + setResult(version); + } + + @Override + public void execute() throws Exception { + File json = repository.getVersionJson(version.getId()).getAbsoluteFile(); + FileUtils.writeText(json, JsonUtils.GSON.toJson(version)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderBMCLVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderBMCLVersionList.java new file mode 100644 index 00000000..f0a23bdd --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderBMCLVersionList.java @@ -0,0 +1,92 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.tungsten.fclcore.download.BMCLAPIDownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.io.HttpRequest; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public final class LiteLoaderBMCLVersionList extends VersionList { + private final BMCLAPIDownloadProvider downloadProvider; + + public LiteLoaderBMCLVersionList(BMCLAPIDownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return false; + } + + private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) { + if (branch == null || repository == null) + return; + + for (Map.Entry entry : branch.getLiteLoader().entrySet()) { + String branchName = entry.getKey(); + LiteLoaderVersion v = entry.getValue(); + if ("latest".equals(branchName)) + continue; + + String version = v.getVersion(); + String url = "https://bmclapi2.bangbang93.com/liteloader/download?version=" + version; + if (snapshot) { + try { + version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/")); + url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar"; + } catch (Exception ignore) { + } + } + + versions.put(key, new LiteLoaderRemoteVersion(gameVersion, + version, Collections.singletonList(url), + v.getTweakClass(), v.getLibraries() + )); + } + } + + @Override + public CompletableFuture refreshAsync() { + return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class) + .thenAcceptAsync(root -> { + lock.writeLock().lock(); + + try { + versions.clear(); + + for (Map.Entry entry : root.getVersions().entrySet()) { + String gameVersion = entry.getKey(); + LiteLoaderGameVersions liteLoader = entry.getValue(); + + String gg = VersionNumber.normalize(gameVersion); + doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false); + doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true); + } + } finally { + lock.writeLock().unlock(); + } + }); + } + + public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json"; + + private static String getLatestSnapshotVersion(String repo) throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(repo + "maven-metadata.xml"); + Element r = doc.getDocumentElement(); + Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0); + Node timestamp = snapshot.getElementsByTagName("timestamp").item(0); + Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0); + return timestamp.getTextContent() + "-" + buildNumber.getTextContent(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderBranch.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderBranch.java new file mode 100644 index 00000000..50810a15 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderBranch.java @@ -0,0 +1,39 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.game.Library; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public final class LiteLoaderBranch { + + @SerializedName("libraries") + private final Collection libraries; + + @SerializedName("com.mumfrey:liteloader") + private final Map liteLoader; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public LiteLoaderBranch() { + this(Collections.emptySet(), Collections.emptyMap()); + } + + public LiteLoaderBranch(Collection libraries, Map liteLoader) { + this.libraries = libraries; + this.liteLoader = liteLoader; + } + + public Collection getLibraries() { + return Collections.unmodifiableCollection(libraries); + } + + public Map getLiteLoader() { + return Collections.unmodifiableMap(liteLoader); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderGameVersions.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderGameVersions.java new file mode 100644 index 00000000..95b62485 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderGameVersions.java @@ -0,0 +1,42 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.google.gson.annotations.SerializedName; + +public final class LiteLoaderGameVersions { + + @SerializedName("repo") + private final LiteLoaderRepository repoitory; + + @SerializedName("artefacts") + private final LiteLoaderBranch artifacts; + + @SerializedName("snapshots") + private final LiteLoaderBranch snapshots; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public LiteLoaderGameVersions() { + this(null, null, null); + } + + public LiteLoaderGameVersions(LiteLoaderRepository repoitory, LiteLoaderBranch artifacts, LiteLoaderBranch snapshots) { + this.repoitory = repoitory; + this.artifacts = artifacts; + this.snapshots = snapshots; + } + + public LiteLoaderRepository getRepoitory() { + return repoitory; + } + + public LiteLoaderBranch getArtifacts() { + return artifacts; + } + + public LiteLoaderBranch getSnapshots() { + return snapshots; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderInstallTask.java new file mode 100644 index 00000000..880ab36d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderInstallTask.java @@ -0,0 +1,66 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.game.Arguments; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.LibrariesDownloadInfo; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.LibraryDownloadInfo; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Lang; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Note: LiteLoader must be installed after Forge. + */ +public final class LiteLoaderInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final LiteLoaderRemoteVersion remote; + private final List> dependents = new ArrayList<>(); + private final List> dependencies = new ArrayList<>(1); + + public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) { + this.dependencyManager = dependencyManager; + this.version = version; + this.remote = remoteVersion; + } + + @Override + public Collection> getDependents() { + return dependents; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public void execute() { + Library library = new Library( + new Artifact("com.mumfrey", "liteloader", remote.getSelfVersion()), + "http://dl.liteloader.com/versions/", + new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrls().get(0))) + ); + + setResult(new Version(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), + remote.getSelfVersion(), + 60000, + new Arguments().addGameArguments("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"), + LibraryAnalyzer.LAUNCH_WRAPPER_MAIN, + Lang.merge(remote.getLibraries(), Collections.singleton(library))) + .setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration + ); + + dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderRemoteVersion.java new file mode 100644 index 00000000..7cd5a5ac --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderRemoteVersion.java @@ -0,0 +1,42 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; + +import java.util.Collection; +import java.util.List; + +public class LiteLoaderRemoteVersion extends RemoteVersion { + private final String tweakClass; + private final Collection libraries; + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param urls the installer or universal jar original URL. + */ + LiteLoaderRemoteVersion(String gameVersion, String selfVersion, List urls, String tweakClass, Collection libraries) { + super(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), gameVersion, selfVersion, null, urls); + + this.tweakClass = tweakClass; + this.libraries = libraries; + } + + public Collection getLibraries() { + return libraries; + } + + public String getTweakClass() { + return tweakClass; + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new LiteLoaderInstallTask(dependencyManager, baseVersion, this); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderRepository.java new file mode 100644 index 00000000..1844412f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderRepository.java @@ -0,0 +1,50 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.google.gson.annotations.SerializedName; + +public final class LiteLoaderRepository { + + @SerializedName("stream") + private final String stream; + + @SerializedName("type") + private final String type; + + @SerializedName("url") + private final String url; + + @SerializedName("classifier") + private final String classifier; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public LiteLoaderRepository() { + this("", "", "", ""); + } + + public LiteLoaderRepository(String stream, String type, String url, String classifier) { + this.stream = stream; + this.type = type; + this.url = url; + this.classifier = classifier; + } + + public String getStream() { + return stream; + } + + public String getType() { + return type; + } + + public String getUrl() { + return url; + } + + public String getClassifier() { + return classifier; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersion.java new file mode 100644 index 00000000..08d38973 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersion.java @@ -0,0 +1,63 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.tungsten.fclcore.game.Library; + +import java.util.Collection; +import java.util.Collections; + +public final class LiteLoaderVersion { + private final String tweakClass; + private final String file; + private final String version; + private final String md5; + private final String timestamp; + private final int lastSuccessfulBuild; + private final Collection libraries; + + /** + * No-arg constructor for Gson. + */ + @SuppressWarnings("unused") + public LiteLoaderVersion() { + this("", "", "", "", "", 0, Collections.emptySet()); + } + + public LiteLoaderVersion(String tweakClass, String file, String version, String md5, String timestamp, int lastSuccessfulBuild, Collection libraries) { + this.tweakClass = tweakClass; + this.file = file; + this.version = version; + this.md5 = md5; + this.timestamp = timestamp; + this.lastSuccessfulBuild = lastSuccessfulBuild; + this.libraries = libraries; + } + + public String getTweakClass() { + return tweakClass; + } + + public String getFile() { + return file; + } + + public String getVersion() { + return version; + } + + public String getMd5() { + return md5; + } + + public String getTimestamp() { + return timestamp; + } + + public int getLastSuccessfulBuild() { + return lastSuccessfulBuild; + } + + public Collection getLibraries() { + return Collections.unmodifiableCollection(libraries); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionList.java new file mode 100644 index 00000000..9c0fb52f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionList.java @@ -0,0 +1,93 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.tungsten.fclcore.download.DownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.io.HttpRequest; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public final class LiteLoaderVersionList extends VersionList { + + private final DownloadProvider downloadProvider; + + public LiteLoaderVersionList(DownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return false; + } + + private void doBranch(String key, String gameVersion, LiteLoaderRepository repository, LiteLoaderBranch branch, boolean snapshot) { + if (branch == null || repository == null) + return; + + for (Map.Entry entry : branch.getLiteLoader().entrySet()) { + String branchName = entry.getKey(); + LiteLoaderVersion v = entry.getValue(); + if ("latest".equals(branchName)) + continue; + + String version = v.getVersion(); + String url = repository.getUrl() + "com/mumfrey/liteloader/" + gameVersion + "/" + v.getFile(); + if (snapshot) { + try { + version = version.replace("SNAPSHOT", getLatestSnapshotVersion(repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/")); + url = repository.getUrl() + "com/mumfrey/liteloader/" + v.getVersion() + "/liteloader-" + version + "-release.jar"; + } catch (Exception ignore) { + } + } + + versions.put(key, new LiteLoaderRemoteVersion(gameVersion, + version, Collections.singletonList(url), + v.getTweakClass(), v.getLibraries() + )); + } + } + + @Override + public CompletableFuture refreshAsync() { + return HttpRequest.GET(downloadProvider.injectURL(LITELOADER_LIST)).getJsonAsync(LiteLoaderVersionsRoot.class) + .thenAcceptAsync(root -> { + lock.writeLock().lock(); + + try { + versions.clear(); + + for (Map.Entry entry : root.getVersions().entrySet()) { + String gameVersion = entry.getKey(); + LiteLoaderGameVersions liteLoader = entry.getValue(); + + String gg = VersionNumber.normalize(gameVersion); + doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getArtifacts(), false); + doBranch(gg, gameVersion, liteLoader.getRepoitory(), liteLoader.getSnapshots(), true); + } + } finally { + lock.writeLock().unlock(); + } + }); + } + + public static final String LITELOADER_LIST = "http://dl.liteloader.com/versions/versions.json"; + + private static String getLatestSnapshotVersion(String repo) throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(repo + "maven-metadata.xml"); + Element r = doc.getDocumentElement(); + Element snapshot = (Element) r.getElementsByTagName("snapshot").item(0); + Node timestamp = snapshot.getElementsByTagName("timestamp").item(0); + Node buildNumber = snapshot.getElementsByTagName("buildNumber").item(0); + return timestamp.getTextContent() + "-" + buildNumber.getTextContent(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionsMeta.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionsMeta.java new file mode 100644 index 00000000..1c6bfaf0 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionsMeta.java @@ -0,0 +1,38 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.google.gson.annotations.SerializedName; + +public final class LiteLoaderVersionsMeta { + + @SerializedName("description") + private final String description; + + @SerializedName("authors") + private final String authors; + + @SerializedName("url") + private final String url; + + public LiteLoaderVersionsMeta() { + this("", "", ""); + } + + public LiteLoaderVersionsMeta(String description, String authors, String url) { + this.description = description; + this.authors = authors; + this.url = url; + } + + public String getDescription() { + return description; + } + + public String getAuthors() { + return authors; + } + + public String getUrl() { + return url; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionsRoot.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionsRoot.java new file mode 100644 index 00000000..5fb588b1 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/liteloader/LiteLoaderVersionsRoot.java @@ -0,0 +1,33 @@ +package com.tungsten.fclcore.download.liteloader; + +import com.google.gson.annotations.SerializedName; + +import java.util.Collections; +import java.util.Map; + +public final class LiteLoaderVersionsRoot { + + @SerializedName("versions") + private final Map versions; + + @SerializedName("meta") + private final LiteLoaderVersionsMeta meta; + + public LiteLoaderVersionsRoot() { + this(Collections.emptyMap(), null); + } + + public LiteLoaderVersionsRoot(Map versions, LiteLoaderVersionsMeta meta) { + this.versions = versions; + this.meta = meta; + } + + public Map getVersions() { + return Collections.unmodifiableMap(versions); + } + + public LiteLoaderVersionsMeta getMeta() { + return meta; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineBMCLVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineBMCLVersionList.java new file mode 100644 index 00000000..8e2dd5b4 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineBMCLVersionList.java @@ -0,0 +1,60 @@ +package com.tungsten.fclcore.download.optifine; + +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.io.HttpRequest; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public final class OptiFineBMCLVersionList extends VersionList { + private final String apiRoot; + + /** + * @param apiRoot API Root of BMCLAPI implementations + */ + public OptiFineBMCLVersionList(String apiRoot) { + this.apiRoot = apiRoot; + } + + @Override + public boolean hasType() { + return true; + } + + @Override + public CompletableFuture refreshAsync() { + return HttpRequest.GET(apiRoot + "/optifine/versionlist").>getJsonAsync(new TypeToken>() { + }.getType()) + .thenAcceptAsync(root -> { + lock.writeLock().lock(); + + try { + versions.clear(); + Set duplicates = new HashSet<>(); + for (OptiFineVersion element : root) { + String version = element.getType() + "_" + element.getPatch(); + String mirror = "https://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch(); + if (!duplicates.add(mirror)) + continue; + + boolean isPre = element.getPatch() != null && (element.getPatch().startsWith("pre") || element.getPatch().startsWith("alpha")); + + if (StringUtils.isBlank(element.getGameVersion())) + continue; + + String gameVersion = VersionNumber.normalize(element.getGameVersion()); + versions.put(gameVersion, new OptiFineRemoteVersion(gameVersion, version, Collections.singletonList(mirror), isPre)); + } + } finally { + lock.writeLock().unlock(); + } + }); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineInstallTask.java new file mode 100644 index 00000000..233e81d6 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineInstallTask.java @@ -0,0 +1,237 @@ +package com.tungsten.fclcore.download.optifine; + +import static com.tungsten.fclcore.util.Lang.getOrDefault; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.UnsupportedInstallationException; +import com.tungsten.fclcore.download.VersionMismatchException; +import com.tungsten.fclcore.game.Arguments; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.game.LibrariesDownloadInfo; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.LibraryDownloadInfo; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.platform.CommandBuilder; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import org.apache.commons.lang3.SystemUtils; +import org.jenkinsci.constant_pool_scanner.ConstantPool; +import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner; +import org.jenkinsci.constant_pool_scanner.ConstantType; +import org.jenkinsci.constant_pool_scanner.Utf8Constant; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * Note: OptiFine should be installed in the end. + */ +public final class OptiFineInstallTask extends Task { + + private final DefaultGameRepository gameRepository; + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final OptiFineRemoteVersion remote; + private final Path installer; + private final List> dependents = new ArrayList<>(0); + private final List> dependencies = new ArrayList<>(1); + private Path dest; + + private final Library optiFineLibrary; + private final Library optiFineInstallerLibrary; + + public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) { + this(dependencyManager, version, remoteVersion, null); + } + + public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion, Path installer) { + this.dependencyManager = dependencyManager; + this.gameRepository = dependencyManager.getGameRepository(); + this.version = version; + this.remote = remoteVersion; + this.installer = installer; + + String mavenVersion = remote.getGameVersion() + "_" + remote.getSelfVersion(); + + optiFineLibrary = new Library(new Artifact("optifine", "OptiFine", mavenVersion)); + + optiFineInstallerLibrary = new Library( + new Artifact("optifine", "OptiFine", mavenVersion, "installer"), null, + new LibrariesDownloadInfo(new LibraryDownloadInfo( + "optifine/OptiFine/" + mavenVersion + "/OptiFine-" + mavenVersion + "-installer.jar", + remote.getUrls().get(0).toString())) + ); + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() throws Exception { + dest = Files.createTempFile("optifine-installer", ".jar"); + + if (installer == null) { + FileDownloadTask task = new FileDownloadTask( + dependencyManager.getDownloadProvider().injectURLsWithCandidates(remote.getUrls()), + dest.toFile(), null); + task.setCacheRepository(dependencyManager.getCacheRepository()); + task.setCaching(true); + dependents.add(task); + } else { + FileUtils.copyFile(installer, dest); + } + } + + @Override + public Collection> getDependents() { + return dependents; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() throws Exception { + String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); + if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && + !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) && + !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) && + !LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass)) + throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER); + + List libraries = new ArrayList<>(4); + libraries.add(optiFineLibrary); + + FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath()); + + // Install launch wrapper modified by OptiFine + boolean hasLaunchWrapper = false; + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) { + if (Files.exists(fs.getPath("optifine/Patcher.class"))) { + String[] command = { + JavaVersion.fromCurrentEnvironment().getBinary().toString(), + "-cp", + dest.toString(), + "optifine.Patcher", + gameRepository.getVersionJar(version).getAbsolutePath(), + dest.toString(), + gameRepository.getLibraryFile(version, optiFineLibrary).toString() + }; + int exitCode = SystemUtils.callExternalProcess(command); + if (exitCode != 0) + throw new IOException("OptiFine patcher failed, command: " + new CommandBuilder().addAll(Arrays.asList(command))); + } else { + FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineLibrary).toPath()); + } + + Path launchWrapper2 = fs.getPath("launchwrapper-2.0.jar"); + if (Files.exists(launchWrapper2)) { + Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper", "2.0")); + File launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper); + FileUtils.makeDirectory(launchWrapperFile.getAbsoluteFile().getParentFile()); + FileUtils.copyFile(launchWrapper2, launchWrapperFile.toPath()); + hasLaunchWrapper = true; + libraries.add(launchWrapper); + } + + Path launchWrapperVersionText = fs.getPath("launchwrapper-of.txt"); + if (Files.exists(launchWrapperVersionText)) { + String launchWrapperVersion = FileUtils.readText(launchWrapperVersionText).trim(); + Path launchWrapperJar = fs.getPath("launchwrapper-of-" + launchWrapperVersion + ".jar"); + + Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper-of", launchWrapperVersion)); + + if (Files.exists(launchWrapperJar)) { + File launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper); + FileUtils.makeDirectory(launchWrapperFile.getAbsoluteFile().getParentFile()); + FileUtils.copyFile(launchWrapperJar, launchWrapperFile.toPath()); + + hasLaunchWrapper = true; + libraries.add(launchWrapper); + } + } + + Path buildofText = fs.getPath("buildof.txt"); + if (Files.exists(buildofText)) { + String buildof = FileUtils.readText(buildofText).trim(); + VersionNumber buildofVer = VersionNumber.asVersion(buildof); + + if (LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass)) { + // OptiFine H1 Pre2+ is compatible with Forge 1.17 + if (buildofVer.compareTo(VersionNumber.asVersion("20210924-190833")) < 0) { + throw new UnsupportedInstallationException(UnsupportedInstallationException.FORGE_1_17_OPTIFINE_H1_PRE2); + } + } + } + } + + if (!hasLaunchWrapper) { + libraries.add(new Library(new Artifact("net.minecraft", "launchwrapper", "1.12"))); + } + + setResult(new Version( + LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), + remote.getSelfVersion(), + 10000, + new Arguments().addGameArguments("--tweakClass", "optifine.OptiFineTweaker"), + LibraryAnalyzer.LAUNCH_WRAPPER_MAIN, + libraries + )); + + dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + } + + /** + * Install OptiFine library from existing local file. + * + * @param dependencyManager game repository + * @param version version.json + * @param installer the OptiFine installer + * @return the task to install library + * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. + * @throws VersionMismatchException if required game version of installer does not match the actual one. + */ + public static Task install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); + if (!gameVersion.isPresent()) throw new IOException(); + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { + Path configClass = fs.getPath("Config.class"); + if (!Files.exists(configClass)) configClass = fs.getPath("net/optifine/Config.class"); + if (!Files.exists(configClass)) configClass = fs.getPath("notch/net/optifine/Config.class"); + if (!Files.exists(configClass)) throw new IOException("Unrecognized installer"); + ConstantPool pool = ConstantPoolScanner.parse(Files.readAllBytes(configClass), ConstantType.UTF8); + List constants = new ArrayList<>(); + pool.list(Utf8Constant.class).forEach(utf8 -> constants.add(utf8.get())); + String mcVersion = getOrDefault(constants, constants.indexOf("MC_VERSION") + 1, null); + String ofEdition = getOrDefault(constants, constants.indexOf("OF_EDITION") + 1, null); + String ofRelease = getOrDefault(constants, constants.indexOf("OF_RELEASE") + 1, null); + + if (mcVersion == null || ofEdition == null || ofRelease == null) + throw new IOException("Unrecognized OptiFine installer"); + + if (!mcVersion.equals(gameVersion.get())) + throw new VersionMismatchException(mcVersion, gameVersion.get()); + + return new OptiFineInstallTask(dependencyManager, version, + new OptiFineRemoteVersion(mcVersion, ofEdition + "_" + ofRelease, Collections.singletonList(""), false), installer); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineRemoteVersion.java new file mode 100644 index 00000000..c251206f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineRemoteVersion.java @@ -0,0 +1,21 @@ +package com.tungsten.fclcore.download.optifine; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; + +import java.util.List; + +public class OptiFineRemoteVersion extends RemoteVersion { + + public OptiFineRemoteVersion(String gameVersion, String selfVersion, List urls, boolean snapshot) { + super(LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), gameVersion, selfVersion, null, snapshot ? Type.SNAPSHOT : Type.RELEASE, urls); + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new OptiFineInstallTask(dependencyManager, baseVersion, this); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineVersion.java new file mode 100644 index 00000000..76fcac03 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/optifine/OptiFineVersion.java @@ -0,0 +1,69 @@ +package com.tungsten.fclcore.download.optifine; + +import com.google.gson.annotations.SerializedName; + +public final class OptiFineVersion { + + @SerializedName("dl") + private final String downloadLink; + + @SerializedName("ver") + private final String version; + + @SerializedName("date") + private final String date; + + @SerializedName("type") + private final String type; + + @SerializedName("patch") + private final String patch; + + @SerializedName("mirror") + private final String mirror; + + @SerializedName("mcversion") + private final String gameVersion; + + public OptiFineVersion() { + this(null, null, null, null, null, null, null); + } + + public OptiFineVersion(String downloadLink, String version, String date, String type, String patch, String mirror, String gameVersion) { + this.downloadLink = downloadLink; + this.version = version; + this.date = date; + this.type = type; + this.patch = patch; + this.mirror = mirror; + this.gameVersion = gameVersion; + } + + public String getDownloadLink() { + return downloadLink; + } + + public String getVersion() { + return version; + } + + public String getDate() { + return date; + } + + public String getType() { + return type; + } + + public String getPatch() { + return patch; + } + + public String getMirror() { + return mirror; + } + + public String getGameVersion() { + return gameVersion; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIInstallTask.java new file mode 100644 index 00000000..29ac0a43 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIInstallTask.java @@ -0,0 +1,48 @@ +package com.tungsten.fclcore.download.quilt; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Note: Quilt should be installed first. + */ +public final class QuiltAPIInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final QuiltAPIRemoteVersion remote; + private final List> dependencies = new ArrayList<>(1); + + public QuiltAPIInstallTask(DefaultDependencyManager dependencyManager, Version version, QuiltAPIRemoteVersion remoteVersion) { + this.dependencyManager = dependencyManager; + this.version = version; + this.remote = remoteVersion; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() throws IOException { + dependencies.add(new FileDownloadTask( + new URL(remote.getVersion().getFile().getUrl()), + dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("quilt-api-" + remote.getVersion().getVersion() + ".jar").toFile(), + remote.getVersion().getFile().getIntegrityCheck()) + ); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIRemoteVersion.java new file mode 100644 index 00000000..ca891edb --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIRemoteVersion.java @@ -0,0 +1,50 @@ +package com.tungsten.fclcore.download.quilt; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.task.Task; + +import java.util.Date; +import java.util.List; + +public class QuiltAPIRemoteVersion extends RemoteVersion { + private final String fullVersion; + private final RemoteMod.Version version; + + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param urls the installer or universal jar original URL. + */ + QuiltAPIRemoteVersion(String gameVersion, String selfVersion, String fullVersion, Date datePublished, RemoteMod.Version version, List urls) { + super(LibraryAnalyzer.LibraryType.QUILT_API.getPatchId(), gameVersion, selfVersion, datePublished, urls); + + this.fullVersion = fullVersion; + this.version = version; + } + + @Override + public String getFullVersion() { + return fullVersion; + } + + public RemoteMod.Version getVersion() { + return version; + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new QuiltAPIInstallTask(dependencyManager, baseVersion, this); + } + + @Override + public int compareTo(RemoteVersion o) { + if (!(o instanceof QuiltAPIRemoteVersion)) return 0; + return -this.getReleaseDate().compareTo(o.getReleaseDate()); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIVersionList.java new file mode 100644 index 00000000..570fa0cb --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltAPIVersionList.java @@ -0,0 +1,36 @@ +package com.tungsten.fclcore.download.quilt; + +import com.tungsten.fclcore.download.DownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.mod.modrinth.ModrinthRemoteModRepository; +import com.tungsten.fclcore.util.Lang; + +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +public class QuiltAPIVersionList extends VersionList { + + private final DownloadProvider downloadProvider; + + public QuiltAPIVersionList(DownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return false; + } + + @Override + public CompletableFuture refreshAsync() { + return CompletableFuture.runAsync(wrap(() -> { + for (RemoteMod.Version modVersion : Lang.toIterable(ModrinthRemoteModRepository.MODS.getRemoteVersionsById("qsl"))) { + for (String gameVersion : modVersion.getGameVersions()) { + versions.put(gameVersion, new QuiltAPIRemoteVersion(gameVersion, modVersion.getVersion(), modVersion.getName(), modVersion.getDatePublished(), modVersion, + Collections.singletonList(modVersion.getFile().getUrl()))); + } + } + })); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltInstallTask.java new file mode 100644 index 00000000..f8013e07 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltInstallTask.java @@ -0,0 +1,202 @@ +package com.tungsten.fclcore.download.quilt; + +import static com.tungsten.fclcore.download.UnsupportedInstallationException.FABRIC_NOT_COMPATIBLE_WITH_FORGE; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.UnsupportedInstallationException; +import com.tungsten.fclcore.game.Arguments; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.GetTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.util.*; + +/** + * Note: Quilt should be installed first. + */ +public final class QuiltInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final Version version; + private final QuiltRemoteVersion remote; + private final GetTask launchMetaTask; + private final List> dependencies = new ArrayList<>(1); + + public QuiltInstallTask(DefaultDependencyManager dependencyManager, Version version, QuiltRemoteVersion remoteVersion) { + this.dependencyManager = dependencyManager; + this.version = version; + this.remote = remoteVersion; + + launchMetaTask = new GetTask(dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls())); + launchMetaTask.setCacheRepository(dependencyManager.getCacheRepository()); + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() throws Exception { + if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).getMainClass())) + throw new UnsupportedInstallationException(FABRIC_NOT_COMPATIBLE_WITH_FORGE); + } + + @Override + public Collection> getDependents() { + return Collections.singleton(launchMetaTask); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() { + setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), QuiltInfo.class), remote.getGameVersion(), remote.getSelfVersion())); + + dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + } + + private Version getPatch(QuiltInfo quiltInfo, String gameVersion, String loaderVersion) { + JsonObject launcherMeta = quiltInfo.launcherMeta; + Arguments arguments = new Arguments(); + + String mainClass; + if (!launcherMeta.get("mainClass").isJsonObject()) { + mainClass = launcherMeta.get("mainClass").getAsString(); + } else { + mainClass = launcherMeta.get("mainClass").getAsJsonObject().get("client").getAsString(); + } + + if (launcherMeta.has("launchwrapper")) { + String clientTweaker = launcherMeta.get("launchwrapper").getAsJsonObject().get("tweakers").getAsJsonObject().get("client").getAsJsonArray().get(0).getAsString(); + arguments = arguments.addGameArguments("--tweakClass", clientTweaker); + } + + JsonObject librariesObject = launcherMeta.getAsJsonObject("libraries"); + List libraries = new ArrayList<>(); + + // "common, server" is hard coded in fabric installer. + // Don't know the purpose of ignoring client libraries. + for (String side : new String[]{"common", "server"}) { + for (JsonElement element : librariesObject.getAsJsonArray(side)) { + libraries.add(JsonUtils.GSON.fromJson(element, Library.class)); + } + } + + // libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.hashed.maven), getMavenRepositoryByGroup(quiltInfo.hashed.maven), null)); + libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.intermediary.maven), getMavenRepositoryByGroup(quiltInfo.intermediary.maven), null)); + libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.loader.maven), getMavenRepositoryByGroup(quiltInfo.loader.maven), null)); + + return new Version(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), loaderVersion, 30000, arguments, mainClass, libraries); + } + + private static String getMavenRepositoryByGroup(String maven) { + Artifact artifact = Artifact.fromDescriptor(maven); + switch (artifact.getGroup()) { + case "net.fabricmc": + return "https://maven.fabricmc.net/"; + case "org.quiltmc": + return "https://maven.quiltmc.org/repository/release/"; + default: + return "https://maven.fabricmc.net/"; + } + } + + public static class QuiltInfo { + private final LoaderInfo loader; + private final IntermediaryInfo hashed; + private final IntermediaryInfo intermediary; + private final JsonObject launcherMeta; + + public QuiltInfo(LoaderInfo loader, IntermediaryInfo hashed, IntermediaryInfo intermediary, JsonObject launcherMeta) { + this.loader = loader; + this.hashed = hashed; + this.intermediary = intermediary; + this.launcherMeta = launcherMeta; + } + + public LoaderInfo getLoader() { + return loader; + } + + public IntermediaryInfo getHashed() { + return hashed; + } + + public IntermediaryInfo getIntermediary() { + return intermediary; + } + + public JsonObject getLauncherMeta() { + return launcherMeta; + } + } + + public static class LoaderInfo { + private final String separator; + private final int build; + private final String maven; + private final String version; + private final boolean stable; + + public LoaderInfo(String separator, int build, String maven, String version, boolean stable) { + this.separator = separator; + this.build = build; + this.maven = maven; + this.version = version; + this.stable = stable; + } + + public String getSeparator() { + return separator; + } + + public int getBuild() { + return build; + } + + public String getMaven() { + return maven; + } + + public String getVersion() { + return version; + } + + public boolean isStable() { + return stable; + } + } + + public static class IntermediaryInfo { + private final String maven; + private final String version; + + public IntermediaryInfo(String maven, String version) { + this.maven = maven; + this.version = version; + } + + public String getMaven() { + return maven; + } + + public String getVersion() { + return version; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltRemoteVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltRemoteVersion.java new file mode 100644 index 00000000..58363778 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltRemoteVersion.java @@ -0,0 +1,27 @@ +package com.tungsten.fclcore.download.quilt; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.download.RemoteVersion; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.task.Task; + +import java.util.List; + +public class QuiltRemoteVersion extends RemoteVersion { + /** + * Constructor. + * + * @param gameVersion the Minecraft version that this remote version suits. + * @param selfVersion the version string of the remote version. + * @param urls the installer or universal jar original URL. + */ + QuiltRemoteVersion(String gameVersion, String selfVersion, List urls) { + super(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), gameVersion, selfVersion, null, urls); + } + + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + return new QuiltInstallTask(dependencyManager, baseVersion, this); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltVersionList.java b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltVersionList.java new file mode 100644 index 00000000..cfccca2b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/download/quilt/QuiltVersionList.java @@ -0,0 +1,90 @@ +package com.tungsten.fclcore.download.quilt; + +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DownloadProvider; +import com.tungsten.fclcore.download.VersionList; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public final class QuiltVersionList extends VersionList { + private final DownloadProvider downloadProvider; + + public QuiltVersionList(DownloadProvider downloadProvider) { + this.downloadProvider = downloadProvider; + } + + @Override + public boolean hasType() { + return false; + } + + @Override + public CompletableFuture refreshAsync() { + return CompletableFuture.runAsync(wrap(() -> { + List gameVersions = getGameVersions(GAME_META_URL); + List loaderVersions = getGameVersions(LOADER_META_URL); + + lock.writeLock().lock(); + + try { + for (String gameVersion : gameVersions) + for (String loaderVersion : loaderVersions) + versions.put(gameVersion, new QuiltRemoteVersion(gameVersion, loaderVersion, + Collections.singletonList(getLaunchMetaUrl(gameVersion, loaderVersion)))); + } finally { + lock.writeLock().unlock(); + } + })); + } + + private static final String LOADER_META_URL = "https://meta.quiltmc.org/v3/versions/loader"; + private static final String GAME_META_URL = "https://meta.quiltmc.org/v3/versions/game"; + + private List getGameVersions(String metaUrl) throws IOException { + String json = NetworkUtils.doGet(NetworkUtils.toURL(downloadProvider.injectURL(metaUrl))); + return JsonUtils.GSON.>fromJson(json, new TypeToken>() { + }.getType()).stream().map(GameVersion::getVersion).collect(Collectors.toList()); + } + + private static String getLaunchMetaUrl(String gameVersion, String loaderVersion) { + return String.format("https://meta.quiltmc.org/v3/versions/loader/%s/%s", gameVersion, loaderVersion); + } + + private static class GameVersion { + private final String version; + private final String maven; + private final boolean stable; + + public GameVersion() { + this("", null, false); + } + + public GameVersion(String version, String maven, boolean stable) { + this.version = version; + this.maven = maven; + this.stable = stable; + } + + public String getVersion() { + return version; + } + + @Nullable + public String getMaven() { + return maven; + } + + public boolean isStable() { + return stable; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/Event.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/Event.java new file mode 100644 index 00000000..6c3d4705 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/Event.java @@ -0,0 +1,96 @@ +package com.tungsten.fclcore.event; + +import com.tungsten.fclcore.util.ToStringBuilder; + +import java.util.Objects; + +public class Event { + + /** + * The object on which the Event initially occurred. + */ + protected final transient Object source; + + /** + * Constructs a prototypical Event. + * + * @param source The object on which the Event initially occurred. + * @throws NullPointerException if source is null. + */ + public Event(Object source) { + Objects.requireNonNull(source); + + this.source = source; + } + + /** + * The object on which the Event initially occurred. + * + * @return The object on which the Event initially occurred. + */ + public Object getSource() { + return source; + } + + /** + * Returns a String representation of this Event. + * + * @return A a String representation of this Event. + */ + public String toString() { + return new ToStringBuilder(this).append("source", source).toString(); + } + + private boolean canceled; + + /** + * true if this event is canceled. + * + * @throws UnsupportedOperationException if trying to cancel a non-cancelable event. + */ + public final boolean isCanceled() { + return canceled; + } + + /** + * @param canceled new value + * @throws UnsupportedOperationException if trying to cancel a non-cancelable event. + */ + public final void setCanceled(boolean canceled) { + if (!isCancelable()) + throw new UnsupportedOperationException("Attempted to cancel a non-cancelable event: " + getClass()); + this.canceled = canceled; + } + + /** + * true if this Event this cancelable. + */ + public boolean isCancelable() { + return false; + } + + public boolean hasResult() { + return false; + } + + private Result result = Result.DEFAULT; + + /** + * Retutns the value set as the result of this event + */ + public Result getResult() { + return result; + } + + public void setResult(Result result) { + if (!hasResult()) + throw new UnsupportedOperationException("Attempted to set result on a no result event: " + this.getClass() + " of type."); + this.result = result; + } + + public enum Result { + DENY, + DEFAULT, + ALLOW + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/EventBus.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/EventBus.java new file mode 100644 index 00000000..ec1cad89 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/EventBus.java @@ -0,0 +1,25 @@ +package com.tungsten.fclcore.event; + +import com.tungsten.fclcore.util.Logging; + +import java.util.concurrent.ConcurrentHashMap; + +public final class EventBus { + + private final ConcurrentHashMap, EventManager> events = new ConcurrentHashMap<>(); + + @SuppressWarnings("unchecked") + public EventManager channel(Class clazz) { + events.putIfAbsent(clazz, new EventManager<>()); + return (EventManager) events.get(clazz); + } + + @SuppressWarnings("unchecked") + public Event.Result fireEvent(Event obj) { + Logging.LOG.info(obj + " gets fired"); + + return channel((Class) obj.getClass()).fireEvent(obj); + } + + public static final EventBus EVENT_BUS = new EventBus(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/EventManager.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/EventManager.java new file mode 100644 index 00000000..de4a8d73 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/EventManager.java @@ -0,0 +1,75 @@ +package com.tungsten.fclcore.event; + +import com.tungsten.fclcore.util.SimpleMultimap; + +import java.lang.ref.WeakReference; +import java.util.EnumMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Consumer; + +public final class EventManager { + + private final SimpleMultimap> handlers + = new SimpleMultimap<>(() -> new EnumMap<>(EventPriority.class), CopyOnWriteArraySet::new); + + public Consumer registerWeak(Consumer consumer) { + register(new WeakListener(consumer)); + return consumer; + } + + public Consumer registerWeak(Consumer consumer, EventPriority priority) { + register(new WeakListener(consumer), priority); + return consumer; + } + + public void register(Consumer consumer) { + register(consumer, EventPriority.NORMAL); + } + + public synchronized void register(Consumer consumer, EventPriority priority) { + if (!handlers.get(priority).contains(consumer)) + handlers.put(priority, consumer); + } + + public void register(Runnable runnable) { + register(t -> runnable.run()); + } + + public void register(Runnable runnable, EventPriority priority) { + register(t -> runnable.run(), priority); + } + + public synchronized Event.Result fireEvent(T event) { + for (EventPriority priority : EventPriority.values()) { + for (Consumer handler : handlers.get(priority)) + handler.accept(event); + } + + if (event.hasResult()) + return event.getResult(); + else + return Event.Result.DEFAULT; + } + + public synchronized void unregister(Consumer consumer) { + handlers.removeValue(consumer); + } + + private class WeakListener implements Consumer { + private final WeakReference> ref; + + public WeakListener(Consumer listener) { + this.ref = new WeakReference<>(listener); + } + + @Override + public void accept(T t) { + Consumer listener = ref.get(); + if (listener == null) { + unregister(this); + } else { + listener.accept(t); + } + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/EventPriority.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/EventPriority.java new file mode 100644 index 00000000..3405db31 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/EventPriority.java @@ -0,0 +1,9 @@ +package com.tungsten.fclcore.event; + +public enum EventPriority { + HIGHEST, + HIGH, + NORMAL, + LOW, + LOWEST +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/FailedEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/FailedEvent.java new file mode 100644 index 00000000..44dbf339 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/FailedEvent.java @@ -0,0 +1,26 @@ +package com.tungsten.fclcore.event; + +public class FailedEvent extends Event { + + private final int failedTime; + private T newResult; + + public FailedEvent(Object source, int failedTime, T newResult) { + super(source); + this.failedTime = failedTime; + this.newResult = newResult; + } + + public int getFailedTime() { + return failedTime; + } + + public T getNewResult() { + return newResult; + } + + public void setNewResult(T newResult) { + this.newResult = newResult; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/GameJsonParseFailedEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/GameJsonParseFailedEvent.java new file mode 100644 index 00000000..c834a6ce --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/GameJsonParseFailedEvent.java @@ -0,0 +1,46 @@ +package com.tungsten.fclcore.event; + +import com.tungsten.fclcore.util.ToStringBuilder; + +import java.io.File; + +/** + * This event gets fired when json of a game version is malformed. You can do something here. + * auto making up for the missing json, don't forget to set result to {@link Event.Result#ALLOW}. + * and even asking for removing the redundant version folder. + * + * The result ALLOW means you have corrected the json. + */ +public final class GameJsonParseFailedEvent extends Event { + private final String version; + private final File jsonFile; + + /** + * + * @param source {@link com.tungsten.fclcore.game.DefaultGameRepository} + * @param jsonFile the minecraft.json file. + * @param version the version name + */ + public GameJsonParseFailedEvent(Object source, File jsonFile, String version) { + super(source); + this.version = version; + this.jsonFile = jsonFile; + } + + public File getJsonFile() { + return jsonFile; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("source", source) + .append("jsonFile", jsonFile) + .append("version", version) + .toString(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/LoadedOneVersionEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/LoadedOneVersionEvent.java new file mode 100644 index 00000000..a1fbd724 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/LoadedOneVersionEvent.java @@ -0,0 +1,41 @@ +package com.tungsten.fclcore.event; + +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.util.ToStringBuilder; + +/** + * This event gets fired when a minecraft version has been loaded. + *
+ * This event is fired on the {@link com.tungsten.fclcore.event.EventBus#EVENT_BUS} + */ +public final class LoadedOneVersionEvent extends Event { + + private final Version version; + + /** + * + * @param source {@link com.tungsten.fclcore.game.GameRepository} + * @param version the version id. + */ + public LoadedOneVersionEvent(Object source, Version version) { + super(source); + this.version = version; + } + + public Version getVersion() { + return version; + } + + @Override + public boolean hasResult() { + return true; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("source", source) + .append("version", version) + .toString(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/RefreshedVersionsEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/RefreshedVersionsEvent.java new file mode 100644 index 00000000..bd263105 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/RefreshedVersionsEvent.java @@ -0,0 +1,19 @@ +package com.tungsten.fclcore.event; + +/** + * This event gets fired when all the versions in .minecraft folder are loaded. + *
+ * This event is fired on the {@link com.tungsten.fclcore.event.EventBus#EVENT_BUS} + */ +public final class RefreshedVersionsEvent extends Event { + + /** + * Constructor. + * + * @param source {@link com.tungsten.fclcore.game.GameRepository} + */ + public RefreshedVersionsEvent(Object source) { + super(source); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/RefreshingVersionsEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/RefreshingVersionsEvent.java new file mode 100644 index 00000000..35f149d4 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/RefreshingVersionsEvent.java @@ -0,0 +1,23 @@ +package com.tungsten.fclcore.event; + +/** + * This event gets fired when loading versions in a .minecraft folder. + *
+ * This event is fired on the {@link com.tungsten.fclcore.event.EventBus#EVENT_BUS} + */ +public final class RefreshingVersionsEvent extends Event { + + /** + * Constructor. + * + * @param source {@link com.tungsten.fclcore.game.GameRepository} + */ + public RefreshingVersionsEvent(Object source) { + super(source); + } + + @Override + public boolean hasResult() { + return true; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/RemoveVersionEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/RemoveVersionEvent.java new file mode 100644 index 00000000..3efd4e3c --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/RemoveVersionEvent.java @@ -0,0 +1,40 @@ +package com.tungsten.fclcore.event; + +import com.tungsten.fclcore.util.ToStringBuilder; + +/** + * This event gets fired when a minecraft version is being removed. + *
+ * This event is fired on the {@link com.tungsten.fclcore.event.EventBus#EVENT_BUS} + */ +public class RemoveVersionEvent extends Event { + + private final String version; + + /** + * + * @param source {@link com.tungsten.fclcore.game.GameRepository} + * @param version the version id. + */ + public RemoveVersionEvent(Object source, String version) { + super(source); + this.version = version; + } + + public String getVersion() { + return version; + } + + @Override + public boolean hasResult() { + return true; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("source", source) + .append("version", version) + .toString(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/event/RenameVersionEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/event/RenameVersionEvent.java new file mode 100644 index 00000000..2ff31fc3 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/event/RenameVersionEvent.java @@ -0,0 +1,46 @@ +package com.tungsten.fclcore.event; + +import com.tungsten.fclcore.util.ToStringBuilder; + +/** + * This event gets fired when a minecraft version is being removed. + *
+ * This event is fired on the {@link com.tungsten.fclcore.event.EventBus#EVENT_BUS} + */ +public class RenameVersionEvent extends Event { + + private final String from, to; + + /** + * + * @param source {@link com.tungsten.fclcore.game.GameRepository} + * @param from the version id. + */ + public RenameVersionEvent(Object source, String from, String to) { + super(source); + this.from = from; + this.to = to; + } + + public String getFromVersion() { + return from; + } + + public String getToVersion() { + return to; + } + + @Override + public boolean hasResult() { + return true; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("source", source) + .append("from", from) + .append("to", to) + .toString(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/Argument.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/Argument.java new file mode 100644 index 00000000..2b554f5d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/Argument.java @@ -0,0 +1,31 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +@JsonAdapter(Argument.Deserializer.class) +public interface Argument extends Cloneable { + + /** + * Parse this argument in form: ${key name} or simply a string. + * + * @param keys the parse map + * @param features the map that contains some features such as 'is_demo_user', 'has_custom_resolution' + * @return parsed argument element, empty if this argument is ignored and will not be added. + */ + List toString(Map keys, Map features); + + class Deserializer implements JsonDeserializer { + @Override + public Argument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) + return new StringArgument(json.getAsString()); + else + return context.deserialize(json, RuledArgument.class); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/Arguments.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/Arguments.java new file mode 100644 index 00000000..be612d5d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/Arguments.java @@ -0,0 +1,116 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.Lang; + +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Collectors; + +public final class Arguments { + + @SerializedName("game") + private final List game; + @SerializedName("jvm") + private final List jvm; + + public Arguments() { + this(null, null); + } + + public Arguments(List game, List jvm) { + this.game = game; + this.jvm = jvm; + } + + @Nullable + public List getGame() { + return game == null ? null : Collections.unmodifiableList(game); + } + + public Arguments withGame(List game) { + return new Arguments(game, jvm); + } + + @Nullable + public List getJvm() { + return jvm == null ? null : Collections.unmodifiableList(jvm); + } + + public Arguments withJvm(List jvm) { + return new Arguments(game, jvm); + } + + public Arguments addGameArguments(String... gameArguments) { + return addGameArguments(Arrays.asList(gameArguments)); + } + + public Arguments addGameArguments(List gameArguments) { + List list = new ArrayList<>(); + for (String arg : gameArguments) { + list.add(new StringArgument(arg)); + } + return new Arguments(Lang.merge(getGame(), list), getJvm()); + } + + public Arguments addJVMArguments(String... jvmArguments) { + return addJVMArguments(Arrays.asList(jvmArguments)); + } + + public Arguments addJVMArguments(List jvmArguments) { + List list = new ArrayList<>(); + for (String arg : jvmArguments) { + list.add(new StringArgument(arg)); + } + return new Arguments(getGame(), Lang.merge(getJvm(), list)); + } + + public static Arguments merge(Arguments a, Arguments b) { + if (a == null) + return b; + else if (b == null) + return a; + else + return new Arguments( + a.game == null && b.game == null ? null : Lang.merge(a.game, b.game), + a.jvm == null && b.jvm == null ? null : Lang.merge(a.jvm, b.jvm)); + } + + public static List parseStringArguments(List arguments, Map keys) { + List list = new ArrayList<>(); + for (String arg : arguments) { + list.addAll(new StringArgument(arg).toString(keys, Collections.emptyMap())); + } + return list; + } + + public static List parseArguments(List arguments, Map keys) { + return parseArguments(arguments, keys, Collections.emptyMap()); + } + + public static List parseArguments(List arguments, Map keys, Map features) { + List list = new ArrayList<>(); + for (Argument arg : arguments) { + list.addAll(arg.toString(keys, Collections.emptyMap())); + } + return list; + } + + public static final List DEFAULT_JVM_ARGUMENTS; + public static final List DEFAULT_GAME_ARGUMENTS; + + static { + List jvm = new ArrayList<>(6); + jvm.add(new StringArgument("-Djava.library.path=${natives_directory}")); + jvm.add(new StringArgument("-Dminecraft.launcher.brand=${launcher_name}")); + jvm.add(new StringArgument("-Dminecraft.launcher.version=${launcher_version}")); + jvm.add(new StringArgument("-cp")); + jvm.add(new StringArgument("${classpath}")); + DEFAULT_JVM_ARGUMENTS = Collections.unmodifiableList(jvm); + + List game = new ArrayList<>(1); + game.add(new RuledArgument(Collections.singletonList(new CompatibilityRule(CompatibilityRule.Action.ALLOW, null, Collections.singletonMap("has_custom_resolution", true))), Arrays.asList("--width", "${resolution_width}", "--height", "${resolution_height}"))); + DEFAULT_GAME_ARGUMENTS = Collections.unmodifiableList(game); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/Artifact.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/Artifact.java new file mode 100644 index 00000000..4ba59cae --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/Artifact.java @@ -0,0 +1,123 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.annotations.JsonAdapter; + +import java.lang.reflect.Type; + +@JsonAdapter(Artifact.Serializer.class) +public final class Artifact { + + private final String group; + private final String name; + private final String version; + private final String classifier; + private final String extension; + + private final String descriptor; + private final String fileName; + private final String path; + + public Artifact(String group, String name, String version) { + this(group, name, version, null); + } + + public Artifact(String group, String name, String version, String classifier) { + this(group, name, version, classifier, null); + } + + public Artifact(String group, String name, String version, String classifier, String extension) { + this.group = group; + this.name = name; + this.version = version; + this.classifier = classifier; + this.extension = extension == null ? "jar" : extension; + + String fileName = this.name + "-" + this.version; + if (classifier != null) fileName += "-" + this.classifier; + this.fileName = fileName + "." + this.extension; + this.path = String.format("%s/%s/%s/%s", this.group.replace(".", "/"), this.name, this.version, this.fileName); + + // group:name:version:classifier@extension + String descriptor = String.format("%s:%s:%s", group, name, version); + if (classifier != null) descriptor += ":" + classifier; + if (!"jar".equals(this.extension)) descriptor += "@" + this.extension; + this.descriptor = descriptor; + } + + public static Artifact fromDescriptor(String descriptor) { + String[] arr = descriptor.split(":", 4); + if (arr.length != 3 && arr.length != 4) + throw new IllegalArgumentException("Artifact name is malformed"); + + String ext = null; + int last = arr.length - 1; + String[] splitted = arr[last].split("@"); + if (splitted.length == 2) { + arr[last] = splitted[0]; + ext = splitted[1]; + } else if (splitted.length > 2) { + throw new IllegalArgumentException("Artifact name is malformed"); + } + + return new Artifact(arr[0].replace("\\", "/"), arr[1], arr[2], arr.length >= 4 ? arr[3] : null, ext); + } + + public String getGroup() { + return group; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getClassifier() { + return classifier; + } + + public Artifact setClassifier(String classifier) { + return new Artifact(group, name, version, classifier, extension); + } + + public String getExtension() { + return extension; + } + + public String getFileName() { + return fileName; + } + + public String getPath() { return path; } + + public String getPath(String root) { + return root + "/" + path; + } + + @Override + public String toString() { + return descriptor; + } + + public static class Serializer implements JsonDeserializer, JsonSerializer { + @Override + public JsonElement serialize(Artifact src, Type typeOfSrc, JsonSerializationContext context) { + return src == null ? JsonNull.INSTANCE : new JsonPrimitive(src.toString()); + } + + @Override + public Artifact deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return json.isJsonPrimitive() ? fromDescriptor(json.getAsJsonPrimitive().getAsString()) : null; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetIndex.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetIndex.java new file mode 100644 index 00000000..3e28fc4e --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetIndex.java @@ -0,0 +1,42 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.ToStringBuilder; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public final class AssetIndex { + + @SerializedName("virtual") + private final boolean virtual; + + @SerializedName("map_to_resources") + private final boolean mapToResources; + + @SerializedName("objects") + private final Map objects; + + public AssetIndex() { + this(false, Collections.emptyMap()); + } + + public AssetIndex(boolean virtual, Map objects) { + this.virtual = this.mapToResources = virtual; + this.objects = new HashMap<>(objects); + } + + public boolean isVirtual() { + return virtual || mapToResources; + } + + public Map getObjects() { + return Collections.unmodifiableMap(objects); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("virtual", virtual).append("objects", objects).toString(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetIndexInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetIndexInfo.java new file mode 100644 index 00000000..e69c4fdf --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetIndexInfo.java @@ -0,0 +1,31 @@ +package com.tungsten.fclcore.game; + +public class AssetIndexInfo extends IdDownloadInfo { + + private final long totalSize; + + public AssetIndexInfo() { + this("", ""); + } + + public AssetIndexInfo(String id, String url) { + this(id, url, null); + } + + public AssetIndexInfo(String id, String url, String sha1) { + this(id, url, sha1, 0); + } + + public AssetIndexInfo(String id, String url, String sha1, int size) { + this(id, url, sha1, size, 0); + } + + public AssetIndexInfo(String id, String url, String sha1, int size, long totalSize) { + super(id, url, sha1, size); + this.totalSize = totalSize; + } + + public long getTotalSize() { + return totalSize; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetObject.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetObject.java new file mode 100644 index 00000000..f686abce --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/AssetObject.java @@ -0,0 +1,48 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.DigestUtils; +import com.tungsten.fclcore.util.Hex; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.Validation; + +import java.io.IOException; +import java.nio.file.Path; + +public final class AssetObject implements Validation { + + private final String hash; + private final long size; + + public AssetObject() { + this("", 0); + } + + public AssetObject(String hash, long size) { + this.hash = hash; + this.size = size; + } + + public String getHash() { + return hash; + } + + public long getSize() { + return size; + } + + public String getLocation() { + return hash.substring(0, 2) + "/" + hash; + } + + @Override + public void validate() throws JsonParseException { + if (StringUtils.isBlank(hash) || hash.length() < 2) + throw new JsonParseException("AssetObject hash cannot be blank."); + } + + public boolean validateChecksum(Path file, boolean defaultValue) throws IOException { + if (hash == null) return defaultValue; + return Hex.encodeHex(DigestUtils.digest("SHA-1", file)).equalsIgnoreCase(hash); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/ClassicLibrary.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/ClassicLibrary.java new file mode 100644 index 00000000..a0f0ddb2 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/ClassicLibrary.java @@ -0,0 +1,5 @@ +package com.tungsten.fclcore.game; + +public class ClassicLibrary { + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/ClassicVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/ClassicVersion.java new file mode 100644 index 00000000..42df8b4b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/ClassicVersion.java @@ -0,0 +1,35 @@ +package com.tungsten.fclcore.game; + +import java.io.File; +import java.util.Arrays; +import java.util.Date; + +/** + * The Minecraft version for 1.5.x and earlier. + */ +public class ClassicVersion extends Version { + + public ClassicVersion() { + super(true, "Classic", null, null, "${auth_player_name} ${auth_session} --workDir ${game_directory}", + null, "net.minecraft.client.Minecraft", null, null, null, null, null, null, + Arrays.asList(new ClassicLibrary("lwjgl"), new ClassicLibrary("jinput"), new ClassicLibrary("lwjgl_util")), + null, null, null, ReleaseType.UNKNOWN, new Date(), new Date(), 0, false, false, null); + } + + private static class ClassicLibrary extends Library { + + public ClassicLibrary(String name) { + super(new Artifact("", "", ""), null, + new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null), + null, null, null, null, null, null); + } + } + + public static boolean hasClassicVersion(File baseDirectory) { + File bin = new File(baseDirectory, "bin"); + return bin.exists() + && new File(bin, "lwjgl.jar").exists() + && new File(bin, "jinput.jar").exists() + && new File(bin, "lwjgl_util.jar").exists(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/CompatibilityRule.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/CompatibilityRule.java new file mode 100644 index 00000000..3ba76b49 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/CompatibilityRule.java @@ -0,0 +1,78 @@ +package com.tungsten.fclcore.game; + +import java.util.*; + +public final class CompatibilityRule { + + private final Action action; + private final OSRestriction os; + private final Map features; + + public CompatibilityRule() { + this(Action.ALLOW, null); + } + + public CompatibilityRule(Action action, OSRestriction os) { + this(action, os, null); + } + + public CompatibilityRule(Action action, OSRestriction os, Map features) { + this.action = action; + this.os = os; + this.features = features; + } + + public Action getAppliedAction(Map supportedFeatures) { + if (os != null && !os.allow()) + return null; + + if (features != null) + for (Map.Entry entry : features.entrySet()) + if (!Objects.equals(supportedFeatures.get(entry.getKey()), entry.getValue())) + return null; + + return action; + } + + public static boolean appliesToCurrentEnvironment(Collection rules) { + return appliesToCurrentEnvironment(rules, Collections.emptyMap()); + } + + public static boolean appliesToCurrentEnvironment(Collection rules, Map features) { + if (rules == null || rules.isEmpty()) + return true; + + Action action = Action.DISALLOW; + for (CompatibilityRule rule : rules) { + Action thisAction = rule.getAppliedAction(features); + if (thisAction != null) + action = thisAction; + } + + return action == Action.ALLOW; + } + + public static boolean equals(Collection rules1, Collection rules2) { + return Objects.hashCode(rules1) == Objects.hashCode(rules2); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CompatibilityRule that = (CompatibilityRule) o; + return action == that.action && + Objects.equals(os, that.os) && + Objects.equals(features, that.features); + } + + @Override + public int hashCode() { + return Objects.hash(action, os, features); + } + + public enum Action { + ALLOW, + DISALLOW + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/CrashReportAnalyzer.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/CrashReportAnalyzer.java new file mode 100644 index 00000000..751c6802 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/CrashReportAnalyzer.java @@ -0,0 +1,216 @@ +package com.tungsten.fclcore.game; + +import com.tungsten.fclcore.util.io.FileUtils; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.file.InvalidPathException; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +// Todo : fix +public final class CrashReportAnalyzer { + + private CrashReportAnalyzer() { + } + + public enum Rule { + // We manually write "Pattern.compile" here for IDEA syntax highlighting. + + OPENJ9(Pattern.compile("(Open J9 is not supported|OpenJ9 is incompatible)")), + TOO_OLD_JAVA(Pattern.compile("java\\.lang\\.UnsupportedClassVersionError: (.*?) version (?\\d+)\\.0"), "expected"), + JVM_32BIT(Pattern.compile("(Could not reserve enough space for (.*?) object heap|The specified size exceeds the maximum representable size)")), + + // Some mods/shader packs do incorrect GL operations. + GL_OPERATION_FAILURE(Pattern.compile("1282: Invalid operation")), + + // Maybe software rendering? Suggest user for using a graphics card. + OPENGL_NOT_SUPPORTED(Pattern.compile("The driver does not appear to support OpenGL")), + GRAPHICS_DRIVER(Pattern.compile("(Pixel format not accelerated|GLX: Failed to create context: GLXBadFBConfig|Couldn't set pixel format|net\\.minecraftforge\\.fml.client\\.SplashProgress|org\\.lwjgl\\.LWJGLException|EXCEPTION_ACCESS_VIOLATION(.|\\n|\\r)+# C {2}\\[(ig|atio|nvoglv))")), + // Out of memory + OUT_OF_MEMORY(Pattern.compile("(java\\.lang\\.OutOfMemoryError|The system is out of physical RAM or swap space)")), + // Memory exceeded + MEMORY_EXCEEDED(Pattern.compile("There is insufficient memory for the Java Runtime Environment to continue")), + // Too high resolution + RESOLUTION_TOO_HIGH(Pattern.compile("Maybe try a (lower resolution|lowerresolution) (resourcepack|texturepack)\\?")), + // game can only run on Java 8. Version of uesr's JVM is too high. + JDK_9(Pattern.compile("java\\.lang\\.ClassCastException: (java\\.base/jdk|class jdk)")), + // user modifies minecraft primary jar without changing hash file + FILE_CHANGED(Pattern.compile("java\\.lang\\.SecurityException: SHA1 digest error for (?.*)"), "file"), + // mod loader/coremod injection fault, prompt user to reinstall game. + NO_SUCH_METHOD_ERROR(Pattern.compile("java\\.lang\\.NoSuchMethodError: (?.*?)"), "class"), + // mod loader/coremod injection fault, prompt user to reinstall game. + NO_CLASS_DEF_FOUND_ERROR(Pattern.compile("java\\.lang\\.NoClassDefFoundError: (?.*)"), "class"), + // coremod wants to access class without "setAccessible" + ILLEGAL_ACCESS_ERROR(Pattern.compile("java\\.lang\\.IllegalAccessError: tried to access class (.*?) from class (?.*?)"), "class"), + // Some mods duplicated + DUPLICATED_MOD(Pattern.compile("Found a duplicate mod (?.*) at (?.*)"), "name", "path"), + // Fabric mod resolution + MOD_RESOLUTION(Pattern.compile("ModResolutionException: (?(.*)[\\n\\r]*( - (.*)[\\n\\r]*)+)"), "reason"), + MOD_RESOLUTION_CONFLICT(Pattern.compile("ModResolutionException: Found conflicting mods: (?.*) conflicts with (?.*)"), "sourcemod", "destmod"), + MOD_RESOLUTION_MISSING(Pattern.compile("ModResolutionException: Could not find required mod: (?.*) requires (?.*)"), "sourcemod", "destmod"), + MOD_RESOLUTION_MISSING_MINECRAFT(Pattern.compile("ModResolutionException: Could not find required mod: (?.*) requires \\{minecraft @ (?.*)}"), "mod", "version"), + MOD_RESOLUTION_COLLECTION(Pattern.compile("ModResolutionException: Could not resolve valid mod collection \\(at: (?.*) requires (?.*)\\)"), "sourcemod", "destmod"), + // Some mods require a file not existing, asking user to manually delete it + FILE_ALREADY_EXISTS(Pattern.compile("java\\.nio\\.file\\.FileAlreadyExistsException: (?.*)"), "file"), + // Forge found some mod crashed in game loading + LOADING_CRASHED_FORGE(Pattern.compile("LoaderExceptionModCrash: Caught exception from (?.*?) \\((?.*)\\)"), "name", "id"), + BOOTSTRAP_FAILED(Pattern.compile("Failed to create mod instance. ModID: (?.*?),"), "id"), + // Fabric found some mod crashed in game loading + LOADING_CRASHED_FABRIC(Pattern.compile("Could not execute entrypoint stage '(.*?)' due to errors, provided by '(?.*)'!"), "id"), + // Fabric may have breaking changes. + // https://github.com/FabricMC/fabric-loader/tree/master/src/main/legacyJava deprecated classes may be removed in the future. + FABRIC_VERSION_0_12(Pattern.compile("java\\.lang\\.NoClassDefFoundError: org/spongepowered/asm/mixin/transformer/FabricMixinTransformerProxy")), + // Minecraft 1.16+Forge with crash because JDK-8273826 + // https://github.com/McModLauncher/modlauncher/issues/91 + MODLAUNCHER_8(Pattern.compile("java\\.lang\\.NoSuchMethodError: ('void sun\\.security\\.util\\.ManifestEntryVerifier\\.\\(java\\.util\\.jar\\.Manifest\\)'|sun\\.security\\.util\\.ManifestEntryVerifier\\.\\(Ljava/util/jar/Manifest;\\)V)")), + // Manually triggerd debug crash + DEBUG_CRASH(Pattern.compile("Manually triggered debug crash")), + CONFIG(Pattern.compile("Failed loading config file (?.*?) of type SERVER for modid (?.*)"), "id", "file"), + // Fabric gives some warnings + FABRIC_WARNINGS(Pattern.compile("Warnings were found!(.*?)[\\n\\r]+(?[^\\[]+)\\["), "reason"), + // Game crashed when ticking entity + ENTITY(Pattern.compile("Entity Type: (?.*)[\\w\\W\\n\\r]*?Entity's Exact location: (?.*)"), "type", "location"), + // Game crashed when tesselating block model + BLOCK(Pattern.compile("Block: (?.*)[\\w\\W\\n\\r]*?Block location: (?.*)"), "type", "location"), + // Cannot find native libraries + UNSATISFIED_LINK_ERROR(Pattern.compile("java.lang.UnsatisfiedLinkError: Failed to locate library: (?.*)"), "name"), + + + + // Mod issues + // TwilightForest is not compatible with OptiFine on Minecraft 1.16. + TWILIGHT_FOREST_OPTIFINE(Pattern.compile("java.lang.IllegalArgumentException: (.*) outside of image bounds (.*)")); + + private final Pattern pattern; + private final String[] groupNames; + + Rule(Pattern pattern, String... groupNames) { + this.pattern = pattern; + this.groupNames = groupNames; + } + + public Pattern getPattern() { + return pattern; + } + + public String[] getGroupNames() { + return groupNames; + } + } + + public static class Result { + private final Rule rule; + private final String log; + private final Matcher matcher; + + public Result(Rule rule, String log, Matcher matcher) { + this.rule = rule; + this.log = log; + this.matcher = matcher; + } + + public Rule getRule() { + return rule; + } + + public String getLog() { + return log; + } + + public Matcher getMatcher() { + return matcher; + } + } + + public static List anaylze(String log) { + List results = new ArrayList<>(); + for (Rule rule : Rule.values()) { + Matcher matcher = rule.pattern.matcher(log); + if (matcher.find()) { + results.add(new Result(rule, log, matcher)); + } + } + return results; + } + + private static final Pattern CRASH_REPORT_LOCATION_PATTERN = Pattern.compile("#@!@# Game crashed! Crash report saved to: #@!@# (?.*)"); + + @Nullable + public static String findCrashReport(String log) throws IOException, InvalidPathException { + Matcher matcher = CRASH_REPORT_LOCATION_PATTERN.matcher(log); + if (matcher.find()) { + return FileUtils.readText(matcher.group("location")); + } else { + return null; + } + } + + public static String extractCrashReport(String rawLog) { + int begin = rawLog.lastIndexOf("---- Minecraft Crash Report ----"); + int end = rawLog.lastIndexOf("#@!@# Game crashed! Crash report saved to"); + if (begin == -1 || end == -1 || begin >= end) return null; + return rawLog.substring(begin, end); + } + + private static final Pattern CRASH_REPORT_STACK_TRACE_PATTERN = Pattern.compile("Description: (.*?)[\\n\\r]+(?[\\w\\W\\n\\r]+)A detailed walkthrough of the error"); + private static final Pattern STACK_TRACE_LINE_PATTERN = Pattern.compile("at (?.*?)\\((?.*?)\\)"); + private static final Pattern STACK_TRACE_LINE_MODULE_PATTERN = Pattern.compile("\\{(?.*)}"); + private static final Set PACKAGE_KEYWORD_BLACK_LIST = new HashSet<>(Arrays.asList( + "net", "minecraft", "item", "block", "player", "tileentity", "events", "common", "client", "entity", "mojang", "main", "gui", "world", "server", "dedicated", // minecraft + "renderer", "chunk", "model", "loading", "color", "pipeline", "inventory", "launcher", "physics", "particle", "gen", "registry", "worldgen", "texture", "biomes", "biome", + "monster", "passive", "ai", "integrated", "tile", "state", "play", "structure", "nbt", "pathfinding", "chunk", "audio", "entities", "items", "renderers", + "storage", + "java", "lang", "util", "nio", "io", "sun", "reflect", "zip", "jdk", "nashorn", "scripts", "runtime", "internal", // java + "mods", "mod", "impl", "org", "com", "cn", "cc", "jp", // title + "core", "config", "registries", "lib", "ruby", "mc", "codec", "channel", "embedded", "netty", "network", "handler", "feature", // misc + "file", "machine", "shader", "general", "helper", "init", "library", "api", "integration", "engine", "preload", "preinit", + "hellominecraft", "jackhuang", // hmcl + "fml", "minecraftforge", "forge", "cpw", "modlauncher", "launchwrapper", "objectweb", "asm", "event", "eventhandler", "handshake", "kcauldron", // forge + "fabricmc", "loader", "game", "knot", "launch", "mixin" // fabric + )); + + public static Set findKeywordsFromCrashReport(String crashReport) { + Matcher matcher = CRASH_REPORT_STACK_TRACE_PATTERN.matcher(crashReport); + Set result = new HashSet<>(); + if (matcher.find()) { + for (String line : matcher.group("stacktrace").split("\\n")) { + Matcher lineMatcher = STACK_TRACE_LINE_PATTERN.matcher(line); + if (lineMatcher.find()) { + String[] method = lineMatcher.group("method").split("\\."); + for (int i = 0; i < method.length - 2; i++) { + if (PACKAGE_KEYWORD_BLACK_LIST.contains(method[i])) { + continue; + } + result.add(method[i]); + } + + Matcher moduleMatcher = STACK_TRACE_LINE_MODULE_PATTERN.matcher(line); + if (moduleMatcher.find()) { + for (String module : moduleMatcher.group("tokens").split(",")) { + String[] split = module.split(":"); + if (split.length >= 2 && "xf".equals(split[0])) { + if (PACKAGE_KEYWORD_BLACK_LIST.contains(split[1])) { + continue; + } + + result.add(split[1]); + } + } + } + } + } + } + return result; + } + + public static int getJavaVersionFromMajorVersion(int majorVersion) { + if (majorVersion >= 46) { + return majorVersion - 44; + } else { + return -1; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/DefaultGameRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/DefaultGameRepository.java new file mode 100644 index 00000000..60145ded --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/DefaultGameRepository.java @@ -0,0 +1,481 @@ +package com.tungsten.fclcore.game; + +import static com.tungsten.fclcore.util.Logging.LOG; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.event.Event; +import com.tungsten.fclcore.event.EventBus; +import com.tungsten.fclcore.event.GameJsonParseFailedEvent; +import com.tungsten.fclcore.event.LoadedOneVersionEvent; +import com.tungsten.fclcore.event.RefreshedVersionsEvent; +import com.tungsten.fclcore.event.RefreshingVersionsEvent; +import com.tungsten.fclcore.event.RemoveVersionEvent; +import com.tungsten.fclcore.event.RenameVersionEvent; +import com.tungsten.fclcore.game.tlauncher.TLauncherVersion; +import com.tungsten.fclcore.mod.ModManager; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.util.Lang; +import com.tungsten.fclcore.util.ToStringBuilder; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.stream.Stream; + +/** + * An implementation of classic Minecraft game repository. + */ +// Todo : fix +public class DefaultGameRepository implements GameRepository { + + private File baseDirectory; + protected Map versions; + private ConcurrentHashMap gameVersions = new ConcurrentHashMap<>(); + + public DefaultGameRepository(File baseDirectory) { + this.baseDirectory = baseDirectory; + } + + public File getBaseDirectory() { + return baseDirectory; + } + + public void setBaseDirectory(File baseDirectory) { + this.baseDirectory = baseDirectory; + } + + @Override + public boolean hasVersion(String id) { + return id != null && versions.containsKey(id); + } + + @Override + public Version getVersion(String id) { + if (!hasVersion(id)) + throw new VersionNotFoundException("Version '" + id + "' does not exist in " + versions.keySet() + "."); + return versions.get(id); + } + + @Override + public int getVersionCount() { + return versions.size(); + } + + @Override + public Collection getVersions() { + return versions.values(); + } + + @Override + public File getLibrariesDirectory(Version version) { + return new File(getBaseDirectory(), "libraries"); + } + + @Override + public File getLibraryFile(Version version, Library lib) { + if ("local".equals(lib.getHint()) && lib.getFileName() != null) + return new File(getVersionRoot(version.getId()), "libraries/" + lib.getFileName()); + else + return new File(getLibrariesDirectory(version), lib.getPath()); + } + + public String getArtifactFile(Version version, Artifact artifact) { + return artifact.getPath(getBaseDirectory() + "/libraries"); + } + + public GameDirectoryType getGameDirectoryType(String id) { + return GameDirectoryType.ROOT_FOLDER; + } + + @Override + public File getRunDirectory(String id) { + switch (getGameDirectoryType(id)) { + case VERSION_FOLDER: return getVersionRoot(id); + case ROOT_FOLDER: return getBaseDirectory(); + default: throw new IllegalStateException(); + } + } + + @Override + public File getVersionJar(Version version) { + Version v = version.resolve(this); + String id = Optional.ofNullable(v.getJar()).orElse(v.getId()); + return new File(getVersionRoot(id), id + ".jar"); + } + + @Override + public String getGameVersion(Version version) { + // This implementation may cause multiple flows against the same version entering + // this function, which is accepted because GameVersion::minecraftVersion should + // be consistent. + File versionJar = getVersionJar(version); + if (gameVersions.containsKey(versionJar)) { + return gameVersions.get(versionJar); + } else { + String gameVersion = GameVersion.minecraftVersion(versionJar); + + if (gameVersion == null) { + LOG.warning("Cannot find out game version of " + version.getId() + ", primary jar: " + versionJar.toString() + ", jar exists: " + versionJar.exists()); + } + + gameVersions.put(versionJar, gameVersion); + return gameVersion; + } + } + + @Override + public File getVersionRoot(String id) { + return new File(getBaseDirectory(), "versions/" + id); + } + + public File getVersionJson(String id) { + return new File(getVersionRoot(id), id + ".json"); + } + + public Version readVersionJson(String id) throws IOException, JsonParseException { + return readVersionJson(getVersionJson(id)); + } + + public Version readVersionJson(File file) throws IOException, JsonParseException { + String jsonText = FileUtils.readText(file); + try { + // Try TLauncher version json format + return JsonUtils.fromNonNullJson(jsonText, TLauncherVersion.class).toVersion(); + } catch (JsonParseException ignored) { + } + + try { + // Try official version json format + return JsonUtils.fromNonNullJson(jsonText, Version.class); + } catch (JsonParseException ignored) { + } + + LOG.warning("Cannot parse version json + " + file.toString() + "\n" + jsonText); + throw new JsonParseException("Version json incorrect"); + } + + @Override + public boolean renameVersion(String from, String to) { + if (EventBus.EVENT_BUS.fireEvent(new RenameVersionEvent(this, from, to)) == Event.Result.DENY) + return false; + + try { + Version fromVersion = getVersion(from); + Path fromDir = getVersionRoot(from).toPath(); + Path toDir = getVersionRoot(to).toPath(); + Files.move(fromDir, toDir); + + Path fromJson = toDir.resolve(from + ".json"); + Path fromJar = toDir.resolve(from + ".jar"); + Path toJson = toDir.resolve(to + ".json"); + Path toJar = toDir.resolve(to + ".jar"); + + boolean hasJarFile = Files.exists(fromJar); + + try { + Files.move(fromJson, toJson); + if (hasJarFile) Files.move(fromJar, toJar); + } catch (IOException e) { + // recovery + Lang.ignoringException(() -> Files.move(toJson, fromJson)); + if (hasJarFile) Lang.ignoringException(() -> Files.move(toJar, fromJar)); + Lang.ignoringException(() -> Files.move(toDir, fromDir)); + throw e; + } + + if (fromVersion.getId().equals(fromVersion.getJar())) + fromVersion = fromVersion.setJar(null); + FileUtils.writeText(toJson.toFile(), JsonUtils.GSON.toJson(fromVersion.setId(to))); + + // fix inheritsFrom of versions that inherits from version [from]. + for (Version version : getVersions()) { + if (from.equals(version.getInheritsFrom())) { + File json = getVersionJson(version.getId()).getAbsoluteFile(); + FileUtils.writeText(json, JsonUtils.GSON.toJson(version.setInheritsFrom(to))); + } + } + return true; + } catch (IOException | JsonParseException | VersionNotFoundException | InvalidPathException e) { + LOG.log(Level.WARNING, "Unable to rename version " + from + " to " + to, e); + return false; + } + } + + public boolean removeVersionFromDisk(String id) { + if (EventBus.EVENT_BUS.fireEvent(new RemoveVersionEvent(this, id)) == Event.Result.DENY) + return false; + if (!versions.containsKey(id)) + return FileUtils.deleteDirectoryQuietly(getVersionRoot(id).getAbsolutePath()); + File file = getVersionRoot(id); + if (!file.exists()) + return true; + // test if no file in this version directory is occupied. + File removedFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + "_removed"); + if (!file.renameTo(removedFile)) + return false; + + try { + versions.remove(id); + + // remove json files first to ensure FCL will not recognize this folder as a valid version. + List jsons = FileUtils.listFilesByExtension(removedFile.getAbsolutePath(), "json"); + for (File json : jsons) { + if (!json.delete()) + LOG.warning("Unable to delete file " + json); + } + // remove the version from version list regardless of whether the directory was removed successfully or not. + try { + FileUtils.deleteDirectory(removedFile.getAbsolutePath()); + } catch (IOException e) { + LOG.log(Level.WARNING, "Unable to remove version folder: " + file, e); + } + return true; + } finally { + refreshVersionsAsync().start(); + } + } + + protected void refreshVersionsImpl() { + Map versions = new TreeMap<>(); + + if (ClassicVersion.hasClassicVersion(getBaseDirectory())) { + Version version = new ClassicVersion(); + versions.put(version.getId(), version); + } + + SimpleVersionProvider provider = new SimpleVersionProvider(); + + File[] files = new File(getBaseDirectory(), "versions").listFiles(); + if (files != null) + Arrays.stream(files).parallel().filter(File::isDirectory).flatMap(dir -> { + String id = dir.getName(); + File json = new File(dir, id + ".json"); + + // If user renamed the json file by mistake or created the json file in a wrong name, + // we will find the only json and rename it to correct name. + if (!json.exists()) { + List jsons = FileUtils.listFilesByExtension(dir, "json"); + if (jsons.size() == 1) { + LOG.info("Renaming json file " + jsons.get(0) + " to " + json); + if (!jsons.get(0).renameTo(json)) { + LOG.warning("Cannot rename json file, ignoring version " + id); + return Stream.empty(); + } + + File jar = new File(dir, FileUtils.getNameWithoutExtension(jsons.get(0)) + ".jar"); + if (jar.exists() && !jar.renameTo(new File(dir, id + ".jar"))) { + LOG.warning("Cannot rename jar file, ignoring version " + id); + return Stream.empty(); + } + } else { + LOG.info("No available json file found, ignoring version " + id); + return Stream.empty(); + } + } + + Version version; + try { + version = readVersionJson(json); + } catch (Exception e) { + LOG.log(Level.WARNING, "Malformed version json " + id, e); + // JsonSyntaxException or IOException or NullPointerException(!!) + if (EventBus.EVENT_BUS.fireEvent(new GameJsonParseFailedEvent(this, json, id)) != Event.Result.ALLOW) + return Stream.empty(); + + try { + version = readVersionJson(json); + } catch (Exception e2) { + LOG.log(Level.SEVERE, "User corrected version json is still malformed", e2); + return Stream.empty(); + } + } + + if (!id.equals(version.getId())) { + String from = id; + String to = version.getId(); + String fromDir = getVersionRoot(from).getAbsolutePath(); + String toDir = getVersionRoot(to).getAbsolutePath(); + FileUtils.rename(fromDir, to); + + String fromJson = toDir + "/" + from + ".json"; + String fromJar = toDir + "/" + from + ".jar"; + String toJsonName = to + ".json"; + String toJarName = to + ".jar"; + + FileUtils.rename(fromJson, toJsonName); + if (new File(fromJar).exists()) + FileUtils.rename(fromJar, toJarName); + } + + return Stream.of(version); + }).forEachOrdered(provider::addVersion); + + for (Version version : provider.getVersionMap().values()) { + try { + Version resolved = version.resolve(provider); + + if (resolved.appliesToCurrentEnvironment() && + EventBus.EVENT_BUS.fireEvent(new LoadedOneVersionEvent(this, resolved)) != Event.Result.DENY) + versions.put(version.getId(), version); + } catch (VersionNotFoundException e) { + LOG.log(Level.WARNING, "Ignoring version " + version.getId() + " because it inherits from a nonexistent version."); + } + } + + this.gameVersions.clear(); + this.versions = versions; + } + + @Override + public void refreshVersions() { + if (EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this)) == Event.Result.DENY) + return; + + refreshVersionsImpl(); + EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this)); + } + + @Override + public AssetIndex getAssetIndex(String version, String assetId) throws IOException { + try { + return Objects.requireNonNull(JsonUtils.GSON.fromJson(FileUtils.readText(getIndexFile(version, assetId)), AssetIndex.class)); + } catch (JsonParseException | NullPointerException e) { + throw new IOException("Asset index file malformed", e); + } + } + + @Override + public String getActualAssetDirectory(String version, String assetId) { + try { + return reconstructAssets(version, assetId); + } catch (IOException | JsonParseException e) { + LOG.log(Level.SEVERE, "Unable to reconstruct asset directory", e); + return getAssetDirectory(version, assetId); + } + } + + @Override + public String getAssetDirectory(String version, String assetId) { + return getBaseDirectory() + "/assets"; + } + + @Override + public String getAssetObject(String version, String assetId, String name) throws IOException { + try { + AssetObject assetObject = getAssetIndex(version, assetId).getObjects().get(name); + if (assetObject == null) return null; + return getAssetObject(version, assetId, assetObject); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException("Unrecognized asset object " + name + " in asset " + assetId + " of version " + version, e); + } + } + + @Override + public String getAssetObject(String version, String assetId, AssetObject obj) { + return getAssetDirectory(version, assetId) + "/objects/" + obj.getLocation(); + } + + @Override + public String getIndexFile(String version, String assetId) { + return getAssetDirectory(version, assetId) + "/indexes/" + assetId + ".json"; + } + + @Override + public String getLoggingObject(String version, String assetId, LoggingInfo loggingInfo) { + return getAssetDirectory(version, assetId) + "/log_configs/" + loggingInfo.getFile().getId(); + } + + protected String reconstructAssets(String version, String assetId) throws IOException, JsonParseException { + Path assetsDir = getAssetDirectory(version, assetId); + Path indexFile = getIndexFile(version, assetId); + Path virtualRoot = assetsDir.resolve("virtual").resolve(assetId); + + if (!Files.isRegularFile(indexFile)) + return assetsDir; + + String assetIndexContent = FileUtils.readText(indexFile); + AssetIndex index = JsonUtils.GSON.fromJson(assetIndexContent, AssetIndex.class); + + if (index == null) + return assetsDir; + + if (index.isVirtual()) { + int cnt = 0; + int tot = index.getObjects().entrySet().size(); + for (Map.Entry entry : index.getObjects().entrySet()) { + Path target = virtualRoot.resolve(entry.getKey()); + Path original = getAssetObject(version, assetsDir, entry.getValue()); + if (Files.exists(original)) { + cnt++; + if (!Files.isRegularFile(target)) + FileUtils.copyFile(original, target); + } + } + + // If the scale new format existent file is lower then 0.1, use the old format. + if (cnt * 10 < tot) + return assetsDir; + else + return virtualRoot; + } + + return assetsDir; + } + + public boolean isLoaded() { + return versions != null; + } + + public File getModpackConfiguration(String version) { + return new File(getVersionRoot(version), "modpack.json"); + } + + /** + * read modpack configuration for a version. + * @param version version installed as modpack + * @param manifest type of ModpackConfiguration + * @return modpack configuration object, or null if this version is not a modpack. + * @throws VersionNotFoundException if version does not exist. + * @throws IOException if an i/o error occurs. + */ + @Nullable + public ModpackConfiguration readModpackConfiguration(String version) throws IOException, VersionNotFoundException { + if (!hasVersion(version)) throw new VersionNotFoundException(version); + File file = getModpackConfiguration(version); + if (!file.exists()) return null; + return JsonUtils.GSON.fromJson(FileUtils.readText(file), new TypeToken>(){}.getType()); + } + + public boolean isModpack(String version) { + return getModpackConfiguration(version).exists(); + } + + public ModManager getModManager(String version) { + return new ModManager(this, version); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("versions", versions == null ? null : versions.keySet()) + .append("baseDirectory", baseDirectory) + .toString(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/DownloadInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/DownloadInfo.java new file mode 100644 index 00000000..4ace8c0f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/DownloadInfo.java @@ -0,0 +1,69 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.DigestUtils; +import com.tungsten.fclcore.util.Hex; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.ToStringBuilder; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; + +import java.io.IOException; +import java.nio.file.Path; + +public class DownloadInfo implements Validation { + + @SerializedName("url") + private final String url; + @SerializedName("sha1") + private final String sha1; + @SerializedName("size") + private final int size; + + public DownloadInfo() { + this(""); + } + + public DownloadInfo(String url) { + this(url, null); + } + + public DownloadInfo(String url, String sha1) { + this(url, sha1, 0); + } + + public DownloadInfo(String url, String sha1, int size) { + this.url = url; + this.sha1 = sha1; + this.size = size; + } + + public String getUrl() { + return url; + } + + public String getSha1() { + return "invalid".equals(sha1) ? null : sha1; + } + + public int getSize() { + return size; + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("url", url).append("sha1", sha1).append("size", size).toString(); + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (StringUtils.isBlank(url)) + throw new TolerableValidationException(); + } + + public boolean validateChecksum(Path file, boolean defaultValue) throws IOException { + if (getSha1() == null) return defaultValue; + return Hex.encodeHex(DigestUtils.digest("SHA-1", file)).equalsIgnoreCase(getSha1()); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/DownloadType.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/DownloadType.java new file mode 100644 index 00000000..730ec1a7 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/DownloadType.java @@ -0,0 +1,9 @@ +package com.tungsten.fclcore.game; + +public enum DownloadType { + CLIENT, + SERVER, + WINDOWS_SERVER, + CLIENT_MAPPINGS, + SERVER_MAPPINGS +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/ExtractRules.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/ExtractRules.java new file mode 100644 index 00000000..a01fccb2 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/ExtractRules.java @@ -0,0 +1,34 @@ +package com.tungsten.fclcore.game; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ExtractRules { + + public static final ExtractRules EMPTY = new ExtractRules(); + + private final List exclude; + + public ExtractRules() { + this.exclude = Collections.emptyList(); + } + + public ExtractRules(List exclude) { + this.exclude = new ArrayList<>(exclude); + } + + public List getExclude() { + return Collections.unmodifiableList(exclude); + } + + public boolean shouldExtract(String path) { + for (String s : exclude) { + if (path.startsWith(s)) { + return false; + } + } + return true; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/GameDirectoryType.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameDirectoryType.java new file mode 100644 index 00000000..63c223ba --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameDirectoryType.java @@ -0,0 +1,16 @@ +package com.tungsten.fclcore.game; + +public enum GameDirectoryType { + /** + * .minecraft + */ + ROOT_FOLDER, + /** + * .minecraft/versions/<version name> + */ + VERSION_FOLDER, + /** + * user customized directory. + */ + CUSTOM +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/GameException.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameException.java new file mode 100644 index 00000000..16abf18b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameException.java @@ -0,0 +1,19 @@ +package com.tungsten.fclcore.game; + +public class GameException extends Exception { + + public GameException() { + } + + public GameException(String message) { + super(message); + } + + public GameException(String message, Throwable cause) { + super(message, cause); + } + + public GameException(Throwable cause) { + super(cause); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/GameJavaVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameJavaVersion.java new file mode 100644 index 00000000..b5e04beb --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameJavaVersion.java @@ -0,0 +1,27 @@ +package com.tungsten.fclcore.game; + +public class GameJavaVersion { + private final String component; + private final int majorVersion; + + public GameJavaVersion() { + this("", 0); + } + + public GameJavaVersion(String component, int majorVersion) { + this.component = component; + this.majorVersion = majorVersion; + } + + public String getComponent() { + return component; + } + + public int getMajorVersion() { + return majorVersion; + } + + public static final GameJavaVersion JAVA_17 = new GameJavaVersion("java-runtime-beta", 17); + public static final GameJavaVersion JAVA_16 = new GameJavaVersion("java-runtime-alpha", 16); + public static final GameJavaVersion JAVA_8 = new GameJavaVersion("jre-legacy", 8); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/GameRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameRepository.java new file mode 100644 index 00000000..1fbeb4c0 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameRepository.java @@ -0,0 +1,221 @@ +package com.tungsten.fclcore.game; + +import com.tungsten.fclcore.task.Task; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public interface GameRepository extends VersionProvider { + + /** + * Does the version of id exist? + * + * @param id the id of version + * @return true if the version exists + */ + @Override + boolean hasVersion(String id); + + /** + * Get the version + * + * @param id the id of version + * @return the version you want + * @throws VersionNotFoundException if no version is id. + */ + @Override + Version getVersion(String id) throws VersionNotFoundException; + + default Version getResolvedVersion(String id) throws VersionNotFoundException { + return getVersion(id).resolve(this); + } + + default Version getResolvedPreservingPatchesVersion(String id) throws VersionNotFoundException { + return getVersion(id).resolvePreservingPatches(this); + } + + /** + * How many version are there? + */ + int getVersionCount(); + + /** + * Gets the collection of versions + * + * @return the collection of versions + */ + Collection getVersions(); + + /** + * Load version list. + * + * This method should be called before launching a version. + * A time-costly operation. + * You'd better execute this method in a new thread. + */ + void refreshVersions(); + + default Task refreshVersionsAsync() { + return Task.runAsync(this::refreshVersions); + } + + /** + * Gets the root folder of specific version. + * The root folders the versions must be unique. + * For example, .minecraft/versions/<version name>/. + */ + File getVersionRoot(String id); + + /** + * Gets the current running directory of the given version for game. + * + * @param id the version id + */ + File getRunDirectory(String id); + + File getLibrariesDirectory(Version version); + + /** + * Get the library file in disk. + * This method allows versions and libraries that are not loaded by this game repository. + * + * @param version the reference of game version + * @param lib the library, {@link Version#getLibraries()} + * @return the library file + */ + File getLibraryFile(Version version, Library lib); + + /** + * Get minecraft jar + * + * @param version resolvedVersion + * @return the minecraft jar + */ + File getVersionJar(Version version); + + /** + * Detect game version. + * + * This method is time-consuming, but the result will be cached. + * Consider running this job in IO scheduler. + * + * @param version version + * @return game version, or empty if an error occurred in detection. + */ + String getGameVersion(Version version); + + /** + * Detect game version. + * + * This method is time-consuming, but the result will be cached. + * Consider running this job in IO scheduler. + * + * @param versionId id of version + * @return game version, or empty if an error occurred in detection. + */ + default String getGameVersion(String versionId) throws VersionNotFoundException { + return getGameVersion(getVersion(versionId)); + } + + /** + * Get minecraft jar + * + * @param version version id + * @return the minecraft jar + */ + default File getVersionJar(String version) throws VersionNotFoundException { + return getVersionJar(getVersion(version).resolve(this)); + } + + /** + * Rename given version to new name. + * + * @param from The id of original version + * @param to The new id of the version + * @throws UnsupportedOperationException if this game repository does not support renaming a version + * @return true if the operation is done successfully, false if version `from` not found, version json is malformed or I/O errors occurred. + */ + boolean renameVersion(String from, String to); + + /** + * Get actual asset directory. + * Will reconstruct assets or do some blocking tasks if necessary. + * You'd better create a new thread to invoke this method. + * + * @param version the id of specific version that is relevant to {@code assetId} + * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} + * @return the actual asset directory + */ + String getActualAssetDirectory(String version, String assetId); + + /** + * Get the asset directory according to the asset id. + * + * @param version the id of specific version that is relevant to {@code assetId} + * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} + * @return the asset directory + */ + String getAssetDirectory(String version, String assetId); + + /** + * Get the file that given asset object refers to + * + * @param version the id of specific version that is relevant to {@code assetId} + * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} + * @param name the asset object name, you can find it in keys of {@link AssetIndex#getObjects()} + * @throws IOException if I/O operation fails. + * @return the file that given asset object refers to + */ + String getAssetObject(String version, String assetId, String name) throws IOException; + + /** + * Get the file that given asset object refers to + * + * @param version the id of specific version that is relevant to {@code assetId} + * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} + * @param obj the asset object, you can find it in {@link AssetIndex#getObjects()} + * @return the file that given asset object refers to + */ + String getAssetObject(String version, String assetId, AssetObject obj); + + /** + * Get asset index that assetId represents + * + * @param version the id of specific version that is relevant to {@code assetId} + * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} + * @return the asset index + */ + AssetIndex getAssetIndex(String version, String assetId) throws IOException; + + /** + * Get the asset_index.json which includes asset objects information. + * + * @param version the id of specific version that is relevant to {@code assetId} + * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} + */ + String getIndexFile(String version, String assetId); + + /** + * Get logging object + * + * @param version the id of specific version that is relevant to {@code assetId} + * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} + * @param loggingInfo the logging info + * @return the file that loggingInfo refers to + */ + String getLoggingObject(String version, String assetId, LoggingInfo loggingInfo); + + default List getClasspath(Version version) { + List classpath = new ArrayList<>(); + for (Library library : version.getLibraries()) + if (library.appliesToCurrentEnvironment() && !library.isNative()) { + File f = getLibraryFile(version, library); + if (f.exists() && f.isFile()) + classpath.add(f.getAbsolutePath()); + } + return classpath; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/GameVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameVersion.java new file mode 100644 index 00000000..8a1bc509 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/GameVersion.java @@ -0,0 +1,99 @@ +package com.tungsten.fclcore.game; + +import static com.tungsten.fclcore.util.Logging.LOG; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import org.jenkinsci.constant_pool_scanner.ConstantPool; +import org.jenkinsci.constant_pool_scanner.ConstantPoolScanner; +import org.jenkinsci.constant_pool_scanner.ConstantType; +import org.jenkinsci.constant_pool_scanner.StringConstant; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +// Todo : fix +public final class GameVersion { + private GameVersion() { + } + + private static String getVersionFromJson(File versionJson) { + try { + Map version = JsonUtils.fromNonNullJson(FileUtils.readText(versionJson), Map.class); + return (String) version.get("name"); + } catch (IOException | JsonParseException e) { + LOG.log(Level.WARNING, "Failed to parse version.json", e); + return null; + } + } + + private static Optional getVersionOfClassMinecraft(byte[] bytecode) throws IOException { + ConstantPool pool = ConstantPoolScanner.parse(bytecode, ConstantType.STRING); + + return StreamSupport.stream(pool.list(StringConstant.class).spliterator(), false) + .map(StringConstant::get) + .filter(s -> s.startsWith("Minecraft Minecraft ")) + .map(s -> s.substring("Minecraft Minecraft ".length())) + .findFirst(); + } + + private static Optional getVersionFromClassMinecraftServer(byte[] bytecode) throws IOException { + ConstantPool pool = ConstantPoolScanner.parse(bytecode, ConstantType.STRING); + + List list = StreamSupport.stream(pool.list(StringConstant.class).spliterator(), false) + .map(StringConstant::get) + .collect(Collectors.toList()); + + int idx = -1; + + for (int i = 0; i < list.size(); ++i) + if (list.get(i).startsWith("Can't keep up!")) { + idx = i; + break; + } + + for (int i = idx - 1; i >= 0; --i) + if (list.get(i).matches(".*[0-9].*")) + return Optional.of(list.get(i)); + + return Optional.empty(); + } + + public static String minecraftVersion(File file) { + if (file == null || !file.exists() || !file.isFile() || !file.canRead()) + return null; + + try (FileSystem gameJar = CompressingUtils.createReadOnlyZipFileSystem(file.toPath())) { + Path versionJson = gameJar.getPath("version.json"); + if (Files.exists(versionJson)) { + Optional result = getVersionFromJson(versionJson); + if (result.isPresent()) + return result; + } + + Path minecraft = gameJar.getPath("net/minecraft/client/Minecraft.class"); + if (Files.exists(minecraft)) { + Optional result = getVersionOfClassMinecraft(Files.readAllBytes(minecraft)); + if (result.isPresent()) + return result; + } + Path minecraftServer = gameJar.getPath("net/minecraft/server/MinecraftServer.class"); + if (Files.exists(minecraftServer)) + return getVersionFromClassMinecraftServer(Files.readAllBytes(minecraftServer)); + return Optional.empty(); + } catch (IOException e) { + return Optional.empty(); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/IdDownloadInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/IdDownloadInfo.java new file mode 100644 index 00000000..6d12e6ff --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/IdDownloadInfo.java @@ -0,0 +1,42 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.TolerableValidationException; + +public class IdDownloadInfo extends DownloadInfo { + + @SerializedName("id") + private final String id; + + public IdDownloadInfo() { + this("", ""); + } + + public IdDownloadInfo(String id, String url) { + this(id, url, null); + } + + public IdDownloadInfo(String id, String url, String sha1) { + this(id, url, sha1, 0); + } + + public IdDownloadInfo(String id, String url, String sha1, int size) { + super(url, sha1, size); + this.id = id; + } + + public String getId() { + return id; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + super.validate(); + + if (StringUtils.isBlank(id)) + throw new JsonParseException("IdDownloadInfo id can not be null"); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/JavaVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/JavaVersion.java new file mode 100644 index 00000000..ffce0de0 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/JavaVersion.java @@ -0,0 +1,44 @@ +package com.tungsten.fclcore.game; + +public class JavaVersion { + + public static final int JAVA_8 = 8; + + public static final int JAVA_17 = 17; + + private final int version; + private final String versionName; + private final int architecture; + + public JavaVersion(int version, String versionName, int architecture) { + this.version = version; + this.versionName = versionName; + this.architecture = architecture; + } + + public int getVersion() { + return version; + } + + public String getVersionName() { + return versionName; + } + + public int getArchitecture() { + return architecture; + } + + public static int getSuitableJavaVersion(Version version) { + if (version.getJavaVersion() == null || version.getJavaVersion().getMajorVersion() == 8) { + return JAVA_8; + } + return JAVA_17; + } + + public static boolean checkJavaVersion(Version version, JavaVersion javaVersion) { + if (version.getJavaVersion() == null || version.getJavaVersion().getMajorVersion() == 8) { + return javaVersion.getVersion() == JAVA_8; + } + return javaVersion.getVersion() == JAVA_17; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/LaunchOptions.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/LaunchOptions.java new file mode 100644 index 00000000..394f51fd --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/LaunchOptions.java @@ -0,0 +1,496 @@ +package com.tungsten.fclcore.game; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.Serializable; +import java.net.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +// Todo : add and delete some options +public class LaunchOptions implements Serializable { + + private File gameDir; + private JavaVersion java; + private String versionName; + private String versionType; + private String profileName; + private List gameArguments = new ArrayList<>(); + private List javaArguments = new ArrayList<>(); + private List javaAgents = new ArrayList<>(0); + private Integer minMemory; + private Integer maxMemory; + private Integer metaspace; + private Integer width; + private Integer height; + private boolean fullscreen; + private String serverIp; + private String wrapper; + private Proxy proxy; + private String proxyUser; + private String proxyPass; + private boolean noGeneratedJVMArgs; + private String preLaunchCommand; + private String postExitCommand; + private ProcessPriority processPriority = ProcessPriority.HIGH; + private boolean daemon; + + /** + * The game directory + */ + public File getGameDir() { + return gameDir; + } + + /** + * The Java Environment that Minecraft runs on. + */ + public JavaVersion getJava() { + return java; + } + + /** + * Will shown in the left bottom corner of the main menu of Minecraft. + * null if use the id of launch version. + */ + public String getVersionName() { + return versionName; + } + + /** + * Will shown in the left bottom corner of the main menu of Minecraft. + * null if use Version.versionType. + */ + public String getVersionType() { + return versionType; + } + + /** + * Don't know what the hell this is. + */ + public String getProfileName() { + return profileName; + } + + /** + * User custom additional minecraft command line arguments. + */ + @NotNull + public List getGameArguments() { + return Collections.unmodifiableList(gameArguments); + } + + /** + * User custom additional java virtual machine command line arguments. + */ + @NotNull + public List getJavaArguments() { + return Collections.unmodifiableList(javaArguments); + } + + @NotNull + public List getJavaAgents() { + return Collections.unmodifiableList(javaAgents); + } + + /** + * The minimum memory that the JVM can allocate. + */ + public Integer getMinMemory() { + return minMemory; + } + + /** + * The maximum memory that the JVM can allocate. + */ + public Integer getMaxMemory() { + return maxMemory; + } + + /** + * The maximum metaspace memory that the JVM can allocate. + * For Java 7 -XX:PermSize and Java 8 -XX:MetaspaceSize + * Containing class instances. + */ + public Integer getMetaspace() { + return metaspace; + } + + /** + * The initial game window width + */ + public Integer getWidth() { + return width; + } + + /** + * The initial game window height + */ + public Integer getHeight() { + return height; + } + + /** + * Is inital game window fullscreen. + */ + public boolean isFullscreen() { + return fullscreen; + } + + /** + * The server ip that will connect to when enter game main menu. + */ + public String getServerIp() { + return serverIp; + } + + /** + * i.e. optirun + */ + public String getWrapper() { + return wrapper; + } + + /** + * Proxy settings + */ + public Proxy getProxy() { + return proxy; + } + + /** + * The user name of the proxy, optional. + */ + public String getProxyUser() { + return proxyUser; + } + + /** + * The password of the proxy, optional + */ + public String getProxyPass() { + return proxyPass; + } + + /** + * Prevent game launcher from generating default JVM arguments like max memory. + */ + public boolean isNoGeneratedJVMArgs() { + return noGeneratedJVMArgs; + } + + /** + * Command called before game launches. + */ + public String getPreLaunchCommand() { + return preLaunchCommand; + } + + /** + * Command called after game exits. + */ + public String getPostExitCommand() { + return postExitCommand; + } + + /** + * Process priority + */ + public ProcessPriority getProcessPriority() { + return processPriority; + } + + /** + * Will launcher keeps alive after game launched or not. + */ + public boolean isDaemon() { + return daemon; + } + + public static class Builder { + + private final LaunchOptions options = new LaunchOptions(); + + public LaunchOptions create() { + return options; + } + + /** + * The game directory + */ + public File getGameDir() { + return options.gameDir; + } + + /** + * The Java Environment that Minecraft runs on. + */ + public JavaVersion getJava() { + return options.java; + } + + /** + * Will shown in the left bottom corner of the main menu of Minecraft. + * null if use the id of launch version. + */ + public String getVersionName() { + return options.versionName; + } + + /** + * Will shown in the left bottom corner of the main menu of Minecraft. + * null if use Version.versionType. + */ + public String getVersionType() { + return options.versionType; + } + + /** + * Don't know what the hell this is. + */ + public String getProfileName() { + return options.profileName; + } + + /** + * User custom additional minecraft command line arguments. + */ + public List getGameArguments() { + return options.gameArguments; + } + + /** + * User custom additional java virtual machine command line arguments. + */ + public List getJavaArguments() { + return options.javaArguments; + } + + public List getJavaAgents() { + return options.javaAgents; + } + + /** + * The minimum memory that the JVM can allocate. + */ + public Integer getMinMemory() { + return options.minMemory; + } + + /** + * The maximum memory that the JVM can allocate. + */ + public Integer getMaxMemory() { + return options.maxMemory; + } + + /** + * The maximum metaspace memory that the JVM can allocate. + * For Java 7 -XX:PermSize and Java 8 -XX:MetaspaceSize + * Containing class instances. + */ + public Integer getMetaspace() { + return options.metaspace; + } + + /** + * The initial game window width + */ + public Integer getWidth() { + return options.width; + } + + /** + * The initial game window height + */ + public Integer getHeight() { + return options.height; + } + + /** + * Is inital game window fullscreen. + */ + public boolean isFullscreen() { + return options.fullscreen; + } + + /** + * The server ip that will connect to when enter game main menu. + */ + public String getServerIp() { + return options.serverIp; + } + + /** + * i.e. optirun + */ + public String getWrapper() { + return options.wrapper; + } + + /** + * Proxy settings + */ + public Proxy getProxy() { + return options.proxy; + } + + /** + * The user name of the proxy, optional. + */ + public String getProxyUser() { + return options.proxyUser; + } + + /** + * The password of the proxy, optional + */ + public String getProxyPass() { + return options.proxyPass; + } + + /** + * Prevent game launcher from generating default JVM arguments like max memory. + */ + public boolean isNoGeneratedJVMArgs() { + return options.noGeneratedJVMArgs; + } + + /** + * Called command line before launching the game. + */ + public String getPreLaunchCommand() { + return options.preLaunchCommand; + } + + public boolean isDaemon() { + return options.daemon; + } + + public Builder setGameDir(File gameDir) { + options.gameDir = gameDir; + return this; + } + + public Builder setJava(JavaVersion java) { + options.java = java; + return this; + } + + public Builder setVersionName(String versionName) { + options.versionName = versionName; + return this; + } + + public Builder setVersionType(String versionType) { + options.versionType = versionType; + return this; + } + + public Builder setProfileName(String profileName) { + options.profileName = profileName; + return this; + } + + public Builder setGameArguments(List gameArguments) { + options.gameArguments.clear(); + options.gameArguments.addAll(gameArguments); + return this; + } + + public Builder setJavaArguments(List javaArguments) { + options.javaArguments.clear(); + options.javaArguments.addAll(javaArguments); + return this; + } + + public Builder setJavaAgents(List javaAgents) { + options.javaAgents.clear(); + options.javaAgents.addAll(javaAgents); + return this; + } + + public Builder setMinMemory(Integer minMemory) { + options.minMemory = minMemory; + return this; + } + + public Builder setMaxMemory(Integer maxMemory) { + options.maxMemory = maxMemory; + return this; + } + + public Builder setMetaspace(Integer metaspace) { + options.metaspace = metaspace; + return this; + } + + public Builder setWidth(Integer width) { + options.width = width; + return this; + } + + public Builder setHeight(Integer height) { + options.height = height; + return this; + } + + public Builder setFullscreen(boolean fullscreen) { + options.fullscreen = fullscreen; + return this; + } + + public Builder setServerIp(String serverIp) { + options.serverIp = serverIp; + return this; + } + + public Builder setWrapper(String wrapper) { + options.wrapper = wrapper; + return this; + } + + public Builder setProxy(Proxy proxy) { + options.proxy = proxy; + return this; + } + + public Builder setProxyUser(String proxyUser) { + options.proxyUser = proxyUser; + return this; + } + + public Builder setProxyPass(String proxyPass) { + options.proxyPass = proxyPass; + return this; + } + + public Builder setNoGeneratedJVMArgs(boolean noGeneratedJVMArgs) { + options.noGeneratedJVMArgs = noGeneratedJVMArgs; + return this; + } + + public Builder setPreLaunchCommand(String preLaunchCommand) { + options.preLaunchCommand = preLaunchCommand; + return this; + } + + public Builder setPostExitCommand(String postExitCommand) { + options.postExitCommand = postExitCommand; + return this; + } + + public Builder setProcessPriority(@NotNull ProcessPriority processPriority) { + options.processPriority = processPriority; + return this; + } + + public Builder setDaemon(boolean daemon) { + options.daemon = daemon; + return this; + } + + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/LibrariesDownloadInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/LibrariesDownloadInfo.java new file mode 100644 index 00000000..276b6400 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/LibrariesDownloadInfo.java @@ -0,0 +1,29 @@ +package com.tungsten.fclcore.game; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public final class LibrariesDownloadInfo { + + private final LibraryDownloadInfo artifact; + private final Map classifiers; + + public LibrariesDownloadInfo(LibraryDownloadInfo artifact) { + this(artifact, null); + } + + public LibrariesDownloadInfo(LibraryDownloadInfo artifact, Map classifiers) { + this.artifact = artifact; + this.classifiers = classifiers == null ? null : new HashMap<>(classifiers); + } + + public LibraryDownloadInfo getArtifact() { + return artifact; + } + + public Map getClassifiers() { + return classifiers == null ? Collections.emptyMap() : Collections.unmodifiableMap(classifiers); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/Library.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/Library.java new file mode 100644 index 00000000..33f295ed --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/Library.java @@ -0,0 +1,197 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.*; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.constant.UrlConstants; +import com.tungsten.fclcore.util.ToStringBuilder; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; +import com.tungsten.fclcore.util.platform.OperatingSystem; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class Library implements Comparable, Validation { + + @SerializedName("name") + private final Artifact artifact; + private final String url; + private final LibrariesDownloadInfo downloads; + private final ExtractRules extract; + private final Map natives; + private final List rules; + private final List checksums; + + @SerializedName(value = "hint", alternate = {"MMC-hint"}) + private final String hint; + + @SerializedName(value = "filename", alternate = {"MMC-filename"}) + private final String fileName; + + public Library(Artifact artifact) { + this(artifact, null, null); + } + + public Library(Artifact artifact, String url, LibrariesDownloadInfo downloads) { + this(artifact, url, downloads, null, null, null, null, null, null); + } + + public Library(Artifact artifact, String url, LibrariesDownloadInfo downloads, List checksums, ExtractRules extract, Map natives, List rules, String hint, String filename) { + this.artifact = artifact; + this.url = url; + this.downloads = downloads; + this.extract = extract; + this.natives = natives; + this.rules = rules; + this.checksums = checksums; + this.hint = hint; + this.fileName = filename; + } + + public String getGroupId() { + return artifact.getGroup(); + } + + public String getArtifactId() { + return artifact.getName(); + } + + public String getName() { + return artifact.toString(); + } + + public String getVersion() { + return artifact.getVersion(); + } + + public String getClassifier() { + if (artifact.getClassifier() == null) + return null; + else + return artifact.getClassifier(); + } + + public ExtractRules getExtract() { + return extract == null ? ExtractRules.EMPTY : extract; + } + + public boolean appliesToCurrentEnvironment() { + return CompatibilityRule.appliesToCurrentEnvironment(rules); + } + + public boolean isNative() { + return natives != null && appliesToCurrentEnvironment(); + } + + protected LibraryDownloadInfo getRawDownloadInfo() { + if (downloads != null) { + if (isNative()) + return downloads.getClassifiers().get(getClassifier()); + else + return downloads.getArtifact(); + } else { + return null; + } + } + + public String getPath() { + LibraryDownloadInfo temp = getRawDownloadInfo(); + if (temp != null && temp.getPath() != null) + return temp.getPath(); + else + return artifact.setClassifier(getClassifier()).getPath(); + } + + public LibraryDownloadInfo getDownload() { + LibraryDownloadInfo temp = getRawDownloadInfo(); + String path = getPath(); + String finalUrl = UrlConstants.DEFAULT_LIBRARY_URL + path; + if (temp != null) { + finalUrl = temp.getUrl(); + } + else if (url != null) { + finalUrl = url + path; + } + return new LibraryDownloadInfo(path, + finalUrl, + temp != null ? temp.getSha1() : null, + temp != null ? temp.getSize() : 0 + ); + } + + public boolean hasDownloadURL() { + LibraryDownloadInfo temp = getRawDownloadInfo(); + if (temp != null) return temp.getUrl() != null; + else return url != null; + } + + public List getChecksums() { + return checksums; + } + + public List getRules() { + return rules; + } + + /** + * Hint for how to locate the library file. + * @return null for default, "local" for location in version/<version>/libraries/filename + */ + @Nullable + public String getHint() { + return hint; + } + + /** + * Available when hint is "local" + * @return the filename of the local library in version/<version>/libraries/$filename + */ + @Nullable + public String getFileName() { + return fileName; + } + + public boolean is(String groupId, String artifactId) { + return getGroupId().equals(groupId) && getArtifactId().equals(artifactId); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("name", getName()).toString(); + } + + @Override + public int compareTo(Library o) { + if (getName().compareTo(o.getName()) == 0) + return Boolean.compare(isNative(), o.isNative()); + else + return getName().compareTo(o.getName()); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Library)) + return false; + + Library other = (Library) obj; + return getName().equals(other.getName()) && (isNative() == other.isNative()); + } + + @Override + public int hashCode() { + return Objects.hash(getName(), isNative()); + } + + public Library setClassifier(String classifier) { + return new Library(artifact.setClassifier(classifier), url, downloads, checksums, extract, natives, rules, hint, fileName); + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (artifact == null) + throw new JsonParseException("Library.name cannot be null"); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/LibraryDownloadInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/LibraryDownloadInfo.java new file mode 100644 index 00000000..3aec6390 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/LibraryDownloadInfo.java @@ -0,0 +1,34 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.annotations.SerializedName; + +public class LibraryDownloadInfo extends DownloadInfo { + + @SerializedName("path") + private final String path; + + public LibraryDownloadInfo() { + this(null); + } + + public LibraryDownloadInfo(String path) { + this(path, ""); + } + + public LibraryDownloadInfo(String path, String url) { + this(path, url, null); + } + + public LibraryDownloadInfo(String path, String url, String sha1) { + this(path, url, sha1, 0); + } + + public LibraryDownloadInfo(String path, String url, String sha1, int size) { + super(url, sha1, size); + this.path = path; + } + + public String getPath() { + return path; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/LoggingInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/LoggingInfo.java new file mode 100644 index 00000000..d836faa9 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/LoggingInfo.java @@ -0,0 +1,56 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; + +public final class LoggingInfo implements Validation { + + @SerializedName("file") + private final IdDownloadInfo file; + @SerializedName("argument") + private final String argument; + @SerializedName("type") + private final String type; + + public LoggingInfo() { + this(new IdDownloadInfo()); + } + + public LoggingInfo(IdDownloadInfo file) { + this(file, ""); + } + + public LoggingInfo(IdDownloadInfo file, String argument) { + this(file, argument, ""); + } + + public LoggingInfo(IdDownloadInfo file, String argument, String type) { + this.file = file; + this.argument = argument; + this.type = type; + } + + public IdDownloadInfo getFile() { + return file; + } + + public String getArgument() { + return argument; + } + + public String getType() { + return type; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + file.validate(); + if (StringUtils.isBlank(argument)) + throw new JsonParseException("LoggingInfo.argument is empty."); + if (StringUtils.isBlank(type)) + throw new JsonParseException("LoggingInfo.type is empty."); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/OSRestriction.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/OSRestriction.java new file mode 100644 index 00000000..7d59335d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/OSRestriction.java @@ -0,0 +1,45 @@ +package com.tungsten.fclcore.game; + +import com.tungsten.fclcore.util.platform.OperatingSystem; + +public final class OSRestriction { + + private final OperatingSystem name; + private final String version; + private final String arch; + + public OSRestriction() { + this(OperatingSystem.UNKNOWN); + } + + public OSRestriction(OperatingSystem name) { + this(name, null); + } + + public OSRestriction(OperatingSystem name, String version) { + this(name, version, null); + } + + public OSRestriction(OperatingSystem name, String version, String arch) { + this.name = name; + this.version = version; + this.arch = arch; + } + + public OperatingSystem getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getArch() { + return arch; + } + + public boolean allow() { + return true; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/ProcessPriority.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/ProcessPriority.java new file mode 100644 index 00000000..a0deb72f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/ProcessPriority.java @@ -0,0 +1,17 @@ +package com.tungsten.fclcore.game; + +public enum ProcessPriority { + LOW(Thread.MIN_PRIORITY), + NORMAL(Thread.NORM_PRIORITY), + HIGH(Thread.MAX_PRIORITY); + + private final int priority; + + ProcessPriority(int priority) { + this.priority = priority; + } + + public int getPriority() { + return priority; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/ReleaseType.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/ReleaseType.java new file mode 100644 index 00000000..e5ab4fc9 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/ReleaseType.java @@ -0,0 +1,21 @@ +package com.tungsten.fclcore.game; + +public enum ReleaseType { + RELEASE("release"), + SNAPSHOT("snapshot"), + MODIFIED("modified"), + OLD_BETA("old-beta"), + OLD_ALPHA("old-alpha"), + UNKNOWN("unknown"); + + private final String id; + + ReleaseType(String id) { + this.id = id; + } + + public String getId() { + return id; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/RuledArgument.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/RuledArgument.java new file mode 100644 index 00000000..d1979059 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/RuledArgument.java @@ -0,0 +1,92 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.*; +import java.util.stream.Collectors; + +@JsonAdapter(RuledArgument.Serializer.class) +public class RuledArgument implements Argument { + + private final List rules; + private final List value; + + public RuledArgument() { + this(null, null); + } + + public RuledArgument(List rules, List args) { + this.rules = rules; + this.value = args; + } + + public List getRules() { + return Collections.unmodifiableList(rules); + } + + public List getValue() { + return Collections.unmodifiableList(value); + } + + @Override + public Object clone() { + return new RuledArgument( + rules == null ? null : new ArrayList<>(rules), + value == null ? null : new ArrayList<>(value) + ); + } + + @Override + public List toString(Map keys, Map features) { + if (CompatibilityRule.appliesToCurrentEnvironment(rules, features) && value != null) { + List list = new ArrayList<>(); + for (String v : value) { + StringArgument stringArgument = new StringArgument(v); + list.add(stringArgument.toString(keys, features).get(0)); + } + return list; + } + return Collections.emptyList(); + } + + public static class Serializer implements JsonSerializer, JsonDeserializer { + @Override + public JsonElement serialize(RuledArgument src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + obj.add("rules", context.serialize(src.rules)); + obj.add("value", context.serialize(src.value)); + return obj; + } + + @Override + public RuledArgument deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject obj = json.getAsJsonObject(); + + List rules = context.deserialize(obj.get("rules"), new TypeToken>() { + }.getType()); + + JsonElement valuesElement; + if (obj.has("values")) { + valuesElement = obj.get("values"); + } else if (obj.has("value")) { + valuesElement = obj.get("value"); + } else { + throw new JsonParseException("RuledArguments instance does not have either value or values member."); + } + + List values; + if (valuesElement.isJsonPrimitive()) { + values = Collections.singletonList(valuesElement.getAsString()); + } else { + values = context.deserialize(valuesElement, new TypeToken>() { + }.getType()); + } + + return new RuledArgument(rules, values); + } + + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/SimpleVersionProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/SimpleVersionProvider.java new file mode 100644 index 00000000..0b663ddf --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/SimpleVersionProvider.java @@ -0,0 +1,30 @@ +package com.tungsten.fclcore.game; + +import java.util.HashMap; +import java.util.Map; + +public class SimpleVersionProvider implements VersionProvider { + + protected final Map versionMap = new HashMap<>(); + + @Override + public boolean hasVersion(String id) { + return versionMap.containsKey(id); + } + + @Override + public Version getVersion(String id) { + if (!hasVersion(id)) + throw new VersionNotFoundException("Version id " + id + " not found"); + else + return versionMap.get(id); + } + + public void addVersion(Version version) { + versionMap.put(version.getId(), version); + } + + public Map getVersionMap() { + return versionMap; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/StringArgument.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/StringArgument.java new file mode 100644 index 00000000..322e3958 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/StringArgument.java @@ -0,0 +1,53 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.annotations.JsonAdapter; + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@JsonAdapter(StringArgument.Serializer.class) +public final class StringArgument implements Argument { + + private final String argument; + + public StringArgument(String argument) { + this.argument = argument; + } + + public String getArgument() { + return argument; + } + + @Override + public List toString(Map keys, Map features) { + String res = argument; + Pattern pattern = Pattern.compile("\\$\\{(.*?)}"); + Matcher m = pattern.matcher(argument); + while (m.find()) { + String entry = m.group(); + res = res.replace(entry, keys.get(entry) == null ? entry : Objects.requireNonNull(keys.get(entry))); + } + return Collections.singletonList(res); + } + + @Override + public String toString() { + return argument; + } + + public class Serializer implements JsonSerializer { + @Override + public JsonElement serialize(StringArgument src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.getArgument()); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/Version.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/Version.java new file mode 100644 index 00000000..5ae9ff07 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/Version.java @@ -0,0 +1,473 @@ +package com.tungsten.fclcore.game; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.constant.UrlConstants; +import com.tungsten.fclcore.util.Lang; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.ToStringBuilder; +import com.tungsten.fclcore.util.gson.JsonMap; +import com.tungsten.fclcore.util.gson.Validation; + +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public class Version implements Comparable, Validation { + + private final String id; + private final String version; + private final Integer priority; + private final String minecraftArguments; + private final Arguments arguments; + private final String mainClass; + private final String inheritsFrom; + private final String jar; + private final AssetIndexInfo assetIndex; + private final String assets; + private final Integer complianceLevel; + @Nullable + private final GameJavaVersion javaVersion; + private final List libraries; + private final List compatibilityRules; + private final JsonMap downloads; + private final JsonMap logging; + private final ReleaseType type; + private final Date time; + private final Date releaseTime; + private final Integer minimumLauncherVersion; + private final Boolean root; + private final Boolean hidden; + private final List patches; + + private transient final boolean resolved; + + public Version(String id) { + this(false, id, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, false, true, null); + } + + /** + * Constructor for patch + * + * @param id patch id + * @param version patch version + * @param priority patch priority + * @param arguments patch additional arguments + * @param mainClass main class to override + * @param libraries additional libraries + */ + public Version(String id, String version, int priority, Arguments arguments, String mainClass, List libraries) { + this(false, id, version, priority, null, arguments, mainClass, null, null, null, null, null, null, libraries, null, null, null, null, null, null, null, null, null, null); + } + + public Version(boolean resolved, String id, String version, Integer priority, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, GameJavaVersion javaVersion, List libraries, List compatibilityRules, Map downloads, Map logging, ReleaseType type, Date time, Date releaseTime, Integer minimumLauncherVersion, Boolean hidden, Boolean root, List patches) { + this.resolved = resolved; + this.id = id; + this.version = version; + this.priority = priority; + this.minecraftArguments = minecraftArguments; + this.arguments = arguments; + this.mainClass = mainClass; + this.inheritsFrom = inheritsFrom; + this.jar = jar; + this.assetIndex = assetIndex; + this.assets = assets; + this.complianceLevel = complianceLevel; + this.javaVersion = javaVersion; + this.libraries = Lang.copyList(libraries); + this.compatibilityRules = Lang.copyList(compatibilityRules); + this.downloads = downloads == null ? null : new JsonMap<>(downloads); + this.logging = logging == null ? null : new JsonMap<>(logging); + this.type = type; + this.time = time == null ? null : (Date) time.clone(); + this.releaseTime = releaseTime == null ? null : (Date) releaseTime.clone(); + this.minimumLauncherVersion = minimumLauncherVersion; + this.hidden = hidden; + this.root = root; + this.patches = Lang.copyList(patches); + } + + public String getMinecraftArguments() { + return minecraftArguments; + } + + public Arguments getArguments() { + return arguments; + } + + public String getMainClass() { + return mainClass; + } + + public Date getTime() { + return time; + } + + public String getId() { + return id; + } + + /** + * Version of the patch. + * Exists only when this version object represents a patch. + * Example: 0.5.0.33 for fabric-loader, 28.0.46 for minecraft-forge. + */ + @Nullable + public String getVersion() { + return version; + } + + public int getPriority() { + return priority == null ? Integer.MIN_VALUE : priority; + } + + public ReleaseType getType() { + return type == null ? ReleaseType.UNKNOWN : type; + } + + public Date getReleaseTime() { + return releaseTime; + } + + public String getJar() { + return jar; + } + + public String getInheritsFrom() { + return inheritsFrom; + } + + public int getMinimumLauncherVersion() { + return minimumLauncherVersion == null ? 0 : minimumLauncherVersion; + } + + public Integer getComplianceLevel() { + return complianceLevel; + } + + public GameJavaVersion getJavaVersion() { + return javaVersion; + } + + public boolean isHidden() { + return hidden; + } + + public boolean isRoot() { + return root; + } + + public boolean isResolved() { + return resolved; + } + + public boolean isResolvedPreservingPatches() { + return inheritsFrom == null && !resolved; + } + + public List getPatches() { + return patches == null ? Collections.emptyList() : patches; + } + + public Map getLogging() { + return logging == null ? Collections.emptyMap() : Collections.unmodifiableMap(logging); + } + + public List getLibraries() { + return libraries == null ? Collections.emptyList() : Collections.unmodifiableList(libraries); + } + + public List getCompatibilityRules() { + return compatibilityRules == null ? Collections.emptyList() : Collections.unmodifiableList(compatibilityRules); + } + + public Map getDownloads() { + return downloads == null ? Collections.emptyMap() : Collections.unmodifiableMap(downloads); + } + + public DownloadInfo getDownloadInfo() { + DownloadInfo client = downloads == null ? null : downloads.get(DownloadType.CLIENT); + String jarName = jar == null ? id : jar; + if (client == null) + return new DownloadInfo(String.format("%s%s/%s.jar", UrlConstants.DEFAULT_VERSION_DOWNLOAD_URL, jarName, jarName)); + else + return client; + } + + public AssetIndexInfo getAssetIndex() { + String assetsId = assets == null ? "legacy" : assets; + return assetIndex == null ? new AssetIndexInfo(assetsId, UrlConstants.DEFAULT_INDEX_URL + assetsId + ".json") : assetIndex; + } + + public boolean appliesToCurrentEnvironment() { + return CompatibilityRule.appliesToCurrentEnvironment(compatibilityRules); + } + + /** + * Resolve given version. + * Resolving version will list all patches within this version and its parents, + * which is for analysis. + */ + public Version resolve(VersionProvider provider) throws VersionNotFoundException { + if (isResolved()) return this; + return resolve(provider, new HashSet<>()).markAsResolved(); + } + + protected Version merge(Version parent, boolean isPatch) { + return new Version( + true, + id, + null, + null, + minecraftArguments == null ? parent.minecraftArguments : minecraftArguments, + Arguments.merge(parent.arguments, arguments), + mainClass == null ? parent.mainClass : mainClass, + null, // inheritsFrom + jar == null ? parent.jar : jar, + assetIndex == null ? parent.assetIndex : assetIndex, + assets == null ? parent.assets : assets, + complianceLevel, + javaVersion == null ? parent.javaVersion : javaVersion, + Lang.merge(this.libraries, parent.libraries), + Lang.merge(parent.compatibilityRules, this.compatibilityRules), + downloads == null ? parent.downloads : downloads, + logging == null ? parent.logging : logging, + type == null ? parent.type : type, + time == null ? parent.time : time, + releaseTime == null ? parent.releaseTime : releaseTime, + Math.max(minimumLauncherVersion, parent.minimumLauncherVersion), + hidden, + true, + isPatch ? parent.patches : Lang.merge(Lang.merge(parent.patches, Collections.singleton(toPatch())), patches)); + } + + protected Version resolve(VersionProvider provider, Set resolvedSoFar) throws VersionNotFoundException { + Version thisVersion; + + if (inheritsFrom == null) { + if (isRoot()) { + thisVersion = new Version(id).setPatches(patches); + } else { + thisVersion = this; + } + thisVersion = this.jar == null ? thisVersion.setJar(id) : thisVersion.setJar(this.jar); + } else { + // To maximize the compatibility. + if (!resolvedSoFar.add(id)) { + Logging.LOG.log(Level.WARNING, "Found circular dependency versions: " + resolvedSoFar); + thisVersion = this.jar == null ? this.setJar(id) : this; + } else { + // It is supposed to auto install an version in getVersion. + thisVersion = merge(provider.getVersion(inheritsFrom).resolve(provider, resolvedSoFar), false); + } + } + + if (patches != null && !patches.isEmpty()) { + // Assume patches themselves do not have patches recursively. + List sortedPatches = patches.stream() + .sorted(Comparator.comparing(Version::getPriority)) + .collect(Collectors.toList()); + for (Version patch : sortedPatches) { + thisVersion = patch.setJar(null).merge(thisVersion, true); + } + } + + return thisVersion.setId(id); + } + + private Version toPatch() { + return this.clearPatches().setHidden(true).setId("resolved." + getId()); + } + + /** + * Resolve the version preserving all dependencies and patches. + */ + public Version resolvePreservingPatches(VersionProvider provider) throws VersionNotFoundException { + return resolvePreservingPatches(provider, new HashSet<>()); + } + + protected Version mergePreservingPatches(Version parent) { + return parent.addPatch(toPatch()).addPatches(patches); + } + + protected Version resolvePreservingPatches(VersionProvider provider, Set resolvedSoFar) throws VersionNotFoundException { + Version thisVersion = isRoot() ? this : new Version(id).addPatch(toPatch()).addPatches(getPatches()); + + if (inheritsFrom == null) { + // keep thisVersion + } else { + // To maximize the compatibility. + if (!resolvedSoFar.add(id)) { + Logging.LOG.log(Level.WARNING, "Found circular dependency versions: " + resolvedSoFar); + // keep thisVersion + } else { + // It is supposed to auto install an version in getVersion. + thisVersion = mergePreservingPatches(provider.getVersion(inheritsFrom).resolvePreservingPatches(provider, resolvedSoFar)); + } + } + + return thisVersion.setId(id).setJar(resolve(provider).getJar()); + } + + private Version markAsResolved() { + return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version markAsUnresolved() { + return new Version(false, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + private Version setHidden(Boolean hidden) { + return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setId(String id) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setVersion(String version) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setPriority(Integer priority) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setMinecraftArguments(String minecraftArguments) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setArguments(Arguments arguments) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setMainClass(String mainClass) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setInheritsFrom(String inheritsFrom) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setJar(String jar) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setLibraries(List libraries) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setLogging(Map logging) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version setPatches(List patches) { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version addPatch(Version... additional) { + return addPatches(Arrays.asList(additional)); + } + + public Version addPatches(@Nullable List additional) { + Set patchIds = new HashSet<>(); + if (additional != null) { + for (Version v : additional) { + patchIds.add(v.getId()); + } + } + List oldPatches = null; + if (this.patches != null) { + oldPatches = new ArrayList<>(); + for (Version v : this.patches) { + if (!patchIds.contains(v.getId())) { + oldPatches.add(v); + } + } + } + List patches = Lang.merge(oldPatches, additional); + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); + } + + public Version clearPatches() { + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, null); + } + + public Version removePatchById(String patchId) { + List newPatches = null; + if (patches != null) { + newPatches = new ArrayList<>(); + for (Version p : patches) { + if (!patchId.equals(p.getId())) { + newPatches.add(p); + } + } + } + return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, newPatches); + } + + public boolean hasPatch(String patchId) { + boolean has = false; + if (patches != null) { + for (Version patch : patches) { + if (patch.getId().equals(patchId)) { + has = true; + break; + } + } + } + return has; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Version && Objects.equals(id, ((Version) obj).id); + } + + @Override + public int compareTo(Version o) { + return id.compareTo(o.id); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("id", id).toString(); + } + + @Override + public void validate() throws JsonParseException { + if (StringUtils.isBlank(id)) + throw new JsonParseException("Version ID cannot be blank"); + if (downloads != null) + for (Map.Entry entry : downloads.entrySet()) { + if (!(entry.getKey() instanceof DownloadType)) + throw new JsonParseException("Version downloads key must be DownloadType"); + if (!(entry.getValue() instanceof DownloadInfo)) + throw new JsonParseException("Version downloads value must be DownloadInfo"); + } + if (logging != null) + for (Map.Entry entry : logging.entrySet()) { + if (!(entry.getKey() instanceof DownloadType)) + throw new JsonParseException("Version logging key must be DownloadType"); + if (!(entry.getValue() instanceof LoggingInfo)) + throw new JsonParseException("Version logging value must be LoggingInfo"); + } + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionJsonType.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionJsonType.java new file mode 100644 index 00000000..43bfd4bc --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionJsonType.java @@ -0,0 +1,6 @@ +package com.tungsten.fclcore.game; + +public enum VersionJsonType { + OFFICIAL, + TLAUNCHER +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionLibraryBuilder.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionLibraryBuilder.java new file mode 100644 index 00000000..d04fe2a2 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionLibraryBuilder.java @@ -0,0 +1,198 @@ +package com.tungsten.fclcore.game; + +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.platform.CommandBuilder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public final class VersionLibraryBuilder { + private final Version version; + private final List mcArgs; + private final List game; + private final List jvm; + private final List libraries; + private final boolean useMcArgs; + private boolean jvmChanged = false; + + public VersionLibraryBuilder(Version version) { + this.version = version; + this.libraries = new ArrayList<>(version.getLibraries()); + this.mcArgs = version.getMinecraftArguments() != null ? StringUtils.tokenize(version.getMinecraftArguments()) : null; + this.game = new ArrayList<>(version.getArguments().getGame() != null ? version.getArguments().getGame() : Arguments.DEFAULT_GAME_ARGUMENTS); + this.jvm = new ArrayList<>(version.getArguments().getJvm() != null ? version.getArguments().getJvm() : Arguments.DEFAULT_JVM_ARGUMENTS); + this.useMcArgs = mcArgs != null; + } + + public Version build() { + Version ret = version; + if (useMcArgs) { + // The official launcher will not parse the "arguments" property when it detects the presence of "mcArgs". + // The "arguments" property with the "rule" is simply ignored here. + for (Argument arg : this.game) { + List argStr = arg.toString(new HashMap<>(), new HashMap<>()); + this.mcArgs.addAll(argStr); + } + ret = ret.setArguments(null); + + // Since $ will be escaped in linux, and our maintain of minecraftArgument will not cause escaping, + // so we regenerate the minecraftArgument without escaping. + ret = ret.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString()); + } else { + if (ret.getArguments() != null) { + ret = ret.setArguments(ret.getArguments().withGame(game)); + if (jvmChanged) { + ret = ret.setArguments(ret.getArguments().withJvm(jvm)); + } + } + else { + ret = ret.setArguments(new Arguments(game, jvmChanged ? jvm : null)); + } + } + return ret.setLibraries(libraries); + } + + public boolean hasTweakClass(String tweakClass) { + boolean match = false; + for (Argument argument : game) { + if (argument.toString().equals(tweakClass)) { + match = true; + break; + } + } + return useMcArgs && mcArgs.contains(tweakClass) || match; + } + + public void removeTweakClass(String target) { + replaceTweakClass(target, null, false); + } + + /** + * Replace existing tweak class without reordering. + * If the tweak class does not exist, the new tweak class will be appended to the end of argument list. + * If the tweak class appears more than one time, the tweak classes will be removed excluding the first one. + * + * @param target the tweak class to replace + * @param replacement the new tweak class to be replaced with + */ + public void replaceTweakClass(String target, String replacement) { + replaceTweakClass(target, replacement, true); + } + + /** + * Replace existing tweak class and add the new tweak class to the end of argument list. + * + * @param target the tweak class to replace + * @param replacement the new tweak class to be replaced with + */ + public void addTweakClass(String target, String replacement) { + replaceTweakClass(target, replacement, false); + } + + /** + * Replace existing tweak class. + * If the tweak class does not exist, the new tweak class will be appended to the end of argument list. + * If the tweak class appears more than one time, the tweak classes will be removed excluding the first one. + * + * @param target the tweak class to replace + * @param replacement the new tweak class to be replaced with, if null, remove the tweak class only + * @param inPlace if true, replace the tweak class in place, otherwise add the tweak class to the end of the argument list without replacement. + */ + public void replaceTweakClass(String target, String replacement, boolean inPlace) { + replaceTweakClass(target, replacement, inPlace, false); + } + + /** + * Replace existing tweak class. + * If the tweak class does not exist, the new tweak class will be added to argument list. + * If the tweak class appears more than one time, the tweak classes will be removed excluding the first one. + * + * @param target the tweak class to replace + * @param replacement the new tweak class to be replaced with, if null, remove the tweak class only + * @param inPlace if true, replace the tweak class in place, otherwise add the tweak class to the end of the argument list without replacement. + * @param reserve if true, add the tweak class to the start of the argument list. + */ + public void replaceTweakClass(String target, String replacement, boolean inPlace, boolean reserve) { + if (replacement == null && inPlace) + throw new IllegalArgumentException("Replacement cannot be null in replace mode"); + + boolean replaced = false; + if (useMcArgs) { + for (int i = 0; i + 1 < mcArgs.size(); ++i) { + String arg0Str = mcArgs.get(i); + String arg1Str = mcArgs.get(i + 1); + if (arg0Str.equals("--tweakClass") && arg1Str.equals(target)) { + if (!replaced && inPlace) { + // for the first one, we replace the tweak class only. + mcArgs.set(i + 1, replacement); + replaced = true; + } else { + // otherwise, we remove the duplicate tweak classes. + mcArgs.remove(i); + mcArgs.remove(i); + --i; + } + } + } + } + + for (int i = 0; i + 1 < game.size(); ++i) { + Argument arg0 = game.get(i); + Argument arg1 = game.get(i + 1); + if (arg0 instanceof StringArgument && arg1 instanceof StringArgument) { + // We need to preserve the tokens + String arg0Str = arg0.toString(); + String arg1Str = arg1.toString(); + if (arg0Str.equals("--tweakClass") && arg1Str.equals(target)) { + if (!replaced && inPlace) { + // for the first one, we replace the tweak class only. + game.set(i + 1, new StringArgument(replacement)); + replaced = true; + } else { + // otherwise, we remove the duplicate tweak classes. + game.remove(i); + game.remove(i); + --i; + } + } + } + } + + // if the tweak class does not exist, add a new one to the end. + if (!replaced && replacement != null) { + if (reserve) { + if (useMcArgs) { + mcArgs.add(0, replacement); + mcArgs.add(0, "--tweakClass"); + } else { + game.add(0, new StringArgument(replacement)); + game.add(0, new StringArgument("--tweakClass")); + } + } else { + game.add(new StringArgument("--tweakClass")); + game.add(new StringArgument(replacement)); + } + } + } + + public List getMutableJvmArguments() { + jvmChanged = true; + return jvm; + } + + public void addGameArgument(String... args) { + for (String arg : args) + game.add(new StringArgument(arg)); + } + + public void addJvmArgument(String... args) { + jvmChanged = true; + for (String arg : args) + jvm.add(new StringArgument(arg)); + } + + public void addLibrary(Library library) { + libraries.add(library); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionNotFoundException.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionNotFoundException.java new file mode 100644 index 00000000..35462c0c --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionNotFoundException.java @@ -0,0 +1,15 @@ +package com.tungsten.fclcore.game; + +public final class VersionNotFoundException extends RuntimeException { + + public VersionNotFoundException() { + } + + public VersionNotFoundException(String message) { + super(message); + } + + public VersionNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionProvider.java new file mode 100644 index 00000000..552ba517 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/VersionProvider.java @@ -0,0 +1,25 @@ +package com.tungsten.fclcore.game; + +/** + * Supports version accessing. + * + * @see Version#resolve + */ +public interface VersionProvider { + + /** + * Does the version of id exist? + * + * @param id the id of version + * @return true if the version exists + */ + boolean hasVersion(String id); + + /** + * Get the version + * + * @param id the id of version + * @return the version you want + */ + Version getVersion(String id) throws VersionNotFoundException; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/World.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/World.java new file mode 100644 index 00000000..d9ff8607 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/World.java @@ -0,0 +1,235 @@ +package com.tungsten.fclcore.game; + +import com.github.steveice10.opennbt.NBTIO; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.LongTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.tungsten.fclcore.util.io.FileUtils; + +import org.jackhuang.hmcl.util.*; +import org.jackhuang.hmcl.util.io.CompressingUtils; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.Unzipper; +import org.jackhuang.hmcl.util.io.Zipper; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.*; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class World { + + private final Path file; + private String fileName; + private String worldName; + private String gameVersion; + private long lastPlayed; + + public World(Path file) throws IOException { + this.file = file; + + if (Files.isDirectory(file)) + loadFromDirectory(); + else if (Files.isRegularFile(file)) + loadFromZip(); + else + throw new IOException("Path " + file + " cannot be recognized as a Minecraft world"); + } + + private void loadFromDirectory() throws IOException { + fileName = FileUtils.getName(file); + Path levelDat = file.resolve("level.dat"); + getWorldName(levelDat); + } + + public Path getFile() { + return file; + } + + public String getFileName() { + return fileName; + } + + public String getWorldName() { + return worldName; + } + + public Path getLevelDatFile() { + return file.resolve("level.dat"); + } + + public long getLastPlayed() { + return lastPlayed; + } + + public String getGameVersion() { + return gameVersion; + } + + private void loadFromZipImpl(Path root) throws IOException { + Path levelDat = root.resolve("level.dat"); + if (!Files.exists(levelDat)) + throw new IOException("Not a valid world zip file since level.dat cannot be found."); + + getWorldName(levelDat); + } + + private void loadFromZip() throws IOException { + try (FileSystem fs = CompressingUtils.readonly(file).setAutoDetectEncoding(true).build()) { + Path cur = fs.getPath("/level.dat"); + if (Files.isRegularFile(cur)) { + fileName = FileUtils.getName(file); + loadFromZipImpl(fs.getPath("/")); + return; + } + + try (Stream stream = Files.list(fs.getPath("/"))) { + Path root = stream.filter(Files::isDirectory).findAny() + .orElseThrow(() -> new IOException("Not a valid world zip file")); + fileName = FileUtils.getName(root); + loadFromZipImpl(root); + } + } + } + + private void getWorldName(Path levelDat) throws IOException { + CompoundTag nbt = parseLevelDat(levelDat); + + CompoundTag data = nbt.get("Data"); + if (data == null) + throw new IOException("level.dat missing Data"); + + if (data.get("LevelName") instanceof StringTag) + worldName = data.get("LevelName").getValue(); + else + throw new IOException("level.dat missing LevelName"); + + if (data.get("LastPlayed") instanceof LongTag) + lastPlayed = data.get("LastPlayed").getValue(); + else + throw new IOException("level.dat missing LastPlayed"); + + gameVersion = null; + if (data.get("Version") instanceof CompoundTag) { + CompoundTag version = data.get("Version"); + + if (version.get("Name") instanceof StringTag) + gameVersion = version.get("Name").getValue(); + } + } + + public void rename(String newName) throws IOException { + if (!Files.isDirectory(file)) + throw new IOException("Not a valid world directory"); + + // Change the name recorded in level.dat + CompoundTag nbt = readLevelDat(); + CompoundTag data = nbt.get("Data"); + data.put(new StringTag("LevelName", newName)); + writeLevelDat(nbt); + + // then change the folder's name + Files.move(file, file.resolveSibling(newName)); + } + + public void install(Path savesDir, String name) throws IOException { + Path worldDir; + try { + worldDir = savesDir.resolve(name); + } catch (InvalidPathException e) { + throw new IOException(e); + } + + if (Files.isDirectory(worldDir)) { + throw new FileAlreadyExistsException("World already exists"); + } + + if (Files.isRegularFile(file)) { + try (FileSystem fs = CompressingUtils.readonly(file).setAutoDetectEncoding(true).build()) { + Path cur = fs.getPath("/level.dat"); + if (Files.isRegularFile(cur)) { + fileName = FileUtils.getName(file); + + new Unzipper(file, worldDir).unzip(); + } else { + try (Stream stream = Files.list(fs.getPath("/"))) { + List subDirs = stream.collect(Collectors.toList()); + if (subDirs.size() != 1) { + throw new IOException("World zip malformed"); + } + String subDirectoryName = FileUtils.getName(subDirs.get(0)); + new Unzipper(file, worldDir) + .setSubDirectory("/" + subDirectoryName + "/") + .unzip(); + } + } + + } + new World(worldDir).rename(name); + } else if (Files.isDirectory(file)) { + FileUtils.copyDirectory(file, worldDir); + } + } + + public void export(Path zip, String worldName) throws IOException { + if (!Files.isDirectory(file)) + throw new IOException(); + + try (Zipper zipper = new Zipper(zip)) { + zipper.putDirectory(file, "/" + worldName + "/"); + } + } + + public CompoundTag readLevelDat() throws IOException { + if (!Files.isDirectory(file)) + throw new IOException("Not a valid world directory"); + + return parseLevelDat(getLevelDatFile()); + } + + public void writeLevelDat(CompoundTag nbt) throws IOException { + if (!Files.isDirectory(file)) + throw new IOException("Not a valid world directory"); + + FileUtils.saveSafely(getLevelDatFile(), os -> { + try (OutputStream gos = new GZIPOutputStream(os)) { + NBTIO.writeTag(gos, nbt); + } + }); + } + + private static CompoundTag parseLevelDat(Path path) throws IOException { + try (InputStream is = new GZIPInputStream(Files.newInputStream(path))) { + Tag nbt = NBTIO.readTag(is); + if (nbt instanceof CompoundTag) + return (CompoundTag) nbt; + else + throw new IOException("level.dat malformed"); + } + } + + public static Stream getWorlds(Path savesDir) { + try { + if (Files.exists(savesDir)) { + return Files.list(savesDir).flatMap(world -> { + try { + return Stream.of(new World(world)); + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Failed to read world " + world, e); + return Stream.empty(); + } + }); + } + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Failed to read saves", e); + } + return Stream.empty(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/tlauncher/TLauncherLibrary.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/tlauncher/TLauncherLibrary.java new file mode 100644 index 00000000..b2fb7230 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/tlauncher/TLauncherLibrary.java @@ -0,0 +1,53 @@ +package com.tungsten.fclcore.game.tlauncher; + +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.game.Artifact; +import com.tungsten.fclcore.game.CompatibilityRule; +import com.tungsten.fclcore.game.ExtractRules; +import com.tungsten.fclcore.game.LibrariesDownloadInfo; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.game.LibraryDownloadInfo; +import com.tungsten.fclcore.util.platform.OperatingSystem; + +import java.util.List; +import java.util.Map; + +public class TLauncherLibrary { + + @SerializedName("name") + private final Artifact name; + private final String url; + private final LibraryDownloadInfo artifact; + + @SerializedName("classifies") // stupid typo made by TLauncher + private final Map classifiers; + private final ExtractRules extract; + private final Map natives; + private final List rules; + private final List checksums; + + public TLauncherLibrary(Artifact name, String url, LibraryDownloadInfo artifact, Map classifiers, ExtractRules extract, Map natives, List rules, List checksums) { + this.name = name; + this.url = url; + this.artifact = artifact; + this.classifiers = classifiers; + this.extract = extract; + this.natives = natives; + this.rules = rules; + this.checksums = checksums; + } + + public Library toLibrary() { + return new Library( + name, + url, + new LibrariesDownloadInfo(artifact, classifiers), + checksums, + extract, + natives, + rules, + null, + null + ); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/game/tlauncher/TLauncherVersion.java b/FCLCore/src/main/java/com/tungsten/fclcore/game/tlauncher/TLauncherVersion.java new file mode 100644 index 00000000..45618b71 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/game/tlauncher/TLauncherVersion.java @@ -0,0 +1,101 @@ +package com.tungsten.fclcore.game.tlauncher; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.game.Arguments; +import com.tungsten.fclcore.game.AssetIndexInfo; +import com.tungsten.fclcore.game.CompatibilityRule; +import com.tungsten.fclcore.game.DownloadInfo; +import com.tungsten.fclcore.game.DownloadType; +import com.tungsten.fclcore.game.GameJavaVersion; +import com.tungsten.fclcore.game.LoggingInfo; +import com.tungsten.fclcore.game.ReleaseType; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.util.gson.JsonMap; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; + +import org.jetbrains.annotations.Nullable; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +public class TLauncherVersion implements Validation { + + private final String id; + private final String minecraftArguments; + private final Arguments arguments; + private final String mainClass; + private final String inheritsFrom; + private final String jar; + private final AssetIndexInfo assetIndex; + private final String assets; + private final Integer complianceLevel; + @Nullable + private final GameJavaVersion javaVersion; + private final List libraries; + private final List compatibilityRules; + private final JsonMap downloads; + private final JsonMap logging; + private final ReleaseType type; + private final Date time; + private final Date releaseTime; + private final Integer minimumLauncherVersion; + private final Integer tlauncherVersion; + + public TLauncherVersion(String id, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, @Nullable GameJavaVersion javaVersion, List libraries, List compatibilityRules, JsonMap downloads, JsonMap logging, ReleaseType type, Date time, Date releaseTime, Integer minimumLauncherVersion, Integer tlauncherVersion) { + this.id = id; + this.minecraftArguments = minecraftArguments; + this.arguments = arguments; + this.mainClass = mainClass; + this.inheritsFrom = inheritsFrom; + this.jar = jar; + this.assetIndex = assetIndex; + this.assets = assets; + this.complianceLevel = complianceLevel; + this.javaVersion = javaVersion; + this.libraries = libraries; + this.compatibilityRules = compatibilityRules; + this.downloads = downloads; + this.logging = logging; + this.type = type; + this.time = time; + this.releaseTime = releaseTime; + this.minimumLauncherVersion = minimumLauncherVersion; + this.tlauncherVersion = tlauncherVersion; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + Validation.requireNonNull(tlauncherVersion, "Not TLauncher version json format"); + } + + public Version toVersion() { + return new Version( + false, + id, + null, + null, + minecraftArguments, + arguments, + mainClass, + inheritsFrom, + jar, + assetIndex, + assets, + complianceLevel, + javaVersion, + libraries == null ? null : libraries.stream().map(TLauncherLibrary::toLibrary).collect(Collectors.toList()), + compatibilityRules, + downloads, + logging, + type, + time, + releaseTime, + minimumLauncherVersion, + null, + null, + null + ); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/Datapack.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/Datapack.java new file mode 100644 index 00000000..58288d12 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/Datapack.java @@ -0,0 +1,236 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.nio.file.*; +import java.util.*; +import java.util.logging.Level; + +public class Datapack { + private boolean isMultiple; + private final Path path; + private final ObservableList info = FXCollections.observableArrayList(); + + public Datapack(Path path) { + this.path = path; + } + + public Path getPath() { + return path; + } + + public ObservableList getInfo() { + return info; + } + + public void installTo(Path worldPath) throws IOException { + Path datapacks = worldPath.resolve("datapacks"); + + Set packs = new HashSet<>(); + for (Pack pack : info) packs.add(pack.getId()); + + if (Files.isDirectory(datapacks)) { + try (DirectoryStream directoryStream = Files.newDirectoryStream(datapacks)) { + for (Path datapack : directoryStream) { + if (Files.isDirectory(datapack) && packs.contains(FileUtils.getName(datapack))) + FileUtils.deleteDirectory(datapack.toFile()); + else if (Files.isRegularFile(datapack) && packs.contains(FileUtils.getNameWithoutExtension(datapack))) + Files.delete(datapack); + } + } + } + + if (isMultiple) { + new Unzipper(path, worldPath) + .setReplaceExistentFile(true) + .setFilter(new Unzipper.FileFilter() { + @Override + public boolean accept(Path destPath, boolean isDirectory, Path zipEntry, String entryPath) { + // We will merge resources.zip instead of replacement. + return !entryPath.equals("resources.zip"); + } + }) + .unzip(); + + try (FileSystem dest = CompressingUtils.createWritableZipFileSystem(worldPath.resolve("resources.zip")); + FileSystem zip = CompressingUtils.createReadOnlyZipFileSystem(path)) { + Path resourcesZip = zip.getPath("resources.zip"); + if (Files.isRegularFile(resourcesZip)) { + Path temp = Files.createTempFile("hmcl", ".zip"); + Files.copy(resourcesZip, temp, StandardCopyOption.REPLACE_EXISTING); + try (FileSystem resources = CompressingUtils.createReadOnlyZipFileSystem(temp)) { + FileUtils.copyDirectory(resources.getPath("/"), dest.getPath("/")); + } + } + Path packMcMeta = dest.getPath("pack.mcmeta"); + Files.write(packMcMeta, Arrays.asList("{", + "\t\"pack\": {", + "\t\t\"pack_format\": 4,", + "\t\t\"description\": \"Modified by HMCL.\"", + "\t}", + "}"), StandardOpenOption.CREATE); + + + Path packPng = dest.getPath("pack.png"); + if (Files.isRegularFile(packPng)) + Files.delete(packPng); + } + } else { + FileUtils.copyFile(path.toFile(), datapacks.resolve(FileUtils.getName(path)).toFile()); + } + } + + public void deletePack(Pack pack) throws IOException { + Path subPath = pack.file; + if (Files.isDirectory(subPath)) + FileUtils.deleteDirectory(subPath.toFile()); + else if (Files.isRegularFile(subPath)) + Files.delete(subPath); + + Platform.runLater(() -> info.removeIf(p -> p.getId().equals(pack.getId()))); + } + + public void loadFromZip() throws IOException { + try (FileSystem fs = CompressingUtils.readonly(path).setAutoDetectEncoding(true).build()) { + Path datapacks = fs.getPath("/datapacks/"); + Path mcmeta = fs.getPath("pack.mcmeta"); + if (Files.exists(datapacks)) { // multiple datapacks + isMultiple = true; + loadFromDir(datapacks); + } else if (Files.exists(mcmeta)) { // single datapack + isMultiple = false; + try { + PackMcMeta pack = JsonUtils.fromNonNullJson(FileUtils.readText(mcmeta), PackMcMeta.class); + Platform.runLater(() -> info.add(new Pack(path, FileUtils.getNameWithoutExtension(path), pack.getPackInfo().getDescription(), this))); + } catch (IOException | JsonParseException e) { + Logging.LOG.log(Level.WARNING, "Failed to read datapack " + path, e); + } + } else { + throw new IOException("Malformed datapack zip"); + } + } + } + + public void loadFromDir() { + try { + loadFromDir(path); + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Failed to read datapacks " + path, e); + } + } + + private void loadFromDir(Path dir) throws IOException { + List info = new ArrayList<>(); + + if (Files.isDirectory(dir)) { + try (DirectoryStream directoryStream = Files.newDirectoryStream(dir)) { + for (Path subDir : directoryStream) { + if (Files.isDirectory(subDir)) { + Path mcmeta = subDir.resolve("pack.mcmeta"); + Path mcmetaDisabled = subDir.resolve("pack.mcmeta.disabled"); + + if (!Files.exists(mcmeta) && !Files.exists(mcmetaDisabled)) + continue; + + boolean enabled = Files.exists(mcmeta); + + try { + PackMcMeta pack = enabled ? JsonUtils.fromNonNullJson(FileUtils.readText(mcmeta), PackMcMeta.class) + : JsonUtils.fromNonNullJson(FileUtils.readText(mcmetaDisabled), PackMcMeta.class); + info.add(new Pack(enabled ? mcmeta : mcmetaDisabled, FileUtils.getName(subDir), pack.getPackInfo().getDescription(), this)); + } catch (IOException | JsonParseException e) { + Logging.LOG.log(Level.WARNING, "Failed to read datapack " + subDir, e); + } + } else if (Files.isRegularFile(subDir)) { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(subDir)) { + Path mcmeta = fs.getPath("pack.mcmeta"); + + if (!Files.exists(mcmeta)) + continue; + + String name = FileUtils.getName(subDir); + if (name.endsWith(".disabled")) { + name = name.substring(0, name.length() - ".disabled".length()); + } + if (!name.endsWith(".zip")) + continue; + name = StringUtils.substringBeforeLast(name, ".zip"); + + PackMcMeta pack = JsonUtils.fromNonNullJson(FileUtils.readText(mcmeta), PackMcMeta.class); + info.add(new Pack(subDir, name, pack.getPackInfo().getDescription(), this)); + } catch (IOException | JsonParseException e) { + Logging.LOG.log(Level.WARNING, "Failed to read datapack " + subDir, e); + } + } + } + } + } + + Platform.runLater(() -> this.info.setAll(info)); + } + + public static class Pack { + private Path file; + private final BooleanProperty active; + private final String id; + private final LocalModFile.Description description; + private final Datapack datapack; + + public Pack(Path file, String id, LocalModFile.Description description, Datapack datapack) { + this.file = file; + this.id = id; + this.description = description; + this.datapack = datapack; + + active = new SimpleBooleanProperty(this, "active", !DISABLED_EXT.equals(FileUtils.getExtension(file))) { + @Override + protected void invalidated() { + Path f = Pack.this.file.toAbsolutePath(), newF; + if (DISABLED_EXT.equals(FileUtils.getExtension(f))) + newF = f.getParent().resolve(FileUtils.getNameWithoutExtension(f)); + else + newF = f.getParent().resolve(FileUtils.getName(f) + "." + DISABLED_EXT); + + try { + Files.move(f, newF); + Pack.this.file = newF; + } catch (IOException e) { + // Mod file is occupied. + Logging.LOG.warning("Unable to rename file " + f + " to " + newF); + } + } + }; + } + + public String getId() { + return id; + } + + public LocalModFile.Description getDescription() { + return description; + } + + public Datapack getDatapack() { + return datapack; + } + + public BooleanProperty activeProperty() { + return active; + } + + public boolean isActive() { + return active.get(); + } + + public void setActive(boolean active) { + this.active.set(active); + } + } + + + private static final String DISABLED_EXT = "disabled"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/FabricModMetadata.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/FabricModMetadata.java new file mode 100644 index 00000000..fe918cbe --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/FabricModMetadata.java @@ -0,0 +1,77 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public final class FabricModMetadata { + private final String id; + private final String name; + private final String version; + private final String description; + private final String icon; + private final List authors; + private final Map contact; + + public FabricModMetadata() { + this("", "", "", "", "", Collections.emptyList(), Collections.emptyMap()); + } + + public FabricModMetadata(String id, String name, String version, String icon, String description, List authors, Map contact) { + this.id = id; + this.name = name; + this.version = version; + this.icon = icon; + this.description = description; + this.authors = authors; + this.contact = contact; + } + + public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { + Path mcmod = fs.getPath("fabric.mod.json"); + if (Files.notExists(mcmod)) + throw new IOException("File " + modFile + " is not a Fabric mod."); + FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class); + String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", ")); + return new LocalModFile(modManager, modManager.getLocalMod(metadata.id, ModLoaderType.FABRIC), modFile, metadata.name, new LocalModFile.Description(metadata.description), + authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon); + } + } + + @JsonAdapter(FabricModAuthorSerializer.class) + public static final class FabricModAuthor { + private final String name; + + public FabricModAuthor() { + this(""); + } + + public FabricModAuthor(String name) { + this.name = name; + } + } + + public static final class FabricModAuthorSerializer implements JsonSerializer, JsonDeserializer { + @Override + public FabricModAuthor deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return json.isJsonPrimitive() ? new FabricModAuthor(json.getAsString()) : new FabricModAuthor(json.getAsJsonObject().getAsJsonPrimitive("name").getAsString()); + } + + @Override + public JsonElement serialize(FabricModAuthor src, Type typeOfSrc, JsonSerializationContext context) { + return src == null ? JsonNull.INSTANCE : new JsonPrimitive(src.name); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ForgeNewModMetadata.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ForgeNewModMetadata.java new file mode 100644 index 00000000..be8a6252 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ForgeNewModMetadata.java @@ -0,0 +1,137 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.logging.Level; + +public final class ForgeNewModMetadata { + + private final String modLoader; + + private final String loaderVersion; + + private final String logoFile; + + private final String license; + + private final List mods; + + public ForgeNewModMetadata() { + this("", "", "", "", Collections.emptyList()); + } + + public ForgeNewModMetadata(String modLoader, String loaderVersion, String logoFile, String license, List mods) { + this.modLoader = modLoader; + this.loaderVersion = loaderVersion; + this.logoFile = logoFile; + this.license = license; + this.mods = mods; + } + + public String getModLoader() { + return modLoader; + } + + public String getLoaderVersion() { + return loaderVersion; + } + + public String getLogoFile() { + return logoFile; + } + + public String getLicense() { + return license; + } + + public List getMods() { + return mods; + } + + public static class Mod { + private final String modId; + private final String version; + private final String displayName; + private final String side; + private final String displayURL; + private final String authors; + private final String description; + + public Mod() { + this("", "", "", "", "", "", ""); + } + + public Mod(String modId, String version, String displayName, String side, String displayURL, String authors, String description) { + this.modId = modId; + this.version = version; + this.displayName = displayName; + this.side = side; + this.displayURL = displayURL; + this.authors = authors; + this.description = description; + } + + public String getModId() { + return modId; + } + + public String getVersion() { + return version; + } + + public String getDisplayName() { + return displayName; + } + + public String getSide() { + return side; + } + + public String getDisplayURL() { + return displayURL; + } + + public String getAuthors() { + return authors; + } + + public String getDescription() { + return description; + } + } + + public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { + Path modstoml = fs.getPath("META-INF/mods.toml"); + if (Files.notExists(modstoml)) + throw new IOException("File " + modFile + " is not a Forge 1.13+ mod."); + ForgeNewModMetadata metadata = new Toml().read(FileUtils.readText(modstoml)).to(ForgeNewModMetadata.class); + if (metadata == null || metadata.getMods().isEmpty()) + throw new IOException("Mod " + modFile + " `mods.toml` is malformed.."); + Mod mod = metadata.getMods().get(0); + Path manifestMF = fs.getPath("META-INF/MANIFEST.MF"); + String jarVersion = ""; + if (Files.exists(manifestMF)) { + try { + Manifest manifest = new Manifest(Files.newInputStream(manifestMF)); + jarVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to parse MANIFEST.MF in file " + modFile); + } + } + return new LocalModFile(modManager, modManager.getLocalMod(mod.getModId(), ModLoaderType.FORGE), modFile, mod.getDisplayName(), new LocalModFile.Description(mod.getDescription()), + mod.getAuthors(), mod.getVersion().replace("${file.jarVersion}", jarVersion), "", + mod.getDisplayURL(), + metadata.getLogoFile()); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ForgeOldModMetadata.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ForgeOldModMetadata.java new file mode 100644 index 00000000..d9019a17 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ForgeOldModMetadata.java @@ -0,0 +1,123 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public final class ForgeOldModMetadata { + + @SerializedName("modid") + private final String modId; + private final String name; + private final String description; + private final String author; + private final String version; + private final String logoFile; + private final String mcversion; + private final String url; + private final String updateUrl; + private final String credits; + private final String[] authorList; + private final String[] authors; + + public ForgeOldModMetadata() { + this("", "", "", "", "", "", "", "", "", "", new String[0], new String[0]); + } + + public ForgeOldModMetadata(String modId, String name, String description, String author, String version, String logoFile, String mcversion, String url, String updateUrl, String credits, String[] authorList, String[] authors) { + this.modId = modId; + this.name = name; + this.description = description; + this.author = author; + this.version = version; + this.logoFile = logoFile; + this.mcversion = mcversion; + this.url = url; + this.updateUrl = updateUrl; + this.credits = credits; + this.authorList = authorList; + this.authors = authors; + } + + public String getModId() { + return modId; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getAuthor() { + return author; + } + + public String getVersion() { + return version; + } + + public String getLogoFile() { + return logoFile; + } + + public String getGameVersion() { + return mcversion; + } + + public String getUrl() { + return url; + } + + public String getUpdateUrl() { + return updateUrl; + } + + public String getCredits() { + return credits; + } + + public String[] getAuthorList() { + return authorList; + } + + public String[] getAuthors() { + return authors; + } + + public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { + Path mcmod = fs.getPath("mcmod.info"); + if (Files.notExists(mcmod)) + throw new IOException("File " + modFile + " is not a Forge mod."); + List modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), + new TypeToken>() { + }.getType()); + if (modList == null || modList.isEmpty()) + throw new IOException("Mod " + modFile + " `mcmod.info` is malformed.."); + ForgeOldModMetadata metadata = modList.get(0); + String authors = metadata.getAuthor(); + if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0) + authors = String.join(", ", metadata.getAuthors()); + if (StringUtils.isBlank(authors) && metadata.getAuthorList().length > 0) + authors = String.join(", ", metadata.getAuthorList()); + if (StringUtils.isBlank(authors)) + authors = metadata.getCredits(); + return new LocalModFile(modManager, modManager.getLocalMod(metadata.getModId(), ModLoaderType.FORGE), modFile, metadata.getName(), new LocalModFile.Description(metadata.getDescription()), + authors, metadata.getVersion(), metadata.getGameVersion(), + StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url, + metadata.getLogoFile()); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/LiteModMetadata.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/LiteModMetadata.java new file mode 100644 index 00000000..1574504d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/LiteModMetadata.java @@ -0,0 +1,100 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.IOUtils; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public final class LiteModMetadata { + private final String name; + private final String version; + private final String mcversion; + private final String revision; + private final String author; + private final String[] classTransformerClasses; + private final String description; + private final String modpackName; + private final String modpackVersion; + private final String checkUpdateUrl; + private final String updateURI; + + public LiteModMetadata() { + this("", "", "", "", "", new String[]{""}, "", "", "", "", ""); + } + + public LiteModMetadata(String name, String version, String mcversion, String revision, String author, String[] classTransformerClasses, String description, String modpackName, String modpackVersion, String checkUpdateUrl, String updateURI) { + this.name = name; + this.version = version; + this.mcversion = mcversion; + this.revision = revision; + this.author = author; + this.classTransformerClasses = classTransformerClasses; + this.description = description; + this.modpackName = modpackName; + this.modpackVersion = modpackVersion; + this.checkUpdateUrl = checkUpdateUrl; + this.updateURI = updateURI; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getGameVersion() { + return mcversion; + } + + public String getRevision() { + return revision; + } + + public String getAuthor() { + return author; + } + + public String[] getClassTransformerClasses() { + return classTransformerClasses; + } + + public String getDescription() { + return description; + } + + public String getModpackName() { + return modpackName; + } + + public String getModpackVersion() { + return modpackVersion; + } + + public String getCheckUpdateUrl() { + return checkUpdateUrl; + } + + public String getUpdateURI() { + return updateURI; + } + + public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { + try (ZipFile zipFile = new ZipFile(modFile.toFile())) { + ZipEntry entry = zipFile.getEntry("litemod.json"); + if (entry == null) + throw new IOException("File " + modFile + "is not a LiteLoader mod."); + LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class); + if (metadata == null) + throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); + return new LocalModFile(modManager, modManager.getLocalMod(metadata.getName(), ModLoaderType.LITE_LOADER), modFile, metadata.getName(), new LocalModFile.Description(metadata.getDescription()), metadata.getAuthor(), + metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), ""); + } + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/LocalMod.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/LocalMod.java new file mode 100644 index 00000000..ec515edb --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/LocalMod.java @@ -0,0 +1,46 @@ +package com.tungsten.fclcore.mod; + +import java.util.HashSet; +import java.util.Objects; + +public class LocalMod { + + private final String id; + private final ModLoaderType modLoaderType; + private final HashSet files = new HashSet<>(); + private final HashSet oldFiles = new HashSet<>(); + + public LocalMod(String id, ModLoaderType modLoaderType) { + this.id = id; + this.modLoaderType = modLoaderType; + } + + public String getId() { + return id; + } + + public ModLoaderType getModLoaderType() { + return modLoaderType; + } + + public HashSet getFiles() { + return files; + } + + public HashSet getOldFiles() { + return oldFiles; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LocalMod localMod = (LocalMod) o; + return Objects.equals(id, localMod.id) && modLoaderType == localMod.modLoaderType; + } + + @Override + public int hashCode() { + return Objects.hash(id, modLoaderType); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/LocalModFile.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/LocalModFile.java new file mode 100644 index 00000000..3c1f4e24 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/LocalModFile.java @@ -0,0 +1,249 @@ +package com.tungsten.fclcore.mod; + +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public final class LocalModFile implements Comparable { + + private Path file; + private final ModManager modManager; + private final LocalMod mod; + private final String name; + private final Description description; + private final String authors; + private final String version; + private final String gameVersion; + private final String url; + private final String fileName; + private final String logoPath; + private final BooleanProperty activeProperty; + + public LocalModFile(ModManager modManager, LocalMod mod, Path file, String name, Description description) { + this(modManager, mod, file, name, description, "", "", "", "", ""); + } + + public LocalModFile(ModManager modManager, LocalMod mod, Path file, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) { + this.modManager = modManager; + this.mod = mod; + this.file = file; + this.name = name; + this.description = description; + this.authors = authors; + this.version = version; + this.gameVersion = gameVersion; + this.url = url; + this.logoPath = logoPath; + + activeProperty = new SimpleBooleanProperty(this, "active", !modManager.isDisabled(file)) { + @Override + protected void invalidated() { + if (isOld()) return; + + Path path = LocalModFile.this.file.toAbsolutePath(); + + try { + if (get()) + LocalModFile.this.file = modManager.enableMod(path); + else + LocalModFile.this.file = modManager.disableMod(path); + } catch (IOException e) { + Logging.LOG.log(Level.SEVERE, "Unable to invert state of mod file " + path, e); + } + } + }; + + fileName = FileUtils.getNameWithoutExtension(ModManager.getModName(file)); + + if (isOld()) { + mod.getOldFiles().add(this); + } else { + mod.getFiles().add(this); + } + } + + public ModManager getModManager() { + return modManager; + } + + public LocalMod getMod() { + return mod; + } + + public Path getFile() { + return file; + } + + public ModLoaderType getModLoaderType() { + return mod.getModLoaderType(); + } + + public String getId() { + return mod.getId(); + } + + public String getName() { + return name; + } + + public Description getDescription() { + return description; + } + + public String getAuthors() { + return authors; + } + + public String getVersion() { + return version; + } + + public String getGameVersion() { + return gameVersion; + } + + public String getUrl() { + return url; + } + + public String getLogoPath() { + return logoPath; + } + + public BooleanProperty activeProperty() { + return activeProperty; + } + + public boolean isActive() { + return activeProperty.get(); + } + + public void setActive(boolean active) { + activeProperty.set(active); + } + + public String getFileName() { + return fileName; + } + + public boolean isOld() { + return modManager.isOld(file); + } + + public void setOld(boolean old) throws IOException { + file = modManager.setOld(this, old); + + if (old) { + mod.getFiles().remove(this); + mod.getOldFiles().add(this); + } else { + mod.getOldFiles().remove(this); + mod.getFiles().add(this); + } + } + + public ModUpdate checkUpdates(String gameVersion, RemoteModRepository repository) throws IOException { + Optional currentVersion = repository.getRemoteVersionByLocalFile(this, file); + if (!currentVersion.isPresent()) return null; + List remoteVersions = repository.getRemoteVersionsById(currentVersion.get().getModid()) + .filter(version -> version.getGameVersions().contains(gameVersion)) + .filter(version -> version.getLoaders().contains(getModLoaderType())) + .filter(version -> version.getDatePublished().compareTo(currentVersion.get().getDatePublished()) > 0) + .sorted(Comparator.comparing(RemoteMod.Version::getDatePublished).reversed()) + .collect(Collectors.toList()); + if (remoteVersions.isEmpty()) return null; + return new ModUpdate(this, currentVersion.get(), remoteVersions); + } + + @Override + public int compareTo(LocalModFile o) { + return getFileName().compareTo(o.getFileName()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LocalModFile && Objects.equals(getFileName(), ((LocalModFile) obj).getFileName()); + } + + @Override + public int hashCode() { + return Objects.hash(getFileName()); + } + + public static class ModUpdate { + private final LocalModFile localModFile; + private final RemoteMod.Version currentVersion; + private final List candidates; + + public ModUpdate(LocalModFile localModFile, RemoteMod.Version currentVersion, List candidates) { + this.localModFile = localModFile; + this.currentVersion = currentVersion; + this.candidates = candidates; + } + + public LocalModFile getLocalMod() { + return localModFile; + } + + public RemoteMod.Version getCurrentVersion() { + return currentVersion; + } + + public List getCandidates() { + return candidates; + } + } + + public static class Description { + private final List parts; + + public Description(String text) { + this.parts = new ArrayList<>(); + this.parts.add(new Part(text, "black")); + } + + public Description(List parts) { + this.parts = parts; + } + + public List getParts() { + return parts; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (Part part : parts) { + builder.append(part.text); + } + return builder.toString(); + } + + public static class Part { + private final String text; + private final String color; + + public Part(String text) { + this(text, ""); + } + + public Part(String text, String color) { + this.text = Objects.requireNonNull(text); + this.color = Objects.requireNonNull(color); + } + + public String getText() { + return text; + } + + public String getColor() { + return color; + } + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/MinecraftInstanceTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/MinecraftInstanceTask.java new file mode 100644 index 00000000..2737ae06 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/MinecraftInstanceTask.java @@ -0,0 +1,65 @@ +package com.tungsten.fclcore.mod; + +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; + +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class MinecraftInstanceTask extends Task> { + + private final File zipFile; + private final Charset encoding; + private final List subDirectories; + private final File jsonFile; + private final T manifest; + private final String type; + private final String name; + private final String version; + + public MinecraftInstanceTask(File zipFile, Charset encoding, List subDirectories, T manifest, ModpackProvider modpackProvider, String name, String version, File jsonFile) { + this.zipFile = zipFile; + this.encoding = encoding; + this.subDirectories = subDirectories.stream().map(FileUtils::normalizePath).collect(Collectors.toList()); + this.manifest = manifest; + this.jsonFile = jsonFile; + this.type = modpackProvider.getName(); + this.name = name; + this.version = version; + } + + @Override + public void execute() throws Exception { + List overrides = new ArrayList<>(); + + try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setEncoding(encoding).build()) { + for (String subDirectory : subDirectories) { + Path root = fs.getPath(subDirectory); + + if (Files.exists(root)) + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String relativePath = root.relativize(file).normalize().toString().replace(File.separatorChar, '/'); + overrides.add(new ModpackConfiguration.FileInformation(relativePath, encodeHex(digest("SHA-1", file)))); + return FileVisitResult.CONTINUE; + } + }); + } + } + + ModpackConfiguration configuration = new ModpackConfiguration<>(manifest, type, name, version, overrides); + FileUtils.writeText(jsonFile, JsonUtils.GSON.toJson(configuration)); + setResult(configuration); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/MismatchedModpackTypeException.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/MismatchedModpackTypeException.java new file mode 100644 index 00000000..eec4a30a --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/MismatchedModpackTypeException.java @@ -0,0 +1,21 @@ +package com.tungsten.fclcore.mod; + +public class MismatchedModpackTypeException extends Exception { + private final String required; + private final String found; + + public MismatchedModpackTypeException(String required, String found) { + super("Required " + required + ", but found " + found); + + this.required = required; + this.found = found; + } + + public String getRequired() { + return required; + } + + public String getFound() { + return found; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModAdviser.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModAdviser.java new file mode 100644 index 00000000..6273e17b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModAdviser.java @@ -0,0 +1,71 @@ +package com.tungsten.fclcore.mod; + +import com.tungsten.fclcore.util.Lang; + +import java.util.List; + +public interface ModAdviser { + + /** + * Suggests the file should be displayed, hidden, or included by default. + * @param fileName full path of fileName + * @param isDirectory whether the path is directory + * @return the suggestion to the file + */ + ModSuggestion advise(String fileName, boolean isDirectory); + + enum ModSuggestion { + SUGGESTED, + NORMAL, + HIDDEN + } + + List MODPACK_BLACK_LIST = Lang.immutableListOf( + "regex:(.*?)\\.log", + "usernamecache.json", "usercache.json", // Minecraft + "launcher_profiles.json", "launcher.pack.lzma", // Old Minecraft Launcher + "launcher_accounts.json", "launcher_cef_log.txt", "launcher_log.txt", "launcher_msa_credentials.bin", "launcher_settings.json", "launcher_ui_state.json", "realms_persistence.json", "webcache2", "treatment_tags.json", // New Minecraft Launcher + "clientId.txt", "PCL.ini", // Plain Craft Launcher + "backup", "pack.json", "launcher.jar", "cache", "modpack.cfg", // HMCL + "manifest.json", "minecraftinstance.json", ".curseclient", // Curse + ".fabric", ".mixin.out", // Fabric + "jars", "logs", "versions", "assets", "libraries", "crash-reports", "NVIDIA", "AMD", "screenshots", "natives", "native", "$native", "server-resource-packs", // Minecraft + "downloads", // Curse + "asm", "backups", "TCNodeTracker", "CustomDISkins", "data", "CustomSkinLoader/caches" // Mods + ); + + List MODPACK_SUGGESTED_BLACK_LIST = Lang.immutableListOf( + "fonts", // BetterFonts + "saves", "servers.dat", "options.txt", // Minecraft + "blueprints" /* BuildCraft */, + "optionsof.txt" /* OptiFine */, + "journeymap" /* JourneyMap */, + "optionsshaders.txt", + "mods/VoxelMods"); + + static ModSuggestion suggestMod(String fileName, boolean isDirectory) { + if (match(MODPACK_BLACK_LIST, fileName, isDirectory)) + return ModSuggestion.HIDDEN; + if (match(MODPACK_SUGGESTED_BLACK_LIST, fileName, isDirectory)) + return ModSuggestion.NORMAL; + else + return ModSuggestion.SUGGESTED; + } + + static boolean match(List l, String fileName, boolean isDirectory) { + for (String s : l) + if (isDirectory) { + if (fileName.startsWith(s + "/")) + return true; + } else { + if (s.startsWith("regex:")) { + if (fileName.matches(s.substring("regex:".length()))) + return true; + } else { + if (fileName.equals(s)) + return true; + } + } + return false; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModLoaderType.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModLoaderType.java new file mode 100644 index 00000000..b706520b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModLoaderType.java @@ -0,0 +1,9 @@ +package com.tungsten.fclcore.mod; + +public enum ModLoaderType { + UNKNOWN, + FORGE, + FABRIC, + LITE_LOADER, + PACK +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModManager.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModManager.java new file mode 100644 index 00000000..3219f7ae --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModManager.java @@ -0,0 +1,296 @@ +package com.tungsten.fclcore.mod; + +import com.tungsten.fclcore.game.GameRepository; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.versioning.VersionNumber; + +import java.io.IOException; +import java.nio.file.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.Objects; +import java.util.TreeSet; + +public final class ModManager { + private final GameRepository repository; + private final String id; + private final TreeSet localModFiles = new TreeSet<>(); + private final HashMap localMods = new HashMap<>(); + + private boolean loaded = false; + + public ModManager(GameRepository repository, String id) { + this.repository = repository; + this.id = id; + } + + public GameRepository getRepository() { + return repository; + } + + public String getVersion() { + return id; + } + + public Path getModsDirectory() { + return repository.getRunDirectory(id).toPath().resolve("mods"); + } + + public LocalMod getLocalMod(String id, ModLoaderType modLoaderType) { + return localMods.computeIfAbsent(new LocalMod(id, modLoaderType), x -> x); + } + + private void addModInfo(Path file) { + try { + LocalModFile localModFile = getModInfo(file); + if (!localModFile.isOld()) { + localModFiles.add(localModFile); + } + } catch (IllegalArgumentException ignore) { + } + } + + public LocalModFile getModInfo(Path modFile) { + String fileName = StringUtils.removeSuffix(FileUtils.getName(modFile), DISABLED_EXTENSION, OLD_EXTENSION); + String description; + if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) { + try { + return ForgeOldModMetadata.fromFile(this, modFile); + } catch (Exception ignore) { + } + + try { + return ForgeNewModMetadata.fromFile(this, modFile); + } catch (Exception ignore) { + } + + try { + return FabricModMetadata.fromFile(this, modFile); + } catch (Exception ignore) { + } + + try { + return PackMcMeta.fromFile(this, modFile); + } catch (Exception ignore) { + } + + description = ""; + } else if (fileName.endsWith(".litemod")) { + try { + return LiteModMetadata.fromFile(this, modFile); + } catch (Exception ignore) { + description = "LiteLoader Mod"; + } + } else { + throw new IllegalArgumentException("File " + modFile + " is not a mod file."); + } + return new LocalModFile(this, + getLocalMod(FileUtils.getNameWithoutExtension(modFile), ModLoaderType.UNKNOWN), + modFile, + FileUtils.getNameWithoutExtension(modFile), + new LocalModFile.Description(description)); + } + + public void refreshMods() throws IOException { + localModFiles.clear(); + localMods.clear(); + if (Files.isDirectory(getModsDirectory())) { + try (DirectoryStream modsDirectoryStream = Files.newDirectoryStream(getModsDirectory())) { + for (Path subitem : modsDirectoryStream) { + if (Files.isDirectory(subitem) && VersionNumber.isIntVersionNumber(FileUtils.getName(subitem))) { + // If the folder name is game version, forge will search mod in this subdirectory + try (DirectoryStream subitemDirectoryStream = Files.newDirectoryStream(subitem)) { + for (Path subsubitem : subitemDirectoryStream) { + addModInfo(subsubitem); + } + } + } else { + addModInfo(subitem); + } + } + } + } + loaded = true; + } + + public Collection getMods() throws IOException { + if (!loaded) + refreshMods(); + return localModFiles; + } + + public void addMod(Path file) throws IOException { + if (!isFileNameMod(file)) + throw new IllegalArgumentException("File " + file + " is not a valid mod file."); + + if (!loaded) + refreshMods(); + + Path modsDirectory = getModsDirectory(); + Files.createDirectories(modsDirectory); + + Path newFile = modsDirectory.resolve(file.getFileName()); + FileUtils.copyFile(file, newFile); + + addModInfo(newFile); + } + + public void removeMods(LocalModFile... localModFiles) throws IOException { + for (LocalModFile localModFile : localModFiles) { + Files.deleteIfExists(localModFile.getFile()); + } + } + + public void rollback(LocalModFile from, LocalModFile to) throws IOException { + if (!loaded) { + throw new IllegalStateException("ModManager Not loaded"); + } + if (!localModFiles.contains(from)) { + throw new IllegalStateException("Rolling back an unknown mod " + from.getFileName()); + } + if (from.isOld()) { + throw new IllegalArgumentException("Rolling back an old mod " + from.getFileName()); + } + if (!to.isOld()) { + throw new IllegalArgumentException("Rolling back to an old path " + to.getFileName()); + } + if (from.getFileName().equals(to.getFileName())) { + // We cannot roll back to the mod with the same name. + return; + } + + LocalMod mod = Objects.requireNonNull(from.getMod()); + if (mod != to.getMod()) { + throw new IllegalArgumentException("Rolling back mod " + from.getFileName() + " to a different mod " + to.getFileName()); + } + if (!mod.getFiles().contains(from) + || !mod.getOldFiles().contains(to)) { + throw new IllegalStateException("LocalMod state corrupt"); + } + + boolean active = from.isActive(); + from.setActive(true); + from.setOld(true); + to.setOld(false); + to.setActive(active); + } + + private Path backupMod(Path file) throws IOException { + Path newPath = file.resolveSibling( + StringUtils.addSuffix( + StringUtils.removeSuffix(FileUtils.getName(file), DISABLED_EXTENSION), + OLD_EXTENSION + ) + ); + if (Files.exists(file)) { + Files.move(file, newPath, StandardCopyOption.REPLACE_EXISTING); + } + return newPath; + } + + private Path restoreMod(Path file) throws IOException { + Path newPath = file.resolveSibling( + StringUtils.removeSuffix(FileUtils.getName(file), OLD_EXTENSION) + ); + if (Files.exists(file)) { + Files.move(file, newPath, StandardCopyOption.REPLACE_EXISTING); + } + return newPath; + } + + public Path setOld(LocalModFile modFile, boolean old) throws IOException { + Path newPath; + if (old) { + newPath = backupMod(modFile.getFile()); + localModFiles.remove(modFile); + } else { + newPath = restoreMod(modFile.getFile()); + localModFiles.add(modFile); + } + return newPath; + } + + public Path disableMod(Path file) throws IOException { + if (isOld(file)) return file; // no need to disable an old mod. + Path disabled = file.resolveSibling(StringUtils.addSuffix(FileUtils.getName(file), DISABLED_EXTENSION)); + if (Files.exists(file)) + Files.move(file, disabled, StandardCopyOption.REPLACE_EXISTING); + return disabled; + } + + public Path enableMod(Path file) throws IOException { + if (isOld(file)) return file; + Path enabled = file.resolveSibling(StringUtils.removeSuffix(FileUtils.getName(file), DISABLED_EXTENSION)); + if (Files.exists(file)) + Files.move(file, enabled, StandardCopyOption.REPLACE_EXISTING); + return enabled; + } + + public static String getModName(Path file) { + return StringUtils.removeSuffix(FileUtils.getName(file), DISABLED_EXTENSION, OLD_EXTENSION); + } + + public boolean isOld(Path file) { + return FileUtils.getName(file).endsWith(OLD_EXTENSION); + } + + public boolean isDisabled(Path file) { + return FileUtils.getName(file).endsWith(DISABLED_EXTENSION); + } + + public static boolean isFileNameMod(Path file) { + String name = getModName(file); + return name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".litemod"); + } + + public static boolean isFileMod(Path modFile) { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { + if (Files.exists(fs.getPath("mcmod.info")) || Files.exists(fs.getPath("META-INF/mods.toml"))) { + // Forge mod + return true; + } + + if (Files.exists(fs.getPath("fabric.mod.json"))) { + // Fabric mod + return true; + } + + if (Files.exists(fs.getPath("litemod.json"))) { + // Liteloader mod + return true; + } + + if (Files.exists(fs.getPath("pack.mcmeta"))) { + // resource pack, data pack + return true; + } + + return false; + } catch (IOException e) { + return false; + } + } + + /** + * Check if "mods" directory has mod file named "fileName" no matter the mod is disabled or not + * + * @param fileName name of the file whose existence is being checked + * @return true if the file exists + */ + public boolean hasSimpleMod(String fileName) { + return Files.exists(getModsDirectory().resolve(StringUtils.removeSuffix(fileName, DISABLED_EXTENSION))) + || Files.exists(getModsDirectory().resolve(StringUtils.addSuffix(fileName, DISABLED_EXTENSION))); + } + + public Path getSimpleModPath(String fileName) { + return getModsDirectory().resolve(fileName); + } + + public static String getMcbbsUrl(String mcbbsId) { + return String.format("https://www.mcbbs.net/thread-%s-1-1.html", mcbbsId); + } + + public static final String DISABLED_EXTENSION = ".disabled"; + public static final String OLD_EXTENSION = ".old"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/Modpack.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/Modpack.java new file mode 100644 index 00000000..970ab0bf --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/Modpack.java @@ -0,0 +1,111 @@ +package com.tungsten.fclcore.mod; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.task.Task; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.List; + +public abstract class Modpack { + private String name; + private String author; + private String version; + private String gameVersion; + private String description; + private transient Charset encoding; + private ModpackManifest manifest; + + public Modpack() { + this("", null, null, null, null, null, null); + } + + public Modpack(String name, String author, String version, String gameVersion, String description, Charset encoding, ModpackManifest manifest) { + this.name = name; + this.author = author; + this.version = version; + this.gameVersion = gameVersion; + this.description = description; + this.encoding = encoding; + this.manifest = manifest; + } + + public String getName() { + return name; + } + + public Modpack setName(String name) { + this.name = name; + return this; + } + + public String getAuthor() { + return author; + } + + public Modpack setAuthor(String author) { + this.author = author; + return this; + } + + public String getVersion() { + return version; + } + + public Modpack setVersion(String version) { + this.version = version; + return this; + } + + public String getGameVersion() { + return gameVersion; + } + + public Modpack setGameVersion(String gameVersion) { + this.gameVersion = gameVersion; + return this; + } + + public String getDescription() { + return description; + } + + public Modpack setDescription(String description) { + this.description = description; + return this; + } + + public Charset getEncoding() { + return encoding; + } + + public Modpack setEncoding(Charset encoding) { + this.encoding = encoding; + return this; + } + + public ModpackManifest getManifest() { + return manifest; + } + + public Modpack setManifest(ModpackManifest manifest) { + this.manifest = manifest; + return this; + } + + public abstract Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name); + + public static boolean acceptFile(String path, List blackList, List whiteList) { + if (path.isEmpty()) + return true; + for (String s : blackList) + if (path.equals(s)) + return false; + if (whiteList == null || whiteList.isEmpty()) + return true; + for (String s : whiteList) + if (path.equals(s)) + return true; + return false; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackCompletionException.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackCompletionException.java new file mode 100644 index 00000000..ded85874 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackCompletionException.java @@ -0,0 +1,18 @@ +package com.tungsten.fclcore.mod; + +public class ModpackCompletionException extends Exception { + public ModpackCompletionException() { + } + + public ModpackCompletionException(String message) { + super(message); + } + + public ModpackCompletionException(String message, Throwable cause) { + super(message, cause); + } + + public ModpackCompletionException(Throwable cause) { + super(cause); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackConfiguration.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackConfiguration.java new file mode 100644 index 00000000..511ab10d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackConfiguration.java @@ -0,0 +1,117 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.gson.Validation; + +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ModpackConfiguration implements Validation { + + private final T manifest; + private final String type; + private final String name; + private final String version; + private final List overrides; + + public ModpackConfiguration() { + this(null, null, "", null, Collections.emptyList()); + } + + public ModpackConfiguration(T manifest, String type, String name, String version, List overrides) { + this.manifest = manifest; + this.type = type; + this.name = name; + this.version = version; + this.overrides = new ArrayList<>(overrides); + } + + public T getManifest() { + return manifest; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + @Nullable + public String getVersion() { + return version; + } + + public ModpackConfiguration setManifest(T manifest) { + return new ModpackConfiguration<>(manifest, type, name, version, overrides); + } + + public ModpackConfiguration setOverrides(List overrides) { + return new ModpackConfiguration<>(manifest, type, name, version, overrides); + } + + public ModpackConfiguration setVersion(String version) { + return new ModpackConfiguration<>(manifest, type, name, version, overrides); + } + + public List getOverrides() { + return Collections.unmodifiableList(overrides); + } + + @Override + public void validate() throws JsonParseException { + if (manifest == null) + throw new JsonParseException("MinecraftInstanceConfiguration missing `manifest`"); + if (type == null) + throw new JsonParseException("MinecraftInstanceConfiguration missing `type`"); + } + + public static class FileInformation implements Validation { + private final String path; // relative + private final String hash; + private final String downloadURL; + + public FileInformation() { + this(null, null); + } + + public FileInformation(String path, String hash) { + this(path, hash, null); + } + + public FileInformation(String path, String hash, String downloadURL) { + this.path = path; + this.hash = hash; + this.downloadURL = downloadURL; + } + + /** + * The relative path to Minecraft run directory + * + * @return the relative path to Minecraft run directory. + */ + public String getPath() { + return path; + } + + public String getDownloadURL() { + return downloadURL; + } + + public String getHash() { + return hash; + } + + @Override + public void validate() throws JsonParseException { + if (path == null) + throw new JsonParseException("FileInformation missing `path`."); + if (hash == null) + throw new JsonParseException("FileInformation missing file hash code."); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackExportInfo.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackExportInfo.java new file mode 100644 index 00000000..7a223622 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackExportInfo.java @@ -0,0 +1,286 @@ +package com.tungsten.fclcore.mod; + +import com.tungsten.fclcore.mod.mcbbs.McbbsModpackManifest; + +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ModpackExportInfo { + + private final List whitelist = new ArrayList<>(); + private String name; + private String author; + private String version; + private String description; + private String url; + + private boolean forceUpdate; + private boolean packWithLauncher; + + private String fileApi; + private int minMemory; + private List supportedJavaVersions; + private String launchArguments; + private String javaArguments; + + private String authlibInjectorServer; + + private List origins = new ArrayList<>(); + + public ModpackExportInfo() {} + + public List getWhitelist() { + return whitelist; + } + + public ModpackExportInfo setWhitelist(List whitelist) { + this.whitelist.clear(); + this.whitelist.addAll(whitelist); + return this; + } + + /** + * Name of this modpack. + */ + public String getName() { + return name; + } + + public ModpackExportInfo setName(String name) { + this.name = name; + return this; + } + + /** + * Author of this modpack. + */ + public String getAuthor() { + return author; + } + + public ModpackExportInfo setAuthor(String author) { + this.author = author; + return this; + } + + /** + * Version of this modpack. + */ + public String getVersion() { + return version; + } + + public ModpackExportInfo setVersion(String version) { + this.version = version; + return this; + } + + /** + * Description of this modpack. + * + * Supports plain HTML text. + */ + public String getDescription() { + return description; + } + + public ModpackExportInfo setDescription(String description) { + this.description = description; + return this; + } + + public String getFileApi() { + return fileApi; + } + + public ModpackExportInfo setFileApi(String fileApi) { + this.fileApi = fileApi; + return this; + } + + /** + * Modpack official introduction webpage link. + */ + public String getUrl() { + return url; + } + + public ModpackExportInfo setUrl(String url) { + this.url = url; + return this; + } + + public boolean isForceUpdate() { + return forceUpdate; + } + + public ModpackExportInfo setForceUpdate(boolean forceUpdate) { + this.forceUpdate = forceUpdate; + return this; + } + + public boolean isPackWithLauncher() { + return packWithLauncher; + } + + public ModpackExportInfo setPackWithLauncher(boolean packWithLauncher) { + this.packWithLauncher = packWithLauncher; + return this; + } + + public int getMinMemory() { + return minMemory; + } + + public ModpackExportInfo setMinMemory(int minMemory) { + this.minMemory = minMemory; + return this; + } + + @Nullable + public List getSupportedJavaVersions() { + return supportedJavaVersions; + } + + public ModpackExportInfo setSupportedJavaVersions(List supportedJavaVersions) { + this.supportedJavaVersions = supportedJavaVersions; + return this; + } + + public String getLaunchArguments() { + return launchArguments; + } + + public ModpackExportInfo setLaunchArguments(String launchArguments) { + this.launchArguments = launchArguments; + return this; + } + + public String getJavaArguments() { + return javaArguments; + } + + public ModpackExportInfo setJavaArguments(String javaArguments) { + this.javaArguments = javaArguments; + return this; + } + + public String getAuthlibInjectorServer() { + return authlibInjectorServer; + } + + public ModpackExportInfo setAuthlibInjectorServer(String authlibInjectorServer) { + this.authlibInjectorServer = authlibInjectorServer; + return this; + } + + public List getOrigins() { + return Collections.unmodifiableList(origins); + } + + public ModpackExportInfo setOrigins(List origins) { + this.origins.clear(); + this.origins.addAll(origins); + return this; + } + + public ModpackExportInfo validate() throws NullPointerException { + return this; + } + + public static class Options { + private boolean requireUrl; + private boolean requireForceUpdate; + private boolean requireFileApi; + private boolean validateFileApi; + private boolean requireMinMemory; + private boolean requireAuthlibInjectorServer; + private boolean requireLaunchArguments; + private boolean requireJavaArguments; + private boolean requireOrigins; + + public Options() { + } + + public boolean isRequireUrl() { + return requireUrl; + } + + public boolean isRequireForceUpdate() { + return requireForceUpdate; + } + + public boolean isRequireFileApi() { + return requireFileApi; + } + + public boolean isValidateFileApi() { + return validateFileApi; + } + + public boolean isRequireMinMemory() { + return requireMinMemory; + } + + public boolean isRequireAuthlibInjectorServer() { + return requireAuthlibInjectorServer; + } + + public boolean isRequireLaunchArguments() { + return requireLaunchArguments; + } + + public boolean isRequireJavaArguments() { + return requireJavaArguments; + } + + public boolean isRequireOrigins() { + return requireOrigins; + } + + public Options requireUrl() { + requireUrl = true; + return this; + } + + public Options requireForceUpdate() { + requireForceUpdate = true; + return this; + } + + public Options requireFileApi(boolean optional) { + requireFileApi = true; + validateFileApi = !optional; + return this; + } + + public Options requireMinMemory() { + requireMinMemory = true; + return this; + } + + public Options requireAuthlibInjectorServer() { + requireAuthlibInjectorServer = true; + return this; + } + + public Options requireLaunchArguments() { + requireLaunchArguments = true; + return this; + } + + public Options requireJavaArguments() { + requireJavaArguments = true; + return this; + } + + public Options requireOrigins() { + requireOrigins = true; + return this; + } + + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackInstallTask.java new file mode 100644 index 00000000..094d2b1b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackInstallTask.java @@ -0,0 +1,92 @@ +package com.tungsten.fclcore.mod; + +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; + +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.*; +import java.util.function.Predicate; + +public class ModpackInstallTask extends Task { + + private final File modpackFile; + private final File dest; + private final Charset charset; + private final List subDirectories; + private final List overrides; + private final Predicate callback; + + /** + * Constructor + * @param modpackFile a zip file + * @param dest destination to store unpacked files + * @param charset charset of the zip file + * @param subDirectories the subdirectory of zip file to unpack + * @param callback test whether the file (given full path) in zip file should be unpacked or not + * @param oldConfiguration old modpack information if upgrade + */ + public ModpackInstallTask(File modpackFile, File dest, Charset charset, List subDirectories, Predicate callback, ModpackConfiguration oldConfiguration) { + this.modpackFile = modpackFile; + this.dest = dest; + this.charset = charset; + this.subDirectories = subDirectories; + this.callback = callback; + + if (oldConfiguration == null) + overrides = Collections.emptyList(); + else + overrides = oldConfiguration.getOverrides(); + } + + @Override + public void execute() throws Exception { + Set entries = new HashSet<>(); + if (!FileUtils.makeDirectory(dest)) + throw new IOException("Unable to make directory " + dest); + + HashMap files = new HashMap<>(); + for (ModpackConfiguration.FileInformation file : overrides) + files.put(file.getPath(), file); + + + for (String subDirectory : subDirectories) { + new Unzipper(modpackFile, dest) + .setSubDirectory(subDirectory) + .setTerminateIfSubDirectoryNotExists() + .setReplaceExistentFile(true) + .setEncoding(charset) + .setFilter((destPath, isDirectory, zipEntry, entryPath) -> { + if (isDirectory) return true; + if (!callback.test(entryPath)) return false; + entries.add(entryPath); + + if (!files.containsKey(entryPath)) { + // If old modpack does not have this entry, add this entry or override the file that user added. + return true; + } else if (!Files.exists(destPath)) { + // If both old and new modpacks have this entry, but the file is deleted by user, leave it missing. + return false; + } else { + // If both old and new modpacks have this entry, and user has modified this file, + // we will not replace it since this modified file is what user expects. + String fileHash = encodeHex(digest("SHA-1", destPath)); + String oldHash = files.get(entryPath).getHash(); + return Objects.equals(oldHash, fileHash); + } + }).unzip(); + } + + // If old modpack have this entry, and new modpack deleted it. Delete this file. + for (ModpackConfiguration.FileInformation file : overrides) { + File original = new File(dest, file.getPath()); + if (original.exists() && !entries.contains(file.getPath())) + original.delete(); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackManifest.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackManifest.java new file mode 100644 index 00000000..5a895bd5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackManifest.java @@ -0,0 +1,5 @@ +package com.tungsten.fclcore.mod; + +public interface ModpackManifest { + ModpackProvider getProvider(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackProvider.java new file mode 100644 index 00000000..d76af352 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackProvider.java @@ -0,0 +1,33 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.LaunchOptions; +import com.tungsten.fclcore.task.Task; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; + +public interface ModpackProvider { + + String getName(); + + Task createCompletionTask(DefaultDependencyManager dependencyManager, String version); + + Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, File zipFile, Modpack modpack) throws MismatchedModpackTypeException; + + /** + * @param zipFile the opened modpack zip file. + * @param file the modpack zip file path. + * @param encoding encoding of zip file. + * @throws IOException if the file is not a valid zip file. + * @throws JsonParseException if the manifest.json is missing or malformed. + * @return the manifest. + */ + Modpack readManifest(ZipFile zipFile, Path file, Charset encoding) throws IOException, JsonParseException; + + default void injectLaunchOptions(String modpackConfigurationJson, LaunchOptions.Builder builder) { + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackUpdateTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackUpdateTask.java new file mode 100644 index 00000000..61e3247a --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/ModpackUpdateTask.java @@ -0,0 +1,62 @@ +package com.tungsten.fclcore.mod; + +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Collections; + +public class ModpackUpdateTask extends Task { + + private final DefaultGameRepository repository; + private final String id; + private final Task updateTask; + private final Path backupFolder; + + public ModpackUpdateTask(DefaultGameRepository repository, String id, Task updateTask) { + this.repository = repository; + this.id = id; + this.updateTask = updateTask; + + Path backup = repository.getBaseDirectory().toPath().resolve("backup"); + while (true) { + int num = (int)(Math.random() * 10000000); + if (!Files.exists(backup.resolve(id + "-" + num))) { + backupFolder = backup.resolve(id + "-" + num); + break; + } + } + } + + @Override + public Collection> getDependencies() { + return Collections.singleton(updateTask); + } + + @Override + public void execute() throws Exception { + FileUtils.copyDirectory(repository.getVersionRoot(id).toPath(), backupFolder); + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + if (isDependenciesSucceeded()) { + // Keep backup game version for further repair. + } else { + // Restore backup + repository.removeVersionFromDisk(id); + + FileUtils.copyDirectory(backupFolder, repository.getVersionRoot(id).toPath()); + + repository.refreshVersionsAsync().start(); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/PackMcMeta.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/PackMcMeta.java new file mode 100644 index 00000000..f0e24875 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/PackMcMeta.java @@ -0,0 +1,141 @@ +package com.tungsten.fclcore.mod; + +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.gson.Validation; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PackMcMeta implements Validation { + + @SerializedName("pack") + private final PackInfo pack; + + public PackMcMeta() { + this(new PackInfo()); + } + + public PackMcMeta(PackInfo packInfo) { + this.pack = packInfo; + } + + public PackInfo getPackInfo() { + return pack; + } + + @Override + public void validate() throws JsonParseException { + if (pack == null) + throw new JsonParseException("pack cannot be null"); + } + + @JsonAdapter(PackInfoDeserializer.class) + public static class PackInfo { + @SerializedName("pack_format") + private final int packFormat; + + @SerializedName("description") + private final LocalModFile.Description description; + + public PackInfo() { + this(0, new LocalModFile.Description(Collections.emptyList())); + } + + public PackInfo(int packFormat, LocalModFile.Description description) { + this.packFormat = packFormat; + this.description = description; + } + + public int getPackFormat() { + return packFormat; + } + + public LocalModFile.Description getDescription() { + return description; + } + } + + public static class PackInfoDeserializer implements JsonDeserializer { + + private String parseText(JsonElement json) throws JsonParseException { + if (json.isJsonPrimitive()) { + JsonPrimitive primitive = json.getAsJsonPrimitive(); + if (primitive.isBoolean()) { + return Boolean.toString(primitive.getAsBoolean()); + } else if (primitive.isNumber()) { + return primitive.getAsNumber().toString(); + } else if (primitive.isString()) { + return primitive.getAsString(); + } else { + throw new JsonParseException("pack.mcmeta text not boolean nor number nor string???"); + } + } else if (json.isJsonArray()) { + JsonArray arr = json.getAsJsonArray(); + if (arr.size() == 0) { + return ""; + } else { + return parseText(arr.get(0)); + } + } else { + throw new JsonParseException("pack.mcmeta text should be a string, a boolean, a number or a list of raw JSON text components"); + } + } + + public LocalModFile.Description.Part deserialize(JsonElement json, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return new LocalModFile.Description.Part(parseText(json)); + } else if (json.isJsonObject()) { + JsonObject obj = json.getAsJsonObject(); + String text = parseText(obj.get("text")); + return new LocalModFile.Description.Part(text); + } else { + throw new JsonParseException("pack.mcmeta Raw JSON text should be string or an object"); + } + } + + @Override + public PackInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + List parts = new ArrayList<>(); + JsonObject packInfo = json.getAsJsonObject(); + int packFormat = packInfo.get("pack_format").getAsInt(); + JsonElement description = packInfo.get("description"); + if (description.isJsonPrimitive()) { + parts.add(new LocalModFile.Description.Part(parseText(description))); + } else if (description.isJsonArray()) { + for (JsonElement element : description.getAsJsonArray()) { + JsonObject descriptionPart = element.getAsJsonObject(); + parts.add(new LocalModFile.Description.Part(descriptionPart.get("text").getAsString(), descriptionPart.get("color").getAsString())); + } + } else { + throw new JsonParseException("pack.mcmeta::pack::description should be String or array of text objects with text and color fields"); + } + return new PackInfo(packFormat, new LocalModFile.Description(parts)); + } + } + + public static LocalModFile fromFile(ModManager modManager, Path modFile) throws IOException, JsonParseException { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile)) { + Path mcmod = fs.getPath("pack.mcmeta"); + if (Files.notExists(mcmod)) + throw new IOException("File " + modFile + " is not a resource pack."); + PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class); + return new LocalModFile( + modManager, + modManager.getLocalMod(FileUtils.getNameWithoutExtension(modFile), ModLoaderType.PACK), + modFile, + FileUtils.getNameWithoutExtension(modFile), + metadata.pack.description, + "", "", "", "", ""); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/RemoteMod.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/RemoteMod.java new file mode 100644 index 00000000..37fafaf9 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/RemoteMod.java @@ -0,0 +1,194 @@ +package com.tungsten.fclcore.mod; + +import static com.tungsten.fclcore.util.io.NetworkUtils.encodeLocation; + +import com.tungsten.fclcore.task.FileDownloadTask; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +public class RemoteMod { + private final String slug; + private final String author; + private final String title; + private final String description; + private final List categories; + private final String pageUrl; + private final String iconUrl; + private final IMod data; + + public RemoteMod(String slug, String author, String title, String description, List categories, String pageUrl, String iconUrl, IMod data) { + this.slug = slug; + this.author = author; + this.title = title; + this.description = description; + this.categories = categories; + this.pageUrl = pageUrl; + this.iconUrl = iconUrl; + this.data = data; + } + + public String getSlug() { + return slug; + } + + public String getAuthor() { + return author; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public List getCategories() { + return categories; + } + + public String getPageUrl() { + return pageUrl; + } + + public String getIconUrl() { + return iconUrl; + } + + public IMod getData() { + return data; + } + + public enum VersionType { + Release, + Beta, + Alpha + } + + public enum Type { + CURSEFORGE, + MODRINTH + } + + public interface IMod { + List loadDependencies(RemoteModRepository modRepository) throws IOException; + + Stream loadVersions(RemoteModRepository modRepository) throws IOException; + } + + public interface IVersion { + Type getType(); + } + + public static class Version { + private final IVersion self; + private final String modid; + private final String name; + private final String version; + private final String changelog; + private final Date datePublished; + private final VersionType versionType; + private final File file; + private final List dependencies; + private final List gameVersions; + private final List loaders; + + public Version(IVersion self, String modid, String name, String version, String changelog, Date datePublished, VersionType versionType, File file, List dependencies, List gameVersions, List loaders) { + this.self = self; + this.modid = modid; + this.name = name; + this.version = version; + this.changelog = changelog; + this.datePublished = datePublished; + this.versionType = versionType; + this.file = file; + this.dependencies = dependencies; + this.gameVersions = gameVersions; + this.loaders = loaders; + } + + public IVersion getSelf() { + return self; + } + + public String getModid() { + return modid; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getChangelog() { + return changelog; + } + + public Date getDatePublished() { + return datePublished; + } + + public VersionType getVersionType() { + return versionType; + } + + public File getFile() { + return file; + } + + public List getDependencies() { + return dependencies; + } + + public List getGameVersions() { + return gameVersions; + } + + public List getLoaders() { + return loaders; + } + } + + public static class File { + private final Map hashes; + private final String url; + private final String filename; + + public File(Map hashes, String url, String filename) { + this.hashes = hashes; + this.url = url; + this.filename = filename; + } + + public Map getHashes() { + return hashes; + } + + public FileDownloadTask.IntegrityCheck getIntegrityCheck() { + if (hashes.containsKey("md5")) { + return new FileDownloadTask.IntegrityCheck("MD5", hashes.get("sha1")); + } else if (hashes.containsKey("sha1")) { + return new FileDownloadTask.IntegrityCheck("SHA-1", hashes.get("sha1")); + } else if (hashes.containsKey("sha512")) { + return new FileDownloadTask.IntegrityCheck("SHA-256", hashes.get("sha1")); + } else { + return null; + } + } + + public String getUrl() { + return encodeLocation (url); + } + + public String getFilename() { + return filename; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/RemoteModRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/RemoteModRepository.java new file mode 100644 index 00000000..0bbaa03e --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/RemoteModRepository.java @@ -0,0 +1,95 @@ +package com.tungsten.fclcore.mod; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public interface RemoteModRepository { + + enum Type { + MOD, + MODPACK, + RESOURCE_PACK, + WORLD, + CUSTOMIZATION + } + + Type getType(); + + enum SortType { + DATE_CREATED, + POPULARITY, + LAST_UPDATED, + NAME, + AUTHOR, + TOTAL_DOWNLOADS + } + + enum SortOrder { + ASC, + DESC + } + + Stream search(String gameVersion, @Nullable Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) + throws IOException; + + Optional getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException; + + RemoteMod getModById(String id) throws IOException; + + RemoteMod.File getModFile(String modId, String fileId) throws IOException; + + Stream getRemoteVersionsById(String id) throws IOException; + + Stream getCategories() throws IOException; + + class Category { + private final Object self; + private final String id; + private final List subcategories; + + public Category(Object self, String id, List subcategories) { + this.self = self; + this.id = id; + this.subcategories = subcategories; + } + + public Object getSelf() { + return self; + } + + public String getId() { + return id; + } + + public List getSubcategories() { + return subcategories; + } + } + + String[] DEFAULT_GAME_VERSIONS = new String[]{ + "1.18.2", "1.18.1", "1.18", + "1.17.1", "1.17", + "1.16.5", "1.16.4", "1.16.3", "1.16.2", "1.16.1", "1.16", + "1.15.2", "1.15.1", "1.15", + "1.14.4", "1.14.3", "1.14.2", "1.14.1", "1.14", + "1.13.2", "1.13.1", "1.13", + "1.12.2", "1.12.1", "1.12", + "1.11.2", "1.11.1", "1.11", + "1.10.2", "1.10.1", "1.10", + "1.9.4", "1.9.3", "1.9.2", "1.9.1", "1.9", + "1.8.9", "1.8.8", "1.8.7", "1.8.6", "1.8.5", "1.8.4", "1.8.3", "1.8.2", "1.8.1", "1.8", + "1.7.10", "1.7.9", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", + "1.6.4", "1.6.2", "1.6.1", + "1.5.2", "1.5.1", + "1.4.7", "1.4.6", "1.4.5", "1.4.4", "1.4.2", + "1.3.2", "1.3.1", + "1.2.5", "1.2.4", "1.2.3", "1.2.2", "1.2.1", + "1.1", + "1.0" + }; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/UnsupportedModpackException.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/UnsupportedModpackException.java new file mode 100644 index 00000000..d4c78e42 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/UnsupportedModpackException.java @@ -0,0 +1,18 @@ +package com.tungsten.fclcore.mod; + +public class UnsupportedModpackException extends Exception { + public UnsupportedModpackException() { + } + + public UnsupportedModpackException(String message) { + super(message); + } + + public UnsupportedModpackException(String message, Throwable cause) { + super(message, cause); + } + + public UnsupportedModpackException(Throwable cause) { + super(cause); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseAddon.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseAddon.java new file mode 100644 index 00000000..9e016cd1 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseAddon.java @@ -0,0 +1,690 @@ +package com.tungsten.fclcore.mod.curse; + +import com.tungsten.fclcore.mod.ModLoaderType; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.mod.RemoteModRepository; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class CurseAddon implements RemoteMod.IMod { + private final int id; + private final int gameId; + private final String name; + private final String slug; + private final Links links; + private final String summary; + private final int status; + private final int downloadCount; + private final boolean isFeatured; + private final int primaryCategoryId; + private final List categories; + private final int classId; + private final List authors; + private final Logo logo; + private final int mainFileId; + private final List latestFiles; + private final List latestFileIndices; + private final Date dateCreated; + private final Date dateModified; + private final Date dateReleased; + private final boolean allowModDistribution; + private final int gamePopularityRank; + private final boolean isAvailable; + private final int thumbsUpCount; + + public CurseAddon(int id, int gameId, String name, String slug, Links links, String summary, int status, int downloadCount, boolean isFeatured, int primaryCategoryId, List categories, int classId, List authors, Logo logo, int mainFileId, List latestFiles, List latestFileIndices, Date dateCreated, Date dateModified, Date dateReleased, boolean allowModDistribution, int gamePopularityRank, boolean isAvailable, int thumbsUpCount) { + this.id = id; + this.gameId = gameId; + this.name = name; + this.slug = slug; + this.links = links; + this.summary = summary; + this.status = status; + this.downloadCount = downloadCount; + this.isFeatured = isFeatured; + this.primaryCategoryId = primaryCategoryId; + this.categories = categories; + this.classId = classId; + this.authors = authors; + this.logo = logo; + this.mainFileId = mainFileId; + this.latestFiles = latestFiles; + this.latestFileIndices = latestFileIndices; + this.dateCreated = dateCreated; + this.dateModified = dateModified; + this.dateReleased = dateReleased; + this.allowModDistribution = allowModDistribution; + this.gamePopularityRank = gamePopularityRank; + this.isAvailable = isAvailable; + this.thumbsUpCount = thumbsUpCount; + } + + public int getId() { + return id; + } + + public int getGameId() { + return gameId; + } + + public String getName() { + return name; + } + + public String getSlug() { + return slug; + } + + public Links getLinks() { + return links; + } + + public String getSummary() { + return summary; + } + + public int getStatus() { + return status; + } + + public int getDownloadCount() { + return downloadCount; + } + + public boolean isFeatured() { + return isFeatured; + } + + public int getPrimaryCategoryId() { + return primaryCategoryId; + } + + public List getCategories() { + return categories; + } + + public int getClassId() { + return classId; + } + + public List getAuthors() { + return authors; + } + + public Logo getLogo() { + return logo; + } + + public int getMainFileId() { + return mainFileId; + } + + public List getLatestFiles() { + return latestFiles; + } + + public List getLatestFileIndices() { + return latestFileIndices; + } + + public Date getDateCreated() { + return dateCreated; + } + + public Date getDateModified() { + return dateModified; + } + + public Date getDateReleased() { + return dateReleased; + } + + public boolean isAllowModDistribution() { + return allowModDistribution; + } + + public int getGamePopularityRank() { + return gamePopularityRank; + } + + public boolean isAvailable() { + return isAvailable; + } + + public int getThumbsUpCount() { + return thumbsUpCount; + } + + @Override + public List loadDependencies(RemoteModRepository modRepository) throws IOException { + Set dependencies = latestFiles.stream() + .flatMap(latestFile -> latestFile.getDependencies().stream()) + .filter(dep -> dep.getRelationType() == 3) + .map(Dependency::getModId) + .collect(Collectors.toSet()); + List mods = new ArrayList<>(); + for (int dependencyId : dependencies) { + mods.add(modRepository.getModById(Integer.toString(dependencyId))); + } + return mods; + } + + @Override + public Stream loadVersions(RemoteModRepository modRepository) throws IOException { + return modRepository.getRemoteVersionsById(Integer.toString(id)); + } + + public RemoteMod toMod() { + String iconUrl = Optional.ofNullable(logo).map(Logo::getThumbnailUrl).orElse(""); + + return new RemoteMod( + slug, + "", + name, + summary, + categories.stream().map(category -> Integer.toString(category.getId())).collect(Collectors.toList()), + links.websiteUrl, + iconUrl, + this + ); + } + + public static class Links { + private final String websiteUrl; + private final String wikiUrl; + private final String issuesUrl; + private final String sourceUrl; + + public Links(String websiteUrl, String wikiUrl, String issuesUrl, String sourceUrl) { + this.websiteUrl = websiteUrl; + this.wikiUrl = wikiUrl; + this.issuesUrl = issuesUrl; + this.sourceUrl = sourceUrl; + } + + public String getWebsiteUrl() { + return websiteUrl; + } + + public String getWikiUrl() { + return wikiUrl; + } + + @Nullable + public String getIssuesUrl() { + return issuesUrl; + } + + @Nullable + public String getSourceUrl() { + return sourceUrl; + } + } + + public static class Author { + private final int id; + private final String name; + private final String url; + + public Author(int id, String name, String url) { + this.id = id; + this.name = name; + this.url = url; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getUrl() { + return url; + } + } + + public static class Logo { + private final int id; + private final int modId; + private final String title; + private final String description; + private final String thumbnailUrl; + private final String url; + + public Logo(int id, int modId, String title, String description, String thumbnailUrl, String url) { + this.id = id; + this.modId = modId; + this.title = title; + this.description = description; + this.thumbnailUrl = thumbnailUrl; + this.url = url; + } + + public int getId() { + return id; + } + + public int getModId() { + return modId; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public String getThumbnailUrl() { + return thumbnailUrl; + } + + public String getUrl() { + return url; + } + } + + public static class Attachment { + private final int id; + private final int projectId; + private final String description; + private final boolean isDefault; + private final String thumbnailUrl; + private final String title; + private final String url; + private final int status; + + public Attachment(int id, int projectId, String description, boolean isDefault, String thumbnailUrl, String title, String url, int status) { + this.id = id; + this.projectId = projectId; + this.description = description; + this.isDefault = isDefault; + this.thumbnailUrl = thumbnailUrl; + this.title = title; + this.url = url; + this.status = status; + } + + public int getId() { + return id; + } + + public int getProjectId() { + return projectId; + } + + public String getDescription() { + return description; + } + + public boolean isDefault() { + return isDefault; + } + + public String getThumbnailUrl() { + return thumbnailUrl; + } + + public String getTitle() { + return title; + } + + public String getUrl() { + return url; + } + + public int getStatus() { + return status; + } + } + + public static class Dependency { + private final int modId; + private final int relationType; + + public Dependency() { + this(0, 1); + } + + public Dependency(int modId, int relationType) { + this.modId = modId; + this.relationType = relationType; + } + + public int getModId() { + return modId; + } + + public int getRelationType() { + return relationType; + } + } + + /** + * @see Schema + */ + public static class LatestFileHash { + private final String value; + private final int algo; + + public LatestFileHash(String value, int algo) { + this.value = value; + this.algo = algo; + } + + public String getValue() { + return value; + } + + public int getAlgo() { + return algo; + } + } + + /** + * @see Schema + */ + public static class LatestFile implements RemoteMod.IVersion { + private final int id; + private final int gameId; + private final int modId; + private final boolean isAvailable; + private final String displayName; + private final String fileName; + private final int releaseType; + private final int fileStatus; + private final List hashes; + private final Date fileDate; + private final int fileLength; + private final int downloadCount; + private final String downloadUrl; + private final List gameVersions; + private final List dependencies; + private final int alternateFileId; + private final boolean isServerPack; + private final long fileFingerprint; + + public LatestFile(int id, int gameId, int modId, boolean isAvailable, String displayName, String fileName, int releaseType, int fileStatus, List hashes, Date fileDate, int fileLength, int downloadCount, String downloadUrl, List gameVersions, List dependencies, int alternateFileId, boolean isServerPack, long fileFingerprint) { + this.id = id; + this.gameId = gameId; + this.modId = modId; + this.isAvailable = isAvailable; + this.displayName = displayName; + this.fileName = fileName; + this.releaseType = releaseType; + this.fileStatus = fileStatus; + this.hashes = hashes; + this.fileDate = fileDate; + this.fileLength = fileLength; + this.downloadCount = downloadCount; + this.downloadUrl = downloadUrl; + this.gameVersions = gameVersions; + this.dependencies = dependencies; + this.alternateFileId = alternateFileId; + this.isServerPack = isServerPack; + this.fileFingerprint = fileFingerprint; + } + + public int getId() { + return id; + } + + public int getGameId() { + return gameId; + } + + public int getModId() { + return modId; + } + + public boolean isAvailable() { + return isAvailable; + } + + public String getDisplayName() { + return displayName; + } + + public String getFileName() { + return fileName; + } + + public int getReleaseType() { + return releaseType; + } + + public int getFileStatus() { + return fileStatus; + } + + public List getHashes() { + return hashes; + } + + public Date getFileDate() { + return fileDate; + } + + public int getFileLength() { + return fileLength; + } + + public int getDownloadCount() { + return downloadCount; + } + + public String getDownloadUrl() { + if (downloadUrl == null) { + // This addon is not allowed for distribution, and downloadUrl will be null. + // We try to find its download url. + return String.format("https://edge.forgecdn.net/files/%d/%d/%s", id / 1000, id % 1000, fileName); + } + return downloadUrl; + } + + public List getGameVersions() { + return gameVersions; + } + + public List getDependencies() { + return dependencies; + } + + public int getAlternateFileId() { + return alternateFileId; + } + + public boolean isServerPack() { + return isServerPack; + } + + public long getFileFingerprint() { + return fileFingerprint; + } + + @Override + public RemoteMod.Type getType() { + return RemoteMod.Type.CURSEFORGE; + } + + public RemoteMod.Version toVersion() { + RemoteMod.VersionType versionType; + switch (getReleaseType()) { + case 1: + versionType = RemoteMod.VersionType.Release; + break; + case 2: + versionType = RemoteMod.VersionType.Beta; + break; + case 3: + versionType = RemoteMod.VersionType.Alpha; + break; + default: + versionType = RemoteMod.VersionType.Release; + break; + } + + ModLoaderType modLoaderType; + if (gameVersions.contains("Forge")) { + modLoaderType = ModLoaderType.FORGE; + } else if (gameVersions.contains("Fabric")) { + modLoaderType = ModLoaderType.FABRIC; + } else { + modLoaderType = ModLoaderType.UNKNOWN; + } + + return new RemoteMod.Version( + this, + Integer.toString(modId), + getDisplayName(), + getFileName(), + null, + getFileDate(), + versionType, + new RemoteMod.File(Collections.emptyMap(), getDownloadUrl(), getFileName()), + Collections.emptyList(), + gameVersions.stream().filter(ver -> ver.startsWith("1.") || ver.contains("w")).collect(Collectors.toList()), + Collections.singletonList(modLoaderType) + ); + } + } + + /** + * @see Schema + */ + public static class LatestFileIndex { + private final String gameVersion; + private final int fileId; + private final String filename; + private final int releaseType; + private final int gameVersionTypeId; + private final int modLoader; + + public LatestFileIndex(String gameVersion, int fileId, String filename, int releaseType, int gameVersionTypeId, int modLoader) { + this.gameVersion = gameVersion; + this.fileId = fileId; + this.filename = filename; + this.releaseType = releaseType; + this.gameVersionTypeId = gameVersionTypeId; + this.modLoader = modLoader; + } + + public String getGameVersion() { + return gameVersion; + } + + public int getFileId() { + return fileId; + } + + public String getFilename() { + return filename; + } + + public int getReleaseType() { + return releaseType; + } + + @Nullable + public int getGameVersionTypeId() { + return gameVersionTypeId; + } + + public int getModLoader() { + return modLoader; + } + } + + public static class Category { + private final int id; + private final int gameId; + private final String name; + private final String slug; + private final String url; + private final String iconUrl; + private final Date dateModified; + private final boolean isClass; + private final int classId; + private final int parentCategoryId; + + private transient final List subcategories; + + public Category() { + this(0, 0, "", "", "", "", new Date(), false, 0, 0); + } + + public Category(int id, int gameId, String name, String slug, String url, String iconUrl, Date dateModified, boolean isClass, int classId, int parentCategoryId) { + this.id = id; + this.gameId = gameId; + this.name = name; + this.slug = slug; + this.url = url; + this.iconUrl = iconUrl; + this.dateModified = dateModified; + this.isClass = isClass; + this.classId = classId; + this.parentCategoryId = parentCategoryId; + + this.subcategories = new ArrayList<>(); + } + + public int getId() { + return id; + } + + public int getGameId() { + return gameId; + } + + public String getName() { + return name; + } + + public String getSlug() { + return slug; + } + + public String getUrl() { + return url; + } + + public String getIconUrl() { + return iconUrl; + } + + public Date getDateModified() { + return dateModified; + } + + public boolean isClass() { + return isClass; + } + + public int getClassId() { + return classId; + } + + public int getParentCategoryId() { + return parentCategoryId; + } + + public List getSubcategories() { + return subcategories; + } + + public RemoteModRepository.Category toCategory() { + return new RemoteModRepository.Category( + this, + Integer.toString(id), + getSubcategories().stream().map(Category::toCategory).collect(Collectors.toList())); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseCompletionTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseCompletionTask.java new file mode 100644 index 00000000..4bdd8e32 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseCompletionTask.java @@ -0,0 +1,154 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModManager; +import com.tungsten.fclcore.mod.ModpackCompletionException; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.stream.Collectors; + +/** + * Complete the CurseForge version. + * + * @author huangyuhui + */ +public final class CurseCompletionTask extends Task { + + private final DefaultDependencyManager dependency; + private final DefaultGameRepository repository; + private final ModManager modManager; + private final String version; + private CurseManifest manifest; + private final List> dependencies = new ArrayList<>(); + + private final AtomicBoolean allNameKnown = new AtomicBoolean(true); + private final AtomicInteger finished = new AtomicInteger(0); + private final AtomicBoolean notFound = new AtomicBoolean(false); + + /** + * Constructor. + * + * @param dependencyManager the dependency manager. + * @param version the existent and physical version. + */ + public CurseCompletionTask(DefaultDependencyManager dependencyManager, String version) { + this(dependencyManager, version, null); + } + + /** + * Constructor. + * + * @param dependencyManager the dependency manager. + * @param version the existent and physical version. + * @param manifest the CurseForgeModpack manifest. + */ + public CurseCompletionTask(DefaultDependencyManager dependencyManager, String version, CurseManifest manifest) { + this.dependency = dependencyManager; + this.repository = dependencyManager.getGameRepository(); + this.modManager = repository.getModManager(version); + this.version = version; + this.manifest = manifest; + + if (manifest == null) + try { + File manifestFile = new File(repository.getVersionRoot(version), "manifest.json"); + if (manifestFile.exists()) + this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), CurseManifest.class); + } catch (Exception e) { + Logging.LOG.log(Level.WARNING, "Unable to read CurseForge modpack manifest.json", e); + } + + setStage("hmcl.modpack.download"); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() throws Exception { + if (manifest == null) + return; + + File root = repository.getVersionRoot(version); + + // Because in China, Curse is too difficult to visit, + // if failed, ignore it and retry next time. + CurseManifest newManifest = manifest.setFiles( + manifest.getFiles().parallelStream() + .map(file -> { + updateProgress(finished.incrementAndGet(), manifest.getFiles().size()); + if (StringUtils.isBlank(file.getFileName()) || file.getUrl() == null) { + try { + RemoteMod.File remoteFile = CurseForgeRemoteModRepository.MODS.getModFile(Integer.toString(file.getProjectID()), Integer.toString(file.getFileID())); + return file.withFileName(remoteFile.getFilename()).withURL(remoteFile.getUrl()); + } catch (FileNotFoundException fof) { + Logging.LOG.log(Level.WARNING, "Could not query api.curseforge.com for deleted mods: " + file.getProjectID() + ", " + file.getFileID(), fof); + notFound.set(true); + return file; + } catch (IOException | JsonParseException e) { + Logging.LOG.log(Level.WARNING, "Unable to fetch the file name projectID=" + file.getProjectID() + ", fileID=" + file.getFileID(), e); + allNameKnown.set(false); + return file; + } + } else { + return file; + } + }) + .collect(Collectors.toList())); + FileUtils.writeText(new File(root, "manifest.json"), JsonUtils.GSON.toJson(newManifest)); + + for (CurseManifestFile file : newManifest.getFiles()) + if (StringUtils.isNotBlank(file.getFileName())) { + if (!modManager.hasSimpleMod(file.getFileName())) { + FileDownloadTask task = new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile()); + task.setCacheRepository(dependency.getCacheRepository()); + task.setCaching(true); + dependencies.add(task.withCounter("hmcl.modpack.download")); + } + } + + if (!dependencies.isEmpty()) { + getProperties().put("total", dependencies.size()); + notifyPropertiesChanged(); + } + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + // Let this task fail if the curse manifest has not been completed. + // But continue other downloads. + if (notFound.get()) + throw new ModpackCompletionException(new FileNotFoundException()); + if (!allNameKnown.get() || !isDependenciesSucceeded()) + throw new ModpackCompletionException(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseForgeRemoteModRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseForgeRemoteModRepository.java new file mode 100644 index 00000000..5301bb0d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseForgeRemoteModRepository.java @@ -0,0 +1,309 @@ +package com.tungsten.fclcore.mod.curse; + +import static com.tungsten.fclcore.util.Lang.mapOf; +import static com.tungsten.fclcore.util.Pair.pair; + +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.mod.LocalModFile; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.mod.RemoteModRepository; +import com.tungsten.fclcore.util.io.HttpRequest; + +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Stream; + +public final class CurseForgeRemoteModRepository implements RemoteModRepository { + + private static final String PREFIX = "https://api.curseforge.com"; + + private static String apiKey; + + static { + apiKey = System.getProperty("hmcl.curseforge.apikey", + JarUtils.thisJar().flatMap(JarUtils::getManifest).map(manifest -> manifest.getMainAttributes().getValue("CurseForge-Api-Key")).orElse("")); + } + + private final Type type; + private final int section; + + public CurseForgeRemoteModRepository(Type type, int section) { + this.type = type; + this.section = section; + } + + @Override + public Type getType() { + return type; + } + + private int toModsSearchSortField(SortType sort) { + // https://docs.curseforge.com/#tocS_ModsSearchSortField + switch (sort) { + case DATE_CREATED: + return 1; + case POPULARITY: + return 2; + case LAST_UPDATED: + return 3; + case NAME: + return 4; + case AUTHOR: + return 5; + case TOTAL_DOWNLOADS: + return 6; + default: + return 8; + } + } + + private String toSortOrder(SortOrder sortOrder) { + // https://docs.curseforge.com/#tocS_SortOrder + switch (sortOrder) { + case ASC: + return "asc"; + case DESC: + return "desc"; + } + return "asc"; + } + + @Override + public Stream search(String gameVersion, @Nullable Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException { + int categoryId = 0; + if (category != null) categoryId = ((CurseAddon.Category) category.getSelf()).getId(); + Response> response = HttpRequest.GET(PREFIX + "/v1/mods/search", + pair("gameId", "432"), + pair("classId", Integer.toString(section)), + pair("categoryId", Integer.toString(categoryId)), + pair("gameVersion", gameVersion), + pair("searchFilter", searchFilter), + pair("sortField", Integer.toString(toModsSearchSortField(sortType))), + pair("sortOrder", toSortOrder(sortOrder)), + pair("index", Integer.toString(pageOffset)), + pair("pageSize", Integer.toString(pageSize))) + .header("X-API-KEY", apiKey) + .getJson(new TypeToken>>() { + }.getType()); + return response.getData().stream().map(CurseAddon::toMod); + } + + @Override + public Optional getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (InputStream stream = Files.newInputStream(file)) { + byte[] buf = new byte[1024]; + int len; + while ((len = stream.read(buf, 0, buf.length)) != -1) { + for (int i = 0; i < len; i++) { + byte b = buf[i]; + if (b != 0x9 && b != 0xa && b != 0xd && b != 0x20) { + baos.write(b); + } + } + } + } + + long hash = Integer.toUnsignedLong(MurmurHash2.hash32(baos.toByteArray(), baos.size(), 1)); + + Response response = HttpRequest.POST(PREFIX + "/v1/fingerprints") + .json(mapOf(pair("fingerprints", Collections.singletonList(hash)))) + .header("X-API-KEY", apiKey) + .getJson(new TypeToken>() { + }.getType()); + + if (response.getData().getExactMatches() == null || response.getData().getExactMatches().isEmpty()) { + return Optional.empty(); + } + + return Optional.of(response.getData().getExactMatches().get(0).getFile().toVersion()); + } + + @Override + public RemoteMod getModById(String id) throws IOException { + Response response = HttpRequest.GET(PREFIX + "/v1/mods/" + id) + .header("X-API-KEY", apiKey) + .getJson(new TypeToken>() { + }.getType()); + return response.data.toMod(); + } + + @Override + public RemoteMod.File getModFile(String modId, String fileId) throws IOException { + Response response = HttpRequest.GET(String.format("%s/v1/mods/%s/files/%s", PREFIX, modId, fileId)) + .header("X-API-KEY", apiKey) + .getJson(new TypeToken>() { + }.getType()); + return response.getData().toVersion().getFile(); + } + + @Override + public Stream getRemoteVersionsById(String id) throws IOException { + Response> response = HttpRequest.GET(PREFIX + "/v1/mods/" + id + "/files", + pair("pageSize", "10000")) + .header("X-API-KEY", apiKey) + .getJson(new TypeToken>>() { + }.getType()); + return response.getData().stream().map(CurseAddon.LatestFile::toVersion); + } + + public List getCategoriesImpl() throws IOException { + Response> categories = HttpRequest.GET(PREFIX + "/v1/categories", pair("gameId", "432")) + .header("X-API-KEY", apiKey) + .getJson(new TypeToken>>() { + }.getType()); + return reorganizeCategories(categories.getData(), section); + } + + @Override + public Stream getCategories() throws IOException { + return getCategoriesImpl().stream().map(CurseAddon.Category::toCategory); + } + + private List reorganizeCategories(List categories, int rootId) { + List result = new ArrayList<>(); + + Map categoryMap = new HashMap<>(); + for (CurseAddon.Category category : categories) { + categoryMap.put(category.getId(), category); + } + for (CurseAddon.Category category : categories) { + if (category.getParentCategoryId() == rootId) { + result.add(category); + } else { + CurseAddon.Category parentCategory = categoryMap.get(category.getParentCategoryId()); + if (parentCategory == null) { + // Category list is not correct, so we ignore this item. + continue; + } + parentCategory.getSubcategories().add(category); + } + } + return result; + } + + public static final int SECTION_BUKKIT_PLUGIN = 5; + public static final int SECTION_MOD = 6; + public static final int SECTION_RESOURCE_PACK = 12; + public static final int SECTION_WORLD = 17; + public static final int SECTION_MODPACK = 4471; + public static final int SECTION_CUSTOMIZATION = 4546; + public static final int SECTION_ADDONS = 4559; // For Pocket Edition + public static final int SECTION_UNKNOWN1 = 4944; + public static final int SECTION_UNKNOWN2 = 4979; + public static final int SECTION_UNKNOWN3 = 4984; + + public static final CurseForgeRemoteModRepository MODS = new CurseForgeRemoteModRepository(Type.MOD, SECTION_MOD); + public static final CurseForgeRemoteModRepository MODPACKS = new CurseForgeRemoteModRepository(Type.MODPACK, SECTION_MODPACK); + public static final CurseForgeRemoteModRepository RESOURCE_PACKS = new CurseForgeRemoteModRepository(Type.RESOURCE_PACK, SECTION_RESOURCE_PACK); + public static final CurseForgeRemoteModRepository WORLDS = new CurseForgeRemoteModRepository(Type.WORLD, SECTION_WORLD); + public static final CurseForgeRemoteModRepository CUSTOMIZATIONS = new CurseForgeRemoteModRepository(Type.CUSTOMIZATION, SECTION_CUSTOMIZATION); + + public static class Pagination { + private final int index; + private final int pageSize; + private final int resultCount; + private final int totalCount; + + public Pagination(int index, int pageSize, int resultCount, int totalCount) { + this.index = index; + this.pageSize = pageSize; + this.resultCount = resultCount; + this.totalCount = totalCount; + } + + public int getIndex() { + return index; + } + + public int getPageSize() { + return pageSize; + } + + public int getResultCount() { + return resultCount; + } + + public int getTotalCount() { + return totalCount; + } + } + + public static class Response { + private final T data; + private final Pagination pagination; + + public Response(T data, Pagination pagination) { + this.data = data; + this.pagination = pagination; + } + + public T getData() { + return data; + } + + public Pagination getPagination() { + return pagination; + } + } + + /** + * @see Schema + */ + private static class FingerprintMatchesResult { + private final boolean isCacheBuilt; + private final List exactMatches; + private final List exactFingerprints; + + public FingerprintMatchesResult(boolean isCacheBuilt, List exactMatches, List exactFingerprints) { + this.isCacheBuilt = isCacheBuilt; + this.exactMatches = exactMatches; + this.exactFingerprints = exactFingerprints; + } + + public boolean isCacheBuilt() { + return isCacheBuilt; + } + + public List getExactMatches() { + return exactMatches; + } + + public List getExactFingerprints() { + return exactFingerprints; + } + } + + /** + * @see Schema + */ + private static class FingerprintMatch { + private final int id; + private final CurseAddon.LatestFile file; + private final List latestFiles; + + public FingerprintMatch(int id, CurseAddon.LatestFile file, List latestFiles) { + this.id = id; + this.file = file; + this.latestFiles = latestFiles; + } + + public int getId() { + return id; + } + + public CurseAddon.LatestFile getFile() { + return file; + } + + public List getLatestFiles() { + return latestFiles; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseInstallTask.java new file mode 100644 index 00000000..c179faf3 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseInstallTask.java @@ -0,0 +1,127 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.MinecraftInstanceTask; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackCompletionException; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackInstallTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Install a downloaded CurseForge modpack. + */ +public final class CurseInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final DefaultGameRepository repository; + private final File zipFile; + private final Modpack modpack; + private final CurseManifest manifest; + private final String name; + private final File run; + private final ModpackConfiguration config; + private final List> dependents = new ArrayList<>(4); + private final List> dependencies = new ArrayList<>(1); + + /** + * Constructor. + * + * @param dependencyManager the dependency manager. + * @param zipFile the CurseForge modpack file. + * @param manifest The manifest content of given CurseForge modpack. + * @param name the new version name + * @see CurseManifest#readCurseForgeModpackManifest + */ + public CurseInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, CurseManifest manifest, String name) { + this.dependencyManager = dependencyManager; + this.zipFile = zipFile; + this.modpack = modpack; + this.manifest = manifest; + this.name = name; + this.repository = dependencyManager.getGameRepository(); + this.run = repository.getRunDirectory(name); + + File json = repository.getModpackConfiguration(name); + if (repository.hasVersion(name) && !json.exists()) + throw new IllegalArgumentException("Version " + name + " already exists."); + + GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getMinecraft().getGameVersion()); + for (CurseManifestModLoader modLoader : manifest.getMinecraft().getModLoaders()) { + if (modLoader.getId().startsWith("forge-")) { + builder.version("forge", modLoader.getId().substring("forge-".length())); + } else if (modLoader.getId().startsWith("fabric-")) { + builder.version("fabric", modLoader.getId().substring("fabric-".length())); + } + } + dependents.add(builder.buildAsync()); + + onDone().register(event -> { + Exception ex = event.getTask().getException(); + if (event.isFailed()) { + if (!(ex instanceof ModpackCompletionException)) { + repository.removeVersionFromDisk(name); + } + } + }); + + ModpackConfiguration config = null; + try { + if (json.exists()) { + config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + + if (!CurseModpackProvider.INSTANCE.getName().equals(config.getType())) + throw new IllegalArgumentException("Version " + name + " is not a Curse modpack. Cannot update this version."); + } + } catch (JsonParseException | IOException ignore) { + } + this.config = config; + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(manifest.getOverrides()), any -> true, config).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(manifest.getOverrides()), manifest, CurseModpackProvider.INSTANCE, manifest.getName(), manifest.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + + dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest)); + } + + @Override + public Collection> getDependents() { + return dependents; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws Exception { + if (config != null) { + // For update, remove mods not listed in new manifest + for (CurseManifestFile oldCurseManifestFile : config.getManifest().getFiles()) { + if (StringUtils.isBlank(oldCurseManifestFile.getFileName())) continue; + File oldFile = new File(run, "mods/" + oldCurseManifestFile.getFileName()); + if (!oldFile.exists()) continue; + if (manifest.getFiles().stream().noneMatch(oldCurseManifestFile::equals)) + if (!oldFile.delete()) + throw new IOException("Unable to delete mod file " + oldFile); + } + } + + File root = repository.getVersionRoot(name); + FileUtils.writeText(new File(root, "manifest.json"), JsonUtils.GSON.toJson(manifest)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifest.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifest.java new file mode 100644 index 00000000..55126638 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifest.java @@ -0,0 +1,93 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.mod.ModpackManifest; +import com.tungsten.fclcore.mod.ModpackProvider; + +import java.util.Collections; +import java.util.List; + +public final class CurseManifest implements ModpackManifest { + + @SerializedName("manifestType") + private final String manifestType; + + @SerializedName("manifestVersion") + private final int manifestVersion; + + @SerializedName("name") + private final String name; + + @SerializedName("version") + private final String version; + + @SerializedName("author") + private final String author; + + @SerializedName("overrides") + private final String overrides; + + @SerializedName("minecraft") + private final CurseManifestMinecraft minecraft; + + @SerializedName("files") + private final List files; + + public CurseManifest() { + this(MINECRAFT_MODPACK, 1, "", "1.0", "", "overrides", new CurseManifestMinecraft(), Collections.emptyList()); + } + + public CurseManifest(String manifestType, int manifestVersion, String name, String version, String author, String overrides, CurseManifestMinecraft minecraft, List files) { + this.manifestType = manifestType; + this.manifestVersion = manifestVersion; + this.name = name; + this.version = version; + this.author = author; + this.overrides = overrides; + this.minecraft = minecraft; + this.files = files; + } + + public String getManifestType() { + return manifestType; + } + + public int getManifestVersion() { + return manifestVersion; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getAuthor() { + return author; + } + + public String getOverrides() { + return overrides; + } + + public CurseManifestMinecraft getMinecraft() { + return minecraft; + } + + public List getFiles() { + return files; + } + + public CurseManifest setFiles(List files) { + return new CurseManifest(manifestType, manifestVersion, name, version, author, overrides, minecraft, files); + } + + @Override + public ModpackProvider getProvider() { + return CurseModpackProvider.INSTANCE; + } + + public static final String MINECRAFT_MODPACK = "minecraftModpack"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestFile.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestFile.java new file mode 100644 index 00000000..70d96569 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestFile.java @@ -0,0 +1,98 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.gson.Validation; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import org.jetbrains.annotations.Nullable; + +import java.net.URL; +import java.util.Objects; + +public final class CurseManifestFile implements Validation { + + @SerializedName("projectID") + private final int projectID; + + @SerializedName("fileID") + private final int fileID; + + @SerializedName("fileName") + private final String fileName; + + @SerializedName("url") + private final String url; + + @SerializedName("required") + private final boolean required; + + public CurseManifestFile() { + this(0, 0, null, null, true); + } + + public CurseManifestFile(int projectID, int fileID, String fileName, String url, boolean required) { + this.projectID = projectID; + this.fileID = fileID; + this.fileName = fileName; + this.url = url; + this.required = required; + } + + public int getProjectID() { + return projectID; + } + + public int getFileID() { + return fileID; + } + + public String getFileName() { + return fileName; + } + + public boolean isRequired() { + return required; + } + + @Override + public void validate() throws JsonParseException { + if (projectID == 0 || fileID == 0) + throw new JsonParseException("Missing Project ID or File ID."); + } + + @Nullable + public URL getUrl() { + if (url == null) { + if (fileName != null) { + return NetworkUtils.toURL(NetworkUtils.encodeLocation(String.format("https://edge.forgecdn.net/files/%d/%d/%s", fileID / 1000, fileID % 1000, fileName))); + } else { + return null; + } + } else { + return NetworkUtils.toURL(NetworkUtils.encodeLocation(url)); + } + } + + public CurseManifestFile withFileName(String fileName) { + return new CurseManifestFile(projectID, fileID, fileName, url, required); + } + + public CurseManifestFile withURL(String url) { + return new CurseManifestFile(projectID, fileID, fileName, url, required); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CurseManifestFile that = (CurseManifestFile) o; + return projectID == that.projectID && + fileID == that.fileID; + } + + @Override + public int hashCode() { + return Objects.hash(projectID, fileID); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestMinecraft.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestMinecraft.java new file mode 100644 index 00000000..078f3fef --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestMinecraft.java @@ -0,0 +1,44 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.Validation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class CurseManifestMinecraft implements Validation { + + @SerializedName("version") + private final String gameVersion; + + @SerializedName("modLoaders") + private final List modLoaders; + + public CurseManifestMinecraft() { + this.gameVersion = ""; + this.modLoaders = Collections.emptyList(); + } + + public CurseManifestMinecraft(String gameVersion, List modLoaders) { + this.gameVersion = gameVersion; + this.modLoaders = new ArrayList<>(modLoaders); + } + + public String getGameVersion() { + return gameVersion; + } + + public List getModLoaders() { + return Collections.unmodifiableList(modLoaders); + } + + @Override + public void validate() throws JsonParseException { + if (StringUtils.isBlank(gameVersion)) + throw new JsonParseException("CurseForge Manifest.gameVersion cannot be blank."); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestModLoader.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestModLoader.java new file mode 100644 index 00000000..ce1c472d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseManifestModLoader.java @@ -0,0 +1,39 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.Validation; + +public final class CurseManifestModLoader implements Validation { + + @SerializedName("id") + private final String id; + + @SerializedName("primary") + private final boolean primary; + + public CurseManifestModLoader() { + this("", false); + } + + public CurseManifestModLoader(String id, boolean primary) { + this.id = id; + this.primary = primary; + } + + public String getId() { + return id; + } + + public boolean isPrimary() { + return primary; + } + + @Override + public void validate() throws JsonParseException { + if (StringUtils.isBlank(id)) + throw new JsonParseException("Curse Forge modpack manifest Mod loader id cannot be blank."); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseMetaMod.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseMetaMod.java new file mode 100644 index 00000000..129c7eb3 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseMetaMod.java @@ -0,0 +1,49 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.annotations.SerializedName; + +/** + * CurseMetaMod is JSON structure for + * https://cursemeta.dries007.net/<projectID>/<fileID>.json + * https://addons-ecs.forgesvc.net/api/v2/addon/<projectID>/file/<fileID> + */ +public final class CurseMetaMod { + @SerializedName(value = "Id", alternate = "id") + private final int id; + + @SerializedName(value = "FileName", alternate = "fileName") + private final String fileName; + + @SerializedName(value = "FileNameOnDisk") + private final String fileNameOnDisk; + + @SerializedName(value = "DownloadURL", alternate = "downloadUrl") + private final String downloadURL; + + public CurseMetaMod() { + this(0, "", "", ""); + } + + public CurseMetaMod(int id, String fileName, String fileNameOnDisk, String downloadURL) { + this.id = id; + this.fileName = fileName; + this.fileNameOnDisk = fileNameOnDisk; + this.downloadURL = downloadURL; + } + + public int getId() { + return id; + } + + public String getFileName() { + return fileName; + } + + public String getFileNameOnDisk() { + return fileNameOnDisk; + } + + public String getDownloadURL() { + return downloadURL; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseModpackProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseModpackProvider.java new file mode 100644 index 00000000..541c8953 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/curse/CurseModpackProvider.java @@ -0,0 +1,58 @@ +package com.tungsten.fclcore.mod.curse; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.mod.MismatchedModpackTypeException; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.mod.ModpackUpdateTask; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.util.zip.ZipFile; + +public final class CurseModpackProvider implements ModpackProvider { + public static final CurseModpackProvider INSTANCE = new CurseModpackProvider(); + + @Override + public String getName() { + return "Curse"; + } + + @Override + public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { + return new CurseCompletionTask(dependencyManager, version); + } + + @Override + public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, File zipFile, Modpack modpack) throws MismatchedModpackTypeException { + if (!(modpack.getManifest() instanceof CurseManifest)) + throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); + + return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new CurseInstallTask(dependencyManager, zipFile, modpack, (CurseManifest) modpack.getManifest(), name)); + } + + @Override + public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException { + CurseManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "manifest.json"), CurseManifest.class); + String description = "No description"; + try { + ZipArchiveEntry modlist = zip.getEntry("modlist.html"); + if (modlist != null) + description = IOUtils.readFullyAsString(zip.getInputStream(modlist)); + } catch (Throwable ignored) { + } + + return new Modpack(manifest.getName(), manifest.getAuthor(), manifest.getVersion(), manifest.getMinecraft().getGameVersion(), description, encoding, manifest) { + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name) { + return new CurseInstallTask(dependencyManager, zipFile, this, manifest, name); + } + }; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackCompletionTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackCompletionTask.java new file mode 100644 index 00000000..e2f38120 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackCompletionTask.java @@ -0,0 +1,312 @@ +package com.tungsten.fclcore.mod.mcbbs; + +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModManager; +import com.tungsten.fclcore.mod.ModpackCompletionException; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.curse.CurseMetaMod; +import com.tungsten.fclcore.task.CompletableFutureTask; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.GetTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.task.TaskCompletableFuture; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class McbbsModpackCompletionTask extends CompletableFutureTask { + + private final DefaultDependencyManager dependency; + private final DefaultGameRepository repository; + private final ModManager modManager; + private final String version; + private final File configurationFile; + private ModpackConfiguration configuration; + private McbbsModpackManifest manifest; + private final List> dependencies = new ArrayList<>(); + + private final AtomicBoolean allNameKnown = new AtomicBoolean(true); + private final AtomicInteger finished = new AtomicInteger(0); + private final AtomicBoolean notFound = new AtomicBoolean(false); + + public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) { + this(dependencyManager, version, null); + } + + public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, String version, ModpackConfiguration configuration) { + this.dependency = dependencyManager; + this.repository = dependencyManager.getGameRepository(); + this.modManager = repository.getModManager(version); + this.version = version; + this.configurationFile = repository.getModpackConfiguration(version); + this.configuration = configuration; + + setStage("hmcl.modpack.download"); + } + + @Override + public CompletableFuture getFuture(TaskCompletableFuture executor) { + return breakable(CompletableFuture.runAsync(wrap(() -> { + if (configuration == null) { + // Load configuration from disk + try { + configuration = JsonUtils.fromNonNullJson(FileUtils.readText(configurationFile), new TypeToken>() { + }.getType()); + } catch (IOException | JsonParseException e) { + throw new IOException("Malformed modpack configuration"); + } + } + manifest = configuration.getManifest(); + if (manifest == null) throw new CustomException(); + })).thenComposeAsync(unused -> { + // we first download latest manifest + return breakable(CompletableFuture.runAsync(wrap(() -> { + if (StringUtils.isBlank(manifest.getFileApi())) { + // skip this phase + throw new CustomException(); + } + })).thenComposeAsync(wrap(unused1 -> { + return executor.one(new GetTask(new URL(manifest.getFileApi() + "/manifest.json"))); + })).thenComposeAsync(wrap(remoteManifestJson -> { + McbbsModpackManifest remoteManifest; + // We needs to update modpack from online server. + try { + remoteManifest = JsonUtils.fromNonNullJson(remoteManifestJson, McbbsModpackManifest.class); + } catch (JsonParseException e) { + throw new IOException("Unable to parse server manifest.json from " + manifest.getFileApi(), e); + } + + Path rootPath = repository.getVersionRoot(version).toPath(); + + Map localFiles = manifest.getFiles().stream().collect(Collectors.toMap(Function.identity(), Function.identity())); + + // for files in new modpack + List newFiles = new ArrayList<>(remoteManifest.getFiles().size()); + List> tasks = new ArrayList<>(); + for (McbbsModpackManifest.File file : remoteManifest.getFiles()) { + Path actualPath = getFilePath(file); + McbbsModpackManifest.File oldFile = localFiles.remove(file); + boolean download = false; + if (oldFile == null) { + // If old modpack does not have this entry, download it + download = true; + } else if (actualPath != null) { + if (!Files.exists(actualPath)) { + // If both old and new modpacks have this entry, but the file is missing... + // Re-download it since network problem may cause file missing + download = true; + } else if (getFileHash(file) != null) { + // If user modified this entry file, we will not replace this file since this modified file is what user expects. + // Or we have downloaded latest file in previous completion task, this time we have no need to download it again. + String fileHash = encodeHex(digest("SHA-1", actualPath)); + String oldHash = getFileHash(oldFile); + String newHash = getFileHash(file); + if (oldHash == null) { + // We don't know whether the file is modified or not, just update it. + download = true; + } else if (!Objects.equals(fileHash, newHash)) { + if (file.isForce()) { + // this file is not allowed to be modified, required by modpack author. + download = true; + } else if (Objects.equals(oldHash, fileHash)) { + download = true; + } + } + } + } else { + // we resolve files with unknown path later. + } + + if (download) { + tasks.add(downloadFile(remoteManifest, file)); + } + + newFiles.add(mergeFile(oldFile, file)); + } + + // If old modpack have this entry, and new modpack deleted it. Delete this file. + // for-loop above removes still existing file in localFiles. Remaining elements + // are files removed by next modpack version. + // Notice that this loop will also remove Curse mods. + for (McbbsModpackManifest.File file : localFiles.keySet()) { + Path actualPath = getFilePath(file); + if (actualPath != null && Files.exists(actualPath)) + Files.deleteIfExists(actualPath); + } + + manifest = remoteManifest.setFiles(newFiles); + return executor.all(tasks.stream().filter(Objects::nonNull).collect(Collectors.toList())); + })).thenAcceptAsync(wrapConsumer(unused1 -> { + File manifestFile = repository.getModpackConfiguration(version); + FileUtils.writeText(manifestFile, JsonUtils.GSON.toJson( + new ModpackConfiguration<>(manifest, this.configuration.getType(), this.manifest.getName(), this.manifest.getVersion(), + this.manifest.getFiles().stream() + .flatMap(file -> file instanceof McbbsModpackManifest.AddonFile + ? Stream.of((McbbsModpackManifest.AddonFile) file) + : Stream.empty()) + .map(file -> new ModpackConfiguration.FileInformation(file.getPath(), file.getHash())) + .collect(Collectors.toList())))); + }))); + }).thenComposeAsync(unused -> { + AtomicBoolean allNameKnown = new AtomicBoolean(true); + AtomicInteger finished = new AtomicInteger(0); + AtomicBoolean notFound = new AtomicBoolean(false); + + return breakable(CompletableFuture.completedFuture(null) + .thenComposeAsync(wrap(unused1 -> { + List> dependencies = new ArrayList<>(); + // Because in China, Curse is too difficult to visit, + // if failed, ignore it and retry next time. + McbbsModpackManifest newManifest = manifest.setFiles( + manifest.getFiles().parallelStream() + .map(rawFile -> { + updateProgress(finished.incrementAndGet(), manifest.getFiles().size()); + if (rawFile instanceof McbbsModpackManifest.CurseFile) { + McbbsModpackManifest.CurseFile file = (McbbsModpackManifest.CurseFile) rawFile; + if (StringUtils.isBlank(file.getFileName())) { + try { + return file.withFileName(NetworkUtils.detectFileName(file.getUrl())); + } catch (IOException e) { + try { + String result = NetworkUtils.doGet(NetworkUtils.toURL(String.format("https://cursemeta.dries007.net/%d/%d.json", file.getProjectID(), file.getFileID()))); + CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class); + return file.withFileName(mod.getFileNameOnDisk()).withURL(mod.getDownloadURL()); + } catch (FileNotFoundException fof) { + Logging.LOG.log(Level.WARNING, "Could not query cursemeta for deleted mods: " + file.getUrl(), fof); + notFound.set(true); + return file; + } catch (IOException | JsonParseException e2) { + try { + String result = NetworkUtils.doGet(NetworkUtils.toURL(String.format("https://addons-ecs.forgesvc.net/api/v2/addon/%d/file/%d", file.getProjectID(), file.getFileID()))); + CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class); + return file.withFileName(mod.getFileName()).withURL(mod.getDownloadURL()); + } catch (FileNotFoundException fof) { + Logging.LOG.log(Level.WARNING, "Could not query forgesvc for deleted mods: " + file.getUrl(), fof); + notFound.set(true); + return file; + } catch (IOException | JsonParseException e3) { + Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), e); + Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), e2); + Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), e3); + allNameKnown.set(false); + return file; + } + } + } + } else { + return file; + } + } else { + return rawFile; + } + }) + .collect(Collectors.toList())); + + manifest = newManifest; + configuration = configuration.setManifest(newManifest); + FileUtils.writeText(configurationFile, JsonUtils.GSON.toJson(configuration)); + + for (McbbsModpackManifest.File file : newManifest.getFiles()) + if (file instanceof McbbsModpackManifest.CurseFile) { + McbbsModpackManifest.CurseFile curseFile = (McbbsModpackManifest.CurseFile) file; + if (StringUtils.isNotBlank(curseFile.getFileName())) { + if (!modManager.hasSimpleMod(curseFile.getFileName())) { + FileDownloadTask task = new FileDownloadTask(curseFile.getUrl(), modManager.getSimpleModPath(curseFile.getFileName()).toFile()); + task.setCacheRepository(dependency.getCacheRepository()); + task.setCaching(true); + dependencies.add(task.withCounter("hmcl.modpack.download")); + } + } + } + + if (!dependencies.isEmpty()) { + getProperties().put("total", dependencies.size()); + notifyPropertiesChanged(); + } + + return executor.all(dependencies); + })).whenComplete(wrap((unused1, ex) -> { + // Let this task fail if the curse manifest has not been completed. + // But continue other downloads. + if (notFound.get()) + throw new ModpackCompletionException(new FileNotFoundException()); + if (!allNameKnown.get() || ex != null) + throw new ModpackCompletionException(); + }))); + })); + } + + @Nullable + private Path getFilePath(McbbsModpackManifest.File file) { + if (file instanceof McbbsModpackManifest.AddonFile) { + return modManager.getRepository().getRunDirectory(modManager.getVersion()).toPath().resolve(((McbbsModpackManifest.AddonFile) file).getPath()); + } else if (file instanceof McbbsModpackManifest.CurseFile) { + String fileName = ((McbbsModpackManifest.CurseFile) file).getFileName(); + if (fileName == null) return null; + return modManager.getSimpleModPath(fileName); + } else { + throw new IllegalArgumentException(); + } + } + + private String getFileHash(McbbsModpackManifest.File file) { + if (file instanceof McbbsModpackManifest.AddonFile) { + return ((McbbsModpackManifest.AddonFile) file).getHash(); + } else { + return null; + } + } + + private Task downloadFile(McbbsModpackManifest remoteManifest, McbbsModpackManifest.File file) throws IOException { + if (file instanceof McbbsModpackManifest.AddonFile) { + McbbsModpackManifest.AddonFile addonFile = (McbbsModpackManifest.AddonFile) file; + return new FileDownloadTask( + new URL(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(addonFile.getPath())), + modManager.getSimpleModPath(addonFile.getPath()).toFile(), + addonFile.getHash() != null ? new FileDownloadTask.IntegrityCheck("SHA-1", addonFile.getHash()) : null); + } else if (file instanceof McbbsModpackManifest.CurseFile) { + // we download it later. + return null; + } else { + throw new IllegalArgumentException(); + } + } + + @NotNull + private McbbsModpackManifest.File mergeFile(@Nullable McbbsModpackManifest.File oldFile, @NotNull McbbsModpackManifest.File newFile) { + if (newFile instanceof McbbsModpackManifest.AddonFile) { + return newFile; + } else if (newFile instanceof McbbsModpackManifest.CurseFile) { + // Preserves prefetched file names and urls. + return oldFile != null ? oldFile : newFile; + } else { + throw new IllegalArgumentException(); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackExportTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackExportTask.java new file mode 100644 index 00000000..b90eee34 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackExportTask.java @@ -0,0 +1,118 @@ +package com.tungsten.fclcore.mod.mcbbs; + +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.FABRIC; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.FORGE; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.LITELOADER; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.MINECRAFT; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.OPTIFINE; +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; + +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.mod.ModAdviser; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackExportInfo; +import com.tungsten.fclcore.mod.curse.CurseManifest; +import com.tungsten.fclcore.mod.curse.CurseManifestModLoader; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class McbbsModpackExportTask extends Task { + private final DefaultGameRepository repository; + private final String version; + private final ModpackExportInfo info; + private final File modpackFile; + + public McbbsModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, File modpackFile) { + this.repository = repository; + this.version = version; + this.info = info.validate(); + this.modpackFile = modpackFile; + + onDone().register(event -> { + if (event.isFailed()) modpackFile.delete(); + }); + } + + @Override + public void execute() throws Exception { + ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); + blackList.add(version + ".jar"); + blackList.add(version + ".json"); + Logging.LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); + try (Zipper zip = new Zipper(modpackFile.toPath())) { + Path runDirectory = repository.getRunDirectory(version).toPath(); + List files = new ArrayList<>(); + zip.putDirectory(runDirectory, "overrides", path -> { + if (Modpack.acceptFile(path, blackList, info.getWhitelist())) { + Path file = runDirectory.resolve(path); + if (Files.isRegularFile(file)) { + String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); + files.add(new McbbsModpackManifest.AddonFile(true, relativePath, encodeHex(digest("SHA-1", file)))); + } + return true; + } else { + return false; + } + }); + + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version)); + String gameVersion = repository.getGameVersion(version) + .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); + + // Mcbbs manifest + List addons = new ArrayList<>(); + addons.add(new McbbsModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion)); + analyzer.getVersion(FORGE).ifPresent(forgeVersion -> + addons.add(new McbbsModpackManifest.Addon(FORGE.getPatchId(), forgeVersion))); + analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion -> + addons.add(new McbbsModpackManifest.Addon(LITELOADER.getPatchId(), liteLoaderVersion))); + analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion -> + addons.add(new McbbsModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion))); + analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> + addons.add(new McbbsModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion))); + + List libraries = new ArrayList<>(); + // TODO libraries + + List origins = new ArrayList<>(); + // TODO origins + + McbbsModpackManifest.Settings settings = new McbbsModpackManifest.Settings(); + McbbsModpackManifest.LaunchInfo launchInfo = new McbbsModpackManifest.LaunchInfo(info.getMinMemory(), info.getSupportedJavaVersions(), StringUtils.tokenize(info.getLaunchArguments()), StringUtils.tokenize(info.getJavaArguments())); + + McbbsModpackManifest mcbbsManifest = new McbbsModpackManifest(McbbsModpackManifest.MANIFEST_TYPE, 2, info.getName(), info.getVersion(), info.getAuthor(), info.getDescription(), info.getFileApi() == null ? null : StringUtils.removeSuffix(info.getFileApi(), "/"), info.getUrl(), info.isForceUpdate(), origins, addons, libraries, files, settings, launchInfo); + zip.putTextFile(JsonUtils.GSON.toJson(mcbbsManifest), "mcbbs.packmeta"); + + // CurseForge manifest + List modLoaders = new ArrayList<>(); + analyzer.getVersion(FORGE).ifPresent(forgeVersion -> modLoaders.add(new CurseManifestModLoader("forge-" + forgeVersion, true))); + analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> modLoaders.add(new CurseManifestModLoader("fabric-" + fabricVersion, true))); + // OptiFine and LiteLoader are not supported by CurseForge modpack. + CurseManifest curseManifest = new CurseManifest(CurseManifest.MINECRAFT_MODPACK, 1, info.getName(), info.getVersion(), info.getAuthor(), "overrides", new CurseManifestMinecraft(gameVersion, modLoaders), Collections.emptyList()); + zip.putTextFile(JsonUtils.GSON.toJson(curseManifest), "manifest.json"); + } + } + + public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options() + .requireFileApi(true) + .requireUrl() + .requireForceUpdate() + .requireMinMemory() + .requireAuthlibInjectorServer() + .requireJavaArguments() + .requireLaunchArguments() + .requireOrigins(); + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackLocalInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackLocalInstallTask.java new file mode 100644 index 00000000..cde95b9a --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackLocalInstallTask.java @@ -0,0 +1,109 @@ +package com.tungsten.fclcore.mod.mcbbs; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.mod.MinecraftInstanceTask; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackInstallTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class McbbsModpackLocalInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final File zipFile; + private final Modpack modpack; + private final McbbsModpackManifest manifest; + private final String name; + private final boolean update; + private final DefaultGameRepository repository; + private final MinecraftInstanceTask instanceTask; + private final List> dependencies = new ArrayList<>(2); + private final List> dependents = new ArrayList<>(4); + + public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, McbbsModpackManifest manifest, String name) { + this.dependencyManager = dependencyManager; + this.zipFile = zipFile; + this.modpack = modpack; + this.manifest = manifest; + this.name = name; + this.repository = dependencyManager.getGameRepository(); + File run = repository.getRunDirectory(name); + + File json = repository.getModpackConfiguration(name); + if (repository.hasVersion(name) && !json.exists()) + throw new IllegalArgumentException("Version " + name + " already exists."); + this.update = repository.hasVersion(name); + + + GameBuilder builder = dependencyManager.gameBuilder().name(name); + for (McbbsModpackManifest.Addon addon : manifest.getAddons()) { + builder.version(addon.getId(), addon.getVersion()); + } + + dependents.add(builder.buildAsync()); + onDone().register(event -> { + if (event.isFailed()) + repository.removeVersionFromDisk(name); + }); + + ModpackConfiguration config = null; + try { + if (json.exists()) { + config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + + if (!McbbsModpackProvider.INSTANCE.getName().equals(config.getType())) + throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version."); + } + } catch (JsonParseException | IOException ignore) { + } + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack")); + instanceTask = new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, McbbsModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)); + dependents.add(instanceTask.withStage("hmcl.modpack")); + } + + @Override + public List> getDependents() { + return dependents; + } + + @Override + public List> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws Exception { + Version version = repository.readVersionJson(name); + Optional mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); + if (!update) { + Version patch = new Version(PATCH_NAME).setLibraries(manifest.getLibraries()); + dependencies.add(repository.saveAsync(version.addPatch(patch))); + } else if (mcbbsPatch.isPresent()) { + // This mcbbs modpack was installed by HMCL. + Version patch = mcbbsPatch.get().setLibraries(manifest.getLibraries()); + dependencies.add(repository.saveAsync(version.addPatch(patch))); + } else { + // This mcbbs modpack was installed by other launchers. + // TODO: maintain libraries. + } + + dependencies.add(new McbbsModpackCompletionTask(dependencyManager, name, instanceTask.getResult())); + } + + private static final String PATCH_NAME = "mcbbs"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackManifest.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackManifest.java new file mode 100644 index 00000000..3c68f2f8 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackManifest.java @@ -0,0 +1,425 @@ +package com.tungsten.fclcore.mod.mcbbs; + +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.MINECRAFT; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.LaunchOptions; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackManifest; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonSubtype; +import com.tungsten.fclcore.util.gson.JsonType; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class McbbsModpackManifest implements ModpackManifest, Validation { + public static final String MANIFEST_TYPE = "minecraftModpack"; + + private final String manifestType; + private final int manifestVersion; + private final String name; + private final String version; + private final String author; + private final String description; + + @Nullable + private final String fileApi; + private final String url; + private final boolean forceUpdate; + @SerializedName("origin") + private final List origins; + private final List addons; + private final List libraries; + private final List files; + private final Settings settings; + private final LaunchInfo launchInfo; + // sandbox and antiCheating are both not supported. + + public McbbsModpackManifest() { + this(MANIFEST_TYPE, 1, "", "", "", "", null, "", false, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), new Settings(), new LaunchInfo()); + } + + public McbbsModpackManifest(String manifestType, int manifestVersion, String name, String version, String author, String description, @Nullable String fileApi, String url, boolean forceUpdate, List origins, List addons, List libraries, List files, Settings settings, LaunchInfo launchInfo) { + this.manifestType = manifestType; + this.manifestVersion = manifestVersion; + this.name = name; + this.version = version; + this.author = author; + this.description = description; + this.fileApi = fileApi; + this.url = url; + this.forceUpdate = forceUpdate; + this.origins = origins; + this.addons = addons; + this.libraries = libraries; + this.files = files; + this.settings = settings; + this.launchInfo = launchInfo; + } + + public String getManifestType() { + return manifestType; + } + + public int getManifestVersion() { + return manifestVersion; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getAuthor() { + return author; + } + + public String getDescription() { + return description; + } + + public String getFileApi() { + return fileApi; + } + + public String getUrl() { + return url; + } + + public boolean isForceUpdate() { + return forceUpdate; + } + + public List getOrigins() { + return origins; + } + + public List getAddons() { + return addons; + } + + public List getLibraries() { + return libraries; + } + + public List getFiles() { + return files; + } + + public Settings getSettings() { + return settings; + } + + public LaunchInfo getLaunchInfo() { + return launchInfo; + } + + public McbbsModpackManifest setFiles(List files) { + return new McbbsModpackManifest(manifestType, manifestVersion, name, version, author, description, fileApi, url, forceUpdate, origins, addons, libraries, files, settings, launchInfo); + } + + @Override + public ModpackProvider getProvider() { + return McbbsModpackProvider.INSTANCE; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (!MANIFEST_TYPE.equals(manifestType)) + throw new JsonParseException("McbbsModpackManifest.manifestType must be 'minecraftModpack'"); +// if (manifestVersion > 1) +// throw new JsonParseException("Only supports version 1 of McbbsModpackManifest"); + if (files == null) + throw new JsonParseException("McbbsModpackManifest.files cannot be null"); + if (addons == null) + throw new JsonParseException("McbbsModpackManifest.addons cannot be null"); + } + + public static final class Origin { + private final String type; + private final int id; + + public Origin() { + this("", 0); + } + + public Origin(String type, int id) { + this.type = type; + this.id = id; + } + + public String getType() { + return type; + } + + public int getId() { + return id; + } + } + + public static final class Addon { + private final String id; + private final String version; + + public Addon() { + this("", ""); + } + + public Addon(String id, String version) { + this.id = id; + this.version = version; + } + + public String getId() { + return id; + } + + public String getVersion() { + return version; + } + } + + public static final class Settings { + @SerializedName("install_mods") + private final boolean installMods; + + @SerializedName("install_resourcepack") + private final boolean installResourcepack; + + public Settings() { + this(true, true); + } + + public Settings(boolean installMods, boolean installResourcepack) { + this.installMods = installMods; + this.installResourcepack = installResourcepack; + } + + public boolean isInstallMods() { + return installMods; + } + + public boolean isInstallResourcepack() { + return installResourcepack; + } + } + + @JsonType( + property = "type", + subtypes = { + @JsonSubtype(clazz = AddonFile.class, name = "addon"), + @JsonSubtype(clazz = CurseFile.class, name = "curse") + } + ) + public static abstract class File implements Validation { + protected final boolean force; + + public File(boolean force) { + this.force = force; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + } + + public boolean isForce() { + return force; + } + } + + public static final class AddonFile extends File { + private final String path; + private final String hash; + + public AddonFile(boolean force, String path, String hash) { + super(force); + this.path = Objects.requireNonNull(path); + this.hash = Objects.requireNonNull(hash); + } + + public String getPath() { + return path; + } + + public String getHash() { + return hash; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + super.validate(); + + Validation.requireNonNull(path, "AddonFile.path cannot be null"); + Validation.requireNonNull(hash, "AddonFile.hash cannot be null"); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AddonFile addonFile = (AddonFile) o; + return path.equals(addonFile.path); + } + + @Override + public int hashCode() { + return Objects.hash(path); + } + } + + public static final class CurseFile extends File { + private final int projectID; + private final int fileID; + private final String fileName; + private final String url; + + public CurseFile() { + this(false, 0, 0, "", ""); + } + + public CurseFile(boolean force, int projectID, int fileID, String fileName, String url) { + super(force); + this.projectID = projectID; + this.fileID = fileID; + this.fileName = fileName; + this.url = url; + } + + public int getProjectID() { + return projectID; + } + + public int getFileID() { + return fileID; + } + + @Nullable + public String getFileName() { + return fileName; + } + + public URL getUrl() { + return url == null ? NetworkUtils.toURL("https://www.curseforge.com/minecraft/mc-mods/" + projectID + "/download/" + fileID + "/file") + : NetworkUtils.toURL(NetworkUtils.encodeLocation(url)); + } + + public CurseFile withFileName(String fileName) { + return new CurseFile(force, projectID, fileID, fileName, url); + } + + public CurseFile withURL(String url) { + return new CurseFile(force, projectID, fileID, fileName, url); + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + super.validate(); + + if (projectID == 0 || fileID == 0) { + throw new JsonParseException("CurseFile.{projectID|fileID} cannot be empty."); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CurseFile curseFile = (CurseFile) o; + return projectID == curseFile.projectID && fileID == curseFile.fileID; + } + + @Override + public int hashCode() { + return Objects.hash(projectID, fileID); + } + } + + public static final class LaunchInfo { + private final int minMemory; + private final List supportJava; + @SerializedName("launchArgument") + private final List launchArguments; + @SerializedName("javaArgument") + private final List javaArguments; + + public LaunchInfo() { + this(0, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + } + + public LaunchInfo(int minMemory, List supportJava, List launchArguments, List javaArguments) { + this.minMemory = minMemory; + this.supportJava = supportJava; + this.launchArguments = launchArguments; + this.javaArguments = javaArguments; + } + + public int getMinMemory() { + return minMemory; + } + + @Nullable + public List getSupportJava() { + return supportJava; + } + + public List getLaunchArguments() { + return Optional.ofNullable(launchArguments).orElseGet(Collections::emptyList); + } + + public List getJavaArguments() { + return Optional.ofNullable(javaArguments).orElseGet(Collections::emptyList); + } + } + + public static class ServerInfo { + private final String authlibInjectorServer; + + public ServerInfo() { + this(null); + } + + public ServerInfo(String authlibInjectorServer) { + this.authlibInjectorServer = authlibInjectorServer; + } + + @Nullable + public String getAuthlibInjectorServer() { + return authlibInjectorServer; + } + } + + public Modpack toModpack(Charset encoding) throws IOException { + String gameVersion = addons.stream().filter(x -> MINECRAFT.getPatchId().equals(x.id)).findAny() + .orElseThrow(() -> new IOException("Cannot find game version")).getVersion(); + return new Modpack(name, author, version, gameVersion, description, encoding, this) { + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, java.io.File zipFile, String name) { + return new McbbsModpackLocalInstallTask(dependencyManager, zipFile, this, McbbsModpackManifest.this, name); + } + }; + } + + public void injectLaunchOptions(LaunchOptions.Builder launchOptions) { + launchOptions.getGameArguments().addAll(launchInfo.getLaunchArguments()); + launchOptions.getJavaArguments().addAll(launchInfo.getJavaArguments()); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackProvider.java new file mode 100644 index 00000000..6cbd2842 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackProvider.java @@ -0,0 +1,72 @@ +package com.tungsten.fclcore.mod.mcbbs; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.LaunchOptions; +import com.tungsten.fclcore.mod.MismatchedModpackTypeException; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.mod.ModpackUpdateTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.util.zip.ZipFile; + +public final class McbbsModpackProvider implements ModpackProvider { + public static final McbbsModpackProvider INSTANCE = new McbbsModpackProvider(); + + @Override + public String getName() { + return "Mcbbs"; + } + + @Override + public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { + return new McbbsModpackCompletionTask(dependencyManager, version); + } + + @Override + public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, File zipFile, Modpack modpack) throws MismatchedModpackTypeException { + if (!(modpack.getManifest() instanceof McbbsModpackManifest)) + throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); + + return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new McbbsModpackLocalInstallTask(dependencyManager, zipFile, modpack, (McbbsModpackManifest) modpack.getManifest(), name)); + } + + @Override + public void injectLaunchOptions(String modpackConfigurationJson, LaunchOptions.Builder builder) { + ModpackConfiguration config = JsonUtils.GSON.fromJson(modpackConfigurationJson, new TypeToken>() { + }.getType()); + + if (!getName().equals(config.getType())) { + throw new IllegalArgumentException("Incorrect manifest type, actual=" + config.getType() + ", expected=" + getName()); + } + + config.getManifest().injectLaunchOptions(builder); + } + + private static Modpack fromManifestFile(String json, Charset encoding) throws IOException, JsonParseException { + McbbsModpackManifest manifest = JsonUtils.fromNonNullJson(json, McbbsModpackManifest.class); + return manifest.toModpack(encoding); + } + + @Override + public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException { + ZipArchiveEntry mcbbsPackMeta = zip.getEntry("mcbbs.packmeta"); + if (mcbbsPackMeta != null) { + return fromManifestFile(IOUtils.readFullyAsString(zip.getInputStream(mcbbsPackMeta)), encoding); + } + ZipArchiveEntry manifestJson = zip.getEntry("manifest.json"); + if (manifestJson != null) { + return fromManifestFile(IOUtils.readFullyAsString(zip.getInputStream(manifestJson)), encoding); + } + throw new IOException("`mcbbs.packmeta` or `manifest.json` cannot be found"); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackRemoteInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackRemoteInstallTask.java new file mode 100644 index 00000000..9b0d7096 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/mcbbs/McbbsModpackRemoteInstallTask.java @@ -0,0 +1,78 @@ +package com.tungsten.fclcore.mod.mcbbs; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class McbbsModpackRemoteInstallTask extends Task { + + private final String name; + private final DefaultDependencyManager dependency; + private final DefaultGameRepository repository; + private final List> dependencies = new ArrayList<>(1); + private final List> dependents = new ArrayList<>(1); + private final McbbsModpackManifest manifest; + + public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, McbbsModpackManifest manifest, String name) { + this.name = name; + this.dependency = dependencyManager; + this.repository = dependencyManager.getGameRepository(); + this.manifest = manifest; + + File json = repository.getModpackConfiguration(name); + if (repository.hasVersion(name) && !json.exists()) + throw new IllegalArgumentException("Version " + name + " already exists."); + + GameBuilder builder = dependencyManager.gameBuilder().name(name); + for (McbbsModpackManifest.Addon addon : manifest.getAddons()) { + builder.version(addon.getId(), addon.getVersion()); + } + + dependents.add(builder.buildAsync()); + onDone().register(event -> { + if (event.isFailed()) + repository.removeVersionFromDisk(name); + }); + + ModpackConfiguration config = null; + try { + if (json.exists()) { + config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + + if (!MODPACK_TYPE.equals(config.getType())) + throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version."); + } + } catch (JsonParseException | IOException ignore) { + } + } + + @Override + public List> getDependents() { + return dependents; + } + + @Override + public List> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws Exception { + dependencies.add(new McbbsModpackCompletionTask(dependency, name, new ModpackConfiguration<>(manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), Collections.emptyList()))); + } + + public static final String MODPACK_TYPE = "Server"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthCompletionTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthCompletionTask.java new file mode 100644 index 00000000..7531e40c --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthCompletionTask.java @@ -0,0 +1,119 @@ +package com.tungsten.fclcore.mod.modrinth; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModpackCompletionException; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; + +public class ModrinthCompletionTask extends Task { + + private final DefaultDependencyManager dependency; + private final DefaultGameRepository repository; + private final String version; + private ModrinthManifest manifest; + private final List> dependencies = new ArrayList<>(); + + private final AtomicBoolean allNameKnown = new AtomicBoolean(true); + private final AtomicInteger finished = new AtomicInteger(0); + private final AtomicBoolean notFound = new AtomicBoolean(false); + + /** + * Constructor. + * + * @param dependencyManager the dependency manager. + * @param version the existent and physical version. + */ + public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, String version) { + this(dependencyManager, version, null); + } + + /** + * Constructor. + * + * @param dependencyManager the dependency manager. + * @param version the existent and physical version. + * @param manifest the CurseForgeModpack manifest. + */ + public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, String version, ModrinthManifest manifest) { + this.dependency = dependencyManager; + this.repository = dependencyManager.getGameRepository(); + this.version = version; + this.manifest = manifest; + + if (manifest == null) + try { + File manifestFile = new File(repository.getVersionRoot(version), "modrinth.index.json"); + if (manifestFile.exists()) + this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), ModrinthManifest.class); + } catch (Exception e) { + Logging.LOG.log(Level.WARNING, "Unable to read Modrinth modpack manifest.json", e); + } + + setStage("hmcl.modpack.download"); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public boolean isRelyingOnDependencies() { + return false; + } + + @Override + public void execute() throws Exception { + if (manifest == null) + return; + + Path runDirectory = repository.getRunDirectory(version).toPath(); + + for (ModrinthManifest.File file : manifest.getFiles()) { + if (file.getEnv() != null && file.getEnv().getOrDefault("client", "required").equals("unsupported")) + continue; + Path filePath = runDirectory.resolve(file.getPath()); + if (!Files.exists(filePath) && !file.getDownloads().isEmpty()) { + FileDownloadTask task = new FileDownloadTask(file.getDownloads().get(0), filePath.toFile()); + task.setCacheRepository(dependency.getCacheRepository()); + task.setCaching(true); + dependencies.add(task.withCounter("hmcl.modpack.download")); + } + } + + if (!dependencies.isEmpty()) { + getProperties().put("total", dependencies.size()); + notifyPropertiesChanged(); + } + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + // Let this task fail if the curse manifest has not been completed. + // But continue other downloads. + if (notFound.get()) + throw new ModpackCompletionException(new FileNotFoundException()); + if (!allNameKnown.get() || !isDependenciesSucceeded()) + throw new ModpackCompletionException(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthInstallTask.java new file mode 100644 index 00000000..a6e86132 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthInstallTask.java @@ -0,0 +1,125 @@ +package com.tungsten.fclcore.mod.modrinth; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.MinecraftInstanceTask; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackCompletionException; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackInstallTask; +import com.tungsten.fclcore.mod.curse.CurseManifest; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public class ModrinthInstallTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final DefaultGameRepository repository; + private final File zipFile; + private final Modpack modpack; + private final ModrinthManifest manifest; + private final String name; + private final File run; + private final ModpackConfiguration config; + private final List> dependents = new ArrayList<>(4); + private final List> dependencies = new ArrayList<>(1); + + public ModrinthInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, ModrinthManifest manifest, String name) { + this.dependencyManager = dependencyManager; + this.zipFile = zipFile; + this.modpack = modpack; + this.manifest = manifest; + this.name = name; + this.repository = dependencyManager.getGameRepository(); + this.run = repository.getRunDirectory(name); + + File json = repository.getModpackConfiguration(name); + if (repository.hasVersion(name) && !json.exists()) + throw new IllegalArgumentException("Version " + name + " already exists."); + + GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion()); + for (Map.Entry modLoader : manifest.getDependencies().entrySet()) { + switch (modLoader.getKey()) { + case "minecraft": + break; + case "forge": + builder.version("forge", modLoader.getValue()); + break; + case "fabric-loader": + builder.version("fabric", modLoader.getValue()); + break; + case "quilt-loader": + builder.version("quilt", modLoader.getValue()); + break; + default: + throw new IllegalStateException("Unsupported mod loader " + modLoader.getKey()); + } + } + dependents.add(builder.buildAsync()); + + onDone().register(event -> { + Exception ex = event.getTask().getException(); + if (event.isFailed()) { + if (!(ex instanceof ModpackCompletionException)) { + repository.removeVersionFromDisk(name); + } + } + }); + + ModpackConfiguration config = null; + try { + if (json.exists()) { + config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + + if (!ModrinthModpackProvider.INSTANCE.getName().equals(config.getType())) + throw new IllegalArgumentException("Version " + name + " is not a Modrinth modpack. Cannot update this version."); + } + } catch (JsonParseException | IOException ignore) { + } + + this.config = config; + List subDirectories = Arrays.asList("/client-overrides", "/overrides"); + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), subDirectories, any -> true, config).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), subDirectories, manifest, ModrinthModpackProvider.INSTANCE, manifest.getName(), manifest.getVersionId(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + + dependencies.add(new ModrinthCompletionTask(dependencyManager, name, manifest)); + } + + @Override + public Collection> getDependents() { + return dependents; + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws Exception { + if (config != null) { + // For update, remove mods not listed in new manifest + for (ModrinthManifest.File oldManifestFile : config.getManifest().getFiles()) { + Path oldFile = run.toPath().resolve(oldManifestFile.getPath()); + if (!Files.exists(oldFile)) continue; + if (manifest.getFiles().stream().noneMatch(oldManifestFile::equals)) { + Files.deleteIfExists(oldFile); + } + } + } + + File root = repository.getVersionRoot(name); + FileUtils.writeText(new File(root, "modrinth.index.json"), JsonUtils.GSON.toJson(manifest)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthManifest.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthManifest.java new file mode 100644 index 00000000..9ca363c5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthManifest.java @@ -0,0 +1,130 @@ +package com.tungsten.fclcore.mod.modrinth; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.mod.ModpackManifest; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; + +import org.jetbrains.annotations.Nullable; + +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class ModrinthManifest implements ModpackManifest, Validation { + + private final String game; + private final int formatVersion; + private final String versionId; + private final String name; + @Nullable + private final String summary; + private final List files; + private final Map dependencies; + + public ModrinthManifest(String game, int formatVersion, String versionId, String name, @Nullable String summary, List files, Map dependencies) { + this.game = game; + this.formatVersion = formatVersion; + this.versionId = versionId; + this.name = name; + this.summary = summary; + this.files = files; + this.dependencies = dependencies; + } + + public String getGame() { + return game; + } + + public int getFormatVersion() { + return formatVersion; + } + + public String getVersionId() { + return versionId; + } + + public String getName() { + return name; + } + + public String getSummary() { + return summary == null ? "" : summary; + } + + public List getFiles() { + return files; + } + + public Map getDependencies() { + return dependencies; + } + + public String getGameVersion() { + return dependencies.get("minecraft"); + } + + @Override + public ModpackProvider getProvider() { + return ModrinthModpackProvider.INSTANCE; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (dependencies == null || dependencies.get("minecraft") == null) { + throw new JsonParseException("missing Modrinth.dependencies.minecraft"); + } + } + + public static class File { + private final String path; + private final Map hashes; + private final Map env; + private final List downloads; + private final int fileSize; + + public File(String path, Map hashes, Map env, List downloads, int fileSize) { + this.path = path; + this.hashes = hashes; + this.env = env; + this.downloads = downloads; + this.fileSize = fileSize; + } + + public String getPath() { + return path; + } + + public Map getHashes() { + return hashes; + } + + public Map getEnv() { + return env; + } + + public List getDownloads() { + return downloads; + } + + public int getFileSize() { + return fileSize; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + File file = (File) o; + return fileSize == file.fileSize && path.equals(file.path) && hashes.equals(file.hashes) && env.equals(file.env) && downloads.equals(file.downloads); + } + + @Override + public int hashCode() { + return Objects.hash(path, hashes, env, downloads, fileSize); + } + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthModpackProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthModpackProvider.java new file mode 100644 index 00000000..27457721 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthModpackProvider.java @@ -0,0 +1,49 @@ +package com.tungsten.fclcore.mod.modrinth; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.mod.MismatchedModpackTypeException; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.mod.ModpackUpdateTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; + +public final class ModrinthModpackProvider implements ModpackProvider { + public static final ModrinthModpackProvider INSTANCE = new ModrinthModpackProvider(); + + @Override + public String getName() { + return "Modrinth"; + } + + @Override + public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { + return new ModrinthCompletionTask(dependencyManager, version); + } + + @Override + public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, File zipFile, Modpack modpack) throws MismatchedModpackTypeException { + if (!(modpack.getManifest() instanceof ModrinthManifest)) + throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); + + return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new ModrinthInstallTask(dependencyManager, zipFile, modpack, (ModrinthManifest) modpack.getManifest(), name)); + } + + @Override + public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException { + ModrinthManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "modrinth.index.json"), ModrinthManifest.class); + return new Modpack(manifest.getName(), "", manifest.getVersionId(), manifest.getGameVersion(), manifest.getSummary(), encoding, manifest) { + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name) { + return new ModrinthInstallTask(dependencyManager, zipFile, this, manifest, name); + } + }; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthRemoteModRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthRemoteModRepository.java new file mode 100644 index 00000000..2864a0d1 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/modrinth/ModrinthRemoteModRepository.java @@ -0,0 +1,703 @@ +package com.tungsten.fclcore.mod.modrinth; + +import static com.tungsten.fclcore.util.Lang.mapOf; +import static com.tungsten.fclcore.util.Pair.pair; + +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.mod.LocalModFile; +import com.tungsten.fclcore.mod.ModLoaderType; +import com.tungsten.fclcore.mod.RemoteMod; +import com.tungsten.fclcore.mod.RemoteModRepository; +import com.tungsten.fclcore.util.DigestUtils; +import com.tungsten.fclcore.util.Hex; +import com.tungsten.fclcore.util.Lang; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.HttpRequest; +import com.tungsten.fclcore.util.io.NetworkUtils; +import com.tungsten.fclcore.util.io.ResponseCodeException; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public final class ModrinthRemoteModRepository implements RemoteModRepository { + public static final ModrinthRemoteModRepository MODS = new ModrinthRemoteModRepository("mod"); + public static final ModrinthRemoteModRepository MODPACKS = new ModrinthRemoteModRepository("modpack"); + + private static final String PREFIX = "https://api.modrinth.com"; + + private final String projectType; + + private ModrinthRemoteModRepository(String projectType) { + this.projectType = projectType; + } + + @Override + public Type getType() { + return Type.MOD; + } + + private static String convertSortType(SortType sortType) { + switch (sortType) { + case DATE_CREATED: + return "newest"; + case POPULARITY: + case NAME: + case AUTHOR: + return "relevance"; + case LAST_UPDATED: + return "updated"; + case TOTAL_DOWNLOADS: + return "downloads"; + default: + throw new IllegalArgumentException("Unsupported sort type " + sortType); + } + } + + @Override + public Stream search(String gameVersion, @Nullable RemoteModRepository.Category category, int pageOffset, int pageSize, String searchFilter, SortType sort, SortOrder sortOrder) throws IOException { + List> facets = new ArrayList<>(); + facets.add(Collections.singletonList("project_type:" + projectType)); + if (StringUtils.isNotBlank(gameVersion)) { + facets.add(Collections.singletonList("versions:" + gameVersion)); + } + if (category != null && StringUtils.isNotBlank(category.getId())) { + facets.add(Collections.singletonList("categories:" + category.getId())); + } + Map query = mapOf( + pair("query", searchFilter), + pair("facets", JsonUtils.UGLY_GSON.toJson(facets)), + pair("offset", Integer.toString(pageOffset)), + pair("limit", Integer.toString(pageSize)), + pair("index", convertSortType(sort)) + ); + Response response = HttpRequest.GET(NetworkUtils.withQuery(PREFIX + "/v2/search", query)) + .getJson(new TypeToken>() { + }.getType()); + return response.getHits().stream().map(ProjectSearchResult::toMod); + } + + @Override + public Optional getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException { + String sha1 = Hex.encodeHex(DigestUtils.digest("SHA-1", file)); + + try { + ProjectVersion mod = HttpRequest.GET(PREFIX + "/v2/version_file/" + sha1, + pair("algorithm", "sha1")) + .getJson(ProjectVersion.class); + return mod.toVersion(); + } catch (ResponseCodeException e) { + if (e.getResponseCode() == 404) { + return Optional.empty(); + } else { + throw e; + } + } + } + + @Override + public RemoteMod getModById(String id) throws IOException { + id = StringUtils.removePrefix(id, "local-"); + Project project = HttpRequest.GET(PREFIX + "/v2/project/" + id).getJson(Project.class); + return project.toMod(); + } + + @Override + public RemoteMod.File getModFile(String modId, String fileId) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Stream getRemoteVersionsById(String id) throws IOException { + id = StringUtils.removePrefix(id, "local-"); + List versions = HttpRequest.GET(PREFIX + "/v2/project/" + id + "/version") + .getJson(new TypeToken>() { + }.getType()); + return versions.stream().map(ProjectVersion::toVersion).flatMap(Lang::toStream); + } + + public List getCategoriesImpl() throws IOException { + List categories = HttpRequest.GET(PREFIX + "/v2/tag/category").getJson(new TypeToken>() {}.getType()); + return categories.stream().filter(category -> category.getProjectType().equals(projectType)).collect(Collectors.toList()); + } + + @Override + public Stream getCategories() throws IOException { + return getCategoriesImpl().stream().map(Category::toCategory); + } + + public static class Category { + private final String icon; + + private final String name; + + @SerializedName("project_type") + private final String projectType; + + public Category() { + this("","",""); + } + + public Category(String icon, String name, String projectType) { + this.icon = icon; + this.name = name; + this.projectType = projectType; + } + + public String getIcon() { + return icon; + } + + public String getName() { + return name; + } + + public String getProjectType() { + return projectType; + } + + public RemoteModRepository.Category toCategory() { + return new RemoteModRepository.Category( + this, + name, + Collections.emptyList()); + } + } + + public static class Project implements RemoteMod.IMod { + private final String slug; + + private final String title; + + private final String description; + + private final List categories; + + /** + * A long body describing project in detail. + */ + private final String body; + + @SerializedName("project_type") + private final String projectType; + + private final int downloads; + + @SerializedName("icon_url") + private final String iconUrl; + + private final String id; + + private final String team; + + private final Date published; + + private final Date updated; + + private final List versions; + + public Project(String slug, String title, String description, List categories, String body, String projectType, int downloads, String iconUrl, String id, String team, Date published, Date updated, List versions) { + this.slug = slug; + this.title = title; + this.description = description; + this.categories = categories; + this.body = body; + this.projectType = projectType; + this.downloads = downloads; + this.iconUrl = iconUrl; + this.id = id; + this.team = team; + this.published = published; + this.updated = updated; + this.versions = versions; + } + + public String getSlug() { + return slug; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public List getCategories() { + return categories; + } + + public String getBody() { + return body; + } + + public String getProjectType() { + return projectType; + } + + public int getDownloads() { + return downloads; + } + + public String getIconUrl() { + return iconUrl; + } + + public String getId() { + return id; + } + + public String getTeam() { + return team; + } + + public Date getPublished() { + return published; + } + + public Date getUpdated() { + return updated; + } + + public List getVersions() { + return versions; + } + + @Override + public List loadDependencies(RemoteModRepository modRepository) throws IOException { + Set dependencies = modRepository.getRemoteVersionsById(getId()) + .flatMap(version -> version.getDependencies().stream()) + .collect(Collectors.toSet()); + List mods = new ArrayList<>(); + for (String dependencyId : dependencies) { + if (StringUtils.isNotBlank(dependencyId)) { + mods.add(modRepository.getModById(dependencyId)); + } + } + return mods; + } + + @Override + public Stream loadVersions(RemoteModRepository modRepository) throws IOException { + return modRepository.getRemoteVersionsById(getId()); + } + + public RemoteMod toMod() { + return new RemoteMod( + slug, + "", + title, + description, + categories, + null, + iconUrl, + (RemoteMod.IMod) this + ); + } + } + + public static class Dependency { + @SerializedName("version_id") + private final String versionId; + + @SerializedName("project_id") + private final String projectId; + + @SerializedName("dependency_type") + private final String dependencyType; + + public Dependency(String versionId, String projectId, String dependencyType) { + this.versionId = versionId; + this.projectId = projectId; + this.dependencyType = dependencyType; + } + + public String getVersionId() { + return versionId; + } + + public String getProjectId() { + return projectId; + } + + public String getDependencyType() { + return dependencyType; + } + } + + public static class ProjectVersion implements RemoteMod.IVersion { + private final String name; + + @SerializedName("version_number") + private final String versionNumber; + + private final String changelog; + + private final List dependencies; + + @SerializedName("game_versions") + private final List gameVersions; + + @SerializedName("version_type") + private final String versionType; + + private final List loaders; + + private final boolean featured; + + private final String id; + + @SerializedName("project_id") + private final String projectId; + + @SerializedName("author_id") + private final String authorId; + + @SerializedName("date_published") + private final Date datePublished; + + private final int downloads; + + @SerializedName("changelog_url") + private final String changelogUrl; + + private final List files; + + public ProjectVersion(String name, String versionNumber, String changelog, List dependencies, List gameVersions, String versionType, List loaders, boolean featured, String id, String projectId, String authorId, Date datePublished, int downloads, String changelogUrl, List files) { + this.name = name; + this.versionNumber = versionNumber; + this.changelog = changelog; + this.dependencies = dependencies; + this.gameVersions = gameVersions; + this.versionType = versionType; + this.loaders = loaders; + this.featured = featured; + this.id = id; + this.projectId = projectId; + this.authorId = authorId; + this.datePublished = datePublished; + this.downloads = downloads; + this.changelogUrl = changelogUrl; + this.files = files; + } + + public String getName() { + return name; + } + + public String getVersionNumber() { + return versionNumber; + } + + public String getChangelog() { + return changelog; + } + + public List getDependencies() { + return dependencies; + } + + public List getGameVersions() { + return gameVersions; + } + + public String getVersionType() { + return versionType; + } + + public List getLoaders() { + return loaders; + } + + public boolean isFeatured() { + return featured; + } + + public String getId() { + return id; + } + + public String getProjectId() { + return projectId; + } + + public String getAuthorId() { + return authorId; + } + + public Date getDatePublished() { + return datePublished; + } + + public int getDownloads() { + return downloads; + } + + public String getChangelogUrl() { + return changelogUrl; + } + + public List getFiles() { + return files; + } + + @Override + public RemoteMod.Type getType() { + return RemoteMod.Type.MODRINTH; + } + + public Optional toVersion() { + RemoteMod.VersionType type; + if ("release".equals(versionType)) { + type = RemoteMod.VersionType.Release; + } else if ("beta".equals(versionType)) { + type = RemoteMod.VersionType.Beta; + } else if ("alpha".equals(versionType)) { + type = RemoteMod.VersionType.Alpha; + } else { + type = RemoteMod.VersionType.Release; + } + + if (files.size() == 0) { + return Optional.empty(); + } + + return Optional.of(new RemoteMod.Version( + this, + projectId, + name, + versionNumber, + changelog, + datePublished, + type, + files.get(0).toFile(), + dependencies.stream().map(Dependency::getProjectId).filter(Objects::nonNull).collect(Collectors.toList()), + gameVersions, + loaders.stream().flatMap(loader -> { + if ("fabric".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FABRIC); + else if ("forge".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FORGE); + else return Stream.empty(); + }).collect(Collectors.toList()) + )); + } + } + + public static class ProjectVersionFile { + private final Map hashes; + private final String url; + private final String filename; + private final boolean primary; + private final int size; + + public ProjectVersionFile(Map hashes, String url, String filename, boolean primary, int size) { + this.hashes = hashes; + this.url = url; + this.filename = filename; + this.primary = primary; + this.size = size; + } + + public Map getHashes() { + return hashes; + } + + public String getUrl() { + return url; + } + + public String getFilename() { + return filename; + } + + public boolean isPrimary() { + return primary; + } + + public int getSize() { + return size; + } + + public RemoteMod.File toFile() { + return new RemoteMod.File(hashes, url, filename); + } + } + + public static class ProjectSearchResult implements RemoteMod.IMod { + private final String slug; + + private final String title; + + private final String description; + + private final List categories; + + @SerializedName("project_type") + private final String projectType; + + private final int downloads; + + @SerializedName("icon_url") + private final String iconUrl; + + @SerializedName("project_id") + private final String projectId; + + private final String author; + + private final List versions; + + @SerializedName("date_created") + private final Date dateCreated; + + @SerializedName("date_modified") + private final Date dateModified; + + @SerializedName("latest_version") + private final String latestVersion; + + public ProjectSearchResult(String slug, String title, String description, List categories, String projectType, int downloads, String iconUrl, String projectId, String author, List versions, Date dateCreated, Date dateModified, String latestVersion) { + this.slug = slug; + this.title = title; + this.description = description; + this.categories = categories; + this.projectType = projectType; + this.downloads = downloads; + this.iconUrl = iconUrl; + this.projectId = projectId; + this.author = author; + this.versions = versions; + this.dateCreated = dateCreated; + this.dateModified = dateModified; + this.latestVersion = latestVersion; + } + + public String getSlug() { + return slug; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public List getCategories() { + return categories; + } + + public String getProjectType() { + return projectType; + } + + public int getDownloads() { + return downloads; + } + + public String getIconUrl() { + return iconUrl; + } + + public String getProjectId() { + return projectId; + } + + public String getAuthor() { + return author; + } + + public List getVersions() { + return versions; + } + + public Date getDateCreated() { + return dateCreated; + } + + public Date getDateModified() { + return dateModified; + } + + public String getLatestVersion() { + return latestVersion; + } + + @Override + public List loadDependencies(RemoteModRepository modRepository) throws IOException { + Set dependencies = modRepository.getRemoteVersionsById(getProjectId()) + .flatMap(version -> version.getDependencies().stream()) + .collect(Collectors.toSet()); + List mods = new ArrayList<>(); + for (String dependencyId : dependencies) { + if (StringUtils.isNotBlank(dependencyId)) { + mods.add(modRepository.getModById(dependencyId)); + } + } + return mods; + } + + @Override + public Stream loadVersions(RemoteModRepository modRepository) throws IOException { + return modRepository.getRemoteVersionsById(getProjectId()); + } + + public RemoteMod toMod() { + return new RemoteMod( + slug, + author, + title, + description, + categories, + String.format("https://modrinth.com/%s/%s", projectType, projectId), + iconUrl, + this + ); + } + } + + public static class Response { + private final int offset; + + private final int limit; + + @SerializedName("total_hits") + private final int totalHits; + + private final List hits; + + public Response() { + this(0, 0, Collections.emptyList()); + } + + public Response(int offset, int limit, List hits) { + this.offset = offset; + this.limit = limit; + this.totalHits = hits.size(); + this.hits = hits; + } + + public int getOffset() { + return offset; + } + + public int getLimit() { + return limit; + } + + public int getTotalHits() { + return totalHits; + } + + public List getHits() { + return hits; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCInstanceConfiguration.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCInstanceConfiguration.java new file mode 100644 index 00000000..3d62c27a --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCInstanceConfiguration.java @@ -0,0 +1,312 @@ +package com.tungsten.fclcore.mod.multimc; + +import com.tungsten.fclcore.mod.ModpackManifest; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.util.Lang; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.Properties; + +public final class MultiMCInstanceConfiguration implements ModpackManifest { + + private final String instanceType; // InstanceType + private final String name; // name + private final String gameVersion; // IntendedVersion + private final Integer permGen; // PermGen + private final String wrapperCommand; // WrapperCommand + private final String preLaunchCommand; // PreLaunchCommand + private final String postExitCommand; // PostExitCommand + private final String notes; // notes + private final String javaPath; // JavaPath + private final String jvmArgs; // JvmArgs + private final boolean fullscreen; // LaunchMaximized + private final Integer width; // MinecraftWinWidth + private final Integer height; // MinecraftWinHeight + private final Integer maxMemory; // MaxMemAlloc + private final Integer minMemory; // MinMemAlloc + private final boolean showConsole; // ShowConsole + private final boolean showConsoleOnError; // ShowConsoleOnError + private final boolean autoCloseConsole; // AutoCloseConsole + private final boolean overrideMemory; // OverrideMemory + private final boolean overrideJavaLocation; // OverrideJavaLocation + private final boolean overrideJavaArgs; // OverrideJavaArgs + private final boolean overrideConsole; // OverrideConsole + private final boolean overrideCommands; // OverrideCommands + private final boolean overrideWindow; // OverrideWindow + + private final MultiMCManifest mmcPack; + + MultiMCInstanceConfiguration(String defaultName, InputStream contentStream, MultiMCManifest mmcPack) throws IOException { + Properties p = new Properties(); + p.load(new InputStreamReader(contentStream, StandardCharsets.UTF_8)); + + this.mmcPack = mmcPack; + + instanceType = p.getProperty("InstanceType"); + autoCloseConsole = Boolean.parseBoolean(p.getProperty("AutoCloseConsole")); + gameVersion = mmcPack != null ? mmcPack.getComponents().stream().filter(e -> "net.minecraft".equals(e.getUid())).findAny() + .orElseThrow(() -> new IOException("Malformed mmc-pack.json")).getVersion() : p.getProperty("IntendedVersion"); + javaPath = p.getProperty("JavaPath"); + jvmArgs = p.getProperty("JvmArgs"); + fullscreen = Boolean.parseBoolean(p.getProperty("LaunchMaximized")); + maxMemory = Lang.toIntOrNull(p.getProperty("MaxMemAlloc")); + minMemory = Lang.toIntOrNull(p.getProperty("MinMemAlloc")); + height = Lang.toIntOrNull(p.getProperty("MinecraftWinHeight")); + width = Lang.toIntOrNull(p.getProperty("MinecraftWinWidth")); + overrideCommands = Boolean.parseBoolean(p.getProperty("OverrideCommands")); + overrideConsole = Boolean.parseBoolean(p.getProperty("OverrideConsole")); + overrideJavaArgs = Boolean.parseBoolean(p.getProperty("OverrideJavaArgs")); + overrideJavaLocation = Boolean.parseBoolean(p.getProperty("OverrideJavaLocation")); + overrideMemory = Boolean.parseBoolean(p.getProperty("OverrideMemory")); + overrideWindow = Boolean.parseBoolean(p.getProperty("OverrideWindow")); + permGen = Lang.toIntOrNull(p.getProperty("PermGen")); + postExitCommand = p.getProperty("PostExitCommand"); + preLaunchCommand = p.getProperty("PreLaunchCommand"); + showConsole = Boolean.parseBoolean(p.getProperty("ShowConsole")); + showConsoleOnError = Boolean.parseBoolean(p.getProperty("ShowConsoleOnError")); + wrapperCommand = p.getProperty("WrapperCommand"); + name = defaultName; + notes = Optional.ofNullable(p.getProperty("notes")).orElse(""); + } + + public MultiMCInstanceConfiguration(String instanceType, String name, String gameVersion, Integer permGen, String wrapperCommand, String preLaunchCommand, String postExitCommand, String notes, String javaPath, String jvmArgs, boolean fullscreen, Integer width, Integer height, Integer maxMemory, Integer minMemory, boolean showConsole, boolean showConsoleOnError, boolean autoCloseConsole, boolean overrideMemory, boolean overrideJavaLocation, boolean overrideJavaArgs, boolean overrideConsole, boolean overrideCommands, boolean overrideWindow) { + this.instanceType = instanceType; + this.name = name; + this.gameVersion = gameVersion; + this.permGen = permGen; + this.wrapperCommand = wrapperCommand; + this.preLaunchCommand = preLaunchCommand; + this.postExitCommand = postExitCommand; + this.notes = notes; + this.javaPath = javaPath; + this.jvmArgs = jvmArgs; + this.fullscreen = fullscreen; + this.width = width; + this.height = height; + this.maxMemory = maxMemory; + this.minMemory = minMemory; + this.showConsole = showConsole; + this.showConsoleOnError = showConsoleOnError; + this.autoCloseConsole = autoCloseConsole; + this.overrideMemory = overrideMemory; + this.overrideJavaLocation = overrideJavaLocation; + this.overrideJavaArgs = overrideJavaArgs; + this.overrideConsole = overrideConsole; + this.overrideCommands = overrideCommands; + this.overrideWindow = overrideWindow; + this.mmcPack = null; + } + + public String getInstanceType() { + return instanceType; + } + + /** + * The instance's name. + */ + public String getName() { + return name; + } + + /** + * The game version of the instance. + */ + public String getGameVersion() { + return gameVersion; + } + + /** + * The permanent generation size of JVM. + * Nullable. + */ + public Integer getPermGen() { + return permGen; + } + + /** + * The command to launch JVM. + */ + public String getWrapperCommand() { + return wrapperCommand; + } + + /** + * The command that will be executed before game launches. + */ + public String getPreLaunchCommand() { + return preLaunchCommand; + } + + /** + * The command that will be executed after game exits. + */ + public String getPostExitCommand() { + return postExitCommand; + } + + /** + * The description of the instance. + */ + public String getNotes() { + return notes; + } + + /** + * JVM installation location. + */ + public String getJavaPath() { + return javaPath; + } + + /** + * The JVM arguments + */ + public String getJvmArgs() { + return jvmArgs; + } + + /** + * True if Minecraft will start in fullscreen mode. + */ + public boolean isFullscreen() { + return fullscreen; + } + + /** + * The initial width of the game window. + * Nullable. + */ + public Integer getWidth() { + return width; + } + + /** + * The initial height of the game window. + * Nullable. + */ + public Integer getHeight() { + return height; + } + + /** + * The maximum memory that JVM can allocate. + * Nullable. + */ + public Integer getMaxMemory() { + return maxMemory; + } + + /** + * The minimum memory that JVM can allocate. + * Nullable. + */ + public Integer getMinMemory() { + return minMemory; + } + + /** + * True if show the console window when game launches. + */ + public boolean isShowConsole() { + return showConsole; + } + + /** + * True if show the console window when game crashes. + */ + public boolean isShowConsoleOnError() { + return showConsoleOnError; + } + + /** + * True if closes the console window when game stops. + */ + public boolean isAutoCloseConsole() { + return autoCloseConsole; + } + + /** + * True if {@link #getMaxMemory}, {@link #getMinMemory}, {@link #getPermGen} will come info force. + */ + public boolean isOverrideMemory() { + return overrideMemory; + } + + /** + * True if {@link #getJavaPath} will come info force. + */ + public boolean isOverrideJavaLocation() { + return overrideJavaLocation; + } + + /** + * True if {@link #getJvmArgs} will come info force. + */ + public boolean isOverrideJavaArgs() { + return overrideJavaArgs; + } + + /** + * True if {@link #isShowConsole}, {@link #isShowConsoleOnError}, {@link #isAutoCloseConsole} will come into force. + */ + public boolean isOverrideConsole() { + return overrideConsole; + } + + /** + * True if {@link #getPreLaunchCommand}, {@link #getPostExitCommand}, {@link #getWrapperCommand} will come into force. + */ + public boolean isOverrideCommands() { + return overrideCommands; + } + + /** + * True if {@link #getHeight}, {@link #getWidth}, {@link #isFullscreen} will come into force. + */ + public boolean isOverrideWindow() { + return overrideWindow; + } + + public Properties toProperties() { + Properties p = new Properties(); + if (instanceType != null) p.setProperty("InstanceType", instanceType); + p.setProperty("AutoCloseConsole", Boolean.toString(autoCloseConsole)); + if (gameVersion != null) p.setProperty("IntendedVersion", gameVersion); + if (javaPath != null) p.setProperty("JavaPath", javaPath); + if (jvmArgs != null) p.setProperty("JvmArgs", jvmArgs); + p.setProperty("LaunchMaximized", Boolean.toString(fullscreen)); + if (maxMemory != null) p.setProperty("MaxMemAlloc", Integer.toString(maxMemory)); + if (minMemory != null) p.setProperty("MinMemAlloc", Integer.toString(minMemory)); + if (height != null) p.setProperty("MinecraftWinHeight", Integer.toString(height)); + if (width != null) p.setProperty("MinecraftWinWidth", Integer.toString(width)); + p.setProperty("OverrideCommands", Boolean.toString(overrideCommands)); + p.setProperty("OverrideConsole", Boolean.toString(overrideConsole)); + p.setProperty("OverrideJavaArgs", Boolean.toString(overrideJavaArgs)); + p.setProperty("OverrideJavaLocation", Boolean.toString(overrideJavaLocation)); + p.setProperty("OverrideMemory", Boolean.toString(overrideMemory)); + p.setProperty("OverrideWindow", Boolean.toString(overrideWindow)); + if (permGen != null) p.setProperty("PermGen", Integer.toString(permGen)); + if (postExitCommand != null) p.setProperty("PostExitCommand", postExitCommand); + if (preLaunchCommand != null) p.setProperty("PreLaunchCommand", preLaunchCommand); + p.setProperty("ShowConsole", Boolean.toString(showConsole)); + p.setProperty("ShowConsoleOnError", Boolean.toString(showConsoleOnError)); + if (wrapperCommand != null) p.setProperty("WrapperCommand", wrapperCommand); + if (name != null) p.setProperty("name", name); + if (notes != null) p.setProperty("notes", notes); + return p; + } + + public MultiMCManifest getMmcPack() { + return mmcPack; + } + + @Override + public ModpackProvider getProvider() { + return MultiMCModpackProvider.INSTANCE; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCInstancePatch.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCInstancePatch.java new file mode 100644 index 00000000..8f6eadf3 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCInstancePatch.java @@ -0,0 +1,73 @@ +package com.tungsten.fclcore.mod.multimc; + +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.game.Library; +import com.tungsten.fclcore.util.Lang; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class MultiMCInstancePatch { + + private final String name; + private final String version; + + @SerializedName("mcVersion") + private final String gameVersion; + private final String mainClass; + private final String fileId; + + @SerializedName("+tweakers") + private final List tweakers; + + @SerializedName("+libraries") + private final List _libraries; + + @SerializedName("libraries") + private final List libraries; + + public MultiMCInstancePatch() { + this("", "", "", "", "", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + } + + public MultiMCInstancePatch(String name, String version, String gameVersion, String mainClass, String fileId, List tweakers, List _libraries, List libraries) { + this.name = name; + this.version = version; + this.gameVersion = gameVersion; + this.mainClass = mainClass; + this.fileId = fileId; + this.tweakers = new ArrayList<>(tweakers); + this._libraries = new ArrayList<>(_libraries); + this.libraries = new ArrayList<>(libraries); + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getGameVersion() { + return gameVersion; + } + + public String getMainClass() { + return mainClass; + } + + public String getFileId() { + return fileId; + } + + public List getTweakers() { + return Collections.unmodifiableList(tweakers); + } + + public List getLibraries() { + return Lang.merge(_libraries, libraries); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCManifest.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCManifest.java new file mode 100644 index 00000000..07f44561 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCManifest.java @@ -0,0 +1,143 @@ +package com.tungsten.fclcore.mod.multimc; + +import com.google.gson.annotations.SerializedName; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.IOUtils; + +import java.io.IOException; +import java.util.List; + +public final class MultiMCManifest { + + @SerializedName("formatVersion") + private final int formatVersion; + + @SerializedName("components") + private final List components; + + public MultiMCManifest(int formatVersion, List components) { + this.formatVersion = formatVersion; + this.components = components; + } + + public int getFormatVersion() { + return formatVersion; + } + + public List getComponents() { + return components; + } + + /** + * Read MultiMC modpack manifest from zip file + * @param zipFile the zip file + * @return the MultiMC modpack manifest. + * @throws IOException if zip file is malformed + * @throws com.google.gson.JsonParseException if manifest is malformed. + */ + public static MultiMCManifest readMultiMCModpackManifest(ZipFile zipFile, String rootEntryName) throws IOException { + ZipArchiveEntry mmcPack = zipFile.getEntry(rootEntryName + "mmc-pack.json"); + if (mmcPack == null) + return null; + String json = IOUtils.readFullyAsString(zipFile.getInputStream(mmcPack)); + MultiMCManifest manifest = JsonUtils.fromNonNullJson(json, MultiMCManifest.class); + if (manifest.getComponents() == null) + throw new IOException("mmc-pack.json malformed."); + + return manifest; + } + + public static final class MultiMCManifestCachedRequires { + @SerializedName("equals") + private final String equalsVersion; + + @SerializedName("uid") + private final String uid; + + @SerializedName("suggests") + private final String suggests; + + public MultiMCManifestCachedRequires(String equalsVersion, String uid, String suggests) { + this.equalsVersion = equalsVersion; + this.uid = uid; + this.suggests = suggests; + } + + public String getEqualsVersion() { + return equalsVersion; + } + + public String getUid() { + return uid; + } + + public String getSuggests() { + return suggests; + } + } + + public static final class MultiMCManifestComponent { + @SerializedName("cachedName") + private final String cachedName; + + @SerializedName("cachedRequires") + private final List cachedRequires; + + @SerializedName("cachedVersion") + private final String cachedVersion; + + @SerializedName("important") + private final boolean important; + + @SerializedName("dependencyOnly") + private final boolean dependencyOnly; + + @SerializedName("uid") + private final String uid; + + @SerializedName("version") + private final String version; + + public MultiMCManifestComponent(boolean important, boolean dependencyOnly, String uid, String version) { + this(null, null, null, important, dependencyOnly, uid, version); + } + + public MultiMCManifestComponent(String cachedName, List cachedRequires, String cachedVersion, boolean important, boolean dependencyOnly, String uid, String version) { + this.cachedName = cachedName; + this.cachedRequires = cachedRequires; + this.cachedVersion = cachedVersion; + this.important = important; + this.dependencyOnly = dependencyOnly; + this.uid = uid; + this.version = version; + } + + public String getCachedName() { + return cachedName; + } + + public List getCachedRequires() { + return cachedRequires; + } + + public String getCachedVersion() { + return cachedVersion; + } + + public boolean isImportant() { + return important; + } + + public boolean isDependencyOnly() { + return dependencyOnly; + } + + public String getUid() { + return uid; + } + + public String getVersion() { + return version; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackExportTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackExportTask.java new file mode 100644 index 00000000..0186918f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackExportTask.java @@ -0,0 +1,79 @@ +package com.tungsten.fclcore.mod.multimc; + +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.FABRIC; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.FORGE; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.LITELOADER; + +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModAdviser; +import com.tungsten.fclcore.mod.ModpackExportInfo; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * Export the game to a mod pack file. + */ +public class MultiMCModpackExportTask extends Task { + private final DefaultGameRepository repository; + private final String versionId; + private final List whitelist; + private final MultiMCInstanceConfiguration configuration; + private final File output; + + /** + * @param output mod pack file. + * @param versionId to locate version.json + */ + public MultiMCModpackExportTask(DefaultGameRepository repository, String versionId, List whitelist, MultiMCInstanceConfiguration configuration, File output) { + this.repository = repository; + this.versionId = versionId; + this.whitelist = whitelist; + this.configuration = configuration; + this.output = output; + + onDone().register(event -> { + if (event.isFailed()) output.delete(); + }); + } + + @Override + public void execute() throws Exception { + ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); + blackList.add(versionId + ".jar"); + blackList.add(versionId + ".json"); + Logging.LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); + try (Zipper zip = new Zipper(output.toPath())) { + zip.putDirectory(repository.getRunDirectory(versionId).toPath(), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist)); + + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId)); + String gameVersion = repository.getGameVersion(versionId) + .orElseThrow(() -> new IOException("Cannot parse the version of " + versionId)); + List components = new ArrayList<>(); + components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, "net.minecraft", gameVersion)); + analyzer.getVersion(FORGE).ifPresent(forgeVersion -> + components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.minecraftforge", forgeVersion))); + analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion -> + components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "com.mumfrey.liteloader", liteLoaderVersion))); + analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> + components.add(new MultiMCManifest.MultiMCManifestComponent(false, false, "net.fabricmc.fabric-loader", fabricVersion))); + MultiMCManifest mmcPack = new MultiMCManifest(1, components); + zip.putTextFile(JsonUtils.GSON.toJson(mmcPack), "mmc-pack.json"); + + StringWriter writer = new StringWriter(); + configuration.toProperties().store(writer, "Auto generated by Hello Minecraft! Launcher"); + zip.putTextFile(writer.toString(), "instance.cfg"); + + zip.putTextFile("", ".packignore"); + } + } + + public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options().requireMinMemory(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackInstallTask.java new file mode 100644 index 00000000..3f1f72d4 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackInstallTask.java @@ -0,0 +1,171 @@ +package com.tungsten.fclcore.mod.multimc; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.game.Version; +import com.tungsten.fclcore.mod.MinecraftInstanceTask; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackInstallTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public final class MultiMCModpackInstallTask extends Task { + + private final File zipFile; + private final Modpack modpack; + private final MultiMCInstanceConfiguration manifest; + private final String name; + private final DefaultGameRepository repository; + private final List> dependencies = new ArrayList<>(1); + private final List> dependents = new ArrayList<>(4); + + public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) { + this.zipFile = zipFile; + this.modpack = modpack; + this.manifest = manifest; + this.name = name; + this.repository = dependencyManager.getGameRepository(); + + File json = repository.getModpackConfiguration(name); + if (repository.hasVersion(name) && !json.exists()) + throw new IllegalArgumentException("Version " + name + " already exists."); + + GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion()); + + if (manifest.getMmcPack() != null) { + Optional forge = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.minecraftforge")).findAny(); + forge.ifPresent(c -> { + if (c.getVersion() != null) + builder.version("forge", c.getVersion()); + }); + + Optional liteLoader = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("com.mumfrey.liteloader")).findAny(); + liteLoader.ifPresent(c -> { + if (c.getVersion() != null) + builder.version("liteloader", c.getVersion()); + }); + + Optional fabric = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.fabricmc.fabric-loader")).findAny(); + fabric.ifPresent(c -> { + if (c.getVersion() != null) + builder.version("fabric", c.getVersion()); + }); + + Optional quilt = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.quiltmc.quilt-loader")).findAny(); + quilt.ifPresent(c -> { + if (c.getVersion() != null) + builder.version("quilt", c.getVersion()); + }); + } + + dependents.add(builder.buildAsync()); + onDone().register(event -> { + if (event.isFailed()) + repository.removeVersionFromDisk(name); + }); + } + + @Override + public List> getDependencies() { + return dependencies; + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() throws Exception { + File run = repository.getRunDirectory(name); + File json = repository.getModpackConfiguration(name); + + ModpackConfiguration config = null; + try { + if (json.exists()) { + config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + + if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType())) + throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version."); + } + } catch (JsonParseException | IOException ignore) { + } + + String subDirectory; + + try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setEncoding(modpack.getEncoding()).build()) { + if (Files.exists(fs.getPath("/" + manifest.getName() + "/.minecraft"))) { + subDirectory = "/" + manifest.getName() + "/.minecraft"; + } else if (Files.exists(fs.getPath("/" + manifest.getName() + "/minecraft"))) { + subDirectory = "/" + manifest.getName() + "/minecraft"; + } else { + subDirectory = "/" + manifest.getName() + "/minecraft"; + } + } + + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(subDirectory), any -> true, config).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(subDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + } + + @Override + public List> getDependents() { + return dependents; + } + + @Override + public void execute() throws Exception { + Version version = repository.readVersionJson(name); + + try (FileSystem fs = CompressingUtils.readonly(zipFile.toPath()).setAutoDetectEncoding(true).build()) { + Path root = MultiMCModpackProvider.getRootPath(fs.getPath("/")); + Path patches = root.resolve("patches"); + + if (Files.exists(patches)) { + try (DirectoryStream directoryStream = Files.newDirectoryStream(patches)) { + for (Path patchJson : directoryStream) { + if (patchJson.toString().endsWith(".json")) { + // If json is malformed, we should stop installing this modpack instead of skipping it. + MultiMCInstancePatch multiMCPatch = JsonUtils.GSON.fromJson(FileUtils.readText(patchJson), MultiMCInstancePatch.class); + + List arguments = new ArrayList<>(); + for (String arg : multiMCPatch.getTweakers()) { + arguments.add("--tweakClass"); + arguments.add(arg); + } + + Version patch = new Version(multiMCPatch.getName(), multiMCPatch.getVersion(), 1, new Arguments().addGameArguments(arguments), multiMCPatch.getMainClass(), multiMCPatch.getLibraries()); + version = version.addPatch(patch); + } + } + } + } + + Path libraries = root.resolve("libraries"); + if (Files.exists(libraries)) + FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).toPath().resolve("libraries")); + + Path jarmods = root.resolve("jarmods"); + if (Files.exists(jarmods)) + FileUtils.copyDirectory(jarmods, repository.getVersionRoot(name).toPath().resolve("jarmods")); + } + + dependencies.add(repository.saveAsync(version)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackProvider.java new file mode 100644 index 00000000..8ac556f9 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/multimc/MultiMCModpackProvider.java @@ -0,0 +1,96 @@ +package com.tungsten.fclcore.mod.multimc; + +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.mod.MismatchedModpackTypeException; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.mod.ModpackUpdateTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.stream.Stream; + +public final class MultiMCModpackProvider implements ModpackProvider { + public static final MultiMCModpackProvider INSTANCE = new MultiMCModpackProvider(); + + @Override + public String getName() { + return "MultiMC"; + } + + @Override + public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { + return null; + } + + @Override + public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, File zipFile, Modpack modpack) throws MismatchedModpackTypeException { + if (!(modpack.getManifest() instanceof MultiMCInstanceConfiguration)) + throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); + + return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new MultiMCModpackInstallTask(dependencyManager, zipFile, modpack, (MultiMCInstanceConfiguration) modpack.getManifest(), name)); + } + + private static boolean testPath(Path root) { + return Files.exists(root.resolve("instance.cfg")); + } + + public static Path getRootPath(Path root) throws IOException { + if (testPath(root)) return root; + try (Stream stream = Files.list(root)) { + Path candidate = stream.filter(Files::isDirectory).findAny() + .orElseThrow(() -> new IOException("Not a valid MultiMC modpack")); + if (testPath(candidate)) return candidate; + throw new IOException("Not a valid MultiMC modpack"); + } + } + + private static String getRootEntryName(ZipFile file) throws IOException { + final String instanceFileName = "instance.cfg"; + + if (file.getEntry(instanceFileName) != null) return ""; + + Enumeration entries = file.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry entry = entries.nextElement(); + String entryName = entry.getName(); + + int idx = entryName.indexOf('/'); + if (idx >= 0 + && entryName.length() == idx + instanceFileName.length() + 1 + && entryName.startsWith(instanceFileName, idx + 1)) + return entryName.substring(0, idx + 1); + } + + throw new IOException("Not a valid MultiMC modpack"); + } + + @Override + public Modpack readManifest(ZipFile modpackFile, Path modpackPath, Charset encoding) throws IOException { + String rootEntryName = getRootEntryName(modpackFile); + MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(modpackFile, rootEntryName); + + String name = rootEntryName.isEmpty() ? FileUtils.getNameWithoutExtension(modpackPath) : rootEntryName.substring(0, rootEntryName.length() - 1); + ZipArchiveEntry instanceEntry = modpackFile.getEntry(rootEntryName + "instance.cfg"); + + if (instanceEntry == null) + throw new IOException("`instance.cfg` not found, " + modpackFile + " is not a valid MultiMC modpack."); + try (InputStream instanceStream = modpackFile.getInputStream(instanceEntry)) { + MultiMCInstanceConfiguration cfg = new MultiMCInstanceConfiguration(name, instanceStream, manifest); + return new Modpack(cfg.getName(), "", "", cfg.getGameVersion(), cfg.getNotes(), encoding, cfg) { + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name) { + return new MultiMCModpackInstallTask(dependencyManager, zipFile, this, cfg, name); + } + }; + } + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackCompletionTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackCompletionTask.java new file mode 100644 index 00000000..23b4be52 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackCompletionTask.java @@ -0,0 +1,172 @@ +package com.tungsten.fclcore.mod.server; + +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.task.FileDownloadTask; +import com.tungsten.fclcore.task.GetTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.StringUtils; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.io.NetworkUtils; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public class ServerModpackCompletionTask extends Task { + + private final DefaultDependencyManager dependencyManager; + private final DefaultGameRepository repository; + private final String version; + private ModpackConfiguration manifest; + private GetTask dependent; + private ServerModpackManifest remoteManifest; + private final List> dependencies = new ArrayList<>(); + + public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) { + this(dependencyManager, version, null); + } + + public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, String version, ModpackConfiguration manifest) { + this.dependencyManager = dependencyManager; + this.repository = dependencyManager.getGameRepository(); + this.version = version; + + if (manifest == null) { + try { + File manifestFile = repository.getModpackConfiguration(version); + if (manifestFile.exists()) { + this.manifest = JsonUtils.GSON.fromJson(FileUtils.readText(manifestFile), new TypeToken>() { + }.getType()); + } + } catch (Exception e) { + Logging.LOG.log(Level.WARNING, "Unable to read Server modpack manifest.json", e); + } + } else { + this.manifest = manifest; + } + + setStage("hmcl.modpack.download"); + } + + @Override + public boolean doPreExecute() { + return true; + } + + @Override + public void preExecute() throws Exception { + if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return; + dependent = new GetTask(new URL(manifest.getManifest().getFileApi() + "/server-manifest.json")); + } + + @Override + public Collection> getDependencies() { + return dependencies; + } + + @Override + public Collection> getDependents() { + return dependent == null ? Collections.emptySet() : Collections.singleton(dependent); + } + + private Map toMap(Collection addons) { + return addons.stream().collect(Collectors.toMap(ServerModpackManifest.Addon::getId, ServerModpackManifest.Addon::getVersion)); + } + + @Override + public void execute() throws Exception { + if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return; + + try { + remoteManifest = JsonUtils.fromNonNullJson(dependent.getResult(), ServerModpackManifest.class); + } catch (JsonParseException e) { + throw new IOException(e); + } + + Map oldAddons = toMap(manifest.getManifest().getAddons()); + Map newAddons = toMap(remoteManifest.getAddons()); + if (!Objects.equals(oldAddons, newAddons)) { + GameBuilder builder = dependencyManager.gameBuilder().name(version); + for (ServerModpackManifest.Addon addon : remoteManifest.getAddons()) { + builder.version(addon.getId(), addon.getVersion()); + } + + dependencies.add(builder.buildAsync()); + } + + Path rootPath = repository.getVersionRoot(version).toPath(); + Map files = manifest.getManifest().getFiles().stream() + .collect(Collectors.toMap(ModpackConfiguration.FileInformation::getPath, + Function.identity())); + + Set remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath) + .collect(Collectors.toSet()); + + int total = 0; + // for files in new modpack + for (ModpackConfiguration.FileInformation file : remoteManifest.getFiles()) { + Path actualPath = rootPath.resolve(file.getPath()); + boolean download; + if (!files.containsKey(file.getPath())) { + // If old modpack does not have this entry, download it + download = true; + } else if (!Files.exists(actualPath)) { + // If both old and new modpacks have this entry, but the file is missing... + // Re-download it since network problem may cause file missing + download = true; + } else { + // If user modified this entry file, we will not replace this file since this modified file is that user expects. + String fileHash = encodeHex(digest("SHA-1", actualPath)); + String oldHash = files.get(file.getPath()).getHash(); + download = !Objects.equals(oldHash, file.getHash()) && Objects.equals(oldHash, fileHash); + } + + if (download) { + total++; + dependencies.add(new FileDownloadTask( + new URL(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(file.getPath())), + actualPath.toFile(), + new FileDownloadTask.IntegrityCheck("SHA-1", file.getHash())) + .withCounter("hmcl.modpack.download")); + } + } + + // If old modpack have this entry, and new modpack deleted it. Delete this file. + for (ModpackConfiguration.FileInformation file : manifest.getManifest().getFiles()) { + Path actualPath = rootPath.resolve(file.getPath()); + if (Files.exists(actualPath) && !remoteFiles.contains(file.getPath())) + Files.deleteIfExists(actualPath); + } + + getProperties().put("total", dependencies.size()); + notifyPropertiesChanged(); + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return; + File manifestFile = repository.getModpackConfiguration(version); + FileUtils.writeText(manifestFile, JsonUtils.GSON.toJson(new ModpackConfiguration<>(remoteManifest, this.manifest.getType(), this.manifest.getName(), this.manifest.getVersion(), remoteManifest.getFiles()))); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackExportTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackExportTask.java new file mode 100644 index 00000000..7f16ef5d --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackExportTask.java @@ -0,0 +1,87 @@ +package com.tungsten.fclcore.mod.server; + +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.FABRIC; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.FORGE; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.LITELOADER; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.MINECRAFT; +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.OPTIFINE; +import static com.tungsten.fclcore.util.DigestUtils.digest; +import static com.tungsten.fclcore.util.Hex.encodeHex; + +import com.tungsten.fclcore.download.LibraryAnalyzer; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModAdviser; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackExportInfo; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class ServerModpackExportTask extends Task { + private final DefaultGameRepository repository; + private final String versionId; + private final ModpackExportInfo exportInfo; + private final File modpackFile; + + public ServerModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo exportInfo, File modpackFile) { + this.repository = repository; + this.versionId = version; + this.exportInfo = exportInfo.validate(); + this.modpackFile = modpackFile; + + onDone().register(event -> { + if (event.isFailed()) modpackFile.delete(); + }); + } + + @Override + public void execute() throws Exception { + ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); + blackList.add(versionId + ".jar"); + blackList.add(versionId + ".json"); + Logging.LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); + try (Zipper zip = new Zipper(modpackFile.toPath())) { + Path runDirectory = repository.getRunDirectory(versionId).toPath(); + List files = new ArrayList<>(); + zip.putDirectory(runDirectory, "overrides", path -> { + if (Modpack.acceptFile(path, blackList, exportInfo.getWhitelist())) { + Path file = runDirectory.resolve(path); + if (Files.isRegularFile(file)) { + String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); + files.add(new ModpackConfiguration.FileInformation(relativePath, encodeHex(digest("SHA-1", file)))); + } + return true; + } else { + return false; + } + }); + + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId)); + String gameVersion = repository.getGameVersion(versionId) + .orElseThrow(() -> new IOException("Cannot parse the version of " + versionId)); + List addons = new ArrayList<>(); + addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion)); + analyzer.getVersion(FORGE).ifPresent(forgeVersion -> + addons.add(new ServerModpackManifest.Addon(FORGE.getPatchId(), forgeVersion))); + analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion -> + addons.add(new ServerModpackManifest.Addon(LITELOADER.getPatchId(), liteLoaderVersion))); + analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion -> + addons.add(new ServerModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion))); + analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> + addons.add(new ServerModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion))); + ServerModpackManifest manifest = new ServerModpackManifest(exportInfo.getName(), exportInfo.getAuthor(), exportInfo.getVersion(), exportInfo.getDescription(), StringUtils.removeSuffix(exportInfo.getFileApi(), "/"), files, addons); + zip.putTextFile(JsonUtils.GSON.toJson(manifest), "server-manifest.json"); + } + } + + public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options() + .requireFileApi(false); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackLocalInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackLocalInstallTask.java new file mode 100644 index 00000000..81b2a9cb --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackLocalInstallTask.java @@ -0,0 +1,83 @@ +package com.tungsten.fclcore.mod.server; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.MinecraftInstanceTask; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackInstallTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ServerModpackLocalInstallTask extends Task { + + private final File zipFile; + private final Modpack modpack; + private final ServerModpackManifest manifest; + private final String name; + private final DefaultGameRepository repository; + private final List> dependencies = new ArrayList<>(); + private final List> dependents = new ArrayList<>(4); + + public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, ServerModpackManifest manifest, String name) { + this.zipFile = zipFile; + this.modpack = modpack; + this.manifest = manifest; + this.name = name; + this.repository = dependencyManager.getGameRepository(); + File run = repository.getRunDirectory(name); + + File json = repository.getModpackConfiguration(name); + if (repository.hasVersion(name) && !json.exists()) + throw new IllegalArgumentException("Version " + name + " already exists."); + + GameBuilder builder = dependencyManager.gameBuilder().name(name); + for (ServerModpackManifest.Addon addon : manifest.getAddons()) { + builder.version(addon.getId(), addon.getVersion()); + } + + dependents.add(builder.buildAsync()); + onDone().register(event -> { + if (event.isFailed()) + repository.removeVersionFromDisk(name); + }); + + ModpackConfiguration config = null; + try { + if (json.exists()) { + config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + + if (!ServerModpackProvider.INSTANCE.getName().equals(config.getType())) + throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version."); + } + } catch (JsonParseException | IOException ignore) { + } + dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, ServerModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + } + + @Override + public List> getDependents() { + return dependents; + } + + @Override + public List> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws Exception { + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackManifest.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackManifest.java new file mode 100644 index 00000000..d3251219 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackManifest.java @@ -0,0 +1,118 @@ +package com.tungsten.fclcore.mod.server; + +import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.MINECRAFT; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.mod.ModpackManifest; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.TolerableValidationException; +import com.tungsten.fclcore.util.gson.Validation; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.List; + +public class ServerModpackManifest implements ModpackManifest, Validation { + private final String name; + private final String author; + private final String version; + private final String description; + private final String fileApi; + private final List files; + private final List addons; + + public ServerModpackManifest() { + this("", "", "", "", "", Collections.emptyList(), Collections.emptyList()); + } + + public ServerModpackManifest(String name, String author, String version, String description, String fileApi, List files, List addons) { + this.name = name; + this.author = author; + this.version = version; + this.description = description; + this.fileApi = fileApi; + this.files = files; + this.addons = addons; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + + public String getVersion() { + return version; + } + + public String getDescription() { + return description; + } + + public String getFileApi() { + return fileApi; + } + + public List getFiles() { + return files; + } + + public List getAddons() { + return addons; + } + + @Override + public ModpackProvider getProvider() { + return ServerModpackProvider.INSTANCE; + } + + @Override + public void validate() throws JsonParseException, TolerableValidationException { + if (fileApi == null) + throw new JsonParseException("ServerModpackManifest.fileApi cannot be blank"); + if (files == null) + throw new JsonParseException("ServerModpackManifest.files cannot be null"); + } + + public static final class Addon { + private final String id; + private final String version; + + public Addon() { + this("", ""); + } + + public Addon(String id, String version) { + this.id = id; + this.version = version; + } + + public String getId() { + return id; + } + + public String getVersion() { + return version; + } + } + + public Modpack toModpack(Charset encoding) throws IOException { + String gameVersion = addons.stream().filter(x -> MINECRAFT.getPatchId().equals(x.id)).findAny() + .orElseThrow(() -> new IOException("Cannot find game version")).getVersion(); + return new Modpack(name, author, version, gameVersion, description, encoding, this) { + @Override + public Task getInstallTask(DefaultDependencyManager dependencyManager, File zipFile, String name) { + return new ServerModpackLocalInstallTask(dependencyManager, zipFile, this, ServerModpackManifest.this, name); + } + }; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackProvider.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackProvider.java new file mode 100644 index 00000000..2c554e60 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackProvider.java @@ -0,0 +1,44 @@ +package com.tungsten.fclcore.mod.server; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.mod.MismatchedModpackTypeException; +import com.tungsten.fclcore.mod.Modpack; +import com.tungsten.fclcore.mod.ModpackProvider; +import com.tungsten.fclcore.mod.ModpackUpdateTask; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Path; + +public final class ServerModpackProvider implements ModpackProvider { + public static final ServerModpackProvider INSTANCE = new ServerModpackProvider(); + + @Override + public String getName() { + return "Server"; + } + + @Override + public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { + return new ServerModpackCompletionTask(dependencyManager, version); + } + + @Override + public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, File zipFile, Modpack modpack) throws MismatchedModpackTypeException { + if (!(modpack.getManifest() instanceof ServerModpackManifest)) + throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); + + return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new ServerModpackLocalInstallTask(dependencyManager, zipFile, modpack, (ServerModpackManifest) modpack.getManifest(), name)); + } + + @Override + public Modpack readManifest(ZipFile zip, Path file, Charset encoding) throws IOException, JsonParseException { + String json = CompressingUtils.readTextZipEntry(zip, "server-manifest.json"); + ServerModpackManifest manifest = JsonUtils.fromNonNullJson(json, ServerModpackManifest.class); + return manifest.toModpack(encoding); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackRemoteInstallTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackRemoteInstallTask.java new file mode 100644 index 00000000..132d5da8 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/mod/server/ServerModpackRemoteInstallTask.java @@ -0,0 +1,78 @@ +package com.tungsten.fclcore.mod.server; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.download.DefaultDependencyManager; +import com.tungsten.fclcore.download.GameBuilder; +import com.tungsten.fclcore.game.DefaultGameRepository; +import com.tungsten.fclcore.mod.ModpackConfiguration; +import com.tungsten.fclcore.task.Task; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ServerModpackRemoteInstallTask extends Task { + + private final String name; + private final DefaultDependencyManager dependency; + private final DefaultGameRepository repository; + private final List> dependencies = new ArrayList<>(1); + private final List> dependents = new ArrayList<>(1); + private final ServerModpackManifest manifest; + + public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, ServerModpackManifest manifest, String name) { + this.name = name; + this.dependency = dependencyManager; + this.repository = dependencyManager.getGameRepository(); + this.manifest = manifest; + + File json = repository.getModpackConfiguration(name); + if (repository.hasVersion(name) && !json.exists()) + throw new IllegalArgumentException("Version " + name + " already exists."); + + GameBuilder builder = dependencyManager.gameBuilder().name(name); + for (ServerModpackManifest.Addon addon : manifest.getAddons()) { + builder.version(addon.getId(), addon.getVersion()); + } + + dependents.add(builder.buildAsync()); + onDone().register(event -> { + if (event.isFailed()) + repository.removeVersionFromDisk(name); + }); + + ModpackConfiguration config = null; + try { + if (json.exists()) { + config = JsonUtils.GSON.fromJson(FileUtils.readText(json), new TypeToken>() { + }.getType()); + + if (!MODPACK_TYPE.equals(config.getType())) + throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version."); + } + } catch (JsonParseException | IOException ignore) { + } + } + + @Override + public List> getDependents() { + return dependents; + } + + @Override + public List> getDependencies() { + return dependencies; + } + + @Override + public void execute() throws Exception { + dependencies.add(new ServerModpackCompletionTask(dependency, name, new ModpackConfiguration<>(manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), Collections.emptyList()))); + } + + public static final String MODPACK_TYPE = "Server"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/AsyncTaskExecutor.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/AsyncTaskExecutor.java new file mode 100644 index 00000000..332dbbb7 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/AsyncTaskExecutor.java @@ -0,0 +1,319 @@ +package com.tungsten.fclcore.task; + +import static com.tungsten.fclcore.task.CompletableFutureTask.resolveException; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.util.Logging; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.*; +import java.util.logging.Level; + +public final class AsyncTaskExecutor extends TaskExecutor { + + private CompletableFuture future; + + public AsyncTaskExecutor(Task task) { + super(task); + } + + @Override + public TaskExecutor start() { + taskListeners.forEach(TaskListener::onStart); + future = executeTasks(null, Collections.singleton(firstTask)) + .thenApplyAsync(exception -> { + boolean success = exception == null; + + if (!success) { + // We log exception stacktrace because some of exceptions occurred because of bugs. + Logging.LOG.log(Level.WARNING, "An exception occurred in task execution", exception); + + Throwable resolvedException = resolveException(exception); + if (resolvedException instanceof RuntimeException && + !(resolvedException instanceof CancellationException) && + !(resolvedException instanceof JsonParseException) && + !(resolvedException instanceof RejectedExecutionException)) { + // Track uncaught RuntimeException which are thrown mostly by our mistake + if (uncaughtExceptionHandler != null) + uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), resolvedException); + } + } + + taskListeners.forEach(it -> it.onStop(success, this)); + return success; + }) + .exceptionally(e -> { + Lang.handleUncaughtException(resolveException(e)); + return false; + }); + return this; + } + + @Override + public boolean test() { + start(); + try { + return future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException ignore) { + // We have dealt with ExecutionException in exception handling and uncaught exception handler. + } catch (CancellationException e) { + Logging.LOG.log(Level.INFO, "Task " + firstTask + " has been cancelled.", e); + } + return false; + } + + @Override + public synchronized void cancel() { + if (future == null) { + throw new IllegalStateException("Cannot cancel a not started TaskExecutor"); + } + + cancelled.set(true); + } + + private CompletableFuture executeTasksExceptionally(Task parentTask, Collection> tasks) { + if (tasks == null || tasks.isEmpty()) + return CompletableFuture.completedFuture(null); + + return CompletableFuture.completedFuture(null) + .thenComposeAsync(unused -> { + totTask.addAndGet(tasks.size()); + + if (isCancelled()) { + for (Task task : tasks) task.setException(new CancellationException()); + return CompletableFuture.runAsync(this::checkCancellation); + } + + return CompletableFuture.allOf(tasks.stream() + .map(task -> CompletableFuture.completedFuture(null) + .thenComposeAsync(unused2 -> executeTask(parentTask, task)) + ).toArray(CompletableFuture[]::new)); + }); + } + + private CompletableFuture executeTasks(Task parentTask, Collection> tasks) { + return executeTasksExceptionally(parentTask, tasks) + .thenApplyAsync(unused -> (Exception) null) + .exceptionally(throwable -> { + Throwable resolved = resolveException(throwable); + if (resolved instanceof Exception) { + return (Exception) resolved; + } else { + // If an error occurred, we just rethrow it. + throw new CompletionException(throwable); + } + }); + } + + private CompletableFuture executeCompletableFutureTask(Task parentTask, CompletableFutureTask task) { + return CompletableFuture.completedFuture(null) + .thenComposeAsync(unused -> { + checkCancellation(); + + task.setCancelled(this::isCancelled); + task.setState(Task.TaskState.READY); + if (parentTask != null && task.getStage() == null) + task.setStage(parentTask.getStage()); + + if (task.getSignificance().shouldLog()) + Logging.LOG.log(Level.FINE, "Executing task: " + task.getName()); + + taskListeners.forEach(it -> it.onReady(task)); + + return task.getFuture(new TaskCompletableFuture() { + @Override + public CompletableFuture one(Task subtask) { + return executeTask(task, subtask); + } + + @Override + public CompletableFuture all(Collection> tasks) { + return executeTasksExceptionally(task, tasks); + } + }); + }) + .thenApplyAsync(result -> { + checkCancellation(); + + if (task.getSignificance().shouldLog()) { + Logging.LOG.log(Level.FINER, "Task finished: " + task.getName()); + } + + task.setResult(result); + task.onDone().fireEvent(new TaskEvent(this, task, false)); + taskListeners.forEach(it -> it.onFinished(task)); + + task.setState(Task.TaskState.SUCCEEDED); + + return result; + }) + .exceptionally(throwable -> { + Throwable resolved = resolveException(throwable); + if (resolved instanceof Exception) { + Exception e = (Exception) resolved; + if (e instanceof InterruptedException || e instanceof CancellationException) { + task.setException(null); + if (task.getSignificance().shouldLog()) { + Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName()); + } + task.onDone().fireEvent(new TaskEvent(this, task, true)); + taskListeners.forEach(it -> it.onFailed(task, e)); + } else { + task.setException(e); + exception = e; + if (task.getSignificance().shouldLog()) { + Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e); + } + task.onDone().fireEvent(new TaskEvent(this, task, true)); + taskListeners.forEach(it -> it.onFailed(task, e)); + } + + task.setState(Task.TaskState.FAILED); + } + + throw new CompletionException(resolved); // rethrow error + }); + } + + private CompletableFuture executeNormalTask(Task parentTask, Task task) { + return CompletableFuture.completedFuture(null) + .thenComposeAsync(unused -> { + checkCancellation(); + + task.setCancelled(this::isCancelled); + task.setState(Task.TaskState.READY); + if (task.getStage() != null) { + task.setInheritedStage(task.getStage()); + } else if (parentTask != null) { + task.setInheritedStage(parentTask.getInheritedStage()); + } + task.setNotifyPropertiesChanged(() -> taskListeners.forEach(it -> it.onPropertiesUpdate(task))); + + if (task.getSignificance().shouldLog()) + Logging.LOG.log(Level.FINE, "Executing task: " + task.getName()); + + taskListeners.forEach(it -> it.onReady(task)); + + if (task.doPreExecute()) { + return CompletableFuture.runAsync(wrap(task::preExecute), task.getExecutor()); + } else { + return CompletableFuture.completedFuture(null); + } + }) + .thenComposeAsync(unused -> executeTasks(task, task.getDependents())) + .thenComposeAsync(dependentsException -> { + boolean isDependentsSucceeded = dependentsException == null; + + if (isDependentsSucceeded) { + task.setDependentsSucceeded(); + } else { + task.setException(dependentsException); + + if (task.isRelyingOnDependents()) { + rethrow(dependentsException); + } + } + + return CompletableFuture.runAsync(wrap(() -> { + task.setState(Task.TaskState.RUNNING); + taskListeners.forEach(it -> it.onRunning(task)); + task.execute(); + }), task.getExecutor()).whenComplete((unused, throwable) -> { + task.setState(Task.TaskState.EXECUTED); + rethrow(throwable); + }); + }) + .thenComposeAsync(unused -> executeTasks(task, task.getDependencies())) + .thenComposeAsync(dependenciesException -> { + boolean isDependenciesSucceeded = dependenciesException == null; + + if (isDependenciesSucceeded) + task.setDependenciesSucceeded(); + + if (task.doPostExecute()) { + return CompletableFuture.runAsync(wrap(task::postExecute), task.getExecutor()) + .thenApply(unused -> dependenciesException); + } else { + return CompletableFuture.completedFuture(dependenciesException); + } + }) + .thenApplyAsync(dependenciesException -> { + boolean isDependenciesSucceeded = dependenciesException == null; + + if (!isDependenciesSucceeded) { + Logging.LOG.severe("Subtasks failed for " + task.getName()); + task.setException(dependenciesException); + if (task.isRelyingOnDependencies()) { + rethrow(dependenciesException); + } + } + + checkCancellation(); + + if (task.getSignificance().shouldLog()) { + Logging.LOG.log(Level.FINER, "Task finished: " + task.getName()); + } + + task.onDone().fireEvent(new TaskEvent(this, task, false)); + taskListeners.forEach(it -> it.onFinished(task)); + + task.setState(Task.TaskState.SUCCEEDED); + + return task.getResult(); + }) + .exceptionally(throwable -> { + Throwable resolved = resolveException(throwable); + if (resolved instanceof Exception) { + Exception e = convertInterruptedException((Exception) resolved); + task.setException(e); + exception = e; + if (e instanceof CancellationException) { + if (task.getSignificance().shouldLog()) { + Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName()); + } + } else { + if (task.getSignificance().shouldLog()) { + Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e); + } + } + task.onDone().fireEvent(new TaskEvent(this, task, true)); + taskListeners.forEach(it -> it.onFailed(task, e)); + + task.setState(Task.TaskState.FAILED); + } + + throw new CompletionException(resolved); // rethrow error + }); + } + + private CompletableFuture executeTask(Task parentTask, Task task) { + if (task instanceof CompletableFutureTask) { + return executeCompletableFutureTask(parentTask, (CompletableFutureTask) task); + } else { + return executeNormalTask(parentTask, task); + } + } + + private void checkCancellation() { + if (isCancelled()) { + throw new CancellationException("Cancelled by user"); + } + } + + private static Exception convertInterruptedException(Exception e) { + if (e instanceof InterruptedException) { + return new CancellationException(e.getMessage()); + } else { + return e; + } + } + + private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler = null; + + public static void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler uncaughtExceptionHandler) { + AsyncTaskExecutor.uncaughtExceptionHandler = uncaughtExceptionHandler; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/CompletableFutureTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/CompletableFutureTask.java new file mode 100644 index 00000000..1a4c039e --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/CompletableFutureTask.java @@ -0,0 +1,31 @@ +package com.tungsten.fclcore.task; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; + +public abstract class CompletableFutureTask extends Task { + + @Override + public void execute() throws Exception { + } + + public abstract CompletableFuture getFuture(TaskCompletableFuture executor); + + protected static Throwable resolveException(Throwable e) { + if (e instanceof ExecutionException || e instanceof CompletionException) + return resolveException(e.getCause()); + else + return e; + } + + public static class CustomException extends RuntimeException {} + + protected static CompletableFuture breakable(CompletableFuture future) { + return future.thenApplyAsync(unused1 -> (Void) null).exceptionally(throwable -> { + if (resolveException(throwable) instanceof CustomException) return null; + else throw new CompletionException(throwable); + }); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/DownloadException.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/DownloadException.java new file mode 100644 index 00000000..2f922f03 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/DownloadException.java @@ -0,0 +1,23 @@ +package com.tungsten.fclcore.task; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.URL; + +import static java.util.Objects.requireNonNull; + +public class DownloadException extends IOException { + + private final URL url; + + public DownloadException(URL url, @NotNull Throwable cause) { + super("Unable to download " + url + ", " + cause.getMessage(), requireNonNull(cause)); + + this.url = url; + } + + public URL getUrl() { + return url; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/FetchTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/FetchTask.java new file mode 100644 index 00000000..4f1bf05c --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/FetchTask.java @@ -0,0 +1,301 @@ +package com.tungsten.fclcore.task; + +import static com.tungsten.fclcore.util.Lang.threadPool; + +import com.tungsten.fclcore.event.Event; +import com.tungsten.fclcore.event.EventBus; +import com.tungsten.fclcore.util.CacheRepository; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.ToStringBuilder; +import com.tungsten.fclcore.util.io.IOUtils; +import com.tungsten.fclcore.util.io.NetworkUtils; +import com.tungsten.fclcore.util.io.ResponseCodeException; + +import java.io.Closeable; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public abstract class FetchTask extends Task { + protected final List urls; + protected final int retry; + protected boolean caching; + protected CacheRepository repository = CacheRepository.getInstance(); + + public FetchTask(List urls, int retry) { + Objects.requireNonNull(urls); + + this.urls = urls.stream().filter(Objects::nonNull).collect(Collectors.toList()); + this.retry = retry; + + if (this.urls.isEmpty()) + throw new IllegalArgumentException("At least one URL is required"); + + setExecutor(download()); + } + + public void setCaching(boolean caching) { + this.caching = caching; + } + + public void setCacheRepository(CacheRepository repository) { + this.repository = repository; + } + + protected void beforeDownload(URL url) throws IOException {} + + protected abstract void useCachedResult(Path cachedFile) throws IOException; + + protected abstract EnumCheckETag shouldCheckETag(); + + protected abstract Context getContext(URLConnection conn, boolean checkETag) throws IOException; + + @Override + public void execute() throws Exception { + Exception exception = null; + URL failedURL = null; + boolean checkETag; + switch (shouldCheckETag()) { + case CHECK_E_TAG: checkETag = true; break; + case NOT_CHECK_E_TAG: checkETag = false; break; + default: return; + } + + int repeat = 0; + download: for (URL url : urls) { + for (int retryTime = 0; retryTime < retry; retryTime++) { + if (isCancelled()) { + break download; + } + + try { + beforeDownload(url); + + updateProgress(0); + + URLConnection conn = NetworkUtils.createConnection(url); + if (checkETag) repository.injectConnection(conn); + + if (conn instanceof HttpURLConnection) { + conn = NetworkUtils.resolveConnection((HttpURLConnection) conn); + int responseCode = ((HttpURLConnection) conn).getResponseCode(); + + if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) { + // Handle cache + try { + Path cache = repository.getCachedRemoteFile(conn); + useCachedResult(cache); + return; + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Unable to use cached file, redownload " + url, e); + repository.removeRemoteEntry(conn); + // Now we must reconnect the server since 304 may result in empty content, + // if we want to redownload the file, we must reconnect the server without etag settings. + retryTime--; + continue; + } + } else if (responseCode / 100 == 4) { + throw new FileNotFoundException(url.toString()); + } else if (responseCode / 100 != 2) { + throw new ResponseCodeException(url, responseCode); + } + } + + long contentLength = conn.getContentLength(); + try (Context context = getContext(conn, checkETag); InputStream stream = conn.getInputStream()) { + int lastDownloaded = 0, downloaded = 0; + byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE]; + while (true) { + if (isCancelled()) break; + + int len = stream.read(buffer); + if (len == -1) break; + + context.write(buffer, 0, len); + + downloaded += len; + + if (contentLength >= 0) { + // Update progress information per second + updateProgress(downloaded, contentLength); + } + + updateDownloadSpeed(downloaded - lastDownloaded); + lastDownloaded = downloaded; + } + + if (isCancelled()) break download; + + updateDownloadSpeed(downloaded - lastDownloaded); + + if (contentLength >= 0 && downloaded != contentLength) + throw new IOException("Unexpected file size: " + downloaded + ", expected: " + contentLength); + + context.withResult(true); + } + + return; + } catch (FileNotFoundException ex) { + failedURL = url; + exception = ex; + Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", not found", ex); + + break; // we will not try this URL again + } catch (IOException ex) { + failedURL = url; + exception = ex; + Logging.LOG.log(Level.WARNING, "Failed to download " + url + ", repeat times: " + (++repeat), ex); + } + } + } + + if (exception != null) + throw new DownloadException(failedURL, exception); + } + + private static final Timer timer = new Timer("DownloadSpeedRecorder", true); + private static final AtomicInteger downloadSpeed = new AtomicInteger(0); + public static final EventBus speedEvent = new EventBus(); + + static { + timer.schedule(new TimerTask() { + @Override + public void run() { + speedEvent.channel(SpeedEvent.class).fireEvent(new SpeedEvent(speedEvent, downloadSpeed.getAndSet(0))); + } + }, 0, 1000); + } + + private static void updateDownloadSpeed(int speed) { + downloadSpeed.addAndGet(speed); + } + + public static class SpeedEvent extends Event { + private final int speed; + + public SpeedEvent(Object source, int speed) { + super(source); + + this.speed = speed; + } + + /** + * Download speed in byte/sec. + * @return download speed + */ + public int getSpeed() { + return speed; + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("speed", speed).toString(); + } + } + + protected static abstract class Context implements Closeable { + private boolean success; + + public abstract void write(byte[] buffer, int offset, int len) throws IOException; + + public final void withResult(boolean success) { + this.success = success; + } + + protected boolean isSuccess() { + return success; + } + } + + protected enum EnumCheckETag { + CHECK_E_TAG, + NOT_CHECK_E_TAG, + CACHED + } + + protected class DownloadState { + private final int startPosition; + private final int endPosition; + private final int currentPosition; + private final boolean finished; + + public DownloadState(int startPosition, int endPosition, int currentPosition) { + if (currentPosition < startPosition || currentPosition > endPosition) { + throw new IllegalArgumentException("Illegal download state: start " + startPosition + ", end " + endPosition + ", cur " + currentPosition); + } + this.startPosition = startPosition; + this.endPosition = endPosition; + this.currentPosition = currentPosition; + finished = currentPosition == endPosition; + } + + public int getStartPosition() { + return startPosition; + } + + public int getEndPosition() { + return endPosition; + } + + public int getCurrentPosition() { + return currentPosition; + } + + public boolean isFinished() { + return finished; + } + } + + protected class DownloadMission { + + + + } + + public static int DEFAULT_CONCURRENCY = Math.min(Runtime.getRuntime().availableProcessors() * 4, 64); + private static int downloadExecutorConcurrency = DEFAULT_CONCURRENCY; + private static volatile ThreadPoolExecutor DOWNLOAD_EXECUTOR; + + /** + * Get singleton instance of the thread pool for file downloading. + * + * @return Thread pool for FetchTask + */ + protected static ExecutorService download() { + if (DOWNLOAD_EXECUTOR == null) { + synchronized (Schedulers.class) { + if (DOWNLOAD_EXECUTOR == null) { + DOWNLOAD_EXECUTOR = threadPool("Download", true, downloadExecutorConcurrency, 10, TimeUnit.SECONDS); + } + } + } + + return DOWNLOAD_EXECUTOR; + } + + public static void setDownloadExecutorConcurrency(int concurrency) { + concurrency = Math.max(concurrency, 1); + synchronized (Schedulers.class) { + downloadExecutorConcurrency = concurrency; + if (DOWNLOAD_EXECUTOR != null) { + DOWNLOAD_EXECUTOR.setCorePoolSize(concurrency); + DOWNLOAD_EXECUTOR.setMaximumPoolSize(concurrency); + } + } + } + + public static int getDownloadExecutorConcurrency() { + synchronized (Schedulers.class) { + return downloadExecutorConcurrency; + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/FileDownloadTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/FileDownloadTask.java new file mode 100644 index 00000000..6e536d1b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/FileDownloadTask.java @@ -0,0 +1,257 @@ +package com.tungsten.fclcore.task; + +import static com.tungsten.fclcore.util.DigestUtils.getDigest; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.math.BigInteger; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.util.*; +import java.util.logging.Level; + +import static java.util.Objects.requireNonNull; + +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.io.ChecksumMismatchException; +import com.tungsten.fclcore.util.io.FileUtils; + +/** + * A task that can download a file online. + */ +public class FileDownloadTask extends FetchTask { + + public static class IntegrityCheck { + private final String algorithm; + private final String checksum; + + public IntegrityCheck(String algorithm, String checksum) { + this.algorithm = requireNonNull(algorithm); + this.checksum = requireNonNull(checksum); + } + + public static IntegrityCheck of(String algorithm, String checksum) { + if (checksum == null) return null; + else return new IntegrityCheck(algorithm, checksum); + } + + public String getAlgorithm() { + return algorithm; + } + + public String getChecksum() { + return checksum; + } + + public MessageDigest createDigest() { + return getDigest(algorithm); + } + + public void performCheck(MessageDigest digest) throws ChecksumMismatchException { + String actualChecksum = String.format("%1$040x", new BigInteger(1, digest.digest())); + if (!checksum.equalsIgnoreCase(actualChecksum)) { + throw new ChecksumMismatchException(algorithm, checksum, actualChecksum); + } + } + } + + private final File file; + private final IntegrityCheck integrityCheck; + private Path candidate; + private final ArrayList integrityCheckHandlers = new ArrayList<>(); + + /** + * @param url the URL of remote file. + * @param file the location that download to. + */ + public FileDownloadTask(URL url, File file) { + this(url, file, null); + } + + /** + * @param url the URL of remote file. + * @param file the location that download to. + * @param integrityCheck the integrity check to perform, null if no integrity check is to be performed + */ + public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck) { + this(Collections.singletonList(url), file, integrityCheck); + } + + /** + * @param url the URL of remote file. + * @param file the location that download to. + * @param integrityCheck the integrity check to perform, null if no integrity check is to be performed + * @param retry the times for retrying if downloading fails. + */ + public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck, int retry) { + this(Collections.singletonList(url), file, integrityCheck, retry); + } + + /** + * Constructor. + * @param urls urls of remote file, will be attempted in order. + * @param file the location that download to. + */ + public FileDownloadTask(List urls, File file) { + this(urls, file, null); + } + + /** + * Constructor. + * @param urls urls of remote file, will be attempted in order. + * @param file the location that download to. + * @param integrityCheck the integrity check to perform, null if no integrity check is to be performed + */ + public FileDownloadTask(List urls, File file, IntegrityCheck integrityCheck) { + this(urls, file, integrityCheck, 3); + } + + /** + * Constructor. + * @param urls urls of remote file, will be attempted in order. + * @param file the location that download to. + * @param integrityCheck the integrity check to perform, null if no integrity check is to be performed + * @param retry the times for retrying if downloading fails. + */ + public FileDownloadTask(List urls, File file, IntegrityCheck integrityCheck, int retry) { + super(urls, retry); + this.file = file; + this.integrityCheck = integrityCheck; + + setName(file.getName()); + } + + public File getFile() { + return file; + } + + public FileDownloadTask setCandidate(Path candidate) { + this.candidate = candidate; + return this; + } + + public void addIntegrityCheckHandler(IntegrityCheckHandler handler) { + integrityCheckHandlers.add(Objects.requireNonNull(handler)); + } + + @Override + protected EnumCheckETag shouldCheckETag() { + // Check cache + if (integrityCheck != null && caching) { + Optional cache = repository.checkExistentFile(candidate, integrityCheck.getAlgorithm(), integrityCheck.getChecksum()); + if (cache.isPresent()) { + try { + FileUtils.copyFile(cache.get().toFile(), file); + Logging.LOG.log(Level.FINER, "Successfully verified file " + file + " from " + urls.get(0)); + return EnumCheckETag.CACHED; + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Failed to copy cache files", e); + } + } + return EnumCheckETag.NOT_CHECK_E_TAG; + } else { + return EnumCheckETag.CHECK_E_TAG; + } + } + + @Override + protected void beforeDownload(URL url) { + Logging.LOG.log(Level.FINER, "Downloading " + url + " to " + file); + } + + @Override + protected void useCachedResult(Path cache) throws IOException { + FileUtils.copyFile(cache.toFile(), file); + } + + @Override + protected Context getContext(URLConnection conn, boolean checkETag) throws IOException { + Path temp = Files.createTempFile(null, null); + RandomAccessFile rFile = new RandomAccessFile(temp.toFile(), "rw"); + MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest(); + + return new Context() { + @Override + public void write(byte[] buffer, int offset, int len) throws IOException { + if (digest != null) { + digest.update(buffer, offset, len); + } + + rFile.write(buffer, offset, len); + } + + @Override + public void close() throws IOException { + try { + rFile.close(); + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Failed to close file: " + rFile, e); + } + + if (!isSuccess()) { + try { + Files.delete(temp); + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Failed to delete file: " + rFile, e); + } + return; + } + + for (IntegrityCheckHandler handler : integrityCheckHandlers) { + handler.checkIntegrity(temp, file.toPath()); + } + + Files.deleteIfExists(file.toPath()); + if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) + throw new IOException("Unable to make parent directory " + file); + + try { + FileUtils.moveFile(temp.toFile(), file); + } catch (Exception e) { + throw new IOException("Unable to move temp file from " + temp + " to " + file, e); + } + + // Integrity check + if (integrityCheck != null) { + integrityCheck.performCheck(digest); + } + + if (caching && integrityCheck != null) { + try { + repository.cacheFile(file.toPath(), integrityCheck.getAlgorithm(), integrityCheck.getChecksum()); + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Failed to cache file", e); + } + } + + if (checkETag) { + repository.cacheRemoteFile(file.toPath(), conn); + } + } + }; + } + + public interface IntegrityCheckHandler { + /** + * Check whether the file is corrupted or not. + * @param filePath the file locates in (maybe in temp directory) + * @param destinationPath for real file name + * @throws IOException if the file is corrupted + */ + void checkIntegrity(Path filePath, Path destinationPath) throws IOException; + } + + public static final IntegrityCheckHandler ZIP_INTEGRITY_CHECK_HANDLER = (filePath, destinationPath) -> { + String ext = FileUtils.getExtension(destinationPath).toLowerCase(); + if (ext.equals("zip") || ext.equals("jar")) { + try (FileSystem ignored = CompressingUtils.createReadOnlyZipFileSystem(filePath)) { + // test for zip format + } + } + }; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/GetTask.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/GetTask.java new file mode 100644 index 00000000..c982ebed --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/GetTask.java @@ -0,0 +1,76 @@ +package com.tungsten.fclcore.task; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.util.*; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.tungsten.fclcore.util.io.FileUtils; + +public final class GetTask extends FetchTask { + + private final Charset charset; + + public GetTask(URL url) { + this(url, UTF_8); + } + + public GetTask(URL url, Charset charset) { + this(url, charset, 3); + } + + public GetTask(URL url, Charset charset, int retry) { + this(Collections.singletonList(url), charset, retry); + } + + public GetTask(List url) { + this(url, UTF_8, 3); + } + + public GetTask(List urls, Charset charset, int retry) { + super(urls, retry); + this.charset = charset; + + setName(urls.get(0).toString()); + } + + @Override + protected EnumCheckETag shouldCheckETag() { + return EnumCheckETag.CHECK_E_TAG; + } + + @Override + protected void useCachedResult(Path cachedFile) throws IOException { + setResult(FileUtils.readText(cachedFile)); + } + + @Override + protected Context getContext(URLConnection conn, boolean checkETag) { + return new Context() { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + @Override + public void write(byte[] buffer, int offset, int len) { + baos.write(buffer, offset, len); + } + + @Override + public void close() throws IOException { + if (!isSuccess()) return; + + String result = baos.toString(charset.name()); + setResult(result); + + if (checkETag) { + repository.cacheText(result, conn); + } + } + }; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/Schedulers.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/Schedulers.java new file mode 100644 index 00000000..e47cf95b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/Schedulers.java @@ -0,0 +1,52 @@ +package com.tungsten.fclcore.task; + +import static com.tungsten.fclcore.util.Lang.threadPool; + +import com.tungsten.fclcore.util.Logging; + +import java.util.concurrent.*; + +public final class Schedulers { + + private Schedulers() { + } + + private static volatile ExecutorService IO_EXECUTOR; + + /** + * Get singleton instance of the thread pool for I/O operations, + * usually for reading files from disk, or Internet connections. + * + * This thread pool has no more than 4 threads, and number of threads will get + * reduced if concurrency is less than thread number. + * + * @return Thread pool for I/O operations. + */ + public static ExecutorService io() { + if (IO_EXECUTOR == null) { + synchronized (Schedulers.class) { + if (IO_EXECUTOR == null) { + IO_EXECUTOR = threadPool("IO", true, 4, 10, TimeUnit.SECONDS); + } + } + } + + return IO_EXECUTOR; + } + + public static Executor defaultScheduler() { + return ForkJoinPool.commonPool(); + } + + public static synchronized void shutdown() { + Logging.LOG.info("Shutting down executor services."); + + // shutdownNow will interrupt all threads. + // So when we want to close the app, no threads need to be waited for finish. + // Sometimes it resolves the problem that the app does not exit. + + if (IO_EXECUTOR != null) + IO_EXECUTOR.shutdownNow(); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/Task.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/Task.java new file mode 100644 index 00000000..2612cadb --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/Task.java @@ -0,0 +1,1108 @@ +package com.tungsten.fclcore.task; + +import com.tungsten.fclcore.event.EventManager; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fclcore.util.ReflectionHelper; +import com.tungsten.fclcore.util.function.ExceptionalConsumer; +import com.tungsten.fclcore.util.function.ExceptionalFunction; +import com.tungsten.fclcore.util.function.ExceptionalRunnable; +import com.tungsten.fclcore.util.function.ExceptionalSupplier; + +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; + +/** + * Disposable task. + */ +public abstract class Task { + + private final EventManager onDone = new EventManager<>(); + + /** + * True if not logging when executing this task. + */ + private TaskSignificance significance = TaskSignificance.MAJOR; + + public final TaskSignificance getSignificance() { + return significance; + } + + public final Task setSignificance(TaskSignificance significance) { + this.significance = significance; + return this; + } + + // cancel + private Supplier cancelled; + + final void setCancelled(Supplier cancelled) { + this.cancelled = cancelled; + } + + protected final boolean isCancelled() { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + return true; + } + + return cancelled != null ? cancelled.get() : false; + } + + // stage + private String stage = null; + + /** + * Stage of task implies the goal of this task, for grouping tasks. + * Stage will inherit from the parent task. + */ + public String getStage() { + return stage; + } + + /** + * You must initialize stage in constructor. + * @param stage the stage + */ + protected final void setStage(String stage) { + this.stage = stage; + } + + private String inheritedStage = null; + + public String getInheritedStage() { + return inheritedStage; + } + + void setInheritedStage(String inheritedStage) { + this.inheritedStage = inheritedStage; + } + + // properties + Map properties; + + public Map getProperties() { + if (properties == null) properties = new HashMap<>(); + return properties; + } + + private Runnable notifyPropertiesChanged; + + void setNotifyPropertiesChanged(Runnable runnable) { + this.notifyPropertiesChanged = runnable; + } + + protected void notifyPropertiesChanged() { + if (notifyPropertiesChanged != null) { + notifyPropertiesChanged.run(); + } + } + + // state + private TaskState state = TaskState.READY; + + public final TaskState getState() { + return state; + } + + final void setState(TaskState state) { + this.state = state; + } + + // last exception + private Exception exception; + + /** + * When task has been cancelled, task.exception will be null. + * + * @return the exception thrown during execution, possibly from dependents or dependencies. + */ + @Nullable + public final Exception getException() { + return exception; + } + + final void setException(Exception e) { + exception = e; + } + + private Executor executor = Schedulers.defaultScheduler(); + + /** + * The executor that decides how this task runs. + */ + public final Executor getExecutor() { + return executor; + } + + public final Task setExecutor(Executor executor) { + this.executor = executor; + return this; + } + + // dependents succeeded + private boolean dependentsSucceeded = false; + + public boolean isDependentsSucceeded() { + return dependentsSucceeded; + } + + void setDependentsSucceeded() { + dependentsSucceeded = true; + } + + // dependencies succeeded + private boolean dependenciesSucceeded = false; + + public boolean isDependenciesSucceeded() { + return dependenciesSucceeded; + } + + void setDependenciesSucceeded() { + dependenciesSucceeded = true; + } + + /** + * True if requires all {@link #getDependents} finishing successfully. + *

+ * **Note** if this field is set false, you are not supposed to invoke [run] + */ + public boolean isRelyingOnDependents() { + return true; + } + + /** + * True if requires all {@link #getDependencies} finishing successfully. + *

+ * **Note** if this field is set false, you are not supposed to invoke [run] + */ + public boolean isRelyingOnDependencies() { + return true; + } + + // name + private String name = getClass().getName(); + + public String getName() { + return name; + } + + public Task setName(String name) { + this.name = name; + return this; + } + + @Override + public String toString() { + if (getClass().getName().equals(getName())) + return getName(); + else + return getClass().getName() + "[" + getName() + "]"; + } + + // result + private T result; + private Consumer resultConsumer; + + /** + * Returns the result of this task. + * + * The result will be generated only if the execution is completed. + */ + public T getResult() { + return result; + } + + protected void setResult(T result) { + this.result = result; + if (resultConsumer != null) + resultConsumer.accept(result); + } + + /** + * Sync the result of this task by given action. + * + * @param action the action to perform when result of this task changed + * @return this Task + */ + public Task storeTo(Consumer action) { + this.resultConsumer = action; + action.accept(getResult()); + return this; + } + + // execution + public boolean doPreExecute() { + return false; + } + + /** + * @throws InterruptedException if current thread is interrupted + * @see Thread#interrupted + */ + public void preExecute() throws Exception {} + + /** + * @throws InterruptedException if current thread is interrupted + * @see Thread#interrupted + */ + public abstract void execute() throws Exception; + + public boolean doPostExecute() { + return false; + } + + /** + * This method will be called after dependency tasks terminated all together. + * + * You can check whether dependencies succeed in this method by calling + * {@link Task#isDependenciesSucceeded()} no matter when + * {@link Task#isRelyingOnDependencies()} returns true or false. + * + * @throws InterruptedException if current thread is interrupted + * @see Thread#interrupted + * @see Task#isDependenciesSucceeded() + */ + public void postExecute() throws Exception {} + + /** + * The collection of sub-tasks that should execute **before** this task running. + */ + public Collection> getDependents() { + return Collections.emptySet(); + } + + /** + * The collection of sub-tasks that should execute **after** this task running. + * Will not be executed if execution fails. + */ + public Collection> getDependencies() { + return Collections.emptySet(); + } + + public EventManager onDone() { + return onDone; + } + + protected long getProgressInterval() { + return 1000L; + } + + private long lastTime = Long.MIN_VALUE; + + protected void updateProgress(long progress, long total) { + updateProgress(1.0 * progress / total); + } + + protected void updateProgress(double progress) { + if (progress < 0 || progress > 1.0) + throw new IllegalArgumentException("Progress is must between 0 and 1."); + long now = System.currentTimeMillis(); + if (lastTime == Long.MIN_VALUE || now - lastTime >= getProgressInterval()) { + updateProgressImmediately(progress); + lastTime = now; + } + } + + protected void updateProgressImmediately(double progress) { + // Todo : update progress + } + + protected final void updateMessage(String newMessage) { + // Todo : update msg + } + + public final T run() throws Exception { + if (getSignificance().shouldLog()) + Logging.LOG.log(Level.FINE, "Executing task: " + getName()); + + for (Task task : getDependents()) + doSubTask(task); + execute(); + for (Task task : getDependencies()) + doSubTask(task); + onDone.fireEvent(new TaskEvent(this, this, false)); + + return getResult(); + } + + private void doSubTask(Task task) throws Exception { + task.run(); + } + + public final TaskExecutor executor() { + return new AsyncTaskExecutor(this); + } + + public final TaskExecutor executor(boolean start) { + TaskExecutor executor = new AsyncTaskExecutor(this); + if (start) + executor.start(); + return executor; + } + + public final TaskExecutor executor(TaskListener taskListener) { + TaskExecutor executor = new AsyncTaskExecutor(this); + executor.addTaskListener(taskListener); + return executor; + } + + public final void start() { + executor().start(); + } + + public final boolean test() { + return executor().test(); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the default Executor, with this + * task's result as the argument to the supplied function. + * + * @param fn the function to use to compute the value of the returned Task + * @param the function's return type + * @return the new Task + */ + public Task thenApplyAsync(ExceptionalFunction fn) { + return thenApplyAsync(Schedulers.defaultScheduler(), fn); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the supplied Executor, with this + * task's result as the argument to the supplied function. + * + * @param executor the executor to use for asynchronous execution + * @param fn the function to use to compute the value of the returned Task + * @param the function's return type + * @return the new Task + */ + public Task thenApplyAsync(Executor executor, ExceptionalFunction fn) { + return thenApplyAsync(getCaller(), executor, fn).setSignificance(TaskSignificance.MODERATE); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the supplied Executor, with this + * task's result as the argument to the supplied function. + * + * @param name the name of this new Task for displaying + * @param executor the executor to use for asynchronous execution + * @param fn the function to use to compute the value of the returned Task + * @param the function's return type + * @return the new Task + */ + public Task thenApplyAsync(String name, Executor executor, ExceptionalFunction fn) { + return new UniApply<>(fn).setExecutor(executor).setName(name); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the default Executor, with this + * task's result as the argument to the supplied action. + * + * @param action the action to perform before completing the + * returned Task + * @return the new Task + */ + public Task thenAcceptAsync(ExceptionalConsumer action) { + return thenAcceptAsync(Schedulers.defaultScheduler(), action); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the supplied Executor, with this + * task's result as the argument to the supplied action. + * + * @param action the action to perform before completing the returned Task + * @param executor the executor to use for asynchronous execution + * @return the new Task + */ + public Task thenAcceptAsync(Executor executor, ExceptionalConsumer action) { + return thenAcceptAsync(getCaller(), executor, action).setSignificance(TaskSignificance.MODERATE); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the supplied Executor, with this + * task's result as the argument to the supplied action. + * + * @param name the name of this new Task for displaying + * @param action the action to perform before completing the returned Task + * @param executor the executor to use for asynchronous execution + * @return the new Task + */ + public Task thenAcceptAsync(String name, Executor executor, ExceptionalConsumer action) { + return thenApplyAsync(name, executor, result -> { + action.accept(result); + return null; + }); + } + + /** + * Returns a new Task that, when this task completes + * normally, executes the given action using the default Executor. + * + * @param action the action to perform before completing the + * returned Task + * @return the new Task + */ + public Task thenRunAsync(ExceptionalRunnable action) { + return thenRunAsync(Schedulers.defaultScheduler(), action); + } + + /** + * Returns a new Task that, when this task completes + * normally, executes the given action using the supplied Executor. + * + * @param action the action to perform before completing the + * returned Task + * @param executor the executor to use for asynchronous execution + * @return the new Task + */ + public Task thenRunAsync(Executor executor, ExceptionalRunnable action) { + return thenRunAsync(getCaller(), executor, action).setSignificance(TaskSignificance.MODERATE); + } + + /** + * Returns a new Task that, when this task completes + * normally, executes the given action using the supplied Executor. + * + * @param name the name of this new Task for displaying + * @param action the action to perform before completing the + * returned Task + * @param executor the executor to use for asynchronous execution + * @return the new Task + */ + public Task thenRunAsync(String name, Executor executor, ExceptionalRunnable action) { + return thenApplyAsync(name, executor, ignore -> { + action.run(); + return null; + }); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the default Executor. + * + * @param fn the function to use to compute the value of the returned Task + * @param the function's return type + * @return the new Task + */ + public final Task thenSupplyAsync(Callable fn) { + return thenComposeAsync(() -> Task.supplyAsync(fn)); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed using the default Executor. + * + * @param name the name of this new Task for displaying + * @param fn the function to use to compute the value of the returned Task + * @param the function's return type + * @return the new Task + */ + public final Task thenSupplyAsync(String name, Callable fn) { + return thenComposeAsync(() -> Task.supplyAsync(name, fn)); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed. + * + * @param other the another Task + * @param the type of the returned Task's result + * @return the Task + */ + public final Task thenComposeAsync(Task other) { + return thenComposeAsync(() -> other); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed. + * + * @param fn the function returning a new Task + * @param the type of the returned Task's result + * @return the Task + */ + public final Task thenComposeAsync(ExceptionalSupplier, ?> fn) { + return thenComposeAsync(Schedulers.defaultScheduler(), fn); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed. + * + * @param fn the function returning a new Task + * @param executor the executor to use for asynchronous execution + * @param the type of the returned Task's result + * @return the Task + */ + public final Task thenComposeAsync(Executor executor, ExceptionalSupplier, ?> fn) { + return new UniCompose<>(fn, true).setExecutor(executor); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed with result of this task as the argument + * to the supplied function. + * + * @param fn the function returning a new Task + * @param the type of the returned Task's result + * @return the Task + */ + public Task thenComposeAsync(ExceptionalFunction, E> fn) { + return thenComposeAsync(Schedulers.defaultScheduler(), fn); + } + + /** + * Returns a new Task that, when this task completes + * normally, is executed with result of this task as the argument + * to the supplied function. + * + * @param fn the function returning a new Task + * @param executor the executor to use for asynchronous execution + * @param the type of the returned Task's result + * @return the Task + */ + public Task thenComposeAsync(Executor executor, ExceptionalFunction, E> fn) { + return new UniCompose<>(fn, true).setExecutor(executor); + } + + public final Task withComposeAsync(Task other) { + return withComposeAsync(() -> other); + } + + public final Task withComposeAsync(ExceptionalSupplier, E> fn) { + return new UniCompose<>(fn, false); + } + + /** + * Returns a new Task that, when this task completes + * normally, executes the given action using the default Executor. + * + * @param action the action to perform before completing the + * returned Task + * @return the new Task + */ + public Task withRunAsync(ExceptionalRunnable action) { + return withRunAsync(Schedulers.defaultScheduler(), action); + } + + /** + * Returns a new Task that, when this task completes + * normally, executes the given action using the supplied Executor. + * + * @param action the action to perform before completing the + * returned Task + * @param executor the executor to use for asynchronous execution + * @return the new Task + */ + public Task withRunAsync(Executor executor, ExceptionalRunnable action) { + return withRunAsync(getCaller(), executor, action).setSignificance(TaskSignificance.MODERATE); + } + + /** + * Returns a new Task that, when this task completes + * normally, executes the given action using the supplied Executor. + * + * @param name the name of this new Task for displaying + * @param action the action to perform before completing the + * returned Task + * @param executor the executor to use for asynchronous execution + * @return the new Task + */ + public Task withRunAsync(String name, Executor executor, ExceptionalRunnable action) { + return new UniCompose<>(() -> Task.runAsync(name, executor, action), false); + } + + /** + * Returns a new Task with the same exception as this task, that executes + * the given action when this task completes. + * + *

When this task is complete, the given action is invoked, a boolean + * value represents the execution status of this task, and the exception + * (or {@code null} if none) of this task as arguments. The returned task + * is completed when the action returns. If the supplied action itself + * encounters an exception, then the returned task exceptionally completes + * with this exception unless this task also completed exceptionally. + * + * @param action the action to perform + * @return the new Task + */ + public final Task whenComplete(FinalizedCallback action) { + return whenComplete(Schedulers.defaultScheduler(), action); + } + + /** + * Returns a new Task with the same exception as this task, that executes + * the given action when this task completes. + * + *

When this task is complete, the given action is invoked, a boolean + * value represents the execution status of this task, and the exception + * (or {@code null} if none, which means when isDependentSucceeded is false, + * exception may be null) of this task as arguments. The returned task + * is completed when the action returns. If the supplied action itself + * encounters an exception, then the returned task exceptionally completes + * with this exception unless this task also completed exceptionally. + * + * @param action the action to perform + * @param executor the executor to use for asynchronous execution + * @return the new Task + */ + public final Task whenComplete(Executor executor, FinalizedCallback action) { + return new Task() { + { + setSignificance(TaskSignificance.MODERATE); + } + + @Override + public void execute() throws Exception { + if (isDependentsSucceeded() != (Task.this.getException() == null)) + throw new AssertionError("When whenComplete succeeded, Task.exception must be null."); + + action.execute(Task.this.getException()); + + if (!isDependentsSucceeded()) { + setSignificance(TaskSignificance.MINOR); + if (Task.this.getException() == null) + throw new AssertionError("When failed, exception cannot be null"); + else + throw Task.this.getException(); + } + } + + @Override + public Collection> getDependents() { + return Collections.singleton(Task.this); + } + + @Override + public boolean isRelyingOnDependents() { + return false; + } + }.setExecutor(executor).setName(getCaller()).setSignificance(TaskSignificance.MODERATE); + } + + /** + * Returns a new Task with the same exception as this task, that executes + * the given action when this task completes. + * + *

When this task is complete, the given action is invoked with the + * result (or {@code null} if none), a boolean value represents the + * execution status of this task, and the exception (or {@code null} + * if none) of this task as arguments. The returned task is completed + * when the action returns. If the supplied action itself encounters an + * exception, then the returned task exceptionally completes with this + * exception unless this task also completed exceptionally. + * + * @param action the action to perform + * @return the new Task + */ + public Task whenComplete(Executor executor, FinalizedCallbackWithResult action) { + return whenComplete(executor, (exception -> action.execute(getResult(), exception))); + } + + /** + * Returns a new Task with the same exception as this task, that executes + * the given actions when this task completes. + * + *

When this task is complete, the given success action is invoked, the + * given failure action is invoked with the exception of this task. The + * returned task is completed when the action returns. If the supplied + * action itself encounters an exception, then the returned task exceptionally + * completes with this exception unless this task also + * completed exceptionally. + * + * @param success the action to perform when this task successfully completed + * @param failure the action to perform when this task exceptionally returned + * @return the new Task + */ + public final Task whenComplete(Executor executor, ExceptionalRunnable success, ExceptionalConsumer failure) { + return whenComplete(executor, exception -> { + if (exception == null) { + if (success != null) + try { + success.run(); + } catch (Exception e) { + Logging.LOG.log(Level.WARNING, "Failed to execute " + success, e); + if (failure != null) + failure.accept(e); + } + } else { + if (failure != null) + failure.accept(exception); + } + }); + } + + /** + * Returns a new Task with the same exception as this task, that executes + * the given actions when this task completes. + * + *

When this task is complete, the given success action is invoked with + * the result, the given failure action is invoked with the exception of + * this task. The returned task is completed when the action returns. If + * the supplied action itself encounters an exception, then the returned + * task exceptionally completes with this exception unless this task also + * completed exceptionally. + * + * @param success the action to perform when this task successfully completed + * @param failure the action to perform when this task exceptionally returned + * @return the new Task + */ + public Task whenComplete(Executor executor, ExceptionalConsumer success, ExceptionalConsumer failure) { + return whenComplete(executor, () -> success.accept(getResult()), failure); + } + + public Task withStage(String stage) { + StageTask task = new StageTask(); + task.setStage(stage); + return task; + } + + public Task withStagesHint(List stages) { + return new StagesHintTask(stages); + } + + public class StagesHintTask extends Task { + private final List stages; + + public StagesHintTask(List stages) { + this.stages = stages; + } + + @Override + public Collection> getDependents() { + return Collections.singleton(Task.this); + } + + @Override + public void execute() { + setResult(Task.this.getResult()); + } + + public List getStages() { + return stages; + } + } + + public Task withCounter(String countStage) { + return new CountTask(countStage); + } + + public static Task runAsync(ExceptionalRunnable closure) { + return runAsync(Schedulers.defaultScheduler(), closure); + } + + public static Task runAsync(String name, ExceptionalRunnable closure) { + return runAsync(name, Schedulers.defaultScheduler(), closure); + } + + public static Task runAsync(Executor executor, ExceptionalRunnable closure) { + return runAsync(getCaller(), executor, closure).setSignificance(TaskSignificance.MODERATE); + } + + public static Task runAsync(String name, Executor executor, ExceptionalRunnable closure) { + return new SimpleTask<>(closure.toCallable()).setExecutor(executor).setName(name); + } + + public static Task composeAsync(ExceptionalSupplier, ?> fn) { + return composeAsync(getCaller(), fn).setSignificance(TaskSignificance.MODERATE); + } + + public static Task composeAsync(String name, ExceptionalSupplier, ?> fn) { + return new Task() { + Task then; + + @Override + public void execute() throws Exception { + then = fn.get(); + if (then != null) + then.storeTo(this::setResult); + } + + @Override + public Collection> getDependencies() { + return then == null ? Collections.emptySet() : Collections.singleton(then); + } + }.setName(name); + } + + public static Task composeAsync(Executor executor, ExceptionalSupplier, ?> fn) { + return composeAsync(fn).setExecutor(executor); + } + + public static Task supplyAsync(Callable callable) { + return supplyAsync(getCaller(), callable).setSignificance(TaskSignificance.MODERATE); + } + + public static Task supplyAsync(Executor executor, Callable callable) { + return supplyAsync(getCaller(), executor, callable).setSignificance(TaskSignificance.MODERATE); + } + + public static Task supplyAsync(String name, Callable callable) { + return supplyAsync(name, Schedulers.defaultScheduler(), callable); + } + + public static Task supplyAsync(String name, Executor executor, Callable callable) { + return new SimpleTask<>(callable).setExecutor(executor).setName(name); + } + + public static Task completed(V value) { + return fromCompletableFuture(CompletableFuture.completedFuture(value)); + } + + /** + * Returns a new Task that is completed when all of the given Tasks + * complete. If any of the given Tasks complete exceptionally, + * then the returned Task also does so. Otherwise, the results, if + * any, of the given Tasks are not reflected in the returned Task, + * but may be obtained by inspecting them individually. If no Tasks + * are provided, returns a Task completed with the value {@code null}. + * + * @param tasks the Tasks + * @return a new Task that is completed when all of the given Tasks complete + */ + public static Task> allOf(Task... tasks) { + return allOf(Arrays.asList(tasks)); + } + + /** + * Returns a new Task that is completed when all of the given Tasks + * complete. If any of the given Tasks complete exceptionally, + * then the returned Task also does so. Otherwise, the results, if + * any, of the given Tasks are not reflected in the returned Task, + * but may be obtained by inspecting them individually. If no Tasks + * are provided, returns a Task completed with the value {@code null}. + * + * @param tasks the Tasks + * @return a new Task that is completed when all of the given Tasks complete + */ + public static Task> allOf(Collection> tasks) { + return new Task>() { + { + setSignificance(TaskSignificance.MINOR); + } + + @Override + public void execute() { + List result = new ArrayList<>(); + for (Task task : tasks) { + result.add(task.getResult()); + } + setResult(result); + } + + @Override + public Collection> getDependents() { + return tasks; + } + }; + } + + /** + * Returns a new task that runs the given tasks sequentially + * and returns the result of the last task. + * + * @param tasks tasks to run sequentially + * @return the combination of these tasks + */ + public static Task runSequentially(Task... tasks) { + if (tasks.length == 0) { + return new SimpleTask<>(() -> null); + } + + Task task = tasks[0]; + for (int i = 1; i < tasks.length; i++) { + task = task.thenComposeAsync(tasks[i]); + } + return task; + } + + public static Task fromCompletableFuture(CompletableFuture future) { + return new CompletableFutureTask() { + @Override + public CompletableFuture getFuture(TaskCompletableFuture executor) { + return future; + } + }; + } + + public enum TaskSignificance { + MAJOR, + MODERATE, + MINOR; + + public boolean shouldLog() { + return this != MINOR; + } + + public boolean shouldShow() { + return this == MAJOR; + } + } + + public enum TaskState { + READY, + RUNNING, + EXECUTED, + SUCCEEDED, + FAILED + } + + public interface FinalizedCallback { + void execute(Exception exception) throws Exception; + } + + public interface FinalizedCallbackWithResult { + void execute(T result, Exception exception) throws Exception; + } + + private static String getCaller() { + return ReflectionHelper.getCaller("com.tungsten.fclcore.task").toString(); + } + + private static final class SimpleTask extends Task { + + private final Callable callable; + + SimpleTask(Callable callable) { + this.callable = callable; + } + + @Override + public void execute() throws Exception { + setResult(callable.call()); + } + } + + private class UniApply extends Task { + private final ExceptionalFunction callable; + + UniApply(ExceptionalFunction callable) { + this.callable = callable; + } + + @Override + public Collection> getDependents() { + return Collections.singleton(Task.this); + } + + @Override + public void execute() throws Exception { + setResult(callable.apply(Task.this.getResult())); + } + } + + /** + * A task that combines two tasks and make sure [pred] runs before succ. + */ + private final class UniCompose extends Task { + + private final boolean relyingOnDependents; + private Task succ; + private final ExceptionalFunction, ?> fn; + + /** + * A task that combines two tasks and make sure pred runs before succ. + * + * @param fn a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred. + * @param relyingOnDependents true if this task chain will be broken when task pred fails. + */ + UniCompose(ExceptionalSupplier, ?> fn, boolean relyingOnDependents) { + this(result -> fn.get(), relyingOnDependents); + } + + /** + * A task that combines two tasks and make sure pred runs before succ. + * + * @param fn a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred. + * @param relyingOnDependents true if this task chain will be broken when task pred fails. + */ + UniCompose(ExceptionalFunction, ?> fn, boolean relyingOnDependents) { + this.fn = fn; + this.relyingOnDependents = relyingOnDependents; + + setSignificance(TaskSignificance.MODERATE); + setName(fn.toString()); + } + + @Override + public void execute() throws Exception { + setName(fn.toString()); + succ = fn.apply(Task.this.getResult()); + if (succ != null) + succ.storeTo(this::setResult); + } + + @Override + public Collection> getDependents() { + return Collections.singleton(Task.this); + } + + @Override + public Collection> getDependencies() { + return succ == null ? Collections.emptySet() : Collections.singleton(succ); + } + + @Override + public boolean isRelyingOnDependents() { + return relyingOnDependents; + } + } + + public class StageTask extends Task { + + @Override + public Collection> getDependents() { + return Collections.singleton(Task.this); + } + + @Override + public void execute() throws Exception { + setResult(Task.this.getResult()); + } + } + + public final class CountTask extends Task { + private final String countStage; + + private CountTask(String countStage) { + this.countStage = countStage; + setSignificance(TaskSignificance.MINOR); + } + + public String getCountStage() { + return countStage; + } + + @Override + public Collection> getDependents() { + return Collections.singleton(Task.this); + } + + @Override + public void execute() throws Exception { + setResult(Task.this.getResult()); + } + + @Override + public boolean doPostExecute() { + return true; + } + + @Override + public void postExecute() throws Exception { + notifyPropertiesChanged(); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskCompletableFuture.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskCompletableFuture.java new file mode 100644 index 00000000..9dd607d7 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskCompletableFuture.java @@ -0,0 +1,11 @@ +package com.tungsten.fclcore.task; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +public interface TaskCompletableFuture { + + CompletableFuture one(Task task); + + CompletableFuture all(Collection> tasks); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskEvent.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskEvent.java new file mode 100644 index 00000000..e23d5a24 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskEvent.java @@ -0,0 +1,24 @@ +package com.tungsten.fclcore.task; + +import com.tungsten.fclcore.event.Event; + +public class TaskEvent extends Event { + + private final Task task; + private final boolean failed; + + public TaskEvent(Object source, Task task, boolean failed) { + super(source); + this.task = task; + this.failed = failed; + } + + public Task getTask() { + return task; + } + + public boolean isFailed() { + return failed; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskExecutor.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskExecutor.java new file mode 100644 index 00000000..0deb9738 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskExecutor.java @@ -0,0 +1,55 @@ +package com.tungsten.fclcore.task; + +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class TaskExecutor { + protected final Task firstTask; + protected final List taskListeners = new ArrayList<>(); + protected final AtomicInteger totTask = new AtomicInteger(0); + protected final AtomicBoolean cancelled = new AtomicBoolean(false); + protected Exception exception; + private final List stages; + + public TaskExecutor(Task task) { + this.firstTask = task; + this.stages = task instanceof Task.StagesHintTask ? ((Task.StagesHintTask) task).getStages() : Collections.emptyList(); + } + + public void addTaskListener(TaskListener taskListener) { + taskListeners.add(taskListener); + } + + /** + * Reason why the task execution failed. + * If cancelled, null is returned. + */ + @Nullable + public Exception getException() { + return exception; + } + + public abstract TaskExecutor start(); + + public abstract boolean test(); + + /** + * Cancel the subscription ant interrupt all tasks. + */ + public abstract void cancel(); + + public boolean isCancelled() { + return cancelled.get(); + } + + public int getTaskCount() { + return totTask.get(); + } + + public List getStages() { + return stages; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskListener.java b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskListener.java new file mode 100644 index 00000000..045fb427 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/task/TaskListener.java @@ -0,0 +1,63 @@ +package com.tungsten.fclcore.task; + +import java.util.EventListener; + +public abstract class TaskListener implements EventListener { + + /** + * Executed when a Task execution chain starts. + */ + public void onStart() { + } + + /** + * Executed before the task's pre-execution and dependents execution. + * + * TaskState of this task is READY. + * + * @param task the task that gets ready. + */ + public void onReady(Task task) { + } + + /** + * Executed when the task's execution starts. + * + * TaskState of this task is RUNNING. + * + * @param task the task which is being run. + */ + public void onRunning(Task task) { + } + + /** + * Executed after the task's dependencies and post-execution finished. + * + * TaskState of the task is EXECUTED. + * + * @param task the task which finishes its work. + */ + public void onFinished(Task task) { + } + + /** + * Executed when an exception occurred during the task's execution. + * + * @param task the task which finishes its work. + */ + public void onFailed(Task task, Throwable throwable) { + onFinished(task); + } + + /** + * Executed when the task execution chain stopped. + * + * @param success true if no error occurred during task execution. + * @param executor the task executor with responsibility to the task execution. + */ + public void onStop(boolean success, TaskExecutor executor) { + } + + public void onPropertiesUpdate(Task task) { + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/CacheRepository.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/CacheRepository.java new file mode 100644 index 00000000..bdf12f43 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/CacheRepository.java @@ -0,0 +1,439 @@ +package com.tungsten.fclcore.util; + +import static com.tungsten.fclcore.util.Logging.LOG; + +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import com.tungsten.fclcore.util.function.ExceptionalSupplier; +import com.tungsten.fclcore.util.gson.JsonUtils; +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.io.IOUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.URLConnection; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.BiFunction; +import java.util.logging.Level; +import java.util.stream.Stream; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class CacheRepository { + private Path commonDirectory; + private Path cacheDirectory; + private Path indexFile; + private Map index; + private Map storages = new HashMap<>(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + public void changeDirectory(Path commonDir) { + commonDirectory = commonDir; + cacheDirectory = commonDir.resolve("cache"); + indexFile = cacheDirectory.resolve("etag.json"); + + lock.writeLock().lock(); + try { + for (Storage storage : storages.values()) { + storage.changeDirectory(cacheDirectory); + } + + if (Files.isRegularFile(indexFile)) { + ETagIndex raw = JsonUtils.GSON.fromJson(FileUtils.readText(indexFile.toFile()), ETagIndex.class); + if (raw == null) + index = new HashMap<>(); + else + index = joinETagIndexes(raw.eTag); + } else + index = new HashMap<>(); + } catch (IOException | JsonParseException e) { + LOG.log(Level.WARNING, "Unable to read index file", e); + index = new HashMap<>(); + } finally { + lock.writeLock().unlock(); + } + + } + + public Path getCommonDirectory() { + return commonDirectory; + } + + public Path getCacheDirectory() { + return cacheDirectory; + } + + public Storage getStorage(String key) { + lock.readLock().lock(); + try { + return storages.computeIfAbsent(key, Storage::new); + } finally { + lock.readLock().unlock(); + } + } + + protected Path getFile(String algorithm, String hash) { + return getCacheDirectory().resolve(algorithm).resolve(hash.substring(0, 2)).resolve(hash); + } + + protected boolean fileExists(String algorithm, String hash) { + if (hash == null) return false; + Path file = getFile(algorithm, hash); + if (Files.exists(file)) { + try { + return Hex.encodeHex(DigestUtils.digest(algorithm, file)).equalsIgnoreCase(hash); + } catch (IOException e) { + return false; + } + } else { + return false; + } + } + + public void tryCacheFile(Path path, String algorithm, String hash) throws IOException { + Path cache = getFile(algorithm, hash); + if (Files.isRegularFile(cache)) return; + FileUtils.copyFile(path.toFile(), cache.toFile()); + } + + public Path cacheFile(Path path, String algorithm, String hash) throws IOException { + Path cache = getFile(algorithm, hash); + FileUtils.copyFile(path.toFile(), cache.toFile()); + return cache; + } + + public Optional checkExistentFile(Path original, String algorithm, String hash) { + if (fileExists(algorithm, hash)) + return Optional.of(getFile(algorithm, hash)); + + if (original != null && Files.exists(original)) { + if (hash != null) { + try { + String checksum = Hex.encodeHex(DigestUtils.digest(algorithm, original)); + if (checksum.equalsIgnoreCase(hash)) + return Optional.of(restore(original, () -> cacheFile(original, algorithm, hash))); + } catch (IOException e) { + // we cannot check the hashcode. + } + } else { + return Optional.of(original); + } + } + + return Optional.empty(); + } + + protected Path restore(Path original, ExceptionalSupplier cacheSupplier) throws IOException { + Path cache = cacheSupplier.get(); + Files.delete(original); + Files.createLink(original, cache); + return cache; + } + + public Path getCachedRemoteFile(URLConnection conn) throws IOException { + String url = conn.getURL().toString(); + lock.readLock().lock(); + ETagItem eTagItem; + try { + eTagItem = index.get(url); + } finally { + lock.readLock().unlock(); + } + if (eTagItem == null) throw new IOException("Cannot find the URL"); + if (StringUtils.isBlank(eTagItem.hash) || !fileExists(SHA1, eTagItem.hash)) throw new FileNotFoundException(); + Path file = getFile(SHA1, eTagItem.hash); + if (Files.getLastModifiedTime(file).toMillis() != eTagItem.localLastModified) { + String hash = Hex.encodeHex(DigestUtils.digest(SHA1, file)); + if (!Objects.equals(hash, eTagItem.hash)) + throw new IOException("This file is modified"); + } + return file; + } + + public void removeRemoteEntry(URLConnection conn) { + String url = conn.getURL().toString(); + lock.readLock().lock(); + try { + index.remove(url); + } finally { + lock.readLock().unlock(); + } + } + + public void injectConnection(URLConnection conn) { + String url = conn.getURL().toString(); + lock.readLock().lock(); + ETagItem eTagItem; + try { + eTagItem = index.get(url); + } finally { + lock.readLock().unlock(); + } + if (eTagItem == null) return; + if (eTagItem.eTag != null) + conn.setRequestProperty("If-None-Match", eTagItem.eTag); + // if (eTagItem.getRemoteLastModified() != null) + // conn.setRequestProperty("If-Modified-Since", eTagItem.getRemoteLastModified()); + } + + public void cacheRemoteFile(Path downloaded, URLConnection conn) throws IOException { + cacheData(() -> { + String hash = Hex.encodeHex(DigestUtils.digest(SHA1, downloaded)); + Path cached = cacheFile(downloaded, SHA1, hash); + return new CacheResult(hash, cached); + }, conn); + } + + public void cacheText(String text, URLConnection conn) throws IOException { + cacheBytes(text.getBytes(UTF_8), conn); + } + + public void cacheBytes(byte[] bytes, URLConnection conn) throws IOException { + cacheData(() -> { + String hash = Hex.encodeHex(DigestUtils.digest(SHA1, bytes)); + Path cached = getFile(SHA1, hash); + FileUtils.writeBytes(cached.toFile(), bytes); + return new CacheResult(hash, cached); + }, conn); + } + + public synchronized void cacheData(ExceptionalSupplier cacheSupplier, URLConnection conn) throws IOException { + String eTag = conn.getHeaderField("ETag"); + if (eTag == null) return; + String url = conn.getURL().toString(); + String lastModified = conn.getHeaderField("Last-Modified"); + CacheResult cacheResult = cacheSupplier.get(); + ETagItem eTagItem = new ETagItem(url, eTag, cacheResult.hash, Files.getLastModifiedTime(cacheResult.cachedFile).toMillis(), lastModified); + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + index.compute(eTagItem.url, updateEntity(eTagItem)); + saveETagIndex(); + } finally { + writeLock.unlock(); + } + } + + private static class CacheResult { + public String hash; + public Path cachedFile; + + public CacheResult(String hash, Path cachedFile) { + this.hash = hash; + this.cachedFile = cachedFile; + } + } + + private BiFunction updateEntity(ETagItem newItem) { + return (key, oldItem) -> { + if (oldItem == null) { + return newItem; + } else if (oldItem.compareTo(newItem) < 0) { + Path cached = getFile(SHA1, oldItem.hash); + try { + Files.deleteIfExists(cached); + } catch (IOException e) { + LOG.log(Level.WARNING, "Cannot delete old file"); + } + return newItem; + } else { + return oldItem; + } + }; + } + + @SafeVarargs + private final Map joinETagIndexes(Collection... indexes) { + Map eTags = new ConcurrentHashMap<>(); + + Stream stream = Arrays.stream(indexes).filter(Objects::nonNull).map(Collection::stream) + .reduce(Stream.empty(), Stream::concat); + + stream.forEach(eTag -> { + eTags.compute(eTag.url, updateEntity(eTag)); + }); + + return eTags; + } + + public void saveETagIndex() throws IOException { + try (RandomAccessFile file = new RandomAccessFile(indexFile.toFile(), "rw"); FileChannel channel = file.getChannel()) { + FileLock lock = channel.lock(); + try { + ETagIndex indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), ETagIndex.class); + Map newIndex = joinETagIndexes(indexOnDisk == null ? null : indexOnDisk.eTag, index.values()); + channel.truncate(0); + OutputStream os = Channels.newOutputStream(channel); + ETagIndex writeTo = new ETagIndex(newIndex.values()); + IOUtils.write(JsonUtils.GSON.toJson(writeTo).getBytes(UTF_8), os); + this.index = newIndex; + } finally { + lock.release(); + } + } + } + + private class ETagIndex { + private final Collection eTag; + + public ETagIndex() { + this.eTag = new HashSet<>(); + } + + public ETagIndex(Collection eTags) { + this.eTag = new HashSet<>(eTags); + } + } + + private class ETagItem { + private final String url; + private final String eTag; + private final String hash; + @SerializedName("local") + private final long localLastModified; + @SerializedName("remote") + private final String remoteLastModified; + + /** + * For Gson. + */ + public ETagItem() { + this(null, null, null, 0, null); + } + + public ETagItem(String url, String eTag, String hash, long localLastModified, String remoteLastModified) { + this.url = url; + this.eTag = eTag; + this.hash = hash; + this.localLastModified = localLastModified; + this.remoteLastModified = remoteLastModified; + } + + public int compareTo(ETagItem other) { + if (!url.equals(other.url)) + throw new IllegalArgumentException(); + + ZonedDateTime thisTime = Lang.ignoringException(() -> ZonedDateTime.parse(remoteLastModified, DateTimeFormatter.RFC_1123_DATE_TIME), null); + ZonedDateTime otherTime = Lang.ignoringException(() -> ZonedDateTime.parse(other.remoteLastModified, DateTimeFormatter.RFC_1123_DATE_TIME), null); + if (thisTime == null && otherTime == null) return 0; + else if (thisTime == null) return -1; + else if (otherTime == null) return 1; + else return thisTime.compareTo(otherTime); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ETagItem eTagItem = (ETagItem) o; + return localLastModified == eTagItem.localLastModified && + Objects.equals(url, eTagItem.url) && + Objects.equals(eTag, eTagItem.eTag) && + Objects.equals(hash, eTagItem.hash) && + Objects.equals(remoteLastModified, eTagItem.remoteLastModified); + } + + @Override + public int hashCode() { + return Objects.hash(url, eTag, hash, localLastModified, remoteLastModified); + } + } + + /** + * Universal cache + */ + public static class Storage { + private final String name; + private Map storage; + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private Path indexFile; + + public Storage(String name) { + this.name = name; + } + + public Object getEntry(String key) { + lock.readLock().lock(); + try { + return storage.get(key); + } finally { + lock.readLock().unlock(); + } + } + + public void putEntry(String key, Object value) { + lock.writeLock().lock(); + try { + storage.put(key, value); + saveToFile(); + } finally { + lock.writeLock().unlock(); + } + } + + private void joinEntries(Map storage) { + this.storage.putAll(storage); + } + + private void changeDirectory(Path cacheDirectory) { + lock.writeLock().lock(); + try { + indexFile = cacheDirectory.resolve(name + ".json"); + if (Files.isRegularFile(indexFile)) { + joinEntries(JsonUtils.fromNonNullJson(FileUtils.readText(indexFile.toFile()), new TypeToken>() { + }.getType())); + } + } catch (IOException | JsonParseException e) { + LOG.log(Level.WARNING, "Unable to read storage {" + name + "} file"); + } finally { + lock.writeLock().unlock(); + } + } + + public void saveToFile() { + try (RandomAccessFile file = new RandomAccessFile(indexFile.toFile(), "rw"); FileChannel channel = file.getChannel()) { + FileLock lock = channel.lock(); + try { + Map indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), new TypeToken>() { + }.getType()); + if (indexOnDisk == null) indexOnDisk = new HashMap<>(); + indexOnDisk.putAll(storage); + channel.truncate(0); + OutputStream os = Channels.newOutputStream(channel); + IOUtils.write(JsonUtils.GSON.toJson(storage).getBytes(UTF_8), os); + this.storage = indexOnDisk; + } finally { + lock.release(); + } + } catch (IOException e) { + LOG.log(Level.WARNING, "Unable to write storage {" + name + "} file"); + } + } + } + + private static CacheRepository instance = new CacheRepository(); + + public static CacheRepository getInstance() { + return instance; + } + + public static void setInstance(CacheRepository instance) { + CacheRepository.instance = instance; + } + + public static final String SHA1 = "SHA-1"; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/DigestUtils.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/DigestUtils.java new file mode 100644 index 00000000..a55ec520 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/DigestUtils.java @@ -0,0 +1,61 @@ +package com.tungsten.fclcore.util; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public final class DigestUtils { + + private DigestUtils() { + } + + private static final int STREAM_BUFFER_LENGTH = 1024; + + public static MessageDigest getDigest(String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e); + } + } + + public static byte[] digest(String algorithm, String data) { + return digest(algorithm, data.getBytes(UTF_8)); + } + + public static byte[] digest(String algorithm, byte[] data) { + return getDigest(algorithm).digest(data); + } + + public static byte[] digest(String algorithm, Path path) throws IOException { + try (InputStream is = Files.newInputStream(path)) { + return digest(algorithm, is); + } + } + + public static byte[] digest(String algorithm, InputStream data) throws IOException { + return digest(getDigest(algorithm), data); + } + + public static byte[] digest(MessageDigest digest, InputStream data) throws IOException { + return updateDigest(digest, data).digest(); + } + + public static MessageDigest updateDigest(MessageDigest digest, InputStream data) throws IOException { + byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; + int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); + + while (read > -1) { + digest.update(buffer, 0, read); + read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); + } + + return digest; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/Hex.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/Hex.java new file mode 100644 index 00000000..dbddee28 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/Hex.java @@ -0,0 +1,50 @@ +package com.tungsten.fclcore.util; + +import java.io.IOException; + +public final class Hex { + + private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + public static byte[] decodeHex(String str) throws IOException { + char[] data = str.toCharArray(); + int len = data.length; + + if ((len & 0x1) != 0) + throw new IOException("Odd number of characters."); + + byte[] out = new byte[len >> 1]; + + int i = 0; + for (int j = 0; j < len; i++) { + int f = toDigit(data[j], j) << 4; + j++; + f |= toDigit(data[j], j); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + public static String encodeHex(byte[] data) { + int l = data.length; + char[] out = new char[l << 1]; + + int i = 0; + for (int j = 0; i < l; i++) { + out[(j++)] = DIGITS_LOWER[((0xF0 & data[i]) >>> 4)]; + out[(j++)] = DIGITS_LOWER[(0xF & data[i])]; + } + return new String(out); + } + + private static int toDigit(char ch, int index) throws IOException { + int digit = Character.digit(ch, 16); + if (digit == -1) + throw new IOException("Illegal hexadecimal character " + ch + " at index " + index); + return digit; + } + + private Hex() {} +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/Lang.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/Lang.java new file mode 100644 index 00000000..664174bb --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/Lang.java @@ -0,0 +1,278 @@ +package com.tungsten.fclcore.util; + +import com.tungsten.fclcore.util.function.ExceptionalBiConsumer; +import com.tungsten.fclcore.util.function.ExceptionalConsumer; +import com.tungsten.fclcore.util.function.ExceptionalFunction; +import com.tungsten.fclcore.util.function.ExceptionalRunnable; +import com.tungsten.fclcore.util.function.ExceptionalSupplier; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.*; +import java.util.stream.Stream; + +public final class Lang { + + private Lang() { + } + + /** + * Construct a mutable map by given key-value pairs. + * @param pairs entries in the new map + * @param the type of keys + * @param the type of values + * @return the map which contains data in {@code pairs}. + */ + @SafeVarargs + public static Map mapOf(Pair... pairs) { + return mapOf(Arrays.asList(pairs)); + } + + /** + * Construct a mutable map by given key-value pairs. + * @param pairs entries in the new map + * @param the type of keys + * @param the type of values + * @return the map which contains data in {@code pairs}. + */ + public static Map mapOf(Iterable> pairs) { + Map map = new LinkedHashMap<>(); + for (Pair pair : pairs) + map.put(pair.getKey(), pair.getValue()); + return map; + } + + @SafeVarargs + public static List immutableListOf(T... elements) { + return Collections.unmodifiableList(Arrays.asList(elements)); + } + + public static > T clamp(T min, T val, T max) { + if (val.compareTo(min) < 0) return min; + else if (val.compareTo(max) > 0) return max; + else return val; + } + + public static double clamp(double min, double val, double max) { + return Math.max(min, Math.min(val, max)); + } + + public static int clamp(int min, int val, int max) { + return Math.max(min, Math.min(val, max)); + } + + public static boolean test(ExceptionalRunnable r) { + try { + r.run(); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean test(ExceptionalSupplier r) { + try { + return r.get(); + } catch (Exception e) { + return false; + } + } + + public static T ignoringException(ExceptionalSupplier supplier) { + return ignoringException(supplier, null); + } + + public static T ignoringException(ExceptionalSupplier supplier, T defaultValue) { + try { + return supplier.get(); + } catch (Exception ignore) { + return defaultValue; + } + } + + public static T getOrDefault(List a, int index, T defaultValue) { + return index < 0 || index >= a.size() ? defaultValue : a.get(index); + } + + public static List removingDuplicates(List list) { + LinkedHashSet set = new LinkedHashSet<>(list.size()); + set.addAll(list); + return new ArrayList<>(set); + } + + /** + * Join two collections into one list. + * + * @param a one collection, to be joined. + * @param b another collection to be joined. + * @param the super type of elements in {@code a} and {@code b} + * @return the joint collection + */ + public static List merge(Collection a, Collection b) { + List result = new ArrayList<>(); + if (a != null) + result.addAll(a); + if (b != null) + result.addAll(b); + return result; + } + + public static List copyList(List list) { + return list == null ? null : list.isEmpty() ? null : new ArrayList<>(list); + } + + public static void executeDelayed(Runnable runnable, TimeUnit timeUnit, long timeout, boolean isDaemon) { + thread(() -> { + try { + timeUnit.sleep(timeout); + runnable.run(); + } catch (InterruptedException ignore) { + } + + }, null, isDaemon); + } + + /** + * Start a thread invoking {@code runnable} immediately. + * @param runnable code to run. + * @return the reference of the started thread + */ + public static Thread thread(Runnable runnable) { + return thread(runnable, null); + } + + /** + * Start a thread invoking {@code runnable} immediately. + * @param runnable code to run + * @param name the name of thread + * @return the reference of the started thread + */ + public static Thread thread(Runnable runnable, String name) { + return thread(runnable, name, false); + } + + /** + * Start a thread invoking {@code runnable} immediately. + * @param runnable code to run + * @param name the name of thread + * @param isDaemon true if thread will be terminated when only daemon threads are running. + * @return the reference of the started thread + */ + public static Thread thread(Runnable runnable, String name, boolean isDaemon) { + Thread thread = new Thread(runnable); + if (isDaemon) + thread.setDaemon(true); + if (name != null) + thread.setName(name); + thread.start(); + return thread; + } + + public static ThreadPoolExecutor threadPool(String name, boolean daemon, int threads, long timeout, TimeUnit timeunit) { + AtomicInteger counter = new AtomicInteger(1); + ThreadPoolExecutor pool = new ThreadPoolExecutor(threads, threads, timeout, timeunit, new LinkedBlockingQueue<>(), r -> { + Thread t = new Thread(r, name + "-" + counter.getAndIncrement()); + t.setDaemon(daemon); + return t; + }); + pool.allowCoreThreadTimeOut(true); + return pool; + } + + public static int parseInt(Object string, int defaultValue) { + try { + return Integer.parseInt(string.toString()); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public static Integer toIntOrNull(Object string) { + try { + if (string == null) return null; + return Integer.parseInt(string.toString()); + } catch (NumberFormatException e) { + return null; + } + } + + public static Double toDoubleOrNull(Object string) { + try { + if (string == null) return null; + return Double.parseDouble(string.toString()); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Find the first non-null reference in given list. + * @param t nullable references list. + * @param the type of nullable references + * @return the first non-null reference. + */ + @SafeVarargs + public static T nonNull(T... t) { + for (T a : t) if (a != null) return a; + return null; + } + + public static Iterable toIterable(Enumeration enumeration) { + if (enumeration == null) { + throw new NullPointerException(); + } + return () -> new Iterator() { + public boolean hasNext() { + return enumeration.hasMoreElements(); + } + + public T next() { + return enumeration.nextElement(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public static Iterable toIterable(Iterator iterator) { + return () -> iterator; + } + + private static Timer timer; + + public static synchronized Timer getTimer() { + if (timer == null) { + timer = new Timer(); + } + return timer; + } + + public static synchronized TimerTask setTimeout(Runnable runnable, long delayMs) { + TimerTask task = new TimerTask() { + @Override + public void run() { + runnable.run(); + } + }; + getTimer().schedule(task, delayMs); + return task; + } + + /** + * This is a useful function to prevent exceptions being eaten when using CompletableFuture. + * You can write: + * ... .exceptionally(handleUncaught); + */ + public static final Function handleUncaught = e -> { + handleUncaughtException(e); + return null; + }; + + public static R handleUncaughtException(Throwable e) { + Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); + return null; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/Logging.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/Logging.java new file mode 100644 index 00000000..a9bf9139 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/Logging.java @@ -0,0 +1,121 @@ +package com.tungsten.fclcore.util; + +import com.tungsten.fclcore.util.io.FileUtils; +import com.tungsten.fclcore.util.io.IOUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.text.MessageFormat; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.*; + +public final class Logging { + private Logging() { + } + + public static final Logger LOG = Logger.getLogger("FCL"); + private static final ByteArrayOutputStream storedLogs = new ByteArrayOutputStream(IOUtils.DEFAULT_BUFFER_SIZE); + + private static final ConcurrentMap forbiddenTokens = new ConcurrentHashMap<>(); + + public static void registerForbiddenToken(String token, String replacement) { + forbiddenTokens.put(token, replacement); + } + + public static void registerAccessToken(String accessToken) { + registerForbiddenToken(accessToken, ""); + } + + public static String filterForbiddenToken(String message) { + for (Map.Entry entry : forbiddenTokens.entrySet()) { + message = message.replace(entry.getKey(), entry.getValue()); + } + return message; + } + + public static void start(String logFolder) { + LOG.setLevel(Level.ALL); + LOG.setUseParentHandlers(false); + LOG.setFilter(record -> { + record.setMessage(filterForbiddenToken(record.getMessage())); + return true; + }); + + try { + FileUtils.makeDirectory(logFolder); + FileHandler fileHandler = new FileHandler(logFolder + "/fcl.log"); + fileHandler.setLevel(Level.FINEST); + fileHandler.setFormatter(DefaultFormatter.INSTANCE); + fileHandler.setEncoding("UTF-8"); + LOG.addHandler(fileHandler); + } catch (IOException e) { + System.err.println("Unable to create fcl.log, " + e.getMessage()); + } + + ConsoleHandler consoleHandler = new ConsoleHandler(); + consoleHandler.setFormatter(DefaultFormatter.INSTANCE); + consoleHandler.setLevel(Level.FINER); + LOG.addHandler(consoleHandler); + + StreamHandler streamHandler = new StreamHandler(storedLogs, DefaultFormatter.INSTANCE) { + @Override + public synchronized void publish(LogRecord record) { + super.publish(record); + flush(); + } + }; + try { + streamHandler.setEncoding("UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + streamHandler.setLevel(Level.ALL); + LOG.addHandler(streamHandler); + } + + public static void initForTest() { + LOG.setLevel(Level.ALL); + LOG.setUseParentHandlers(false); + + ConsoleHandler consoleHandler = new ConsoleHandler(); + consoleHandler.setFormatter(DefaultFormatter.INSTANCE); + consoleHandler.setLevel(Level.FINER); + LOG.addHandler(consoleHandler); + } + + public static byte[] getRawLogs() { + return storedLogs.toByteArray(); + } + + public static String getLogs() { + try { + return storedLogs.toString("UTF-8"); + } catch (UnsupportedEncodingException e) { + return e.getMessage(); + } + } + + private static final class DefaultFormatter extends Formatter { + + static final DefaultFormatter INSTANCE = new DefaultFormatter(); + private static final MessageFormat format = new MessageFormat("[{0,date,HH:mm:ss}] [{1}.{2}/{3}] {4}\n"); + + @Override + public String format(LogRecord record) { + String log = format.format(new Object[]{ + new Date(record.getMillis()), + record.getSourceClassName(), record.getSourceMethodName(), record.getLevel().getName(), + record.getMessage() + }, new StringBuffer(128), null).toString(); + if (record.getThrown() != null) + log += StringUtils.getStackTrace(record.getThrown()); + + return log; + } + + } +} \ No newline at end of file diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/Pair.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/Pair.java new file mode 100644 index 00000000..04e63bfc --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/Pair.java @@ -0,0 +1,65 @@ +package com.tungsten.fclcore.util; + +import java.util.Map; +import java.util.Objects; + +public final class Pair implements Map.Entry { + + public static Pair pair(K key, V value) { + return new Pair<>(key, value); + } + + private K key; + private V value; + + private Pair(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + public void setKey(K key) { + this.key = key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(V value) { + V original = this.value; + this.value = value; + return original; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 23 * hash + Objects.hashCode(this.key); + hash = 23 * hash + Objects.hashCode(this.value); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Pair other = (Pair) obj; + return Objects.equals(this.key, other.key) && Objects.equals(this.value, other.value); + } + + @Override + public String toString() { + return "(" + key + ", " + value + ")"; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/ReflectionHelper.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/ReflectionHelper.java new file mode 100644 index 00000000..107d2181 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/ReflectionHelper.java @@ -0,0 +1,45 @@ +package com.tungsten.fclcore.util; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.function.Predicate; + +public final class ReflectionHelper { + private ReflectionHelper() { + } + + private static Method accessible0; + + static { + try { + accessible0 = AccessibleObject.class.getDeclaredMethod("setAccessible0", boolean.class); + accessible0.setAccessible(true); + } catch (Throwable ignored) { + } + } + + public static void setAccessible(AccessibleObject obj) throws InvocationTargetException, IllegalAccessException { + accessible0.invoke(obj, true); + } + + /** + * Get caller, this method is caller sensitive. + * + * @param packageFilter returns false if we consider the given package is internal calls, not the caller + * @return the caller, method name, source file, line number + */ + public static StackTraceElement getCaller(String packageFilter) { + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + // element[0] is Thread.currentThread().getStackTrace() + // element[1] is ReflectionHelper.getCaller(packageFilter) + // so element[2] is caller of this method. + StackTraceElement caller = elements[2]; + for (int i = 3; i < elements.length; ++i) { + if (!StringUtils.substringBeforeLast(elements[i].getClassName(), '.').equals(packageFilter) && + !caller.getClassName().equals(elements[i].getClassName())) + return elements[i]; + } + return caller; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/SimpleMultimap.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/SimpleMultimap.java new file mode 100644 index 00000000..280133a3 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/SimpleMultimap.java @@ -0,0 +1,84 @@ +package com.tungsten.fclcore.util; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +/** + * A simple implementation of Multimap. + * Just a combination of map and set. + */ +public final class SimpleMultimap { + + private final Map> map; + private final Supplier> valuer; + + public SimpleMultimap(Supplier>> mapper, Supplier> valuer) { + this.map = mapper.get(); + this.valuer = valuer; + } + + public int size() { + return values().size(); + } + + public Set keys() { + return map.keySet(); + } + + public Collection values() { + Collection res = valuer.get(); + for (Map.Entry> entry : map.entrySet()) + res.addAll(entry.getValue()); + return res; + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean containsKey(K key) { + return map.containsKey(key) && !map.get(key).isEmpty(); + } + + public Collection get(K key) { + return map.computeIfAbsent(key, any -> valuer.get()); + } + + public void put(K key, V value) { + Collection set = get(key); + set.add(value); + } + + public void putAll(K key, Collection value) { + Collection set = get(key); + set.addAll(value); + } + + public Collection removeKey(K key) { + return map.remove(key); + } + + public boolean removeValue(V value) { + boolean flag = false; + for (Collection c : map.values()) + flag |= c.remove(value); + return flag; + } + + public boolean removeValue(K key, V value) { + return get(key).remove(value); + } + + public void clear() { + map.clear(); + } + + public void clear(K key) { + if (map.containsKey(key)) + map.get(key).clear(); + else + map.put(key, valuer.get()); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/StringUtils.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/StringUtils.java new file mode 100644 index 00000000..dfa5bf51 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/StringUtils.java @@ -0,0 +1,261 @@ +package com.tungsten.fclcore.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Pattern; + +public final class StringUtils { + + private StringUtils() { + } + + public static String getStackTrace(Throwable throwable) { + StringWriter stringWriter = new StringWriter(512); + try (PrintWriter printWriter = new PrintWriter(stringWriter)) { + throwable.printStackTrace(printWriter); + } + return stringWriter.toString(); + } + + public static boolean isBlank(String str) { + return str == null || str.trim().isEmpty(); + } + + public static boolean isNotBlank(String str) { + return !isBlank(str); + } + + public static String substringBeforeLast(String str, char delimiter) { + return substringBeforeLast(str, delimiter, str); + } + + public static String substringBeforeLast(String str, char delimiter, String missingDelimiterValue) { + int index = str.lastIndexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(0, index); + } + + public static String substringBeforeLast(String str, String delimiter) { + return substringBeforeLast(str, delimiter, str); + } + + public static String substringBeforeLast(String str, String delimiter, String missingDelimiterValue) { + int index = str.lastIndexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(0, index); + } + + public static String substringBefore(String str, char delimiter) { + return substringBefore(str, delimiter, str); + } + + public static String substringBefore(String str, char delimiter, String missingDelimiterValue) { + int index = str.indexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(0, index); + } + + public static String substringBefore(String str, String delimiter) { + return substringBefore(str, delimiter, str); + } + + public static String substringBefore(String str, String delimiter, String missingDelimiterValue) { + int index = str.indexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(0, index); + } + + public static String substringAfterLast(String str, char delimiter) { + return substringAfterLast(str, delimiter, ""); + } + + public static String substringAfterLast(String str, char delimiter, String missingDelimiterValue) { + int index = str.lastIndexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(index + 1); + } + + public static String substringAfterLast(String str, String delimiter) { + return substringAfterLast(str, delimiter, ""); + } + + public static String substringAfterLast(String str, String delimiter, String missingDelimiterValue) { + int index = str.lastIndexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(index + delimiter.length()); + } + + public static String substringAfter(String str, char delimiter) { + return substringAfter(str, delimiter, ""); + } + + public static String substringAfter(String str, char delimiter, String missingDelimiterValue) { + int index = str.indexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(index + 1); + } + + public static String substringAfter(String str, String delimiter) { + return substringAfter(str, delimiter, ""); + } + + public static String substringAfter(String str, String delimiter, String missingDelimiterValue) { + int index = str.indexOf(delimiter); + return index == -1 ? missingDelimiterValue : str.substring(index + delimiter.length()); + } + + public static boolean isSurrounded(String str, String prefix, String suffix) { + return str.startsWith(prefix) && str.endsWith(suffix); + } + + public static String removeSurrounding(String str, String delimiter) { + return removeSurrounding(str, delimiter, delimiter); + } + + public static String removeSurrounding(String str, String prefix, String suffix) { + if ((str.length() >= prefix.length() + suffix.length()) && str.startsWith(prefix) && str.endsWith(suffix)) + return str.substring(prefix.length(), str.length() - suffix.length()); + else + return str; + } + + public static String addPrefix(String str, String prefix) { + if (str.startsWith(prefix)) + return str; + else + return prefix + str; + } + + public static String addSuffix(String str, String suffix) { + if (str.endsWith(suffix)) + return str; + else + return str + suffix; + } + + public static String removePrefix(String str, String... prefixes) { + for (String prefix : prefixes) + if (str.startsWith(prefix)) + return str.substring(prefix.length()); + return str; + } + + /** + * Remove one suffix of the suffixes of the string. + */ + public static String removeSuffix(String str, String... suffixes) { + for (String suffix : suffixes) + if (str.endsWith(suffix)) + return str.substring(0, str.length() - suffix.length()); + return str; + } + + public static boolean containsOne(Collection patterns, String... targets) { + for (String pattern : patterns) + for (String target : targets) + if (pattern.toLowerCase().contains(target.toLowerCase())) + return true; + return false; + } + + public static boolean containsOne(String pattern, String... targets) { + for (String target : targets) + if (pattern.toLowerCase().contains(target.toLowerCase())) + return true; + return false; + } + + public static boolean containsOne(String pattern, char... targets) { + for (char target : targets) + if (pattern.toLowerCase().indexOf(Character.toLowerCase(target)) >= 0) + return true; + return false; + } + + public static List tokenize(String str) { + if (str == null) + return new ArrayList<>(); + else + return tokenize(str, " \t\n\r\f"); + } + + public static List tokenize(String str, String delim) { + ArrayList result = new ArrayList<>(); + StringTokenizer tokenizer = new StringTokenizer(str, delim); + while (tokenizer.hasMoreTokens()) { + delim = tokenizer.nextToken(); + result.add(delim); + } + + return result; + } + + public static String parseColorEscapes(String original) { + return original.replaceAll("\u00A7\\d", ""); + } + + public static String parseEscapeSequence(String str) { + StringBuilder builder = new StringBuilder(); + boolean inEscape = false; + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + if (ch == '\033') { + inEscape = true; + } + if (!inEscape) { + builder.append(ch); + } + if (inEscape && ch == 'm') { + inEscape = false; + } + } + return builder.toString(); + } + + public static String repeats(char ch, int repeat) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < repeat; i++) { + result.append(ch); + } + return result.toString(); + } + + public static boolean isASCII(CharSequence cs) { + return US_ASCII_ENCODER.canEncode(cs); + } + + /** + * Class for computing the longest common subsequence between strings. + */ + public static class LongestCommonSubsequence { + // We reuse dynamic programming storage array here to reduce allocations. + private final int[][] f; + private final int maxLengthA; + private final int maxLengthB; + + public LongestCommonSubsequence(int maxLengthA, int maxLengthB) { + this.maxLengthA = maxLengthA; + this.maxLengthB = maxLengthB; + f = new int[maxLengthA + 1][]; + for (int i = 0; i <= maxLengthA; i++) { + f[i] = new int[maxLengthB + 1]; + } + } + + public int calc(CharSequence a, CharSequence b) { + if (a.length() > maxLengthA || b.length() > maxLengthB) { + throw new IllegalArgumentException("Too large length"); + } + for (int i = 1; i <= a.length(); i++) { + for (int j = 1; j <= b.length(); j++) { + if (a.charAt(i - 1) == b.charAt(j - 1)) { + f[i][j] = 1 + f[i - 1][j - 1]; + } else { + f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]); + } + } + } + return f[a.length()][b.length()]; + } + } + + public static final Pattern CHINESE_PATTERN = Pattern.compile("[\\u4e00-\\u9fa5]"); + + public static final CharsetEncoder US_ASCII_ENCODER = StandardCharsets.US_ASCII.newEncoder(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/ToStringBuilder.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/ToStringBuilder.java new file mode 100644 index 00000000..49a0776b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/ToStringBuilder.java @@ -0,0 +1,24 @@ +package com.tungsten.fclcore.util; + +public class ToStringBuilder { + + private final StringBuilder stringBuilder; + private boolean first = true; + + public ToStringBuilder(Object object) { + stringBuilder = new StringBuilder(object.getClass().getSimpleName()).append(" ["); + } + + public ToStringBuilder append(String name, Object content) { + if (!first) + stringBuilder.append(", "); + first = false; + stringBuilder.append(name).append('=').append(content); + return this; + } + + @Override + public String toString() { + return stringBuilder.toString() + "]"; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalBiConsumer.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalBiConsumer.java new file mode 100644 index 00000000..8ed4ce4e --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalBiConsumer.java @@ -0,0 +1,16 @@ +package com.tungsten.fclcore.util.function; + +import static java.util.Objects.requireNonNull; + +public interface ExceptionalBiConsumer { + void accept(T t, U u) throws E; + + default ExceptionalBiConsumer andThen(ExceptionalBiConsumer after) { + requireNonNull(after); + + return (l, r) -> { + this.accept(l, r); + after.accept(l, r); + }; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalBiFunction.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalBiFunction.java new file mode 100644 index 00000000..28b5685b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalBiFunction.java @@ -0,0 +1,12 @@ +package com.tungsten.fclcore.util.function; + +import static java.util.Objects.requireNonNull; + +public interface ExceptionalBiFunction { + R apply(T t, U u) throws E; + + default ExceptionalBiFunction andThen(ExceptionalFunction after) { + requireNonNull(after); + return (t, u) -> after.apply(apply(t, u)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalConsumer.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalConsumer.java new file mode 100644 index 00000000..84af2b50 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalConsumer.java @@ -0,0 +1,24 @@ +package com.tungsten.fclcore.util.function; + +public interface ExceptionalConsumer { + void accept(T t) throws E; + + static ExceptionalConsumer fromRunnable(ExceptionalRunnable runnable) { + return new ExceptionalConsumer() { + @Override + public void accept(T o) throws E { + runnable.run(); + } + + @Override + public String toString() { + return runnable.toString(); + } + }; + } + + static ExceptionalConsumer empty() { + return s -> { + }; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalFunction.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalFunction.java new file mode 100644 index 00000000..7ae2c2d5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalFunction.java @@ -0,0 +1,9 @@ +package com.tungsten.fclcore.util.function; + +public interface ExceptionalFunction { + R apply(T t) throws E; + + static ExceptionalFunction identity() { + return t -> t; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalPredicate.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalPredicate.java new file mode 100644 index 00000000..4c28c781 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalPredicate.java @@ -0,0 +1,5 @@ +package com.tungsten.fclcore.util.function; + +public interface ExceptionalPredicate { + boolean test(T t) throws E; +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalRunnable.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalRunnable.java new file mode 100644 index 00000000..7dbff446 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalRunnable.java @@ -0,0 +1,16 @@ +package com.tungsten.fclcore.util.function; + +import java.util.concurrent.Callable; + +public interface ExceptionalRunnable { + + void run() throws E; + + default Callable toCallable() { + return () -> { + run(); + return null; + }; + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalSupplier.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalSupplier.java new file mode 100644 index 00000000..4c9671ea --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/function/ExceptionalSupplier.java @@ -0,0 +1,11 @@ +package com.tungsten.fclcore.util.function; + +import java.util.concurrent.Callable; + +public interface ExceptionalSupplier { + R get() throws E; + + default Callable toCallable() { + return this::get; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/DateTypeAdapter.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/DateTypeAdapter.java new file mode 100644 index 00000000..feca4e79 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/DateTypeAdapter.java @@ -0,0 +1,85 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.util.Date; +import java.util.Locale; + +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME; + +public final class DateTypeAdapter implements JsonSerializer, JsonDeserializer { + + public static final DateTypeAdapter INSTANCE = new DateTypeAdapter(); + + private DateTypeAdapter() { + } + + @Override + public JsonElement serialize(Date t, Type type, JsonSerializationContext jsc) { + synchronized (EN_US_FORMAT) { + return new JsonPrimitive(serializeToString(t)); + } + } + + @Override + public Date deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + if (!(json instanceof JsonPrimitive)) + throw new JsonParseException("The date should be a string value"); + else { + Date date = deserializeToDate(json.getAsString()); + if (type == Date.class) + return date; + else + throw new IllegalArgumentException(this.getClass().toString() + " cannot be deserialized to " + type); + } + } + + public static final DateFormat EN_US_FORMAT = DateFormat.getDateTimeInstance(2, 2, Locale.US); + public static final DateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + + public static final DateTimeFormatter ISO_DATE_TIME = new DateTimeFormatterBuilder() + .append(ISO_LOCAL_DATE_TIME) + .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd() + .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd() + .optionalStart().appendOffset("+HH", "Z").optionalEnd() + .optionalStart().appendOffsetId().optionalEnd() + .toFormatter(); + + public static Date deserializeToDate(String string) { + synchronized (EN_US_FORMAT) { + try { + return EN_US_FORMAT.parse(string); + } catch (ParseException ex1) { + try { + ZonedDateTime zonedDateTime = ZonedDateTime.parse(string, ISO_DATE_TIME); + return Date.from(zonedDateTime.toInstant()); + } catch (DateTimeParseException e) { + try { + LocalDateTime localDateTime = LocalDateTime.parse(string, ISO_LOCAL_DATE_TIME); + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } catch (DateTimeParseException e2) { + throw new JsonParseException("Invalid date: " + string, e); + } + } + } + } + } + + public static String serializeToString(Date date) { + synchronized (EN_US_FORMAT) { + String result = ISO_8601_FORMAT.format(date); + return result.substring(0, 22) + ":" + result.substring(22); + } + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/EnumOrdinalDeserializer.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/EnumOrdinalDeserializer.java new file mode 100644 index 00000000..c9a4cce2 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/EnumOrdinalDeserializer.java @@ -0,0 +1,41 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +public class EnumOrdinalDeserializer> implements JsonDeserializer { + + private Map mapping = new HashMap<>(); + + public EnumOrdinalDeserializer(Class enumClass) { + for (T constant : enumClass.getEnumConstants()) { + mapping.put(String.valueOf(constant.ordinal()), constant); + String name = constant.name(); + try { + SerializedName annotation = enumClass.getField(name).getAnnotation(SerializedName.class); + if (annotation != null) { + name = annotation.value(); + for (String alternate : annotation.alternate()) { + mapping.put(alternate, constant); + } + } + } catch (NoSuchFieldException e) { + throw new AssertionError(e); + } + mapping.put(name, constant); + } + } + + @Override + public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return mapping.get(json.getAsString()); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/FileTypeAdapter.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/FileTypeAdapter.java new file mode 100644 index 00000000..05bc725e --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/FileTypeAdapter.java @@ -0,0 +1,31 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.*; + +import java.io.File; +import java.lang.reflect.Type; + +public final class FileTypeAdapter implements JsonSerializer, JsonDeserializer { + + public static final FileTypeAdapter INSTANCE = new FileTypeAdapter(); + + private FileTypeAdapter() { + } + + @Override + public JsonElement serialize(File t, Type type, JsonSerializationContext jsc) { + if (t == null) + return JsonNull.INSTANCE; + else + return new JsonPrimitive(t.getPath()); + } + + @Override + public File deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException { + if (je == null) + return null; + else + return new File(je.getAsString()); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/GsonSerializerHelper.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/GsonSerializerHelper.java new file mode 100644 index 00000000..cad9d8ee --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/GsonSerializerHelper.java @@ -0,0 +1,15 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializer; + +public abstract class GsonSerializerHelper implements JsonSerializer, JsonDeserializer { + + protected static void add(JsonObject object, String property, JsonElement value) { + if (value == null) return; + object.add(property, value); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/InstantTypeAdapter.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/InstantTypeAdapter.java new file mode 100644 index 00000000..478a5ca5 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/InstantTypeAdapter.java @@ -0,0 +1,34 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +public final class InstantTypeAdapter implements JsonSerializer, JsonDeserializer { + public static final InstantTypeAdapter INSTANCE = new InstantTypeAdapter(); + + private InstantTypeAdapter() { + } + + @Override + public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (!(json instanceof JsonPrimitive)) { + throw new JsonParseException("The instant should be a string value"); + } else { + Instant instant = Instant.parse(json.getAsString()); + if (typeOfT == Instant.class) { + return instant; + } else { + throw new IllegalArgumentException(this.getClass() + " cannot be deserialized to " + typeOfT); + } + } + } + + @Override + public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault()).format(src)); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonMap.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonMap.java new file mode 100644 index 00000000..13f86546 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonMap.java @@ -0,0 +1,34 @@ +package com.tungsten.fclcore.util.gson; + +import java.util.HashMap; +import java.util.Map; + +/** + * To resolve JsonParseException: duplicate key: null + * By skipping inserting data with key null + * @param + * @param + */ +public class JsonMap extends HashMap { + public JsonMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public JsonMap(int initialCapacity) { + super(initialCapacity); + } + + public JsonMap() { + super(); + } + + public JsonMap(Map m) { + super(m); + } + + @Override + public V put(K key, V value) { + if (key == null) return null; + return super.put(key, value); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonSubtype.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonSubtype.java new file mode 100644 index 00000000..6e5bd413 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonSubtype.java @@ -0,0 +1,7 @@ +package com.tungsten.fclcore.util.gson; + +public @interface JsonSubtype { + Class clazz(); + + String name(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonType.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonType.java new file mode 100644 index 00000000..1a98b510 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonType.java @@ -0,0 +1,14 @@ +package com.tungsten.fclcore.util.gson; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface JsonType { + String property(); + + JsonSubtype[] subtypes(); +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonTypeAdapterFactory.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonTypeAdapterFactory.java new file mode 100644 index 00000000..43fdc1af --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonTypeAdapterFactory.java @@ -0,0 +1,114 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.*; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class JsonTypeAdapterFactory implements TypeAdapterFactory { + + public static final JsonTypeAdapterFactory INSTANCE = new JsonTypeAdapterFactory(); + + private TypeAdapter createForJsonType(Gson gson, TypeToken type) { + Class rawType = type.getRawType(); + JsonType jsonType = rawType.getDeclaredAnnotation(JsonType.class); + if (jsonType == null) + return null; + JsonSubtype[] subtypes = jsonType.subtypes(); + Map> labelTypeAdapterMap = new HashMap<>(); + Map, TypeAdapter> classTypeAdapterMap = new HashMap<>(); + Map, JsonSubtype> classJsonSubtypeMap = new HashMap<>(); + for (JsonSubtype subtype : subtypes) { + TypeAdapter typeAdapter = gson.getDelegateAdapter(this, TypeToken.get(subtype.clazz())); + labelTypeAdapterMap.put(subtype.name(), typeAdapter); + classTypeAdapterMap.put(subtype.clazz(), typeAdapter); + classJsonSubtypeMap.put(subtype.clazz(), subtype); + } + + return new TypeAdapter() { + @Override + public void write(JsonWriter out, T value) throws IOException { + Class type = value.getClass(); + @SuppressWarnings("unchecked") + TypeAdapter delegate = (TypeAdapter) classTypeAdapterMap.get(type); + if (delegate == null) { + throw new JsonParseException("Cannot serialize " + type.getName() + ". Please check your @JsonType configuration"); + } + JsonSubtype subtype = classJsonSubtypeMap.get(type); + JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); + if (jsonObject.has(jsonType.property())) { + throw new JsonParseException("Cannot serialize " + type.getName() + ". Because it has already defined a field named '" + jsonType.property() + "'"); + } + jsonObject.add(jsonType.property(), new JsonPrimitive(subtype.name())); + Streams.write(jsonObject, out); + } + + @Override + public T read(JsonReader in) { + JsonElement jsonElement = Streams.parse(in); + JsonElement typeLabelElement = jsonElement.getAsJsonObject().get(jsonType.property()); + if (typeLabelElement == null) { + throw new JsonParseException("Cannot deserialize " + type + ". Because it does not define a field named '" + jsonType.property() + "'"); + } + String typeLabel = typeLabelElement.getAsString(); + @SuppressWarnings("unchecked") + TypeAdapter delegate = (TypeAdapter) labelTypeAdapterMap.get(typeLabel); + if (delegate == null) { + throw new JsonParseException("Cannot deserialize " + type + " with subtype '" + typeLabel + "'"); + } + + return delegate.fromJsonTree(jsonElement); + } + }; + } + + private TypeAdapter createForJsonSubtype(Gson gson, TypeToken type) { + Class rawType = type.getRawType(); + if (rawType.getSuperclass() == null) return null; + JsonType jsonType = rawType.getSuperclass().getDeclaredAnnotation(JsonType.class); + if (jsonType == null) + return null; + JsonSubtype jsonSubtype = null; + for (JsonSubtype subtype : jsonType.subtypes()) { + if (subtype.clazz() == rawType) { + jsonSubtype = subtype; + } + } + if (jsonSubtype == null) + return null; + final JsonSubtype subtype = jsonSubtype; + + TypeAdapter delegate = gson.getDelegateAdapter(this, type); + + return new TypeAdapter() { + @Override + public void write(JsonWriter out, T value) throws IOException { + Class type = value.getClass(); + JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); + if (jsonObject.has(jsonType.property())) { + throw new JsonParseException("Cannot serialize " + type.getName() + ". Because it has already defined a field named '" + jsonType.property() + "'"); + } + jsonObject.add(jsonType.property(), new JsonPrimitive(subtype.name())); + Streams.write(jsonObject, out); + } + + @Override + public T read(JsonReader in) throws IOException { + return delegate.read(in); + } + }; + } + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + TypeAdapter typeAdapter = createForJsonType(gson, type); + if (typeAdapter == null) + typeAdapter = createForJsonSubtype(gson, type); + return typeAdapter; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonUtils.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonUtils.java new file mode 100644 index 00000000..82901559 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/JsonUtils.java @@ -0,0 +1,69 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; + +import java.io.File; +import java.lang.reflect.Type; +import java.time.Instant; +import java.util.Date; +import java.util.UUID; + +public final class JsonUtils { + + public static final Gson GSON = defaultGsonBuilder().create(); + + public static final Gson UGLY_GSON = new GsonBuilder() + .registerTypeAdapterFactory(JsonTypeAdapterFactory.INSTANCE) + .registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE) + .registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE) + .create(); + + private JsonUtils() { + } + + public static T fromNonNullJson(String json, Class classOfT) throws JsonParseException { + T parsed = GSON.fromJson(json, classOfT); + if (parsed == null) + throw new JsonParseException("Json object cannot be null."); + return parsed; + } + + public static T fromNonNullJson(String json, Type type) throws JsonParseException { + T parsed = GSON.fromJson(json, type); + if (parsed == null) + throw new JsonParseException("Json object cannot be null."); + return parsed; + } + + public static T fromMaybeMalformedJson(String json, Class classOfT) throws JsonParseException { + try { + return GSON.fromJson(json, classOfT); + } catch (JsonSyntaxException e) { + return null; + } + } + + public static T fromMaybeMalformedJson(String json, Type type) throws JsonParseException { + try { + return GSON.fromJson(json, type); + } catch (JsonSyntaxException e) { + return null; + } + } + + public static GsonBuilder defaultGsonBuilder() { + return new GsonBuilder() + .enableComplexMapKeySerialization() + .setPrettyPrinting() + .registerTypeAdapter(Instant.class, InstantTypeAdapter.INSTANCE) + .registerTypeAdapter(Date.class, DateTypeAdapter.INSTANCE) + .registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE) + .registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE) + .registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE) + .registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE) + .registerTypeAdapterFactory(JsonTypeAdapterFactory.INSTANCE); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/LowerCaseEnumTypeAdapterFactory.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/LowerCaseEnumTypeAdapterFactory.java new file mode 100644 index 00000000..cb86551f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/LowerCaseEnumTypeAdapterFactory.java @@ -0,0 +1,53 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; + +public final class LowerCaseEnumTypeAdapterFactory implements TypeAdapterFactory { + + public static final LowerCaseEnumTypeAdapterFactory INSTANCE = new LowerCaseEnumTypeAdapterFactory(); + + @Override + @SuppressWarnings("unchecked") + public TypeAdapter create(Gson gson, TypeToken tt) { + Class rawType = tt.getRawType(); + if (!rawType.isEnum()) + return null; + + HashMap lowercaseToConstant = new HashMap<>(); + for (Object constant : rawType.getEnumConstants()) + lowercaseToConstant.put(toLowercase(constant), (T) constant); + + return new TypeAdapter() { + @Override + public void write(JsonWriter writer, T t) throws IOException { + if (t == null) + writer.nullValue(); + else + writer.value(toLowercase(t)); + } + + @Override + public T read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return lowercaseToConstant.get(reader.nextString().toLowerCase()); + } + }; + } + + private static String toLowercase(Object o) { + return o.toString().toLowerCase(Locale.US); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/TolerableValidationException.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/TolerableValidationException.java new file mode 100644 index 00000000..a3c175b6 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/TolerableValidationException.java @@ -0,0 +1,12 @@ +package com.tungsten.fclcore.util.gson; + +/** + * This exception gets thrown by implementations of {@link Validation#validate()} if you want to replace + * the nullable JSON-parsed object which does not satisfy the constraint with null value. + * @see Validation + */ +public final class TolerableValidationException extends Exception { + + public TolerableValidationException() { + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/UUIDTypeAdapter.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/UUIDTypeAdapter.java new file mode 100644 index 00000000..249cc1ed --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/UUIDTypeAdapter.java @@ -0,0 +1,37 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.UUID; + +public final class UUIDTypeAdapter extends TypeAdapter { + + public static final UUIDTypeAdapter INSTANCE = new UUIDTypeAdapter(); + + @Override + public void write(JsonWriter writer, UUID value) throws IOException { + writer.value(value == null ? null : fromUUID(value)); + } + + @Override + public UUID read(JsonReader reader) throws IOException { + try { + return fromString(reader.nextString()); + } catch (IllegalArgumentException e) { + throw new JsonParseException("UUID malformed"); + } + } + + public static String fromUUID(UUID value) { + return value.toString().replace("-", ""); + } + + public static UUID fromString(String input) { + return UUID.fromString(input.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); + } + +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/Validation.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/Validation.java new file mode 100644 index 00000000..953208da --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/Validation.java @@ -0,0 +1,24 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.JsonParseException; + +public interface Validation { + + /** + * 1. Check some non-null fields and; + * 2. Check strings and; + * 3. Check generic type of lists <T> and maps <K, V> are correct. + * + * Will be called immediately after initialization. + * Throw an exception when values are malformed. + * + * @throws JsonParseException if fields are filled in wrong format or wrong type. + * @throws TolerableValidationException if we want to replace this object with null (i.e. the object does not fulfill the constraints). + */ + void validate() throws JsonParseException, TolerableValidationException; + + static void requireNonNull(Object object, String message) throws JsonParseException { + if (object == null) + throw new JsonParseException(message); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/ValidationTypeAdapterFactory.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/ValidationTypeAdapterFactory.java new file mode 100644 index 00000000..129162b1 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/gson/ValidationTypeAdapterFactory.java @@ -0,0 +1,48 @@ +package com.tungsten.fclcore.util.gson; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; + +public final class ValidationTypeAdapterFactory implements TypeAdapterFactory { + + public static final ValidationTypeAdapterFactory INSTANCE = new ValidationTypeAdapterFactory(); + + @Override + public TypeAdapter create(Gson gson, TypeToken tt) { + final TypeAdapter delegate = gson.getDelegateAdapter(this, tt); + return new TypeAdapter() { + @Override + public void write(JsonWriter writer, T t) throws IOException { + if (t instanceof Validation) { + try { + ((Validation) t).validate(); + } catch (TolerableValidationException e) { + delegate.write(writer, null); + return; + } + } + + delegate.write(writer, t); + } + + @Override + public T read(JsonReader reader) throws IOException { + T t = delegate.read(reader); + if (t instanceof Validation) { + try { + ((Validation) t).validate(); + } catch (TolerableValidationException e) { + return null; + } + } + return t; + } + }; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/io/ChecksumMismatchException.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/ChecksumMismatchException.java new file mode 100644 index 00000000..5eb4f17b --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/ChecksumMismatchException.java @@ -0,0 +1,41 @@ +package com.tungsten.fclcore.util.io; + +import com.tungsten.fclcore.download.ArtifactMalformedException; +import com.tungsten.fclcore.util.DigestUtils; +import com.tungsten.fclcore.util.Hex; + +import java.io.IOException; +import java.nio.file.Path; + +public class ChecksumMismatchException extends ArtifactMalformedException { + + private final String algorithm; + private final String expectedChecksum; + private final String actualChecksum; + + public ChecksumMismatchException(String algorithm, String expectedChecksum, String actualChecksum) { + super("Incorrect checksum (" + algorithm + "), expected: " + expectedChecksum + ", actual: " + actualChecksum); + this.algorithm = algorithm; + this.expectedChecksum = expectedChecksum; + this.actualChecksum = actualChecksum; + } + + public String getAlgorithm() { + return algorithm; + } + + public String getExpectedChecksum() { + return expectedChecksum; + } + + public String getActualChecksum() { + return actualChecksum; + } + + public static void verifyChecksum(Path file, String algorithm, String expectedChecksum) throws IOException { + String checksum = Hex.encodeHex(DigestUtils.digest(algorithm, file)); + if (!checksum.equalsIgnoreCase(expectedChecksum)) { + throw new ChecksumMismatchException(algorithm, expectedChecksum, checksum); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/io/FileUtils.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/FileUtils.java new file mode 100644 index 00000000..d81e1559 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/FileUtils.java @@ -0,0 +1,240 @@ +package com.tungsten.fclcore.util.io; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.tungsten.fclcore.util.Lang; +import com.tungsten.fclcore.util.StringUtils; + +public final class FileUtils { + + private FileUtils() { + } + + public static String getNameWithoutExtension(String fileName) { + return StringUtils.substringBeforeLast(fileName, '.'); + } + + public static String getNameWithoutExtension(File file) { + return StringUtils.substringBeforeLast(file.getName(), '.'); + } + + public static String getExtension(File file) { + return StringUtils.substringAfterLast(file.getName(), '.'); + } + + public static String normalizePath(String path) { + return StringUtils.addPrefix(StringUtils.removeSuffix(path, "/", "\\"), "/"); + } + + public static String getName(String path) { + return StringUtils.removeSuffix(new File(path).getName(), "/", "\\"); + } + + public static String readText(String file) throws IOException { + return readText(new File(file), UTF_8); + } + + public static String readText(File file) throws IOException { + return readText(file, UTF_8); + } + + public static String readText(File file, Charset charset) throws IOException { + FileInputStream inputStream = new FileInputStream(file); + byte[] bytes = new byte [inputStream.available()]; + inputStream.read(bytes); + inputStream.close(); + return new String(bytes, charset); + } + + public static void writeText(String file, String text) throws IOException { + writeBytes(new File(file), text); + } + + public static void writeText(File file, String text) throws IOException { + writeBytes(file, text); + } + + public static void writeBytes(File file, String text) throws IOException { + String parent = file.getParent(); + makeDirectory(parent); + makeFile(file.getAbsolutePath()); + FileWriter fileWriter = new FileWriter(file.getAbsolutePath()); + fileWriter.write(text); + fileWriter.close(); + } + + public static void deleteDirectory(String directory) throws IOException { + if (!new File(directory).exists()) + return; + + cleanDirectory(directory); + + if (!new File(directory).delete()) { + String message = "Unable to delete directory " + directory + "."; + + throw new IOException(message); + } + } + + public static boolean deleteDirectoryQuietly(String directory) { + try { + deleteDirectory(directory); + return true; + } catch (IOException e) { + return false; + } + } + + public static boolean copyDirectory(String src, String dest) throws IOException { + File srcDir = new File(src); + File destDir = new File(dest); + if (!srcDir.isDirectory()) { + return false; + } + if (!destDir.isDirectory() && !destDir.mkdirs()) { + return false; + } + File[] files = srcDir.listFiles(); + if (files == null) { + return true; + } + for (File file : files) { + File destFile = new File(destDir, file.getName()); + if (file.isFile()) { + if (!copyFile(file.getAbsolutePath(), destFile.getAbsolutePath())) { + return false; + } + } + else if (file.isDirectory()) { + if (!copyDirectory(file.getAbsolutePath(), destFile.getAbsolutePath())) { + return false; + } + } + } + return true; + } + + public static boolean copyDirectoryQuietly(String src, String dest) { + try { + copyDirectory(src, dest); + return true; + } catch (IOException e) { + return false; + } + } + + public static void cleanDirectory(String directory) throws IOException { + if (!new File(directory).exists()) { + if (!makeDirectory(directory)) + throw new IOException("Failed to create directory: " + directory); + return; + } + + if (!new File(directory).isDirectory()) { + String message = directory + " is not a directory"; + throw new IllegalArgumentException(message); + } + + File[] files = new File(directory).listFiles(); + if (files == null) + throw new IOException("Failed to list contents of " + directory); + + IOException exception = null; + for (File file : files) + try { + forceDelete(file.getAbsolutePath()); + } catch (IOException ioe) { + exception = ioe; + } + + if (null != exception) + throw exception; + } + + public static boolean cleanDirectoryQuietly(String directory) { + try { + cleanDirectory(directory); + return true; + } catch (IOException e) { + return false; + } + } + + public static void forceDelete(String file) throws IOException { + if (new File(file).isDirectory()) { + deleteDirectory(file); + } else { + boolean filePresent = new File(file).exists(); + if (!new File(file).delete()) { + if (!filePresent) + throw new FileNotFoundException("File does not exist: " + file); + throw new IOException("Unable to delete file: " + file); + } + } + } + + public static boolean copyFile(String srcFile, String destFile) throws IOException { + Objects.requireNonNull(srcFile, "Source must not be null"); + Objects.requireNonNull(destFile, "Destination must not be null"); + File src = new File(srcFile); + File dest = new File(destFile); + InputStream inputStream = new BufferedInputStream(new FileInputStream(src)); + OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(dest)); + byte[] flush = new byte[1024]; + int len; + while ((len = inputStream.read(flush)) != -1) { + outputStream.write(flush,0,len); + } + outputStream.flush(); + outputStream.close(); + inputStream.close(); + return true; + } + + public static boolean copyFileQuietly(String srcFile, String destFile) { + try { + copyFile(srcFile, destFile); + return true; + } catch (IOException e) { + return false; + } + } + + public static boolean moveFile(String srcFile, String destFile) throws IOException { + boolean copy = copyFile(srcFile, destFile); + boolean delete = new File(srcFile).delete(); + return copy && delete; + } + + public static boolean makeDirectory(String directory) { + new File(directory).mkdirs(); + return new File(directory).isDirectory(); + } + + public static boolean makeFile(String file) { + return makeDirectory(new File(file).getParent()) && (new File(file).exists() || Lang.test(new File(file)::createNewFile)); + } + + public static boolean rename(String path, String newName) { + File file = new File(path); + String newPath = path.substring(0, path.lastIndexOf("/") + 1) + newName; + File newFile = new File(newPath); + return file.renameTo(newFile); + } + + public static List listFilesByExtension(String file, String extension) { + List result = new ArrayList<>(); + File[] files = new File(file).listFiles(); + if (files != null) + for (File it : files) + if (extension.equals(getExtension(it))) + result.add(it); + return result; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/io/HttpRequest.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/HttpRequest.java new file mode 100644 index 00000000..d117856c --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/HttpRequest.java @@ -0,0 +1,223 @@ +package com.tungsten.fclcore.util.io; + +import static com.tungsten.fclcore.util.Lang.mapOf; +import static com.tungsten.fclcore.util.gson.JsonUtils.GSON; +import static com.tungsten.fclcore.util.io.NetworkUtils.createHttpConnection; +import static com.tungsten.fclcore.util.io.NetworkUtils.resolveConnection; + +import com.google.gson.JsonParseException; +import com.tungsten.fclcore.task.Schedulers; +import com.tungsten.fclcore.util.Pair; +import com.tungsten.fclcore.util.function.ExceptionalBiConsumer; +import com.tungsten.fclcore.util.function.ExceptionalSupplier; +import com.tungsten.fclcore.util.gson.JsonUtils; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public abstract class HttpRequest { + protected final String url; + protected final String method; + protected final Map headers = new HashMap<>(); + protected ExceptionalBiConsumer responseCodeTester; + protected final Set toleratedHttpCodes = new HashSet<>(); + protected int retryTimes = 1; + protected boolean ignoreHttpCode; + + private HttpRequest(String url, String method) { + this.url = url; + this.method = method; + } + + public HttpRequest accept(String contentType) { + return header("Accept", contentType); + } + + public HttpRequest authorization(String token) { + return header("Authorization", token); + } + + public HttpRequest authorization(String tokenType, String tokenString) { + return authorization(tokenType + " " + tokenString); + } + + public HttpRequest authorization(Authorization authorization) { + return authorization(authorization.getTokenType(), authorization.getAccessToken()); + } + + public HttpRequest header(String key, String value) { + headers.put(key, value); + return this; + } + + public HttpRequest ignoreHttpCode() { + ignoreHttpCode = true; + return this; + } + + public HttpRequest retry(int retryTimes) { + if (retryTimes < 1) { + throw new IllegalArgumentException("retryTimes >= 1"); + } + this.retryTimes = retryTimes; + return this; + } + + public abstract String getString() throws IOException; + + public CompletableFuture getStringAsync() { + return CompletableFuture.supplyAsync(wrap(this::getString), Schedulers.io()); + } + + public T getJson(Class typeOfT) throws IOException, JsonParseException { + return JsonUtils.fromNonNullJson(getString(), typeOfT); + } + + public T getJson(Type type) throws IOException, JsonParseException { + return JsonUtils.fromNonNullJson(getString(), type); + } + + public CompletableFuture getJsonAsync(Class typeOfT) { + return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, typeOfT)); + } + + public CompletableFuture getJsonAsync(Type type) { + return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, type)); + } + + public HttpRequest filter(ExceptionalBiConsumer responseCodeTester) { + this.responseCodeTester = responseCodeTester; + return this; + } + + public HttpRequest ignoreHttpErrorCode(int code) { + toleratedHttpCodes.add(code); + return this; + } + + public HttpURLConnection createConnection() throws IOException { + HttpURLConnection con = createHttpConnection(new URL(url)); + con.setRequestMethod(method); + for (Map.Entry entry : headers.entrySet()) { + con.setRequestProperty(entry.getKey(), entry.getValue()); + } + return con; + } + + public static class HttpGetRequest extends HttpRequest { + public HttpGetRequest(String url) { + super(url, "GET"); + } + + public String getString() throws IOException { + return getStringWithRetry(() -> { + HttpURLConnection con = createConnection(); + con = resolveConnection(con); + return IOUtils.readFullyAsString(con.getInputStream(), StandardCharsets.UTF_8); + }, retryTimes); + } + } + + public static final class HttpPostRequest extends HttpRequest { + private byte[] bytes; + + public HttpPostRequest(String url) { + super(url, "POST"); + } + + public HttpPostRequest contentType(String contentType) { + headers.put("Content-Type", contentType); + return this; + } + + public HttpPostRequest json(Object payload) throws JsonParseException { + return string(payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json"); + } + + public HttpPostRequest form(Map params) { + return string(NetworkUtils.withQuery("", params), "application/x-www-form-urlencoded"); + } + + @SafeVarargs + public final HttpPostRequest form(Pair... params) { + return form(mapOf(params)); + } + + public HttpPostRequest string(String payload, String contentType) { + bytes = payload.getBytes(UTF_8); + header("Content-Length", "" + bytes.length); + contentType(contentType + "; charset=utf-8"); + return this; + } + + public String getString() throws IOException { + return getStringWithRetry(() -> { + HttpURLConnection con = createConnection(); + con.setDoOutput(true); + + try (OutputStream os = con.getOutputStream()) { + os.write(bytes); + } + + if (responseCodeTester != null) { + responseCodeTester.accept(new URL(url), con.getResponseCode()); + } else { + if (con.getResponseCode() / 100 != 2) { + if (!ignoreHttpCode && !toleratedHttpCodes.contains(con.getResponseCode())) { + String data = NetworkUtils.readData(con); + throw new ResponseCodeException(new URL(url), con.getResponseCode(), data); + } + } + } + + return NetworkUtils.readData(con); + }, retryTimes); + } + } + + public static HttpGetRequest GET(String url) { + return new HttpGetRequest(url); + } + + @SafeVarargs + public static HttpGetRequest GET(String url, Pair... query) { + return GET(NetworkUtils.withQuery(url, mapOf(query))); + } + + public static HttpPostRequest POST(String url) throws MalformedURLException { + return new HttpPostRequest(url); + } + + private static String getStringWithRetry(ExceptionalSupplier supplier, int retryTimes) throws IOException { + SocketTimeoutException exception = null; + for (int i = 0; i < retryTimes; i++) { + try { + return supplier.get(); + } catch (SocketTimeoutException e) { + exception = e; + } + } + if (exception != null) + throw exception; + throw new IOException("retry 0"); + } + + public interface Authorization { + String getTokenType(); + + String getAccessToken(); + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/io/IOUtils.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/IOUtils.java new file mode 100644 index 00000000..adce7d28 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/IOUtils.java @@ -0,0 +1,73 @@ +package com.tungsten.fclcore.util.io; + +import java.io.*; +import java.nio.charset.Charset; + +public final class IOUtils { + + private IOUtils() { + } + + public static final int DEFAULT_BUFFER_SIZE = 8 * 1024; + + /** + * Read all bytes to a buffer from given input stream. The stream will not be closed. + * + * @param stream the InputStream being read. + * @return all bytes read from the stream + * @throws IOException if an I/O error occurs. + */ + public static byte[] readFullyWithoutClosing(InputStream stream) throws IOException { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + copyTo(stream, result); + return result.toByteArray(); + } + + /** + * Read all bytes to a buffer from given input stream, and close the input stream finally. + * + * @param stream the InputStream being read, closed finally. + * @return all bytes read from the stream + * @throws IOException if an I/O error occurs. + */ + public static ByteArrayOutputStream readFully(InputStream stream) throws IOException { + try (InputStream is = stream) { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + copyTo(is, result); + return result; + } + } + + public static byte[] readFullyAsByteArray(InputStream stream) throws IOException { + return readFully(stream).toByteArray(); + } + + public static String readFullyAsString(InputStream stream) throws IOException { + return readFully(stream).toString("UTF-8"); + } + + public static String readFullyAsString(InputStream stream, Charset charset) throws IOException { + return readFully(stream).toString(charset.name()); + } + + public static void write(String text, OutputStream outputStream) throws IOException { + write(text.getBytes(), outputStream); + } + + public static void write(byte[] bytes, OutputStream outputStream) throws IOException { + copyTo(new ByteArrayInputStream(bytes), outputStream); + } + + public static void copyTo(InputStream src, OutputStream dest) throws IOException { + copyTo(src, dest, new byte[DEFAULT_BUFFER_SIZE]); + } + + public static void copyTo(InputStream src, OutputStream dest, byte[] buf) throws IOException { + while (true) { + int len = src.read(buf); + if (len == -1) + break; + dest.write(buf, 0, len); + } + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/io/NetworkUtils.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/NetworkUtils.java new file mode 100644 index 00000000..b2fc00c6 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/NetworkUtils.java @@ -0,0 +1,287 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2021 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.tungsten.fclcore.util.io; + +import org.jackhuang.hmcl.util.Pair; + +import java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.Map.Entry; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.jackhuang.hmcl.util.Pair.pair; +import static org.jackhuang.hmcl.util.StringUtils.*; + +/** + * + * @author huangyuhui + */ +public final class NetworkUtils { + public static final String PARAMETER_SEPARATOR = "&"; + public static final String NAME_VALUE_SEPARATOR = "="; + + private NetworkUtils() { + } + + public static String withQuery(String baseUrl, Map params) { + StringBuilder sb = new StringBuilder(baseUrl); + boolean first = true; + for (Entry param : params.entrySet()) { + if (param.getValue() == null) + continue; + if (first) { + if (!baseUrl.isEmpty()) { + sb.append('?'); + } + first = false; + } else { + sb.append(PARAMETER_SEPARATOR); + } + sb.append(encodeURL(param.getKey())); + sb.append(NAME_VALUE_SEPARATOR); + sb.append(encodeURL(param.getValue())); + } + return sb.toString(); + } + + public static List> parseQuery(URI uri) { + return parseQuery(uri.getRawQuery()); + } + + public static List> parseQuery(String queryParameterString) { + if (queryParameterString == null) return Collections.emptyList(); + + List> result = new ArrayList<>(); + + try (Scanner scanner = new Scanner(queryParameterString)) { + scanner.useDelimiter("&"); + while (scanner.hasNext()) { + String[] nameValue = scanner.next().split(NAME_VALUE_SEPARATOR); + if (nameValue.length <= 0 || nameValue.length > 2) { + throw new IllegalArgumentException("bad query string"); + } + + String name = decodeURL(nameValue[0]); + String value = nameValue.length == 2 ? decodeURL(nameValue[1]) : null; + result.add(pair(name, value)); + } + } + return result; + } + + public static URLConnection createConnection(URL url) throws IOException { + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + connection.setRequestProperty("Accept-Language", Locale.getDefault().toString()); + return connection; + } + + public static HttpURLConnection createHttpConnection(URL url) throws IOException { + return (HttpURLConnection) createConnection(url); + } + + /** + * @see Curl + * @param location the url to be URL encoded + * @return encoded URL + */ + public static String encodeLocation(String location) { + StringBuilder sb = new StringBuilder(); + boolean left = true; + for (char ch : location.toCharArray()) { + switch (ch) { + case ' ': + if (left) + sb.append("%20"); + else + sb.append('+'); + break; + case '?': + left = false; + // fallthrough + default: + if (ch >= 0x80) + sb.append(encodeURL(Character.toString(ch))); + else + sb.append(ch); + break; + } + } + + return sb.toString(); + } + + /** + * This method is a work-around that aims to solve problem when "Location" in + * stupid server's response is not encoded. + * + * @see Issue with libcurl + * @param conn the stupid http connection. + * @return manually redirected http connection. + * @throws IOException if an I/O error occurs. + */ + public static HttpURLConnection resolveConnection(HttpURLConnection conn) throws IOException { + int redirect = 0; + while (true) { + + conn.setUseCaches(false); + conn.setConnectTimeout(5000); + conn.setReadTimeout(5000); + conn.setInstanceFollowRedirects(false); + Map> properties = conn.getRequestProperties(); + String method = conn.getRequestMethod(); + int code = conn.getResponseCode(); + if (code >= 300 && code <= 307 && code != 306 && code != 304) { + String newURL = conn.getHeaderField("Location"); + conn.disconnect(); + + if (redirect > 20) { + throw new IOException("Too much redirects"); + } + + HttpURLConnection redirected = (HttpURLConnection) new URL(conn.getURL(), encodeLocation(newURL)) + .openConnection(); + properties + .forEach((key, value) -> value.forEach(element -> redirected.addRequestProperty(key, element))); + redirected.setRequestMethod(method); + conn = redirected; + ++redirect; + } else { + break; + } + } + return conn; + } + + public static String doGet(URL url) throws IOException { + HttpURLConnection con = createHttpConnection(url); + con = resolveConnection(con); + return IOUtils.readFullyAsString(con.getInputStream(), StandardCharsets.UTF_8); + } + + public static String doPost(URL u, Map params) throws IOException { + StringBuilder sb = new StringBuilder(); + if (params != null) { + for (Entry e : params.entrySet()) + sb.append(e.getKey()).append("=").append(e.getValue()).append("&"); + sb.deleteCharAt(sb.length() - 1); + } + return doPost(u, sb.toString()); + } + + public static String doPost(URL u, String post) throws IOException { + return doPost(u, post, "application/x-www-form-urlencoded"); + } + + public static String doPost(URL url, String post, String contentType) throws IOException { + byte[] bytes = post.getBytes(UTF_8); + + HttpURLConnection con = createHttpConnection(url); + con.setRequestMethod("POST"); + con.setDoOutput(true); + con.setRequestProperty("Content-Type", contentType + "; charset=utf-8"); + con.setRequestProperty("Content-Length", "" + bytes.length); + try (OutputStream os = con.getOutputStream()) { + os.write(bytes); + } + return readData(con); + } + + public static String readData(HttpURLConnection con) throws IOException { + try { + try (InputStream stdout = con.getInputStream()) { + return IOUtils.readFullyAsString(stdout, UTF_8); + } + } catch (IOException e) { + try (InputStream stderr = con.getErrorStream()) { + if (stderr == null) + throw e; + return IOUtils.readFullyAsString(stderr, UTF_8); + } + } + } + + public static String detectFileName(URL url) throws IOException { + HttpURLConnection conn = resolveConnection(createHttpConnection(url)); + int code = conn.getResponseCode(); + if (code / 100 == 4) + throw new FileNotFoundException(); + if (code / 100 != 2) + throw new IOException(url + ": response code " + conn.getResponseCode()); + + return detectFileName(conn); + } + + public static String detectFileName(HttpURLConnection conn) { + String disposition = conn.getHeaderField("Content-Disposition"); + if (disposition == null || !disposition.contains("filename=")) { + String u = conn.getURL().toString(); + return decodeURL(substringAfterLast(u, '/')); + } else { + return decodeURL(removeSurrounding(substringAfter(disposition, "filename="), "\"")); + } + } + + public static URL toURL(String str) { + try { + return new URL(str); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + + public static boolean isURL(String str) { + try { + new URL(str); + return true; + } catch (MalformedURLException e) { + return false; + } + } + + public static boolean urlExists(URL url) throws IOException { + HttpURLConnection con = createHttpConnection(url); + con = resolveConnection(con); + int responseCode = con.getResponseCode(); + con.disconnect(); + return responseCode / 100 == 2; + } + + // ==== Shortcut methods for encoding/decoding URLs in UTF-8 ==== + public static String encodeURL(String toEncode) { + try { + return URLEncoder.encode(toEncode, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new Error(); + } + } + + public static String decodeURL(String toDecode) { + try { + return URLDecoder.decode(toDecode, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new Error(); + } + } + // ==== +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/io/ResponseCodeException.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/ResponseCodeException.java new file mode 100644 index 00000000..bd4b0aea --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/io/ResponseCodeException.java @@ -0,0 +1,37 @@ +package com.tungsten.fclcore.util.io; + +import java.io.IOException; +import java.net.URL; + +public class ResponseCodeException extends IOException { + + private final URL url; + private final int responseCode; + private final String data; + + public ResponseCodeException(URL url, int responseCode) { + super("Unable to request url " + url + ", response code: " + responseCode); + this.url = url; + this.responseCode = responseCode; + this.data = null; + } + + public ResponseCodeException(URL url, int responseCode, String data) { + super("Unable to request url " + url + ", response code: " + responseCode + ", data: " + data); + this.url = url; + this.responseCode = responseCode; + this.data = data; + } + + public URL getUrl() { + return url; + } + + public int getResponseCode() { + return responseCode; + } + + public String getData() { + return data; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/platform/CommandBuilder.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/platform/CommandBuilder.java new file mode 100644 index 00000000..77f6713f --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/platform/CommandBuilder.java @@ -0,0 +1,130 @@ +package com.tungsten.fclcore.util.platform; + +import static com.tungsten.fclcore.util.Logging.LOG; + +import com.tungsten.fclcore.util.StringUtils; + +import java.util.*; + +public final class CommandBuilder { + + private final List raw = new ArrayList<>(); + + public CommandBuilder() { + + } + + private String parse(String s) { + return toShellStringLiteral(s); + } + + /** + * Parsing will ignore your manual escaping + * + * @param args commands + * @return this + */ + public CommandBuilder add(String... args) { + for (String s : args) + raw.add(new Item(s, true)); + return this; + } + + public CommandBuilder addAll(Collection args) { + for (String s : args) + raw.add(new Item(s, true)); + return this; + } + + public CommandBuilder addWithoutParsing(String... args) { + for (String s : args) + raw.add(new Item(s, false)); + return this; + } + + public CommandBuilder addAllWithoutParsing(Collection args) { + for (String s : args) + raw.add(new Item(s, false)); + return this; + } + + public String addDefault(String opt) { + for (Item item : raw) { + if (item.arg.equals(opt)) { + return item.arg; + } + } + raw.add(new Item(opt, true)); + return null; + } + + public String addDefault(String opt, String value) { + for (Item item : raw) { + if (item.arg.startsWith(opt)) { + LOG.info("Default option '" + opt + value + "' is suppressed by '" + item.arg + "'"); + return item.arg; + } + } + raw.add(new Item(opt + value, true)); + return null; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < raw.size(); i++) { + if (i != 0) { + stringBuilder.append(" "); + } + stringBuilder.append(raw.get(i).parse ? parse(raw.get(i).arg) : raw.get(i).arg); + } + return stringBuilder.toString(); + } + + public List asList() { + List list = new ArrayList<>(); + for (Item item : raw) { + list.add(item.arg); + } + return list; + } + + private static class Item { + String arg; + boolean parse; + + Item(String arg, boolean parse) { + this.arg = arg; + this.parse = parse; + } + + @Override + public String toString() { + return parse ? toShellStringLiteral(arg) : arg; + } + } + + public static String pwshString(String str) { + return "'" + str.replace("'", "''") + "'"; + } + + public static boolean hasExecutionPolicy() { + return true; + } + + public static boolean setExecutionPolicy() { + return true; + } + + public static String toShellStringLiteral(String s) { + String escaping = " \t\"!#$&'()*,;<=>?[\\]^`{|}~"; + String escaped = "\"$&`"; + if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0 || StringUtils.containsOne(s, escaping.toCharArray())) { + // The argument has not been quoted, add quotes. + for (char ch : escaped.toCharArray()) + s = s.replace("" + ch, "\\" + ch); + return '"' + s + '"'; + } else + return s; + } +} diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/platform/OperatingSystem.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/platform/OperatingSystem.java new file mode 100644 index 00000000..79071d49 --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/platform/OperatingSystem.java @@ -0,0 +1,31 @@ +package com.tungsten.fclcore.util.platform; + +public enum OperatingSystem { + /** + * Microsoft Windows. + */ + WINDOWS("windows"), + /** + * Linux and Unix like OS, including Solaris. + */ + LINUX("linux"), + /** + * Mac OS X. + */ + OSX("osx"), + /** + * Unknown operating system. + */ + UNKNOWN("universal"); + + private final String checkedName; + + OperatingSystem(String checkedName) { + this.checkedName = checkedName; + } + + public String getCheckedName() { + return checkedName; + } + +} \ No newline at end of file diff --git a/FCLCore/src/main/java/com/tungsten/fclcore/util/versioning/VersionNumber.java b/FCLCore/src/main/java/com/tungsten/fclcore/util/versioning/VersionNumber.java new file mode 100644 index 00000000..f46472de --- /dev/null +++ b/FCLCore/src/main/java/com/tungsten/fclcore/util/versioning/VersionNumber.java @@ -0,0 +1,357 @@ +package com.tungsten.fclcore.util.versioning; + +import com.tungsten.fclcore.util.StringUtils; + +import java.math.BigInteger; +import java.util.*; + +/** + * Copied from org.apache.maven.artifact.versioning.ComparableVersion + * Apache License 2.0 + * + * Maybe we can migrate to org.jenkins-ci:version-number:1.7? + * @see Specification + */ +public class VersionNumber implements Comparable { + + public static VersionNumber asVersion(String version) { + Objects.requireNonNull(version); + return new VersionNumber(version); + } + + public static String normalize(String str) { + return new VersionNumber(str).getCanonical(); + } + + public static boolean isIntVersionNumber(String version) { + boolean bool = true; + for (int i = 0; i < version.length(); i++) { + if (version.charAt(i) != '.' && (version.charAt(i) < '0' || version.charAt(i) > '9')) { + bool = false; + break; + } + } + if (bool && !version.contains("..") && StringUtils.isNotBlank(version)) { + String[] arr = version.split("\\."); + for (String str : arr) + if (str.length() > 9) + // Numbers which are larger than 1e9 cannot be stored as integer. + return false; + return true; + } else { + return false; + } + } + + private String value; + private String canonical; + private ListItem items; + + private interface Item { + int INTEGER_ITEM = 0; + int STRING_ITEM = 1; + int LIST_ITEM = 2; + + int compareTo(Item item); + + int getType(); + + boolean isNull(); + } + + /** + * Represents a numeric item in the version item list. + */ + private static class IntegerItem + implements Item { + private final BigInteger value; + + public static final IntegerItem ZERO = new IntegerItem(); + + private IntegerItem() { + this.value = BigInteger.ZERO; + } + + IntegerItem(String str) { + this.value = new BigInteger(str); + } + + public int getType() { + return INTEGER_ITEM; + } + + public boolean isNull() { + return BigInteger.ZERO.equals(value); + } + + public int compareTo(Item item) { + if (item == null) { + return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + switch (item.getType()) { + case INTEGER_ITEM: + return value.compareTo(((IntegerItem) item).value); + + case STRING_ITEM: + return 1; // 1.1 > 1-sp + + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new RuntimeException("invalid item: " + item.getClass()); + } + } + + public String toString() { + return value.toString(); + } + } + + /** + * Represents a string in the version item list, usually a qualifier. + */ + private static class StringItem + implements Item { + private String value; + + StringItem(String value) { + this.value = value; + } + + public int getType() { + return STRING_ITEM; + } + + public boolean isNull() { + return value.isEmpty(); + } + + public int compareTo(Item item) { + if (item == null) { + // 1-string > 1 + return 1; + } + switch (item.getType()) { + case INTEGER_ITEM: + return -1; // 1.any < 1.1 ? + + case STRING_ITEM: + return value.compareTo(((StringItem) item).value); + + case LIST_ITEM: + return -1; // 1.any < 1-1 + + default: + throw new RuntimeException("invalid item: " + item.getClass()); + } + } + + public String toString() { + return value; + } + } + + /** + * Represents a version list item. This class is used both for the global item list and for sub-lists (which start + * with '-(number)' in the version specification). + */ + private static class ListItem + extends ArrayList + implements Item { + Character separator; + + public ListItem() {} + + public ListItem(char separator) { + this.separator = separator; + } + + public int getType() { + return LIST_ITEM; + } + + public boolean isNull() { + return (size() == 0); + } + + void normalize() { + for (int i = size() - 1; i >= 0; i--) { + Item lastItem = get(i); + + if (lastItem.isNull()) { + // remove null trailing items: 0, "", empty list + remove(i); + } else if (!(lastItem instanceof ListItem)) { + break; + } + } + } + + public int compareTo(Item item) { + if (item == null) { + if (size() == 0) { + return 0; // 1-0 = 1- (normalize) = 1 + } + Item first = get(0); + return first.compareTo(null); + } + switch (item.getType()) { + case INTEGER_ITEM: + return -1; // 1-1 < 1.0.x + + case STRING_ITEM: + return 1; // 1-1 > 1-sp + + case LIST_ITEM: + Iterator left = iterator(); + Iterator right = ((ListItem) item).iterator(); + + while (left.hasNext() || right.hasNext()) { + Item l = left.hasNext() ? left.next() : null; + Item r = right.hasNext() ? right.next() : null; + + // if this is shorter, then invert the compare and mul with -1 + int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r); + + if (result != 0) { + return result; + } + } + + return 0; + + default: + throw new RuntimeException("invalid item: " + item.getClass()); + } + } + + public String toString() { + StringBuilder buffer = new StringBuilder(); + for (Item item : this) { + if (buffer.length() > 0) { + if (!(item instanceof ListItem)) + buffer.append('.'); + } + buffer.append(item); + } + if (separator != null) + return separator + buffer.toString(); + else + return buffer.toString(); + } + } + + public VersionNumber(String version) { + parseVersion(version); + } + + private void parseVersion(String version) { + this.value = version; + + ListItem list = items = new ListItem(); + + Stack stack = new Stack<>(); + stack.push(list); + + boolean isDigit = false; + + int startIndex = 0; + + for (int i = 0; i < version.length(); i++) { + char c = version.charAt(i); + + if (c == '.') { + if (i == startIndex) { + list.add(IntegerItem.ZERO); + } else { + list.add(parseItem(version.substring(startIndex, i))); + } + startIndex = i + 1; + } else if ("!\"#$%&'()*+,-/:;<=>?@[\\]^_`{|}~".indexOf(c) != -1) { + if (i == startIndex) { + list.add(IntegerItem.ZERO); + } else { + list.add(parseItem(version.substring(startIndex, i))); + } + startIndex = i + 1; + + list.add(list = new ListItem(c)); + stack.push(list); + } else if (Character.isDigit(c)) { + if (!isDigit && i > startIndex) { + list.add(parseItem(version.substring(startIndex, i))); + startIndex = i; + + list.add(list = new ListItem()); + stack.push(list); + } + + isDigit = true; + } else { + if (isDigit && i > startIndex) { + list.add(parseItem(version.substring(startIndex, i))); + startIndex = i; + + list.add(list = new ListItem()); + stack.push(list); + } + + isDigit = false; + } + } + + if (version.length() > startIndex) { + list.add(parseItem(version.substring(startIndex))); + } + + while (!stack.isEmpty()) { + list = (ListItem) stack.pop(); + list.normalize(); + } + + canonical = items.toString(); + } + + private static Item parseItem(String buf) { + boolean bool = true; + for (int i = 0; i < buf.length(); i++) { + if (!Character.isDigit(buf.charAt(i))) { + bool = false; + break; + } + } + return bool ? new IntegerItem(buf) : new StringItem(buf); + } + + @Override + public int compareTo(VersionNumber o) { + return items.compareTo(o.items); + } + + @Override + public String toString() { + return value; + } + + public String getCanonical() { + return canonical; + } + + @Override + public boolean equals(Object o) { + return o instanceof VersionNumber && canonical.equals(((VersionNumber) o).canonical); + } + + @Override + public int hashCode() { + return canonical.hashCode(); + } + + public static final Comparator VERSION_COMPARATOR = (s, t1) -> { + VersionNumber v = VersionNumber.asVersion(s); + VersionNumber v1 = VersionNumber.asVersion(t1); + return v.compareTo(v1); + }; +} diff --git a/FCLCore/src/test/java/com/tungsten/fclcore/ExampleUnitTest.java b/FCLCore/src/test/java/com/tungsten/fclcore/ExampleUnitTest.java new file mode 100644 index 00000000..a007f7a6 --- /dev/null +++ b/FCLCore/src/test/java/com/tungsten/fclcore/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.tungsten.fclcore; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/FCLauncher/.gitignore b/FCLauncher/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/FCLauncher/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/FCLauncher/build.gradle b/FCLauncher/build.gradle new file mode 100644 index 00000000..5c71fc6e --- /dev/null +++ b/FCLauncher/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'com.android.library' +} + +android { + namespace 'com.tungsten.fclauncher' + compileSdk 32 + + defaultConfig { + minSdk 23 + targetSdk 32 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + externalNativeBuild { + ndkBuild { + path file('src/main/jni/Android.mk') + } + } +} + +dependencies { + + implementation 'com.jaredrummler:android-device-names:2.1.0' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.6.1' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/FCLauncher/consumer-rules.pro b/FCLauncher/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/FCLauncher/proguard-rules.pro b/FCLauncher/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/FCLauncher/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/FCLauncher/src/androidTest/java/com/tungsten/fclauncher/ExampleInstrumentedTest.java b/FCLauncher/src/androidTest/java/com/tungsten/fclauncher/ExampleInstrumentedTest.java new file mode 100644 index 00000000..2f57ea81 --- /dev/null +++ b/FCLauncher/src/androidTest/java/com/tungsten/fclauncher/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.tungsten.fclauncher; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.tungsten.fclauncher.test", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/FCLauncher/src/main/AndroidManifest.xml b/FCLauncher/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/FCLauncher/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/FCLauncher/src/main/java/com/tungsten/fclauncher/FCLConfig.java b/FCLauncher/src/main/java/com/tungsten/fclauncher/FCLConfig.java new file mode 100644 index 00000000..8a4d73ec --- /dev/null +++ b/FCLauncher/src/main/java/com/tungsten/fclauncher/FCLConfig.java @@ -0,0 +1,83 @@ +package com.tungsten.fclauncher; + +import android.content.Context; +import android.view.Surface; + +import com.tungsten.fclauncher.bridge.FCLBridgeCallback; + +public class FCLConfig { + + public enum Renderer { + RENDERER_GL4ES("libgl4es.so", "libgl4es_egl.so"), + RENDERER_ZINK("libGL.so", "libEGL.so"); + + private final String glLibName; + private final String eglLibName; + + Renderer(String glLibName, String eglLibName) { + this.glLibName = glLibName; + this.eglLibName = eglLibName; + } + + public String getGlLibName() { + return glLibName; + } + + public String getEglLibName() { + return eglLibName; + } + } + + private final Context context; + private final Surface surface; + private final String logDir; + private final String javaPath; + private final String workingDir; + private final Renderer renderer; + private final String[] args; + private final FCLBridgeCallback callback; + + public FCLConfig(Context context, Surface surface, String logDir, String javaPath, String workingDir, Renderer renderer, String[] args, FCLBridgeCallback callback) { + this.context = context; + this.surface = surface; + this.logDir = logDir; + this.javaPath = javaPath; + this.workingDir = workingDir; + this.renderer = renderer; + this.args = args; + this.callback = callback; + } + + public Context getContext() { + return context; + } + + public Surface getSurface() { + return surface; + } + + public String getLogDir() { + return logDir; + } + + public String getJavaPath() { + return javaPath; + } + + public String getWorkingDir() { + return workingDir; + } + + public Renderer getRenderer() { + return renderer; + } + + public String[] getArgs() { + return args; + } + + public FCLBridgeCallback getCallback() { + return callback; + } + +} diff --git a/FCLauncher/src/main/java/com/tungsten/fclauncher/FCLauncher.java b/FCLauncher/src/main/java/com/tungsten/fclauncher/FCLauncher.java new file mode 100644 index 00000000..14df03ec --- /dev/null +++ b/FCLauncher/src/main/java/com/tungsten/fclauncher/FCLauncher.java @@ -0,0 +1,332 @@ +package com.tungsten.fclauncher; + +import static com.tungsten.fclauncher.utils.Architecture.ARCH_X86; +import static com.tungsten.fclauncher.utils.Architecture.is64BitsDevice; + +import android.content.Context; +import android.util.ArrayMap; + +import com.jaredrummler.android.device.DeviceName; +import com.tungsten.fclauncher.bridge.FCLBridge; +import com.tungsten.fclauncher.utils.Architecture; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class FCLauncher { + + // Todo : don't crash when launch 1.17+ with OpenGL 2.1 + // Todo : mouse scroll event + // Todo : custom logger + // Todo : mesa + + private static void printTaskTitle(String task) { + System.out.println("==================== " + task + " ===================="); + } + + private static void logStartInfo(String task) { + printTaskTitle("Start " + task); + System.out.println("Device: " + DeviceName.getDeviceName()); + System.out.println("Architecture: " + Architecture.archAsString(Architecture.getDeviceArchitecture())); + } + + private static Map readJREReleaseProperties(String javaPath) throws IOException { + Map jreReleaseMap = new ArrayMap<>(); + BufferedReader jreReleaseReader = new BufferedReader(new FileReader(javaPath + "/release")); + String currLine; + while ((currLine = jreReleaseReader.readLine()) != null) { + if (currLine.contains("=")) { + String[] keyValue = currLine.split("="); + jreReleaseMap.put(keyValue[0], keyValue[1].replace("\"", "")); + } + } + jreReleaseReader.close(); + return jreReleaseMap; + } + + private static String getJreLibDir(String javaPath) throws IOException { + String jreArchitecture = readJREReleaseProperties(javaPath).get("OS_ARCH"); + if (Architecture.archAsInt(jreArchitecture) == ARCH_X86) { + jreArchitecture = "i386/i486/i586"; + } + String jreLibDir = "/lib"; + if (jreArchitecture == null) { + throw new IOException("Unsupported architecture!"); + } + for (String arch : jreArchitecture.split("/")) { + File file = new File(javaPath, "lib/" + arch); + if (file.exists() && file.isDirectory()) { + jreLibDir = "/lib/" + arch; + } + } + return jreLibDir; + } + + private static String getJvmLibDir(String javaPath) throws IOException { + String jvmLibDir; + File jvmFile = new File(javaPath + getJreLibDir(javaPath) + "/server/libjvm.so"); + jvmLibDir = jvmFile.exists() ? "/server" : "/client"; + return jvmLibDir; + } + + private static String getLibraryPath(Context context, String javaPath) throws IOException { + String nativeDir = context.getApplicationInfo().nativeLibraryDir; + String libDirName = is64BitsDevice() ? "lib64" : "lib"; + String jreLibDir = getJreLibDir(javaPath); + String jvmLibDir = getJvmLibDir(javaPath); + String jliLibDir = "/jli"; + String split = ":"; + return javaPath + + jreLibDir + + split + + + javaPath + + jreLibDir + + jliLibDir + + split + + + javaPath + + jreLibDir + + jvmLibDir + + split + + + "/system/" + + libDirName + + split + + + "/vendor/" + + libDirName + + split + + + "/vendor/" + + libDirName + + "/hw" + + split + + + nativeDir; + } + + private static String[] rebaseArgs(FCLConfig config) throws IOException { + ArrayList argList = new ArrayList<>(Arrays.asList(config.getArgs())); + argList.add(1, "-Djava.library.path=" + getLibraryPath(config.getContext(), config.getJavaPath())); + String[] args = new String[argList.size()]; + for (int i = 0; i < argList.size(); i++) { + args[i] = argList.get(i).replace("${glLibName}", config.getRenderer().getGlLibName()); + } + return args; + } + + private static void addCommonEnv(FCLConfig config, HashMap envMap) { + envMap.put("HOME", config.getLogDir()); + envMap.put("JAVA_HOME", config.getJavaPath()); + } + + private static void addRendererEnv(FCLConfig config, HashMap envMap) { + // Todo : mesa env + String nativeDir = config.getContext().getApplicationInfo().nativeLibraryDir; + envMap.put("LIBGL_NAME", config.getRenderer().getGlLibName()); + envMap.put("LIBEGL_NAME", config.getRenderer().getEglLibName()); + if (config.getRenderer() == FCLConfig.Renderer.RENDERER_GL4ES) { + envMap.put("LIBGL_MIPMAP", "3"); + envMap.put("LIBGL_NORMALIZE", "1"); + envMap.put("LIBGL_VSYNC", "1"); + envMap.put("LIBGL_NOINTOVLHACK", "1"); + } + else { + envMap.put("LIBGL_DRIVERS_PATH", nativeDir); + envMap.put("MESA_GL_VERSION_OVERRIDE", "4.6"); + envMap.put("MESA_GLSL_VERSION_OVERRIDE", "460"); + envMap.put("GALLIUM_DRIVER", "zink"); + envMap.put("MESA_GLSL_CACHE_DIR", config.getContext().getCacheDir().getAbsolutePath()); + } + } + + private static void setEnv(FCLConfig config, FCLBridge bridge, boolean render) { + HashMap envMap = new HashMap<>(); + addCommonEnv(config, envMap); + if (render) { + addRendererEnv(config, envMap); + } + printTaskTitle("Env Map"); + for (String key : envMap.keySet()) { + System.out.println("Env: " + key + "=" + envMap.get(key)); + bridge.setenv(key, envMap.get(key)); + } + printTaskTitle("Env Map"); + } + + private static void setUpJavaRuntime(FCLConfig config, FCLBridge bridge) throws IOException { + String jreLibDir = config.getJavaPath() + getJreLibDir(config.getJavaPath()); + String jliLibDir = jreLibDir + "/jli"; + String jvmLibDir = jreLibDir + getJvmLibDir(config.getJavaPath()); + // dlopen jre + bridge.dlopen(jliLibDir + "/libjli.so"); + bridge.dlopen(jvmLibDir + "/libjvm.so"); + bridge.dlopen(jreLibDir + "/libfreetype.so"); + bridge.dlopen(jreLibDir + "/libverify.so"); + bridge.dlopen(jreLibDir + "/libjava.so"); + bridge.dlopen(jreLibDir + "/libnet.so"); + bridge.dlopen(jreLibDir + "/libnio.so"); + bridge.dlopen(jreLibDir + "/libawt.so"); + bridge.dlopen(jreLibDir + "/libawt_headless.so"); + bridge.dlopen(jreLibDir + "/libfontmanager.so"); + bridge.dlopen(jreLibDir + "/libtinyiconv.so"); + bridge.dlopen(jreLibDir + "/libinstrument.so"); + } + + private static void setupGraphicAndSoundEngine(FCLConfig config, FCLBridge bridge) { + String nativeDir = config.getContext().getApplicationInfo().nativeLibraryDir; + + bridge.dlopen(nativeDir + "/libopenal.so"); + + // Todo : mesa + bridge.dlopen(nativeDir + "/" + config.getRenderer().getGlLibName()); + bridge.dlopen(nativeDir + "/" + config.getRenderer().getEglLibName()); + if (config.getRenderer() == FCLConfig.Renderer.RENDERER_ZINK) { + bridge.dlopen(nativeDir + "/libglapi.so"); + bridge.dlopen(nativeDir + "/libexpat.so"); + bridge.dlopen(nativeDir + "/zink_dri.so"); + } + } + + private static void launch(FCLConfig config, FCLBridge bridge, String task) throws IOException { + printTaskTitle(task + " Arguments"); + String[] args = rebaseArgs(config); + for (String arg : args) { + System.out.println(task + " argument: " + arg); + } + bridge.setupJLI(); + System.out.println("OpenJDK exited with code : " + bridge.jliLaunch(args)); + } + + public static FCLBridge launchMinecraft(FCLConfig config) { + + // initialize FCLBridge + FCLBridge bridge = new FCLBridge(config.getContext(), config.getCallback()); + + Thread gameThread = new Thread(() -> { + try { + // redirect log path + bridge.redirectStdio(config.getLogDir() + "/latest_game.log"); + + logStartInfo("Minecraft"); + + // set graphic output and event pipe + bridge.setFCLNativeWindow(config.getSurface()); + bridge.setEventPipe(); + + // env + setEnv(config, bridge, true); + + // setup java runtime + setUpJavaRuntime(config, bridge); + + // setup graphic and sound engine + setupGraphicAndSoundEngine(config, bridge); + + // hook exit + bridge.setupExitTrap(bridge); + + // set working directory + System.out.println("Working directory: " + config.getWorkingDir()); + bridge.chdir(config.getWorkingDir()); + + // launch game + launch(config, bridge, "Minecraft"); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + gameThread.setPriority(Thread.MAX_PRIORITY); + gameThread.start(); + + return bridge; + } + + public static FCLBridge launchJavaGUI(FCLConfig config) { + + // initialize FCLBridge + FCLBridge bridge = new FCLBridge(config.getContext(), config.getCallback()); + + Thread javaGUIThread = new Thread(() -> { + try { + // redirect log path + bridge.redirectStdio(config.getLogDir() + "/latest_java_gui.log"); + + logStartInfo("Java GUI"); + + // set graphic output and event pipe + bridge.setFCLNativeWindow(config.getSurface()); + bridge.setEventPipe(); + + // env + setEnv(config, bridge, true); + + // setup java runtime + setUpJavaRuntime(config, bridge); + + // setup graphic and sound engine + setupGraphicAndSoundEngine(config, bridge); + + // hook exit + bridge.setupExitTrap(bridge); + + // set working directory + System.out.println("Working directory: " + config.getWorkingDir()); + bridge.chdir(config.getWorkingDir()); + + // launch java gui + launch(config, bridge, "Java GUI"); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + javaGUIThread.start(); + + return bridge; + } + + public static void launchAPIInstaller(FCLConfig config) { + + // initialize FCLBridge + FCLBridge bridge = new FCLBridge(config.getContext(), config.getCallback()); + + Thread apiInstallerThread = new Thread(() -> { + try { + // redirect log path + bridge.redirectStdio(config.getLogDir() + "/latest_api_installer.log"); + + logStartInfo("API Installer"); + + // env + setEnv(config, bridge, false); + + // setup java runtime + setUpJavaRuntime(config, bridge); + + // hook exit + bridge.setupExitTrap(bridge); + + // set working directory + System.out.println("Working directory: " + config.getWorkingDir()); + bridge.chdir(config.getWorkingDir()); + + // launch api installer + launch(config, bridge, "API Installer"); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + apiInstallerThread.start(); + } + +} diff --git a/FCLauncher/src/main/java/com/tungsten/fclauncher/bridge/FCLBridge.java b/FCLauncher/src/main/java/com/tungsten/fclauncher/bridge/FCLBridge.java new file mode 100644 index 00000000..8187ca37 --- /dev/null +++ b/FCLauncher/src/main/java/com/tungsten/fclauncher/bridge/FCLBridge.java @@ -0,0 +1,110 @@ +package com.tungsten.fclauncher.bridge; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.view.Surface; + +public class FCLBridge { + + public static final int KeyPress = 2; + public static final int KeyRelease = 3; + public static final int ButtonPress = 4; + public static final int ButtonRelease = 5; + public static final int MotionNotify = 6; + public static final int ConfigureNotify = 22; + public static final int FCLMessage = 37; + + public static final int Button1 = 1; + public static final int Button2 = 2; + public static final int Button3 = 3; + public static final int Button4 = 4; + public static final int Button5 = 5; + public static final int Button6 = 6; + public static final int Button7 = 7; + + public static final int CursorEnabled = 1; + public static final int CursorDisabled = 0; + + public static final int ShiftMask = 1 << 0; + public static final int LockMask = 1 << 1; + public static final int ControlMask = 1 << 2; + public static final int Mod1Mask = 1 << 3; + public static final int Mod2Mask = 1 << 4; + public static final int Mod3Mask = 1 << 5; + public static final int Mod4Mask = 1 << 6; + public static final int Mod5Mask = 1 << 7; + + public static final int CloseRequest = 0; + + public final Context context; + public final FCLBridgeCallback callback; + + static { + System.loadLibrary("xhook"); + System.loadLibrary("fcl"); + System.loadLibrary("glfw"); + } + + public FCLBridge(Context context, FCLBridgeCallback callback) { + this.context = context; + this.callback = callback; + } + + public native void setFCLNativeWindow(Surface surface); + public native void redirectStdio(String path); + public native int chdir(String path); + public native void setenv(String key, String value); + public native int dlopen(String path); + public native void setupExitTrap(FCLBridge bridge); + public native void setEventPipe(); + public native void pushEvent(long time, int type, int keycode, int keyChar); + public native void setupJLI(); + public native int jliLaunch(String[] args); + + public void pushEventMouseButton(int button, boolean press) { + pushEvent(System.nanoTime(), press ? ButtonPress : ButtonRelease, button, 0); + } + + public void pushEventPointer(int x, int y) { + pushEvent(System.nanoTime(), MotionNotify, x, y); + } + + public void pushEventKey(int keyCode, int keyChar, boolean press) { + pushEvent(System.nanoTime(), press ? KeyPress : KeyRelease, keyCode, keyChar); + } + + public void pushEventWindow(int width, int height) { + pushEvent(System.nanoTime(), ConfigureNotify, width, height); + } + + public void pushEventMessage(int msg) { + pushEvent(System.nanoTime(), FCLMessage, msg, 0); + } + + // Loader function + public void exit(int code) { + callback.onExit(code); + } + + // FCLBridge callbacks + public void setCursorMode(int mode) { + callback.onCursorModeChange(mode); + } + + public void setPrimaryClipString(String string) { + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("FCL Clipboard", string); + clipboard.setPrimaryClip(clip); + } + + public String getPrimaryClipString() { + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (!clipboard.hasPrimaryClip()) { + return null; + } + ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); + return item.getText().toString(); + } + +} diff --git a/FCLauncher/src/main/java/com/tungsten/fclauncher/bridge/FCLBridgeCallback.java b/FCLauncher/src/main/java/com/tungsten/fclauncher/bridge/FCLBridgeCallback.java new file mode 100644 index 00000000..eff39d58 --- /dev/null +++ b/FCLauncher/src/main/java/com/tungsten/fclauncher/bridge/FCLBridgeCallback.java @@ -0,0 +1,8 @@ +package com.tungsten.fclauncher.bridge; + +public interface FCLBridgeCallback { + + void onCursorModeChange(int mode); + void onExit(int code); + +} diff --git a/FCLauncher/src/main/java/com/tungsten/fclauncher/utils/Architecture.java b/FCLauncher/src/main/java/com/tungsten/fclauncher/utils/Architecture.java new file mode 100644 index 00000000..ff20cd5c --- /dev/null +++ b/FCLauncher/src/main/java/com/tungsten/fclauncher/utils/Architecture.java @@ -0,0 +1,99 @@ +package com.tungsten.fclauncher.utils; + +import android.os.Build; + +/** + * This class aims at providing a simple and easy way to deal with the device architecture. + */ +public class Architecture { + + public static int UNSUPPORTED_ARCH = -1; + public static int ARCH_ARM64 = 0x1; + public static int ARCH_ARM = 0x2; + public static int ARCH_X86 = 0x4; + public static int ARCH_X86_64 = 0x8; + + /** + * Tell us if the device supports 64 bits architecture + * @return If the device supports 64 bits architecture + */ + public static boolean is64BitsDevice() { + return Build.SUPPORTED_64_BIT_ABIS.length != 0; + } + + /** + * Tell us if the device supports 32 bits architecture + * Note, that a 64 bits device won't be reported as supporting 32 bits. + * @return If the device supports 32 bits architecture + */ + public static boolean is32BitsDevice() { + return !is64BitsDevice(); + } + + /** + * Tells the device supported architecture. + * Since mips(/64) has been phased out long ago, is isn't checked here. + * + * @return ARCH_ARM || ARCH_ARM64 || ARCH_X86 || ARCH_86_64 + */ + public static int getDeviceArchitecture() { + if (isx86Device()) { + return is64BitsDevice() ? ARCH_X86_64 : ARCH_X86; + } + return is64BitsDevice() ? ARCH_ARM64 : ARCH_ARM; + } + + /** + * Tell is the device is based on an x86 processor. + * It doesn't tell if the device is 64 or 32 bits. + * @return Whether or not the device is x86 based. + */ + public static boolean isx86Device() { + //We check the whole range of supported ABIs, + //Since asus zenfones can place arm before their native instruction set. + String[] ABI = is64BitsDevice() ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS; + int comparedArch = is64BitsDevice() ? ARCH_X86_64 : ARCH_X86; + for (String str : ABI) { + if (archAsInt(str) == comparedArch) return true; + } + return false; + } + + /** + * Tell is the device is based on an arm processor. + * It doesn't tell if the device is 64 or 32 bits. + * @return Whether or not the device is arm based. + */ + public static boolean isArmDevice() { + return !isx86Device(); + } + + /** + * Convert an architecture from a String to an int. + * @param arch The architecture as a String + * @return The architecture as an int, can be UNSUPPORTED_ARCH if unknown. + */ + public static int archAsInt(String arch) { + arch = arch.toLowerCase().trim().replace(" ", ""); + if (arch.contains("arm64") || arch.equals("aarch64")) return ARCH_ARM64; + if (arch.contains("arm") || arch.equals("aarch32")) return ARCH_ARM; + if (arch.contains("x86_64") || arch.contains("amd64")) return ARCH_X86_64; + if (arch.contains("x86") || (arch.startsWith("i") && arch.endsWith("86"))) return ARCH_X86; + //Shouldn't happen + return UNSUPPORTED_ARCH; + } + + /** + * Convert to a string an architecture. + * @param arch The architecture as an int. + * @return "arm64" || "arm" || "x86_64" || "x86" || "UNSUPPORTED_ARCH" + */ + public static String archAsString(int arch) { + if (arch == ARCH_ARM64) return "arm64"; + if (arch == ARCH_ARM) return "arm"; + if (arch == ARCH_X86_64) return "x86_64"; + if (arch == ARCH_X86) return "x86"; + return "UNSUPPORTED_ARCH"; + } + +} \ No newline at end of file diff --git a/FCLauncher/src/main/jni/Android.mk b/FCLauncher/src/main/jni/Android.mk new file mode 100644 index 00000000..9219755b --- /dev/null +++ b/FCLauncher/src/main/jni/Android.mk @@ -0,0 +1,50 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := xhook +LOCAL_SRC_FILES := xhook/xhook.c \ + xhook/xh_core.c \ + xhook/xh_elf.c \ + xhook/xh_jni.c \ + xhook/xh_log.c \ + xhook/xh_util.c \ + xhook/xh_version.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/xhook/include +LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden +LOCAL_CONLYFLAGS := -std=c11 +LOCAL_LDLIBS := -llog +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := fcl +LOCAL_SHARED_LIBRARIES := xhook +LOCAL_SRC_FILES := fcl/fcl_bridge.c \ + fcl/fcl_event.c \ + fcl/fcl_loader.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/xhook/include \ + $(LOCAL_PATH)/fcl/include +LOCAL_LDLIBS := -llog -ldl -landroid +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := glfw +LOCAL_SHARED_LIBRARIES := fcl +LOCAL_SRC_FILES := glfw/context.c \ + glfw/init.c \ + glfw/input.c \ + glfw/monitor.c \ + glfw/vulkan.c \ + glfw/window.c \ + glfw/fcl_init.c \ + glfw/fcl_monitor.c \ + glfw/fcl_window.c \ + glfw/null_joystick.c \ + glfw/egl_context.c \ + glfw/osmesa_context.c \ + glfw/posix_thread.c \ + glfw/posix_time.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/fcl/include \ + $(LOCAL_PATH)/glfw/include +LOCAL_CFLAGS := -Wall -fuse-ld=gold -Werror=implicit-function-declaration +LOCAL_LDLIBS := -llog -ldl -landroid +include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/FCLauncher/src/main/jni/Application.mk b/FCLauncher/src/main/jni/Application.mk new file mode 100644 index 00000000..8d9cd016 --- /dev/null +++ b/FCLauncher/src/main/jni/Application.mk @@ -0,0 +1,2 @@ +APP_STL := system +APP_PLATFORM := android-23 \ No newline at end of file diff --git a/FCLauncher/src/main/jni/fcl/fcl_bridge.c b/FCLauncher/src/main/jni/fcl/fcl_bridge.c new file mode 100644 index 00000000..476dc4ae --- /dev/null +++ b/FCLauncher/src/main/jni/fcl/fcl_bridge.c @@ -0,0 +1,59 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include + +FCLInternal fcl; + +ANativeWindow* fclGetNativeWindow() { + return fcl.window; +} + +void fclSetPrimaryClipString(const char* string) { + PrepareFCLBridgeJNI(); + CallFCLBridgeJNIFunc( , Void, setPrimaryClipString, "(Ljava/lang/String;)V", (*env)->NewStringUTF(env, string)); +} + +const char* fclGetPrimaryClipString() { + PrepareFCLBridgeJNI(); + if (fcl.clipboard_string != NULL) { + free(fcl.clipboard_string); + fcl.clipboard_string = NULL; + } + CallFCLBridgeJNIFunc(jstring clipstr = , Object, getPrimaryClipString, "()Ljava/lang/String;"); + const char* string = NULL; + if (clipstr != NULL) { + string = (*env)->GetStringUTFChars(env, clipstr, NULL); + if (string != NULL) { + fcl.clipboard_string = strdup(string); + } + } + return fcl.clipboard_string; +} + +JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setFCLNativeWindow(JNIEnv* env, jclass clazz, jobject surface) { + fcl.window = ANativeWindow_fromSurface(env, surface); + FCL_INTERNAL_LOG("setFCLNativeWindow : %p", fcl.window); +} + +JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + memset(&fcl, 0, sizeof(fcl)); + fcl.android_jvm = vm; + JNIEnv* env = 0; + jint result = (*fcl.android_jvm)->AttachCurrentThread(fcl.android_jvm, &env, 0); + if (result != JNI_OK || env == 0) { + FCL_INTERNAL_LOG("Failed to attach thread to JavaVM."); + abort(); + } + jclass class_FCLBridge = (*env)->FindClass(env, "com/tungsten/fclauncher/bridge/FCLBridge"); + if (class_FCLBridge == 0) { + FCL_INTERNAL_LOG("Failed to find class: com/tungsten/fclauncher/bridge/FCLBridge."); + abort(); + } + fcl.class_FCLBridge = (jclass)(*env)->NewGlobalRef(env, class_FCLBridge); + return JNI_VERSION_1_2; +} \ No newline at end of file diff --git a/FCLauncher/src/main/jni/fcl/fcl_event.c b/FCLauncher/src/main/jni/fcl/fcl_event.c new file mode 100644 index 00000000..a512e39e --- /dev/null +++ b/FCLauncher/src/main/jni/fcl/fcl_event.c @@ -0,0 +1,171 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +void EventQueue_init(EventQueue* queue) { + queue->count = 0; + queue->head = NULL; + queue->tail = NULL; +} + +FCLEvent* EventQueue_add(EventQueue* queue) { + FCLEvent* ret = NULL; + QueueElement* e = malloc(sizeof(QueueElement)); + if (e != NULL) { + e->next = NULL; + if (queue->count > 0) { + queue->tail->next = e; + queue->tail = e; + } + else { // count == 0 + queue->head = e; + queue->tail = e; + } + queue->count++; + ret = &queue->tail->event; + } + return ret; +} + +int EventQueue_take(EventQueue* queue, FCLEvent* event) { + int ret = 0; + if (queue->count > 0) { + QueueElement* e = queue->head; + if (queue->count == 1) { + queue->head = NULL; + queue->tail = NULL; + } + else { + queue->head = e->next; + } + queue->count--; + ret = 1; + if (event != NULL) { + memcpy(event, &e->event, sizeof(FCLEvent)); + } + free(e); + } + return ret; +} + +void EventQueue_clear(EventQueue* queue) { + while (queue->count > 0) { + EventQueue_take(queue, NULL); + } +} + +void fclSetCursorMode(int mode) { + if (!fcl.has_event_pipe) { + return; + } + PrepareFCLBridgeJNI(); + CallFCLBridgeJNIFunc( , Void, setCursorMode, "(I)V", mode); +} + +int fclGetEventFd() { + if (!fcl.has_event_pipe) { + return -1; + } + return fcl.event_pipe_fd[0]; +} + +int fclWaitForEvent(int timeout) { + if (!fcl.has_event_pipe) { + return 0; + } + struct epoll_event ev; + int ret = epoll_wait(fcl.epoll_fd, &ev, 1, timeout); + if (ret > 0 && (ev.events & EPOLLIN)) { + return 1; + } + return 0; +} + +int fclPollEvent(FCLEvent* event) { + if (!fcl.has_event_pipe) { + return 0; + } + if (pthread_mutex_lock(&fcl.event_queue_mutex)) { + FCL_INTERNAL_LOG("Failed to acquire mutex"); + return 0; + } + char c; + int ret = 0; + if (read(fcl.event_pipe_fd[0], &c, 1) > 0) { + ret = EventQueue_take(&fcl.event_queue, event); + } + if (pthread_mutex_unlock(&fcl.event_queue_mutex)) { + FCL_INTERNAL_LOG("Failed to release mutex"); + return 0; + } + return ret; +} + +JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_pushEvent(JNIEnv* env, jclass clazz, jlong time, jint type, jint p1, jint p2) { + if (!fcl.has_event_pipe) { + return; + } + if (pthread_mutex_lock(&fcl.event_queue_mutex)) { + FCL_INTERNAL_LOG("Failed to acquire mutex"); + return; + } + FCLEvent* event = EventQueue_add(&fcl.event_queue); + if (event == NULL) { + FCL_INTERNAL_LOG("Failed to add event to event queue"); + return; + } + event->time = time; + event->type = type; + event->state = 0; + switch (type) { + case MotionNotify: + event->x = p1; + event->y = p2; + break; + case ButtonPress: + case ButtonRelease: + event->button = p1; + break; + case KeyPress: + case KeyRelease: + event->keycode = p1; + event->keychar = p2; + break; + case ConfigureNotify: + event->width = p1; + event->height = p2; + break; + case FCLMessage: + event->message = p1; + break; + } + write(fcl.event_pipe_fd[1], "E", 1); + if (pthread_mutex_unlock(&fcl.event_queue_mutex)) { + FCL_INTERNAL_LOG("Failed to release mutex"); + } +} + +JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setEventPipe(JNIEnv* env, jclass clazz) { + if (pipe(fcl.event_pipe_fd) == -1) { + FCL_INTERNAL_LOG("Failed to create event pipe : %s", strerror(errno)); + return; + } + fcl.epoll_fd = epoll_create(3); + if (fcl.epoll_fd == -1) { + FCL_INTERNAL_LOG("Failed to get epoll fd : %s", strerror(errno)); + return; + } + struct epoll_event ev; + ev.events = EPOLLIN; + ev.data.fd = fcl.event_pipe_fd[0]; + if (epoll_ctl(fcl.epoll_fd, EPOLL_CTL_ADD, fcl.event_pipe_fd[0], &ev) == -1) { + FCL_INTERNAL_LOG("Failed to add epoll event : %s", strerror(errno)); + return; + } + EventQueue_init(&fcl.event_queue); + pthread_mutex_init(&fcl.event_queue_mutex, NULL); + fcl.has_event_pipe = 1; + FCL_INTERNAL_LOG("Succeeded to set event pipe"); +} \ No newline at end of file diff --git a/FCLauncher/src/main/jni/fcl/fcl_loader.c b/FCLauncher/src/main/jni/fcl/fcl_loader.c new file mode 100644 index 00000000..c4f06178 --- /dev/null +++ b/FCLauncher/src/main/jni/fcl/fcl_loader.c @@ -0,0 +1,143 @@ +// +// Created by Tungsten on 2022/10/12. +// + +#include +#include +#include +#include +#include +#include +#include + +#define FULL_VERSION "1.8.0-internal" +#define DOT_VERSION "1.8" +#define PROGNAME "java" +#define LAUNCHER_NAME "openjdk" + +static char* const_progname = PROGNAME; +static const char* const_launcher = LAUNCHER_NAME; +static const char** const_jargs = NULL; +static const char** const_appclasspath = NULL; +static const jboolean const_cpwildcard = JNI_TRUE; +static const jboolean const_javaw = JNI_FALSE; +static const jint const_ergo_class = 0; //DEFAULT_POLICY + +static volatile jobject exitTrap_bridge; +static volatile jmethodID exitTrap_method; +static JavaVM *exitTrap_jvm; + +void (*old_exit)(int code); +void custom_exit(int code) { + JNIEnv *env; + (*exitTrap_jvm)->AttachCurrentThread(exitTrap_jvm, &env, NULL); + (*env)->CallVoidMethod(env, exitTrap_bridge, exitTrap_method, code); + (*env)->DeleteGlobalRef(env, exitTrap_bridge); + (*exitTrap_jvm)->DetachCurrentThread(exitTrap_jvm); + old_exit(code); +} + +JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_redirectStdio(JNIEnv* env, jclass clazz, jstring path) { + char const* file = (*env)->GetStringUTFChars(env, path, 0); + + int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + dup2(fd, 1); + dup2(fd, 2); + + (*env)->ReleaseStringUTFChars(env, path, file); +} + +JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_chdir(JNIEnv* env, jclass clazz, jstring path) { + char const* dir = (*env)->GetStringUTFChars(env, path, 0); + + int b = chdir(dir); + + (*env)->ReleaseStringUTFChars(env, path, dir); + return b; +} + +JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setenv(JNIEnv* env, jclass clazz, jstring str1, jstring str2) { + char const* name = (*env)->GetStringUTFChars(env, str1, 0); + char const* value = (*env)->GetStringUTFChars(env, str2, 0); + + setenv(name, value, 1); + + (*env)->ReleaseStringUTFChars(env, str1, name); + (*env)->ReleaseStringUTFChars(env, str2, value); +} + +JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_dlopen(JNIEnv* env, jclass clazz, jstring str) { + dlerror(); + + int ret = 0; + char const* lib_name = (*env)->GetStringUTFChars(env, str, 0); + + void* handle; + dlerror(); + handle = dlopen(lib_name, RTLD_GLOBAL); + __android_log_print(dlerror() == NULL ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR, "FCL", "loading %s (error = %s)", lib_name, dlerror()); + + if (handle == NULL) { + ret = -1; + } + + (*env)->ReleaseStringUTFChars(env, str, lib_name); + return ret; +} + +JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setupExitTrap(JNIEnv *env, jclass clazz, jobject bridge) { + exitTrap_bridge = (*env)->NewGlobalRef(env, bridge); + (*env)->GetJavaVM(env, &exitTrap_jvm); + jclass exitTrap_exitClass = (*env)->NewGlobalRef(env,(*env)->FindClass(env, "com/tungsten/fclauncher/bridge/FCLBridge")); + exitTrap_method = (*env)->GetMethodID(env, exitTrap_exitClass, "exit", "(I)V"); + (*env)->DeleteGlobalRef(env, exitTrap_exitClass); + xhook_register(".*\\.so$", "exit", custom_exit, (void **) &old_exit); + xhook_refresh(1); +} + +int +(*JLI_Launch)(int argc, char ** argv, /* main argc, argc */ + int jargc, const char** jargv, /* java args */ + int appclassc, const char** appclassv, /* app classpath */ + const char* fullversion, /* full version defined */ + const char* dotversion, /* dot version defined */ + const char* pname, /* program name */ + const char* lname, /* launcher name */ + jboolean javaargs, /* JAVA_ARGS */ + jboolean cpwildcard, /* classpath wildcard */ + jboolean javaw, /* windows-only javaw */ + jint ergo_class /* ergnomics policy */ +); + +JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setupJLI(JNIEnv* env, jclass clazz){ + + void* handle; + handle = dlopen("libjli.so", RTLD_LAZY); + JLI_Launch = (int (*)(int, char **, int, const char**, int, const char**, const char*, const char*, const char*, const char*, jboolean, jboolean, jboolean, jint))dlsym(handle, "JLI_Launch"); + +} + +JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_jliLaunch(JNIEnv *env, jclass clazz, jobjectArray argsArray){ + int argc = (*env)->GetArrayLength(env, argsArray); + char* argv[argc]; + for (int i = 0; i < argc; i++) { + jstring str = (*env)->GetObjectArrayElement(env, argsArray, i); + int len = (*env)->GetStringUTFLength(env, str); + char* buf = malloc(len + 1); + int characterLen = (*env)->GetStringLength(env, str); + (*env)->GetStringUTFRegion(env, str, 0, characterLen, buf); + buf[len] = 0; + argv[i] = buf; + } + + return JLI_Launch(argc, argv, + sizeof(const_jargs) / sizeof(char *), const_jargs, + sizeof(const_appclasspath) / sizeof(char *), const_appclasspath, + FULL_VERSION, + DOT_VERSION, + (const_progname != NULL) ? const_progname : *argv, + (const_launcher != NULL) ? const_launcher : *argv, + (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE, + const_cpwildcard, const_javaw, const_ergo_class); + +} \ No newline at end of file diff --git a/FCLauncher/src/main/jni/fcl/include/fcl_bridge.h b/FCLauncher/src/main/jni/fcl/include/fcl_bridge.h new file mode 100644 index 00000000..a22ab58c --- /dev/null +++ b/FCLauncher/src/main/jni/fcl/include/fcl_bridge.h @@ -0,0 +1,19 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#ifndef FOLD_CRAFT_LAUNCHER_FCL_BRIDGE_H +#define FOLD_CRAFT_LAUNCHER_FCL_BRIDGE_H + +#include +#include "fcl_event.h" + +ANativeWindow* fclGetNativeWindow(void); +int fclWaitForEvent(int timeout); +int fclPollEvent(FCLEvent* event); +int fclGetEventFd(void); +void fclSetCursorMode(int mode); +void fclSetPrimaryClipString(const char* string); +const char* fclGetPrimaryClipString(void); + +#endif //FOLD_CRAFT_LAUNCHER_FCL_BRIDGE_H diff --git a/FCLauncher/src/main/jni/fcl/include/fcl_event.h b/FCLauncher/src/main/jni/fcl/include/fcl_event.h new file mode 100644 index 00000000..98156a3c --- /dev/null +++ b/FCLauncher/src/main/jni/fcl/include/fcl_event.h @@ -0,0 +1,52 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#ifndef FOLD_CRAFT_LAUNCHER_FCL_EVENT_H +#define FOLD_CRAFT_LAUNCHER_FCL_EVENT_H + +#define KeyPress 2 +#define KeyRelease 3 +#define ButtonPress 4 +#define ButtonRelease 5 +#define MotionNotify 6 +#define ConfigureNotify 22 +#define FCLMessage 37 + +#define Button1 1 +#define Button2 2 +#define Button3 3 +#define Button4 4 +#define Button5 5 +#define Button6 6 +#define Button7 7 + +#define CursorEnabled 1 +#define CursorDisabled 0 + +#define ShiftMask (1<<0) +#define LockMask (1<<1) +#define ControlMask (1<<2) +#define Mod1Mask (1<<3) +#define Mod2Mask (1<<4) +#define Mod3Mask (1<<5) +#define Mod4Mask (1<<6) +#define Mod5Mask (1<<7) + +#define CloseRequest 0 + +typedef struct { + long long time; + int type; + int state; + int button; + int message; + int x; + int y; + int keycode; + int keychar; + int width; + int height; +} FCLEvent; + +#endif //FOLD_CRAFT_LAUNCHER_FCL_EVENT_H diff --git a/FCLauncher/src/main/jni/fcl/include/fcl_internal.h b/FCLauncher/src/main/jni/fcl/include/fcl_internal.h new file mode 100644 index 00000000..649b70eb --- /dev/null +++ b/FCLauncher/src/main/jni/fcl/include/fcl_internal.h @@ -0,0 +1,73 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#ifndef FOLD_CRAFT_LAUNCHER_FCL_INTERNAL_H +#define FOLD_CRAFT_LAUNCHER_FCL_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fcl_bridge.h" +#include "fcl_keycodes.h" + +typedef struct _QueueElement { + struct _QueueElement* next; + FCLEvent event; +} QueueElement; + +typedef struct { + int count; + int capacity; + QueueElement* head; + QueueElement* tail; +} EventQueue; + +typedef struct { + JavaVM* android_jvm; + jclass class_FCLBridge; + ANativeWindow* window; + char* clipboard_string; + EventQueue event_queue; + pthread_mutex_t event_queue_mutex; + int has_event_pipe; + int event_pipe_fd[2]; + int epoll_fd; +} FCLInternal; + +extern FCLInternal fcl; + +#define FCL_INTERNAL_LOG(x...) do { \ + fprintf(stderr, "[FCL Internal] %s:%d\n", __FILE__, __LINE__); \ + fprintf(stderr, x); \ + fprintf(stderr, "\n"); \ + fflush(stderr); \ + } while (0) + +#define PrepareFCLBridgeJNI() \ + JavaVM* vm = fcl.android_jvm; \ + JNIEnv* env = NULL; \ + jint attached = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_2); \ + if (attached == JNI_EDETACHED) { \ + attached = (*vm)->AttachCurrentThread(vm, &env, NULL); \ + if (attached != JNI_OK || env == NULL) { \ + FCL_INTERNAL_LOG("Failed to attach thread to Android JavaVM."); \ + } \ + } \ + do {} while(0) + +#define CallFCLBridgeJNIFunc(return_exp, func_type, func_name, signature, args...) \ + jmethodID FCLBridge_##func_name = (*env)->GetMethodID(env, fcl.class_FCLBridge, #func_name, signature); \ + if (FCLBridge_##func_name == NULL) { \ + FCL_INTERNAL_LOG("Failed to find static method FCLBridge_"#func_name ); \ + } \ + return_exp (*env)->Call##func_type##Method(env, fcl.class_FCLBridge, FCLBridge_##func_name, ##args); \ + do {} while(0) + +#endif //FOLD_CRAFT_LAUNCHER_FCL_INTERNAL_H diff --git a/FCLauncher/src/main/jni/fcl/include/fcl_keycodes.h b/FCLauncher/src/main/jni/fcl/include/fcl_keycodes.h new file mode 100644 index 00000000..54e580eb --- /dev/null +++ b/FCLauncher/src/main/jni/fcl/include/fcl_keycodes.h @@ -0,0 +1,149 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#ifndef FOLD_CRAFT_LAUNCHER_FCL_KEYCODE_H +#define FOLD_CRAFT_LAUNCHER_FCL_KEYCODE_H + +/* + * from linux/input-event-codes.h +**/ + +#define FCL_MAX_SCANCODE 240 +#define FCL_MIN_SCANCODE 0 + +#define KEY_RESERVED 0 + +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 + +#define KEY_F11 87 +#define KEY_F12 88 + +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 + +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 + +#define KEY_KPEQUAL 117 + +#define KEY_PAUSE 119 + +#define KEY_KPCOMMA 121 + +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 + +#define KEY_MENU 139 + +#define KEY_F13 183 +#define KEY_F14 184 +#define KEY_F15 185 +#define KEY_F16 186 +#define KEY_F17 187 +#define KEY_F18 188 +#define KEY_F19 189 +#define KEY_F20 190 +#define KEY_F21 191 +#define KEY_F22 192 +#define KEY_F23 193 +#define KEY_F24 194 + +#define KEY_PRINT 210 + +#define KEY_UNKNOWN 240 + +#endif //FOLD_CRAFT_LAUNCHER_FCL_KEYCODE_H diff --git a/FCLauncher/src/main/jni/glfw/context.c b/FCLauncher/src/main/jni/glfw/context.c new file mode 100644 index 00000000..c665a2eb --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/context.c @@ -0,0 +1,731 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Checks whether the desired context attributes are valid +// +// This function checks things like whether the specified client API version +// exists and whether all relevant options have supported and non-conflicting +// values +// +GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) +{ + if (ctxconfig->share) + { + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->share->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return GLFW_FALSE; + } + } + + if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && + ctxconfig->source != GLFW_EGL_CONTEXT_API && + ctxconfig->source != GLFW_OSMESA_CONTEXT_API) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context creation API 0x%08X", + ctxconfig->source); + return GLFW_FALSE; + } + + if (ctxconfig->client != GLFW_NO_API && + ctxconfig->client != GLFW_OPENGL_API && + ctxconfig->client != GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid client API 0x%08X", + ctxconfig->client); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || + (ctxconfig->major == 1 && ctxconfig->minor > 5) || + (ctxconfig->major == 2 && ctxconfig->minor > 1) || + (ctxconfig->major == 3 && ctxconfig->minor > 3)) + { + // OpenGL 1.0 is the smallest valid version + // OpenGL 1.x series ended with version 1.5 + // OpenGL 2.x series ended with version 2.1 + // OpenGL 3.x series ended with version 3.3 + // For now, let everything else through + + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid OpenGL version %i.%i", + ctxconfig->major, ctxconfig->minor); + return GLFW_FALSE; + } + + if (ctxconfig->profile) + { + if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE && + ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid OpenGL profile 0x%08X", + ctxconfig->profile); + return GLFW_FALSE; + } + + if (ctxconfig->major <= 2 || + (ctxconfig->major == 3 && ctxconfig->minor < 2)) + { + // Desktop OpenGL context profiles are only defined for version 3.2 + // and above + + _glfwInputError(GLFW_INVALID_VALUE, + "Context profiles are only defined for OpenGL version 3.2 and above"); + return GLFW_FALSE; + } + } + + if (ctxconfig->forward && ctxconfig->major <= 2) + { + // Forward-compatible contexts are only defined for OpenGL version 3.0 and above + _glfwInputError(GLFW_INVALID_VALUE, + "Forward-compatibility is only defined for OpenGL version 3.0 and above"); + return GLFW_FALSE; + } + } + else if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major < 1 || ctxconfig->minor < 0 || + (ctxconfig->major == 1 && ctxconfig->minor > 1) || + (ctxconfig->major == 2 && ctxconfig->minor > 0)) + { + // OpenGL ES 1.0 is the smallest valid version + // OpenGL ES 1.x series ended with version 1.1 + // OpenGL ES 2.x series ended with version 2.0 + // For now, let everything else through + + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid OpenGL ES version %i.%i", + ctxconfig->major, ctxconfig->minor); + return GLFW_FALSE; + } + } + + if (ctxconfig->robustness) + { + if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION && + ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context robustness mode 0x%08X", + ctxconfig->robustness); + return GLFW_FALSE; + } + } + + if (ctxconfig->release) + { + if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE && + ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context release behavior 0x%08X", + ctxconfig->release); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +// Chooses the framebuffer config that best matches the desired one +// +const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, + const _GLFWfbconfig* alternatives, + unsigned int count) +{ + unsigned int i; + unsigned int missing, leastMissing = UINT_MAX; + unsigned int colorDiff, leastColorDiff = UINT_MAX; + unsigned int extraDiff, leastExtraDiff = UINT_MAX; + const _GLFWfbconfig* current; + const _GLFWfbconfig* closest = NULL; + + for (i = 0; i < count; i++) + { + current = alternatives + i; + + if (desired->stereo > 0 && current->stereo == 0) + { + // Stereo is a hard constraint + continue; + } + + // Count number of missing buffers + { + missing = 0; + + if (desired->alphaBits > 0 && current->alphaBits == 0) + missing++; + + if (desired->depthBits > 0 && current->depthBits == 0) + missing++; + + if (desired->stencilBits > 0 && current->stencilBits == 0) + missing++; + + if (desired->auxBuffers > 0 && + current->auxBuffers < desired->auxBuffers) + { + missing += desired->auxBuffers - current->auxBuffers; + } + + if (desired->samples > 0 && current->samples == 0) + { + // Technically, several multisampling buffers could be + // involved, but that's a lower level implementation detail and + // not important to us here, so we count them as one + missing++; + } + + if (desired->transparent != current->transparent) + missing++; + } + + // These polynomials make many small channel size differences matter + // less than one large channel size difference + + // Calculate color channel size difference value + { + colorDiff = 0; + + if (desired->redBits != GLFW_DONT_CARE) + { + colorDiff += (desired->redBits - current->redBits) * + (desired->redBits - current->redBits); + } + + if (desired->greenBits != GLFW_DONT_CARE) + { + colorDiff += (desired->greenBits - current->greenBits) * + (desired->greenBits - current->greenBits); + } + + if (desired->blueBits != GLFW_DONT_CARE) + { + colorDiff += (desired->blueBits - current->blueBits) * + (desired->blueBits - current->blueBits); + } + } + + // Calculate non-color channel size difference value + { + extraDiff = 0; + + if (desired->alphaBits != GLFW_DONT_CARE) + { + extraDiff += (desired->alphaBits - current->alphaBits) * + (desired->alphaBits - current->alphaBits); + } + + if (desired->depthBits != GLFW_DONT_CARE) + { + extraDiff += (desired->depthBits - current->depthBits) * + (desired->depthBits - current->depthBits); + } + + if (desired->stencilBits != GLFW_DONT_CARE) + { + extraDiff += (desired->stencilBits - current->stencilBits) * + (desired->stencilBits - current->stencilBits); + } + + if (desired->accumRedBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumRedBits - current->accumRedBits) * + (desired->accumRedBits - current->accumRedBits); + } + + if (desired->accumGreenBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumGreenBits - current->accumGreenBits) * + (desired->accumGreenBits - current->accumGreenBits); + } + + if (desired->accumBlueBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumBlueBits - current->accumBlueBits) * + (desired->accumBlueBits - current->accumBlueBits); + } + + if (desired->accumAlphaBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) * + (desired->accumAlphaBits - current->accumAlphaBits); + } + + if (desired->samples != GLFW_DONT_CARE) + { + extraDiff += (desired->samples - current->samples) * + (desired->samples - current->samples); + } + + if (desired->sRGB && !current->sRGB) + extraDiff++; + } + + // Figure out if the current one is better than the best one found so far + // Least number of missing buffers is the most important heuristic, + // then color buffer size match and lastly size match for other buffers + + if (missing < leastMissing) + closest = current; + else if (missing == leastMissing) + { + if ((colorDiff < leastColorDiff) || + (colorDiff == leastColorDiff && extraDiff < leastExtraDiff)) + { + closest = current; + } + } + + if (current == closest) + { + leastMissing = missing; + leastColorDiff = colorDiff; + leastExtraDiff = extraDiff; + } + } + + return closest; +} + +// Retrieves the attributes of the current context +// +GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig) +{ + int i; + _GLFWwindow* previous; + const char* version; + const char* prefixes[] = + { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + NULL + }; + + window->context.source = ctxconfig->source; + window->context.client = GLFW_OPENGL_API; + + previous = _glfwPlatformGetTls(&_glfw.contextSlot); + glfwMakeContextCurrent((GLFWwindow*) window); + + window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) + window->context.getProcAddress("glGetIntegerv"); + window->context.GetString = (PFNGLGETSTRINGPROC) + window->context.getProcAddress("glGetString"); + if (!window->context.GetIntegerv || !window->context.GetString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + version = (const char*) window->context.GetString(GL_VERSION); + if (!version) + { + if (ctxconfig->client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OpenGL version string retrieval is broken"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OpenGL ES version string retrieval is broken"); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + for (i = 0; prefixes[i]; i++) + { + const size_t length = strlen(prefixes[i]); + + if (strncmp(version, prefixes[i], length) == 0) + { + version += length; + window->context.client = GLFW_OPENGL_ES_API; + break; + } + } + + if (!sscanf(version, "%d.%d.%d", + &window->context.major, + &window->context.minor, + &window->context.revision)) + { + if (window->context.client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "No version found in OpenGL version string"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "No version found in OpenGL ES version string"); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + if (window->context.major < ctxconfig->major || + (window->context.major == ctxconfig->major && + window->context.minor < ctxconfig->minor)) + { + // The desired OpenGL version is greater than the actual version + // This only happens if the machine lacks {GLX|WGL}_ARB_create_context + // /and/ the user has requested an OpenGL version greater than 1.0 + + // For API consistency, we emulate the behavior of the + // {GLX|WGL}_ARB_create_context extension and fail here + + if (window->context.client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "Requested OpenGL version %i.%i, got version %i.%i", + ctxconfig->major, ctxconfig->minor, + window->context.major, window->context.minor); + } + else + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "Requested OpenGL ES version %i.%i, got version %i.%i", + ctxconfig->major, ctxconfig->minor, + window->context.major, window->context.minor); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + + if (window->context.major >= 3) + { + // OpenGL 3.0+ uses a different function for extension string retrieval + // We cache it here instead of in glfwExtensionSupported mostly to alert + // users as early as possible that their build may be broken + + window->context.GetStringi = (PFNGLGETSTRINGIPROC) + window->context.getProcAddress("glGetStringi"); + if (!window->context.GetStringi) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Entry point retrieval is broken"); + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_FALSE; + } + } + + if (window->context.client == GLFW_OPENGL_API) + { + // Read back context flags (OpenGL 3.0 and above) + if (window->context.major >= 3) + { + GLint flags; + window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags); + + if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) + window->context.forward = GLFW_TRUE; + + if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) + window->context.debug = GLFW_TRUE; + else if (glfwExtensionSupported("GL_ARB_debug_output") && + ctxconfig->debug) + { + // HACK: This is a workaround for older drivers (pre KHR_debug) + // not setting the debug bit in the context flags for + // debug contexts + window->context.debug = GLFW_TRUE; + } + + if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR) + window->context.noerror = GLFW_TRUE; + } + + // Read back OpenGL context profile (OpenGL 3.2 and above) + if (window->context.major >= 4 || + (window->context.major == 3 && window->context.minor >= 2)) + { + GLint mask; + window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + + if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; + else if (mask & GL_CONTEXT_CORE_PROFILE_BIT) + window->context.profile = GLFW_OPENGL_CORE_PROFILE; + else if (glfwExtensionSupported("GL_ARB_compatibility")) + { + // HACK: This is a workaround for the compatibility profile bit + // not being set in the context flags if an OpenGL 3.2+ + // context was created without having requested a specific + // version + window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; + } + } + + // Read back robustness strategy + if (glfwExtensionSupported("GL_ARB_robustness")) + { + // NOTE: We avoid using the context flags for detection, as they are + // only present from 3.0 while the extension applies from 1.1 + + GLint strategy; + window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, + &strategy); + + if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) + window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; + else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) + window->context.robustness = GLFW_NO_RESET_NOTIFICATION; + } + } + else + { + // Read back robustness strategy + if (glfwExtensionSupported("GL_EXT_robustness")) + { + // NOTE: The values of these constants match those of the OpenGL ARB + // one, so we can reuse them here + + GLint strategy; + window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, + &strategy); + + if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) + window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; + else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) + window->context.robustness = GLFW_NO_RESET_NOTIFICATION; + } + } + + if (glfwExtensionSupported("GL_KHR_context_flush_control")) + { + GLint behavior; + window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior); + + if (behavior == GL_NONE) + window->context.release = GLFW_RELEASE_BEHAVIOR_NONE; + else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) + window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; + } + + // Clearing the front buffer to black to avoid garbage pixels left over from + // previous uses of our bit of VRAM + { + PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) + window->context.getProcAddress("glClear"); + glClear(GL_COLOR_BUFFER_BIT); + + if (window->doublebuffer) + window->context.swapBuffers(window); + } + + glfwMakeContextCurrent((GLFWwindow*) previous); + return GLFW_TRUE; +} + +// Searches an extension string for the specified extension +// +GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) +{ + const char* start = extensions; + + for (;;) + { + const char* where; + const char* terminator; + + where = strstr(start, string); + if (!where) + return GLFW_FALSE; + + terminator = where + strlen(string); + if (where == start || *(where - 1) == ' ') + { + if (*terminator == ' ' || *terminator == '\0') + break; + } + + start = terminator; + } + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); + + _GLFW_REQUIRE_INIT(); + + if (window && window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot make current with a window that has no OpenGL or OpenGL ES context"); + return; + } + + if (previous) + { + if (!window || window->context.source != previous->context.source) + previous->context.makeCurrent(NULL); + } + + if (window) + window->context.makeCurrent(window); +} + +GLFWAPI GLFWwindow* glfwGetCurrentContext(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetTls(&_glfw.contextSlot); +} + +GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot swap buffers of a window that has no OpenGL or OpenGL ES context"); + return; + } + + window->context.swapBuffers(window); +} + +GLFWAPI void glfwSwapInterval(int interval) +{ + _GLFWwindow* window; + + _GLFW_REQUIRE_INIT(); + + window = _glfwPlatformGetTls(&_glfw.contextSlot); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot set swap interval without a current OpenGL or OpenGL ES context"); + return; + } + + window->context.swapInterval(interval); +} + +GLFWAPI int glfwExtensionSupported(const char* extension) +{ + _GLFWwindow* window; + assert(extension != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + window = _glfwPlatformGetTls(&_glfw.contextSlot); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot query extension without a current OpenGL or OpenGL ES context"); + return GLFW_FALSE; + } + + if (*extension == '\0') + { + _glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string"); + return GLFW_FALSE; + } + + if (window->context.major >= 3) + { + int i; + GLint count; + + // Check if extension is in the modern OpenGL extensions string list + + window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count); + + for (i = 0; i < count; i++) + { + const char* en = (const char*) + window->context.GetStringi(GL_EXTENSIONS, i); + if (!en) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Extension string retrieval is broken"); + return GLFW_FALSE; + } + + if (strcmp(en, extension) == 0) + return GLFW_TRUE; + } + } + else + { + // Check if extension is in the old style OpenGL extensions string + + const char* extensions = (const char*) + window->context.GetString(GL_EXTENSIONS); + if (!extensions) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Extension string retrieval is broken"); + return GLFW_FALSE; + } + + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + // Check if extension is in the platform-specific string + return window->context.extensionSupported(extension); +} + +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) +{ + _GLFWwindow* window; + assert(procname != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + window = _glfwPlatformGetTls(&_glfw.contextSlot); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, + "Cannot query entry point without a current OpenGL or OpenGL ES context"); + return NULL; + } + + return window->context.getProcAddress(procname); +} + diff --git a/FCLauncher/src/main/jni/glfw/egl_context.c b/FCLauncher/src/main/jni/glfw/egl_context.c new file mode 100644 index 00000000..fa54f4a8 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/egl_context.c @@ -0,0 +1,653 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include +#include +#include + + +// Return a description of the specified EGL error +// +static const char* getEGLErrorString(EGLint error) +{ + switch (error) + { + case EGL_SUCCESS: + return "Success"; + case EGL_NOT_INITIALIZED: + return "EGL is not or could not be initialized"; + case EGL_BAD_ACCESS: + return "EGL cannot access a requested resource"; + case EGL_BAD_ALLOC: + return "EGL failed to allocate resources for the requested operation"; + case EGL_BAD_ATTRIBUTE: + return "An unrecognized attribute or attribute value was passed in the attribute list"; + case EGL_BAD_CONTEXT: + return "An EGLContext argument does not name a valid EGL rendering context"; + case EGL_BAD_CONFIG: + return "An EGLConfig argument does not name a valid EGL frame buffer configuration"; + case EGL_BAD_CURRENT_SURFACE: + return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid"; + case EGL_BAD_DISPLAY: + return "An EGLDisplay argument does not name a valid EGL display connection"; + case EGL_BAD_SURFACE: + return "An EGLSurface argument does not name a valid surface configured for GL rendering"; + case EGL_BAD_MATCH: + return "Arguments are inconsistent"; + case EGL_BAD_PARAMETER: + return "One or more argument values are invalid"; + case EGL_BAD_NATIVE_PIXMAP: + return "A NativePixmapType argument does not refer to a valid native pixmap"; + case EGL_BAD_NATIVE_WINDOW: + return "A NativeWindowType argument does not refer to a valid native window"; + case EGL_CONTEXT_LOST: + return "The application must destroy all contexts and reinitialise"; + default: + return "ERROR: UNKNOWN EGL ERROR"; + } +} + +// Returns the specified attribute of the specified EGLConfig +// +static int getEGLConfigAttrib(EGLConfig config, int attrib) +{ + int value; + eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value); + return value; +} + +// Return the EGLConfig most closely matching the specified hints +// +static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* desired, + EGLConfig* result) +{ + EGLConfig* nativeConfigs; + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, nativeCount, usableCount; + + eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); + if (!nativeCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned"); + return GLFW_FALSE; + } + + nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); + eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); + + usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableCount = 0; + + for (i = 0; i < nativeCount; i++) + { + const EGLConfig n = nativeConfigs[i]; + _GLFWfbconfig* u = usableConfigs + usableCount; + + // Only consider RGB(A) EGLConfigs + if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) + continue; + + // Only consider window EGLConfigs + if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) + continue; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) + continue; + } + else + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) + continue; + } + } + else if (ctxconfig->client == GLFW_OPENGL_API) + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) + continue; + } + + u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); + u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE); + u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE); + + u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE); + u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); + u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); + + u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); + u->doublebuffer = desired->doublebuffer; + + u->handle = (uintptr_t) n; + usableCount++; + } + + closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + if (closest) + *result = (EGLConfig) closest->handle; + + free(nativeConfigs); + free(usableConfigs); + + return closest != NULL; +} + +static void makeContextCurrentEGL(_GLFWwindow* window) +{ + if (window) + { + if (!eglMakeCurrent(_glfw.egl.display, + window->context.egl.surface, + window->context.egl.surface, + window->context.egl.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to make context current: %s", + getEGLErrorString(eglGetError())); + return; + } + } + else + { + if (!eglMakeCurrent(_glfw.egl.display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to clear current context: %s", + getEGLErrorString(eglGetError())); + return; + } + } + + _glfwPlatformSetTls(&_glfw.contextSlot, window); +} + +static void swapBuffersEGL(_GLFWwindow* window) +{ + if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: The context must be current on the calling thread when swapping buffers"); + return; + } + + eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); +} + +static void swapIntervalEGL(int interval) +{ + eglSwapInterval(_glfw.egl.display, interval); +} + +static int extensionSupportedEGL(const char* extension) +{ + const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressEGL(const char* procname) +{ + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + + if (window->context.egl.client) + { + GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, + procname); + if (proc) + return proc; + } + + return eglGetProcAddress(procname); +} + +static void destroyContextEGL(_GLFWwindow* window) +{ + { + if (window->context.egl.client) + { + _glfw_dlclose(window->context.egl.client); + window->context.egl.client = NULL; + } + } + + if (window->context.egl.surface) + { + eglDestroySurface(_glfw.egl.display, window->context.egl.surface); + window->context.egl.surface = EGL_NO_SURFACE; + } + + if (window->context.egl.handle) + { + eglDestroyContext(_glfw.egl.display, window->context.egl.handle); + window->context.egl.handle = EGL_NO_CONTEXT; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize EGL +// +GLFWbool _glfwInitEGL(void) +{ + int i; + const char* sonames[] = + { + getenv("LIBEGL_NAME") + }; + + if (_glfw.egl.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.egl.handle = _glfw_dlopen(sonames[i]); + if (_glfw.egl.handle) + break; + } + + if (!_glfw.egl.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found"); + return GLFW_FALSE; + } + + _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); + + _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) + _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); + _glfw.egl.GetConfigs = (PFN_eglGetConfigs) + _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); + _glfw.egl.GetDisplay = (PFN_eglGetDisplay) + _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); + _glfw.egl.GetError = (PFN_eglGetError) + _glfw_dlsym(_glfw.egl.handle, "eglGetError"); + _glfw.egl.Initialize = (PFN_eglInitialize) + _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); + _glfw.egl.Terminate = (PFN_eglTerminate) + _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); + _glfw.egl.BindAPI = (PFN_eglBindAPI) + _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); + _glfw.egl.CreateContext = (PFN_eglCreateContext) + _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); + _glfw.egl.DestroySurface = (PFN_eglDestroySurface) + _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); + _glfw.egl.DestroyContext = (PFN_eglDestroyContext) + _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); + _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) + _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); + _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) + _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); + _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) + _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); + _glfw.egl.SwapInterval = (PFN_eglSwapInterval) + _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); + _glfw.egl.QueryString = (PFN_eglQueryString) + _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); + _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) + _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); + + if (!_glfw.egl.GetConfigAttrib || + !_glfw.egl.GetConfigs || + !_glfw.egl.GetDisplay || + !_glfw.egl.GetError || + !_glfw.egl.Initialize || + !_glfw.egl.Terminate || + !_glfw.egl.BindAPI || + !_glfw.egl.CreateContext || + !_glfw.egl.DestroySurface || + !_glfw.egl.DestroyContext || + !_glfw.egl.CreateWindowSurface || + !_glfw.egl.MakeCurrent || + !_glfw.egl.SwapBuffers || + !_glfw.egl.SwapInterval || + !_glfw.egl.QueryString || + !_glfw.egl.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to load required entry points"); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + _glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY); + if (_glfw.egl.display == EGL_NO_DISPLAY) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to get EGL display: %s", + getEGLErrorString(eglGetError())); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to initialize EGL: %s", + getEGLErrorString(eglGetError())); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + _glfw.egl.KHR_create_context = + extensionSupportedEGL("EGL_KHR_create_context"); + _glfw.egl.KHR_create_context_no_error = + extensionSupportedEGL("EGL_KHR_create_context_no_error"); + _glfw.egl.KHR_gl_colorspace = + extensionSupportedEGL("EGL_KHR_gl_colorspace"); + _glfw.egl.KHR_get_all_proc_addresses = + extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); + _glfw.egl.KHR_context_flush_control = + extensionSupportedEGL("EGL_KHR_context_flush_control"); + + return GLFW_TRUE; +} + +// Terminate EGL +// +void _glfwTerminateEGL(void) +{ + if (_glfw.egl.display) + { + eglTerminate(_glfw.egl.display); + _glfw.egl.display = EGL_NO_DISPLAY; + } + + if (_glfw.egl.handle) + { + _glfw_dlclose(_glfw.egl.handle); + _glfw.egl.handle = NULL; + } +} + +#define setAttrib(a, v) \ +{ \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +// Create the OpenGL or OpenGL ES context +// +GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + EGLint attribs[40]; + EGLConfig config; + EGLContext share = NULL; + int index = 0; + + if (!_glfw.egl.display) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available"); + return GLFW_FALSE; + } + + if (ctxconfig->share) + share = ctxconfig->share->context.egl.handle; + + if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "EGL: Failed to find a suitable EGLConfig"); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (!eglBindAPI(EGL_OPENGL_ES_API)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to bind OpenGL ES: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + } + else + { + if (!eglBindAPI(EGL_OPENGL_API)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to bind OpenGL: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + } + + if (_glfw.egl.KHR_create_context) + { + int mask = 0, flags = 0; + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; + } + + if (ctxconfig->debug) + flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; + + if (ctxconfig->robustness) + { + if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) + { + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_NO_RESET_NOTIFICATION_KHR); + } + else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) + { + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_LOSE_CONTEXT_ON_RESET_KHR); + } + + flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; + } + + if (ctxconfig->noerror) + { + if (_glfw.egl.KHR_create_context_no_error) + setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); + setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); + } + + if (mask) + setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); + + if (flags) + setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); + } + else + { + if (ctxconfig->client == GLFW_OPENGL_ES_API) + setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); + } + + if (_glfw.egl.KHR_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + } + } + + setAttrib(EGL_NONE, EGL_NONE); + + window->context.egl.handle = eglCreateContext(_glfw.egl.display, + config, share, attribs); + + if (window->context.egl.handle == EGL_NO_CONTEXT) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "EGL: Failed to create context: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + + // Set up attributes for surface creation + index = 0; + + if (fbconfig->sRGB) + { + if (_glfw.egl.KHR_gl_colorspace) + setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + } + + if (!fbconfig->doublebuffer) + setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); + + setAttrib(EGL_NONE, EGL_NONE); + + window->context.egl.surface = + eglCreateWindowSurface(_glfw.egl.display, + config, + _GLFW_EGL_NATIVE_WINDOW, + attribs); + if (window->context.egl.surface == EGL_NO_SURFACE) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to create window surface: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + + window->context.egl.config = config; + + // Load the appropriate client library + if (!_glfw.egl.KHR_get_all_proc_addresses) + { + int i; + const char** sonames; + const char* es1sonames[] = + { + "libGLESv1_CM.so.1", + "libGLES_CM.so.1", + }; + const char* es2sonames[] = + { + "libGLESv2.so.2", + }; + const char* glsonames[] = + { + "libgl4es.so", + }; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + sonames = es1sonames; + else + sonames = es2sonames; + } + else + sonames = glsonames; + + for (i = 0; sonames[i]; i++) + { + // HACK: Match presence of lib prefix to increase chance of finding + // a matching pair in the jungle that is Win32 EGL/GLES + if (_glfw.egl.prefix != (strncmp(sonames[i], "lib", 3) == 0)) + continue; + + window->context.egl.client = _glfw_dlopen(sonames[i]); + if (window->context.egl.client) + break; + } + + if (!window->context.egl.client) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to load client library"); + return GLFW_FALSE; + } + } + + window->context.makeCurrent = makeContextCurrentEGL; + window->context.swapBuffers = swapBuffersEGL; + window->context.swapInterval = swapIntervalEGL; + window->context.extensionSupported = extensionSupportedEGL; + window->context.getProcAddress = getProcAddressEGL; + window->context.destroy = destroyContextEGL; + + return GLFW_TRUE; +} + +#undef setAttrib + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI EGLDisplay glfwGetEGLDisplay(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY); + return _glfw.egl.display; +} + +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return EGL_NO_CONTEXT; + } + + return window->context.egl.handle; +} + +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return EGL_NO_SURFACE; + } + + return window->context.egl.surface; +} + diff --git a/FCLauncher/src/main/jni/glfw/fcl_init.c b/FCLauncher/src/main/jni/glfw/fcl_init.c new file mode 100644 index 00000000..93a86f48 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/fcl_init.c @@ -0,0 +1,260 @@ +// +// Created by Tungsten on 2022/10/11. +// +//======================================================================== +// This file is derived from x11_init.c +//======================================================================== + +#include + +#include + +#include +#include + + +// Create key code translation tables +// derived from wl_init.c +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.fcl.keycodes, -1, sizeof(_glfw.fcl.keycodes)); + memset(_glfw.fcl.scancodes, -1, sizeof(_glfw.fcl.scancodes)); + + _glfw.fcl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; + _glfw.fcl.keycodes[KEY_1] = GLFW_KEY_1; + _glfw.fcl.keycodes[KEY_2] = GLFW_KEY_2; + _glfw.fcl.keycodes[KEY_3] = GLFW_KEY_3; + _glfw.fcl.keycodes[KEY_4] = GLFW_KEY_4; + _glfw.fcl.keycodes[KEY_5] = GLFW_KEY_5; + _glfw.fcl.keycodes[KEY_6] = GLFW_KEY_6; + _glfw.fcl.keycodes[KEY_7] = GLFW_KEY_7; + _glfw.fcl.keycodes[KEY_8] = GLFW_KEY_8; + _glfw.fcl.keycodes[KEY_9] = GLFW_KEY_9; + _glfw.fcl.keycodes[KEY_0] = GLFW_KEY_0; + _glfw.fcl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; + _glfw.fcl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; + _glfw.fcl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; + _glfw.fcl.keycodes[KEY_Q] = GLFW_KEY_Q; + _glfw.fcl.keycodes[KEY_W] = GLFW_KEY_W; + _glfw.fcl.keycodes[KEY_E] = GLFW_KEY_E; + _glfw.fcl.keycodes[KEY_R] = GLFW_KEY_R; + _glfw.fcl.keycodes[KEY_T] = GLFW_KEY_T; + _glfw.fcl.keycodes[KEY_Y] = GLFW_KEY_Y; + _glfw.fcl.keycodes[KEY_U] = GLFW_KEY_U; + _glfw.fcl.keycodes[KEY_I] = GLFW_KEY_I; + _glfw.fcl.keycodes[KEY_O] = GLFW_KEY_O; + _glfw.fcl.keycodes[KEY_P] = GLFW_KEY_P; + _glfw.fcl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; + _glfw.fcl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; + _glfw.fcl.keycodes[KEY_A] = GLFW_KEY_A; + _glfw.fcl.keycodes[KEY_S] = GLFW_KEY_S; + _glfw.fcl.keycodes[KEY_D] = GLFW_KEY_D; + _glfw.fcl.keycodes[KEY_F] = GLFW_KEY_F; + _glfw.fcl.keycodes[KEY_G] = GLFW_KEY_G; + _glfw.fcl.keycodes[KEY_H] = GLFW_KEY_H; + _glfw.fcl.keycodes[KEY_J] = GLFW_KEY_J; + _glfw.fcl.keycodes[KEY_K] = GLFW_KEY_K; + _glfw.fcl.keycodes[KEY_L] = GLFW_KEY_L; + _glfw.fcl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.fcl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.fcl.keycodes[KEY_Z] = GLFW_KEY_Z; + _glfw.fcl.keycodes[KEY_X] = GLFW_KEY_X; + _glfw.fcl.keycodes[KEY_C] = GLFW_KEY_C; + _glfw.fcl.keycodes[KEY_V] = GLFW_KEY_V; + _glfw.fcl.keycodes[KEY_B] = GLFW_KEY_B; + _glfw.fcl.keycodes[KEY_N] = GLFW_KEY_N; + _glfw.fcl.keycodes[KEY_M] = GLFW_KEY_M; + _glfw.fcl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; + _glfw.fcl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; + _glfw.fcl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; + _glfw.fcl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.fcl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; + _glfw.fcl.keycodes[KEY_TAB] = GLFW_KEY_TAB; + _glfw.fcl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.fcl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.fcl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; + _glfw.fcl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.fcl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; + _glfw.fcl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; + _glfw.fcl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; + _glfw.fcl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; + _glfw.fcl.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.fcl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; + _glfw.fcl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.fcl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; + _glfw.fcl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.fcl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; + _glfw.fcl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; + _glfw.fcl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.fcl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; + _glfw.fcl.keycodes[KEY_HOME] = GLFW_KEY_HOME; + _glfw.fcl.keycodes[KEY_END] = GLFW_KEY_END; + _glfw.fcl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; + _glfw.fcl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.fcl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; + _glfw.fcl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; + _glfw.fcl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; + _glfw.fcl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; + _glfw.fcl.keycodes[KEY_UP] = GLFW_KEY_UP; + _glfw.fcl.keycodes[KEY_F1] = GLFW_KEY_F1; + _glfw.fcl.keycodes[KEY_F2] = GLFW_KEY_F2; + _glfw.fcl.keycodes[KEY_F3] = GLFW_KEY_F3; + _glfw.fcl.keycodes[KEY_F4] = GLFW_KEY_F4; + _glfw.fcl.keycodes[KEY_F5] = GLFW_KEY_F5; + _glfw.fcl.keycodes[KEY_F6] = GLFW_KEY_F6; + _glfw.fcl.keycodes[KEY_F7] = GLFW_KEY_F7; + _glfw.fcl.keycodes[KEY_F8] = GLFW_KEY_F8; + _glfw.fcl.keycodes[KEY_F9] = GLFW_KEY_F9; + _glfw.fcl.keycodes[KEY_F10] = GLFW_KEY_F10; + _glfw.fcl.keycodes[KEY_F11] = GLFW_KEY_F11; + _glfw.fcl.keycodes[KEY_F12] = GLFW_KEY_F12; + _glfw.fcl.keycodes[KEY_F13] = GLFW_KEY_F13; + _glfw.fcl.keycodes[KEY_F14] = GLFW_KEY_F14; + _glfw.fcl.keycodes[KEY_F15] = GLFW_KEY_F15; + _glfw.fcl.keycodes[KEY_F16] = GLFW_KEY_F16; + _glfw.fcl.keycodes[KEY_F17] = GLFW_KEY_F17; + _glfw.fcl.keycodes[KEY_F18] = GLFW_KEY_F18; + _glfw.fcl.keycodes[KEY_F19] = GLFW_KEY_F19; + _glfw.fcl.keycodes[KEY_F20] = GLFW_KEY_F20; + _glfw.fcl.keycodes[KEY_F21] = GLFW_KEY_F21; + _glfw.fcl.keycodes[KEY_F22] = GLFW_KEY_F22; + _glfw.fcl.keycodes[KEY_F23] = GLFW_KEY_F23; + _glfw.fcl.keycodes[KEY_F24] = GLFW_KEY_F24; + _glfw.fcl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; + _glfw.fcl.keycodes[KEY_KPASTERISK] = GLFW_KEY_KP_MULTIPLY; + _glfw.fcl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; + _glfw.fcl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; + _glfw.fcl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; + _glfw.fcl.keycodes[KEY_KP1] = GLFW_KEY_KP_1; + _glfw.fcl.keycodes[KEY_KP2] = GLFW_KEY_KP_2; + _glfw.fcl.keycodes[KEY_KP3] = GLFW_KEY_KP_3; + _glfw.fcl.keycodes[KEY_KP4] = GLFW_KEY_KP_4; + _glfw.fcl.keycodes[KEY_KP5] = GLFW_KEY_KP_5; + _glfw.fcl.keycodes[KEY_KP6] = GLFW_KEY_KP_6; + _glfw.fcl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; + _glfw.fcl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; + _glfw.fcl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; + _glfw.fcl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.fcl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + _glfw.fcl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_DECIMAL; + + for (scancode = 0; scancode < 256; scancode++) + { + // Store the reverse translation for faster key name lookup + if (_glfw.fcl.keycodes[scancode] > 0) + _glfw.fcl.scancodes[_glfw.fcl.keycodes[scancode]] = scancode; + } + + strcpy(_glfw.fcl.keynames[GLFW_KEY_GRAVE_ACCENT], "`"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_1], "1"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_2], "2"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_3], "3"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_4], "4"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_5], "5"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_6], "6"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_7], "7"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_8], "8"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_9], "9"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_0], "0"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_MINUS], "-"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_EQUAL], "="); + strcpy(_glfw.fcl.keynames[GLFW_KEY_Q], "q"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_W], "w"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_E], "e"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_R], "r"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_T], "t"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_Y], "y"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_U], "u"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_I], "i"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_O], "o"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_P], "p"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_LEFT_BRACKET], "["); + strcpy(_glfw.fcl.keynames[GLFW_KEY_RIGHT_BRACKET], "]"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_A], "a"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_S], "s"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_D], "d"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_F], "f"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_G], "g"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_H], "h"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_J], "j"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_K], "k"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_L], "l"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_SEMICOLON], ";"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_APOSTROPHE], "'"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_Z], "z"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_X], "x"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_C], "c"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_V], "v"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_B], "b"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_N], "n"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_M], "m"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_COMMA], ","); + strcpy(_glfw.fcl.keynames[GLFW_KEY_PERIOD], "."); + strcpy(_glfw.fcl.keynames[GLFW_KEY_SLASH], "/"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_BACKSLASH], "\\"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_DIVIDE], "/"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_MULTIPLY], "*"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_ADD], "+"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_SUBTRACT], "-"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_0], "0"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_1], "1"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_2], "2"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_3], "3"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_4], "4"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_5], "5"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_6], "6"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_7], "7"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_8], "8"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_9], "9"); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_EQUAL], "="); + strcpy(_glfw.fcl.keynames[GLFW_KEY_KP_DECIMAL], "."); +} + +// Retrieve system content scale via folklore heuristics +// +static void getSystemContentScale(float* xscale, float* yscale) +{ + *xscale = 1.f; + *yscale = 1.f; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + getSystemContentScale(&_glfw.fcl.contentScaleX, &_glfw.fcl.contentScaleY); + createKeyTables(); + + _glfwInitTimerPOSIX(); + _glfwPollMonitorsFCL(); + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + _glfwTerminateEGL(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " FCL EGL OSMesa" +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + " clock_gettime" +#else + " gettimeofday" +#endif +#if defined(__linux__) + " evdev" +#endif +#if defined(_GLFW_BUILD_DLL) + " shared" +#endif + ; +} + diff --git a/FCLauncher/src/main/jni/glfw/fcl_monitor.c b/FCLauncher/src/main/jni/glfw/fcl_monitor.c new file mode 100644 index 00000000..338eeada --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/fcl_monitor.c @@ -0,0 +1,123 @@ +// +// Created by Tungsten on 2022/10/11. +// +//======================================================================== +// This file is derived from x11_monitor.c +//======================================================================== + +#include + +#include +#include +#include + + +static void modeChangeHandle(int width, int height, void* data) +{ + GLFWvidmode mode; + _GLFWmonitor* monitor = data; + + mode.width = width; + mode.height = height; + mode.redBits = 8; + mode.greenBits = 8; + mode.blueBits = 8; + mode.refreshRate = 0; + + monitor->modeCount++; + monitor->modes = + realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); + monitor->modes[monitor->modeCount - 1] = mode; + + monitor->fcl.currentMode = monitor->modeCount - 1; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPollMonitorsFCL(void) +{ + struct ANativeWindow* window = fclGetNativeWindow(); + const float dpi = 141.f; + _GLFWmonitor* monitor = _glfwAllocMonitor("FCL Monitor 0", + (int) (ANativeWindow_getWidth(window) * 25.4f / dpi), + (int) (ANativeWindow_getHeight(window) * 25.4f / dpi)); + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_FIRST); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) +{ +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.fcl.contentScaleX; + if (yscale) + *yscale = _glfw.fcl.contentScaleY; +} + +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) +{ + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + if (width) + *width = monitor->modes[monitor->fcl.currentMode].width; + if (height) + *height = monitor->modes[monitor->fcl.currentMode].height; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +{ + if (monitor->modes == NULL || monitor->modeCount == 0) { + struct ANativeWindow* window = fclGetNativeWindow(); + modeChangeHandle(ANativeWindow_getWidth(window), + ANativeWindow_getHeight(window), + monitor); + } + *count = monitor->modeCount; + return monitor->modes; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + if (monitor->modes == NULL || monitor->modeCount == 0) { + struct ANativeWindow* window = fclGetNativeWindow(); + modeChangeHandle(ANativeWindow_getWidth(window), + ANativeWindow_getHeight(window), + monitor); + } + *mode = monitor->modes[monitor->fcl.currentMode]; +} + +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "FCL: Gamma ramp access not supported"); + return GLFW_FALSE; +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "FCL: Gamma ramp access not supported"); +} + diff --git a/FCLauncher/src/main/jni/glfw/fcl_window.c b/FCLauncher/src/main/jni/glfw/fcl_window.c new file mode 100644 index 00000000..2d22cc0a --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/fcl_window.c @@ -0,0 +1,732 @@ +// +// Created by Tungsten on 2022/10/11. +// +//======================================================================== +// This file is derived from x11_window.c +//======================================================================== + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Translates an FCL event modifier state mask +// +static int translateState(int state) +{ + int mods = 0; + + if (state & ShiftMask) + mods |= GLFW_MOD_SHIFT; + if (state & ControlMask) + mods |= GLFW_MOD_CONTROL; + if (state & Mod1Mask) + mods |= GLFW_MOD_ALT; + if (state & Mod4Mask) + mods |= GLFW_MOD_SUPER; + if (state & LockMask) + mods |= GLFW_MOD_CAPS_LOCK; + if (state & Mod2Mask) + mods |= GLFW_MOD_NUM_LOCK; + + return mods; +} + +// Translates an FCL key code to a GLFW key token +// +static int translateKey(int scancode) +{ + // Use the pre-filled LUT (see createKeyTables() in fcl_init.c) + if (scancode < 0 || scancode > 255) + return GLFW_KEY_UNKNOWN; + + return _glfw.fcl.keycodes[scancode]; +} + +// Apply disabled cursor mode to a focused window +// +static void disableCursor(_GLFWwindow* window) +{ + _glfw.fcl.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.fcl.restoreCursorPosX, + &_glfw.fcl.restoreCursorPosY); + // updateCursorImage(window); + _glfwCenterCursorInContentArea(window); + fclSetCursorMode(CursorDisabled); +} + +// Exit disabled cursor mode for the specified window +// +static void enableCursor(_GLFWwindow* window) +{ + _glfw.fcl.disabledCursorWindow = NULL; + fclSetCursorMode(CursorEnabled); + _glfwPlatformSetCursorPos(window, + _glfw.fcl.restoreCursorPosX, + _glfw.fcl.restoreCursorPosY); + // updateCursorImage(window); +} + +// Get the ANativeWindow and peer infomation +// +static GLFWbool createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + // window width and height requirements ignored + window->fcl.handle = fclGetNativeWindow(); + ANativeWindow_acquire(window->fcl.handle); + + if (!window->fcl.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "FCL: Failed to get window"); + return GLFW_FALSE; + } + + _glfw.fcl.eventCurrent = window; + + if (!wndconfig->decorated) + _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); + + window->fcl.maximized = GLFW_TRUE; + _glfwPlatformSetWindowTitle(window, wndconfig->title); + _glfwPlatformGetWindowPos(window, &window->fcl.xpos, &window->fcl.ypos); + _glfwPlatformGetWindowSize(window, &window->fcl.width, &window->fcl.height); + + return GLFW_TRUE; +} + +// Make the specified window and its video mode active on its monitor +// +static void acquireMonitor(_GLFWwindow* window) +{ + _glfwInputMonitorWindow(window->monitor, window); +} + +// Remove the window +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindow(window->monitor, NULL); +} + +// Process the specified FCL event +// +static void processEvent(FCLEvent *event) +{ + _GLFWwindow* window = _glfw.fcl.eventCurrent; + + switch (event->type) + { + case KeyPress: + { + const int keycode = event->keycode; + const int keychar = event->keychar; + const int key = translateKey(keycode); + const int mods = translateState(event->state); + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + + _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); + if (keychar) { + _glfwInputChar(window, keychar, mods, plain); + } + return; + } + + case KeyRelease: + { + const int keycode = event->keycode; + const int key = translateKey(keycode); + const int mods = translateState(event->state); + + _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods); + return; + } + + case ButtonPress: + { + const int mods = translateState(event->state); + + if (event->button == Button1) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); + else if (event->button == Button2) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); + else if (event->button == Button3) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); + + // Like X11, FCL provides scroll events as mouse button presses + else if (event->button == Button4) + _glfwInputScroll(window, 0.0, 1.0); + else if (event->button == Button5) + _glfwInputScroll(window, 0.0, -1.0); + else if (event->button == Button6) + _glfwInputScroll(window, 1.0, 0.0); + else if (event->button == Button7) + _glfwInputScroll(window, -1.0, 0.0); + + else + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + _glfwInputMouseClick(window, + event->button - Button1 - 4, + GLFW_PRESS, + mods); + } + + return; + } + + case ButtonRelease: + { + const int mods = translateState(event->state); + + if (event->button == Button1) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE, + mods); + } + else if (event->button == Button2) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_MIDDLE, + GLFW_RELEASE, + mods); + } + else if (event->button == Button3) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE, + mods); + } + else if (event->button > Button7) + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + _glfwInputMouseClick(window, + event->button - Button1 - 4, + GLFW_RELEASE, + mods); + } + + return; + } + + case MotionNotify: + { + const int x = event->x; + const int y = event->y; + + if (x != window->fcl.warpCursorPosX || + y != window->fcl.warpCursorPosY) + { + // The cursor was moved by something other than GLFW + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (_glfw.fcl.disabledCursorWindow != window) + return; + if (window->rawMouseMotion) + return; + + const int dx = x - window->fcl.lastCursorPosX; + const int dy = y - window->fcl.lastCursorPosY; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + } + else + _glfwInputCursorPos(window, x, y); + } + + window->fcl.lastCursorPosX = x; + window->fcl.lastCursorPosY = y; + return; + } + + case ConfigureNotify: + { + const int width = event->width; + const int height = event->height; + if (width != window->fcl.width || + height != window->fcl.height) + { + _glfwInputFramebufferSize(window, + width, + height); + + _glfwInputWindowSize(window, + width, + height); + + window->fcl.width = width; + window->fcl.height = height; + } + + return; + } + + case FCLMessage: + { + if (event->message == CloseRequest) + { + // The FCL was asked to close the window, for + // example by the user pressing 'back' key + _glfwInputWindowCloseRequest(window); + } + } + } +} + +static void handleEvents(int timeout) +{ + if (fclWaitForEvent(timeout) == 0) { + return; + } + FCLEvent event; + while (fclPollEvent(&event)) + { + processEvent(&event); + if (fclWaitForEvent(0) == 0) { + break; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createNativeWindow(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_EGL_CONTEXT_API || + ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + + if (window->monitor) + { + _glfwPlatformShowWindow(window); + acquireMonitor(window); + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (_glfw.fcl.disabledCursorWindow == window) + _glfw.fcl.disabledCursorWindow = NULL; + + if (window->monitor) + releaseMonitor(window); + + if (window->context.destroy) + window->context.destroy(window); + + if (window->fcl.handle) + { + ANativeWindow_release(window->fcl.handle); + window->fcl.handle = NULL; + } +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = ANativeWindow_getWidth(window->fcl.handle); + if (height) + *height = ANativeWindow_getHeight(window->fcl.handle); +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->monitor) + { + if (window->monitor->window == window) + acquireMonitor(window); + } +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + _glfwPlatformGetWindowSize(window, width, height); +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + if (left) + *left = 0; + if (top) + *top = 0; + if (right) + *right = 0; + if (bottom) + *bottom = 0; +} + +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.fcl.contentScaleX; + if (yscale) + *yscale = _glfw.fcl.contentScaleY; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + // Are these codes meaningful? + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + + return; + } + + if (window->monitor) + { + _glfwPlatformSetWindowDecorated(window, window->decorated); + _glfwPlatformSetWindowFloating(window, window->floating); + releaseMonitor(window); + } + + _glfwInputWindowMonitor(window, monitor); + + if (window->monitor) + { + acquireMonitor(window); + } +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return GLFW_TRUE; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return GLFW_TRUE; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return GLFW_TRUE; +} + +int _glfwPlatformWindowHovered(_GLFWwindow* window) +{ + return GLFW_TRUE; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) +{ +} + +GLFWbool _glfwPlatformRawMouseMotionSupported(void) +{ + return GLFW_FALSE; +} + +void _glfwPlatformPollEvents(void) +{ + handleEvents(0); +} + +void _glfwPlatformWaitEvents(void) +{ + handleEvents(-1); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + handleEvents((int) (timeout * 1e3)); +} + +void _glfwPlatformPostEmptyEvent(void) +{ +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + int x, y; + + // fclGetCursorPos(&x, &y); + x = 0; + y = 0; + + if (xpos) + *xpos = x; + if (ypos) + *ypos = y; +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ + // Store the new position so it can be recognized later + window->fcl.warpCursorPosX = (int) x; + window->fcl.warpCursorPosY = (int) y; + // fclSetCursorPos(x, y); +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + if (mode == GLFW_CURSOR_DISABLED) + { + if (_glfwPlatformWindowFocused(window)) + disableCursor(window); + } + else if (_glfw.fcl.disabledCursorWindow == window) + enableCursor(window); + // else + // updateCursorImage(window); +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + if (scancode < 0 || scancode > 0xff || + _glfw.fcl.keycodes[scancode] == GLFW_KEY_UNKNOWN) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); + return NULL; + } + + const int key = _glfw.fcl.keycodes[scancode]; + return _glfw.fcl.keynames[key]; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.fcl.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + // updateCursorImage(window); + } +} + +void _glfwPlatformSetClipboardString(const char* string) +{ + fclSetPrimaryClipString(string); +} + +const char* _glfwPlatformGetClipboardString(void) +{ + return fclGetPrimaryClipString(); +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_android_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_android_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + return GLFW_FALSE; +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + VkResult err; + VkAndroidSurfaceCreateInfoKHR sci; + PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR; + + vkCreateAndroidSurfaceKHR = (PFN_vkCreateAndroidSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateAndroidSurfaceKHR"); + if (!vkCreateAndroidSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "FCL: Vulkan instance missing VK_KHR_android_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + sci.window = window->fcl.handle; + + err = vkCreateAndroidSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "FCL: Failed to create Vulkan Android surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI struct ANativeWindow* glfwGetFCLWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->fcl.handle; +} + diff --git a/FCLauncher/src/main/jni/glfw/include/egl_context.h b/FCLauncher/src/main/jni/glfw/include/egl_context.h new file mode 100644 index 00000000..57d854a4 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/egl_context.h @@ -0,0 +1,170 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#define EGLAPIENTRY +typedef void* EGLNativeDisplayType; +typedef struct ANativeWindow* EGLNativeWindowType; + +#define EGL_SUCCESS 0x3000 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300a +#define EGL_BAD_NATIVE_WINDOW 0x300b +#define EGL_BAD_PARAMETER 0x300c +#define EGL_BAD_SURFACE 0x300d +#define EGL_CONTEXT_LOST 0x300e +#define EGL_COLOR_BUFFER_TYPE 0x303f +#define EGL_RGB_BUFFER 0x308e +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_WINDOW_BIT 0x0004 +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_OPENGL_ES_BIT 0x0001 +#define EGL_OPENGL_ES2_BIT 0x0004 +#define EGL_OPENGL_BIT 0x0008 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_RED_SIZE 0x3024 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_SAMPLES 0x3031 +#define EGL_OPENGL_ES_API 0x30a0 +#define EGL_OPENGL_API 0x30a2 +#define EGL_NONE 0x3038 +#define EGL_RENDER_BUFFER 0x3086 +#define EGL_SINGLE_BUFFER 0x3085 +#define EGL_EXTENSIONS 0x3055 +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 +#define EGL_NATIVE_VISUAL_ID 0x302e +#define EGL_NO_SURFACE ((EGLSurface) 0) +#define EGL_NO_DISPLAY ((EGLDisplay) 0) +#define EGL_NO_CONTEXT ((EGLContext) 0) +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) + +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd +#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be +#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf +#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 +#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 +#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb +#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd +#define EGL_CONTEXT_FLAGS_KHR 0x30fc +#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 +#define EGL_GL_COLORSPACE_KHR 0x309d +#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 + +typedef int EGLint; +typedef unsigned int EGLBoolean; +typedef unsigned int EGLenum; +typedef void* EGLConfig; +typedef void* EGLContext; +typedef void* EGLDisplay; +typedef void* EGLSurface; + +// EGL function pointer typedefs +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); +typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); +typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); +typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); +typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); +typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); +typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); +#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib +#define eglGetConfigs _glfw.egl.GetConfigs +#define eglGetDisplay _glfw.egl.GetDisplay +#define eglGetError _glfw.egl.GetError +#define eglInitialize _glfw.egl.Initialize +#define eglTerminate _glfw.egl.Terminate +#define eglBindAPI _glfw.egl.BindAPI +#define eglCreateContext _glfw.egl.CreateContext +#define eglDestroySurface _glfw.egl.DestroySurface +#define eglDestroyContext _glfw.egl.DestroyContext +#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface +#define eglMakeCurrent _glfw.egl.MakeCurrent +#define eglSwapBuffers _glfw.egl.SwapBuffers +#define eglSwapInterval _glfw.egl.SwapInterval +#define eglQueryString _glfw.egl.QueryString +#define eglGetProcAddress _glfw.egl.GetProcAddress + +#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl + + +// EGL-specific per-context data +// +typedef struct _GLFWcontextEGL +{ + EGLConfig config; + EGLContext handle; + EGLSurface surface; + + void* client; + +} _GLFWcontextEGL; + +// EGL-specific global data +// +typedef struct _GLFWlibraryEGL +{ + EGLDisplay display; + EGLint major, minor; + GLFWbool prefix; + + GLFWbool KHR_create_context; + GLFWbool KHR_create_context_no_error; + GLFWbool KHR_gl_colorspace; + GLFWbool KHR_get_all_proc_addresses; + GLFWbool KHR_context_flush_control; + + void* handle; + + PFN_eglGetConfigAttrib GetConfigAttrib; + PFN_eglGetConfigs GetConfigs; + PFN_eglGetDisplay GetDisplay; + PFN_eglGetError GetError; + PFN_eglInitialize Initialize; + PFN_eglTerminate Terminate; + PFN_eglBindAPI BindAPI; + PFN_eglCreateContext CreateContext; + PFN_eglDestroySurface DestroySurface; + PFN_eglDestroyContext DestroyContext; + PFN_eglCreateWindowSurface CreateWindowSurface; + PFN_eglMakeCurrent MakeCurrent; + PFN_eglSwapBuffers SwapBuffers; + PFN_eglSwapInterval SwapInterval; + PFN_eglQueryString QueryString; + PFN_eglGetProcAddress GetProcAddress; + +} _GLFWlibraryEGL; + + +GLFWbool _glfwInitEGL(void); +void _glfwTerminateEGL(void); +GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + diff --git a/FCLauncher/src/main/jni/glfw/include/fcl_platform.h b/FCLauncher/src/main/jni/glfw/include/fcl_platform.h new file mode 100644 index 00000000..e6a268b6 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/fcl_platform.h @@ -0,0 +1,113 @@ +// +// Created by Tungsten on 2022/10/11. +// +//======================================================================== +// This file is derived from x11_platform.h +//======================================================================== + +#include +#include +#include +#include + +#include + +typedef VkFlags VkAndroidSurfaceCreateFlagsKHR; + +typedef struct VkAndroidSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkAndroidSurfaceCreateFlagsKHR flags; + struct ANativeWindow* window; +} VkAndroidSurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateAndroidSurfaceKHR)(VkInstance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*); + +#include "posix_thread.h" +#include "posix_time.h" +#include "egl_context.h" +#include "osmesa_context.h" +#include "null_joystick.h" + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->fcl.handle) +#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowFCL fcl +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryFCL fcl +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorFCL fcl +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorFCL fcl + +#define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } + +// FCL-specific per-window data +// +typedef struct _GLFWwindowFCL +{ + struct ANativeWindow* handle; + + GLFWbool overrideRedirect; + GLFWbool iconified; + GLFWbool maximized; + + // Whether the visual supports framebuffer transparency + GLFWbool transparent; + + // Cached position and size used to filter out duplicate events + int width, height; + int xpos, ypos; + + // The last received cursor position, regardless of source + int lastCursorPosX, lastCursorPosY; + // The last position the cursor was warped to by GLFW + int warpCursorPosX, warpCursorPosY; + +} _GLFWwindowFCL; + +// FCL-specific global data +// +typedef struct _GLFWlibraryFCL +{ + // System content scale + float contentScaleX, contentScaleY; + // Key name string + char keynames[GLFW_KEY_LAST + 1][5]; + // FCL keycode to GLFW key LUT + short int keycodes[256]; + // GLFW key to FCL keycode LUT + short int scancodes[GLFW_KEY_LAST + 1]; + // Where to place the cursor when re-enabled + double restoreCursorPosX, restoreCursorPosY; + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + // The window receiving and processing events + _GLFWwindow* eventCurrent; + +} _GLFWlibraryFCL; + +// FCL-specific per-monitor data +// +typedef struct _GLFWmonitorFCL +{ + // Current monitor mode index + int currentMode; + +} _GLFWmonitorFCL; + +// FCL-specific per-cursor data +// +typedef struct _GLFWcursorFCL +{ + // Useless struct filler + void* handle; + +} _GLFWcursorFCL; + + +void _glfwPollMonitorsFCL(void); + diff --git a/FCLauncher/src/main/jni/glfw/include/glfw3.h b/FCLauncher/src/main/jni/glfw/include/glfw3.h new file mode 100644 index 00000000..be3d0a0d --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/glfw3.h @@ -0,0 +1,5890 @@ +/************************************************************************* + * GLFW 3.3 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2019 Camilla Löwy + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_h_ +#define _glfw3_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3.h + * @brief The header of the GLFW 3 API. + * + * This is the header file of the GLFW 3 API. It defines all its types and + * declares all its functions. + * + * For more information about how to use this file, see @ref build_include. + */ +/*! @defgroup context Context reference + * @brief Functions and types related to OpenGL and OpenGL ES contexts. + * + * This is the reference documentation for OpenGL and OpenGL ES context related + * functions. For more task-oriented information, see the @ref context_guide. + */ +/*! @defgroup vulkan Vulkan support reference + * @brief Functions and types related to Vulkan. + * + * This is the reference documentation for Vulkan related functions and types. + * For more task-oriented information, see the @ref vulkan_guide. + */ +/*! @defgroup init Initialization, version and error reference + * @brief Functions and types related to initialization and error handling. + * + * This is the reference documentation for initialization and termination of + * the library, version management and error handling. For more task-oriented + * information, see the @ref intro_guide. + */ +/*! @defgroup input Input reference + * @brief Functions and types related to input handling. + * + * This is the reference documentation for input related functions and types. + * For more task-oriented information, see the @ref input_guide. + */ +/*! @defgroup monitor Monitor reference + * @brief Functions and types related to monitors. + * + * This is the reference documentation for monitor related functions and types. + * For more task-oriented information, see the @ref monitor_guide. + */ +/*! @defgroup window Window reference + * @brief Functions and types related to windows. + * + * This is the reference documentation for window related functions and types, + * including creation, deletion and event polling. For more task-oriented + * information, see the @ref window_guide. + */ + + +/************************************************************************* + * Compiler- and platform-specific preprocessor work + *************************************************************************/ + +/* If we are we on Windows, we want a single define for it. + */ +#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)) + #define _WIN32 +#endif /* _WIN32 */ + +/* Include because most Windows GLU headers need wchar_t and + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +/* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ + +/* The Vulkan header may have indirectly included windows.h (because of + * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. + */ + +/* It is customary to use APIENTRY for OpenGL function pointer declarations on + * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. + */ +#if !defined(APIENTRY) + #if defined(_WIN32) + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #define GLFW_APIENTRY_DEFINED +#endif /* APIENTRY */ + +/* Some Windows OpenGL headers need this. + */ +#if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) + #define GLFW_WINGDIAPI_DEFINED +#endif /* WINGDIAPI */ + +/* Some Windows GLU headers need this. + */ +#if !defined(CALLBACK) && defined(_WIN32) + #define CALLBACK __stdcall + #define GLFW_CALLBACK_DEFINED +#endif /* CALLBACK */ + +/* Include the chosen OpenGL or OpenGL ES headers. + */ +#if defined(GLFW_INCLUDE_ES1) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES2) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES3) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES31) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES32) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_GLCOREARB) + + #if defined(__APPLE__) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif /*GLFW_INCLUDE_GLEXT*/ + + #else /*__APPLE__*/ + + #include + + #endif /*__APPLE__*/ + +#elif defined(GLFW_INCLUDE_GLU) + + #if defined(__APPLE__) + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #else /*__APPLE__*/ + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) && \ + !defined(__gl_h_) && \ + !defined(__gles1_gl_h_) && \ + !defined(__gles2_gl2_h_) && \ + !defined(__gles2_gl3_h_) && \ + !defined(__gles2_gl31_h_) && \ + !defined(__gles2_gl32_h_) && \ + !defined(__gl_glcorearb_h_) && \ + !defined(__gl2_h_) /*legacy*/ && \ + !defined(__gl3_h_) /*legacy*/ && \ + !defined(__gl31_h_) /*legacy*/ && \ + !defined(__gl32_h_) /*legacy*/ && \ + !defined(__glcorearb_h_) /*legacy*/ && \ + !defined(__GL_H__) /*non-standard*/ && \ + !defined(__gltypes_h_) /*non-standard*/ && \ + !defined(__glee_h_) /*non-standard*/ + +#endif /* OpenGL and OpenGL ES headers */ + +#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) + /* GLFW_DLL must be defined by applications that are linking against the DLL + * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW + * configuration header when compiling the DLL version of the library. + */ + #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" +#endif + +/* GLFWAPI is used to declare public API functions for export + * from the DLL / shared library / dynamic library. + */ +#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllexport) +#elif defined(_WIN32) && defined(GLFW_DLL) + /* We are calling GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllimport) +#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a shared / dynamic library */ + #define GLFWAPI __attribute__((visibility("default"))) +#else + /* We are building or calling GLFW as a static library */ + #define GLFWAPI +#endif + + +/************************************************************************* + * GLFW API tokens + *************************************************************************/ + +/*! @name GLFW version macros + * @{ */ +/*! @brief The major version number of the GLFW header. + * + * The major version number of the GLFW header. This is incremented when the + * API is changed in non-compatible ways. + * @ingroup init + */ +#define GLFW_VERSION_MAJOR 3 +/*! @brief The minor version number of the GLFW header. + * + * The minor version number of the GLFW header. This is incremented when + * features are added to the API but it remains backward-compatible. + * @ingroup init + */ +#define GLFW_VERSION_MINOR 3 +/*! @brief The revision number of the GLFW header. + * + * The revision number of the GLFW header. This is incremented when a bug fix + * release is made that does not contain any API changes. + * @ingroup init + */ +#define GLFW_VERSION_REVISION 5 +/*! @} */ + +/*! @brief One. + * + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal + * to one. + * + * @ingroup init + */ +#define GLFW_TRUE 1 +/*! @brief Zero. + * + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is + * equal to zero. + * + * @ingroup init + */ +#define GLFW_FALSE 0 + +/*! @name Key and button actions + * @{ */ +/*! @brief The key or mouse button was released. + * + * The key or mouse button was released. + * + * @ingroup input + */ +#define GLFW_RELEASE 0 +/*! @brief The key or mouse button was pressed. + * + * The key or mouse button was pressed. + * + * @ingroup input + */ +#define GLFW_PRESS 1 +/*! @brief The key was held down until it repeated. + * + * The key was held down until it repeated. + * + * @ingroup input + */ +#define GLFW_REPEAT 2 +/*! @} */ + +/*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. + * + * See [joystick hat input](@ref joystick_hat) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_HAT_CENTERED 0 +#define GLFW_HAT_UP 1 +#define GLFW_HAT_RIGHT 2 +#define GLFW_HAT_DOWN 4 +#define GLFW_HAT_LEFT 8 +#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) +#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) +#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) +#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +/*! @} */ + +/*! @defgroup keys Keyboard keys + * @brief Keyboard key IDs. + * + * See [key input](@ref input_key) for how these are used. + * + * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), + * but re-arranged to map to 7-bit ASCII for printable keys (function keys are + * put in the 256+ range). + * + * The naming of the key codes follow these rules: + * - The US keyboard layout is used + * - Names of printable alpha-numeric characters are used (e.g. "A", "R", + * "3", etc.) + * - For non-alphanumeric characters, Unicode:ish names are used (e.g. + * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not + * correspond to the Unicode standard (usually for brevity) + * - Keys that lack a clear US mapping are named "WORLD_x" + * - For non-printable keys, custom names are used (e.g. "F4", + * "BACKSPACE", etc.) + * + * @ingroup input + * @{ + */ + +/* The unknown key */ +#define GLFW_KEY_UNKNOWN -1 + +/* Printable keys */ +#define GLFW_KEY_SPACE 32 +#define GLFW_KEY_APOSTROPHE 39 /* ' */ +#define GLFW_KEY_COMMA 44 /* , */ +#define GLFW_KEY_MINUS 45 /* - */ +#define GLFW_KEY_PERIOD 46 /* . */ +#define GLFW_KEY_SLASH 47 /* / */ +#define GLFW_KEY_0 48 +#define GLFW_KEY_1 49 +#define GLFW_KEY_2 50 +#define GLFW_KEY_3 51 +#define GLFW_KEY_4 52 +#define GLFW_KEY_5 53 +#define GLFW_KEY_6 54 +#define GLFW_KEY_7 55 +#define GLFW_KEY_8 56 +#define GLFW_KEY_9 57 +#define GLFW_KEY_SEMICOLON 59 /* ; */ +#define GLFW_KEY_EQUAL 61 /* = */ +#define GLFW_KEY_A 65 +#define GLFW_KEY_B 66 +#define GLFW_KEY_C 67 +#define GLFW_KEY_D 68 +#define GLFW_KEY_E 69 +#define GLFW_KEY_F 70 +#define GLFW_KEY_G 71 +#define GLFW_KEY_H 72 +#define GLFW_KEY_I 73 +#define GLFW_KEY_J 74 +#define GLFW_KEY_K 75 +#define GLFW_KEY_L 76 +#define GLFW_KEY_M 77 +#define GLFW_KEY_N 78 +#define GLFW_KEY_O 79 +#define GLFW_KEY_P 80 +#define GLFW_KEY_Q 81 +#define GLFW_KEY_R 82 +#define GLFW_KEY_S 83 +#define GLFW_KEY_T 84 +#define GLFW_KEY_U 85 +#define GLFW_KEY_V 86 +#define GLFW_KEY_W 87 +#define GLFW_KEY_X 88 +#define GLFW_KEY_Y 89 +#define GLFW_KEY_Z 90 +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ +#define GLFW_KEY_BACKSLASH 92 /* \ */ +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +/* Function keys */ +#define GLFW_KEY_ESCAPE 256 +#define GLFW_KEY_ENTER 257 +#define GLFW_KEY_TAB 258 +#define GLFW_KEY_BACKSPACE 259 +#define GLFW_KEY_INSERT 260 +#define GLFW_KEY_DELETE 261 +#define GLFW_KEY_RIGHT 262 +#define GLFW_KEY_LEFT 263 +#define GLFW_KEY_DOWN 264 +#define GLFW_KEY_UP 265 +#define GLFW_KEY_PAGE_UP 266 +#define GLFW_KEY_PAGE_DOWN 267 +#define GLFW_KEY_HOME 268 +#define GLFW_KEY_END 269 +#define GLFW_KEY_CAPS_LOCK 280 +#define GLFW_KEY_SCROLL_LOCK 281 +#define GLFW_KEY_NUM_LOCK 282 +#define GLFW_KEY_PRINT_SCREEN 283 +#define GLFW_KEY_PAUSE 284 +#define GLFW_KEY_F1 290 +#define GLFW_KEY_F2 291 +#define GLFW_KEY_F3 292 +#define GLFW_KEY_F4 293 +#define GLFW_KEY_F5 294 +#define GLFW_KEY_F6 295 +#define GLFW_KEY_F7 296 +#define GLFW_KEY_F8 297 +#define GLFW_KEY_F9 298 +#define GLFW_KEY_F10 299 +#define GLFW_KEY_F11 300 +#define GLFW_KEY_F12 301 +#define GLFW_KEY_F13 302 +#define GLFW_KEY_F14 303 +#define GLFW_KEY_F15 304 +#define GLFW_KEY_F16 305 +#define GLFW_KEY_F17 306 +#define GLFW_KEY_F18 307 +#define GLFW_KEY_F19 308 +#define GLFW_KEY_F20 309 +#define GLFW_KEY_F21 310 +#define GLFW_KEY_F22 311 +#define GLFW_KEY_F23 312 +#define GLFW_KEY_F24 313 +#define GLFW_KEY_F25 314 +#define GLFW_KEY_KP_0 320 +#define GLFW_KEY_KP_1 321 +#define GLFW_KEY_KP_2 322 +#define GLFW_KEY_KP_3 323 +#define GLFW_KEY_KP_4 324 +#define GLFW_KEY_KP_5 325 +#define GLFW_KEY_KP_6 326 +#define GLFW_KEY_KP_7 327 +#define GLFW_KEY_KP_8 328 +#define GLFW_KEY_KP_9 329 +#define GLFW_KEY_KP_DECIMAL 330 +#define GLFW_KEY_KP_DIVIDE 331 +#define GLFW_KEY_KP_MULTIPLY 332 +#define GLFW_KEY_KP_SUBTRACT 333 +#define GLFW_KEY_KP_ADD 334 +#define GLFW_KEY_KP_ENTER 335 +#define GLFW_KEY_KP_EQUAL 336 +#define GLFW_KEY_LEFT_SHIFT 340 +#define GLFW_KEY_LEFT_CONTROL 341 +#define GLFW_KEY_LEFT_ALT 342 +#define GLFW_KEY_LEFT_SUPER 343 +#define GLFW_KEY_RIGHT_SHIFT 344 +#define GLFW_KEY_RIGHT_CONTROL 345 +#define GLFW_KEY_RIGHT_ALT 346 +#define GLFW_KEY_RIGHT_SUPER 347 +#define GLFW_KEY_MENU 348 + +#define GLFW_KEY_LAST GLFW_KEY_MENU + +/*! @} */ + +/*! @defgroup mods Modifier key flags + * @brief Modifier key flags. + * + * See [key input](@ref input_key) for how these are used. + * + * @ingroup input + * @{ */ + +/*! @brief If this bit is set one or more Shift keys were held down. + * + * If this bit is set one or more Shift keys were held down. + */ +#define GLFW_MOD_SHIFT 0x0001 +/*! @brief If this bit is set one or more Control keys were held down. + * + * If this bit is set one or more Control keys were held down. + */ +#define GLFW_MOD_CONTROL 0x0002 +/*! @brief If this bit is set one or more Alt keys were held down. + * + * If this bit is set one or more Alt keys were held down. + */ +#define GLFW_MOD_ALT 0x0004 +/*! @brief If this bit is set one or more Super keys were held down. + * + * If this bit is set one or more Super keys were held down. + */ +#define GLFW_MOD_SUPER 0x0008 +/*! @brief If this bit is set the Caps Lock key is enabled. + * + * If this bit is set the Caps Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_CAPS_LOCK 0x0010 +/*! @brief If this bit is set the Num Lock key is enabled. + * + * If this bit is set the Num Lock key is enabled and the @ref + * GLFW_LOCK_KEY_MODS input mode is set. + */ +#define GLFW_MOD_NUM_LOCK 0x0020 + +/*! @} */ + +/*! @defgroup buttons Mouse buttons + * @brief Mouse button IDs. + * + * See [mouse button input](@ref input_mouse_button) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 +/*! @} */ + +/*! @defgroup joysticks Joysticks + * @brief Joystick IDs. + * + * See [joystick input](@ref joystick) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 +/*! @} */ + +/*! @defgroup gamepad_buttons Gamepad buttons + * @brief Gamepad buttons. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_BUTTON_A 0 +#define GLFW_GAMEPAD_BUTTON_B 1 +#define GLFW_GAMEPAD_BUTTON_X 2 +#define GLFW_GAMEPAD_BUTTON_Y 3 +#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 +#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 +#define GLFW_GAMEPAD_BUTTON_BACK 6 +#define GLFW_GAMEPAD_BUTTON_START 7 +#define GLFW_GAMEPAD_BUTTON_GUIDE 8 +#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 +#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 +#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 +#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 +#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 +#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 +#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT + +#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A +#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B +#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X +#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y +/*! @} */ + +/*! @defgroup gamepad_axes Gamepad axes + * @brief Gamepad axes. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_AXIS_LEFT_X 0 +#define GLFW_GAMEPAD_AXIS_LEFT_Y 1 +#define GLFW_GAMEPAD_AXIS_RIGHT_X 2 +#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 +#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 +#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 +#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER +/*! @} */ + +/*! @defgroup errors Error codes + * @brief Error codes. + * + * See [error handling](@ref error_handling) for how these are used. + * + * @ingroup init + * @{ */ +/*! @brief No error has occurred. + * + * No error has occurred. + * + * @analysis Yay. + */ +#define GLFW_NO_ERROR 0 +/*! @brief GLFW has not been initialized. + * + * This occurs if a GLFW function was called that must not be called unless the + * library is [initialized](@ref intro_init). + * + * @analysis Application programmer error. Initialize GLFW before calling any + * function that requires initialization. + */ +#define GLFW_NOT_INITIALIZED 0x00010001 +/*! @brief No context is current for this thread. + * + * This occurs if a GLFW function was called that needs and operates on the + * current OpenGL or OpenGL ES context but no context is current on the calling + * thread. One such function is @ref glfwSwapInterval. + * + * @analysis Application programmer error. Ensure a context is current before + * calling functions that require a current context. + */ +#define GLFW_NO_CURRENT_CONTEXT 0x00010002 +/*! @brief One of the arguments to the function was an invalid enum value. + * + * One of the arguments to the function was an invalid enum value, for example + * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_ENUM 0x00010003 +/*! @brief One of the arguments to the function was an invalid value. + * + * One of the arguments to the function was an invalid value, for example + * requesting a non-existent OpenGL or OpenGL ES version like 2.7. + * + * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead + * result in a @ref GLFW_VERSION_UNAVAILABLE error. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_VALUE 0x00010004 +/*! @brief A memory allocation failed. + * + * A memory allocation failed. + * + * @analysis A bug in GLFW or the underlying operating system. Report the bug + * to our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_OUT_OF_MEMORY 0x00010005 +/*! @brief GLFW could not find support for the requested API on the system. + * + * GLFW could not find support for the requested API on the system. + * + * @analysis The installed graphics driver does not support the requested + * API, or does not support it via the chosen context creation backend. + * Below are a few examples. + * + * @par + * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only + * supports OpenGL ES via EGL, while Nvidia and Intel only support it via + * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa + * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary + * driver. Older graphics drivers do not support Vulkan. + */ +#define GLFW_API_UNAVAILABLE 0x00010006 +/*! @brief The requested OpenGL or OpenGL ES version is not available. + * + * The requested OpenGL or OpenGL ES version (including any requested context + * or framebuffer hints) is not available on this machine. + * + * @analysis The machine does not support your requirements. If your + * application is sufficiently flexible, downgrade your requirements and try + * again. Otherwise, inform the user that their machine does not match your + * requirements. + * + * @par + * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 + * comes out before the 4.x series gets that far, also fail with this error and + * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions + * will exist. + */ +#define GLFW_VERSION_UNAVAILABLE 0x00010007 +/*! @brief A platform-specific error occurred that does not match any of the + * more specific categories. + * + * A platform-specific error occurred that does not match any of the more + * specific categories. + * + * @analysis A bug or configuration error in GLFW, the underlying operating + * system or its drivers, or a lack of required resources. Report the issue to + * our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_PLATFORM_ERROR 0x00010008 +/*! @brief The requested format is not supported or available. + * + * If emitted during window creation, the requested pixel format is not + * supported. + * + * If emitted when querying the clipboard, the contents of the clipboard could + * not be converted to the requested format. + * + * @analysis If emitted during window creation, one or more + * [hard constraints](@ref window_hints_hard) did not match any of the + * available pixel formats. If your application is sufficiently flexible, + * downgrade your requirements and try again. Otherwise, inform the user that + * their machine does not match your requirements. + * + * @par + * If emitted when querying the clipboard, ignore the error or report it to + * the user, as appropriate. + */ +#define GLFW_FORMAT_UNAVAILABLE 0x00010009 +/*! @brief The specified window does not have an OpenGL or OpenGL ES context. + * + * A window that does not have an OpenGL or OpenGL ES context was passed to + * a function that requires it to have one. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_NO_WINDOW_CONTEXT 0x0001000A +/*! @} */ + +/*! @addtogroup window + * @{ */ +/*! @brief Input focus window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUSED_hint) or + * [window attribute](@ref GLFW_FOCUSED_attrib). + */ +#define GLFW_FOCUSED 0x00020001 +/*! @brief Window iconification window attribute + * + * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib). + */ +#define GLFW_ICONIFIED 0x00020002 +/*! @brief Window resize-ability window hint and attribute + * + * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and + * [window attribute](@ref GLFW_RESIZABLE_attrib). + */ +#define GLFW_RESIZABLE 0x00020003 +/*! @brief Window visibility window hint and attribute + * + * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and + * [window attribute](@ref GLFW_VISIBLE_attrib). + */ +#define GLFW_VISIBLE 0x00020004 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_DECORATED_hint) and + * [window attribute](@ref GLFW_DECORATED_attrib). + */ +#define GLFW_DECORATED 0x00020005 +/*! @brief Window auto-iconification window hint and attribute + * + * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and + * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). + */ +#define GLFW_AUTO_ICONIFY 0x00020006 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_FLOATING_hint) and + * [window attribute](@ref GLFW_FLOATING_attrib). + */ +#define GLFW_FLOATING 0x00020007 +/*! @brief Window maximization window hint and attribute + * + * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and + * [window attribute](@ref GLFW_MAXIMIZED_attrib). + */ +#define GLFW_MAXIMIZED 0x00020008 +/*! @brief Cursor centering window hint + * + * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). + */ +#define GLFW_CENTER_CURSOR 0x00020009 +/*! @brief Window framebuffer transparency hint and attribute + * + * Window framebuffer transparency + * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and + * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). + */ +#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A +/*! @brief Mouse cursor hover window attribute. + * + * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib). + */ +#define GLFW_HOVERED 0x0002000B +/*! @brief Input focus on calling show window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or + * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib). + */ +#define GLFW_FOCUS_ON_SHOW 0x0002000C + +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). + */ +#define GLFW_RED_BITS 0x00021001 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS). + */ +#define GLFW_GREEN_BITS 0x00021002 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS). + */ +#define GLFW_BLUE_BITS 0x00021003 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS). + */ +#define GLFW_ALPHA_BITS 0x00021004 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS). + */ +#define GLFW_DEPTH_BITS 0x00021005 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS). + */ +#define GLFW_STENCIL_BITS 0x00021006 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS). + */ +#define GLFW_ACCUM_RED_BITS 0x00021007 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS). + */ +#define GLFW_ACCUM_GREEN_BITS 0x00021008 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS). + */ +#define GLFW_ACCUM_BLUE_BITS 0x00021009 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS). + */ +#define GLFW_ACCUM_ALPHA_BITS 0x0002100A +/*! @brief Framebuffer auxiliary buffer hint. + * + * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS). + */ +#define GLFW_AUX_BUFFERS 0x0002100B +/*! @brief OpenGL stereoscopic rendering hint. + * + * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO). + */ +#define GLFW_STEREO 0x0002100C +/*! @brief Framebuffer MSAA samples hint. + * + * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES). + */ +#define GLFW_SAMPLES 0x0002100D +/*! @brief Framebuffer sRGB hint. + * + * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE). + */ +#define GLFW_SRGB_CAPABLE 0x0002100E +/*! @brief Monitor refresh rate hint. + * + * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE). + */ +#define GLFW_REFRESH_RATE 0x0002100F +/*! @brief Framebuffer double buffering hint. + * + * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). + */ +#define GLFW_DOUBLEBUFFER 0x00021010 + +/*! @brief Context client API hint and attribute. + * + * Context client API [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ +#define GLFW_CLIENT_API 0x00022001 +/*! @brief Context client API major version hint and attribute. + * + * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). + */ +#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +/*! @brief Context client API minor version hint and attribute. + * + * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). + */ +#define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +/*! @brief Context client API revision number hint and attribute. + * + * Context client API revision number + * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). + */ +#define GLFW_CONTEXT_REVISION 0x00022004 +/*! @brief Context robustness hint and attribute. + * + * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) + * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). + */ +#define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +/*! @brief OpenGL forward-compatibility hint and attribute. + * + * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) + * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). + */ +#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +/*! @brief Debug mode context hint and attribute. + * + * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and + * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). + */ +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +/*! @brief OpenGL profile hint and attribute. + * + * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and + * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). + */ +#define GLFW_OPENGL_PROFILE 0x00022008 +/*! @brief Context flush-on-release hint and attribute. + * + * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and + * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). + */ +#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 +/*! @brief Context error suppression hint and attribute. + * + * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and + * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). + */ +#define GLFW_CONTEXT_NO_ERROR 0x0002200A +/*! @brief Context creation API hint and attribute. + * + * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and + * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). + */ +#define GLFW_CONTEXT_CREATION_API 0x0002200B +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). + */ +#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). + */ +#define GLFW_COCOA_FRAME_NAME 0x00023002 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). + */ +#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_CLASS_NAME 0x00024001 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ +#define GLFW_X11_INSTANCE_NAME 0x00024002 +/*! @} */ + +#define GLFW_NO_API 0 +#define GLFW_OPENGL_API 0x00030001 +#define GLFW_OPENGL_ES_API 0x00030002 + +#define GLFW_NO_ROBUSTNESS 0 +#define GLFW_NO_RESET_NOTIFICATION 0x00031001 +#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 + +#define GLFW_OPENGL_ANY_PROFILE 0 +#define GLFW_OPENGL_CORE_PROFILE 0x00032001 +#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 + +#define GLFW_CURSOR 0x00033001 +#define GLFW_STICKY_KEYS 0x00033002 +#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 +#define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 + +#define GLFW_CURSOR_NORMAL 0x00034001 +#define GLFW_CURSOR_HIDDEN 0x00034002 +#define GLFW_CURSOR_DISABLED 0x00034003 + +#define GLFW_ANY_RELEASE_BEHAVIOR 0 +#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 +#define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 + +#define GLFW_NATIVE_CONTEXT_API 0x00036001 +#define GLFW_EGL_CONTEXT_API 0x00036002 +#define GLFW_OSMESA_CONTEXT_API 0x00036003 + +/*! @defgroup shapes Standard cursor shapes + * @brief Standard system cursor shapes. + * + * See [standard cursor creation](@ref cursor_standard) for how these are used. + * + * @ingroup input + * @{ */ + +/*! @brief The regular arrow cursor shape. + * + * The regular arrow cursor. + */ +#define GLFW_ARROW_CURSOR 0x00036001 +/*! @brief The text input I-beam cursor shape. + * + * The text input I-beam cursor shape. + */ +#define GLFW_IBEAM_CURSOR 0x00036002 +/*! @brief The crosshair shape. + * + * The crosshair shape. + */ +#define GLFW_CROSSHAIR_CURSOR 0x00036003 +/*! @brief The hand shape. + * + * The hand shape. + */ +#define GLFW_HAND_CURSOR 0x00036004 +/*! @brief The horizontal resize arrow shape. + * + * The horizontal resize arrow shape. + */ +#define GLFW_HRESIZE_CURSOR 0x00036005 +/*! @brief The vertical resize arrow shape. + * + * The vertical resize arrow shape. + */ +#define GLFW_VRESIZE_CURSOR 0x00036006 +/*! @} */ + +#define GLFW_CONNECTED 0x00040001 +#define GLFW_DISCONNECTED 0x00040002 + +/*! @addtogroup init + * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). + */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). + */ +#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). + */ +#define GLFW_COCOA_MENUBAR 0x00051002 +/*! @} */ + +#define GLFW_DONT_CARE -1 + + +/************************************************************************* + * GLFW API types + *************************************************************************/ + +/*! @brief Client API function pointer type. + * + * Generic function pointer used for returning client API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 3.0. + * + * @ingroup context + */ +typedef void (*GLFWglproc)(void); + +/*! @brief Vulkan API function pointer type. + * + * Generic function pointer used for returning Vulkan API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref vulkan_proc + * @sa @ref glfwGetInstanceProcAddress + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +typedef void (*GLFWvkproc)(void); + +/*! @brief Opaque monitor object. + * + * Opaque monitor object. + * + * @see @ref monitor_object + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWmonitor GLFWmonitor; + +/*! @brief Opaque window object. + * + * Opaque window object. + * + * @see @ref window_object + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef struct GLFWwindow GLFWwindow; + +/*! @brief Opaque cursor object. + * + * Opaque cursor object. + * + * @see @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef struct GLFWcursor GLFWcursor; + +/*! @brief The function pointer type for error callbacks. + * + * This is the function pointer type for error callbacks. An error callback + * function has the following signature: + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * + * @param[in] error_code An [error code](@ref errors). Future releases may add + * more error codes. + * @param[in] description A UTF-8 encoded string describing the error. + * + * @pointer_lifetime The error description string is valid until the callback + * function returns. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.0. + * + * @ingroup init + */ +typedef void (* GLFWerrorfun)(int,const char*); + +/*! @brief The function pointer type for window position callbacks. + * + * This is the function pointer type for window position callbacks. A window + * position callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * + * @param[in] window The window that was moved. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the + * upper-left corner of the content area of the window. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the + * upper-left corner of the content area of the window. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPosCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); + +/*! @brief The function pointer type for window size callbacks. + * + * This is the function pointer type for window size callbacks. A window size + * callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int width, int height) + * @endcode + * + * @param[in] window The window that was resized. + * @param[in] width The new width, in screen coordinates, of the window. + * @param[in] height The new height, in screen coordinates, of the window. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSizeCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); + +/*! @brief The function pointer type for window close callbacks. + * + * This is the function pointer type for window close callbacks. A window + * close callback function has the following signature: + * @code + * void function_name(GLFWwindow* window) + * @endcode + * + * @param[in] window The window that the user attempted to close. + * + * @sa @ref window_close + * @sa @ref glfwSetWindowCloseCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowclosefun)(GLFWwindow*); + +/*! @brief The function pointer type for window content refresh callbacks. + * + * This is the function pointer type for window content refresh callbacks. + * A window content refresh callback function has the following signature: + * @code + * void function_name(GLFWwindow* window); + * @endcode + * + * @param[in] window The window whose content needs to be refreshed. + * + * @sa @ref window_refresh + * @sa @ref glfwSetWindowRefreshCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); + +/*! @brief The function pointer type for window focus callbacks. + * + * This is the function pointer type for window focus callbacks. A window + * focus callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * + * @param[in] window The window that gained or lost input focus. + * @param[in] focused `GLFW_TRUE` if the window was given input focus, or + * `GLFW_FALSE` if it lost it. + * + * @sa @ref window_focus + * @sa @ref glfwSetWindowFocusCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); + +/*! @brief The function pointer type for window iconify callbacks. + * + * This is the function pointer type for window iconify callbacks. A window + * iconify callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * + * @param[in] window The window that was iconified or restored. + * @param[in] iconified `GLFW_TRUE` if the window was iconified, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_iconify + * @sa @ref glfwSetWindowIconifyCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); + +/*! @brief The function pointer type for window maximize callbacks. + * + * This is the function pointer type for window maximize callbacks. A window + * maximize callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * + * @param[in] window The window that was maximized or restored. + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_maximize + * @sa glfwSetWindowMaximizeCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); + +/*! @brief The function pointer type for framebuffer size callbacks. + * + * This is the function pointer type for framebuffer size callbacks. + * A framebuffer size callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * + * @param[in] window The window whose framebuffer was resized. + * @param[in] width The new width, in pixels, of the framebuffer. + * @param[in] height The new height, in pixels, of the framebuffer. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); + +/*! @brief The function pointer type for window content scale callbacks. + * + * This is the function pointer type for window content scale callbacks. + * A window content scale callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * + * @param[in] window The window whose content scale changed. + * @param[in] xscale The new x-axis content scale of the window. + * @param[in] yscale The new y-axis content scale of the window. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); + +/*! @brief The function pointer type for mouse button callbacks. + * + * This is the function pointer type for mouse button callback functions. + * A mouse button callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] button The [mouse button](@ref buttons) that was pressed or + * released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases + * may add more actions. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_mouse_button + * @sa @ref glfwSetMouseButtonCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); + +/*! @brief The function pointer type for cursor position callbacks. + * + * This is the function pointer type for cursor position callbacks. A cursor + * position callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] xpos The new cursor x-coordinate, relative to the left edge of + * the content area. + * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the + * content area. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPosCallback + * + * @since Added in version 3.0. Replaces `GLFWmouseposfun`. + * + * @ingroup input + */ +typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); + +/*! @brief The function pointer type for cursor enter/leave callbacks. + * + * This is the function pointer type for cursor enter/leave callbacks. + * A cursor enter/leave callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content + * area, or `GLFW_FALSE` if it left it. + * + * @sa @ref cursor_enter + * @sa @ref glfwSetCursorEnterCallback + * + * @since Added in version 3.0. + * + * @ingroup input + */ +typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); + +/*! @brief The function pointer type for scroll callbacks. + * + * This is the function pointer type for scroll callbacks. A scroll callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] xoffset The scroll offset along the x-axis. + * @param[in] yoffset The scroll offset along the y-axis. + * + * @sa @ref scrolling + * @sa @ref glfwSetScrollCallback + * + * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. + * + * @ingroup input + */ +typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); + +/*! @brief The function pointer type for keyboard key callbacks. + * + * This is the function pointer type for keyboard key callbacks. A keyboard + * key callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] key The [keyboard key](@ref keys) that was pressed or released. + * @param[in] scancode The system-specific scancode of the key. + * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future + * releases may add more actions. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_key + * @sa @ref glfwSetKeyCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle, scancode and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); + +/*! @brief The function pointer type for Unicode character callbacks. + * + * This is the function pointer type for Unicode character callbacks. + * A Unicode character callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * + * @sa @ref input_char + * @sa @ref glfwSetCharCallback + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); + +/*! @brief The function pointer type for Unicode character with modifiers + * callbacks. + * + * This is the function pointer type for Unicode character with modifiers + * callbacks. It is called for each input character, regardless of what + * modifier keys are held down. A Unicode character with modifiers callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_char + * @sa @ref glfwSetCharModsCallback + * + * @deprecated Scheduled for removal in version 4.0. + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); + +/*! @brief The function pointer type for path drop callbacks. + * + * This is the function pointer type for path drop callbacks. A path drop + * callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * + * @param[in] window The window that received the event. + * @param[in] path_count The number of dropped paths. + * @param[in] paths The UTF-8 encoded file and/or directory path names. + * + * @pointer_lifetime The path array and its strings are valid until the + * callback function returns. + * + * @sa @ref path_drop + * @sa @ref glfwSetDropCallback + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWdropfun)(GLFWwindow*,int,const char*[]); + +/*! @brief The function pointer type for monitor configuration callbacks. + * + * This is the function pointer type for monitor configuration callbacks. + * A monitor callback function has the following signature: + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * + * @param[in] monitor The monitor that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. + * + * @sa @ref monitor_event + * @sa @ref glfwSetMonitorCallback + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); + +/*! @brief The function pointer type for joystick configuration callbacks. + * + * This is the function pointer type for joystick configuration callbacks. + * A joystick configuration callback function has the following signature: + * @code + * void function_name(int jid, int event) + * @endcode + * + * @param[in] jid The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. + * + * @sa @ref joystick_event + * @sa @ref glfwSetJoystickCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWjoystickfun)(int,int); + +/*! @brief Video mode type. + * + * This describes a single video mode. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * @sa @ref glfwGetVideoModes + * + * @since Added in version 1.0. + * @glfw3 Added refresh rate member. + * + * @ingroup monitor + */ +typedef struct GLFWvidmode +{ + /*! The width, in screen coordinates, of the video mode. + */ + int width; + /*! The height, in screen coordinates, of the video mode. + */ + int height; + /*! The bit depth of the red channel of the video mode. + */ + int redBits; + /*! The bit depth of the green channel of the video mode. + */ + int greenBits; + /*! The bit depth of the blue channel of the video mode. + */ + int blueBits; + /*! The refresh rate, in Hz, of the video mode. + */ + int refreshRate; +} GLFWvidmode; + +/*! @brief Gamma ramp. + * + * This describes the gamma ramp for a monitor. + * + * @sa @ref monitor_gamma + * @sa @ref glfwGetGammaRamp + * @sa @ref glfwSetGammaRamp + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWgammaramp +{ + /*! An array of value describing the response of the red channel. + */ + unsigned short* red; + /*! An array of value describing the response of the green channel. + */ + unsigned short* green; + /*! An array of value describing the response of the blue channel. + */ + unsigned short* blue; + /*! The number of elements in each array. + */ + unsigned int size; +} GLFWgammaramp; + +/*! @brief Image data. + * + * This describes a single 2D image. See the documentation for each related + * function what the expected pixel format is. + * + * @sa @ref cursor_custom + * @sa @ref window_icon + * + * @since Added in version 2.1. + * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window + */ +typedef struct GLFWimage +{ + /*! The width, in pixels, of this image. + */ + int width; + /*! The height, in pixels, of this image. + */ + int height; + /*! The pixel data of this image, arranged left-to-right, top-to-bottom. + */ + unsigned char* pixels; +} GLFWimage; + +/*! @brief Gamepad input state + * + * This describes the input state of a gamepad. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +typedef struct GLFWgamepadstate +{ + /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS` + * or `GLFW_RELEASE`. + */ + unsigned char buttons[15]; + /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0 + * to 1.0 inclusive. + */ + float axes[6]; +} GLFWgamepadstate; + + +/************************************************************************* + * GLFW API functions + *************************************************************************/ + +/*! @brief Initializes the GLFW library. + * + * This function initializes the GLFW library. Before most GLFW functions can + * be used, GLFW must be initialized, and before an application terminates GLFW + * should be terminated in order to free any resources allocated during or + * after initialization. + * + * If this function fails, it calls @ref glfwTerminate before returning. If it + * succeeds, you should call @ref glfwTerminate before the application exits. + * + * Additional calls to this function after successful initialization but before + * termination will return `GLFW_TRUE` immediately. + * + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark @macos This function will change the current directory of the + * application to the `Contents/Resources` subdirectory of the application's + * bundle, if present. This can be disabled with the @ref + * GLFW_COCOA_CHDIR_RESOURCES init hint. + * + * @remark @x11 This function will set the `LC_CTYPE` category of the + * application locale according to the current environment if that category is + * still "C". This is because the "C" locale breaks Unicode text input. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwTerminate + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI int glfwInit(void); + +/*! @brief Terminates the GLFW library. + * + * This function destroys all remaining windows and cursors, restores any + * modified gamma ramps and frees any other allocated resources. Once this + * function is called, you must again call @ref glfwInit successfully before + * you will be able to use most GLFW functions. + * + * If GLFW has been successfully initialized, this function should be called + * before the application exits. If initialization fails, there is no need to + * call this function, as it is called by @ref glfwInit before it returns + * failure. + * + * This function has no effect if GLFW is not initialized. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark This function may be called before @ref glfwInit. + * + * @warning The contexts of any remaining windows must not be current on any + * other thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwInit + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwTerminate(void); + +/*! @brief Sets the specified init hint to the desired value. + * + * This function sets hints for the next initialization of GLFW. + * + * The values you set hints to are never reset by GLFW, but they only take + * effect during initialization. Once GLFW has been initialized, any values + * you set will be ignored until the library is terminated and initialized + * again. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [init hint](@ref init_hints) to set. + * @param[in] value The new value of the init hint. + * + * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref + * GLFW_INVALID_VALUE. + * + * @remarks This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa init_hints + * @sa glfwInit + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI void glfwInitHint(int hint, int value); + +/*! @brief Retrieves the version of the GLFW library. + * + * This function retrieves the major, minor and revision numbers of the GLFW + * library. It is intended for when you are using GLFW as a shared library and + * want to ensure that you are using the minimum required version. + * + * Any or all of the version arguments may be `NULL`. + * + * @param[out] major Where to store the major version number, or `NULL`. + * @param[out] minor Where to store the minor version number, or `NULL`. + * @param[out] rev Where to store the revision number, or `NULL`. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersionString + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); + +/*! @brief Returns a string describing the compile-time configuration. + * + * This function returns the compile-time generated + * [version string](@ref intro_version_string) of the GLFW library binary. It + * describes the version, platform, compiler and any platform-specific + * compile-time options. It should not be confused with the OpenGL or OpenGL + * ES version string, queried with `glGetString`. + * + * __Do not use the version string__ to parse the GLFW library version. The + * @ref glfwGetVersion function provides the version of the running library + * binary in numerical format. + * + * @return The ASCII encoded GLFW version string. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @pointer_lifetime The returned string is static and compile-time generated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersion + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI const char* glfwGetVersionString(void); + +/*! @brief Returns and clears the last error for the calling thread. + * + * This function returns and clears the [error code](@ref errors) of the last + * error that occurred on the calling thread, and optionally a UTF-8 encoded + * human-readable description of it. If no error has occurred since the last + * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is + * set to `NULL`. + * + * @param[in] description Where to store the error description pointer, or `NULL`. + * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR + * (zero). + * + * @errors None. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * next error occurs or the library is terminated. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI int glfwGetError(const char** description); + +/*! @brief Sets the error callback. + * + * This function sets the error callback, which is called with an error code + * and a human-readable description each time a GLFW error occurs. + * + * The error code is set before the callback is called. Calling @ref + * glfwGetError from the error callback will return the same value as the error + * code argument. + * + * The error callback is called on the thread where the error occurred. If you + * are using GLFW from multiple threads, your error callback needs to be + * written accordingly. + * + * Because the description string may have been generated specifically for that + * error, it is not guaranteed to be valid after the callback has returned. If + * you wish to use it after the callback returns, you need to make a copy. + * + * Once set, the error callback remains set even after the library has been + * terminated. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set. + * + * @callback_signature + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * For more information about the callback parameters, see the + * [callback pointer type](@ref GLFWerrorfun). + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref error_handling + * @sa @ref glfwGetError + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); + +/*! @brief Returns the currently connected monitors. + * + * This function returns an array of handles for all currently connected + * monitors. The primary monitor is always first in the returned array. If no + * monitors were found, this function returns `NULL`. + * + * @param[out] count Where to store the number of monitors in the returned + * array. This is set to zero if an error occurred. + * @return An array of monitor handles, or `NULL` if no monitors were found or + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * monitor configuration changes or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_monitors + * @sa @ref monitor_event + * @sa @ref glfwGetPrimaryMonitor + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); + +/*! @brief Returns the primary monitor. + * + * This function returns the primary monitor. This is usually the monitor + * where elements like the task bar or global menu bar are located. + * + * @return The primary monitor, or `NULL` if no monitors were found or if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @remark The primary monitor is always first in the array returned by @ref + * glfwGetMonitors. + * + * @sa @ref monitor_monitors + * @sa @ref glfwGetMonitors + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); + +/*! @brief Returns the position of the monitor's viewport on the virtual screen. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the specified monitor. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); + +/*! @brief Retrieves the work area of the monitor. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the work area of the specified monitor along with the work area + * size in screen coordinates. The work area is defined as the area of the + * monitor not occluded by the operating system task bar where present. If no + * task bar exists then the work area is the monitor resolution in screen + * coordinates. + * + * Any or all of the position and size arguments may be `NULL`. If an error + * occurs, all non-`NULL` position and size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. + * @param[out] height Where to store the monitor height, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_workarea + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); + +/*! @brief Returns the physical size of the monitor. + * + * This function returns the size, in millimetres, of the display area of the + * specified monitor. + * + * Some systems do not provide accurate monitor size information, either + * because the monitor + * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) + * data is incorrect or because the driver does not report it accurately. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] widthMM Where to store the width, in millimetres, of the + * monitor's display area, or `NULL`. + * @param[out] heightMM Where to store the height, in millimetres, of the + * monitor's display area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @win32 calculates the returned physical size from the + * current resolution and system DPI instead of querying the monitor EDID data. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); + +/*! @brief Retrieves the content scale for the specified monitor. + * + * This function retrieves the content scale for the specified monitor. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * The content scale may depend on both the monitor resolution and pixel + * density and on user settings. It may be very different from the raw DPI + * calculated from the physical size and current resolution. + * + * @param[in] monitor The monitor to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); + +/*! @brief Returns the name of the specified monitor. + * + * This function returns a human-readable name, encoded as UTF-8, of the + * specified monitor. The name typically reflects the make and model of the + * monitor and is not guaranteed to be unique among the connected monitors. + * + * @param[in] monitor The monitor to query. + * @return The UTF-8 encoded name of the monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); + +/*! @brief Sets the user pointer of the specified monitor. + * + * This function sets the user-defined pointer of the specified monitor. The + * current value is retained until the monitor is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwGetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer); + +/*! @brief Returns the user pointer of the specified monitor. + * + * This function returns the current value of the user-defined pointer of the + * specified monitor. The initial value is `NULL`. + * + * This function may be called from the monitor callback, even for a monitor + * that is being disconnected. + * + * @param[in] monitor The monitor whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref monitor_userptr + * @sa @ref glfwSetMonitorUserPointer + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); + +/*! @brief Sets the monitor configuration callback. + * + * This function sets the monitor configuration callback, or removes the + * currently set callback. This is called when a monitor is connected to or + * disconnected from the system. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmonitorfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_event + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); + +/*! @brief Returns the available video modes for the specified monitor. + * + * This function returns an array of all video modes supported by the specified + * monitor. The returned array is sorted in ascending order, first by color + * bit depth (the sum of all channel depths), then by resolution area (the + * product of width and height), then resolution width and finally by refresh + * rate. + * + * @param[in] monitor The monitor to query. + * @param[out] count Where to store the number of video modes in the returned + * array. This is set to zero if an error occurred. + * @return An array of video modes, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected, this function is called again for that monitor or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * + * @since Added in version 1.0. + * @glfw3 Changed to return an array of modes for a specific monitor. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); + +/*! @brief Returns the current mode of the specified monitor. + * + * This function returns the current video mode of the specified monitor. If + * you have created a full screen window for that monitor, the return value + * will depend on whether that window is iconified. + * + * @param[in] monitor The monitor to query. + * @return The current mode of the monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoModes + * + * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); + +/*! @brief Generates a gamma ramp and sets it for the specified monitor. + * + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. + * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] gamma The desired exponent. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); + +/*! @brief Returns the current gamma ramp for the specified monitor. + * + * This function returns the current gamma ramp of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The current gamma ramp, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while + * returning `NULL`. + * + * @pointer_lifetime The returned structure and its arrays are allocated and + * freed by GLFW. You should not free them yourself. They are valid until the + * specified monitor is disconnected, this function is called again for that + * monitor or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); + +/*! @brief Sets the current gamma ramp for the specified monitor. + * + * This function sets the current gamma ramp for the specified monitor. The + * original gamma ramp for that monitor is saved by GLFW the first time this + * function is called and is restored by @ref glfwTerminate. + * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] ramp The gamma ramp to use. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. + * + * @remark @win32 The gamma ramp size must be 256. + * + * @remark @wayland Gamma handling is a privileged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified gamma ramp is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +/*! @brief Resets all window hints to their default values. + * + * This function resets all window hints to their + * [default values](@ref window_hints_values). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHint + * @sa @ref glfwWindowHintString + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwDefaultWindowHints(void); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only integer value hints can be set with this function. String value hints + * are set with @ref glfwWindowHintString. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHintString + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHint(int hint, int value); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to this + * function or @ref glfwDefaultWindowHints, or until the library is terminated. + * + * Only string type hints can be set with this function. Integer value hints + * are set with @ref glfwWindowHint. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHint + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHintString(int hint, const char* value); + +/*! @brief Creates a window and its associated context. + * + * This function creates a window and its associated OpenGL or OpenGL ES + * context. Most of the options controlling how the window and its context + * should be created are specified with [window hints](@ref window_hints). + * + * Successful creation does not change which context is current. Before you + * can use the newly created context, you need to + * [make it current](@ref context_current). For information about the `share` + * parameter, see @ref context_sharing. + * + * The created window, framebuffer and context may differ from what you + * requested, as not all parameters and hints are + * [hard constraints](@ref window_hints_hard). This includes the size of the + * window, especially for full screen windows. To query the actual attributes + * of the created window, framebuffer and context, see @ref + * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. + * + * To create a full screen window, you need to specify the monitor the window + * will cover. If no monitor is specified, the window will be windowed mode. + * Unless you have a way for the user to choose a specific monitor, it is + * recommended that you pick the primary monitor. For more information on how + * to query connected monitors, see @ref monitor_monitors. + * + * For full screen windows, the specified size becomes the resolution of the + * window's _desired video mode_. As long as a full screen window is not + * iconified, the supported video mode most closely matching the desired video + * mode is set for the specified monitor. For more information about full + * screen windows, including the creation of so called _windowed full screen_ + * or _borderless full screen_ windows, see @ref window_windowed_full_screen. + * + * Once you have created the window, you can switch it between windowed and + * full screen mode with @ref glfwSetWindowMonitor. This will not affect its + * OpenGL or OpenGL ES context. + * + * By default, newly created windows use the placement recommended by the + * window system. To create the window at a specific position, make it + * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window + * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) + * it. + * + * As long as at least one full screen window is not iconified, the screensaver + * is prohibited from starting. + * + * Window systems put limits on window sizes. Very large or very small window + * dimensions may be overridden by the window system on creation. Check the + * actual [size](@ref window_size) after creation. + * + * The [swap interval](@ref buffer_swap) is not set during window creation and + * the initial value may vary depending on driver settings and defaults. + * + * @param[in] width The desired width, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] height The desired height, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] title The initial, UTF-8 encoded window title. + * @param[in] monitor The monitor to use for full screen mode, or `NULL` for + * windowed mode. + * @param[in] share The window whose context to share resources with, or `NULL` + * to not share resources. + * @return The handle of the created window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref + * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @win32 Window creation will fail if the Microsoft GDI software + * OpenGL implementation is the only one available. + * + * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it + * will be set as the initial icon for the window. If no such icon is present, + * the `IDI_APPLICATION` icon will be used instead. To set a different icon, + * see @ref glfwSetWindowIcon. + * + * @remark @win32 The context to share resources with must not be current on + * any other thread. + * + * @remark @macos The OS only supports forward-compatible core profile contexts + * for OpenGL versions 3.2 and later. Before creating an OpenGL context of + * version 3.2 or later you must set the + * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and + * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly. + * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, but the dock icon will be the same as the application bundle's icon. + * For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @macos The first time a window is created the menu bar is created. + * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu + * bar. Otherwise a minimal menu bar is created manually with common commands + * like Hide, Quit and About. The About entry opens a minimal about dialog + * with information from the application's bundle. Menu bar creation can be + * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. + * + * @remark @macos On OS X 10.10 and later the window frame will not be rendered + * at full resolution on Retina displays unless the + * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) + * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the + * application bundle's `Info.plist`. For more information, see + * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) + * in the Mac Developer Library. The GLFW test and example programs use + * a custom `Info.plist` template for this, which can be found as + * `CMake/MacOSXBundleInfo.plist.in` in the source tree. + * + * @remark @macos When activating frame autosaving with + * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified + * window size and position may be overridden by previously saved values. + * + * @remark @x11 Some window managers will not respect the placement of + * initially hidden windows. + * + * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for + * a window to reach its requested state. This means you may not be able to + * query the final size, position or other attributes directly after window + * creation. + * + * @remark @x11 The class part of the `WM_CLASS` window property will by + * default be set to the window title passed to this function. The instance + * part will use the contents of the `RESOURCE_NAME` environment variable, if + * present and not empty, or fall back to the window title. Set the + * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and + * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to + * override this. + * + * @remark @wayland Compositors should implement the xdg-decoration protocol + * for GLFW to decorate the window properly. If this protocol isn't + * supported, or if the compositor prefers client-side decorations, a very + * simple fallback frame will be drawn using the wp_viewporter protocol. A + * compositor can still emit close, maximize or fullscreen events, using for + * instance a keybind mechanism. If neither of these protocols is supported, + * the window won't be decorated. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size or refresh rate. + * + * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol + * to be implemented in the user's compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwDestroyWindow + * + * @since Added in version 3.0. Replaces `glfwOpenWindow`. + * + * @ingroup window + */ +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); + +/*! @brief Destroys the specified window and its context. + * + * This function destroys the specified window and its context. On calling + * this function, no further callbacks will be called for that window. + * + * If the context of the specified window is current on the main thread, it is + * detached before being destroyed. + * + * @param[in] window The window to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @note The context of the specified window must not be current on any other + * thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwCreateWindow + * + * @since Added in version 3.0. Replaces `glfwCloseWindow`. + * + * @ingroup window + */ +GLFWAPI void glfwDestroyWindow(GLFWwindow* window); + +/*! @brief Checks the close flag of the specified window. + * + * This function returns the value of the close flag of the specified window. + * + * @param[in] window The window to query. + * @return The value of the close flag. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); + +/*! @brief Sets the close flag of the specified window. + * + * This function sets the value of the close flag of the specified window. + * This can be used to override the user's attempt to close the window, or + * to signal that it should be closed. + * + * @param[in] window The window whose flag to change. + * @param[in] value The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); + +/*! @brief Sets the title of the specified window. + * + * This function sets the window title, encoded as UTF-8, of the specified + * window. + * + * @param[in] window The window whose title to change. + * @param[in] title The UTF-8 encoded window title. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos The window title will not be updated until the next time you + * process events. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_title + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); + +/*! @brief Sets the icon for the specified window. + * + * This function sets the icon of the specified window. If passed an array of + * candidate images, those of or closest to the sizes desired by the system are + * selected. If no images are specified, the window reverts to its default + * icon. + * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The desired image sizes varies depending on platform and system settings. + * The selected images will be rescaled as needed. Good sizes include 16x16, + * 32x32 and 48x48. + * + * @param[in] window The window whose icon to set. + * @param[in] count The number of images in the specified array, or zero to + * revert to the default window icon. + * @param[in] images The images to create the icon from. This is ignored if + * count is zero. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, so this function does nothing. The dock icon will be the same as + * the application bundle's icon. For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @wayland There is no existing protocol to change an icon, the + * window will thus inherit the one defined in the application's desktop file. + * This function always emits @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_icon + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); + +/*! @brief Retrieves the position of the content area of the specified window. + * + * This function retrieves the position, in screen coordinates, of the + * upper-left corner of the content area of the specified window. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The window to query. + * @param[out] xpos Where to store the x-coordinate of the upper-left corner of + * the content area, or `NULL`. + * @param[out] ypos Where to store the y-coordinate of the upper-left corner of + * the content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to retrieve the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); + +/*! @brief Sets the position of the content area of the specified window. + * + * This function sets the position, in screen coordinates, of the upper-left + * corner of the content area of the specified windowed mode window. If the + * window is a full screen window, this function does nothing. + * + * __Do not use this function__ to move an already visible window unless you + * have very good reasons for doing so, as it will confuse and annoy the user. + * + * The window manager may put limits on what positions are allowed. GLFW + * cannot and should not override these limits. + * + * @param[in] window The window to query. + * @param[in] xpos The x-coordinate of the upper-left corner of the content area. + * @param[in] ypos The y-coordinate of the upper-left corner of the content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to set the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwGetWindowPos + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); + +/*! @brief Retrieves the size of the content area of the specified window. + * + * This function retrieves the size, in screen coordinates, of the content area + * of the specified window. If you wish to retrieve the size of the + * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose size to retrieve. + * @param[out] width Where to store the width, in screen coordinates, of the + * content area, or `NULL`. + * @param[out] height Where to store the height, in screen coordinates, of the + * content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSize + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Sets the size limits of the specified window. + * + * This function sets the size limits of the content area of the specified + * window. If the window is full screen, the size limits only take effect + * once it is made windowed. If the window is not resizable, this function + * does nothing. + * + * The size limits are applied immediately to a windowed mode window and may + * cause it to be resized. + * + * The maximum dimensions must be greater than or equal to the minimum + * dimensions and all must be greater than or equal to zero. + * + * @param[in] window The window to set limits for. + * @param[in] minwidth The minimum width, in screen coordinates, of the content + * area, or `GLFW_DONT_CARE`. + * @param[in] minheight The minimum height, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the content + * area, or `GLFW_DONT_CARE`. + * @param[in] maxheight The maximum height, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowAspectRatio + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); + +/*! @brief Sets the aspect ratio of the specified window. + * + * This function sets the required aspect ratio of the content area of the + * specified window. If the window is full screen, the aspect ratio only takes + * effect once it is made windowed. If the window is not resizable, this + * function does nothing. + * + * The aspect ratio is specified as a numerator and a denominator and both + * values must be greater than zero. For example, the common 16:9 aspect ratio + * is specified as 16 and 9, respectively. + * + * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect + * ratio limit is disabled. + * + * The aspect ratio is applied immediately to a windowed mode window and may + * cause it to be resized. + * + * @param[in] window The window to set limits for. + * @param[in] numer The numerator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * @param[in] denom The denominator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The aspect ratio will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowSizeLimits + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); + +/*! @brief Sets the size of the content area of the specified window. + * + * This function sets the size, in screen coordinates, of the content area of + * the specified window. + * + * For full screen windows, this function updates the resolution of its desired + * video mode and switches to the video mode closest to it, without affecting + * the window's context. As the context is unaffected, the bit depths of the + * framebuffer remain unchanged. + * + * If you wish to update the refresh rate of the desired video mode in addition + * to its resolution, see @ref glfwSetWindowMonitor. + * + * The window manager may put limits on what sizes are allowed. GLFW cannot + * and should not override these limits. + * + * @param[in] window The window to resize. + * @param[in] width The desired width, in screen coordinates, of the window + * content area. + * @param[in] height The desired height, in screen coordinates, of the window + * content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwGetWindowSize + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); + +/*! @brief Retrieves the size of the framebuffer of the specified window. + * + * This function retrieves the size, in pixels, of the framebuffer of the + * specified window. If you wish to retrieve the size of the window in screen + * coordinates, see @ref glfwGetWindowSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose framebuffer to query. + * @param[out] width Where to store the width, in pixels, of the framebuffer, + * or `NULL`. + * @param[out] height Where to store the height, in pixels, of the framebuffer, + * or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Retrieves the size of the frame of the window. + * + * This function retrieves the size, in screen coordinates, of each edge of the + * frame of the specified window. This size includes the title bar, if the + * window has one. The size of the frame may vary depending on the + * [window-related hints](@ref window_hints_wnd) used to create it. + * + * Because this function retrieves the size of each window frame edge and not + * the offset along a particular coordinate axis, the retrieved values will + * always be zero or positive. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose frame size to query. + * @param[out] left Where to store the size, in screen coordinates, of the left + * edge of the window frame, or `NULL`. + * @param[out] top Where to store the size, in screen coordinates, of the top + * edge of the window frame, or `NULL`. + * @param[out] right Where to store the size, in screen coordinates, of the + * right edge of the window frame, or `NULL`. + * @param[out] bottom Where to store the size, in screen coordinates, of the + * bottom edge of the window frame, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); + +/*! @brief Retrieves the content scale for the specified window. + * + * This function retrieves the content scale for the specified window. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. + * + * On systems where each monitors can have its own content scale, the window + * content scale will depend on which monitor the system considers the window + * to be on. + * + * @param[in] window The window to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwSetWindowContentScaleCallback + * @sa @ref glfwGetMonitorContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); + +/*! @brief Returns the opacity of the whole window. + * + * This function returns the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. If the system + * does not support whole window transparency, this function always returns one. + * + * The initial opacity value for newly created windows is one. + * + * @param[in] window The window to query. + * @return The opacity value of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwSetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window); + +/*! @brief Sets the opacity of the whole window. + * + * This function sets the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. + * + * The initial opacity value for newly created windows is one. + * + * A window created with framebuffer transparency may not use whole window + * transparency. The results of doing this are undefined. + * + * @param[in] window The window to set the opacity for. + * @param[in] opacity The desired opacity of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwGetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); + +/*! @brief Iconifies the specified window. + * + * This function iconifies (minimizes) the specified window if it was + * previously restored. If the window is already iconified, this function does + * nothing. + * + * If the specified window is a full screen window, the original monitor + * resolution is restored until the window is restored. + * + * @param[in] window The window to iconify. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no concept of iconification in wl_shell, this + * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated + * protocol. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwRestoreWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwIconifyWindow(GLFWwindow* window); + +/*! @brief Restores the specified window. + * + * This function restores the specified window if it was previously iconified + * (minimized) or maximized. If the window is already restored, this function + * does nothing. + * + * If the specified window is a full screen window, the resolution chosen for + * the window is restored on the selected monitor. + * + * @param[in] window The window to restore. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); + +/*! @brief Maximizes the specified window. + * + * This function maximizes the specified window if it was previously not + * maximized. If the window is already maximized, this function does nothing. + * + * If the specified window is a full screen window, this function does nothing. + * + * @param[in] window The window to maximize. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @par Thread Safety + * This function may only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwRestoreWindow + * + * @since Added in GLFW 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); + +/*! @brief Makes the specified window visible. + * + * This function makes the specified window visible if it was previously + * hidden. If the window is already visible or is in full screen mode, this + * function does nothing. + * + * By default, windowed mode windows are focused when shown + * Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint + * to change this behavior for all newly created windows, or change the + * behavior for an existing window with @ref glfwSetWindowAttrib. + * + * @param[in] window The window to make visible. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwHideWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwShowWindow(GLFWwindow* window); + +/*! @brief Hides the specified window. + * + * This function hides the specified window if it was previously visible. If + * the window is already hidden or is in full screen mode, this function does + * nothing. + * + * @param[in] window The window to hide. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwShowWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwHideWindow(GLFWwindow* window); + +/*! @brief Brings the specified window to front and sets input focus. + * + * This function brings the specified window to front and sets input focus. + * The window should already be visible and not iconified. + * + * By default, both windowed and full screen mode windows are focused when + * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to + * disable this behavior. + * + * Also by default, windowed mode windows are focused when shown + * with @ref glfwShowWindow. Set the + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior. + * + * __Do not use this function__ to steal focus from other applications unless + * you are certain that is what the user wants. Focus stealing can be + * extremely disruptive. + * + * For a less disruptive way of getting the user's attention, see + * [attention requests](@ref window_attention). + * + * @param[in] window The window to give input focus. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland It is not possible for an application to bring its windows + * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * @sa @ref window_attention + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwFocusWindow(GLFWwindow* window); + +/*! @brief Requests user attention to the specified window. + * + * This function requests user attention to the specified window. On + * platforms where this is not supported, attention is requested to the + * application as a whole. + * + * Once the user has given attention, usually by focusing the window or + * application, the system will end the request automatically. + * + * @param[in] window The window to request attention to. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos Attention is requested to the application as a whole, not the + * specific window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attention + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); + +/*! @brief Returns the monitor that the window uses for full screen mode. + * + * This function returns the handle of the monitor that the specified window is + * in full screen on. + * + * @param[in] window The window to query. + * @return The monitor, or `NULL` if the window is in windowed mode or an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); + +/*! @brief Sets the mode, monitor, video mode and placement of a window. + * + * This function sets the monitor that the window uses for full screen mode or, + * if the monitor is `NULL`, makes it windowed mode. + * + * When setting a monitor, this function updates the width, height and refresh + * rate of the desired video mode and switches to the video mode closest to it. + * The window position is ignored when setting a monitor. + * + * When the monitor is `NULL`, the position, width and height are used to + * place the window content area. The refresh rate is ignored when no monitor + * is specified. + * + * If you only wish to update the resolution of a full screen window or the + * size of a windowed mode window, see @ref glfwSetWindowSize. + * + * When a window transitions from full screen to windowed mode, this function + * restores any previous window settings such as whether it is decorated, + * floating, resizable, has size or aspect ratio limits, etc. + * + * @param[in] window The window whose monitor, size or video mode to set. + * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. + * @param[in] xpos The desired x-coordinate of the upper-left corner of the + * content area. + * @param[in] ypos The desired y-coordinate of the upper-left corner of the + * content area. + * @param[in] width The desired with, in screen coordinates, of the content + * area or video mode. + * @param[in] height The desired height, in screen coordinates, of the content + * area or video mode. + * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, + * or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise + * affected by any resizing or mode switching, although you may need to update + * your viewport if the framebuffer size has changed. + * + * @remark @wayland The desired window position is ignored, as there is no way + * for an application to set this property. + * + * @remark @wayland Setting the window to full screen will not attempt to + * change the mode, no matter what the requested size or refresh rate. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref window_full_screen + * @sa @ref glfwGetWindowMonitor + * @sa @ref glfwSetWindowSize + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); + +/*! @brief Returns an attribute of the specified window. + * + * This function returns the value of an attribute of the specified window or + * its OpenGL or OpenGL ES context. + * + * @param[in] window The window to query. + * @param[in] attrib The [window attribute](@ref window_attribs) whose value to + * return. + * @return The value of the attribute, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @remark Framebuffer related hints are not window attributes. See @ref + * window_attribs_fb for more information. + * + * @remark Zero is a valid value for many window and context related + * attributes so you cannot use a return value of zero as an indication of + * errors. However, this function should not fail as long as it is passed + * valid arguments and the library has been [initialized](@ref intro_init). + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwSetWindowAttrib + * + * @since Added in version 3.0. Replaces `glfwGetWindowParam` and + * `glfwGetGLVersion`. + * + * @ingroup window + */ +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); + +/*! @brief Sets an attribute of the specified window. + * + * This function sets the value of an attribute of the specified window. + * + * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), + * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), + * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib), + * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and + * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib). + * + * Some of these attributes are ignored for full screen windows. The new + * value will take effect if the window is later made windowed. + * + * Some of these attributes are ignored for windowed mode windows. The new + * value will take effect if the window is later made full screen. + * + * @param[in] window The window to set the attribute for. + * @param[in] attrib A supported window attribute. + * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark Calling @ref glfwGetWindowAttrib will always return the latest + * value, even if that value is ignored by the current mode of the window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwGetWindowAttrib + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); + +/*! @brief Sets the user pointer of the specified window. + * + * This function sets the user-defined pointer of the specified window. The + * current value is retained until the window is destroyed. The initial value + * is `NULL`. + * + * @param[in] window The window whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwGetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); + +/*! @brief Returns the user pointer of the specified window. + * + * This function returns the current value of the user-defined pointer of the + * specified window. The initial value is `NULL`. + * + * @param[in] window The window whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwSetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); + +/*! @brief Sets the position callback for the specified window. + * + * This function sets the position callback of the specified window, which is + * called when the window is moved. The callback is provided with the + * position, in screen coordinates, of the upper-left corner of the content + * area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowposfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland This callback will never be called, as there is no way for + * an application to know its global position. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); + +/*! @brief Sets the size callback for the specified window. + * + * This function sets the size callback of the specified window, which is + * called when the window is resized. The callback is provided with the size, + * in screen coordinates, of the content area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowsizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); + +/*! @brief Sets the close callback for the specified window. + * + * This function sets the close callback of the specified window, which is + * called when the user attempts to close the window, for example by clicking + * the close widget in the title bar. + * + * The close flag is set before this callback is called, but you can modify it + * at any time with @ref glfwSetWindowShouldClose. + * + * The close callback is not triggered by @ref glfwDestroyWindow. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowclosefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @macos Selecting Quit from the application menu will trigger the + * close callback for all windows. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_close + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); + +/*! @brief Sets the refresh callback for the specified window. + * + * This function sets the refresh callback of the specified window, which is + * called when the content area of the window needs to be redrawn, for example + * if the window has been exposed after having been covered by another window. + * + * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where + * the window contents are saved off-screen, this callback may be called only + * very infrequently or never at all. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowrefreshfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_refresh + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); + +/*! @brief Sets the focus callback for the specified window. + * + * This function sets the focus callback of the specified window, which is + * called when the window gains or loses input focus. + * + * After the focus callback is called for a window that lost input focus, + * synthetic key and mouse button release events will be generated for all such + * that had been pressed. For more information, see @ref glfwSetKeyCallback + * and @ref glfwSetMouseButtonCallback. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowfocusfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); + +/*! @brief Sets the iconify callback for the specified window. + * + * This function sets the iconification callback of the specified window, which + * is called when the window is iconified or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowiconifyfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland The wl_shell protocol has no concept of iconification, + * this callback will never be called when using this deprecated protocol. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); + +/*! @brief Sets the maximize callback for the specified window. + * + * This function sets the maximization callback of the specified window, which + * is called when the window is maximized or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowmaximizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_maximize + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); + +/*! @brief Sets the framebuffer resize callback for the specified window. + * + * This function sets the framebuffer resize callback of the specified window, + * which is called when the framebuffer of the specified window is resized. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWframebuffersizefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); + +/*! @brief Sets the window content scale callback for the specified window. + * + * This function sets the window content scale callback of the specified window, + * which is called when the content scale of the specified window changes. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowcontentscalefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); + +/*! @brief Processes all pending events. + * + * This function processes only those events that are already in the event + * queue and then returns immediately. Processing events will cause the window + * and input callbacks associated with those events to be called. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 1.0. + * + * @ingroup window + */ +GLFWAPI void glfwPollEvents(void); + +/*! @brief Waits until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue. Once one or more events are available, + * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue + * are processed and the function then returns immediately. Processing events + * will cause the window and input callbacks associated with those events to be + * called. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 2.5. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEvents(void); + +/*! @brief Waits with timeout until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue, or until the specified timeout is reached. If + * one or more events are available, it behaves exactly like @ref + * glfwPollEvents, i.e. the events in the queue are processed and the function + * then returns immediately. Processing events will cause the window and input + * callbacks associated with those events to be called. + * + * The timeout value must be a positive finite number. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. + * + * Event processing is not required for joystick input to work. + * + * @param[in] timeout The maximum amount of time, in seconds, to wait. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEvents + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEventsTimeout(double timeout); + +/*! @brief Posts an empty event to the event queue. + * + * This function posts an empty event from the current thread to the event + * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwPostEmptyEvent(void); + +/*! @brief Returns the value of an input option for the specified window. + * + * This function returns the value of an input option for the specified window. + * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. + * + * @param[in] window The window to query. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); + +/*! @brief Sets an input option for the specified window. + * + * This function sets an input mode option for the specified window. The mode + * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. + * + * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor + * modes: + * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the + * content area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual + * and unlimited cursor movement. This is useful for implementing for + * example 3D camera controls. + * + * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to + * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are + * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` + * the next time it is called even if the key had been released before the + * call. This is useful when you are only interested in whether keys have been + * pressed but not when or in which order. + * + * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either + * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. + * If sticky mouse buttons are enabled, a mouse button press will ensure that + * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even + * if the mouse button had been released before the call. This is useful when + * you are only interested in whether mouse buttons have been pressed but not + * when or in which order. + * + * If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to + * enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled, + * callbacks that receive modifier bits will also have the @ref + * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, + * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. + * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * + * @param[in] window The window whose input mode to set. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. + * @param[in] value The new value of the specified input mode. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwGetInputMode + * + * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. + * + * @ingroup input + */ +GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); + +/*! @brief Returns whether raw mouse motion is supported. + * + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. + * + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. + * + * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + +/*! @brief Returns the layout-specific name of the specified printable key. + * + * This function returns the name of the specified printable key, encoded as + * UTF-8. This is typically the character that key would produce without any + * modifier keys, intended for displaying key bindings to the user. For dead + * keys, it is typically the diacritic it would add to a character. + * + * __Do not use this function__ for [text input](@ref input_char). You will + * break text input for many languages even if it happens to work for yours. + * + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key, + * otherwise the scancode is ignored. If you specify a non-printable key, or + * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this + * function returns `NULL` but does not emit an error. + * + * This behavior allows you to always pass in the arguments in the + * [key callback](@ref input_key) without modification. + * + * The printable keys are: + * - `GLFW_KEY_APOSTROPHE` + * - `GLFW_KEY_COMMA` + * - `GLFW_KEY_MINUS` + * - `GLFW_KEY_PERIOD` + * - `GLFW_KEY_SLASH` + * - `GLFW_KEY_SEMICOLON` + * - `GLFW_KEY_EQUAL` + * - `GLFW_KEY_LEFT_BRACKET` + * - `GLFW_KEY_RIGHT_BRACKET` + * - `GLFW_KEY_BACKSLASH` + * - `GLFW_KEY_WORLD_1` + * - `GLFW_KEY_WORLD_2` + * - `GLFW_KEY_0` to `GLFW_KEY_9` + * - `GLFW_KEY_A` to `GLFW_KEY_Z` + * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` + * - `GLFW_KEY_KP_DECIMAL` + * - `GLFW_KEY_KP_DIVIDE` + * - `GLFW_KEY_KP_MULTIPLY` + * - `GLFW_KEY_KP_SUBTRACT` + * - `GLFW_KEY_KP_ADD` + * - `GLFW_KEY_KP_EQUAL` + * + * Names for printable keys depend on keyboard layout, while names for + * non-printable keys are the same across layouts but depend on the application + * language and should be localized along with other user interface text. + * + * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. + * @param[in] scancode The scancode of the key to query. + * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark The contents of the returned string may change when a keyboard + * layout change event is received. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key_name + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetKeyName(int key, int scancode); + +/*! @brief Returns the platform-specific scancode of the specified key. + * + * This function returns the platform-specific scancode of the specified key. + * + * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this + * method will return `-1`. + * + * @param[in] key Any [named key](@ref keys). + * @return The platform-specific scancode for the key, or `-1` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref input_key + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetKeyScancode(int key); + +/*! @brief Returns the last reported state of a keyboard key for the specified + * window. + * + * This function returns the last state reported for the specified key to the + * specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to + * the key callback. + * + * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns + * `GLFW_PRESS` the first time you call it for a key that was pressed, even if + * that key has already been released. + * + * The key functions deal with physical keys, with [key tokens](@ref keys) + * named after their use on the standard US keyboard layout. If you want to + * input text, use the Unicode character callback instead. + * + * The [modifier key bit masks](@ref mods) are not key tokens and cannot be + * used with this function. + * + * __Do not use this function__ to implement [text input](@ref input_char). + * + * @param[in] window The desired window. + * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is + * not a valid key for this function. + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetKey(GLFWwindow* window, int key); + +/*! @brief Returns the last reported state of a mouse button for the specified + * window. + * + * This function returns the last state reported for the specified mouse button + * to the specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. + * + * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. + * + * @param[in] window The desired window. + * @param[in] button The desired [mouse button](@ref buttons). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); + +/*! @brief Retrieves the position of the cursor relative to the content area of + * the window. + * + * This function returns the position of the cursor, in screen coordinates, + * relative to the upper-left corner of the content area of the specified + * window. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * The coordinate can be converted to their integer equivalents with the + * `floor` function. Casting directly to an integer type works for positive + * coordinates, but fails for negative ones. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The desired window. + * @param[out] xpos Where to store the cursor x-coordinate, relative to the + * left edge of the content area, or `NULL`. + * @param[out] ypos Where to store the cursor y-coordinate, relative to the to + * top edge of the content area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPos + * + * @since Added in version 3.0. Replaces `glfwGetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); + +/*! @brief Sets the position of the cursor, relative to the content area of the + * window. + * + * This function sets the position, in screen coordinates, of the cursor + * relative to the upper-left corner of the content area of the specified + * window. The window must have input focus. If the window does not have + * input focus when this function is called, it fails silently. + * + * __Do not use this function__ to implement things like camera controls. GLFW + * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the + * cursor, transparently re-centers it and provides unconstrained cursor + * motion. See @ref glfwSetInputMode for more information. + * + * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is + * unconstrained and limited only by the minimum and maximum values of + * a `double`. + * + * @param[in] window The desired window. + * @param[in] xpos The desired x-coordinate, relative to the left edge of the + * content area. + * @param[in] ypos The desired y-coordinate, relative to the top edge of the + * content area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland This function will only work when the cursor mode is + * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwGetCursorPos + * + * @since Added in version 3.0. Replaces `glfwSetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); + +/*! @brief Creates a custom cursor. + * + * Creates a new custom cursor image that can be set for a window with @ref + * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. + * Any remaining cursors are destroyed by @ref glfwTerminate. + * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * + * The cursor hotspot is specified in pixels, relative to the upper-left corner + * of the cursor image. Like all other coordinate systems in GLFW, the X-axis + * points to the right and the Y-axis points down. + * + * @param[in] image The desired cursor image. + * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. + * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. + * @return The handle of the created cursor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwDestroyCursor + * @sa @ref glfwCreateStandardCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); + +/*! @brief Creates a cursor with a standard shape. + * + * Returns a cursor with a [standard shape](@ref shapes), that can be set for + * a window with @ref glfwSetCursor. + * + * @param[in] shape One of the [standard shapes](@ref shapes). + * @return A new cursor ready to use or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); + +/*! @brief Destroys a cursor. + * + * This function destroys a cursor previously created with @ref + * glfwCreateCursor. Any remaining cursors will be destroyed by @ref + * glfwTerminate. + * + * If the specified cursor is current for any window, that window will be + * reverted to the default cursor. This does not affect the cursor mode. + * + * @param[in] cursor The cursor object to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); + +/*! @brief Sets the cursor for the window. + * + * This function sets the cursor image to be used when the cursor is over the + * content area of the specified window. The set cursor will only be visible + * when the [cursor mode](@ref cursor_mode) of the window is + * `GLFW_CURSOR_NORMAL`. + * + * On some platforms, the set cursor may not be visible unless the window also + * has input focus. + * + * @param[in] window The window to set the cursor for. + * @param[in] cursor The cursor to set, or `NULL` to switch back to the default + * arrow cursor. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); + +/*! @brief Sets the key callback. + * + * This function sets the key callback of the specified window, which is called + * when a key is pressed, repeated or released. + * + * The key functions deal with physical keys, with layout independent + * [key tokens](@ref keys) named after their values in the standard US keyboard + * layout. If you want to input text, use the + * [character callback](@ref glfwSetCharCallback) instead. + * + * When a window loses input focus, it will generate synthetic key release + * events for all pressed keys. You can tell these events from user-generated + * events by the fact that the synthetic ones are generated after the focus + * loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * The scancode of a key is specific to that platform or sometimes even to that + * machine. Scancodes are intended to allow users to bind keys that don't have + * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their + * state is not saved and so it cannot be queried with @ref glfwGetKey. + * + * Sometimes GLFW needs to generate synthetic key events, in which case the + * scancode may be zero. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new key callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWkeyfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); + +/*! @brief Sets the Unicode character callback. + * + * This function sets the character callback of the specified window, which is + * called when a Unicode character is input. + * + * The character callback is intended for Unicode text input. As it deals with + * characters, it is keyboard layout dependent, whereas the + * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 + * to physical keys, as a key may produce zero, one or more characters. If you + * want to know whether a specific physical key was pressed or released, see + * the key callback instead. + * + * The character callback behaves as system text input normally does and will + * not be called if modifier keys are held down that would prevent normal text + * input on that platform, for example a Super (Command) key on macOS or Alt key + * on Windows. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); + +/*! @brief Sets the Unicode character with modifiers callback. + * + * This function sets the character with modifiers callback of the specified + * window, which is called when a Unicode character is input regardless of what + * modifier keys are used. + * + * The character with modifiers callback is intended for implementing custom + * Unicode character input. For regular Unicode text input, see the + * [character callback](@ref glfwSetCharCallback). Like the character + * callback, the character with modifiers callback deals with characters and is + * keyboard layout dependent. Characters do not map 1:1 to physical keys, as + * a key may produce zero, one or more characters. If you want to know whether + * a specific physical key was pressed or released, see the + * [key callback](@ref glfwSetKeyCallback) instead. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * [error](@ref error_handling) occurred. + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharmodsfun). + * + * @deprecated Scheduled for removal in version 4.0. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); + +/*! @brief Sets the mouse button callback. + * + * This function sets the mouse button callback of the specified window, which + * is called when a mouse button is pressed or released. + * + * When a window loses input focus, it will generate synthetic mouse button + * release events for all pressed mouse buttons. You can tell these events + * from user-generated events by the fact that the synthetic ones are generated + * after the focus loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmousebuttonfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); + +/*! @brief Sets the cursor position callback. + * + * This function sets the cursor position callback of the specified window, + * which is called when the cursor is moved. The callback is provided with the + * position, in screen coordinates, relative to the upper-left corner of the + * content area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorposfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * + * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); + +/*! @brief Sets the cursor enter/leave callback. + * + * This function sets the cursor boundary crossing callback of the specified + * window, which is called when the cursor enters or leaves the content area of + * the window. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorenterfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_enter + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); + +/*! @brief Sets the scroll callback. + * + * This function sets the scroll callback of the specified window, which is + * called when a scrolling device is used, such as a mouse wheel or scrolling + * area of a touchpad. + * + * The scroll callback receives all scrolling input, like that from a mouse + * wheel or a touchpad scrolling area. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new scroll callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWscrollfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref scrolling + * + * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); + +/*! @brief Sets the path drop callback. + * + * This function sets the path drop callback of the specified window, which is + * called when one or more dragged paths are dropped on the window. + * + * Because the path array and its strings may have been generated specifically + * for that event, they are not guaranteed to be valid after the callback has + * returned. If you wish to use them after the callback returns, you need to + * make a deep copy. + * + * @param[in] window The window whose callback to set. + * @param[in] callback The new file drop callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWdropfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland File drop is currently unimplemented. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref path_drop + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); + +/*! @brief Returns whether the specified joystick is present. + * + * This function returns whether the specified joystick is present. + * + * There is no need to call this function before other functions that accept + * a joystick ID, as they all check for presence before performing any other + * work. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick + * + * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickPresent(int jid); + +/*! @brief Returns the values of all axes of the specified joystick. + * + * This function returns the values of all axes of the specified joystick. + * Each element in the array is a value between -1.0 and 1.0. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of axis values in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of axis values, or `NULL` if the joystick is not present or + * an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_axis + * + * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. + * + * @ingroup input + */ +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); + +/*! @brief Returns the state of all buttons of the specified joystick. + * + * This function returns the state of all buttons of the specified joystick. + * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. + * + * For backward compatibility with earlier versions that did not have @ref + * glfwGetJoystickHats, the button array also includes all hats, each + * represented as four buttons. The hats are in the same order as returned by + * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and + * _left_. To disable these extra buttons, set the @ref + * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of button states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of button states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_button + * + * @since Added in version 2.2. + * @glfw3 Changed to return a dynamic array. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); + +/*! @brief Returns the state of all hats of the specified joystick. + * + * This function returns the state of all hats of the specified joystick. + * Each element in the array is one of the following values: + * + * Name | Value + * ---- | ----- + * `GLFW_HAT_CENTERED` | 0 + * `GLFW_HAT_UP` | 1 + * `GLFW_HAT_RIGHT` | 2 + * `GLFW_HAT_DOWN` | 4 + * `GLFW_HAT_LEFT` | 8 + * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` + * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` + * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` + * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` + * + * The diagonal directions are bitwise combinations of the primary (up, right, + * down and left) directions and you can test for these individually by ANDing + * it with the corresponding direction. + * + * @code + * if (hats[2] & GLFW_HAT_RIGHT) + * { + * // State of hat 2 could be right-up, right or right-down + * } + * @endcode + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of hat states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of hat states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_hat + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); + +/*! @brief Returns the name of the specified joystick. + * + * This function returns the name, encoded as UTF-8, of the specified joystick. + * The returned string is allocated and freed by GLFW. You should not free it + * yourself. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_name + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickName(int jid); + +/*! @brief Returns the SDL compatible GUID of the specified joystick. + * + * This function returns the SDL compatible GUID, as a UTF-8 encoded + * hexadecimal string, of the specified joystick. The returned string is + * allocated and freed by GLFW. You should not free it yourself. + * + * The GUID is what connects a joystick to a gamepad mapping. A connected + * joystick will always have a GUID even if there is no gamepad mapping + * assigned to it. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to + * uniquely identify the make and model of a joystick but does not identify + * a specific unit, e.g. all wired Xbox 360 controllers will have the same + * GUID on that platform. The GUID for a unit may vary between platforms + * depending on what hardware information the platform specific APIs provide. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickGUID(int jid); + +/*! @brief Sets the user pointer of the specified joystick. + * + * This function sets the user-defined pointer of the specified joystick. The + * current value is retained until the joystick is disconnected. The initial + * value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwGetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer); + +/*! @brief Returns the user pointer of the specified joystick. + * + * This function returns the current value of the user-defined pointer of the + * specified joystick. The initial value is `NULL`. + * + * This function may be called from the joystick callback, even for a joystick + * that is being disconnected. + * + * @param[in] jid The joystick whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref joystick_userptr + * @sa @ref glfwSetJoystickUserPointer + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI void* glfwGetJoystickUserPointer(int jid); + +/*! @brief Returns whether the specified joystick has a gamepad mapping. + * + * This function returns whether the specified joystick is both present and has + * a gamepad mapping. + * + * If the specified joystick is present but does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check if a joystick is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickIsGamepad(int jid); + +/*! @brief Sets the joystick configuration callback. + * + * This function sets the joystick configuration callback, or removes the + * currently set callback. This is called when a joystick is connected to or + * disconnected from the system. + * + * For joystick connection and disconnection events to be delivered on all + * platforms, you need to call one of the [event processing](@ref events) + * functions. Joystick disconnection may also be detected and the callback + * called by joystick functions. The function will then return whatever it + * returns if the joystick is not present. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(int jid, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWjoystickfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_event + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); + +/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. + * + * This function parses the specified ASCII encoded string and updates the + * internal list with any gamepad mappings it finds. This string may + * contain either a single gamepad mapping or many mappings separated by + * newlines. The parser supports the full format of the `gamecontrollerdb.txt` + * source file including empty lines and comments. + * + * See @ref gamepad_mapping for a description of the format. + * + * If there is already a gamepad mapping for a given GUID in the internal list, + * it will be replaced by the one passed to this function. If the library is + * terminated and re-initialized the internal list will revert to the built-in + * default. + * + * @param[in] string The string containing the gamepad mappings. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * @sa @ref glfwGetGamepadName + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwUpdateGamepadMappings(const char* string); + +/*! @brief Returns the human-readable gamepad name for the specified joystick. + * + * This function returns the human-readable name of the gamepad from the + * gamepad mapping assigned to the specified joystick. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `NULL` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the gamepad, or `NULL` if the + * joystick is not present, does not have a mapping or an + * [error](@ref error_handling) occurred. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, the gamepad mappings are updated or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetGamepadName(int jid); + +/*! @brief Retrieves the state of the specified joystick remapped as a gamepad. + * + * This function retrieves the state of the specified joystick remapped to + * an Xbox-like gamepad. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * The Guide button may not be available for input as it is often hooked by the + * system or the Steam client. + * + * Not all devices have all the buttons or axes provided by @ref + * GLFWgamepadstate. Unavailable buttons and axes will always report + * `GLFW_RELEASE` and 0.0 respectively. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] state The gamepad input state of the joystick. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is + * connected, it has no gamepad mapping or an [error](@ref error_handling) + * occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwUpdateGamepadMappings + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); + +/*! @brief Sets the clipboard to the specified string. + * + * This function sets the system clipboard to the specified, UTF-8 encoded + * string. + * + * @param[in] window Deprecated. Any valid window or `NULL`. + * @param[in] string A UTF-8 encoded string. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa @ref glfwGetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); + +/*! @brief Returns the contents of the clipboard as a string. + * + * This function returns the contents of the system clipboard, if it contains + * or is convertible to a UTF-8 encoded string. If the clipboard is empty or + * if its contents cannot be converted, `NULL` is returned and a @ref + * GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @param[in] window Deprecated. Any valid window or `NULL`. + * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa @ref glfwSetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); + +/*! @brief Returns the GLFW time. + * + * This function returns the current GLFW time, in seconds. Unless the time + * has been set using @ref glfwSetTime it measures time elapsed since GLFW was + * initialized. + * + * This function and @ref glfwSetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. + * + * The resolution of the timer is system dependent, but is usually on the order + * of a few micro- or nanoseconds. It uses the highest-resolution monotonic + * time source on each supported platform. + * + * @return The current time, in seconds, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal base time is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwSetTime. + * + * @sa @ref time + * + * @since Added in version 1.0. + * + * @ingroup input + */ +GLFWAPI double glfwGetTime(void); + +/*! @brief Sets the GLFW time. + * + * This function sets the current GLFW time, in seconds. The value must be + * a positive finite number less than or equal to 18446744073.0, which is + * approximately 584.5 years. + * + * This function and @ref glfwGetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. + * + * @param[in] time The new value, in seconds. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @remark The upper limit of GLFW time is calculated as + * floor((264 - 1) / 109) and is due to implementations + * storing nanoseconds in 64 bits. The limit may be increased in the future. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal base time is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwGetTime. + * + * @sa @ref time + * + * @since Added in version 2.2. + * + * @ingroup input + */ +GLFWAPI void glfwSetTime(double time); + +/*! @brief Returns the current value of the raw timer. + * + * This function returns the current value of the raw timer, measured in + * 1 / frequency seconds. To get the frequency, call @ref + * glfwGetTimerFrequency. + * + * @return The value of the timer, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerFrequency + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerValue(void); + +/*! @brief Returns the frequency, in Hz, of the raw timer. + * + * This function returns the frequency, in Hz, of the raw timer. + * + * @return The frequency of the timer, in Hz, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerValue + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerFrequency(void); + +/*! @brief Makes the context of the specified window current for the calling + * thread. + * + * This function makes the OpenGL or OpenGL ES context of the specified window + * current on the calling thread. A context must only be made current on + * a single thread at a time and each thread can have only a single current + * context at a time. + * + * When moving a context between threads, you must make it non-current on the + * old thread before making it current on the new one. + * + * By default, making a context non-current implicitly forces a pipeline flush. + * On machines that support `GL_KHR_context_flush_control`, you can control + * whether a context performs this flush by setting the + * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) + * hint. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * @param[in] window The window whose context to make current, or `NULL` to + * detach the current context. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwGetCurrentContext + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); + +/*! @brief Returns the window whose context is current on the calling thread. + * + * This function returns the window whose OpenGL or OpenGL ES context is + * current on the calling thread. + * + * @return The window whose context is current, or `NULL` if no window's + * context is current. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwMakeContextCurrent + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI GLFWwindow* glfwGetCurrentContext(void); + +/*! @brief Swaps the front and back buffers of the specified window. + * + * This function swaps the front and back buffers of the specified window when + * rendering with OpenGL or OpenGL ES. If the swap interval is greater than + * zero, the GPU driver waits the specified number of screen updates before + * swapping the buffers. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see `vkQueuePresentKHR` instead. + * + * @param[in] window The window whose buffers to swap. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark __EGL:__ The context of the specified window must be current on the + * calling thread. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapInterval + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSwapBuffers(GLFWwindow* window); + +/*! @brief Sets the swap interval for the current context. + * + * This function sets the swap interval for the current OpenGL or OpenGL ES + * context, i.e. the number of screen updates to wait from the time @ref + * glfwSwapBuffers was called before swapping the buffers and returning. This + * is sometimes called _vertical synchronization_, _vertical retrace + * synchronization_ or just _vsync_. + * + * A context that supports either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap + * intervals, which allows the driver to swap immediately even if a frame + * arrives a little bit late. You can check for these extensions with @ref + * glfwExtensionSupported. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see the present mode of your swapchain instead. + * + * @param[in] interval The minimum number of screen updates to wait for + * until the buffers are swapped by @ref glfwSwapBuffers. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark This function is not called during context creation, leaving the + * swap interval set to whatever is the default on that platform. This is done + * because some swap interval extensions used by GLFW do not allow the swap + * interval to be reset to zero once it has been set to a non-zero value. + * + * @remark Some GPU drivers do not honor the requested swap interval, either + * because of a user setting that overrides the application's request or due to + * bugs in the driver. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapBuffers + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI void glfwSwapInterval(int interval); + +/*! @brief Returns whether the specified extension is available. + * + * This function returns whether the specified + * [API extension](@ref context_glext) is supported by the current OpenGL or + * OpenGL ES context. It searches both for client API extension and context + * creation API extensions. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * As this functions retrieves and searches one or more extension strings each + * call, it is recommended that you cache its results if it is going to be used + * frequently. The extension strings will not change during the lifetime of + * a context, so there is no danger in doing this. + * + * This function does not apply to Vulkan. If you are using Vulkan, see @ref + * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` + * and `vkEnumerateDeviceExtensionProperties` instead. + * + * @param[in] extension The ASCII encoded name of the extension. + * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI int glfwExtensionSupported(const char* extension); + +/*! @brief Returns the address of the specified function for the current + * context. + * + * This function returns the address of the specified OpenGL or OpenGL ES + * [core or extension function](@ref context_glext), if it is supported + * by the current context. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and + * `vkGetDeviceProcAddr` instead. + * + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark The address of a given function is not guaranteed to be the same + * between contexts. + * + * @remark This function may return a non-`NULL` address despite the + * associated version or extension not being available. Always check the + * context version or extension string first. + * + * @pointer_lifetime The returned function pointer is valid until the context + * is destroyed or the library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwExtensionSupported + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); + +/*! @brief Returns whether the Vulkan loader and an ICD have been found. + * + * This function returns whether the Vulkan loader and any minimally functional + * ICD have been found. + * + * The availability of a Vulkan loader and even an ICD does not by itself + * guarantee that surface creation or even instance creation is possible. + * For example, on Fermi systems Nvidia will install an ICD that provides no + * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check + * whether the extensions necessary for Vulkan surface creation are available + * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue + * family of a physical device supports image presentation. + * + * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_support + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwVulkanSupported(void); + +/*! @brief Returns the Vulkan instance extensions required by GLFW. + * + * This function returns an array of names of Vulkan instance extensions required + * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the + * list will always contain `VK_KHR_surface`, so if you don't require any + * additional extensions you can pass this list directly to the + * `VkInstanceCreateInfo` struct. + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available. + * + * If Vulkan is available but no set of extensions allowing window surface + * creation was found, this function returns `NULL`. You may still use Vulkan + * for off-screen rendering and compute work. + * + * @param[out] count Where to store the number of extensions in the returned + * array. This is set to zero if an error occurred. + * @return An array of ASCII encoded extension names, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @remark Additional extensions may be required by future versions of GLFW. + * You should check if any extensions you wish to enable are already in the + * returned array, as it is an error to specify an extension more than once in + * the `VkInstanceCreateInfo` struct. + * + * @remark @macos GLFW currently supports both the `VK_MVK_macos_surface` and + * the newer `VK_EXT_metal_surface` extensions. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_ext + * @sa @ref glfwCreateWindowSurface + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); + +#if defined(VK_VERSION_1_0) + +/*! @brief Returns the address of the specified Vulkan instance function. + * + * This function returns the address of the specified Vulkan core or extension + * function for the specified instance. If instance is set to `NULL` it can + * return any function exported from the Vulkan loader, including at least the + * following functions: + * + * - `vkEnumerateInstanceExtensionProperties` + * - `vkEnumerateInstanceLayerProperties` + * - `vkCreateInstance` + * - `vkGetInstanceProcAddr` + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available. + * + * This function is equivalent to calling `vkGetInstanceProcAddr` with + * a platform-specific query of the Vulkan loader as a fallback. + * + * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve + * functions related to instance creation. + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @pointer_lifetime The returned function pointer is valid until the library + * is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_proc + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); + +/*! @brief Returns whether the specified queue family can present images. + * + * This function returns whether the specified queue family of the specified + * physical device supports presentation to the platform GLFW was built for. + * + * If Vulkan or the required window surface creation instance extensions are + * not available on the machine, or if the specified instance was not created + * with the required extensions, this function returns `GLFW_FALSE` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is at least minimally available and @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * @param[in] instance The instance that the physical device belongs to. + * @param[in] device The physical device that the queue family belongs to. + * @param[in] queuefamily The index of the queue family to query. + * @return `GLFW_TRUE` if the queue family supports presentation, or + * `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @remark @macos This function currently always returns `GLFW_TRUE`, as the + * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide + * a `vkGetPhysicalDevice*PresentationSupport` type function. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_present + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); + +/*! @brief Creates a Vulkan surface for the specified window. + * + * This function creates a Vulkan surface for the specified window. + * + * If the Vulkan loader or at least one minimally functional ICD were not found, + * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref + * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether + * Vulkan is at least minimally available. + * + * If the required window surface creation instance extensions are not + * available or if the specified instance was not created with these extensions + * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * The window surface cannot be shared with another API so the window must + * have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib) + * set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error + * and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`. + * + * The window surface must be destroyed before the specified Vulkan instance. + * It is the responsibility of the caller to destroy the window surface. GLFW + * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the + * surface. + * + * @param[in] instance The Vulkan instance to create the surface in. + * @param[in] window The window to create the surface for. + * @param[in] allocator The allocator to use, or `NULL` to use the default + * allocator. + * @param[out] surface Where to store the handle of the surface. This is set + * to `VK_NULL_HANDLE` if an error occurred. + * @return `VK_SUCCESS` if successful, or a Vulkan error code if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE + * + * @remark If an error occurs before the creation call is made, GLFW returns + * the Vulkan error code most appropriate for the error. Appropriate use of + * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should + * eliminate almost all occurrences of these errors. + * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * + * @remark @macos This function creates and sets a `CAMetalLayer` instance for + * the window content view, which is required for MoltenVK to function. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_surface + * @sa @ref glfwGetRequiredInstanceExtensions + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +#endif /*VK_VERSION_1_0*/ + + +/************************************************************************* + * Global definition cleanup + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +#ifdef GLFW_WINGDIAPI_DEFINED + #undef WINGDIAPI + #undef GLFW_WINGDIAPI_DEFINED +#endif + +#ifdef GLFW_CALLBACK_DEFINED + #undef CALLBACK + #undef GLFW_CALLBACK_DEFINED +#endif + +/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally + * defined by some gl.h variants (OpenBSD) so define it after if needed. + */ +#ifndef GLAPIENTRY + #define GLAPIENTRY APIENTRY +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_h_ */ + diff --git a/FCLauncher/src/main/jni/glfw/include/glfw3native.h b/FCLauncher/src/main/jni/glfw/include/glfw3native.h new file mode 100644 index 00000000..d887b94f --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/glfw3native.h @@ -0,0 +1,541 @@ +/************************************************************************* + * GLFW 3.3 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2018 Camilla Löwy + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_native_h_ +#define _glfw3_native_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3native.h + * @brief The header of the native access functions. + * + * This is the header file of the native access functions. See @ref native for + * more information. + */ +/*! @defgroup native Native access + * @brief Functions related to accessing native handles. + * + * **By using the native access functions you assert that you know what you're + * doing and how to fix problems caused by using them. If you don't, you + * shouldn't be using them.** + * + * Before the inclusion of @ref glfw3native.h, you may define zero or more + * window system API macro and zero or more context creation API macros. + * + * The chosen backends must match those the library was compiled for. Failure + * to do this will cause a link-time error. + * + * The available window API macros are: + * * `GLFW_EXPOSE_NATIVE_WIN32` + * * `GLFW_EXPOSE_NATIVE_COCOA` + * * `GLFW_EXPOSE_NATIVE_X11` + * * `GLFW_EXPOSE_NATIVE_WAYLAND` + * + * The available context API macros are: + * * `GLFW_EXPOSE_NATIVE_WGL` + * * `GLFW_EXPOSE_NATIVE_NSGL` + * * `GLFW_EXPOSE_NATIVE_GLX` + * * `GLFW_EXPOSE_NATIVE_EGL` + * * `GLFW_EXPOSE_NATIVE_OSMESA` + * + * These macros select which of the native access functions that are declared + * and which platform-specific headers to include. It is then up your (by + * definition platform-specific) code to handle which of these should be + * defined. + */ + + +/************************************************************************* + * System headers and types + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) + // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + // example to allow applications to correctly declare a GL_KHR_debug callback) + // but windows.h assumes no one will define APIENTRY before it does + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif + #include +#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) + #if defined(__OBJC__) + #import + #else + #include + typedef void* id; + #endif +#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #include + #include +#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_GLX) + #include +#endif +#if defined(GLFW_EXPOSE_NATIVE_EGL) + #include +#endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) + #include +#endif + + +/************************************************************************* + * Functions + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) +/*! @brief Returns the adapter device name of the specified monitor. + * + * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) + * of the specified monitor, or `NULL` if an [error](@ref error_handling) + * occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the display device name of the specified monitor. + * + * @return The UTF-8 encoded display device name (for example + * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `HWND` of the specified window. + * + * @return The `HWND` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) +/*! @brief Returns the `HGLRC` of the specified window. + * + * @return The `HGLRC` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_COCOA) +/*! @brief Returns the `CGDirectDisplayID` of the specified monitor. + * + * @return The `CGDirectDisplayID` of the specified monitor, or + * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the `NSWindow` of the specified window. + * + * @return The `NSWindow` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_NSGL) +/*! @brief Returns the `NSOpenGLContext` of the specified window. + * + * @return The `NSOpenGLContext` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_X11) +/*! @brief Returns the `Display` used by GLFW. + * + * @return The `Display` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI Display* glfwGetX11Display(void); + +/*! @brief Returns the `RRCrtc` of the specified monitor. + * + * @return The `RRCrtc` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the `RROutput` of the specified monitor. + * + * @return The `RROutput` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `Window` of the specified window. + * + * @return The `Window` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI Window glfwGetX11Window(GLFWwindow* window); + +/*! @brief Sets the current primary selection to the specified string. + * + * @param[in] string A UTF-8 encoded string. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwGetX11SelectionString + * @sa glfwSetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI void glfwSetX11SelectionString(const char* string); + +/*! @brief Returns the contents of the current primary selection as a string. + * + * If the selection is empty or if its contents cannot be converted, `NULL` + * is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @return The contents of the selection as a UTF-8 encoded string, or `NULL` + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the + * library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwSetX11SelectionString + * @sa glfwGetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetX11SelectionString(void); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_GLX) +/*! @brief Returns the `GLXContext` of the specified window. + * + * @return The `GLXContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); + +/*! @brief Returns the `GLXWindow` of the specified window. + * + * @return The `GLXWindow` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) +/*! @brief Returns the `struct wl_display*` used by GLFW. + * + * @return The `struct wl_display*` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); + +/*! @brief Returns the `struct wl_output*` of the specified monitor. + * + * @return The `struct wl_output*` of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the main `struct wl_surface*` of the specified window. + * + * @return The main `struct wl_surface*` of the specified window, or `NULL` if + * an [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_EGL) +/*! @brief Returns the `EGLDisplay` used by GLFW. + * + * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLDisplay glfwGetEGLDisplay(void); + +/*! @brief Returns the `EGLContext` of the specified window. + * + * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); + +/*! @brief Returns the `EGLSurface` of the specified window. + * + * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) +/*! @brief Retrieves the color buffer associated with the specified window. + * + * @param[in] window The window whose color buffer to retrieve. + * @param[out] width Where to store the width of the color buffer, or `NULL`. + * @param[out] height Where to store the height of the color buffer, or `NULL`. + * @param[out] format Where to store the OSMesa pixel format of the color + * buffer, or `NULL`. + * @param[out] buffer Where to store the address of the color buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); + +/*! @brief Retrieves the depth buffer associated with the specified window. + * + * @param[in] window The window whose depth buffer to retrieve. + * @param[out] width Where to store the width of the depth buffer, or `NULL`. + * @param[out] height Where to store the height of the depth buffer, or `NULL`. + * @param[out] bytesPerValue Where to store the number of bytes per depth + * buffer element, or `NULL`. + * @param[out] buffer Where to store the address of the depth buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); + +/*! @brief Returns the `OSMesaContext` of the specified window. + * + * @return The `OSMesaContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_native_h_ */ + diff --git a/FCLauncher/src/main/jni/glfw/include/glfw_config.h b/FCLauncher/src/main/jni/glfw/include/glfw_config.h new file mode 100644 index 00000000..fab4784f --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/glfw_config.h @@ -0,0 +1,62 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2010-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As glfw_config.h.in, this file is used by CMake to produce the +// glfw_config.h configuration header file. If you are adding a feature +// requiring conditional compilation, this is where to add the macro. +//======================================================================== +// As glfw_config.h, this file defines compile-time option macros for a +// specific platform and development environment. If you are using the +// GLFW CMake files, modify glfw_config.h.in instead of this file. If you +// are using your own build system, make this file define the appropriate +// macros in whatever way is suitable. +//======================================================================== + +// Define this to 1 if building GLFW for X11 +#define _GLFW_X11 +// Define this to 1 if building GLFW for Win32 +#define _GLFW_WIN32 +// Define this to 1 if building GLFW for Cocoa +#define _GLFW_COCOA +// Define this to 1 if building GLFW for Wayland +#define _GLFW_WAYLAND +// Define this to 1 if building GLFW for Boat +#define _GLFW_FCL +// Define this to 1 if building GLFW for OSMesa +#define _GLFW_OSMESA + +// Define this to 1 if building as a shared library / dynamic library / DLL +#define _GLFW_BUILD_DLL +// Define this to 1 to use Vulkan loader linked statically into application +#define _GLFW_VULKAN_STATIC + +// Define this to 1 to force use of high-performance GPU on hybrid systems +#define _GLFW_USE_HYBRID_HPG + +// Define this to 1 if xkbcommon supports the compose key +#define HAVE_XKBCOMMON_COMPOSE_H +// Define this to 1 if the libc supports memfd_create() +#define HAVE_MEMFD_CREATE + diff --git a/FCLauncher/src/main/jni/glfw/include/internal.h b/FCLauncher/src/main/jni/glfw/include/internal.h new file mode 100644 index 00000000..2178bf1f --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/internal.h @@ -0,0 +1,763 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2019 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#pragma once + +#if defined(_GLFW_USE_CONFIG_H) + #include +#endif + +#if defined(GLFW_INCLUDE_GLCOREARB) || \ + defined(GLFW_INCLUDE_ES1) || \ + defined(GLFW_INCLUDE_ES2) || \ + defined(GLFW_INCLUDE_ES3) || \ + defined(GLFW_INCLUDE_ES31) || \ + defined(GLFW_INCLUDE_ES32) || \ + defined(GLFW_INCLUDE_NONE) || \ + defined(GLFW_INCLUDE_GLEXT) || \ + defined(GLFW_INCLUDE_GLU) || \ + defined(GLFW_INCLUDE_VULKAN) || \ + defined(GLFW_DLL) +#endif + +#define GLFW_INCLUDE_NONE +#include + +#define _GLFW_INSERT_FIRST 0 +#define _GLFW_INSERT_LAST 1 + +#define _GLFW_POLL_PRESENCE 0 +#define _GLFW_POLL_AXES 1 +#define _GLFW_POLL_BUTTONS 2 +#define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS) + +#define _GLFW_MESSAGE_SIZE 1024 + +typedef int GLFWbool; + +typedef struct _GLFWerror _GLFWerror; +typedef struct _GLFWinitconfig _GLFWinitconfig; +typedef struct _GLFWwndconfig _GLFWwndconfig; +typedef struct _GLFWctxconfig _GLFWctxconfig; +typedef struct _GLFWfbconfig _GLFWfbconfig; +typedef struct _GLFWcontext _GLFWcontext; +typedef struct _GLFWwindow _GLFWwindow; +typedef struct _GLFWlibrary _GLFWlibrary; +typedef struct _GLFWmonitor _GLFWmonitor; +typedef struct _GLFWcursor _GLFWcursor; +typedef struct _GLFWmapelement _GLFWmapelement; +typedef struct _GLFWmapping _GLFWmapping; +typedef struct _GLFWjoystick _GLFWjoystick; +typedef struct _GLFWtls _GLFWtls; +typedef struct _GLFWmutex _GLFWmutex; + +typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); +typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); +typedef void (* _GLFWswapintervalfun)(int); +typedef int (* _GLFWextensionsupportedfun)(const char*); +typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); +typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); + +#define GL_VERSION 0x1f02 +#define GL_NONE 0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_EXTENSIONS 0x1f03 +#define GL_NUM_EXTENSIONS 0x821d +#define GL_CONTEXT_FLAGS 0x821e +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82fb +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82fc +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 + +typedef int GLint; +typedef unsigned int GLuint; +typedef unsigned int GLenum; +typedef unsigned int GLbitfield; +typedef unsigned char GLubyte; + +typedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield); +typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum); +typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*); +typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint); + +#define VK_NULL_HANDLE 0 + +typedef void* VkInstance; +typedef void* VkPhysicalDevice; +typedef uint64_t VkSurfaceKHR; +typedef uint32_t VkFlags; +typedef uint32_t VkBool32; + +typedef enum VkStructureType +{ + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, + VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, + VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkStructureType; + +typedef enum VkResult +{ + VK_SUCCESS = 0, + VK_NOT_READY = 1, + VK_TIMEOUT = 2, + VK_EVENT_SET = 3, + VK_EVENT_RESET = 4, + VK_INCOMPLETE = 5, + VK_ERROR_OUT_OF_HOST_MEMORY = -1, + VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, + VK_ERROR_INITIALIZATION_FAILED = -3, + VK_ERROR_DEVICE_LOST = -4, + VK_ERROR_MEMORY_MAP_FAILED = -5, + VK_ERROR_LAYER_NOT_PRESENT = -6, + VK_ERROR_EXTENSION_NOT_PRESENT = -7, + VK_ERROR_FEATURE_NOT_PRESENT = -8, + VK_ERROR_INCOMPATIBLE_DRIVER = -9, + VK_ERROR_TOO_MANY_OBJECTS = -10, + VK_ERROR_FORMAT_NOT_SUPPORTED = -11, + VK_ERROR_SURFACE_LOST_KHR = -1000000000, + VK_SUBOPTIMAL_KHR = 1000001003, + VK_ERROR_OUT_OF_DATE_KHR = -1000001004, + VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, + VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_RESULT_MAX_ENUM = 0x7FFFFFFF +} VkResult; + +typedef struct VkAllocationCallbacks VkAllocationCallbacks; + +typedef struct VkExtensionProperties +{ + char extensionName[256]; + uint32_t specVersion; +} VkExtensionProperties; + +typedef void (APIENTRY * PFN_vkVoidFunction)(void); + +#if defined(_GLFW_VULKAN_STATIC) + PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance,const char*); + VkResult vkEnumerateInstanceExtensionProperties(const char*,uint32_t*,VkExtensionProperties*); +#else + typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); + typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); + #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties + #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr +#endif + +#include "fcl_platform.h" + +// Constructs a version number string from the public header macros +#define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r +#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r) +#define _GLFW_VERSION_NUMBER _GLFW_MAKE_VERSION(GLFW_VERSION_MAJOR, \ + GLFW_VERSION_MINOR, \ + GLFW_VERSION_REVISION) + +// Checks for whether the library has been initialized +#define _GLFW_REQUIRE_INIT() \ + if (!_glfw.initialized) \ + { \ + _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ + return; \ + } +#define _GLFW_REQUIRE_INIT_OR_RETURN(x) \ + if (!_glfw.initialized) \ + { \ + _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ + return x; \ + } + +// Swaps the provided pointers +#define _GLFW_SWAP_POINTERS(x, y) \ + { \ + void* t; \ + t = x; \ + x = y; \ + y = t; \ + } + +// Per-thread error structure +// +struct _GLFWerror +{ + _GLFWerror* next; + int code; + char description[_GLFW_MESSAGE_SIZE]; +}; + +// Initialization configuration +// +// Parameters relating to the initialization of the library +// +struct _GLFWinitconfig +{ + GLFWbool hatButtons; + struct { + GLFWbool menubar; + GLFWbool chdir; + } ns; +}; + +// Window configuration +// +// Parameters relating to the creation of the window but not directly related +// to the framebuffer. This is used to pass window creation parameters from +// shared code to the platform API. +// +struct _GLFWwndconfig +{ + int width; + int height; + const char* title; + GLFWbool resizable; + GLFWbool visible; + GLFWbool decorated; + GLFWbool focused; + GLFWbool autoIconify; + GLFWbool floating; + GLFWbool maximized; + GLFWbool centerCursor; + GLFWbool focusOnShow; + GLFWbool scaleToMonitor; + struct { + GLFWbool retina; + char frameName[256]; + } ns; + struct { + char className[256]; + char instanceName[256]; + } x11; +}; + +// Context configuration +// +// Parameters relating to the creation of the context but not directly related +// to the framebuffer. This is used to pass context creation parameters from +// shared code to the platform API. +// +struct _GLFWctxconfig +{ + int client; + int source; + int major; + int minor; + GLFWbool forward; + GLFWbool debug; + GLFWbool noerror; + int profile; + int robustness; + int release; + _GLFWwindow* share; + struct { + GLFWbool offline; + } nsgl; +}; + +// Framebuffer configuration +// +// This describes buffers and their sizes. It also contains +// a platform-specific ID used to map back to the backend API object. +// +// It is used to pass framebuffer parameters from shared code to the platform +// API and also to enumerate and select available framebuffer configs. +// +struct _GLFWfbconfig +{ + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + GLFWbool stereo; + int samples; + GLFWbool sRGB; + GLFWbool doublebuffer; + GLFWbool transparent; + uintptr_t handle; +}; + +// Context structure +// +struct _GLFWcontext +{ + int client; + int source; + int major, minor, revision; + GLFWbool forward, debug, noerror; + int profile; + int robustness; + int release; + + PFNGLGETSTRINGIPROC GetStringi; + PFNGLGETINTEGERVPROC GetIntegerv; + PFNGLGETSTRINGPROC GetString; + + _GLFWmakecontextcurrentfun makeCurrent; + _GLFWswapbuffersfun swapBuffers; + _GLFWswapintervalfun swapInterval; + _GLFWextensionsupportedfun extensionSupported; + _GLFWgetprocaddressfun getProcAddress; + _GLFWdestroycontextfun destroy; + + // This is defined in the context API's context.h + _GLFW_PLATFORM_CONTEXT_STATE; + // This is defined in egl_context.h + _GLFW_EGL_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_CONTEXT_STATE; + +}; + +// Window and context structure +// +struct _GLFWwindow +{ + struct _GLFWwindow* next; + + // Window settings and state + GLFWbool resizable; + GLFWbool decorated; + GLFWbool autoIconify; + GLFWbool floating; + GLFWbool focusOnShow; + GLFWbool shouldClose; + void* userPointer; + GLFWbool doublebuffer; + GLFWvidmode videoMode; + _GLFWmonitor* monitor; + _GLFWcursor* cursor; + + int minwidth, minheight; + int maxwidth, maxheight; + int numer, denom; + + GLFWbool stickyKeys; + GLFWbool stickyMouseButtons; + GLFWbool lockKeyMods; + int cursorMode; + char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; + char keys[GLFW_KEY_LAST + 1]; + // Virtual cursor position when cursor is disabled + double virtualCursorPosX, virtualCursorPosY; + GLFWbool rawMouseMotion; + + _GLFWcontext context; + + struct { + GLFWwindowposfun pos; + GLFWwindowsizefun size; + GLFWwindowclosefun close; + GLFWwindowrefreshfun refresh; + GLFWwindowfocusfun focus; + GLFWwindowiconifyfun iconify; + GLFWwindowmaximizefun maximize; + GLFWframebuffersizefun fbsize; + GLFWwindowcontentscalefun scale; + GLFWmousebuttonfun mouseButton; + GLFWcursorposfun cursorPos; + GLFWcursorenterfun cursorEnter; + GLFWscrollfun scroll; + GLFWkeyfun key; + GLFWcharfun character; + GLFWcharmodsfun charmods; + GLFWdropfun drop; + } callbacks; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_WINDOW_STATE; + +}; + +// Monitor structure +// +struct _GLFWmonitor +{ + char name[128]; + void* userPointer; + + // Physical dimensions in millimeters. + int widthMM, heightMM; + + // The window whose video mode is current on this monitor + _GLFWwindow* window; + + GLFWvidmode* modes; + int modeCount; + GLFWvidmode currentMode; + + GLFWgammaramp originalRamp; + GLFWgammaramp currentRamp; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_MONITOR_STATE; + +}; + +// Cursor structure +// +struct _GLFWcursor +{ + _GLFWcursor* next; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_CURSOR_STATE; +}; + +// Gamepad mapping element structure +// +struct _GLFWmapelement +{ + uint8_t type; + uint8_t index; + int8_t axisScale; + int8_t axisOffset; +}; + +// Gamepad mapping structure +// +struct _GLFWmapping +{ + char name[128]; + char guid[33]; + _GLFWmapelement buttons[15]; + _GLFWmapelement axes[6]; +}; + +// Joystick structure +// +struct _GLFWjoystick +{ + GLFWbool present; + float* axes; + int axisCount; + unsigned char* buttons; + int buttonCount; + unsigned char* hats; + int hatCount; + char name[128]; + void* userPointer; + char guid[33]; + _GLFWmapping* mapping; + + // This is defined in the joystick API's joystick.h + _GLFW_PLATFORM_JOYSTICK_STATE; + +}; + +// Thread local storage structure +// +struct _GLFWtls +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_TLS_STATE; +}; + +// Mutex structure +// +struct _GLFWmutex +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_MUTEX_STATE; +}; + +// Library global data +// +struct _GLFWlibrary +{ + GLFWbool initialized; + + struct { + _GLFWinitconfig init; + _GLFWfbconfig framebuffer; + _GLFWwndconfig window; + _GLFWctxconfig context; + int refreshRate; + } hints; + + _GLFWerror* errorListHead; + _GLFWcursor* cursorListHead; + _GLFWwindow* windowListHead; + + _GLFWmonitor** monitors; + int monitorCount; + + _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; + _GLFWmapping* mappings; + int mappingCount; + + _GLFWtls errorSlot; + _GLFWtls contextSlot; + _GLFWmutex errorLock; + + struct { + uint64_t offset; + // This is defined in the platform's time.h + _GLFW_PLATFORM_LIBRARY_TIMER_STATE; + } timer; + + struct { + GLFWbool available; + void* handle; + char* extensions[2]; +#if !defined(_GLFW_VULKAN_STATIC) + PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; + PFN_vkGetInstanceProcAddr GetInstanceProcAddr; +#endif + GLFWbool KHR_surface; + + GLFWbool KHR_android_surface; + } vk; + + struct { + GLFWmonitorfun monitor; + GLFWjoystickfun joystick; + } callbacks; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; + // This is defined in the context API's context.h + _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; + // This is defined in the platform's joystick.h + _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; + // This is defined in egl_context.h + _GLFW_EGL_LIBRARY_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_LIBRARY_CONTEXT_STATE; + +}; + +// Global state shared between compilation units of GLFW +// +extern _GLFWlibrary _glfw; + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void); +void _glfwPlatformTerminate(void); +const char* _glfwPlatformGetVersionString(void); + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled); +GLFWbool _glfwPlatformRawMouseMotionSupported(void); +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, int xhot, int yhot); +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); + +const char* _glfwPlatformGetScancodeName(int scancode); +int _glfwPlatformGetKeyScancode(int key); + +void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor); +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale); +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height); +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +void _glfwPlatformSetClipboardString(const char* string); +const char* _glfwPlatformGetClipboardString(void); + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); +void _glfwPlatformUpdateGamepadGUID(char* guid); + +uint64_t _glfwPlatformGetTimerValue(void); +uint64_t _glfwPlatformGetTimerFrequency(void); + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwPlatformDestroyWindow(_GLFWwindow* window); +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images); +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight); +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom); +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale); +void _glfwPlatformIconifyWindow(_GLFWwindow* window); +void _glfwPlatformRestoreWindow(_GLFWwindow* window); +void _glfwPlatformMaximizeWindow(_GLFWwindow* window); +void _glfwPlatformShowWindow(_GLFWwindow* window); +void _glfwPlatformHideWindow(_GLFWwindow* window); +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window); +void _glfwPlatformFocusWindow(_GLFWwindow* window); +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, + int xpos, int ypos, int width, int height, + int refreshRate); +int _glfwPlatformWindowFocused(_GLFWwindow* window); +int _glfwPlatformWindowIconified(_GLFWwindow* window); +int _glfwPlatformWindowVisible(_GLFWwindow* window); +int _glfwPlatformWindowMaximized(_GLFWwindow* window); +int _glfwPlatformWindowHovered(_GLFWwindow* window); +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); + +void _glfwPlatformPollEvents(void); +void _glfwPlatformWaitEvents(void); +void _glfwPlatformWaitEventsTimeout(double timeout); +void _glfwPlatformPostEmptyEvent(void); + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily); +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface); + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); +void _glfwPlatformDestroyTls(_GLFWtls* tls); +void* _glfwPlatformGetTls(_GLFWtls* tls); +void _glfwPlatformSetTls(_GLFWtls* tls, void* value); + +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex); +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex); +void _glfwPlatformLockMutex(_GLFWmutex* mutex); +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); +void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); +void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); +void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); +void _glfwInputWindowContentScale(_GLFWwindow* window, + float xscale, float yscale); +void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified); +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized); +void _glfwInputWindowDamage(_GLFWwindow* window); +void _glfwInputWindowCloseRequest(_GLFWwindow* window); +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); + +void _glfwInputKey(_GLFWwindow* window, + int key, int scancode, int action, int mods); +void _glfwInputChar(_GLFWwindow* window, + unsigned int codepoint, int mods, GLFWbool plain); +void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); +void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); +void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); +void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); +void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); +void _glfwInputJoystick(_GLFWjoystick* js, int event); +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); + +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); + +#if defined(__GNUC__) +void _glfwInputError(int code, const char* format, ...) + __attribute__((format(printf, 2, 3))); +#else +void _glfwInputError(int code, const char* format, ...); +#endif + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions); +const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, + const _GLFWfbconfig* alternatives, + unsigned int count); +GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig); +GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); + +const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, + const GLFWvidmode* desired); +int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second); +_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM); +void _glfwFreeMonitor(_GLFWmonitor* monitor); +void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); +void _glfwFreeGammaArrays(GLFWgammaramp* ramp); +void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); + +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount); +void _glfwFreeJoystick(_GLFWjoystick* js); +void _glfwCenterCursorInContentArea(_GLFWwindow* window); + +GLFWbool _glfwInitVulkan(int mode); +void _glfwTerminateVulkan(void); +const char* _glfwGetVulkanResultString(VkResult result); + +char* _glfw_strdup(const char* source); +float _glfw_fminf(float a, float b); +float _glfw_fmaxf(float a, float b); + diff --git a/FCLauncher/src/main/jni/glfw/include/mappings.h b/FCLauncher/src/main/jni/glfw/include/mappings.h new file mode 100644 index 00000000..606824a6 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/mappings.h @@ -0,0 +1,476 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2018 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== + +// All gamepad mappings not labeled GLFW are copied from the +// SDL_GameControllerDB project under the following license: +// +// Simple DirectMedia Layer +// Copyright (C) 1997-2013 Sam Lantinga +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +const char* _glfwDefaultMappings[] = +{ +"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", +"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", +"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", +"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", +"030000008f0e00001200000000000000,Acme,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,", +"030000006b1400000055000000000000,bigben ps3padstreetnew,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,", +"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,", +"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"030000004f04000023b3000000000000,Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", +"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,", +"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000451300000010000000000000,Generic USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00004d00000000000000,HORIPAD3 A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,", +"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,", +"03000000b50700001403000000000000,IMPACT BLACK,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"030000006f0e00002401000000000000,INJUSTICE FightStick for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", +"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,", +"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008433000000000000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008483000000000000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b6,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000008305000031b0000000000000,MaxfireBlaze3,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,", +"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,", +"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", +"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Windows,", +"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,leftx:h0.6,lefty:h0.12,rightshoulder:b5,rightstick:a2,righttrigger:b7,rightx:h0.9,righty:h0.4,start:b9,x:b2,y:b3,platform:Windows,", +"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,", +"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000008f0e00007530000000000000,PS (R) Gamepad,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000100800000100000000000000,PS1 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000100800000300000000000000,PS2 USB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", +"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,", +"03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,", +"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,", +"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", +"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", +"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,", +"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", +"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", +"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"030000006f0e00001e01000000000000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,platform:Windows,", +"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", +"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", +"03000000300f00001101000000000000,saitek rumble pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", +"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", +"030000008f0e00000800000000000000,SpeedLink Strike FX Wireless,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,", +"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", +"030000004f04000015b3000000000000,Thrustmaster Dual Analog 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,", +"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", +"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", +"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", +"03000000380700006652000000000000,UnKnown,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", +"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", +"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", +"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", +"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", +"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,", +"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,", +"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", +"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,", +"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,", +"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", +"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", +"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", +"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,", +"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,", +"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", +"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,", +"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", +"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", +"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", +"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", +"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,", +"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,", +"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", +"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,", +"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,", +"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,", +"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", +"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", +"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,", +"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", +"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", +"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", +"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", +"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,", +"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,", +"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", +"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006f0e00001f01000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,", +"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", +"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,", +"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,", +"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", +"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,", +"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", +"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", +"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,platform:Linux,", +"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,", +"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", +"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", +"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,platform:Linux,", +"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", +"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,", +"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", +"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", +"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", +"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", +"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,", +"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", +"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", +"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", +"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,", +"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", +"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", +"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", +"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux,", +"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", +"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", +"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", +"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", +"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,", +"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", +"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", +"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,", +"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", +"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", +"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", +"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,", +"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,", +"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,", +"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,", +"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,", +"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,", +"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,", +"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,", +"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,", + +"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +NULL +}; + diff --git a/FCLauncher/src/main/jni/glfw/include/null_joystick.h b/FCLauncher/src/main/jni/glfw/include/null_joystick.h new file mode 100644 index 00000000..9ae14d2f --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/null_joystick.h @@ -0,0 +1,9 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#define _GLFW_PLATFORM_JOYSTICK_STATE struct { int dummyJoystick; } +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } + +#define _GLFW_PLATFORM_MAPPING_NAME "" + diff --git a/FCLauncher/src/main/jni/glfw/include/osmesa_context.h b/FCLauncher/src/main/jni/glfw/include/osmesa_context.h new file mode 100644 index 00000000..8c280fa6 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/osmesa_context.h @@ -0,0 +1,73 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#define OSMESA_RGBA 0x1908 +#define OSMESA_FORMAT 0x22 +#define OSMESA_DEPTH_BITS 0x30 +#define OSMESA_STENCIL_BITS 0x31 +#define OSMESA_ACCUM_BITS 0x32 +#define OSMESA_PROFILE 0x33 +#define OSMESA_CORE_PROFILE 0x34 +#define OSMESA_COMPAT_PROFILE 0x35 +#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 +#define OSMESA_CONTEXT_MINOR_VERSION 0x37 + +typedef void* OSMesaContext; +typedef void (*OSMESAproc)(void); + +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); +typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); +typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); +typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); +#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt +#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs +#define OSMesaDestroyContext _glfw.osmesa.DestroyContext +#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent +#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer +#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer +#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress + +#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa +#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa + + +// OSMesa-specific per-context data +// +typedef struct _GLFWcontextOSMesa +{ + OSMesaContext handle; + int width; + int height; + void* buffer; + +} _GLFWcontextOSMesa; + +// OSMesa-specific global data +// +typedef struct _GLFWlibraryOSMesa +{ + void* handle; + + PFN_OSMesaCreateContextExt CreateContextExt; + PFN_OSMesaCreateContextAttribs CreateContextAttribs; + PFN_OSMesaDestroyContext DestroyContext; + PFN_OSMesaMakeCurrent MakeCurrent; + PFN_OSMesaGetColorBuffer GetColorBuffer; + PFN_OSMesaGetDepthBuffer GetDepthBuffer; + PFN_OSMesaGetProcAddress GetProcAddress; + +} _GLFWlibraryOSMesa; + + +GLFWbool _glfwInitOSMesa(void); +void _glfwTerminateOSMesa(void); +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + diff --git a/FCLauncher/src/main/jni/glfw/include/posix_thread.h b/FCLauncher/src/main/jni/glfw/include/posix_thread.h new file mode 100644 index 00000000..035fabdb --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/posix_thread.h @@ -0,0 +1,28 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix +#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix + + +// POSIX-specific thread local storage data +// +typedef struct _GLFWtlsPOSIX +{ + GLFWbool allocated; + pthread_key_t key; + +} _GLFWtlsPOSIX; + +// POSIX-specific mutex data +// +typedef struct _GLFWmutexPOSIX +{ + GLFWbool allocated; + pthread_mutex_t handle; + +} _GLFWmutexPOSIX; + diff --git a/FCLauncher/src/main/jni/glfw/include/posix_time.h b/FCLauncher/src/main/jni/glfw/include/posix_time.h new file mode 100644 index 00000000..504e3aac --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/include/posix_time.h @@ -0,0 +1,22 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix + +#include +#include + + +// POSIX-specific global timer data +// +typedef struct _GLFWtimerPOSIX +{ + GLFWbool monotonic; + uint64_t frequency; + +} _GLFWtimerPOSIX; + + +void _glfwInitTimerPOSIX(void); + diff --git a/FCLauncher/src/main/jni/glfw/init.c b/FCLauncher/src/main/jni/glfw/init.c new file mode 100644 index 00000000..db494b2e --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/init.c @@ -0,0 +1,315 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include +#include + +#include +#include +#include +#include +#include + + +// The global variables below comprise all mutable global data in GLFW +// +// Any other global variable is a bug + +// Global state shared between compilation units of GLFW +// +_GLFWlibrary _glfw = { GLFW_FALSE }; + +// These are outside of _glfw so they can be used before initialization and +// after termination +// +static _GLFWerror _glfwMainThreadError; +static GLFWerrorfun _glfwErrorCallback; +static _GLFWinitconfig _glfwInitHints = +{ + GLFW_TRUE, // hat buttons + { + GLFW_TRUE, // macOS menu bar + GLFW_TRUE // macOS bundle chdir + } +}; + +// Terminate the library +// +static void terminate(void) +{ + int i; + + memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); + + while (_glfw.windowListHead) + glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead); + + while (_glfw.cursorListHead) + glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead); + + for (i = 0; i < _glfw.monitorCount; i++) + { + _GLFWmonitor* monitor = _glfw.monitors[i]; + if (monitor->originalRamp.size) + _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); + _glfwFreeMonitor(monitor); + } + + free(_glfw.monitors); + _glfw.monitors = NULL; + _glfw.monitorCount = 0; + + free(_glfw.mappings); + _glfw.mappings = NULL; + _glfw.mappingCount = 0; + + _glfwTerminateVulkan(); + _glfwPlatformTerminate(); + + _glfw.initialized = GLFW_FALSE; + + while (_glfw.errorListHead) + { + _GLFWerror* error = _glfw.errorListHead; + _glfw.errorListHead = error->next; + free(error); + } + + _glfwPlatformDestroyTls(&_glfw.contextSlot); + _glfwPlatformDestroyTls(&_glfw.errorSlot); + _glfwPlatformDestroyMutex(&_glfw.errorLock); + + memset(&_glfw, 0, sizeof(_glfw)); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +char* _glfw_strdup(const char* source) +{ + const size_t length = strlen(source); + char* result = calloc(length + 1, 1); + strcpy(result, source); + return result; +} + +float _glfw_fminf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a < b) + return a; + else + return b; +} + +float _glfw_fmaxf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a > b) + return a; + else + return b; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code of an error +// +void _glfwInputError(int code, const char* format, ...) +{ + _GLFWerror* error; + char description[_GLFW_MESSAGE_SIZE]; + + if (format) + { + va_list vl; + + va_start(vl, format); + vsnprintf(description, sizeof(description), format, vl); + va_end(vl); + + description[sizeof(description) - 1] = '\0'; + } + else + { + if (code == GLFW_NOT_INITIALIZED) + strcpy(description, "The GLFW library is not initialized"); + else if (code == GLFW_NO_CURRENT_CONTEXT) + strcpy(description, "There is no current context"); + else if (code == GLFW_INVALID_ENUM) + strcpy(description, "Invalid argument for enum parameter"); + else if (code == GLFW_INVALID_VALUE) + strcpy(description, "Invalid value for parameter"); + else if (code == GLFW_OUT_OF_MEMORY) + strcpy(description, "Out of memory"); + else if (code == GLFW_API_UNAVAILABLE) + strcpy(description, "The requested API is unavailable"); + else if (code == GLFW_VERSION_UNAVAILABLE) + strcpy(description, "The requested API version is unavailable"); + else if (code == GLFW_PLATFORM_ERROR) + strcpy(description, "A platform-specific error occurred"); + else if (code == GLFW_FORMAT_UNAVAILABLE) + strcpy(description, "The requested format is unavailable"); + else if (code == GLFW_NO_WINDOW_CONTEXT) + strcpy(description, "The specified window has no context"); + else + strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); + } + + if (_glfw.initialized) + { + error = _glfwPlatformGetTls(&_glfw.errorSlot); + if (!error) + { + error = calloc(1, sizeof(_GLFWerror)); + _glfwPlatformSetTls(&_glfw.errorSlot, error); + _glfwPlatformLockMutex(&_glfw.errorLock); + error->next = _glfw.errorListHead; + _glfw.errorListHead = error; + _glfwPlatformUnlockMutex(&_glfw.errorLock); + } + } + else + error = &_glfwMainThreadError; + + error->code = code; + strcpy(error->description, description); + + if (_glfwErrorCallback) + _glfwErrorCallback(code, description); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwInit(void) +{ + if (_glfw.initialized) + return GLFW_TRUE; + + memset(&_glfw, 0, sizeof(_glfw)); + _glfw.hints.init = _glfwInitHints; + + if (!_glfwPlatformInit()) + { + terminate(); + return GLFW_FALSE; + } + + if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || + !_glfwPlatformCreateTls(&_glfw.errorSlot) || + !_glfwPlatformCreateTls(&_glfw.contextSlot)) + { + terminate(); + return GLFW_FALSE; + } + + _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); + + _glfw.initialized = GLFW_TRUE; + _glfw.timer.offset = _glfwPlatformGetTimerValue(); + + glfwDefaultWindowHints(); + + { + int i; + + for (i = 0; _glfwDefaultMappings[i]; i++) + { + if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i])) + { + terminate(); + return GLFW_FALSE; + } + } + } + + return GLFW_TRUE; +} + +GLFWAPI void glfwTerminate(void) +{ + if (!_glfw.initialized) + return; + + terminate(); +} + +GLFWAPI void glfwInitHint(int hint, int value) +{ + switch (hint) + { + case GLFW_JOYSTICK_HAT_BUTTONS: + _glfwInitHints.hatButtons = value; + return; + case GLFW_COCOA_CHDIR_RESOURCES: + _glfwInitHints.ns.chdir = value; + return; + case GLFW_COCOA_MENUBAR: + _glfwInitHints.ns.menubar = value; + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid init hint 0x%08X", hint); +} + +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) +{ + if (major != NULL) + *major = GLFW_VERSION_MAJOR; + if (minor != NULL) + *minor = GLFW_VERSION_MINOR; + if (rev != NULL) + *rev = GLFW_VERSION_REVISION; +} + +GLFWAPI const char* glfwGetVersionString(void) +{ + return _glfwPlatformGetVersionString(); +} + +GLFWAPI int glfwGetError(const char** description) +{ + _GLFWerror* error; + int code = GLFW_NO_ERROR; + + if (description) + *description = NULL; + + if (_glfw.initialized) + error = _glfwPlatformGetTls(&_glfw.errorSlot); + else + error = &_glfwMainThreadError; + + if (error) + { + code = error->code; + error->code = GLFW_NO_ERROR; + if (description && code) + *description = error->description; + } + + return code; +} + +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) +{ + _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); + return cbfun; +} + diff --git a/FCLauncher/src/main/jni/glfw/input.c b/FCLauncher/src/main/jni/glfw/input.c new file mode 100644 index 00000000..334c697a --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/input.c @@ -0,0 +1,1328 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include +#include +#include +#include + +// Internal key state used for sticky keys +#define _GLFW_STICK 3 + +// Internal constants for gamepad mapping source types +#define _GLFW_JOYSTICK_AXIS 1 +#define _GLFW_JOYSTICK_BUTTON 2 +#define _GLFW_JOYSTICK_HATBIT 3 + +// Finds a mapping based on joystick GUID +// +static _GLFWmapping* findMapping(const char* guid) +{ + int i; + + for (i = 0; i < _glfw.mappingCount; i++) + { + if (strcmp(_glfw.mappings[i].guid, guid) == 0) + return _glfw.mappings + i; + } + + return NULL; +} + +// Checks whether a gamepad mapping element is present in the hardware +// +static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e, + const _GLFWjoystick* js) +{ + if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount) + return GLFW_FALSE; + else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount) + return GLFW_FALSE; + else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +// Finds a mapping based on joystick GUID and verifies element indices +// +static _GLFWmapping* findValidMapping(const _GLFWjoystick* js) +{ + _GLFWmapping* mapping = findMapping(js->guid); + if (mapping) + { + int i; + + for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) + { + if (!isValidElementForJoystick(mapping->buttons + i, js)) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid button in gamepad mapping %s (%s)", + mapping->guid, + mapping->name); + return NULL; + } + } + + for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) + { + if (!isValidElementForJoystick(mapping->axes + i, js)) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid axis in gamepad mapping %s (%s)", + mapping->guid, + mapping->name); + return NULL; + } + } + } + + return mapping; +} + +// Parses an SDL_GameControllerDB line and adds it to the mapping list +// +static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) +{ + const char* c = string; + size_t i, length; + struct + { + const char* name; + _GLFWmapelement* element; + } fields[] = + { + { "platform", NULL }, + { "a", mapping->buttons + GLFW_GAMEPAD_BUTTON_A }, + { "b", mapping->buttons + GLFW_GAMEPAD_BUTTON_B }, + { "x", mapping->buttons + GLFW_GAMEPAD_BUTTON_X }, + { "y", mapping->buttons + GLFW_GAMEPAD_BUTTON_Y }, + { "back", mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK }, + { "start", mapping->buttons + GLFW_GAMEPAD_BUTTON_START }, + { "guide", mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE }, + { "leftshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER }, + { "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER }, + { "leftstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB }, + { "rightstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB }, + { "dpup", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP }, + { "dpright", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT }, + { "dpdown", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN }, + { "dpleft", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT }, + { "lefttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER }, + { "righttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER }, + { "leftx", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X }, + { "lefty", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y }, + { "rightx", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X }, + { "righty", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y } + }; + + length = strcspn(c, ","); + if (length != 32 || c[length] != ',') + { + _glfwInputError(GLFW_INVALID_VALUE, NULL); + return GLFW_FALSE; + } + + memcpy(mapping->guid, c, length); + c += length + 1; + + length = strcspn(c, ","); + if (length >= sizeof(mapping->name) || c[length] != ',') + { + _glfwInputError(GLFW_INVALID_VALUE, NULL); + return GLFW_FALSE; + } + + memcpy(mapping->name, c, length); + c += length + 1; + + while (*c) + { + if (*c == '+' || *c == '-') + return GLFW_FALSE; + + for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) + { + length = strlen(fields[i].name); + if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':') + continue; + + c += length + 1; + + if (fields[i].element) + { + _GLFWmapelement* e = fields[i].element; + int8_t minimum = -1; + int8_t maximum = 1; + + if (*c == '+') + { + minimum = 0; + c += 1; + } + else if (*c == '-') + { + maximum = 0; + c += 1; + } + + if (*c == 'a') + e->type = _GLFW_JOYSTICK_AXIS; + else if (*c == 'b') + e->type = _GLFW_JOYSTICK_BUTTON; + else if (*c == 'h') + e->type = _GLFW_JOYSTICK_HATBIT; + else + break; + + if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned long hat = strtoul(c + 1, (char**) &c, 10); + const unsigned long bit = strtoul(c + 1, (char**) &c, 10); + e->index = (uint8_t) ((hat << 4) | bit); + } + else + e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10); + + if (e->type == _GLFW_JOYSTICK_AXIS) + { + e->axisScale = 2 / (maximum - minimum); + e->axisOffset = -(maximum + minimum); + + if (*c == '~') + { + e->axisScale = -e->axisScale; + e->axisOffset = -e->axisOffset; + } + } + } + else + { + length = strlen(_GLFW_PLATFORM_MAPPING_NAME); + if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0) + return GLFW_FALSE; + } + + break; + } + + c += strcspn(c, ","); + c += strspn(c, ","); + } + + for (i = 0; i < 32; i++) + { + if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F') + mapping->guid[i] += 'a' - 'A'; + } + + _glfwPlatformUpdateGamepadGUID(mapping->guid); + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code of a physical key event +// +void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key >= 0 && key <= GLFW_KEY_LAST) + { + GLFWbool repeated = GLFW_FALSE; + + if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE) + return; + + if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS) + repeated = GLFW_TRUE; + + if (action == GLFW_RELEASE && window->stickyKeys) + window->keys[key] = _GLFW_STICK; + else + window->keys[key] = (char) action; + + if (repeated) + action = GLFW_REPEAT; + } + + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + + if (window->callbacks.key) + window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); +} + +// Notifies shared code of a Unicode codepoint input event +// The 'plain' parameter determines whether to emit a regular character event +// +void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain) +{ + if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) + return; + + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + + if (window->callbacks.charmods) + window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); + + if (plain) + { + if (window->callbacks.character) + window->callbacks.character((GLFWwindow*) window, codepoint); + } +} + +// Notifies shared code of a scroll event +// +void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) +{ + if (window->callbacks.scroll) + window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset); +} + +// Notifies shared code of a mouse button click event +// +void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) +{ + if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) + return; + + if (!window->lockKeyMods) + mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); + + if (action == GLFW_RELEASE && window->stickyMouseButtons) + window->mouseButtons[button] = _GLFW_STICK; + else + window->mouseButtons[button] = (char) action; + + if (window->callbacks.mouseButton) + window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods); +} + +// Notifies shared code of a cursor motion event +// The position is specified in content area relative screen coordinates +// +void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) +{ + if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos) + return; + + window->virtualCursorPosX = xpos; + window->virtualCursorPosY = ypos; + + if (window->callbacks.cursorPos) + window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos); +} + +// Notifies shared code of a cursor enter/leave event +// +void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) +{ + if (window->callbacks.cursorEnter) + window->callbacks.cursorEnter((GLFWwindow*) window, entered); +} + +// Notifies shared code of files or directories dropped on a window +// +void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) +{ + if (window->callbacks.drop) + window->callbacks.drop((GLFWwindow*) window, count, paths); +} + +// Notifies shared code of a joystick connection or disconnection +// +void _glfwInputJoystick(_GLFWjoystick* js, int event) +{ + const int jid = (int) (js - _glfw.joysticks); + + if (_glfw.callbacks.joystick) + _glfw.callbacks.joystick(jid, event); +} + +// Notifies shared code of the new value of a joystick axis +// +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) +{ + js->axes[axis] = value; +} + +// Notifies shared code of the new value of a joystick button +// +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) +{ + js->buttons[button] = value; +} + +// Notifies shared code of the new value of a joystick hat +// +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) +{ + const int base = js->buttonCount + hat * 4; + + js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; + + js->hats[hat] = value; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Returns an available joystick object with arrays and name allocated +// +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount) +{ + int jid; + _GLFWjoystick* js; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.joysticks[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return NULL; + + js = _glfw.joysticks + jid; + js->present = GLFW_TRUE; + js->axes = calloc(axisCount, sizeof(float)); + js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1); + js->hats = calloc(hatCount, 1); + js->axisCount = axisCount; + js->buttonCount = buttonCount; + js->hatCount = hatCount; + + strncpy(js->name, name, sizeof(js->name) - 1); + strncpy(js->guid, guid, sizeof(js->guid) - 1); + js->mapping = findValidMapping(js); + + return js; +} + +// Frees arrays and name and flags the joystick object as unused +// +void _glfwFreeJoystick(_GLFWjoystick* js) +{ + free(js->axes); + free(js->buttons); + free(js->hats); + memset(js, 0, sizeof(_GLFWjoystick)); +} + +// Center the cursor in the content area of the specified window +// +void _glfwCenterCursorInContentArea(_GLFWwindow* window) +{ + int width, height; + + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + switch (mode) + { + case GLFW_CURSOR: + return window->cursorMode; + case GLFW_STICKY_KEYS: + return window->stickyKeys; + case GLFW_STICKY_MOUSE_BUTTONS: + return window->stickyMouseButtons; + case GLFW_LOCK_KEY_MODS: + return window->lockKeyMods; + case GLFW_RAW_MOUSE_MOTION: + return window->rawMouseMotion; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); + return 0; +} + +GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (mode == GLFW_CURSOR) + { + if (value != GLFW_CURSOR_NORMAL && + value != GLFW_CURSOR_HIDDEN && + value != GLFW_CURSOR_DISABLED) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid cursor mode 0x%08X", + value); + return; + } + + if (window->cursorMode == value) + return; + + window->cursorMode = value; + + _glfwPlatformGetCursorPos(window, + &window->virtualCursorPosX, + &window->virtualCursorPosY); + _glfwPlatformSetCursorMode(window, value); + } + else if (mode == GLFW_STICKY_KEYS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyKeys == value) + return; + + if (!value) + { + int i; + + // Release all sticky keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == _GLFW_STICK) + window->keys[i] = GLFW_RELEASE; + } + } + + window->stickyKeys = value; + } + else if (mode == GLFW_STICKY_MOUSE_BUTTONS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyMouseButtons == value) + return; + + if (!value) + { + int i; + + // Release all sticky mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == _GLFW_STICK) + window->mouseButtons[i] = GLFW_RELEASE; + } + } + + window->stickyMouseButtons = value; + } + else if (mode == GLFW_LOCK_KEY_MODS) + { + window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE; + } + else if (mode == GLFW_RAW_MOUSE_MOTION) + { + if (!_glfwPlatformRawMouseMotionSupported()) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Raw mouse motion is not supported on this system"); + return; + } + + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->rawMouseMotion == value) + return; + + window->rawMouseMotion = value; + _glfwPlatformSetRawMouseMotion(window, value); + } + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); +} + +GLFWAPI int glfwRawMouseMotionSupported(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + return _glfwPlatformRawMouseMotionSupported(); +} + +GLFWAPI const char* glfwGetKeyName(int key, int scancode) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (key != GLFW_KEY_UNKNOWN) + { + if (key != GLFW_KEY_KP_EQUAL && + (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) && + (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2)) + { + return NULL; + } + + scancode = _glfwPlatformGetKeyScancode(key); + } + + return _glfwPlatformGetScancodeName(scancode); +} + +GLFWAPI int glfwGetKeyScancode(int key) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(-1); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + return _glfwPlatformGetKeyScancode(key); +} + +GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + if (window->keys[key] == _GLFW_STICK) + { + // Sticky mode: release key now + window->keys[key] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) window->keys[key]; +} + +GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); + + if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button); + return GLFW_RELEASE; + } + + if (window->mouseButtons[button] == _GLFW_STICK) + { + // Sticky mode: release mouse button now + window->mouseButtons[button] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) window->mouseButtons[button]; +} + +GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (xpos) + *xpos = window->virtualCursorPosX; + if (ypos) + *ypos = window->virtualCursorPosY; + } + else + _glfwPlatformGetCursorPos(window, xpos, ypos); +} + +GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX || + ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid cursor position %f %f", + xpos, ypos); + return; + } + + if (!_glfwPlatformWindowFocused(window)) + return; + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + // Only update the accumulated position if the cursor is disabled + window->virtualCursorPosX = xpos; + window->virtualCursorPosY = ypos; + } + else + { + // Update system cursor position + _glfwPlatformSetCursorPos(window, xpos, ypos); + } +} + +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) +{ + _GLFWcursor* cursor; + + assert(image != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + cursor = calloc(1, sizeof(_GLFWcursor)); + cursor->next = _glfw.cursorListHead; + _glfw.cursorListHead = cursor; + + if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot)) + { + glfwDestroyCursor((GLFWcursor*) cursor); + return NULL; + } + + return (GLFWcursor*) cursor; +} + +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) +{ + _GLFWcursor* cursor; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (shape != GLFW_ARROW_CURSOR && + shape != GLFW_IBEAM_CURSOR && + shape != GLFW_CROSSHAIR_CURSOR && + shape != GLFW_HAND_CURSOR && + shape != GLFW_HRESIZE_CURSOR && + shape != GLFW_VRESIZE_CURSOR) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape); + return NULL; + } + + cursor = calloc(1, sizeof(_GLFWcursor)); + cursor->next = _glfw.cursorListHead; + _glfw.cursorListHead = cursor; + + if (!_glfwPlatformCreateStandardCursor(cursor, shape)) + { + glfwDestroyCursor((GLFWcursor*) cursor); + return NULL; + } + + return (GLFWcursor*) cursor; +} + +GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) +{ + _GLFWcursor* cursor = (_GLFWcursor*) handle; + + _GLFW_REQUIRE_INIT(); + + if (cursor == NULL) + return; + + // Make sure the cursor is not being used by any window + { + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->cursor == cursor) + glfwSetCursor((GLFWwindow*) window, NULL); + } + } + + _glfwPlatformDestroyCursor(cursor); + + // Unlink cursor from global linked list + { + _GLFWcursor** prev = &_glfw.cursorListHead; + + while (*prev != cursor) + prev = &((*prev)->next); + + *prev = cursor->next; + } + + free(cursor); +} + +GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) +{ + _GLFWwindow* window = (_GLFWwindow*) windowHandle; + _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + window->cursor = cursor; + + _glfwPlatformSetCursor(window, cursor); +} + +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun); + return cbfun; +} + +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun); + return cbfun; +} + +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); + return cbfun; +} + +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, + GLFWmousebuttonfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun); + return cbfun; +} + +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, + GLFWcursorposfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun); + return cbfun; +} + +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, + GLFWcursorenterfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun); + return cbfun; +} + +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, + GLFWscrollfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun); + return cbfun; +} + +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun); + return cbfun; +} + +GLFWAPI int glfwJoystickPresent(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); +} + +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES)) + return NULL; + + *count = js->axisCount; + return js->axes; +} + +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + return NULL; + + if (_glfw.hints.init.hatButtons) + *count = js->buttonCount + js->hatCount * 4; + else + *count = js->buttonCount; + + return js->buttons; +} + +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + return NULL; + + *count = js->hatCount; + return js->hats; +} + +GLFWAPI const char* glfwGetJoystickName(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + return js->name; +} + +GLFWAPI const char* glfwGetJoystickGUID(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + return js->guid; +} + +GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT(); + + js = _glfw.joysticks + jid; + if (!js->present) + return; + + js->userPointer = pointer; +} + +GLFWAPI void* glfwGetJoystickUserPointer(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + return js->userPointer; +} + +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); + return cbfun; +} + +GLFWAPI int glfwUpdateGamepadMappings(const char* string) +{ + int jid; + const char* c = string; + + assert(string != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + while (*c) + { + if ((*c >= '0' && *c <= '9') || + (*c >= 'a' && *c <= 'f') || + (*c >= 'A' && *c <= 'F')) + { + char line[1024]; + + const size_t length = strcspn(c, "\r\n"); + if (length < sizeof(line)) + { + _GLFWmapping mapping = {{0}}; + + memcpy(line, c, length); + line[length] = '\0'; + + if (parseMapping(&mapping, line)) + { + _GLFWmapping* previous = findMapping(mapping.guid); + if (previous) + *previous = mapping; + else + { + _glfw.mappingCount++; + _glfw.mappings = + realloc(_glfw.mappings, + sizeof(_GLFWmapping) * _glfw.mappingCount); + _glfw.mappings[_glfw.mappingCount - 1] = mapping; + } + } + } + + c += length; + } + else + { + c += strcspn(c, "\r\n"); + c += strspn(c, "\r\n"); + } + } + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + js->mapping = findValidMapping(js); + } + + return GLFW_TRUE; +} + +GLFWAPI int glfwJoystickIsGamepad(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return GLFW_FALSE; + + return js->mapping != NULL; +} + +GLFWAPI const char* glfwGetGamepadName(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + if (!js->mapping) + return NULL; + + return js->mapping->name; +} + +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) +{ + int i; + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(state != NULL); + + memset(state, 0, sizeof(GLFWgamepadstate)); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL)) + return GLFW_FALSE; + + if (!js->mapping) + return GLFW_FALSE; + + for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) + { + const _GLFWmapelement* e = js->mapping->buttons + i; + if (e->type == _GLFW_JOYSTICK_AXIS) + { + const float value = js->axes[e->index] * e->axisScale + e->axisOffset; + // HACK: This should be baked into the value transform + if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0)) + { + if (value >= 0.f) + state->buttons[i] = GLFW_PRESS; + } + else + { + if (value <= 0.f) + state->buttons[i] = GLFW_PRESS; + } + } + else if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = e->index >> 4; + const unsigned int bit = e->index & 0xf; + if (js->hats[hat] & bit) + state->buttons[i] = GLFW_PRESS; + } + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->buttons[i] = js->buttons[e->index]; + } + + for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) + { + const _GLFWmapelement* e = js->mapping->axes + i; + if (e->type == _GLFW_JOYSTICK_AXIS) + { + const float value = js->axes[e->index] * e->axisScale + e->axisOffset; + state->axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.f), 1.f); + } + else if (e->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = e->index >> 4; + const unsigned int bit = e->index & 0xf; + if (js->hats[hat] & bit) + state->axes[i] = 1.f; + else + state->axes[i] = -1.f; + } + else if (e->type == _GLFW_JOYSTICK_BUTTON) + state->axes[i] = js->buttons[e->index] * 2.f - 1.f; + } + + return GLFW_TRUE; +} + +GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) +{ + assert(string != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetClipboardString(string); +} + +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetClipboardString(); +} + +GLFWAPI double glfwGetTime(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0.0); + return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) / + _glfwPlatformGetTimerFrequency(); +} + +GLFWAPI void glfwSetTime(double time) +{ + _GLFW_REQUIRE_INIT(); + + if (time != time || time < 0.0 || time > 18446744073.0) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time); + return; + } + + _glfw.timer.offset = _glfwPlatformGetTimerValue() - + (uint64_t) (time * _glfwPlatformGetTimerFrequency()); +} + +GLFWAPI uint64_t glfwGetTimerValue(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerValue(); +} + +GLFWAPI uint64_t glfwGetTimerFrequency(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerFrequency(); +} diff --git a/FCLauncher/src/main/jni/glfw/monitor.c b/FCLauncher/src/main/jni/glfw/monitor.c new file mode 100644 index 00000000..4174795d --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/monitor.c @@ -0,0 +1,517 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include +#include +#include +#include +#include + + +// Lexically compare video modes, used by qsort +// +static int compareVideoModes(const void* fp, const void* sp) +{ + const GLFWvidmode* fm = fp; + const GLFWvidmode* sm = sp; + const int fbpp = fm->redBits + fm->greenBits + fm->blueBits; + const int sbpp = sm->redBits + sm->greenBits + sm->blueBits; + const int farea = fm->width * fm->height; + const int sarea = sm->width * sm->height; + + // First sort on color bits per pixel + if (fbpp != sbpp) + return fbpp - sbpp; + + // Then sort on screen area + if (farea != sarea) + return farea - sarea; + + // Then sort on width + if (fm->width != sm->width) + return fm->width - sm->width; + + // Lastly sort on refresh rate + return fm->refreshRate - sm->refreshRate; +} + +// Retrieves the available modes for the specified monitor +// +static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) +{ + int modeCount; + GLFWvidmode* modes; + + if (monitor->modes) + return GLFW_TRUE; + + modes = _glfwPlatformGetVideoModes(monitor, &modeCount); + if (!modes) + return GLFW_FALSE; + + qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); + + free(monitor->modes); + monitor->modes = modes; + monitor->modeCount = modeCount; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code of a monitor connection or disconnection +// +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) +{ + if (action == GLFW_CONNECTED) + { + _glfw.monitorCount++; + _glfw.monitors = + realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); + + if (placement == _GLFW_INSERT_FIRST) + { + memmove(_glfw.monitors + 1, + _glfw.monitors, + ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); + _glfw.monitors[0] = monitor; + } + else + _glfw.monitors[_glfw.monitorCount - 1] = monitor; + } + else if (action == GLFW_DISCONNECTED) + { + int i; + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->monitor == monitor) + { + int width, height, xoff, yoff; + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); + _glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL); + _glfwPlatformSetWindowPos(window, xoff, yoff); + } + } + + for (i = 0; i < _glfw.monitorCount; i++) + { + if (_glfw.monitors[i] == monitor) + { + _glfw.monitorCount--; + memmove(_glfw.monitors + i, + _glfw.monitors + i + 1, + ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); + break; + } + } + } + + if (_glfw.callbacks.monitor) + _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); + + if (action == GLFW_DISCONNECTED) + _glfwFreeMonitor(monitor); +} + +// Notifies shared code that a full screen window has acquired or released +// a monitor +// +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) +{ + monitor->window = window; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Allocates and returns a monitor object with the specified name and dimensions +// +_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) +{ + _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); + monitor->widthMM = widthMM; + monitor->heightMM = heightMM; + + strncpy(monitor->name, name, sizeof(monitor->name) - 1); + + return monitor; +} + +// Frees a monitor object and any data associated with it +// +void _glfwFreeMonitor(_GLFWmonitor* monitor) +{ + if (monitor == NULL) + return; + + _glfwPlatformFreeMonitor(monitor); + + _glfwFreeGammaArrays(&monitor->originalRamp); + _glfwFreeGammaArrays(&monitor->currentRamp); + + free(monitor->modes); + free(monitor); +} + +// Allocates red, green and blue value arrays of the specified size +// +void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) +{ + ramp->red = calloc(size, sizeof(unsigned short)); + ramp->green = calloc(size, sizeof(unsigned short)); + ramp->blue = calloc(size, sizeof(unsigned short)); + ramp->size = size; +} + +// Frees the red, green and blue value arrays and clears the struct +// +void _glfwFreeGammaArrays(GLFWgammaramp* ramp) +{ + free(ramp->red); + free(ramp->green); + free(ramp->blue); + + memset(ramp, 0, sizeof(GLFWgammaramp)); +} + +// Chooses the video mode most closely matching the desired one +// +const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, + const GLFWvidmode* desired) +{ + int i; + unsigned int sizeDiff, leastSizeDiff = UINT_MAX; + unsigned int rateDiff, leastRateDiff = UINT_MAX; + unsigned int colorDiff, leastColorDiff = UINT_MAX; + const GLFWvidmode* current; + const GLFWvidmode* closest = NULL; + + if (!refreshVideoModes(monitor)) + return NULL; + + for (i = 0; i < monitor->modeCount; i++) + { + current = monitor->modes + i; + + colorDiff = 0; + + if (desired->redBits != GLFW_DONT_CARE) + colorDiff += abs(current->redBits - desired->redBits); + if (desired->greenBits != GLFW_DONT_CARE) + colorDiff += abs(current->greenBits - desired->greenBits); + if (desired->blueBits != GLFW_DONT_CARE) + colorDiff += abs(current->blueBits - desired->blueBits); + + sizeDiff = abs((current->width - desired->width) * + (current->width - desired->width) + + (current->height - desired->height) * + (current->height - desired->height)); + + if (desired->refreshRate != GLFW_DONT_CARE) + rateDiff = abs(current->refreshRate - desired->refreshRate); + else + rateDiff = UINT_MAX - current->refreshRate; + + if ((colorDiff < leastColorDiff) || + (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) || + (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff)) + { + closest = current; + leastSizeDiff = sizeDiff; + leastRateDiff = rateDiff; + leastColorDiff = colorDiff; + } + } + + return closest; +} + +// Performs lexical comparison between two @ref GLFWvidmode structures +// +int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm) +{ + return compareVideoModes(fm, sm); +} + +// Splits a color depth into red, green and blue bit depths +// +void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) +{ + int delta; + + // We assume that by 32 the user really meant 24 + if (bpp == 32) + bpp = 24; + + // Convert "bits per pixel" to red, green & blue sizes + + *red = *green = *blue = bpp / 3; + delta = bpp - (*red * 3); + if (delta >= 1) + *green = *green + 1; + + if (delta == 2) + *red = *red + 1; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) +{ + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + *count = _glfw.monitorCount; + return (GLFWmonitor**) _glfw.monitors; +} + +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfw.monitorCount) + return NULL; + + return (GLFWmonitor*) _glfw.monitors[0]; +} + +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformGetMonitorPos(monitor, xpos, ypos); +} + +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, + int* xpos, int* ypos, + int* width, int* height) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height); +} + +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (widthMM) + *widthMM = 0; + if (heightMM) + *heightMM = 0; + + _GLFW_REQUIRE_INIT(); + + if (widthMM) + *widthMM = monitor->widthMM; + if (heightMM) + *heightMM = monitor->heightMM; +} + +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, + float* xscale, float* yscale) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); +} + +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->name; +} + +GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT(); + monitor->userPointer = pointer; +} + +GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->userPointer; +} + +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun); + return cbfun; +} + +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!refreshVideoModes(monitor)) + return NULL; + + *count = monitor->modeCount; + return monitor->modes; +} + +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + _glfwPlatformGetVideoMode(monitor, &monitor->currentMode); + return &monitor->currentMode; +} + +GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) +{ + unsigned int i; + unsigned short* values; + GLFWgammaramp ramp; + const GLFWgammaramp* original; + assert(handle != NULL); + assert(gamma > 0.f); + assert(gamma <= FLT_MAX); + + _GLFW_REQUIRE_INIT(); + + if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma); + return; + } + + original = glfwGetGammaRamp(handle); + if (!original) + return; + + values = calloc(original->size, sizeof(unsigned short)); + + for (i = 0; i < original->size; i++) + { + float value; + + // Calculate intensity + value = i / (float) (original->size - 1); + // Apply gamma curve + value = powf(value, 1.f / gamma) * 65535.f + 0.5f; + // Clamp to value range + value = _glfw_fminf(value, 65535.f); + + values[i] = (unsigned short) value; + } + + ramp.red = values; + ramp.green = values; + ramp.blue = values; + ramp.size = original->size; + + glfwSetGammaRamp(handle, &ramp); + free(values); +} + +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + _glfwFreeGammaArrays(&monitor->currentRamp); + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp)) + return NULL; + + return &monitor->currentRamp; +} + +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + assert(ramp != NULL); + assert(ramp->size > 0); + assert(ramp->red != NULL); + assert(ramp->green != NULL); + assert(ramp->blue != NULL); + + if (ramp->size <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid gamma ramp size %i", + ramp->size); + return; + } + + _GLFW_REQUIRE_INIT(); + + if (!monitor->originalRamp.size) + { + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp)) + return; + } + + _glfwPlatformSetGammaRamp(monitor, ramp); +} + diff --git a/FCLauncher/src/main/jni/glfw/null_joystick.c b/FCLauncher/src/main/jni/glfw/null_joystick.c new file mode 100644 index 00000000..608ccc48 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/null_joystick.c @@ -0,0 +1,19 @@ +// +// Created by Tungsten on 2022/10/11. +// + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +#include + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +{ + return GLFW_FALSE; +} + +void _glfwPlatformUpdateGamepadGUID(char* guid) +{ +} + diff --git a/FCLauncher/src/main/jni/glfw/osmesa_context.c b/FCLauncher/src/main/jni/glfw/osmesa_context.c new file mode 100644 index 00000000..4a2893a7 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/osmesa_context.c @@ -0,0 +1,346 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include +#include +#include + +#include + +static void makeContextCurrentOSMesa(_GLFWwindow* window) +{ + if (window) + { + int width, height; + _glfwPlatformGetFramebufferSize(window, &width, &height); + + // Check to see if we need to allocate a new buffer + if ((window->context.osmesa.buffer == NULL) || + (width != window->context.osmesa.width) || + (height != window->context.osmesa.height)) + { + free(window->context.osmesa.buffer); + + // Allocate the new buffer (width * height * 8-bit RGBA) + window->context.osmesa.buffer = calloc(4, (size_t) width * height); + window->context.osmesa.width = width; + window->context.osmesa.height = height; + } + + if (!OSMesaMakeCurrent(window->context.osmesa.handle, + window->context.osmesa.buffer, + GL_UNSIGNED_BYTE, + width, height)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to make context current"); + return; + } + } + + _glfwPlatformSetTls(&_glfw.contextSlot, window); +} + +static GLFWglproc getProcAddressOSMesa(const char* procname) +{ + return (GLFWglproc) OSMesaGetProcAddress(procname); +} + +static void destroyContextOSMesa(_GLFWwindow* window) +{ + if (window->context.osmesa.handle) + { + OSMesaDestroyContext(window->context.osmesa.handle); + window->context.osmesa.handle = NULL; + } + + if (window->context.osmesa.buffer) + { + free(window->context.osmesa.buffer); + window->context.osmesa.width = 0; + window->context.osmesa.height = 0; + } +} + +static void swapBuffersOSMesa(_GLFWwindow* window) +{ + // No double buffering on OSMesa +} + +static void swapIntervalOSMesa(int interval) +{ + // No swap interval on OSMesa +} + +static int extensionSupportedOSMesa(const char* extension) +{ + // OSMesa does not have extensions + return GLFW_FALSE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitOSMesa(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_OSMESA_LIBRARY) + _GLFW_OSMESA_LIBRARY, +#elif defined(_WIN32) + "libOSMesa.dll", + "OSMesa.dll", +#elif defined(__APPLE__) + "libOSMesa.8.dylib", +#elif defined(__CYGWIN__) + "libOSMesa-8.so", +#else + "libOSMesa.so.8", + "libOSMesa.so.6", +#endif + NULL + }; + + if (_glfw.osmesa.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); + if (_glfw.osmesa.handle) + break; + } + + if (!_glfw.osmesa.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found"); + return GLFW_FALSE; + } + + _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); + _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); + _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); + _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); + _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); + _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); + _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); + + if (!_glfw.osmesa.CreateContextExt || + !_glfw.osmesa.DestroyContext || + !_glfw.osmesa.MakeCurrent || + !_glfw.osmesa.GetColorBuffer || + !_glfw.osmesa.GetDepthBuffer || + !_glfw.osmesa.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to load required entry points"); + + _glfwTerminateOSMesa(); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwTerminateOSMesa(void) +{ + if (_glfw.osmesa.handle) + { + _glfw_dlclose(_glfw.osmesa.handle); + _glfw.osmesa.handle = NULL; + } +} + +#define setAttrib(a, v) \ +{ \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + OSMesaContext share = NULL; + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "OSMesa: OpenGL ES is not available on OSMesa"); + return GLFW_FALSE; + } + + if (ctxconfig->share) + share = ctxconfig->share->context.osmesa.handle; + + if (OSMesaCreateContextAttribs) + { + int index = 0, attribs[40]; + + setAttrib(OSMESA_FORMAT, OSMESA_RGBA); + setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); + setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + setAttrib(OSMESA_ACCUM_BITS, accumBits); + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + } + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + } + + if (ctxconfig->forward) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Forward-compatible contexts not supported"); + return GLFW_FALSE; + } + + setAttrib(0, 0); + + window->context.osmesa.handle = + OSMesaCreateContextAttribs(attribs, share); + } + else + { + if (ctxconfig->profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: OpenGL profiles unavailable"); + return GLFW_FALSE; + } + + window->context.osmesa.handle = + OSMesaCreateContextExt(OSMESA_RGBA, + fbconfig->depthBits, + fbconfig->stencilBits, + accumBits, + share); + } + + if (window->context.osmesa.handle == NULL) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Failed to create context"); + return GLFW_FALSE; + } + + window->context.makeCurrent = makeContextCurrentOSMesa; + window->context.swapBuffers = swapBuffersOSMesa; + window->context.swapInterval = swapIntervalOSMesa; + window->context.extensionSupported = extensionSupportedOSMesa; + window->context.getProcAddress = getProcAddressOSMesa; + window->context.destroy = destroyContextOSMesa; + + return GLFW_TRUE; +} + +#undef setAttrib + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, + int* height, int* format, void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaFormat; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetColorBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaFormat, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve color buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (format) + *format = mesaFormat; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, + int* width, int* height, + int* bytesPerValue, + void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaBytes; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaBytes, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve depth buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (bytesPerValue) + *bytesPerValue = mesaBytes; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.osmesa.handle; +} + diff --git a/FCLauncher/src/main/jni/glfw/posix_thread.c b/FCLauncher/src/main/jni/glfw/posix_thread.c new file mode 100644 index 00000000..29bb84df --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/posix_thread.c @@ -0,0 +1,80 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) +{ + assert(tls->posix.allocated == GLFW_FALSE); + + if (pthread_key_create(&tls->posix.key, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "POSIX: Failed to create context TLS"); + return GLFW_FALSE; + } + + tls->posix.allocated = GLFW_TRUE; + return GLFW_TRUE; +} + +void _glfwPlatformDestroyTls(_GLFWtls* tls) +{ + if (tls->posix.allocated) + pthread_key_delete(tls->posix.key); + memset(tls, 0, sizeof(_GLFWtls)); +} + +void* _glfwPlatformGetTls(_GLFWtls* tls) +{ + assert(tls->posix.allocated == GLFW_TRUE); + return pthread_getspecific(tls->posix.key); +} + +void _glfwPlatformSetTls(_GLFWtls* tls, void* value) +{ + assert(tls->posix.allocated == GLFW_TRUE); + pthread_setspecific(tls->posix.key, value); +} + +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_FALSE); + + if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create mutex"); + return GLFW_FALSE; + } + + return mutex->posix.allocated = GLFW_TRUE; +} + +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) +{ + if (mutex->posix.allocated) + pthread_mutex_destroy(&mutex->posix.handle); + memset(mutex, 0, sizeof(_GLFWmutex)); +} + +void _glfwPlatformLockMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_TRUE); + pthread_mutex_lock(&mutex->posix.handle); +} + +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_TRUE); + pthread_mutex_unlock(&mutex->posix.handle); +} + diff --git a/FCLauncher/src/main/jni/glfw/posix_time.c b/FCLauncher/src/main/jni/glfw/posix_time.c new file mode 100644 index 00000000..c6b2fe82 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/posix_time.c @@ -0,0 +1,62 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include +#include + +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialise timer +// +void _glfwInitTimerPOSIX(void) +{ +#if defined(CLOCK_MONOTONIC) + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + _glfw.timer.posix.monotonic = GLFW_TRUE; + _glfw.timer.posix.frequency = 1000000000; + } + else +#endif + { + _glfw.timer.posix.monotonic = GLFW_FALSE; + _glfw.timer.posix.frequency = 1000000; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +uint64_t _glfwPlatformGetTimerValue(void) +{ +#if defined(CLOCK_MONOTONIC) + if (_glfw.timer.posix.monotonic) + { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; + } + else +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; + } +} + +uint64_t _glfwPlatformGetTimerFrequency(void) +{ + return _glfw.timer.posix.frequency; +} + diff --git a/FCLauncher/src/main/jni/glfw/vulkan.c b/FCLauncher/src/main/jni/glfw/vulkan.c new file mode 100644 index 00000000..9e0cc026 --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/vulkan.c @@ -0,0 +1,312 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include +#include + +#define _GLFW_FIND_LOADER 1 +#define _GLFW_REQUIRE_LOADER 2 + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitVulkan(int mode) +{ + VkResult err; + VkExtensionProperties* ep; + uint32_t i, count; + + if (_glfw.vk.available) + return GLFW_TRUE; + +#if !defined(_GLFW_VULKAN_STATIC) +#if defined(_GLFW_VULKAN_LIBRARY) + _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); +#elif defined(_GLFW_WIN32) + _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); +#elif defined(_GLFW_COCOA) + _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); + if (!_glfw.vk.handle) + _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS(); +#elif defined(_GLFW_FCL) + _glfw.vk.handle = _glfw_dlopen("libvulkan.so"); +#else + _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); +#endif + if (!_glfw.vk.handle) + { + if (mode == _GLFW_REQUIRE_LOADER) + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); + + return GLFW_FALSE; + } + + _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) + _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); + if (!_glfw.vk.GetInstanceProcAddr) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Loader does not export vkGetInstanceProcAddr"); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) + vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); + if (!_glfw.vk.EnumerateInstanceExtensionProperties) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties"); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } +#endif // _GLFW_VULKAN_STATIC + + err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); + if (err) + { + // NOTE: This happens on systems with a loader but without any Vulkan ICD + if (mode == _GLFW_REQUIRE_LOADER) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to query instance extension count: %s", + _glfwGetVulkanResultString(err)); + } + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + ep = calloc(count, sizeof(VkExtensionProperties)); + + err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); + if (err) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to query instance extensions: %s", + _glfwGetVulkanResultString(err)); + + free(ep); + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + for (i = 0; i < count; i++) + { + if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) + _glfw.vk.KHR_surface = GLFW_TRUE; +#if defined(_GLFW_WIN32) + else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) + _glfw.vk.KHR_win32_surface = GLFW_TRUE; +#elif defined(_GLFW_COCOA) + else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) + _glfw.vk.MVK_macos_surface = GLFW_TRUE; + else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0) + _glfw.vk.EXT_metal_surface = GLFW_TRUE; +#elif defined(_GLFW_X11) + else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) + _glfw.vk.KHR_xlib_surface = GLFW_TRUE; + else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) + _glfw.vk.KHR_xcb_surface = GLFW_TRUE; +#elif defined(_GLFW_FCL) + else if (strcmp(ep[i].extensionName, "VK_KHR_android_surface") == 0) + _glfw.vk.KHR_android_surface = GLFW_TRUE; +#elif defined(_GLFW_WAYLAND) + else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) + _glfw.vk.KHR_wayland_surface = GLFW_TRUE; +#endif + } + + free(ep); + + _glfw.vk.available = GLFW_TRUE; + + _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); + + return GLFW_TRUE; +} + +void _glfwTerminateVulkan(void) +{ +#if !defined(_GLFW_VULKAN_STATIC) + if (_glfw.vk.handle) + _glfw_dlclose(_glfw.vk.handle); +#endif +} + +const char* _glfwGetVulkanResultString(VkResult result) +{ + switch (result) + { + case VK_SUCCESS: + return "Success"; + case VK_NOT_READY: + return "A fence or query has not yet completed"; + case VK_TIMEOUT: + return "A wait operation has not completed in the specified time"; + case VK_EVENT_SET: + return "An event is signaled"; + case VK_EVENT_RESET: + return "An event is unsignaled"; + case VK_INCOMPLETE: + return "A return array was too small for the result"; + case VK_ERROR_OUT_OF_HOST_MEMORY: + return "A host memory allocation has failed"; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + return "A device memory allocation has failed"; + case VK_ERROR_INITIALIZATION_FAILED: + return "Initialization of an object could not be completed for implementation-specific reasons"; + case VK_ERROR_DEVICE_LOST: + return "The logical or physical device has been lost"; + case VK_ERROR_MEMORY_MAP_FAILED: + return "Mapping of a memory object has failed"; + case VK_ERROR_LAYER_NOT_PRESENT: + return "A requested layer is not present or could not be loaded"; + case VK_ERROR_EXTENSION_NOT_PRESENT: + return "A requested extension is not supported"; + case VK_ERROR_FEATURE_NOT_PRESENT: + return "A requested feature is not supported"; + case VK_ERROR_INCOMPATIBLE_DRIVER: + return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible"; + case VK_ERROR_TOO_MANY_OBJECTS: + return "Too many objects of the type have already been created"; + case VK_ERROR_FORMAT_NOT_SUPPORTED: + return "A requested format is not supported on this device"; + case VK_ERROR_SURFACE_LOST_KHR: + return "A surface is no longer available"; + case VK_SUBOPTIMAL_KHR: + return "A swapchain no longer matches the surface properties exactly, but can still be used"; + case VK_ERROR_OUT_OF_DATE_KHR: + return "A surface has changed in such a way that it is no longer compatible with the swapchain"; + case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + return "The display used by a swapchain does not use the same presentable image layout"; + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API"; + case VK_ERROR_VALIDATION_FAILED_EXT: + return "A validation layer found an error"; + default: + return "ERROR: UNKNOWN VULKAN ERROR"; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwVulkanSupported(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + return _glfwInitVulkan(_GLFW_FIND_LOADER); +} + +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) +{ + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return NULL; + + if (!_glfw.vk.extensions[0]) + return NULL; + + *count = 2; + return (const char**) _glfw.vk.extensions; +} + +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, + const char* procname) +{ + GLFWvkproc proc; + assert(procname != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return NULL; + + proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); +#if defined(_GLFW_VULKAN_STATIC) + if (!proc) + { + if (strcmp(procname, "vkGetInstanceProcAddr") == 0) + return (GLFWvkproc) vkGetInstanceProcAddr; + } +#else + if (!proc) + proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); +#endif + + return proc; +} + +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + assert(instance != VK_NULL_HANDLE); + assert(device != VK_NULL_HANDLE); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return GLFW_FALSE; + + if (!_glfw.vk.extensions[0]) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Window surface creation extensions not found"); + return GLFW_FALSE; + } + + return _glfwPlatformGetPhysicalDevicePresentationSupport(instance, + device, + queuefamily); +} + +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, + GLFWwindow* handle, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(instance != VK_NULL_HANDLE); + assert(window != NULL); + assert(surface != NULL); + + *surface = VK_NULL_HANDLE; + + _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); + + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) + return VK_ERROR_INITIALIZATION_FAILED; + + if (!_glfw.vk.extensions[0]) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Window surface creation extensions not found"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + if (window->context.client != GLFW_NO_API) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API"); + return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; + } + + return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); +} + diff --git a/FCLauncher/src/main/jni/glfw/window.c b/FCLauncher/src/main/jni/glfw/window.c new file mode 100644 index 00000000..6273abab --- /dev/null +++ b/FCLauncher/src/main/jni/glfw/window.c @@ -0,0 +1,1078 @@ +// +// Created by Tungsten on 2022/10/11. +// + +#include + +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +// Notifies shared code that a window has lost or received input focus +// +void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) +{ + if (window->callbacks.focus) + window->callbacks.focus((GLFWwindow*) window, focused); + + if (!focused) + { + int key, button; + + for (key = 0; key <= GLFW_KEY_LAST; key++) + { + if (window->keys[key] == GLFW_PRESS) + { + const int scancode = _glfwPlatformGetKeyScancode(key); + _glfwInputKey(window, key, scancode, GLFW_RELEASE, 0); + } + } + + for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) + { + if (window->mouseButtons[button] == GLFW_PRESS) + _glfwInputMouseClick(window, button, GLFW_RELEASE, 0); + } + } +} + +// Notifies shared code that a window has moved +// The position is specified in content area relative screen coordinates +// +void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) +{ + if (window->callbacks.pos) + window->callbacks.pos((GLFWwindow*) window, x, y); +} + +// Notifies shared code that a window has been resized +// The size is specified in screen coordinates +// +void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->callbacks.size) + window->callbacks.size((GLFWwindow*) window, width, height); +} + +// Notifies shared code that a window has been iconified or restored +// +void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) +{ + if (window->callbacks.iconify) + window->callbacks.iconify((GLFWwindow*) window, iconified); +} + +// Notifies shared code that a window has been maximized or restored +// +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) +{ + if (window->callbacks.maximize) + window->callbacks.maximize((GLFWwindow*) window, maximized); +} + +// Notifies shared code that a window framebuffer has been resized +// The size is specified in pixels +// +void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) +{ + if (window->callbacks.fbsize) + window->callbacks.fbsize((GLFWwindow*) window, width, height); +} + +// Notifies shared code that a window content scale has changed +// The scale is specified as the ratio between the current and default DPI +// +void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale) +{ + if (window->callbacks.scale) + window->callbacks.scale((GLFWwindow*) window, xscale, yscale); +} + +// Notifies shared code that the window contents needs updating +// +void _glfwInputWindowDamage(_GLFWwindow* window) +{ + if (window->callbacks.refresh) + window->callbacks.refresh((GLFWwindow*) window); +} + +// Notifies shared code that the user wishes to close a window +// +void _glfwInputWindowCloseRequest(_GLFWwindow* window) +{ + window->shouldClose = GLFW_TRUE; + + if (window->callbacks.close) + window->callbacks.close((GLFWwindow*) window); +} + +// Notifies shared code that a window has changed its desired monitor +// +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) +{ + window->monitor = monitor; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, + const char* title, + GLFWmonitor* monitor, + GLFWwindow* share) +{ + _GLFWfbconfig fbconfig; + _GLFWctxconfig ctxconfig; + _GLFWwndconfig wndconfig; + _GLFWwindow* window; + + assert(title != NULL); + assert(width >= 0); + assert(height >= 0); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (width <= 0 || height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window size %ix%i", + width, height); + + return NULL; + } + + fbconfig = _glfw.hints.framebuffer; + ctxconfig = _glfw.hints.context; + wndconfig = _glfw.hints.window; + + wndconfig.width = width; + wndconfig.height = height; + wndconfig.title = title; + ctxconfig.share = (_GLFWwindow*) share; + + if (!_glfwIsValidContextConfig(&ctxconfig)) + return NULL; + + window = calloc(1, sizeof(_GLFWwindow)); + window->next = _glfw.windowListHead; + _glfw.windowListHead = window; + + window->videoMode.width = width; + window->videoMode.height = height; + window->videoMode.redBits = fbconfig.redBits; + window->videoMode.greenBits = fbconfig.greenBits; + window->videoMode.blueBits = fbconfig.blueBits; + window->videoMode.refreshRate = _glfw.hints.refreshRate; + + window->monitor = (_GLFWmonitor*) monitor; + window->resizable = wndconfig.resizable; + window->decorated = wndconfig.decorated; + window->autoIconify = wndconfig.autoIconify; + window->floating = wndconfig.floating; + window->focusOnShow = wndconfig.focusOnShow; + window->cursorMode = GLFW_CURSOR_NORMAL; + + window->doublebuffer = fbconfig.doublebuffer; + + window->minwidth = GLFW_DONT_CARE; + window->minheight = GLFW_DONT_CARE; + window->maxwidth = GLFW_DONT_CARE; + window->maxheight = GLFW_DONT_CARE; + window->numer = GLFW_DONT_CARE; + window->denom = GLFW_DONT_CARE; + + // Open the actual window and create its context + if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) + { + glfwDestroyWindow((GLFWwindow*) window); + return NULL; + } + + if (ctxconfig.client != GLFW_NO_API) + { + if (!_glfwRefreshContextAttribs(window, &ctxconfig)) + { + glfwDestroyWindow((GLFWwindow*) window); + return NULL; + } + } + + if (window->monitor) + { + if (wndconfig.centerCursor) + _glfwCenterCursorInContentArea(window); + } + else + { + if (wndconfig.visible) + { + _glfwPlatformShowWindow(window); + if (wndconfig.focused) + _glfwPlatformFocusWindow(window); + } + } + + return (GLFWwindow*) window; +} + +void glfwDefaultWindowHints(void) +{ + _GLFW_REQUIRE_INIT(); + + // The default is OpenGL with minimum version 1.0 + memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context)); + _glfw.hints.context.client = GLFW_OPENGL_API; + _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; + _glfw.hints.context.major = 1; + _glfw.hints.context.minor = 0; + + // The default is a focused, visible, resizable window with decorations + memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window)); + _glfw.hints.window.resizable = GLFW_TRUE; + _glfw.hints.window.visible = GLFW_TRUE; + _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.focused = GLFW_TRUE; + _glfw.hints.window.autoIconify = GLFW_TRUE; + _glfw.hints.window.centerCursor = GLFW_TRUE; + _glfw.hints.window.focusOnShow = GLFW_TRUE; + + // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, + // double buffered + memset(&_glfw.hints.framebuffer, 0, sizeof(_glfw.hints.framebuffer)); + _glfw.hints.framebuffer.redBits = 8; + _glfw.hints.framebuffer.greenBits = 8; + _glfw.hints.framebuffer.blueBits = 8; + _glfw.hints.framebuffer.alphaBits = 8; + _glfw.hints.framebuffer.depthBits = 24; + _glfw.hints.framebuffer.stencilBits = 8; + _glfw.hints.framebuffer.doublebuffer = GLFW_TRUE; + + // The default is to select the highest available refresh rate + _glfw.hints.refreshRate = GLFW_DONT_CARE; + + // The default is to use full Retina resolution framebuffers + _glfw.hints.window.ns.retina = GLFW_TRUE; +} + +GLFWAPI void glfwWindowHint(int hint, int value) +{ + _GLFW_REQUIRE_INIT(); + + switch (hint) + { + case GLFW_RED_BITS: + _glfw.hints.framebuffer.redBits = value; + return; + case GLFW_GREEN_BITS: + _glfw.hints.framebuffer.greenBits = value; + return; + case GLFW_BLUE_BITS: + _glfw.hints.framebuffer.blueBits = value; + return; + case GLFW_ALPHA_BITS: + _glfw.hints.framebuffer.alphaBits = value; + return; + case GLFW_DEPTH_BITS: + _glfw.hints.framebuffer.depthBits = value; + return; + case GLFW_STENCIL_BITS: + _glfw.hints.framebuffer.stencilBits = value; + return; + case GLFW_ACCUM_RED_BITS: + _glfw.hints.framebuffer.accumRedBits = value; + return; + case GLFW_ACCUM_GREEN_BITS: + _glfw.hints.framebuffer.accumGreenBits = value; + return; + case GLFW_ACCUM_BLUE_BITS: + _glfw.hints.framebuffer.accumBlueBits = value; + return; + case GLFW_ACCUM_ALPHA_BITS: + _glfw.hints.framebuffer.accumAlphaBits = value; + return; + case GLFW_AUX_BUFFERS: + _glfw.hints.framebuffer.auxBuffers = value; + return; + case GLFW_STEREO: + _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_DOUBLEBUFFER: + _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_TRANSPARENT_FRAMEBUFFER: + _glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_SAMPLES: + _glfw.hints.framebuffer.samples = value; + return; + case GLFW_SRGB_CAPABLE: + _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_RESIZABLE: + _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_DECORATED: + _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_FOCUSED: + _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_AUTO_ICONIFY: + _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_FLOATING: + _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_MAXIMIZED: + _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_VISIBLE: + _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_COCOA_RETINA_FRAMEBUFFER: + _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_COCOA_GRAPHICS_SWITCHING: + _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_SCALE_TO_MONITOR: + _glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CENTER_CURSOR: + _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_FOCUS_ON_SHOW: + _glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CLIENT_API: + _glfw.hints.context.client = value; + return; + case GLFW_CONTEXT_CREATION_API: + _glfw.hints.context.source = value; + return; + case GLFW_CONTEXT_VERSION_MAJOR: + _glfw.hints.context.major = value; + return; + case GLFW_CONTEXT_VERSION_MINOR: + _glfw.hints.context.minor = value; + return; + case GLFW_CONTEXT_ROBUSTNESS: + _glfw.hints.context.robustness = value; + return; + case GLFW_OPENGL_FORWARD_COMPAT: + _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_OPENGL_DEBUG_CONTEXT: + _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CONTEXT_NO_ERROR: + _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_OPENGL_PROFILE: + _glfw.hints.context.profile = value; + return; + case GLFW_CONTEXT_RELEASE_BEHAVIOR: + _glfw.hints.context.release = value; + return; + case GLFW_REFRESH_RATE: + _glfw.hints.refreshRate = value; + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); +} + +GLFWAPI void glfwWindowHintString(int hint, const char* value) +{ + assert(value != NULL); + + _GLFW_REQUIRE_INIT(); + + switch (hint) + { + case GLFW_COCOA_FRAME_NAME: + strncpy(_glfw.hints.window.ns.frameName, value, + sizeof(_glfw.hints.window.ns.frameName) - 1); + return; + case GLFW_X11_CLASS_NAME: + strncpy(_glfw.hints.window.x11.className, value, + sizeof(_glfw.hints.window.x11.className) - 1); + return; + case GLFW_X11_INSTANCE_NAME: + strncpy(_glfw.hints.window.x11.instanceName, value, + sizeof(_glfw.hints.window.x11.instanceName) - 1); + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint); +} + +GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + + _GLFW_REQUIRE_INIT(); + + // Allow closing of NULL (to match the behavior of free) + if (window == NULL) + return; + + // Clear all callbacks to avoid exposing a half torn-down window object + memset(&window->callbacks, 0, sizeof(window->callbacks)); + + // The window's context must not be current on another thread when the + // window is destroyed + if (window == _glfwPlatformGetTls(&_glfw.contextSlot)) + glfwMakeContextCurrent(NULL); + + _glfwPlatformDestroyWindow(window); + + // Unlink window from global linked list + { + _GLFWwindow** prev = &_glfw.windowListHead; + + while (*prev != window) + prev = &((*prev)->next); + + *prev = window->next; + } + + free(window); +} + +GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return window->shouldClose; +} + +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + window->shouldClose = value; +} + +GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(title != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetWindowTitle(window, title); +} + +GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, + int count, const GLFWimage* images) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(count >= 0); + assert(count == 0 || images != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetWindowIcon(window, count, images); +} + +GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowPos(window, xpos, ypos); +} + +GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformSetWindowPos(window, xpos, ypos); +} + +GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowSize(window, width, height); +} + +GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(width >= 0); + assert(height >= 0); + + _GLFW_REQUIRE_INIT(); + + window->videoMode.width = width; + window->videoMode.height = height; + + _glfwPlatformSetWindowSize(window, width, height); +} + +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (minwidth != GLFW_DONT_CARE && minheight != GLFW_DONT_CARE) + { + if (minwidth < 0 || minheight < 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window minimum size %ix%i", + minwidth, minheight); + return; + } + } + + if (maxwidth != GLFW_DONT_CARE && maxheight != GLFW_DONT_CARE) + { + if (maxwidth < 0 || maxheight < 0 || + maxwidth < minwidth || maxheight < minheight) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window maximum size %ix%i", + maxwidth, maxheight); + return; + } + } + + window->minwidth = minwidth; + window->minheight = minheight; + window->maxwidth = maxwidth; + window->maxheight = maxheight; + + if (window->monitor || !window->resizable) + return; + + _glfwPlatformSetWindowSizeLimits(window, + minwidth, minheight, + maxwidth, maxheight); +} + +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(numer != 0); + assert(denom != 0); + + _GLFW_REQUIRE_INIT(); + + if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) + { + if (numer <= 0 || denom <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window aspect ratio %i:%i", + numer, denom); + return; + } + } + + window->numer = numer; + window->denom = denom; + + if (window->monitor || !window->resizable) + return; + + _glfwPlatformSetWindowAspectRatio(window, numer, denom); +} + +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetFramebufferSize(window, width, height); +} + +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, + int* left, int* top, + int* right, int* bottom) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (left) + *left = 0; + if (top) + *top = 0; + if (right) + *right = 0; + if (bottom) + *bottom = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); +} + +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, + float* xscale, float* yscale) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowContentScale(window, xscale, yscale); +} + +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(1.f); + return _glfwPlatformGetWindowOpacity(window); +} + +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(opacity == opacity); + assert(opacity >= 0.f); + assert(opacity <= 1.f); + + _GLFW_REQUIRE_INIT(); + + if (opacity != opacity || opacity < 0.f || opacity > 1.f) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid window opacity %f", opacity); + return; + } + + _glfwPlatformSetWindowOpacity(window, opacity); +} + +GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformIconifyWindow(window); +} + +GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformRestoreWindow(window); +} + +GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformMaximizeWindow(window); +} + +GLFWAPI void glfwShowWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformShowWindow(window); + + if (window->focusOnShow) + _glfwPlatformFocusWindow(window); +} + +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformRequestWindowAttention(window); +} + +GLFWAPI void glfwHideWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformHideWindow(window); +} + +GLFWAPI void glfwFocusWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformFocusWindow(window); +} + +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + switch (attrib) + { + case GLFW_FOCUSED: + return _glfwPlatformWindowFocused(window); + case GLFW_ICONIFIED: + return _glfwPlatformWindowIconified(window); + case GLFW_VISIBLE: + return _glfwPlatformWindowVisible(window); + case GLFW_MAXIMIZED: + return _glfwPlatformWindowMaximized(window); + case GLFW_HOVERED: + return _glfwPlatformWindowHovered(window); + case GLFW_FOCUS_ON_SHOW: + return window->focusOnShow; + case GLFW_TRANSPARENT_FRAMEBUFFER: + return _glfwPlatformFramebufferTransparent(window); + case GLFW_RESIZABLE: + return window->resizable; + case GLFW_DECORATED: + return window->decorated; + case GLFW_FLOATING: + return window->floating; + case GLFW_AUTO_ICONIFY: + return window->autoIconify; + case GLFW_CLIENT_API: + return window->context.client; + case GLFW_CONTEXT_CREATION_API: + return window->context.source; + case GLFW_CONTEXT_VERSION_MAJOR: + return window->context.major; + case GLFW_CONTEXT_VERSION_MINOR: + return window->context.minor; + case GLFW_CONTEXT_REVISION: + return window->context.revision; + case GLFW_CONTEXT_ROBUSTNESS: + return window->context.robustness; + case GLFW_OPENGL_FORWARD_COMPAT: + return window->context.forward; + case GLFW_OPENGL_DEBUG_CONTEXT: + return window->context.debug; + case GLFW_OPENGL_PROFILE: + return window->context.profile; + case GLFW_CONTEXT_RELEASE_BEHAVIOR: + return window->context.release; + case GLFW_CONTEXT_NO_ERROR: + return window->context.noerror; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); + return 0; +} + +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + value = value ? GLFW_TRUE : GLFW_FALSE; + + if (attrib == GLFW_AUTO_ICONIFY) + window->autoIconify = value; + else if (attrib == GLFW_RESIZABLE) + { + if (window->resizable == value) + return; + + window->resizable = value; + if (!window->monitor) + _glfwPlatformSetWindowResizable(window, value); + } + else if (attrib == GLFW_DECORATED) + { + if (window->decorated == value) + return; + + window->decorated = value; + if (!window->monitor) + _glfwPlatformSetWindowDecorated(window, value); + } + else if (attrib == GLFW_FLOATING) + { + if (window->floating == value) + return; + + window->floating = value; + if (!window->monitor) + _glfwPlatformSetWindowFloating(window, value); + } + else if (attrib == GLFW_FOCUS_ON_SHOW) + window->focusOnShow = value; + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); +} + +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return (GLFWmonitor*) window->monitor; +} + +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, + GLFWmonitor* mh, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + _GLFWwindow* window = (_GLFWwindow*) wh; + _GLFWmonitor* monitor = (_GLFWmonitor*) mh; + assert(window != NULL); + assert(width >= 0); + assert(height >= 0); + + _GLFW_REQUIRE_INIT(); + + if (width <= 0 || height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window size %ix%i", + width, height); + return; + } + + if (refreshRate < 0 && refreshRate != GLFW_DONT_CARE) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid refresh rate %i", + refreshRate); + return; + } + + window->videoMode.width = width; + window->videoMode.height = height; + window->videoMode.refreshRate = refreshRate; + + _glfwPlatformSetWindowMonitor(window, monitor, + xpos, ypos, width, height, + refreshRate); +} + +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + window->userPointer = pointer; +} + +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->userPointer; +} + +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, + GLFWwindowposfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, + GLFWwindowsizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle, + GLFWwindowclosefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle, + GLFWwindowrefreshfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle, + GLFWwindowfocusfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, + GLFWwindowiconifyfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, + GLFWwindowmaximizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); + return cbfun; +} + +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle, + GLFWframebuffersizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* handle, + GLFWwindowcontentscalefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun); + return cbfun; +} + +GLFWAPI void glfwPollEvents(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformPollEvents(); +} + +GLFWAPI void glfwWaitEvents(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformWaitEvents(); +} + +GLFWAPI void glfwWaitEventsTimeout(double timeout) +{ + _GLFW_REQUIRE_INIT(); + assert(timeout == timeout); + assert(timeout >= 0.0); + assert(timeout <= DBL_MAX); + + if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", timeout); + return; + } + + _glfwPlatformWaitEventsTimeout(timeout); +} + +GLFWAPI void glfwPostEmptyEvent(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformPostEmptyEvent(); +} + diff --git a/FCLauncher/src/main/jni/xhook/include/queue.h b/FCLauncher/src/main/jni/xhook/include/queue.h new file mode 100644 index 00000000..c2443bef --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/queue.h @@ -0,0 +1,554 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $ + */ + +#ifndef QUEUE_H +#define QUEUE_H + +/* #include */ +#define __containerof(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field))) + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type, qual) \ + struct name { \ + struct type *qual slh_first; /* first element */ \ + } + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type, qual) \ + struct { \ + struct type *qual sle_next; /* next element */ \ + } + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ + } while (0) + +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ + } while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ + } while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ + } while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ + } while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + } while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ + } while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type, qual) \ + struct name { \ + struct type *qual lh_first; /* first element */ \ + } + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type, qual) \ + struct { \ + struct type *qual le_next; /* next element */ \ + struct type *qual *le_prev; /* address of previous next element */ \ + } + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ + } while (0) + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, struct type, field.le_next)) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ + } while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ + } while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ + } while (0) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + } while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ + } while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type, qual) \ + struct name { \ + struct type *qual stqh_first;/* first element */ \ + struct type *qual *stqh_last;/* addr of last next element */ \ + } + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type, qual) \ + struct { \ + struct type *qual stqe_next; /* next element */ \ + } + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, struct type, field.stqe_next)) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ + } while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ + } while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ + } while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + } while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ + } while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type, qual) \ + struct name { \ + struct type *qual tqh_first; /* first element */ \ + struct type *qual *tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type, qual) \ + struct { \ + struct type *qual tqe_next; /* next element */ \ + struct type *qual *tqe_prev; /* address of previous next element */ \ + } + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + } while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ + } while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ + } while (0) + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/tree.h b/FCLauncher/src/main/jni/xhook/include/tree.h new file mode 100644 index 00000000..dc938ae5 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/tree.h @@ -0,0 +1,768 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* $FreeBSD: stable/9/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */ + +/*- + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TREE_H +#define TREE_H + +/* #include */ +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/xh_core.h b/FCLauncher/src/main/jni/xhook/include/xh_core.h new file mode 100644 index 00000000..35087945 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/xh_core.h @@ -0,0 +1,48 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_CORE_H +#define XH_CORE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +int xh_core_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func); + +int xh_core_ignore(const char *pathname_regex_str, const char *symbol); + +int xh_core_refresh(int async); + +void xh_core_clear(); + +void xh_core_enable_debug(int flag); + +void xh_core_enable_sigsegv_protection(int flag); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/xh_elf.h b/FCLauncher/src/main/jni/xhook/include/xh_elf.h new file mode 100644 index 00000000..1697dc48 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/xh_elf.h @@ -0,0 +1,85 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_ELF_H +#define XH_ELF_H 1 + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + const char *pathname; + + ElfW(Addr) base_addr; + ElfW(Addr) bias_addr; + + ElfW(Ehdr) *ehdr; + ElfW(Phdr) *phdr; + + ElfW(Dyn) *dyn; //.dynamic + ElfW(Word) dyn_sz; + + const char *strtab; //.dynstr (string-table) + ElfW(Sym) *symtab; //.dynsym (symbol-index to string-table's offset) + + ElfW(Addr) relplt; //.rel.plt or .rela.plt + ElfW(Word) relplt_sz; + + ElfW(Addr) reldyn; //.rel.dyn or .rela.dyn + ElfW(Word) reldyn_sz; + + ElfW(Addr) relandroid; //android compressed rel or rela + ElfW(Word) relandroid_sz; + + //for ELF hash + uint32_t *bucket; + uint32_t bucket_cnt; + uint32_t *chain; + uint32_t chain_cnt; //invalid for GNU hash + + //append for GNU hash + uint32_t symoffset; + ElfW(Addr) *bloom; + uint32_t bloom_sz; + uint32_t bloom_shift; + + int is_use_rela; + int is_use_gnu_hash; +} xh_elf_t; + +int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname); +int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func); + +int xh_elf_check_elfheader(uintptr_t base_addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/xh_errno.h b/FCLauncher/src/main/jni/xhook/include/xh_errno.h new file mode 100644 index 00000000..e628cd77 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/xh_errno.h @@ -0,0 +1,37 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_ERRNO_H +#define XH_ERRNO_H 1 + +#define XH_ERRNO_UNKNOWN 1001 +#define XH_ERRNO_INVAL 1002 +#define XH_ERRNO_NOMEM 1003 +#define XH_ERRNO_REPEAT 1004 +#define XH_ERRNO_NOTFND 1005 +#define XH_ERRNO_BADMAPS 1006 +#define XH_ERRNO_FORMAT 1007 +#define XH_ERRNO_ELFINIT 1008 +#define XH_ERRNO_SEGVERR 1009 + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/xh_log.h b/FCLauncher/src/main/jni/xhook/include/xh_log.h new file mode 100644 index 00000000..e108c4b0 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/xh_log.h @@ -0,0 +1,45 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_LOG_H +#define XH_LOG_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern android_LogPriority xh_log_priority; + +#define XH_LOG_TAG "xhook" +#define XH_LOG_DEBUG(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_DEBUG) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) +#define XH_LOG_INFO(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_INFO) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) +#define XH_LOG_WARN(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_WARN) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) +#define XH_LOG_ERROR(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_ERROR) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/xh_util.h b/FCLauncher/src/main/jni/xhook/include/xh_util.h new file mode 100644 index 00000000..b57f8dc6 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/xh_util.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_UTILS_H +#define XH_UTILS_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__LP64__) +#define XH_UTIL_FMT_LEN "16" +#define XH_UTIL_FMT_X "llx" +#else +#define XH_UTIL_FMT_LEN "8" +#define XH_UTIL_FMT_X "x" +#endif + +#define XH_UTIL_FMT_FIXED_X XH_UTIL_FMT_LEN XH_UTIL_FMT_X +#define XH_UTIL_FMT_FIXED_S XH_UTIL_FMT_LEN "s" + +int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot); +int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot); +int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot); +void xh_util_flush_instruction_cache(uintptr_t addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/xh_version.h b/FCLauncher/src/main/jni/xhook/include/xh_version.h new file mode 100644 index 00000000..b70b4f2f --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/xh_version.h @@ -0,0 +1,41 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_VERSION_H +#define XH_VERSION_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned int xh_version(); + +const char *xh_version_str(); + +const char *xh_version_str_full(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/FCLauncher/src/main/jni/xhook/include/xhook.h b/FCLauncher/src/main/jni/xhook/include/xhook.h new file mode 100644 index 00000000..93dd5b4c --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/include/xhook.h @@ -0,0 +1,50 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XHOOK_H +#define XHOOK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#define XHOOK_EXPORT __attribute__((visibility("default"))) + +int xhook_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func) XHOOK_EXPORT; + +int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT; + +int xhook_refresh(int async) XHOOK_EXPORT; + +void xhook_clear() XHOOK_EXPORT; + +void xhook_enable_debug(int flag) XHOOK_EXPORT; + +void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/FCLauncher/src/main/jni/xhook/xh_core.c b/FCLauncher/src/main/jni/xhook/xh_core.c new file mode 100644 index 00000000..01d4fe5b --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/xh_core.c @@ -0,0 +1,676 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XH_CORE_DEBUG 0 + +//registered hook info collection +typedef struct xh_core_hook_info +{ +#if XH_CORE_DEBUG + char *pathname_regex_str; +#endif + regex_t pathname_regex; + char *symbol; + void *new_func; + void **old_func; + TAILQ_ENTRY(xh_core_hook_info,) link; +} xh_core_hook_info_t; +typedef TAILQ_HEAD(xh_core_hook_info_queue, xh_core_hook_info,) xh_core_hook_info_queue_t; + +//ignored hook info collection +typedef struct xh_core_ignore_info +{ +#if XH_CORE_DEBUG + char *pathname_regex_str; +#endif + regex_t pathname_regex; + char *symbol; //NULL meaning for all symbols + TAILQ_ENTRY(xh_core_ignore_info,) link; +} xh_core_ignore_info_t; +typedef TAILQ_HEAD(xh_core_ignore_info_queue, xh_core_ignore_info,) xh_core_ignore_info_queue_t; + +//required info from /proc/self/maps +typedef struct xh_core_map_info +{ + char *pathname; + uintptr_t base_addr; + xh_elf_t elf; + RB_ENTRY(xh_core_map_info) link; +} xh_core_map_info_t; +static __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b) +{ + return strcmp(a->pathname, b->pathname); +} +typedef RB_HEAD(xh_core_map_info_tree, xh_core_map_info) xh_core_map_info_tree_t; +RB_GENERATE_STATIC(xh_core_map_info_tree, xh_core_map_info, link, xh_core_map_info_cmp) + +//signal handler for SIGSEGV +//for xh_elf_init(), xh_elf_hook(), xh_elf_check_elfheader() +static int xh_core_sigsegv_enable = 1; //enable by default +static struct sigaction xh_core_sigsegv_act_old; +static volatile int xh_core_sigsegv_flag = 0; +static sigjmp_buf xh_core_sigsegv_env; +static void xh_core_sigsegv_handler(int sig) +{ + (void)sig; + + if(xh_core_sigsegv_flag) + siglongjmp(xh_core_sigsegv_env, 1); + else + sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL); +} +static int xh_core_add_sigsegv_handler() +{ + struct sigaction act; + + if(!xh_core_sigsegv_enable) return 0; + + if(0 != sigemptyset(&act.sa_mask)) return (0 == errno ? XH_ERRNO_UNKNOWN : errno); + act.sa_handler = xh_core_sigsegv_handler; + + if(0 != sigaction(SIGSEGV, &act, &xh_core_sigsegv_act_old)) + return (0 == errno ? XH_ERRNO_UNKNOWN : errno); + + return 0; +} +static void xh_core_del_sigsegv_handler() +{ + if(!xh_core_sigsegv_enable) return; + + sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL); +} + + +static xh_core_hook_info_queue_t xh_core_hook_info = TAILQ_HEAD_INITIALIZER(xh_core_hook_info); +static xh_core_ignore_info_queue_t xh_core_ignore_info = TAILQ_HEAD_INITIALIZER(xh_core_ignore_info); +static xh_core_map_info_tree_t xh_core_map_info = RB_INITIALIZER(&xh_core_map_info); +static pthread_mutex_t xh_core_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t xh_core_cond = PTHREAD_COND_INITIALIZER; +static volatile int xh_core_inited = 0; +static volatile int xh_core_init_ok = 0; +static volatile int xh_core_async_inited = 0; +static volatile int xh_core_async_init_ok = 0; +static pthread_mutex_t xh_core_refresh_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_t xh_core_refresh_thread_tid; +static volatile int xh_core_refresh_thread_running = 0; +static volatile int xh_core_refresh_thread_do = 0; + + +int xh_core_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func) +{ + xh_core_hook_info_t *hi; + regex_t regex; + + if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL; + + if(xh_core_inited) + { + XH_LOG_ERROR("do not register hook after refresh(): %s, %s", pathname_regex_str, symbol); + return XH_ERRNO_INVAL; + } + + if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL; + + if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM; + if(NULL == (hi->symbol = strdup(symbol))) + { + free(hi); + return XH_ERRNO_NOMEM; + } +#if XH_CORE_DEBUG + if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str))) + { + free(hi->symbol); + free(hi); + return XH_ERRNO_NOMEM; + } +#endif + hi->pathname_regex = regex; + hi->new_func = new_func; + hi->old_func = old_func; + + pthread_mutex_lock(&xh_core_mutex); + TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link); + pthread_mutex_unlock(&xh_core_mutex); + + return 0; +} + +int xh_core_ignore(const char *pathname_regex_str, const char *symbol) +{ + xh_core_ignore_info_t *ii; + regex_t regex; + + if(NULL == pathname_regex_str) return XH_ERRNO_INVAL; + + if(xh_core_inited) + { + XH_LOG_ERROR("do not ignore hook after refresh(): %s, %s", pathname_regex_str, symbol ? symbol : "ALL"); + return XH_ERRNO_INVAL; + } + + if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL; + + if(NULL == (ii = malloc(sizeof(xh_core_ignore_info_t)))) return XH_ERRNO_NOMEM; + if(NULL != symbol) + { + if(NULL == (ii->symbol = strdup(symbol))) + { + free(ii); + return XH_ERRNO_NOMEM; + } + } + else + { + ii->symbol = NULL; //ignore all symbols + } +#if XH_CORE_DEBUG + if(NULL == (ii->pathname_regex_str = strdup(pathname_regex_str))) + { + free(ii->symbol); + free(ii); + return XH_ERRNO_NOMEM; + } +#endif + ii->pathname_regex = regex; + + pthread_mutex_lock(&xh_core_mutex); + TAILQ_INSERT_TAIL(&xh_core_ignore_info, ii, link); + pthread_mutex_unlock(&xh_core_mutex); + + return 0; +} + +static int xh_core_check_elf_header(uintptr_t base_addr, const char *pathname) +{ + if(!xh_core_sigsegv_enable) + { + return xh_elf_check_elfheader(base_addr); + } + else + { + int ret = XH_ERRNO_UNKNOWN; + + xh_core_sigsegv_flag = 1; + if(0 == sigsetjmp(xh_core_sigsegv_env, 1)) + { + ret = xh_elf_check_elfheader(base_addr); + } + else + { + ret = XH_ERRNO_SEGVERR; + XH_LOG_WARN("catch SIGSEGV when check_elfheader: %s", pathname); + } + xh_core_sigsegv_flag = 0; + return ret; + } +} + +static void xh_core_hook_impl(xh_core_map_info_t *mi) +{ + //init + if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return; + + //hook + xh_core_hook_info_t *hi; + xh_core_ignore_info_t *ii; + int ignore; + TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info + { + if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0)) + { + ignore = 0; + TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info + { + if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0)) + { + if(NULL == ii->symbol) //ignore all symbols + return; + + if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol + { + ignore = 1; + break; + } + } + } + + if(0 == ignore) + xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func); + } + } +} + +static void xh_core_hook(xh_core_map_info_t *mi) +{ + if(!xh_core_sigsegv_enable) + { + xh_core_hook_impl(mi); + } + else + { + xh_core_sigsegv_flag = 1; + if(0 == sigsetjmp(xh_core_sigsegv_env, 1)) + { + xh_core_hook_impl(mi); + } + else + { + XH_LOG_WARN("catch SIGSEGV when init or hook: %s", mi->pathname); + } + xh_core_sigsegv_flag = 0; + } +} + +static void xh_core_refresh_impl() +{ + char line[512]; + FILE *fp; + uintptr_t base_addr; + uintptr_t prev_base_addr = 0; + char perm[5]; + char prev_perm[5] = "---p"; + unsigned long offset; + unsigned long prev_offset = 0; + int pathname_pos; + char *pathname; + char prev_pathname[512] = {0}; + size_t pathname_len; + xh_core_map_info_t *mi, *mi_tmp; + xh_core_map_info_t mi_key; + xh_core_hook_info_t *hi; + xh_core_ignore_info_t *ii; + int match; + xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed); + + if(NULL == (fp = fopen("/proc/self/maps", "r"))) + { + XH_LOG_ERROR("fopen /proc/self/maps failed"); + return; + } + + while(fgets(line, sizeof(line), fp)) + { + if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; + + // do not touch the shared memory + if (perm[3] != 'p') continue; + + // Ignore permission PROT_NONE maps + if (perm[0] == '-' && perm[1] == '-' && perm[2] == '-') + continue; + + //get pathname + while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) + pathname_pos += 1; + if(pathname_pos >= (int)(sizeof(line) - 1)) continue; + pathname = line + pathname_pos; + pathname_len = strlen(pathname); + if(0 == pathname_len) continue; + if(pathname[pathname_len - 1] == '\n') + { + pathname[pathname_len - 1] = '\0'; + pathname_len -= 1; + } + if(0 == pathname_len) continue; + if('[' == pathname[0]) continue; + + // Find non-executable map, we need record it. Because so maps can begin with + // an non-executable map. + if (perm[2] != 'x') { + prev_offset = offset; + prev_base_addr = base_addr; + memcpy(prev_perm, perm, sizeof(prev_perm)); + strcpy(prev_pathname, pathname); + continue; + } + + // Find executable map if offset == 0, it OK, + // or we need check previous map for base address. + if (offset != 0) { + if (strcmp(prev_pathname, pathname) || prev_offset != 0 || prev_perm[0] != 'r') { + continue; + } + // The previous map is real begin map + base_addr = prev_base_addr; + } + + //check pathname + //if we need to hook this elf? + match = 0; + TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info + { + if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0)) + { + TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info + { + if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0)) + { + if(NULL == ii->symbol) + goto check_finished; + + if(0 == strcmp(ii->symbol, hi->symbol)) + goto check_continue; + } + } + + match = 1; + check_continue: + break; + } + } + check_finished: + if(0 == match) continue; + + //check elf header format + //We are trying to do ELF header checking as late as possible. + if(0 != xh_core_check_elf_header(base_addr, pathname)) continue; + + //check existed map item + mi_key.pathname = pathname; + if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key))) + { + //exist + RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); + + //repeated? + //We only keep the first one, that is the real base address + if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi)) + { +#if XH_CORE_DEBUG + XH_LOG_DEBUG("repeated map info when update: %s", line); +#endif + free(mi->pathname); + free(mi); + continue; + } + + //re-hook if base_addr changed + if(mi->base_addr != base_addr) + { + mi->base_addr = base_addr; + xh_core_hook(mi); + } + } + else + { + //not exist, create a new map info + if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue; + if(NULL == (mi->pathname = strdup(pathname))) + { + free(mi); + continue; + } + mi->base_addr = base_addr; + + //repeated? + //We only keep the first one, that is the real base address + if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi)) + { +#if XH_CORE_DEBUG + XH_LOG_DEBUG("repeated map info when create: %s", line); +#endif + free(mi->pathname); + free(mi); + continue; + } + + //hook + xh_core_hook(mi); //hook + } + } + fclose(fp); + + //free all missing map item, maybe dlclosed? + RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp) + { +#if XH_CORE_DEBUG + XH_LOG_DEBUG("remove missing map info: %s", mi->pathname); +#endif + RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); + if(mi->pathname) free(mi->pathname); + free(mi); + } + + //save the new refreshed map info tree + xh_core_map_info = map_info_refreshed; + + XH_LOG_INFO("map refreshed"); + +#if XH_CORE_DEBUG + RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info) + XH_LOG_DEBUG(" %"PRIxPTR" %s\n", mi->base_addr, mi->pathname); +#endif +} + +static void *xh_core_refresh_thread_func(void *arg) +{ + (void)arg; + + pthread_setname_np(pthread_self(), "xh_refresh_loop"); + + while(xh_core_refresh_thread_running) + { + //waiting for a refresh task or exit + pthread_mutex_lock(&xh_core_mutex); + while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running) + { + pthread_cond_wait(&xh_core_cond, &xh_core_mutex); + } + if(!xh_core_refresh_thread_running) + { + pthread_mutex_unlock(&xh_core_mutex); + break; + } + xh_core_refresh_thread_do = 0; + pthread_mutex_unlock(&xh_core_mutex); + + //refresh + pthread_mutex_lock(&xh_core_refresh_mutex); + xh_core_refresh_impl(); + pthread_mutex_unlock(&xh_core_refresh_mutex); + } + + return NULL; +} + +static void xh_core_init_once() +{ + if(xh_core_inited) return; + + pthread_mutex_lock(&xh_core_mutex); + + if(xh_core_inited) goto end; + + xh_core_inited = 1; + + //dump debug info + XH_LOG_INFO("%s\n", xh_version_str_full()); +#if XH_CORE_DEBUG + xh_core_hook_info_t *hi; + TAILQ_FOREACH(hi, &xh_core_hook_info, link) + XH_LOG_INFO(" hook: %s @ %s, (%p, %p)\n", hi->symbol, hi->pathname_regex_str, + hi->new_func, hi->old_func); + xh_core_ignore_info_t *ii; + TAILQ_FOREACH(ii, &xh_core_ignore_info, link) + XH_LOG_INFO(" ignore: %s @ %s\n", ii->symbol ? ii->symbol : "ALL ", + ii->pathname_regex_str); +#endif + + //register signal handler + if(0 != xh_core_add_sigsegv_handler()) goto end; + + //OK + xh_core_init_ok = 1; + + end: + pthread_mutex_unlock(&xh_core_mutex); +} + +static void xh_core_init_async_once() +{ + if(xh_core_async_inited) return; + + pthread_mutex_lock(&xh_core_mutex); + + if(xh_core_async_inited) goto end; + + xh_core_async_inited = 1; + + //create async refresh thread + xh_core_refresh_thread_running = 1; + if(0 != pthread_create(&xh_core_refresh_thread_tid, NULL, &xh_core_refresh_thread_func, NULL)) + { + xh_core_refresh_thread_running = 0; + goto end; + } + + //OK + xh_core_async_init_ok = 1; + + end: + pthread_mutex_unlock(&xh_core_mutex); +} + +int xh_core_refresh(int async) +{ + //init + xh_core_init_once(); + if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN; + + if(async) + { + //init for async + xh_core_init_async_once(); + if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN; + + //refresh async + pthread_mutex_lock(&xh_core_mutex); + xh_core_refresh_thread_do = 1; + pthread_cond_signal(&xh_core_cond); + pthread_mutex_unlock(&xh_core_mutex); + } + else + { + //refresh sync + pthread_mutex_lock(&xh_core_refresh_mutex); + xh_core_refresh_impl(); + pthread_mutex_unlock(&xh_core_refresh_mutex); + } + + return 0; +} + +void xh_core_clear() +{ + //stop the async refresh thread + if(xh_core_async_init_ok) + { + pthread_mutex_lock(&xh_core_mutex); + xh_core_refresh_thread_running = 0; + pthread_cond_signal(&xh_core_cond); + pthread_mutex_unlock(&xh_core_mutex); + + pthread_join(xh_core_refresh_thread_tid, NULL); + xh_core_async_init_ok = 0; + } + xh_core_async_inited = 0; + + //unregister the sig handler + if(xh_core_init_ok) + { + xh_core_del_sigsegv_handler(); + xh_core_init_ok = 0; + } + xh_core_inited = 0; + + pthread_mutex_lock(&xh_core_mutex); + pthread_mutex_lock(&xh_core_refresh_mutex); + + //free all map info + xh_core_map_info_t *mi, *mi_tmp; + RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp) + { + RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); + if(mi->pathname) free(mi->pathname); + free(mi); + } + + //free all hook info + xh_core_hook_info_t *hi, *hi_tmp; + TAILQ_FOREACH_SAFE(hi, &xh_core_hook_info, link, hi_tmp) + { + TAILQ_REMOVE(&xh_core_hook_info, hi, link); +#if XH_CORE_DEBUG + free(hi->pathname_regex_str); +#endif + regfree(&(hi->pathname_regex)); + free(hi->symbol); + free(hi); + } + + //free all ignore info + xh_core_ignore_info_t *ii, *ii_tmp; + TAILQ_FOREACH_SAFE(ii, &xh_core_ignore_info, link, ii_tmp) + { + TAILQ_REMOVE(&xh_core_ignore_info, ii, link); +#if XH_CORE_DEBUG + free(ii->pathname_regex_str); +#endif + regfree(&(ii->pathname_regex)); + free(ii->symbol); + free(ii); + } + + pthread_mutex_unlock(&xh_core_refresh_mutex); + pthread_mutex_unlock(&xh_core_mutex); +} + +void xh_core_enable_debug(int flag) +{ + xh_log_priority = (flag ? ANDROID_LOG_DEBUG : ANDROID_LOG_WARN); +} + +void xh_core_enable_sigsegv_protection(int flag) +{ + xh_core_sigsegv_enable = (flag ? 1 : 0); +} diff --git a/FCLauncher/src/main/jni/xhook/xh_elf.c b/FCLauncher/src/main/jni/xhook/xh_elf.c new file mode 100644 index 00000000..5e901cca --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/xh_elf.c @@ -0,0 +1,1046 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XH_ELF_DEBUG 0 + +#ifndef EI_ABIVERSION +#define EI_ABIVERSION 8 +#endif + +#if defined(__arm__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT //.rel.plt +#define XH_ELF_R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT //.rel.dyn +#define XH_ELF_R_GENERIC_ABS R_ARM_ABS32 //.rel.dyn +#elif defined(__aarch64__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT +#define XH_ELF_R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT +#define XH_ELF_R_GENERIC_ABS R_AARCH64_ABS64 +#elif defined(__i386__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_386_JMP_SLOT +#define XH_ELF_R_GENERIC_GLOB_DAT R_386_GLOB_DAT +#define XH_ELF_R_GENERIC_ABS R_386_32 +#elif defined(__x86_64__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT +#define XH_ELF_R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT +#define XH_ELF_R_GENERIC_ABS R_X86_64_64 +#endif + +#if defined(__LP64__) +#define XH_ELF_R_SYM(info) ELF64_R_SYM(info) +#define XH_ELF_R_TYPE(info) ELF64_R_TYPE(info) +#else +#define XH_ELF_R_SYM(info) ELF32_R_SYM(info) +#define XH_ELF_R_TYPE(info) ELF32_R_TYPE(info) +#endif + +//iterator for plain PLT +typedef struct +{ + uint8_t *cur; + uint8_t *end; + int is_use_rela; +} xh_elf_plain_reloc_iterator_t; + +static void xh_elf_plain_reloc_iterator_init(xh_elf_plain_reloc_iterator_t *self, + ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela) +{ + self->cur = (uint8_t *)rel; + self->end = self->cur + rel_sz; + self->is_use_rela = is_use_rela; +} + +static void *xh_elf_plain_reloc_iterator_next(xh_elf_plain_reloc_iterator_t *self) +{ + if(self->cur >= self->end) return NULL; + + void *ret = (void *)(self->cur); + self->cur += (self->is_use_rela ? sizeof(ElfW(Rela)) : sizeof(ElfW(Rel))); + return ret; +} + +//sleb128 decoder +typedef struct +{ + uint8_t *cur; + uint8_t *end; +} xh_elf_sleb128_decoder_t; + +static void xh_elf_sleb128_decoder_init(xh_elf_sleb128_decoder_t *self, + ElfW(Addr) rel, ElfW(Word) rel_sz) +{ + self->cur = (uint8_t *)rel; + self->end = self->cur + rel_sz; +} + +static int xh_elf_sleb128_decoder_next(xh_elf_sleb128_decoder_t *self, size_t *ret) +{ + size_t value = 0; + static const size_t size = 8 * sizeof(value); + size_t shift = 0; + uint8_t byte; + + do + { + if(self->cur >= self->end) + return XH_ERRNO_FORMAT; + + byte = *(self->cur)++; + value |= ((size_t)(byte & 127) << shift); + shift += 7; + } while(byte & 128); + + if(shift < size && (byte & 64)) + { + value |= -((size_t)(1) << shift); + } + + *ret = value; + return 0; +} + +//iterator for sleb128 decoded packed PLT +typedef struct +{ + xh_elf_sleb128_decoder_t decoder; + size_t relocation_count; + size_t group_size; + size_t group_flags; + size_t group_r_offset_delta; + size_t relocation_index; + size_t relocation_group_index; + ElfW(Rela) rela; + ElfW(Rel) rel; + ElfW(Addr) r_offset; + size_t r_info; + ssize_t r_addend; + int is_use_rela; +} xh_elf_packed_reloc_iterator_t; + +const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1; +const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2; +const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4; +const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8; + +static int xh_elf_packed_reloc_iterator_init(xh_elf_packed_reloc_iterator_t *self, + ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela) +{ + int r; + + memset(self, 0, sizeof(xh_elf_packed_reloc_iterator_t)); + xh_elf_sleb128_decoder_init(&(self->decoder), rel, rel_sz); + self->is_use_rela = is_use_rela; + + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->relocation_count)))) return r; + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_offset)))) return r; + return 0; +} + +static int xh_elf_packed_reloc_iterator_read_group_fields(xh_elf_packed_reloc_iterator_t *self) +{ + int r; + size_t val; + + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_size)))) return r; + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_flags)))) return r; + + if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_r_offset_delta)))) return r; + + if(self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG) + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_info)))) return r; + + if((self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) && + (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG)) + { + if(0 == self->is_use_rela) + { + XH_LOG_ERROR("unexpected r_addend in android.rel section"); + return XH_ERRNO_FORMAT; + } + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &val))) return r; + self->r_addend += (ssize_t)val; + } + else if(0 == (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG)) + { + self->r_addend = 0; + } + + self->relocation_group_index = 0; + return 0; +} + +static void *xh_elf_packed_reloc_iterator_next(xh_elf_packed_reloc_iterator_t *self) +{ + size_t val; + + if(self->relocation_index >= self->relocation_count) return NULL; + + if(self->relocation_group_index == self->group_size) + { + if(0 != xh_elf_packed_reloc_iterator_read_group_fields(self)) return NULL; + } + + if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) + { + self->r_offset += self->group_r_offset_delta; + } + else + { + if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL; + self->r_offset += val; + } + + if(0 == (self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG)) + if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &(self->r_info))) return NULL; + + if(self->is_use_rela && + (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) && + (0 == (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG))) + { + if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL; + self->r_addend += (ssize_t)val; + } + + self->relocation_index++; + self->relocation_group_index++; + + if(self->is_use_rela) + { + self->rela.r_offset = self->r_offset; + self->rela.r_info = self->r_info; + self->rela.r_addend = self->r_addend; + return (void *)(&(self->rela)); + } + else + { + self->rel.r_offset = self->r_offset; + self->rel.r_info = self->r_info; + return (void *)(&(self->rel)); + } +} + +//ELF header checker +int xh_elf_check_elfheader(uintptr_t base_addr) +{ + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base_addr; + + //check magic + if(0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return XH_ERRNO_FORMAT; + + //check class (64/32) +#if defined(__LP64__) + if(ELFCLASS64 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT; +#else + if(ELFCLASS32 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT; +#endif + + //check endian (little/big) + if(ELFDATA2LSB != ehdr->e_ident[EI_DATA]) return XH_ERRNO_FORMAT; + + //check version + if(EV_CURRENT != ehdr->e_ident[EI_VERSION]) return XH_ERRNO_FORMAT; + + //check type + if(ET_EXEC != ehdr->e_type && ET_DYN != ehdr->e_type) return XH_ERRNO_FORMAT; + + //check machine +#if defined(__arm__) + if(EM_ARM != ehdr->e_machine) return XH_ERRNO_FORMAT; +#elif defined(__aarch64__) + if(EM_AARCH64 != ehdr->e_machine) return XH_ERRNO_FORMAT; +#elif defined(__i386__) + if(EM_386 != ehdr->e_machine) return XH_ERRNO_FORMAT; +#elif defined(__x86_64__) + if(EM_X86_64 != ehdr->e_machine) return XH_ERRNO_FORMAT; +#else + return XH_ERRNO_FORMAT; +#endif + + //check version + if(EV_CURRENT != ehdr->e_version) return XH_ERRNO_FORMAT; + + return 0; +} + +//ELF hash func +static uint32_t xh_elf_hash(const uint8_t *name) +{ + uint32_t h = 0, g; + + while (*name) { + h = (h << 4) + *name++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + + return h; +} + +//GNU hash func +static uint32_t xh_elf_gnu_hash(const uint8_t *name) +{ + uint32_t h = 5381; + + while(*name != 0) + { + h += (h << 5) + *name++; + } + return h; +} + +static ElfW(Phdr) *xh_elf_get_first_segment_by_type(xh_elf_t *self, ElfW(Word) type) +{ + ElfW(Phdr) *phdr; + + for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++) + { + if(phdr->p_type == type) + { + return phdr; + } + } + return NULL; +} + +static ElfW(Phdr) *xh_elf_get_first_segment_by_type_offset(xh_elf_t *self, ElfW(Word) type, ElfW(Off) offset) +{ + ElfW(Phdr) *phdr; + + for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++) + { + if(phdr->p_type == type && phdr->p_offset == offset) + { + return phdr; + } + } + return NULL; +} + +static int xh_elf_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + uint32_t hash = xh_elf_hash((uint8_t *)symbol); + const char *symbol_cur; + uint32_t i; + + for(i = self->bucket[hash % self->bucket_cnt]; 0 != i; i = self->chain[i]) + { + symbol_cur = self->strtab + self->symtab[i].st_name; + + if(0 == strcmp(symbol, symbol_cur)) + { + *symidx = i; + XH_LOG_INFO("found %s at symidx: %u (ELF_HASH)\n", symbol, *symidx); + return 0; + } + } + + return XH_ERRNO_NOTFND; +} + +static int xh_elf_gnu_hash_lookup_def(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + uint32_t hash = xh_elf_gnu_hash((uint8_t *)symbol); + + static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8; + size_t word = self->bloom[(hash / elfclass_bits) % self->bloom_sz]; + size_t mask = 0 + | (size_t)1 << (hash % elfclass_bits) + | (size_t)1 << ((hash >> self->bloom_shift) % elfclass_bits); + + //if at least one bit is not set, this symbol is surely missing + if((word & mask) != mask) return XH_ERRNO_NOTFND; + + //ignore STN_UNDEF + uint32_t i = self->bucket[hash % self->bucket_cnt]; + if(i < self->symoffset) return XH_ERRNO_NOTFND; + + //loop through the chain + while(1) + { + const char *symname = self->strtab + self->symtab[i].st_name; + const uint32_t symhash = self->chain[i - self->symoffset]; + + if((hash | (uint32_t)1) == (symhash | (uint32_t)1) && 0 == strcmp(symbol, symname)) + { + *symidx = i; + XH_LOG_INFO("found %s at symidx: %u (GNU_HASH DEF)\n", symbol, *symidx); + return 0; + } + + //chain ends with an element with the lowest bit set to 1 + if(symhash & (uint32_t)1) break; + + i++; + } + + return XH_ERRNO_NOTFND; +} + +static int xh_elf_gnu_hash_lookup_undef(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + uint32_t i; + + for(i = 0; i < self->symoffset; i++) + { + const char *symname = self->strtab + self->symtab[i].st_name; + if(0 == strcmp(symname, symbol)) + { + *symidx = i; + XH_LOG_INFO("found %s at symidx: %u (GNU_HASH UNDEF)\n", symbol, *symidx); + return 0; + } + } + return XH_ERRNO_NOTFND; +} + +static int xh_elf_gnu_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + if(0 == xh_elf_gnu_hash_lookup_def(self, symbol, symidx)) return 0; + if(0 == xh_elf_gnu_hash_lookup_undef(self, symbol, symidx)) return 0; + return XH_ERRNO_NOTFND; +} + +static int xh_elf_find_symidx_by_name(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + if(self->is_use_gnu_hash) + return xh_elf_gnu_hash_lookup(self, symbol, symidx); + else + return xh_elf_hash_lookup(self, symbol, symidx); +} + +static int xh_elf_replace_function(xh_elf_t *self, const char *symbol, ElfW(Addr) addr, void *new_func, void **old_func) +{ + void *old_addr; + unsigned int old_prot = 0; + unsigned int need_prot = PROT_READ | PROT_WRITE; + int r; + + //already replaced? + //here we assume that we always have read permission, is this a problem? + if(*(void **)addr == new_func) return 0; + + //get old prot + if(0 != (r = xh_util_get_addr_protect(addr, self->pathname, &old_prot))) + { + XH_LOG_ERROR("get addr prot failed. ret: %d", r); + return r; + } + + if(old_prot != need_prot) + { + //set new prot + if(0 != (r = xh_util_set_addr_protect(addr, need_prot))) + { + XH_LOG_ERROR("set addr prot failed. ret: %d", r); + return r; + } + } + + //save old func + old_addr = *(void **)addr; + if(NULL != old_func) *old_func = old_addr; + + //replace func + *(void **)addr = new_func; //segmentation fault sometimes + + if(old_prot != need_prot) + { + //restore the old prot + if(0 != (r = xh_util_set_addr_protect(addr, old_prot))) + { + XH_LOG_WARN("restore addr prot failed. ret: %d", r); + } + } + + //clear cache + xh_util_flush_instruction_cache(addr); + + XH_LOG_INFO("XH_HK_OK %p: %p -> %p %s %s\n", (void *)addr, old_addr, new_func, symbol, self->pathname); + return 0; +} + +static int xh_elf_check(xh_elf_t *self) +{ + if(0 == self->base_addr) + { + XH_LOG_ERROR("base_addr == 0\n"); + return 1; + } + if(0 == self->bias_addr) + { + XH_LOG_ERROR("bias_addr == 0\n"); + return 1; + } + if(NULL == self->ehdr) + { + XH_LOG_ERROR("ehdr == NULL\n"); + return 1; + } + if(NULL == self->phdr) + { + XH_LOG_ERROR("phdr == NULL\n"); + return 1; + } + if(NULL == self->strtab) + { + XH_LOG_ERROR("strtab == NULL\n"); + return 1; + } + if(NULL == self->symtab) + { + XH_LOG_ERROR("symtab == NULL\n"); + return 1; + } + if(NULL == self->bucket) + { + XH_LOG_ERROR("bucket == NULL\n"); + return 1; + } + if(NULL == self->chain) + { + XH_LOG_ERROR("chain == NULL\n"); + return 1; + } + if(1 == self->is_use_gnu_hash && NULL == self->bloom) + { + XH_LOG_ERROR("bloom == NULL\n"); + return 1; + } + + return 0; +} + +#if XH_ELF_DEBUG + +static void xh_elf_dump_elfheader(xh_elf_t *self) +{ + static char alpha_tab[17] = "0123456789ABCDEF"; + int i; + uint8_t ch; + char buff[EI_NIDENT * 3 + 1]; + + for(i = 0; i < EI_NIDENT; i++) + { + ch = self->ehdr->e_ident[i]; + buff[i * 3 + 0] = alpha_tab[(int)((ch >> 4) & 0x0F)]; + buff[i * 3 + 1] = alpha_tab[(int)(ch & 0x0F)]; + buff[i * 3 + 2] = ' '; + } + buff[EI_NIDENT * 3] = '\0'; + + XH_LOG_DEBUG("Elf Header:\n"); + XH_LOG_DEBUG(" Magic: %s\n", buff); + XH_LOG_DEBUG(" Class: %#x\n", self->ehdr->e_ident[EI_CLASS]); + XH_LOG_DEBUG(" Data: %#x\n", self->ehdr->e_ident[EI_DATA]); + XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_ident[EI_VERSION]); + XH_LOG_DEBUG(" OS/ABI: %#x\n", self->ehdr->e_ident[EI_OSABI]); + XH_LOG_DEBUG(" ABI Version: %#x\n", self->ehdr->e_ident[EI_ABIVERSION]); + XH_LOG_DEBUG(" Type: %#x\n", self->ehdr->e_type); + XH_LOG_DEBUG(" Machine: %#x\n", self->ehdr->e_machine); + XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_version); + XH_LOG_DEBUG(" Entry point address: %"XH_UTIL_FMT_X"\n", self->ehdr->e_entry); + XH_LOG_DEBUG(" Start of program headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_phoff); + XH_LOG_DEBUG(" Start of section headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_shoff); + XH_LOG_DEBUG(" Flags: %#x\n", self->ehdr->e_flags); + XH_LOG_DEBUG(" Size of this header: %u (bytes)\n", self->ehdr->e_ehsize); + XH_LOG_DEBUG(" Size of program headers: %u (bytes)\n", self->ehdr->e_phentsize); + XH_LOG_DEBUG(" Number of program headers: %u\n", self->ehdr->e_phnum); + XH_LOG_DEBUG(" Size of section headers: %u (bytes)\n", self->ehdr->e_shentsize); + XH_LOG_DEBUG(" Number of section headers: %u\n", self->ehdr->e_shnum); + XH_LOG_DEBUG(" Section header string table index: %u\n", self->ehdr->e_shstrndx); +} + +static void xh_elf_dump_programheader(xh_elf_t *self) +{ + ElfW(Phdr) *phdr = self->phdr; + size_t i; + + XH_LOG_DEBUG("Program Headers:\n"); + XH_LOG_DEBUG(" %-8s " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-8s " \ + "%-s\n", + "Type", + "Offset", + "VirtAddr", + "PhysAddr", + "FileSiz", + "MemSiz", + "Flg", + "Align"); + for(i = 0; i < self->ehdr->e_phnum; i++, phdr++) + { + XH_LOG_DEBUG(" %-8x " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%-8x " \ + "%"XH_UTIL_FMT_X"\n", + phdr->p_type, + phdr->p_offset, + phdr->p_vaddr, + phdr->p_paddr, + phdr->p_filesz, + phdr->p_memsz, + phdr->p_flags, + phdr->p_align); + } +} + +static void xh_elf_dump_dynamic(xh_elf_t *self) +{ + ElfW(Dyn) *dyn = self->dyn; + size_t dyn_cnt = (self->dyn_sz / sizeof(ElfW(Dyn))); + size_t i; + + XH_LOG_DEBUG("Dynamic section contains %zu entries:\n", dyn_cnt); + XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \ + "%s\n", + "Tag", + "Val"); + for(i = 0; i < dyn_cnt; i++, dyn++) + { + XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_X" " \ + "%-"XH_UTIL_FMT_X"\n", + dyn->d_tag, + dyn->d_un.d_val); + } +} + +static void xh_elf_dump_rel(xh_elf_t *self, const char *type, ElfW(Addr) rel_addr, ElfW(Word) rel_sz) +{ + ElfW(Rela) *rela; + ElfW(Rel) *rel; + ElfW(Word) cnt; + ElfW(Word) i; + ElfW(Sym) *sym; + + if(self->is_use_rela) + { + rela = (ElfW(Rela) *)(rel_addr); + cnt = rel_sz / sizeof(ElfW(Rela)); + } + else + { + rel = (ElfW(Rel) *)(rel_addr); + cnt = rel_sz / sizeof(ElfW(Rel)); + } + + XH_LOG_DEBUG("Relocation section '.rel%s%s' contains %u entries:\n", + (self->is_use_rela ? "a" : ""), type, cnt); + XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-8s " \ + "%-8s " \ + "%-8s " \ + "%s\n", + "Offset", + "Info", + "Type", + "Sym.Idx", + "Sym.Val", + "Sym.Name"); + const char *fmt = " %."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%.8x " \ + "%.8u " \ + "%.8x " \ + "%s\n"; + for(i = 0; i < cnt; i++) + { + if(self->is_use_rela) + { + sym = &(self->symtab[XH_ELF_R_SYM(rela[i].r_info)]); + XH_LOG_DEBUG(fmt, + rela[i].r_offset, + rela[i].r_info, + XH_ELF_R_TYPE(rela[i].r_info), + XH_ELF_R_SYM(rela[i].r_info), + sym->st_value, + self->strtab + sym->st_name); + } + else + { + sym = &(self->symtab[XH_ELF_R_SYM(rel[i].r_info)]); + XH_LOG_DEBUG(fmt, + rel[i].r_offset, + rel[i].r_info, + XH_ELF_R_TYPE(rel[i].r_info), + XH_ELF_R_SYM(rel[i].r_info), + sym->st_value, + self->strtab + sym->st_name); + } + } +} + +static void xh_elf_dump_symtab(xh_elf_t *self) +{ + if(self->is_use_gnu_hash) return; + + ElfW(Word) symtab_cnt = self->chain_cnt; + ElfW(Word) i; + + XH_LOG_DEBUG("Symbol table '.dynsym' contains %u entries:\n", symtab_cnt); + XH_LOG_DEBUG(" %-8s " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%s\n", + "Idx", + "Value", + "Name"); + for(i = 0; i < symtab_cnt; i++) + { + XH_LOG_DEBUG(" %-8u " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%s\n", + i, + self->symtab[i].st_value, + self->strtab + self->symtab[i].st_name); + } +} + +static void xh_elf_dump(xh_elf_t *self) +{ + if(xh_log_priority < ANDROID_LOG_DEBUG) return; + + XH_LOG_DEBUG("Elf Pathname: %s\n", self->pathname); + XH_LOG_DEBUG("Elf bias addr: %p\n", (void *)self->bias_addr); + xh_elf_dump_elfheader(self); + xh_elf_dump_programheader(self); + xh_elf_dump_dynamic(self); + xh_elf_dump_rel(self, ".plt", self->relplt, self->relplt_sz); + xh_elf_dump_rel(self, ".dyn", self->reldyn, self->reldyn_sz); + xh_elf_dump_symtab(self); +} + +#endif + +int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname) +{ + if(0 == base_addr || NULL == pathname) return XH_ERRNO_INVAL; + + //always reset + memset(self, 0, sizeof(xh_elf_t)); + + self->pathname = pathname; + self->base_addr = (ElfW(Addr))base_addr; + self->ehdr = (ElfW(Ehdr) *)base_addr; + self->phdr = (ElfW(Phdr) *)(base_addr + self->ehdr->e_phoff); //segmentation fault sometimes + + //find the first load-segment with offset 0 + ElfW(Phdr) *phdr0 = xh_elf_get_first_segment_by_type_offset(self, PT_LOAD, 0); + if(NULL == phdr0) + { + XH_LOG_ERROR("Can NOT found the first load segment. %s", pathname); + return XH_ERRNO_FORMAT; + } + +#if XH_ELF_DEBUG + if(0 != phdr0->p_vaddr) + XH_LOG_DEBUG("first load-segment vaddr NOT 0 (vaddr: %p). %s", + (void *)(phdr0->p_vaddr), pathname); +#endif + + //save load bias addr + if(self->base_addr < phdr0->p_vaddr) return XH_ERRNO_FORMAT; + self->bias_addr = self->base_addr - phdr0->p_vaddr; + + //find dynamic-segment + ElfW(Phdr) *dhdr = xh_elf_get_first_segment_by_type(self, PT_DYNAMIC); + if(NULL == dhdr) + { + XH_LOG_ERROR("Can NOT found dynamic segment. %s", pathname); + return XH_ERRNO_FORMAT; + } + + //parse dynamic-segment + self->dyn = (ElfW(Dyn) *)(self->bias_addr + dhdr->p_vaddr); + self->dyn_sz = dhdr->p_memsz; + ElfW(Dyn) *dyn = self->dyn; + ElfW(Dyn) *dyn_end = self->dyn + (self->dyn_sz / sizeof(ElfW(Dyn))); + uint32_t *raw; + for(; dyn < dyn_end; dyn++) + { + switch(dyn->d_tag) //segmentation fault sometimes + { + case DT_NULL: + //the end of the dynamic-section + dyn = dyn_end; + break; + case DT_STRTAB: + { + self->strtab = (const char *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->strtab) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_SYMTAB: + { + self->symtab = (ElfW(Sym) *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->symtab) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_PLTREL: + //use rel or rela? + self->is_use_rela = (dyn->d_un.d_val == DT_RELA ? 1 : 0); + break; + case DT_JMPREL: + { + self->relplt = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->relplt) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_PLTRELSZ: + self->relplt_sz = dyn->d_un.d_val; + break; + case DT_REL: + case DT_RELA: + { + self->reldyn = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->reldyn) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_RELSZ: + case DT_RELASZ: + self->reldyn_sz = dyn->d_un.d_val; + break; + case DT_ANDROID_REL: + case DT_ANDROID_RELA: + { + self->relandroid = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->relandroid) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_ANDROID_RELSZ: + case DT_ANDROID_RELASZ: + self->relandroid_sz = dyn->d_un.d_val; + break; + case DT_HASH: + { + //ignore DT_HASH when ELF contains DT_GNU_HASH hash table + if(1 == self->is_use_gnu_hash) continue; + + raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT; + self->bucket_cnt = raw[0]; + self->chain_cnt = raw[1]; + self->bucket = &raw[2]; + self->chain = &(self->bucket[self->bucket_cnt]); + break; + } + case DT_GNU_HASH: + { + raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT; + self->bucket_cnt = raw[0]; + self->symoffset = raw[1]; + self->bloom_sz = raw[2]; + self->bloom_shift = raw[3]; + self->bloom = (ElfW(Addr) *)(&raw[4]); + self->bucket = (uint32_t *)(&(self->bloom[self->bloom_sz])); + self->chain = (uint32_t *)(&(self->bucket[self->bucket_cnt])); + self->is_use_gnu_hash = 1; + break; + } + default: + break; + } + } + + //check android rel/rela + if(0 != self->relandroid) + { + const char *rel = (const char *)self->relandroid; + if(self->relandroid_sz < 4 || + rel[0] != 'A' || + rel[1] != 'P' || + rel[2] != 'S' || + rel[3] != '2') + { + XH_LOG_ERROR("android rel/rela format error\n"); + return XH_ERRNO_FORMAT; + } + + self->relandroid += 4; + self->relandroid_sz -= 4; + } + + //check elf info + if(0 != xh_elf_check(self)) + { + XH_LOG_ERROR("elf init check failed. %s", pathname); + return XH_ERRNO_FORMAT; + } + +#if XH_ELF_DEBUG + xh_elf_dump(self); +#endif + + XH_LOG_INFO("init OK: %s (%s %s PLT:%u DYN:%u ANDROID:%u)\n", self->pathname, + self->is_use_rela ? "RELA" : "REL", + self->is_use_gnu_hash ? "GNU_HASH" : "ELF_HASH", + self->relplt_sz, self->reldyn_sz, self->relandroid_sz); + + return 0; +} + +static int xh_elf_find_and_replace_func(xh_elf_t *self, const char *section, + int is_plt, const char *symbol, + void *new_func, void **old_func, + uint32_t symidx, void *rel_common, + int *found) +{ + ElfW(Rela) *rela; + ElfW(Rel) *rel; + ElfW(Addr) r_offset; + size_t r_info; + size_t r_sym; + size_t r_type; + ElfW(Addr) addr; + int r; + + if(NULL != found) *found = 0; + + if(self->is_use_rela) + { + rela = (ElfW(Rela) *)rel_common; + r_info = rela->r_info; + r_offset = rela->r_offset; + } + else + { + rel = (ElfW(Rel) *)rel_common; + r_info = rel->r_info; + r_offset = rel->r_offset; + } + + //check sym + r_sym = XH_ELF_R_SYM(r_info); + if(r_sym != symidx) return 0; + + //check type + r_type = XH_ELF_R_TYPE(r_info); + if(is_plt && r_type != XH_ELF_R_GENERIC_JUMP_SLOT) return 0; + if(!is_plt && (r_type != XH_ELF_R_GENERIC_GLOB_DAT && r_type != XH_ELF_R_GENERIC_ABS)) return 0; + + //we found it + XH_LOG_INFO("found %s at %s offset: %p\n", symbol, section, (void *)r_offset); + if(NULL != found) *found = 1; + + //do replace + addr = self->bias_addr + r_offset; + if(addr < self->base_addr) return XH_ERRNO_FORMAT; + if(0 != (r = xh_elf_replace_function(self, symbol, addr, new_func, old_func))) + { + XH_LOG_ERROR("replace function failed: %s at %s\n", symbol, section); + return r; + } + + return 0; +} + +int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func) +{ + uint32_t symidx; + void *rel_common; + xh_elf_plain_reloc_iterator_t plain_iter; + xh_elf_packed_reloc_iterator_t packed_iter; + int found; + int r; + + if(NULL == self->pathname) + { + XH_LOG_ERROR("not inited\n"); + return XH_ERRNO_ELFINIT; //not inited? + } + + if(NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL; + + XH_LOG_INFO("hooking %s in %s\n", symbol, self->pathname); + + //find symbol index by symbol name + if(0 != (r = xh_elf_find_symidx_by_name(self, symbol, &symidx))) return 0; + + //replace for .rel(a).plt + if(0 != self->relplt) + { + xh_elf_plain_reloc_iterator_init(&plain_iter, self->relplt, self->relplt_sz, self->is_use_rela); + while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter))) + { + if(0 != (r = xh_elf_find_and_replace_func(self, + (self->is_use_rela ? ".rela.plt" : ".rel.plt"), 1, + symbol, new_func, old_func, + symidx, rel_common, &found))) return r; + if(found) break; + } + } + + //replace for .rel(a).dyn + if(0 != self->reldyn) + { + xh_elf_plain_reloc_iterator_init(&plain_iter, self->reldyn, self->reldyn_sz, self->is_use_rela); + while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter))) + { + if(0 != (r = xh_elf_find_and_replace_func(self, + (self->is_use_rela ? ".rela.dyn" : ".rel.dyn"), 0, + symbol, new_func, old_func, + symidx, rel_common, NULL))) return r; + } + } + + //replace for .rel(a).android + if(0 != self->relandroid) + { + xh_elf_packed_reloc_iterator_init(&packed_iter, self->relandroid, self->relandroid_sz, self->is_use_rela); + while(NULL != (rel_common = xh_elf_packed_reloc_iterator_next(&packed_iter))) + { + if(0 != (r = xh_elf_find_and_replace_func(self, + (self->is_use_rela ? ".rela.android" : ".rel.android"), 0, + symbol, new_func, old_func, + symidx, rel_common, NULL))) return r; + } + } + + return 0; +} diff --git a/FCLauncher/src/main/jni/xhook/xh_jni.c b/FCLauncher/src/main/jni/xhook/xh_jni.c new file mode 100644 index 00000000..6acf7e61 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/xh_jni.c @@ -0,0 +1,59 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#include +#include + +#define JNI_API_DEF(f) Java_com_qiyi_xhook_NativeHandler_##f + +JNIEXPORT jint JNI_API_DEF(refresh)(JNIEnv *env, jobject obj, jboolean async) +{ + (void)env; + (void)obj; + + return xhook_refresh(async ? 1 : 0); +} + +JNIEXPORT void JNI_API_DEF(clear)(JNIEnv *env, jobject obj) +{ + (void)env; + (void)obj; + + xhook_clear(); +} + +JNIEXPORT void JNI_API_DEF(enableDebug)(JNIEnv *env, jobject obj, jboolean flag) +{ + (void)env; + (void)obj; + + xhook_enable_debug(flag ? 1 : 0); +} + +JNIEXPORT void JNI_API_DEF(enableSigSegvProtection)(JNIEnv *env, jobject obj, jboolean flag) +{ + (void)env; + (void)obj; + + xhook_enable_sigsegv_protection(flag ? 1 : 0); +} diff --git a/FCLauncher/src/main/jni/xhook/xh_log.c b/FCLauncher/src/main/jni/xhook/xh_log.c new file mode 100644 index 00000000..efa5549b --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/xh_log.c @@ -0,0 +1,27 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#include +#include + +android_LogPriority xh_log_priority = ANDROID_LOG_WARN; diff --git a/FCLauncher/src/main/jni/xhook/xh_util.c b/FCLauncher/src/main/jni/xhook/xh_util.c new file mode 100644 index 00000000..919ae8ea --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/xh_util.c @@ -0,0 +1,121 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_START(addr) ((addr) & PAGE_MASK) +#define PAGE_END(addr) (PAGE_START(addr + sizeof(uintptr_t) - 1) + PAGE_SIZE) +#define PAGE_COVER(addr) (PAGE_END(addr) - PAGE_START(addr)) + +int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot) +{ + uintptr_t start_addr = addr; + uintptr_t end_addr = addr + len; + FILE *fp; + char line[512]; + uintptr_t start, end; + char perm[5]; + int load0 = 1; + int found_all = 0; + + *prot = 0; + + if(NULL == (fp = fopen("/proc/self/maps", "r"))) return XH_ERRNO_BADMAPS; + + while(fgets(line, sizeof(line), fp)) + { + if(NULL != pathname) + if(NULL == strstr(line, pathname)) continue; + + if(sscanf(line, "%"PRIxPTR"-%"PRIxPTR" %4s ", &start, &end, perm) != 3) continue; + + if(perm[3] != 'p') continue; + + if(start_addr >= start && start_addr < end) + { + if(load0) + { + //first load segment + if(perm[0] == 'r') *prot |= PROT_READ; + if(perm[1] == 'w') *prot |= PROT_WRITE; + if(perm[2] == 'x') *prot |= PROT_EXEC; + load0 = 0; + } + else + { + //others + if(perm[0] != 'r') *prot &= ~PROT_READ; + if(perm[1] != 'w') *prot &= ~PROT_WRITE; + if(perm[2] != 'x') *prot &= ~PROT_EXEC; + } + + if(end_addr <= end) + { + found_all = 1; + break; //finished + } + else + { + start_addr = end; //try to find the next load segment + } + } + } + + fclose(fp); + + if(!found_all) return XH_ERRNO_SEGVERR; + + return 0; +} + +int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot) +{ + return xh_util_get_mem_protect(addr, sizeof(addr), pathname, prot); +} + +int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot) +{ + if(0 != mprotect((void *)PAGE_START(addr), PAGE_COVER(addr), (int)prot)) + return 0 == errno ? XH_ERRNO_UNKNOWN : errno; + + return 0; +} + +void xh_util_flush_instruction_cache(uintptr_t addr) +{ + __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr)); +} diff --git a/FCLauncher/src/main/jni/xhook/xh_version.c b/FCLauncher/src/main/jni/xhook/xh_version.c new file mode 100644 index 00000000..b2e3222f --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/xh_version.c @@ -0,0 +1,66 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#include + +#define XH_VERSION_MAJOR 1 +#define XH_VERSION_MINOR 2 +#define XH_VERSION_EXTRA 0 + +#define XH_VERSION ((XH_VERSION_MAJOR << 16) | (XH_VERSION_MINOR << 8) | (XH_VERSION_EXTRA)) + +#define XH_VERSION_TO_STR_HELPER(x) #x +#define XH_VERSION_TO_STR(x) XH_VERSION_TO_STR_HELPER(x) + +#define XH_VERSION_STR XH_VERSION_TO_STR(XH_VERSION_MAJOR) "." \ + XH_VERSION_TO_STR(XH_VERSION_MINOR) "." \ + XH_VERSION_TO_STR(XH_VERSION_EXTRA) + +#if defined(__arm__) +#define XH_VERSION_ARCH "arm" +#elif defined(__aarch64__) +#define XH_VERSION_ARCH "aarch64" +#elif defined(__i386__) +#define XH_VERSION_ARCH "x86" +#elif defined(__x86_64__) +#define XH_VERSION_ARCH "x86_64" +#else +#define XH_VERSION_ARCH "unknown" +#endif + +#define XH_VERSION_STR_FULL "libxhook "XH_VERSION_STR" ("XH_VERSION_ARCH")" + +unsigned int xh_version() +{ + return XH_VERSION; +} + +const char *xh_version_str() +{ + return XH_VERSION_STR; +} + +const char *xh_version_str_full() +{ + return XH_VERSION_STR_FULL; +} diff --git a/FCLauncher/src/main/jni/xhook/xhook.c b/FCLauncher/src/main/jni/xhook/xhook.c new file mode 100644 index 00000000..9d4b7765 --- /dev/null +++ b/FCLauncher/src/main/jni/xhook/xhook.c @@ -0,0 +1,56 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// 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: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// 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. +// + +// Created by caikelun on 2018-04-11. + +#include +#include + +int xhook_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func) +{ + return xh_core_register(pathname_regex_str, symbol, new_func, old_func); +} + +int xhook_ignore(const char *pathname_regex_str, const char *symbol) +{ + return xh_core_ignore(pathname_regex_str, symbol); +} + +int xhook_refresh(int async) +{ + return xh_core_refresh(async); +} + +void xhook_clear() +{ + return xh_core_clear(); +} + +void xhook_enable_debug(int flag) +{ + return xh_core_enable_debug(flag); +} + +void xhook_enable_sigsegv_protection(int flag) +{ + return xh_core_enable_sigsegv_protection(flag); +} diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/libgl4es.so b/FCLauncher/src/main/jniLibs/arm64-v8a/libgl4es.so new file mode 100644 index 00000000..8d5e2e32 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/libgl4es.so differ diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/libgl4es_egl.so b/FCLauncher/src/main/jniLibs/arm64-v8a/libgl4es_egl.so new file mode 100644 index 00000000..1e2d30d7 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/libgl4es_egl.so differ diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl.so b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl.so new file mode 100644 index 00000000..5efe4896 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl.so differ diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl2.so b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl2.so new file mode 100644 index 00000000..d344dd16 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl2.so differ diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_opengl.so b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_opengl.so new file mode 100644 index 00000000..4e863599 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_opengl.so differ diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_stb.so b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_stb.so new file mode 100644 index 00000000..0a10e2ef Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_stb.so differ diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_tinyfd.so b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_tinyfd.so new file mode 100644 index 00000000..417435e5 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/liblwjgl_tinyfd.so differ diff --git a/FCLauncher/src/main/jniLibs/arm64-v8a/libopenal.so b/FCLauncher/src/main/jniLibs/arm64-v8a/libopenal.so new file mode 100644 index 00000000..7b8c78ba Binary files /dev/null and b/FCLauncher/src/main/jniLibs/arm64-v8a/libopenal.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/libgl4es.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/libgl4es.so new file mode 100644 index 00000000..48f857ed Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/libgl4es.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/libgl4es_egl.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/libgl4es_egl.so new file mode 100644 index 00000000..1ef5934d Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/libgl4es_egl.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl.so new file mode 100644 index 00000000..369cda8c Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl2.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl2.so new file mode 100644 index 00000000..8459d799 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl2.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_opengl.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_opengl.so new file mode 100644 index 00000000..011fc9ea Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_opengl.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_stb.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_stb.so new file mode 100644 index 00000000..cebe102e Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_stb.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_tinyfd.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_tinyfd.so new file mode 100644 index 00000000..86207193 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/liblwjgl_tinyfd.so differ diff --git a/FCLauncher/src/main/jniLibs/armeabi-v7a/libopenal.so b/FCLauncher/src/main/jniLibs/armeabi-v7a/libopenal.so new file mode 100644 index 00000000..e1454930 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/armeabi-v7a/libopenal.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/libgl4es.so b/FCLauncher/src/main/jniLibs/x86/libgl4es.so new file mode 100644 index 00000000..435d3bc6 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/libgl4es.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/libgl4es_egl.so b/FCLauncher/src/main/jniLibs/x86/libgl4es_egl.so new file mode 100644 index 00000000..82de2849 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/libgl4es_egl.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/liblwjgl.so b/FCLauncher/src/main/jniLibs/x86/liblwjgl.so new file mode 100644 index 00000000..c10c7aed Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/liblwjgl.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/liblwjgl2.so b/FCLauncher/src/main/jniLibs/x86/liblwjgl2.so new file mode 100644 index 00000000..beeff357 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/liblwjgl2.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/liblwjgl_opengl.so b/FCLauncher/src/main/jniLibs/x86/liblwjgl_opengl.so new file mode 100644 index 00000000..c7d6d6fa Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/liblwjgl_opengl.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/liblwjgl_stb.so b/FCLauncher/src/main/jniLibs/x86/liblwjgl_stb.so new file mode 100644 index 00000000..502422d9 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/liblwjgl_stb.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/liblwjgl_tinyfd.so b/FCLauncher/src/main/jniLibs/x86/liblwjgl_tinyfd.so new file mode 100644 index 00000000..58d3eccc Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/liblwjgl_tinyfd.so differ diff --git a/FCLauncher/src/main/jniLibs/x86/libopenal.so b/FCLauncher/src/main/jniLibs/x86/libopenal.so new file mode 100644 index 00000000..b1d396d8 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86/libopenal.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/libgl4es.so b/FCLauncher/src/main/jniLibs/x86_64/libgl4es.so new file mode 100644 index 00000000..5848ade3 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/libgl4es.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/libgl4es_egl.so b/FCLauncher/src/main/jniLibs/x86_64/libgl4es_egl.so new file mode 100644 index 00000000..95094838 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/libgl4es_egl.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/liblwjgl.so b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl.so new file mode 100644 index 00000000..9e32b2c8 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/liblwjgl2.so b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl2.so new file mode 100644 index 00000000..d1139879 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl2.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_opengl.so b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_opengl.so new file mode 100644 index 00000000..bf488e54 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_opengl.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_stb.so b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_stb.so new file mode 100644 index 00000000..fe956f34 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_stb.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_tinyfd.so b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_tinyfd.so new file mode 100644 index 00000000..1d6ede72 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/liblwjgl_tinyfd.so differ diff --git a/FCLauncher/src/main/jniLibs/x86_64/libopenal.so b/FCLauncher/src/main/jniLibs/x86_64/libopenal.so new file mode 100644 index 00000000..1b59e892 Binary files /dev/null and b/FCLauncher/src/main/jniLibs/x86_64/libopenal.so differ diff --git a/FCLauncher/src/test/java/com/tungsten/fclauncher/ExampleUnitTest.java b/FCLauncher/src/test/java/com/tungsten/fclauncher/ExampleUnitTest.java new file mode 100644 index 00000000..9b77a432 --- /dev/null +++ b/FCLauncher/src/test/java/com/tungsten/fclauncher/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.tungsten.fclauncher; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..e62ec04c --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 00000000..97e582b7 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# FoldCraftLauncher + Fold Craft Launcher, an Android Minecraft : Java Edition launcher. diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..90f90082 --- /dev/null +++ b/build.gradle @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '7.3.0' apply false + id 'com.android.library' version '7.3.0' apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..ac74161c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true +android.injected.testOnly=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..72f31252 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Oct 11 19:03:04 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..4f906e0c --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/private_key.pepk b/private_key.pepk new file mode 100644 index 00000000..2bffcd62 Binary files /dev/null and b/private_key.pepk differ diff --git a/release/output-metadata.json b/release/output-metadata.json new file mode 100644 index 00000000..b6a37cbd --- /dev/null +++ b/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.tungsten.fcl", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "FCL-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..73279671 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "Fold Craft Launcher" +include ':FCL' +include ':FCLCore' +include ':FCLauncher'