├── .github └── workflows │ ├── gradle.yml │ └── publish.yml ├── .gitignore ├── HEADER ├── LICENSE ├── README.md ├── TODO.md ├── base ├── build.gradle └── src │ └── main │ └── java │ └── me │ └── lambdaurora │ └── mcpatcherpatcher │ ├── BiomeUtils.java │ ├── Closeable.java │ ├── ErrorType.java │ ├── Identifiers.java │ ├── MCPatcherPatcher.java │ ├── MCPatcherPatcherApplication.java │ ├── ResourceType.java │ ├── converter │ ├── CETConverter.java │ ├── Converter.java │ ├── RETConverter.java │ └── SkyConverter.java │ ├── fs │ ├── FileAccessor.java │ ├── ResourceAccessor.java │ ├── ZipAccessor.java │ └── ZipOutputAccessor.java │ ├── image │ ├── BasicBufferedImage.java │ ├── BasicImage.java │ ├── BufferedImageProvider.java │ └── ImageProvider.java │ └── mcpatcher │ ├── MCPatcherParser.java │ └── RandomEntityRule.java ├── build.gradle ├── fabric ├── build.gradle └── src │ └── main │ ├── java │ └── me │ │ └── lambdaurora │ │ └── mcpatcherpatcher │ │ └── fabric │ │ ├── MCPatcherPatcherFabric.java │ │ ├── fs │ │ └── ResourceManagerAccessor.java │ │ ├── image │ │ ├── BasicNativeImage.java │ │ └── NativeImageProvider.java │ │ ├── mixin │ │ ├── ReloadableResourceManagerImplMixin.java │ │ └── ZipResourcePackMixin.java │ │ ├── resource │ │ └── MCPPResourcePack.java │ │ └── util │ │ └── IdentifierUtils.java │ └── resources │ ├── fabric.mod.json │ └── mcpatcherpatcher.mixins.json ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Gradle 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Set up JDK 8 12 | uses: actions/setup-java@v1 13 | with: 14 | java-version: 8 15 | - name: Grant execute permission for gradlew 16 | run: chmod +x gradlew 17 | - name: Build with Gradle 18 | run: ./gradlew shadowRemapJar 19 | - name: Upload build artifacts 20 | uses: actions/upload-artifact@v1 21 | with: 22 | name: build-artifacts 23 | path: fabric/build/libs -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout sources 13 | uses: actions/checkout@v2 14 | - name: Set up JDK 8 15 | uses: actions/setup-java@v1 16 | with: 17 | java-version: 8 18 | - name: Grant execute permission for gradlew 19 | run: chmod +x gradlew 20 | - name: Upload assets to CurseForge 21 | run: ./gradlew shadowRemapJar 22 | env: 23 | BUILD_RELEASE: ${{ github.event.prerelease == false }} 24 | - name: Upload assets to GitHub 25 | uses: AButler/upload-release-assets@v2.0 26 | with: 27 | files: 'fabric/build/libs/*;LICENSE' 28 | repo-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # LambdAurora's ignore file 3 | # 4 | # v0.13 5 | 6 | # JetBrains 7 | .idea/ 8 | *.iml 9 | *.ipr 10 | *.iws 11 | ## Intellij IDEA 12 | out/ 13 | ## CLion 14 | cmake-build-debug*/ 15 | cmake-build-release*/ 16 | ## Eclipse 17 | eclipse 18 | *.launch 19 | .settings 20 | .metadata 21 | .classpath 22 | .project 23 | ## Visual Studio 24 | .vs/ 25 | CMakeSettings.json 26 | 27 | # Build system 28 | ## Cargo 29 | Cargo.lock 30 | ## CMake 31 | CMakeCache.txt 32 | CMakeFiles/ 33 | ## Gradle 34 | .gradle/ 35 | ## Node.JS 36 | node_modules/ 37 | 38 | # Editors 39 | ## VSCode 40 | .vscode/ 41 | 42 | # Logging 43 | logs/ 44 | 45 | # Languages 46 | ## Java 47 | classes/ 48 | ## Python 49 | __pycache__/ 50 | ## Rust 51 | **/*.rs.bk 52 | 53 | # OS 54 | ## Windows 55 | desktop.ini 56 | 57 | # File types 58 | *.dll 59 | *.so 60 | *.dylib 61 | *.lib 62 | lib*.a 63 | *.png~ 64 | 65 | # Common 66 | bin/ 67 | build/ 68 | dist/ 69 | run/ 70 | target/ 71 | -------------------------------------------------------------------------------- /HEADER: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 LambdAurora 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU Lesser General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU Lesser General Public License for more details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see . -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCPatcherPatcher 2 | 3 | ![Java 8](https://img.shields.io/badge/language-Java%208-9B599A.svg?style=flat-square) 4 | [![GitHub license](https://img.shields.io/github/license/LambdAurora/MCPatcherPatcher?style=flat-square)](https://raw.githubusercontent.com/LambdAurora/MCPatcherPatcher/master/LICENSE) 5 | 6 | A ~~cursed~~ resource pack converter from MCPatcher/OptiFine format to newer and alternative formats. 7 | 8 | # Use 9 | 10 | This project can be utilized in 2 ways: 11 | - Fabric Mod - This method converts the resource pack during runtime. This is the easiest, and only requires the jar to be put in the mods folder. It will not output any files. 12 | - Standalone Jar - This will output the converted files, so that you can modify them. 13 | 14 | This project does not have the ability to display the resource packs, it simply just to convert them into formats for other Fabric mods. 15 | The following list is all the mods that MCPatcherPatcher can convert to. 16 | - [FabricSkyboxes](https://www.curseforge.com/minecraft/mc-mods/fabricskyboxes) - Adds Skybox support. 17 | - [Varied Mob Textures](https://www.curseforge.com/minecraft/mc-mods/varied-mob-textures) - Adds CET (custom entity texture) and RET (random entity texture) support. 18 | 19 | At the time of writing, this project does not have any releases and only works as a Fabric Mod. If you would like to use this, follow the instructions below. 20 | 21 | # Build 22 | 23 | 1. Clone this using `git clone` or Download and Extract the ZIP via GitHub. 24 | 2. Make sure your current folder is MCPatcherPatcher, if you cloned the repository `cd MCPatcherPatcher`. 25 | 3. Build the binary using `./gradlew shadowRemapJar` (macOS/Linux) or `gradlew shadowRemapJar` (Windows). 26 | 4. Inside the MCPatcherPatcher folder, the output jar will be located at `fabric/build/libs`. 27 | 28 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # MCPatcherPatcher TODO 2 | 3 | - Add CIT conversion to [INM](https://github.com/Linguardium/ItemNbtModels) format. 4 | - Finish CET conversion to [VMT](https://github.com/Digifox03/variedMobs) format. 5 | - Make it a Fabric mod for runtime conversion. 6 | -------------------------------------------------------------------------------- /base/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | id 'application' 5 | } 6 | 7 | version = project.version 8 | archivesBaseName = project.archives_base_name 9 | mainClassName = 'me.lambdaurora.mcpatcherpatcher.MCPatcherPatcherApplication' 10 | 11 | configurations { 12 | api.extendsFrom shadowInclude 13 | } 14 | 15 | dependencies { 16 | shadowInclude("org.aperlambda:lambdajcommon:1.8.0") { 17 | exclude group: 'com.google.code.gson' 18 | exclude group: 'com.google.guava' 19 | } 20 | 21 | shadowInclude("com.google.code.gson:gson:2.8.6") 22 | } 23 | 24 | java { 25 | sourceCompatibility = JavaVersion.VERSION_1_8 26 | targetCompatibility = JavaVersion.VERSION_1_8 27 | 28 | withSourcesJar() 29 | } 30 | 31 | jar { 32 | from "../LICENSE" 33 | manifest { 34 | attributes( 35 | 'Main-Class': mainClassName 36 | ) 37 | } 38 | } 39 | 40 | shadowJar { 41 | configurations = [project.configurations.shadowInclude] 42 | } 43 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/BiomeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher; 19 | 20 | import org.aperlambda.lambdacommon.Identifier; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * Represents biome-related utilities. 28 | *

29 | * This only exists to fix legacy formatting from MCPatcher/OptiFine. 30 | * 31 | * @author LambdAurora 32 | * @version 1.0.0 33 | * @since 1.0.0 34 | */ 35 | public final class BiomeUtils 36 | { 37 | private static final Map BIOMES = new HashMap<>(); 38 | 39 | private BiomeUtils() 40 | { 41 | throw new UnsupportedOperationException("BiomeUtils only contains static definitions"); 42 | } 43 | 44 | public static @NotNull Identifier parseBiome(@NotNull String biome) 45 | { 46 | try { 47 | return new Identifier(biome); // Congrats, it's valid! 48 | } catch (IllegalArgumentException e) { 49 | // Oh shit, it's the obsolete format, STOP HOLDING TO THE PAST 50 | Identifier biomeId = BIOMES.get(biome); 51 | if (biomeId == null) { 52 | String name = biome.replace("+", ""); 53 | if (biome.endsWith("+")) 54 | name = "modified_" + name; 55 | 56 | int i = 0; 57 | while (i < name.length()) { 58 | char c = name.charAt(i); 59 | if (Character.isUpperCase(c)) { 60 | String prefix = "_"; 61 | if (i == 0) 62 | prefix = ""; 63 | String begin = name.substring(0, i); 64 | name = begin + prefix + Character.toLowerCase(c) + name.substring(i + 1); 65 | } 66 | i++; 67 | } 68 | 69 | biomeId = Identifiers.mc(name); 70 | } 71 | return biomeId; 72 | } 73 | } 74 | 75 | static { 76 | BIOMES.put("Jungle", Identifiers.mc("jungle")); 77 | BIOMES.put("JungleHills", Identifiers.mc("jungle_hills")); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/Closeable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher; 19 | 20 | public interface Closeable 21 | { 22 | void close(); 23 | } 24 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/ErrorType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher; 19 | 20 | /** 21 | * Represents error types. 22 | * 23 | * @author LambdAurora 24 | * @version 1.0.0 25 | * @since 1.0.0 26 | */ 27 | // @TODO make it more useful 28 | public enum ErrorType 29 | { 30 | INPUTSTREAM_IO, 31 | PROPERTIES_READ 32 | } 33 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/Identifiers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher; 19 | 20 | import org.aperlambda.lambdacommon.Identifier; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | /** 24 | * Represents an utility class for Identifiers. 25 | * 26 | * @author LambdAurora 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | public final class Identifiers 31 | { 32 | private Identifiers() 33 | { 34 | throw new UnsupportedOperationException("Identifiers only contains static definitions."); 35 | } 36 | 37 | /** 38 | * Returns an identifier in the Minecraft namespace. 39 | * 40 | * @param name The name. 41 | * @return The identifier. 42 | */ 43 | public static @NotNull Identifier mc(@NotNull String name) 44 | { 45 | return new Identifier("minecraft", name); 46 | } 47 | 48 | public static @NotNull Identifier parseIdentifier(@NotNull String id) 49 | { 50 | try { 51 | return new Identifier(id); 52 | } catch (IllegalArgumentException e) { 53 | return mc(id); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/MCPatcherPatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.converter.CETConverter; 21 | import me.lambdaurora.mcpatcherpatcher.converter.Converter; 22 | import me.lambdaurora.mcpatcherpatcher.converter.RETConverter; 23 | import me.lambdaurora.mcpatcherpatcher.converter.SkyConverter; 24 | import me.lambdaurora.mcpatcherpatcher.fs.ResourceAccessor; 25 | import me.lambdaurora.mcpatcherpatcher.fs.ZipAccessor; 26 | import me.lambdaurora.mcpatcherpatcher.fs.ZipOutputAccessor; 27 | import me.lambdaurora.mcpatcherpatcher.image.ImageProvider; 28 | import org.jetbrains.annotations.NotNull; 29 | 30 | import java.io.File; 31 | import java.io.FileOutputStream; 32 | import java.io.IOException; 33 | import java.util.ArrayList; 34 | import java.util.List; 35 | import java.util.function.BiFunction; 36 | import java.util.zip.ZipOutputStream; 37 | 38 | public class MCPatcherPatcher 39 | { 40 | private final List> converters = new ArrayList<>(); 41 | private final ImageProvider imageProvider; 42 | 43 | public MCPatcherPatcher(@NotNull ImageProvider imageProvider) 44 | { 45 | this.imageProvider = imageProvider; 46 | this.init(); 47 | } 48 | 49 | private void init() 50 | { 51 | this.converters.add(CETConverter::new); 52 | this.converters.add(RETConverter::new); 53 | this.converters.add(SkyConverter::new); 54 | } 55 | 56 | public void convert(@NotNull ResourceAccessor input, @NotNull ResourceAccessor output) 57 | { 58 | input.copy(output, "pack.mcmeta"); 59 | input.copy(output, "pack.png"); 60 | this.converters.forEach(f -> { 61 | Converter converter = f.apply(input, output); 62 | System.out.println("Applying " + converter.getName() + " conversion."); 63 | converter.convert(imageProvider); 64 | if (converter instanceof Closeable) 65 | ((Closeable) converter).close(); 66 | }); 67 | } 68 | 69 | public void convert(@NotNull File inputFile, @NotNull File outputFile) throws IOException 70 | { 71 | if (!inputFile.exists()) 72 | System.out.println("Input File does not exist!"); 73 | ResourceAccessor input; 74 | try { 75 | input = new ZipAccessor(inputFile); 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | return; 79 | } 80 | 81 | ResourceAccessor out; 82 | ZipOutputStream zipOutputStream; 83 | try { 84 | zipOutputStream = new ZipOutputStream(new FileOutputStream(outputFile)); 85 | out = new ZipOutputAccessor(zipOutputStream); 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | return; 89 | } 90 | 91 | this.convert(input, out); 92 | 93 | zipOutputStream.close(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/MCPatcherPatcherApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.image.BufferedImageProvider; 21 | 22 | import javax.swing.*; 23 | import javax.swing.border.LineBorder; 24 | import java.awt.*; 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.io.OutputStream; 28 | import java.io.PrintStream; 29 | import java.util.ArrayList; 30 | import java.util.Arrays; 31 | import java.util.List; 32 | 33 | public class MCPatcherPatcherApplication extends JFrame 34 | { 35 | private static final MCPatcherPatcher patcherInterface = new MCPatcherPatcher(new BufferedImageProvider()); 36 | 37 | private JLabel inputLabel; 38 | private JLabel outputLabel; 39 | private JScrollPane consoleLogScrollPane; 40 | private JTextArea consoleLog; 41 | private JTextField inputTextField; 42 | private JTextField outputTextField; 43 | private JButton browseInput; 44 | private JButton browseOutput; 45 | private JButton convert; 46 | private JFileChooser fileChooser; 47 | 48 | private File inputDirectory; 49 | private File outputDirectory; 50 | 51 | public MCPatcherPatcherApplication() 52 | { 53 | this.setPreferredSize(new Dimension(550, 400)); 54 | this.initComponents(); 55 | this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 56 | this.setLayout(null); 57 | this.setResizable(false); 58 | this.setupComponents(); 59 | this.loadComponents(); 60 | this.setTitle("MCPatcherPatcher"); 61 | this.pack(); 62 | this.setLocationRelativeTo(null); 63 | } 64 | 65 | private void initComponents() 66 | { 67 | this.inputLabel = new JLabel(); 68 | this.outputLabel = new JLabel(); 69 | this.consoleLog = new JTextArea(); 70 | this.inputTextField = new JTextField(); 71 | this.outputTextField = new JTextField(); 72 | this.browseInput = new JButton(); 73 | this.browseOutput = new JButton(); 74 | this.convert = new JButton(); 75 | this.fileChooser = new JFileChooser(); 76 | this.inputDirectory = new File(System.getProperty("user.dir") + File.separator + "input"); 77 | this.outputDirectory = new File(System.getProperty("user.dir") + File.separator + "output"); 78 | this.createDirectoryIfNotExist(this.inputDirectory); 79 | this.createDirectoryIfNotExist(this.outputDirectory); 80 | PrintStream printStream = new PrintStream(new CustomOutputStream(this.consoleLog)); 81 | System.setOut(printStream); 82 | System.setErr(printStream); 83 | } 84 | 85 | private void setupComponents() 86 | { 87 | this.fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 88 | this.fileChooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); 89 | 90 | Font labelFont = new Font("Arial", Font.PLAIN, 12); 91 | Font consoleLogFont = new Font("Consolas", Font.PLAIN, 12); 92 | 93 | Dim2i inputLabelDim = new Dim2i(10, 5, 50, 30); 94 | this.inputLabel.setBounds(inputLabelDim.getOriginX(), inputLabelDim.getOriginY(), inputLabelDim.getWidth(), inputLabelDim.getHeight()); 95 | this.inputLabel.setFont(labelFont); 96 | this.inputLabel.setText("Input"); 97 | 98 | Dim2i inputTextFieldDim = new Dim2i(inputLabelDim.getLimitX() + 5, inputLabelDim.getOriginY(), (int) this.getPreferredSize().getWidth() - 120, inputLabelDim.getHeight()); 99 | this.inputTextField.setBounds(inputTextFieldDim.getOriginX(), inputTextFieldDim.getOriginY(), inputTextFieldDim.getWidth(), inputTextFieldDim.getHeight()); 100 | this.inputTextField.setEditable(false); 101 | this.inputTextField.setText(this.inputDirectory.getAbsolutePath()); 102 | 103 | Dim2i browseInputDim = new Dim2i(inputTextFieldDim.getLimitX() + 5, inputLabelDim.getOriginY(), inputLabelDim.getHeight(), inputLabelDim.getHeight()); 104 | this.browseInput.setBounds(browseInputDim.getOriginX(), browseInputDim.getOriginY(), browseInputDim.getWidth(), browseInputDim.getHeight()); 105 | this.browseInput.setText("..."); 106 | 107 | Dim2i outputLabelDim = new Dim2i(inputLabelDim.getOriginX(), browseInputDim.getLimitY() + 5, 50, inputLabelDim.getHeight()); 108 | this.outputLabel.setBounds(outputLabelDim.getOriginX(), outputLabelDim.getOriginY(), outputLabelDim.getWidth(), outputLabelDim.getHeight()); 109 | this.outputLabel.setFont(labelFont); 110 | this.outputLabel.setText("Output"); 111 | 112 | Dim2i outputTextFieldDim = new Dim2i(outputLabelDim.getLimitX() + 5, outputLabelDim.getOriginY(), (int) this.getPreferredSize().getWidth() - 120, outputLabelDim.getHeight()); 113 | this.outputTextField.setBounds(outputTextFieldDim.getOriginX(), outputTextFieldDim.getOriginY(), outputTextFieldDim.getWidth(), outputTextFieldDim.getHeight()); 114 | this.outputTextField.setEditable(false); 115 | this.outputTextField.setText(this.outputDirectory.getAbsolutePath()); 116 | 117 | Dim2i browseOutputDim = new Dim2i(outputTextFieldDim.getLimitX() + 5, outputLabelDim.getOriginY(), outputLabelDim.getHeight(), outputLabelDim.getHeight()); 118 | this.browseOutput.setBounds(browseOutputDim.getOriginX(), browseOutputDim.getOriginY(), browseOutputDim.getWidth(), browseOutputDim.getHeight()); 119 | this.browseOutput.setText("..."); 120 | 121 | Dim2i consoleLogDim = new Dim2i(10, browseOutputDim.getLimitY() + 10, (int) this.getPreferredSize().getWidth() - 30, 122 | (int) this.getPreferredSize().getHeight() - browseOutputDim.getLimitY() - 85); 123 | this.consoleLog.setBounds(consoleLogDim.getOriginX(), consoleLogDim.getOriginY(), consoleLogDim.getWidth(), consoleLogDim.getHeight()); 124 | this.consoleLog.setFont(consoleLogFont); 125 | this.consoleLog.setEditable(false); 126 | this.consoleLogScrollPane = new JScrollPane(consoleLog); 127 | this.consoleLogScrollPane.setBounds(consoleLogDim.getOriginX(), consoleLogDim.getOriginY(), consoleLogDim.getWidth(), consoleLogDim.getHeight()); 128 | this.consoleLogScrollPane.setBorder(new LineBorder(Color.GRAY)); 129 | 130 | Dim2i convertDim = new Dim2i(10, consoleLogDim.getLimitY() + 5, (int) this.getPreferredSize().getWidth() - 30, 30); 131 | this.convert.setBounds(convertDim.getOriginX(), convertDim.getOriginY(), convertDim.getWidth(), convertDim.getHeight()); 132 | this.convert.setText("Convert"); 133 | 134 | this.browseInput.addActionListener(e -> { 135 | if (this.fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { 136 | this.inputDirectory = this.fileChooser.getSelectedFile(); 137 | this.inputTextField.setText(this.inputDirectory.getAbsolutePath()); 138 | System.out.printf("Input Selected: %s%n", this.inputDirectory.getAbsolutePath()); 139 | } 140 | }); 141 | 142 | this.browseOutput.addActionListener(e -> { 143 | UIManager.put("FileChooser.readOnly", Boolean.TRUE); 144 | if (this.fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { 145 | this.outputDirectory = this.fileChooser.getSelectedFile(); 146 | this.outputLabel.setText(this.outputDirectory.getAbsolutePath()); 147 | System.out.printf("Output Selected: %s%n", this.outputDirectory.getAbsolutePath()); 148 | } 149 | }); 150 | 151 | this.convert.addActionListener(e -> { 152 | if (this.inputTextField.getText().isEmpty()) { 153 | JOptionPane.showMessageDialog(this, "Input Directory not selected!"); 154 | return; 155 | } 156 | if (this.outputTextField.getText().isEmpty()) { 157 | JOptionPane.showMessageDialog(this, "Output Directory not selected!"); 158 | return; 159 | } 160 | if (this.inputTextField.getText().equals(this.outputTextField.getText())) { 161 | JOptionPane.showMessageDialog(this, "Output Directory can not be the same as Input Directory!"); 162 | } 163 | 164 | List validResourcePacks = new ArrayList<>(); 165 | // Does not fully verify if zip is valid resource pack 166 | Arrays.stream(this.inputDirectory.listFiles()).filter(file -> file.isFile() && file.getName().endsWith(".zip")).forEach(validResourcePacks::add); 167 | 168 | new Thread(() -> { 169 | validResourcePacks.forEach(resourcePack -> { 170 | System.out.println("Converting " + resourcePack.getName()); 171 | try { 172 | patcherInterface.convert(resourcePack, new File(this.outputDirectory.getAbsolutePath() + File.separator + resourcePack.getName())); 173 | } catch (IOException ioException) { 174 | ioException.printStackTrace(); 175 | } 176 | System.out.println("Converted " + resourcePack.getName()); 177 | }); 178 | }).start(); 179 | }); 180 | } 181 | 182 | private void loadComponents() 183 | { 184 | this.add(this.inputLabel); 185 | this.add(this.outputLabel); 186 | this.add(this.consoleLogScrollPane); 187 | this.add(this.inputTextField); 188 | this.add(this.outputTextField); 189 | this.add(this.browseInput); 190 | this.add(this.browseOutput); 191 | this.add(this.convert); 192 | } 193 | 194 | public static void main(String[] args) throws IOException 195 | { 196 | if (Arrays.asList(args).isEmpty()) { 197 | SwingUtilities.invokeLater(() -> { 198 | try { 199 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 200 | } catch (Exception ex) { 201 | ex.printStackTrace(); 202 | } 203 | MCPatcherPatcherApplication mcPatcherPatcher = new MCPatcherPatcherApplication(); 204 | mcPatcherPatcher.setVisible(true); 205 | }); 206 | } else { 207 | String singleRuntimeOptionLine = String.join(" ", args); 208 | String[] options = singleRuntimeOptionLine.split("--"); 209 | if (options.length == 0) { 210 | System.out.println("No"); 211 | } else { 212 | File inputFile = null; 213 | File outputFile = null; 214 | 215 | for (String option : options) { 216 | option = option.trim(); 217 | if (option.toLowerCase().startsWith("input") && !option.equalsIgnoreCase("input") && !option.equalsIgnoreCase("input ")) { 218 | inputFile = new File(option.substring("input ".length())); 219 | } else if (option.toLowerCase().startsWith("output") && !option.equalsIgnoreCase("output") && !option.equalsIgnoreCase("output ")) { 220 | outputFile = new File(option.substring("output ".length())); 221 | } 222 | } 223 | if (inputFile == null) { 224 | System.out.println("Missing Input File Path!"); 225 | return; 226 | } 227 | if (outputFile == null) { 228 | System.out.println("Missing Output File Path!"); 229 | return; 230 | } 231 | if (!inputFile.getAbsolutePath().endsWith(".zip")) { 232 | System.out.println("Invalid Input File!"); 233 | return; 234 | } 235 | if (!outputFile.getAbsolutePath().endsWith(".zip")) { 236 | System.out.println("Invalid Output File!"); 237 | return; 238 | } 239 | 240 | patcherInterface.convert(inputFile, outputFile); 241 | System.out.printf("Output File: %s%n", outputFile.getAbsolutePath()); 242 | } 243 | } 244 | } 245 | 246 | private boolean createDirectoryIfNotExist(File file) { 247 | if (!file.exists()) 248 | return file.mkdirs(); 249 | else 250 | return file.isDirectory(); 251 | } 252 | 253 | private static class CustomOutputStream extends OutputStream 254 | { 255 | private final JTextArea textArea; 256 | 257 | public CustomOutputStream(JTextArea textArea) 258 | { 259 | this.textArea = textArea; 260 | } 261 | 262 | @Override 263 | public void write(int b) 264 | { 265 | textArea.append(String.valueOf((char) b)); 266 | textArea.setCaretPosition(textArea.getDocument().getLength()); 267 | } 268 | } 269 | 270 | private static class Dim2i 271 | { 272 | private final int x; 273 | private final int y; 274 | private final int width; 275 | private final int height; 276 | 277 | public Dim2i(int x, int y, int width, int height) 278 | { 279 | this.x = x; 280 | this.y = y; 281 | this.width = width; 282 | this.height = height; 283 | } 284 | 285 | public int getOriginX() 286 | { 287 | return this.x; 288 | } 289 | 290 | public int getOriginY() 291 | { 292 | return this.y; 293 | } 294 | 295 | public int getWidth() 296 | { 297 | return this.width; 298 | } 299 | 300 | public int getHeight() 301 | { 302 | return this.height; 303 | } 304 | 305 | public int getLimitX() 306 | { 307 | return this.x + this.width; 308 | } 309 | 310 | public int getLimitY() 311 | { 312 | return this.y + this.height; 313 | } 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/ResourceType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher; 19 | 20 | import org.aperlambda.lambdacommon.utils.Nameable; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | /** 24 | * Represents the resource types. 25 | * 26 | * @author LambdAurora 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | public enum ResourceType implements Nameable 31 | { 32 | ASSETS("assets"), 33 | DATA("data"); 34 | 35 | private final String name; 36 | 37 | ResourceType(@NotNull String name) 38 | { 39 | this.name = name; 40 | } 41 | 42 | @Override 43 | public @NotNull String getName() 44 | { 45 | return this.name; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/converter/CETConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.converter; 19 | 20 | import com.google.gson.JsonArray; 21 | import com.google.gson.JsonObject; 22 | import me.lambdaurora.mcpatcherpatcher.ErrorType; 23 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 24 | import me.lambdaurora.mcpatcherpatcher.fs.ResourceAccessor; 25 | import me.lambdaurora.mcpatcherpatcher.image.ImageProvider; 26 | import me.lambdaurora.mcpatcherpatcher.mcpatcher.RandomEntityRule; 27 | import org.aperlambda.lambdacommon.Identifier; 28 | import org.aperlambda.lambdacommon.LambdaConstants; 29 | import org.jetbrains.annotations.NotNull; 30 | 31 | import java.io.IOException; 32 | import java.io.InputStream; 33 | import java.util.*; 34 | import java.util.regex.Matcher; 35 | import java.util.regex.Pattern; 36 | 37 | /** 38 | * Represents the converter for Custom Entity Textures. (Like RET but with properties files) 39 | *

40 | * Converts to the Varied Mob Textures (VMT) format. 41 | * 42 | * @author LambdAurora 43 | * @version 1.0.0 44 | * @since 1.0.0 45 | */ 46 | public class CETConverter extends Converter 47 | { 48 | public static final String CET_PARENT = "optifine/random"; 49 | public static final Pattern CET_PATTERN = Pattern.compile("optifine/random/(?\\w+)/(?(?:\\w+/)+)?(?\\w+)\\.properties$"); 50 | 51 | public CETConverter(@NotNull ResourceAccessor input, @NotNull ResourceAccessor output) 52 | { 53 | super(input, output); 54 | } 55 | 56 | @Override 57 | public @NotNull Map convert(@NotNull ImageProvider imageProvider) 58 | { 59 | Map failed = new HashMap<>(); 60 | 61 | this.input.getNamespaces(ResourceType.ASSETS).stream() 62 | .map(namespace -> new Identifier(namespace, CET_PARENT)) 63 | .forEach(parent -> this.input.searchIn(ResourceType.ASSETS, parent) 64 | .filter(id -> id.getName().endsWith(".properties")) 65 | .forEach(id -> { 66 | Matcher matcher = CET_PATTERN.matcher(id.getName()); 67 | if (matcher.find()) { 68 | String type = matcher.group("type"); 69 | String sub = matcher.group("sub"); 70 | String name = matcher.group("name"); 71 | 72 | if (type == null || name == null) 73 | return; 74 | if (sub == null) 75 | sub = ""; 76 | 77 | Identifier vmtId = new Identifier(id.getNamespace(), 78 | "varied/textures/" + type + "/" + sub + name + ".json"); 79 | 80 | InputStream inputStream = this.input.getInputStream(ResourceType.ASSETS, id); 81 | if (inputStream == null) { 82 | failed.put(id, ErrorType.INPUTSTREAM_IO); 83 | return; 84 | } 85 | 86 | Properties properties = new Properties(); 87 | try { 88 | properties.load(inputStream); 89 | inputStream.close(); 90 | } catch (IOException e) { 91 | failed.put(id, ErrorType.PROPERTIES_READ); 92 | return; 93 | } finally { 94 | try { 95 | inputStream.close(); 96 | } catch (IOException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | List rules = new ArrayList<>(); 102 | 103 | int count = properties.size(); 104 | for (int i = 0; i < count; i++) { 105 | int index = i + 1; 106 | 107 | String valTextures = properties.getProperty("textures." + index); 108 | if (valTextures == null) 109 | valTextures = properties.getProperty("skins." + index); 110 | 111 | if (valTextures != null) { 112 | rules.add(new RandomEntityRule(index, 113 | new Identifier(id.getNamespace(), id.getName().replace(".properties", "")), 114 | valTextures, 115 | properties)); 116 | } 117 | } 118 | 119 | this.convert(vmtId, rules); 120 | } 121 | })); 122 | 123 | return failed; 124 | } 125 | 126 | /** 127 | * Converts one MCPatcher file to VMT format. The MCPatcher file is parsed as a set of rules. 128 | * 129 | * @param vmtId The VMT metadata file identifier. 130 | * @param rules The rules of the MCPatcher properties file. 131 | */ 132 | public void convert(@NotNull Identifier vmtId, @NotNull List rules) 133 | { 134 | if (rules.size() == 0) 135 | return; 136 | 137 | JsonObject json; 138 | if (rules.size() > 1) { 139 | json = new JsonObject(); 140 | json.addProperty("type", "varied-mobs:seq"); 141 | JsonArray choices = new JsonArray(); 142 | rules.forEach(rule -> choices.add(rule.toJson())); 143 | json.add("choices", choices); 144 | } else { 145 | json = rules.get(0).toJson(); 146 | } 147 | 148 | String res = LambdaConstants.GSON_PRETTY.toJson(json); 149 | this.output.put(ResourceType.ASSETS, vmtId, res.getBytes()); 150 | } 151 | 152 | @Override 153 | public @NotNull String getName() 154 | { 155 | return "CET"; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/converter/Converter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.converter; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.ErrorType; 21 | import me.lambdaurora.mcpatcherpatcher.fs.ResourceAccessor; 22 | import me.lambdaurora.mcpatcherpatcher.image.ImageProvider; 23 | import org.aperlambda.lambdacommon.Identifier; 24 | import org.aperlambda.lambdacommon.utils.Nameable; 25 | import org.jetbrains.annotations.NotNull; 26 | 27 | import java.util.Map; 28 | 29 | /** 30 | * Represents a converter. 31 | * 32 | * @author LambdAurora 33 | * @version 1.0.0 34 | * @since 1.0.0 35 | */ 36 | public abstract class Converter implements Nameable 37 | { 38 | protected final ResourceAccessor input; 39 | protected final ResourceAccessor output; 40 | 41 | public Converter(@NotNull ResourceAccessor input, @NotNull ResourceAccessor output) 42 | { 43 | this.input = input; 44 | this.output = output; 45 | } 46 | 47 | /** 48 | * Converts the resource pack using the format associated to this converter to a new format. 49 | * 50 | * @param imageProvider An image provider. 51 | * @return A map of failed conversions and their error type. 52 | */ 53 | public abstract @NotNull Map convert(@NotNull ImageProvider imageProvider); 54 | } 55 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/converter/RETConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.converter; 19 | 20 | import com.google.gson.JsonArray; 21 | import com.google.gson.JsonObject; 22 | import me.lambdaurora.mcpatcherpatcher.ErrorType; 23 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 24 | import me.lambdaurora.mcpatcherpatcher.fs.ResourceAccessor; 25 | import me.lambdaurora.mcpatcherpatcher.image.ImageProvider; 26 | import org.aperlambda.lambdacommon.Identifier; 27 | import org.aperlambda.lambdacommon.LambdaConstants; 28 | import org.jetbrains.annotations.NotNull; 29 | 30 | import java.util.ArrayList; 31 | import java.util.HashMap; 32 | import java.util.List; 33 | import java.util.Map; 34 | import java.util.regex.Matcher; 35 | import java.util.regex.Pattern; 36 | 37 | /** 38 | * Represents the Random Entity Textures converter 39 | *

40 | * Converts to the Varied Mob Textures (VMT) format. 41 | * 42 | * @author LambdAurora 43 | * @version 1.0.0 44 | * @since 1.0.0 45 | */ 46 | public class RETConverter extends Converter 47 | { 48 | private static final Pattern RET_PATTERN = Pattern.compile("optifine/random/(?\\w+)/(?(?:\\w+/)+)?(?[A-z_-]+)(?\\d+)?\\.png$"); 49 | 50 | public RETConverter(@NotNull ResourceAccessor input, @NotNull ResourceAccessor output) 51 | { 52 | super(input, output); 53 | } 54 | 55 | @Override 56 | public @NotNull Map convert(@NotNull ImageProvider imageProvider) 57 | { 58 | Map failed = new HashMap<>(); 59 | Map> retIds = new HashMap<>(); 60 | this.input.getNamespaces(ResourceType.ASSETS).stream() 61 | .map(namespace -> new Identifier(namespace, CETConverter.CET_PARENT)) 62 | .forEach(parent -> this.input.searchIn(ResourceType.ASSETS, parent) 63 | .filter(id -> id.getName().endsWith(".png") && !id.getName().endsWith("-.png")) 64 | .forEach(id -> { 65 | Matcher matcher = RET_PATTERN.matcher(id.getName()); 66 | if (matcher.find()) { 67 | String type = matcher.group("type"); 68 | String sub = matcher.group("sub"); 69 | String name = matcher.group("name"); 70 | String n = matcher.group("n"); 71 | 72 | if (type == null || name == null) 73 | return; 74 | if (n == null) 75 | n = ""; 76 | if (sub == null) 77 | sub = ""; 78 | 79 | String path = type + "/" + sub + name; 80 | 81 | Identifier propertiesId = new Identifier(id.getNamespace(), 82 | CETConverter.CET_PARENT).sub(path + ".properties"); 83 | 84 | if (this.input.has(ResourceType.ASSETS, propertiesId)) { 85 | return; 86 | } 87 | 88 | Identifier vmtId = new Identifier(id.getNamespace(), 89 | "varied/textures/" + path + ".json"); 90 | 91 | retIds.computeIfAbsent(vmtId, k -> new ArrayList<>()).add(new TextureEntry(type, sub, name, n, id)); 92 | } 93 | })); 94 | 95 | retIds.forEach(this::convert); 96 | 97 | return failed; 98 | } 99 | 100 | /** 101 | * Converts the set of RET textures to a valid VMT format. 102 | * 103 | * @param vmtId The VMT metadata file identifier. 104 | * @param textures The textures. 105 | */ 106 | private void convert(@NotNull Identifier vmtId, @NotNull List textures) 107 | { 108 | JsonObject json = new JsonObject(); 109 | if (textures.size() > 1) { 110 | json.addProperty("type", "varied-mobs:pick"); 111 | } else { 112 | json.addProperty("type", "varied-mobs:result"); 113 | } 114 | 115 | JsonArray jsonTextures = new JsonArray(); 116 | 117 | textures.forEach(entry -> { 118 | Identifier vmtTexId = entry.toVMT(); 119 | 120 | jsonTextures.add(vmtTexId.toString()); 121 | 122 | this.input.rename(this.output, ResourceType.ASSETS, entry.ofId, vmtTexId); 123 | }); 124 | 125 | if (textures.size() == 1) { 126 | json.addProperty("result", jsonTextures.get(0).getAsString()); 127 | } else 128 | json.add("choices", jsonTextures); 129 | 130 | String res = LambdaConstants.GSON_PRETTY.toJson(json); 131 | this.output.put(ResourceType.ASSETS, vmtId, res.getBytes()); 132 | } 133 | 134 | @Override 135 | public @NotNull String getName() 136 | { 137 | return "RET"; 138 | } 139 | 140 | /** 141 | * Represents a texture entry. 142 | * 143 | * @version 1.0.0 144 | * @since 1.0.0 145 | */ 146 | private static class TextureEntry 147 | { 148 | public final String type; 149 | public final String sub; 150 | public final String name; 151 | public final String n; 152 | public final Identifier ofId; 153 | 154 | private TextureEntry(@NotNull String type, @NotNull String sub, @NotNull String name, @NotNull String n, @NotNull Identifier ofId) 155 | { 156 | this.type = type; 157 | this.sub = sub; 158 | this.name = name; 159 | this.n = n; 160 | this.ofId = ofId; 161 | } 162 | 163 | public @NotNull Identifier toVMT() 164 | { 165 | return new Identifier(this.ofId.getNamespace(), 166 | "textures/" + type + "/" + sub + "varied/" + name + n + ".png"); 167 | } 168 | 169 | @Override 170 | public String toString() 171 | { 172 | return "TextureEntry{" + 173 | "type='" + type + '\'' + 174 | ", sub='" + sub + '\'' + 175 | ", name='" + name + '\'' + 176 | ", n='" + n + '\'' + 177 | ", ofId=" + ofId + 178 | '}'; 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/converter/SkyConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.converter; 19 | 20 | import com.google.gson.JsonArray; 21 | import com.google.gson.JsonObject; 22 | import me.lambdaurora.mcpatcherpatcher.Closeable; 23 | import me.lambdaurora.mcpatcherpatcher.ErrorType; 24 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 25 | import me.lambdaurora.mcpatcherpatcher.fs.ResourceAccessor; 26 | import me.lambdaurora.mcpatcherpatcher.image.BasicImage; 27 | import me.lambdaurora.mcpatcherpatcher.image.ImageProvider; 28 | import me.lambdaurora.mcpatcherpatcher.mcpatcher.MCPatcherParser; 29 | import org.aperlambda.lambdacommon.Identifier; 30 | import org.aperlambda.lambdacommon.LambdaConstants; 31 | import org.jetbrains.annotations.NotNull; 32 | 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.util.HashMap; 36 | import java.util.Map; 37 | import java.util.Objects; 38 | import java.util.Properties; 39 | import java.util.regex.Matcher; 40 | import java.util.regex.Pattern; 41 | 42 | /** 43 | * Represents the converter for Custom Sky Boxes. 44 | *

45 | * Converts to the fabricskyboxes (FSB) format. 46 | * 47 | * @author FlashyReese 48 | * @version 1.0.0 49 | * @since 1.0.0 50 | */ 51 | public class SkyConverter extends Converter implements Closeable 52 | { 53 | public static final String FABRICSKYBOXES_NAMESPACE = "fabricskyboxes"; 54 | public static final String FABRICSKYBOXES_PARENT = "sky"; 55 | public static final String OPTIFINE_SKY_PARENT = "optifine/sky"; 56 | public static final Pattern OPTIFINE_SKY_PATTERN = Pattern.compile("optifine/sky/(?\\w+)/(?\\w+).properties$"); 57 | public static final String MCPATCHER_SKY_PARENT = "mcpatcher/sky"; 58 | public static final Pattern MCPATCHER_SKY_PATTERN = Pattern.compile("mcpatcher/sky/(?\\w+)/(?\\w+).properties$"); 59 | 60 | private final Map cached = new HashMap<>(); 61 | 62 | public SkyConverter(@NotNull ResourceAccessor input, @NotNull ResourceAccessor output) 63 | { 64 | super(input, output); 65 | } 66 | 67 | @Override 68 | public @NotNull Map convert(@NotNull ImageProvider imageProvider) 69 | { 70 | Map failed = new HashMap<>(); 71 | this.convertNamespace(imageProvider, failed, OPTIFINE_SKY_PARENT, OPTIFINE_SKY_PATTERN); 72 | this.convertNamespace(imageProvider, failed, MCPATCHER_SKY_PARENT, MCPATCHER_SKY_PATTERN); 73 | return failed; 74 | } 75 | 76 | /** 77 | * Converts a specific namespace 78 | * 79 | * @param imageProvider The Image Provider 80 | * @param failed The Identifier-ErrorType Map 81 | * @param skyParent The parent namespace 82 | * @param pattern The pattern for namespace 83 | */ 84 | private void convertNamespace(ImageProvider imageProvider, Map failed, String skyParent, Pattern pattern) 85 | { 86 | this.input.getNamespaces(ResourceType.ASSETS).stream() 87 | .map(namespace -> new Identifier(namespace, skyParent)) 88 | .forEach(parent -> this.input.searchIn(ResourceType.ASSETS, parent) 89 | .filter(id -> id.getName().endsWith(".properties")) 90 | .forEach(id -> { 91 | Matcher matcher = pattern.matcher(id.getName()); 92 | if (matcher.find()) { 93 | String world = matcher.group("world"); 94 | String name = matcher.group("name"); 95 | 96 | if (world == null || name == null) 97 | return; 98 | 99 | Identifier fsbId = new Identifier(FABRICSKYBOXES_NAMESPACE, 100 | String.format("%s/%s_%s.json", FABRICSKYBOXES_PARENT, world.equals("world0") ? "overworld" : world, name)); 101 | 102 | InputStream inputStream = this.input.getInputStream(ResourceType.ASSETS, id); 103 | if (inputStream == null) { 104 | failed.put(id, ErrorType.INPUTSTREAM_IO); 105 | return; 106 | } 107 | 108 | Properties properties = new Properties(); 109 | try { 110 | properties.load(inputStream); 111 | } catch (IOException e) { 112 | failed.put(id, ErrorType.PROPERTIES_READ); 113 | return; 114 | } finally { 115 | try { 116 | inputStream.close(); 117 | } catch (IOException e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | if (properties.size() == 0) { 123 | return; 124 | } 125 | 126 | Identifier textureId; 127 | if (properties.containsKey("source")) { 128 | String source = properties.getProperty("source"); 129 | if (source.startsWith("./")) { 130 | textureId = new Identifier(id.getNamespace(), parent.getName() + String.format("/%s/%s", world, source.substring(2))); 131 | } else if (source.startsWith("assets/")) { 132 | int firstIndex = source.indexOf("/") + 1; 133 | int secondIndex = source.indexOf("/", firstIndex); 134 | String sourceNamespace = source.substring(firstIndex, secondIndex); 135 | textureId = new Identifier(sourceNamespace, source.substring(secondIndex + 1)); 136 | } else { 137 | //There is no way you're in another ResourceType and if you are >:(, this will not work inputStream still reads from ASSETS 138 | int firstIndex = source.indexOf("/") + 1; 139 | String sourceNamespace = source.substring(0, firstIndex - 1); 140 | textureId = new Identifier(sourceNamespace, source.substring(firstIndex)); 141 | } 142 | } else { 143 | textureId = new Identifier(id.getNamespace(), parent.getName() + String.format("/%s/%s.png", world, name)); 144 | } 145 | 146 | InputStream textureInputStream = this.input.getInputStream(ResourceType.ASSETS, textureId); 147 | if (textureInputStream == null) { 148 | failed.put(textureId, ErrorType.INPUTSTREAM_IO); 149 | return; 150 | } 151 | 152 | BasicImage textureImage; 153 | try { 154 | textureImage = imageProvider.readImage(textureInputStream); 155 | } catch (IOException e) { 156 | failed.put(textureId, ErrorType.INPUTSTREAM_IO); 157 | return; 158 | } 159 | 160 | this.convert(fsbId, textureId, textureImage, properties, world); 161 | 162 | textureImage.close(); 163 | } 164 | })); 165 | } 166 | 167 | /** 168 | * Converts one MCPatcher file to FSB format. 169 | * 170 | * @param fsbId The FSB metadata file identifier. 171 | * @param textureId The texture file identifier. 172 | * @param textureImage The texture BasicImage 173 | * @param properties The MCPatcher properties file. 174 | * @param world The world name 175 | */ 176 | private void convert(@NotNull Identifier fsbId, @NotNull Identifier textureId, @NotNull BasicImage textureImage, @NotNull Properties properties, @NotNull String world) 177 | { 178 | JsonObject json = null; 179 | if (properties.size() > 1) { 180 | json = new JsonObject(); 181 | 182 | json.addProperty("schemaVersion", 2); 183 | json.addProperty("type", "square-textured"); 184 | 185 | json.addProperty("blend", properties.containsKey("blend")); 186 | 187 | JsonObject texturesObject = new JsonObject(); 188 | this.processSkyboxTexture(texturesObject, textureId, textureImage); 189 | json.add("textures", texturesObject); 190 | 191 | JsonObject propertiesObject = new JsonObject(); 192 | this.processProperties(propertiesObject, properties); 193 | json.add("properties", propertiesObject); 194 | 195 | JsonObject conditionsObject = new JsonObject(); 196 | this.processConditions(conditionsObject, properties, world); 197 | json.add("conditions", conditionsObject); 198 | 199 | } 200 | 201 | if (json == null) 202 | return; 203 | 204 | String res = LambdaConstants.GSON_PRETTY.toJson(json); 205 | this.cached.put(fsbId, res.getBytes()); 206 | } 207 | 208 | /** 209 | * Converts Optifine skybox textures to FSB format. 210 | * 211 | * @param json The FSB JSON file. 212 | * @param textureId The Skybox Texture Identifier file. 213 | * @param textureImage The Skybox Texture file. 214 | */ 215 | private void processSkyboxTexture(@NotNull JsonObject json, @NotNull Identifier textureId, @NotNull BasicImage textureImage) 216 | { 217 | String textureName = textureId.getName().substring(textureId.getName().lastIndexOf("/") + 1, textureId.getName().lastIndexOf(".")); 218 | int scale = textureImage.getHeight() / 2; 219 | this.processFaceTexture(json, textureName, "top", textureImage.getSubImage(scale, 0, scale, scale)); 220 | this.processFaceTexture(json, textureName, "bottom", textureImage.getSubImage(0, 0, scale, scale)); 221 | this.processFaceTexture(json, textureName, "north", textureImage.getSubImage(0, scale, scale, scale)); 222 | this.processFaceTexture(json, textureName, "south", textureImage.getSubImage(scale * 2, scale, scale, scale)); 223 | this.processFaceTexture(json, textureName, "east", textureImage.getSubImage(scale, scale, scale, scale)); 224 | this.processFaceTexture(json, textureName, "west", textureImage.getSubImage(scale * 2, 0, scale, scale)); 225 | } 226 | 227 | /** 228 | * Generates new face textures. 229 | * 230 | * @param json The FSB JSON file. 231 | * @param textureName The Name of Skybox Texture file. 232 | * @param face The Name of Texture Face. 233 | * @param texture The Texture Face file. 234 | */ 235 | private void processFaceTexture(@NotNull JsonObject json, @NotNull String textureName, @NotNull String face, @NotNull BasicImage texture) 236 | { 237 | Identifier faceId = new Identifier(FABRICSKYBOXES_NAMESPACE, String.format("%s/%s.png", FABRICSKYBOXES_PARENT, String.format("%s_%s", textureName, face))); 238 | if (!this.cached.containsKey(faceId)) 239 | this.cached.put(faceId, texture.getBytes()); 240 | json.addProperty(face, faceId.toString()); 241 | } 242 | 243 | /** 244 | * Converts MCPatcher Sky Properties to FabricSkyboxes properties 245 | * 246 | * @param json The properties object for FabricSkyboxes 247 | * @param properties The sky properties 248 | */ 249 | private void processProperties(@NotNull JsonObject json, @NotNull Properties properties) 250 | { 251 | JsonObject fade = new JsonObject(); 252 | // Convert fade 253 | int startFadeIn = Objects.requireNonNull(MCPatcherParser.toTickTime(properties.getProperty("startFadeIn"))).intValue(); 254 | int endFadeIn = Objects.requireNonNull(MCPatcherParser.toTickTime(properties.getProperty("endFadeIn"))).intValue(); 255 | int endFadeOut = Objects.requireNonNull(MCPatcherParser.toTickTime(properties.getProperty("endFadeOut"))).intValue(); 256 | int startFadeOut; 257 | if (properties.containsKey("startFadeOut")) { 258 | startFadeOut = Objects.requireNonNull(MCPatcherParser.toTickTime(properties.getProperty("startFadeOut"))).intValue(); 259 | } else { 260 | startFadeOut = endFadeOut - (endFadeIn - startFadeIn); 261 | if (startFadeIn <= startFadeOut && endFadeIn >= startFadeOut) { 262 | startFadeOut = endFadeOut; 263 | } 264 | } 265 | fade.addProperty("startFadeIn", MCPatcherParser.normalizeTickTime(startFadeIn)); 266 | fade.addProperty("endFadeIn", MCPatcherParser.normalizeTickTime(endFadeIn)); 267 | fade.addProperty("startFadeOut", MCPatcherParser.normalizeTickTime(startFadeOut)); 268 | fade.addProperty("endFadeOut", MCPatcherParser.normalizeTickTime(endFadeOut)); 269 | 270 | json.add("fade", fade); 271 | 272 | // Convert rotation 273 | if (properties.containsKey("rotate")) { //"@FlashyReese Did you forget to parse rotate= into "shouldRotate":?" -AMereBagatelle 274 | json.addProperty("shouldRotate", Boolean.parseBoolean(properties.getProperty("rotate"))); 275 | } 276 | 277 | JsonObject rotation = new JsonObject(); 278 | JsonArray jsonAxis = new JsonArray(); 279 | if (properties.containsKey("axis")) { 280 | String[] axis = properties.getProperty("axis").split(" "); 281 | for (String a : axis) { 282 | jsonAxis.add(Float.parseFloat(a) * 180); 283 | } 284 | } else { 285 | //Default South 286 | jsonAxis.add(0f); 287 | jsonAxis.add(0f); 288 | jsonAxis.add(180f); 289 | } 290 | rotation.add("axis", jsonAxis); 291 | 292 | json.add("rotation", rotation); 293 | 294 | if (properties.containsKey("speed")) { 295 | json.addProperty("transitionSpeed", Float.parseFloat(properties.getProperty("speed"))); 296 | } 297 | } 298 | 299 | 300 | /** 301 | * Converts properties into conditions object 302 | * 303 | * @param json The conditions object 304 | * @param properties The sky properties 305 | * @param world The world string 306 | */ 307 | private void processConditions(@NotNull JsonObject json, @NotNull Properties properties, @NotNull String world) 308 | { 309 | if (properties.containsKey("weather")) { 310 | String[] weathers = properties.getProperty("weather").split(" "); 311 | JsonArray jsonWeather = new JsonArray(); 312 | for (String weather : weathers) { 313 | jsonWeather.add(weather); 314 | } 315 | json.add("weather", jsonWeather); 316 | } 317 | 318 | if (properties.containsKey("biomes")) { 319 | String[] biomes = properties.getProperty("biomes").split(" "); 320 | JsonArray jsonBiomes = new JsonArray(); 321 | for (String biome : biomes) { 322 | jsonBiomes.add(biome); 323 | } 324 | json.add("biomes", jsonBiomes); 325 | } 326 | 327 | JsonArray worlds = new JsonArray(); 328 | worlds.add(world.equals("world0") ? "minecraft:overworld" : world); 329 | json.add("worlds", worlds); 330 | } 331 | 332 | @Override 333 | public void close() 334 | { 335 | for (Map.Entry entry : this.cached.entrySet()) { 336 | Identifier identifier = entry.getKey(); 337 | byte[] out = entry.getValue(); 338 | this.output.put(ResourceType.ASSETS, identifier, out); 339 | } 340 | 341 | this.cached.clear(); 342 | } 343 | 344 | @Override 345 | public @NotNull String getName() 346 | { 347 | return "Sky"; 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/fs/FileAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fs; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 21 | import org.aperlambda.lambdacommon.Identifier; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | import java.io.*; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.Collection; 29 | import java.util.List; 30 | import java.util.stream.Collectors; 31 | import java.util.stream.Stream; 32 | 33 | /** 34 | * Represents a file resource accessor. 35 | * 36 | * @author LambdAurora 37 | * @version 1.0.0 38 | * @since 1.0.0 39 | */ 40 | public class FileAccessor implements ResourceAccessor 41 | { 42 | private final File directory; 43 | 44 | public FileAccessor(@NotNull File directory) 45 | { 46 | this.directory = directory.getAbsoluteFile(); 47 | } 48 | 49 | @Override 50 | public boolean has(@NotNull ResourceType type, @NotNull Identifier identifier) 51 | { 52 | return new File(directory, ResourceAccessor.asPath(type, identifier)).exists(); 53 | } 54 | 55 | @Override 56 | public @NotNull Stream searchIn(@NotNull ResourceType type, @NotNull Identifier parent) 57 | { 58 | File parentFile = new File(directory, ResourceAccessor.asPath(type, parent)); 59 | 60 | if (!parentFile.exists() || !parentFile.isDirectory()) 61 | return Stream.builder().build(); 62 | 63 | List id = new ArrayList<>(); 64 | 65 | this.searchIn(type, parentFile.getAbsoluteFile(), id); 66 | 67 | return id.stream(); 68 | } 69 | 70 | private void searchIn(@NotNull ResourceType type, @NotNull File parent, @NotNull List results) 71 | { 72 | if (!parent.isDirectory()) { 73 | String path = parent.getPath().replace(this.directory.getPath(), ""); 74 | if (path.length() == 0) 75 | return; 76 | 77 | if (path.startsWith(File.separator)) { 78 | path = path.substring(File.separator.length()); 79 | if (path.length() == 0) 80 | return; 81 | } 82 | 83 | if (!path.startsWith(type.getName() + File.separator)) 84 | return; 85 | path = path.substring((type.getName() + File.separator).length()); 86 | 87 | int first = path.indexOf(File.separator); 88 | if (first == path.length() - 1 || first == -1) 89 | return; 90 | 91 | results.add(new Identifier(path.substring(0, first), path.substring(first + 1).replaceAll(File.pathSeparator, "/"))); 92 | } else { 93 | for (File file : parent.listFiles()) { 94 | this.searchIn(type, file.getAbsoluteFile(), results); 95 | } 96 | } 97 | } 98 | 99 | @Override 100 | public @Nullable InputStream getInputStream(@NotNull String path) 101 | { 102 | File file = new File(directory, path); 103 | if (!file.exists()) 104 | return null; 105 | try { 106 | return new FileInputStream(file); 107 | } catch (FileNotFoundException e) { 108 | return null; 109 | } 110 | } 111 | 112 | @Override 113 | public @NotNull Collection getNamespaces(@NotNull ResourceType type) 114 | { 115 | return Arrays.stream(new File(directory, type.getName()).listFiles()).filter(File::isDirectory).map(File::getName).collect(Collectors.toList()); 116 | } 117 | 118 | @Override 119 | public boolean put(@NotNull String path, @NotNull byte[] out) 120 | { 121 | File file = new File(directory, path); 122 | if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) 123 | return false; 124 | try { 125 | FileOutputStream outF = new FileOutputStream(file); 126 | 127 | outF.write(out); 128 | 129 | outF.close(); 130 | return true; 131 | } catch (IOException e) { 132 | return false; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/fs/ResourceAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fs; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 21 | import org.aperlambda.lambdacommon.Identifier; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | import java.io.ByteArrayOutputStream; 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.io.OutputStream; 29 | import java.util.Collection; 30 | import java.util.Objects; 31 | import java.util.stream.Stream; 32 | 33 | /** 34 | * Represents an abstraction of resource access. 35 | *

36 | * This allows to re-implement easily the resource pack patcher to ZIP, folder or runtime resource packs. 37 | * 38 | * @author LambdAurora 39 | * @version 1.0.0 40 | * @since 1.0.0 41 | */ 42 | public interface ResourceAccessor 43 | { 44 | /** 45 | * Returns whether the specified resource exists or not. 46 | * 47 | * @param type The resource type. 48 | * @param identifier The resource identifier. 49 | * @return True if the specified resource exists, else false. 50 | */ 51 | boolean has(@NotNull ResourceType type, @NotNull Identifier identifier); 52 | 53 | /** 54 | * Returns a stream of resource identifiers present in the specified parent folder. 55 | * 56 | * @param type The resource type. 57 | * @param parent The parent resource identifier. 58 | * @return The stream of identifiers. 59 | */ 60 | @NotNull Stream searchIn(@NotNull ResourceType type, @NotNull Identifier parent); 61 | 62 | /** 63 | * Returns an input stream from the specified resource. 64 | * 65 | * @param type The resource type. 66 | * @param identifier The resource identifier. 67 | * @return An input stream if the resource exists and can be opened, else null. 68 | * @see #getInputStream(String) 69 | */ 70 | default @Nullable InputStream getInputStream(@NotNull ResourceType type, @NotNull Identifier identifier) 71 | { 72 | return this.getInputStream(asPath(type, identifier)); 73 | } 74 | 75 | /** 76 | * Returns an input stream from the specified path. 77 | * 78 | * @param path The path to the resource. 79 | * @return An input stream if the resource exists and can be opened, else null. 80 | * @see #getInputStream(ResourceType, Identifier) 81 | */ 82 | @Nullable InputStream getInputStream(@NotNull String path); 83 | 84 | /** 85 | * Returns the list of namespaces existing in the resource pack. 86 | * 87 | * @param type The resource type. 88 | * @return A list of namespaces. 89 | */ 90 | @NotNull Collection getNamespaces(@NotNull ResourceType type); 91 | 92 | /** 93 | * Puts the data in the specified resource. 94 | * 95 | * @param type The resource type. 96 | * @param identifier The resource identifier. 97 | * @param out The data. 98 | * @return True if the data has been successfully put, else false. 99 | * @see #put(String, byte[]) 100 | */ 101 | default boolean put(@NotNull ResourceType type, @NotNull Identifier identifier, @NotNull byte[] out) 102 | { 103 | return this.put(asPath(type, identifier), out); 104 | } 105 | 106 | /** 107 | * Puts the data in the specified resource. 108 | * 109 | * @param path The resource path. 110 | * @param out The data. 111 | * @return True if the data has been successfully put, else false. 112 | * @see #put(ResourceType, Identifier, byte[]) 113 | */ 114 | boolean put(@NotNull String path, @NotNull byte[] out); 115 | 116 | /** 117 | * Copies the resource from this accessor to the specified accessor. 118 | * 119 | * @param outAccessor The output resource accessor. 120 | * @param type The resource type. 121 | * @param identifier The resource identifier. 122 | * @see #copy(ResourceAccessor, String) 123 | */ 124 | default void copy(@NotNull ResourceAccessor outAccessor, @NotNull ResourceType type, @NotNull Identifier identifier) 125 | { 126 | InputStream in = this.getInputStream(type, identifier); 127 | if (in == null) { 128 | return; 129 | } 130 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 131 | try { 132 | transferTo(in, out); 133 | outAccessor.put(type, identifier, out.toByteArray()); 134 | out.close(); 135 | in.close(); 136 | } catch (IOException e) { 137 | } 138 | } 139 | 140 | static long transferTo(@NotNull InputStream in, @NotNull OutputStream out) throws IOException 141 | { 142 | Objects.requireNonNull(in, "in"); 143 | Objects.requireNonNull(out, "out"); 144 | long transferred = 0L; 145 | 146 | int read; 147 | for (byte[] buffer = new byte[8192]; (read = in.read(buffer, 0, 8192)) >= 0; transferred += (long) read) { 148 | out.write(buffer, 0, read); 149 | } 150 | 151 | return transferred; 152 | } 153 | 154 | /** 155 | * Copies the resource from this accessor to the specified accessor. 156 | * 157 | * @param outAccessor The output resource accessor. 158 | * @param path The resource path. 159 | * @see #copy(ResourceAccessor, ResourceType, Identifier) 160 | */ 161 | default void copy(@NotNull ResourceAccessor outAccessor, @NotNull String path) 162 | { 163 | InputStream in = this.getInputStream(path); 164 | if (in == null) { 165 | return; 166 | } 167 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 168 | try { 169 | transferTo(in, out); 170 | outAccessor.put(path, out.toByteArray()); 171 | out.close(); 172 | in.close(); 173 | } catch (IOException e) { 174 | } 175 | } 176 | 177 | default boolean rename(@NotNull ResourceAccessor outAccessor, @NotNull ResourceType type, @NotNull Identifier before, @NotNull Identifier after) 178 | { 179 | InputStream in = this.getInputStream(type, before); 180 | if (in == null) { 181 | return false; 182 | } 183 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 184 | try { 185 | transferTo(in, out); 186 | outAccessor.put(type, after, out.toByteArray()); 187 | out.close(); 188 | in.close(); 189 | return true; 190 | } catch (IOException e) { 191 | return false; 192 | } 193 | } 194 | 195 | static @NotNull String asPath(@NotNull ResourceType type, @NotNull Identifier identifier) 196 | { 197 | return type.getName() + "/" + identifier.getNamespace() + "/" + identifier.getName(); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/fs/ZipAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fs; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 21 | import org.aperlambda.lambdacommon.Identifier; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.util.Collection; 29 | import java.util.stream.Collectors; 30 | import java.util.stream.Stream; 31 | import java.util.zip.ZipEntry; 32 | import java.util.zip.ZipFile; 33 | 34 | /** 35 | * Represents the ZIP resource input accessor. 36 | *

37 | * This is read-only. 38 | * 39 | * @author LambdAurora 40 | * @version 1.0.0 41 | * @since 1.0.0 42 | */ 43 | public class ZipAccessor implements ResourceAccessor 44 | { 45 | private final ZipFile zip; 46 | 47 | public ZipAccessor(@NotNull File file) throws IOException 48 | { 49 | this.zip = new ZipFile(file); 50 | } 51 | 52 | @Override 53 | public boolean has(@NotNull ResourceType type, @NotNull Identifier identifier) 54 | { 55 | return this.zip.getEntry(ResourceAccessor.asPath(type, identifier)) != null; 56 | } 57 | 58 | @Override 59 | public @Nullable InputStream getInputStream(@NotNull String path) 60 | { 61 | ZipEntry entry = this.zip.getEntry(path); 62 | if (entry == null) 63 | return null; 64 | try { 65 | return this.zip.getInputStream(entry); 66 | } catch (IOException e) { 67 | return null; 68 | } 69 | } 70 | 71 | @Override 72 | public @NotNull Stream searchIn(@NotNull ResourceType type, @NotNull Identifier parent) 73 | { 74 | return this.zip.stream() 75 | .filter(entry -> entry.getName().startsWith(type.getName() + "/") 76 | && !entry.isDirectory() 77 | && !entry.getName().endsWith("/.DS_Store") /* Come on, don't include this in your resource packs >:( */) 78 | .map(entry -> { 79 | String name = entry.getName().replace(type.getName() + "/", ""); 80 | int first = name.indexOf("/"); 81 | if (first == name.length() - 1 || first == -1) 82 | return Identifier.IDENTIFIER_INVALID; 83 | return new Identifier(name.substring(0, first), name.substring(first + 1)); 84 | }) 85 | .filter(id -> !id.equals(Identifier.IDENTIFIER_INVALID)) 86 | .filter(id -> id.getNamespace().equals(parent.getNamespace())) 87 | .filter(id -> id.getName().startsWith(parent.getName())); 88 | } 89 | 90 | @Override 91 | public @NotNull Collection getNamespaces(@NotNull ResourceType type) 92 | { 93 | return this.zip.stream().filter(entry -> entry.getName().startsWith(type.getName() + "/") && entry.isDirectory()) 94 | .map(entry -> entry.getName().split("/")) 95 | .filter(entry -> entry.length == 2) 96 | .map(entry -> entry[1]) 97 | .distinct() 98 | .collect(Collectors.toList()); 99 | } 100 | 101 | @Override 102 | public boolean put(@NotNull String path, @NotNull byte[] out) 103 | { 104 | // Not supported. 105 | return false; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/fs/ZipOutputAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fs; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 21 | import org.aperlambda.lambdacommon.Identifier; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.util.*; 28 | import java.util.stream.Collectors; 29 | import java.util.stream.Stream; 30 | import java.util.zip.ZipEntry; 31 | import java.util.zip.ZipOutputStream; 32 | 33 | /** 34 | * Represents the ZIP resource output accessor. 35 | *

36 | * This is write-only and read-only to an extent. 37 | * 38 | * @author LambdAurora 39 | * @version 1.0.0 40 | * @since 1.0.0 41 | */ 42 | public class ZipOutputAccessor implements ResourceAccessor 43 | { 44 | private final Map> resources = new HashMap<>(); 45 | private final ZipOutputStream zipOut; 46 | 47 | public ZipOutputAccessor(@NotNull ZipOutputStream zipOut) 48 | { 49 | this.zipOut = zipOut; 50 | } 51 | 52 | @Override 53 | public boolean has(@NotNull ResourceType type, @NotNull Identifier identifier) 54 | { 55 | return false; 56 | } 57 | 58 | @Override 59 | public @NotNull Stream searchIn(@NotNull ResourceType type, @NotNull Identifier parent) 60 | { 61 | return Stream.builder().build(); 62 | } 63 | 64 | @Override 65 | public @Nullable InputStream getInputStream(@NotNull String path) 66 | { 67 | return null; 68 | } 69 | 70 | @Override 71 | public @NotNull Collection getNamespaces(@NotNull ResourceType type) 72 | { 73 | return this.resources.getOrDefault(type, new ArrayList<>()) 74 | .stream().map(Identifier::getNamespace) 75 | .distinct().collect(Collectors.toList()); 76 | } 77 | 78 | @Override 79 | public boolean put(@NotNull ResourceType type, @NotNull Identifier identifier, @NotNull byte[] out) 80 | { 81 | boolean result = this.put(ResourceAccessor.asPath(type, identifier), out); 82 | 83 | if (result) { 84 | List identifiers = this.resources.computeIfAbsent(type, k -> new ArrayList<>()); 85 | identifiers.add(identifier); 86 | } 87 | 88 | return result; 89 | } 90 | 91 | @Override 92 | public boolean put(@NotNull String path, @NotNull byte[] out) 93 | { 94 | ZipEntry entry = new ZipEntry(path); 95 | try { 96 | this.zipOut.putNextEntry(entry); 97 | 98 | this.zipOut.write(out, 0, out.length); 99 | this.zipOut.closeEntry(); 100 | 101 | return true; 102 | } catch (IOException e) { 103 | return false; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/image/BasicBufferedImage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.image; 19 | 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import javax.imageio.ImageIO; 23 | import java.awt.image.BufferedImage; 24 | import java.io.ByteArrayOutputStream; 25 | import java.io.IOException; 26 | 27 | /** 28 | * Represents a basic image using BufferedImage as implementation. 29 | * 30 | * @author LambdAurora 31 | * @version 1.0.0 32 | * @since 1.0.0 33 | */ 34 | public class BasicBufferedImage implements BasicImage 35 | { 36 | private final BufferedImage image; 37 | 38 | public BasicBufferedImage(@NotNull BufferedImage image) 39 | { 40 | this.image = image; 41 | } 42 | 43 | @Override 44 | public int getWidth() 45 | { 46 | return this.image.getWidth(); 47 | } 48 | 49 | @Override 50 | public int getHeight() 51 | { 52 | return this.image.getHeight(); 53 | } 54 | 55 | @Override 56 | public int getPixelColor(int x, int y) 57 | { 58 | return this.image.getRGB(x, y); 59 | } 60 | 61 | @Override 62 | public void setPixelColor(int x, int y, int color) 63 | { 64 | this.image.setRGB(x, y, color); 65 | } 66 | 67 | @Override 68 | public byte[] getBytes() 69 | { 70 | try { 71 | return toByteArrayAutoClosable(this.image, "png"); 72 | } catch (IOException e) { 73 | return new byte[0]; 74 | } 75 | } 76 | 77 | @Override 78 | public @NotNull BasicImage getSubImage(int x, int y, int width, int height) 79 | { 80 | return new BasicBufferedImage(this.image.getSubimage(x, y, width, height)); 81 | } 82 | 83 | @Override 84 | public void close() 85 | { 86 | } 87 | 88 | /** 89 | * @author FlashyReese 90 | */ 91 | private static byte[] toByteArrayAutoClosable(@NotNull BufferedImage image, @NotNull String imageType) throws IOException 92 | { 93 | try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { 94 | ImageIO.write(image, imageType, out); // Technically we don't need a imageType param, I'm pretty sure all mc resource packs are png only. 95 | return out.toByteArray(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/image/BasicImage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.image; 19 | 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | /** 23 | * Represents a basic image. 24 | * 25 | * @author LambdAurora 26 | * @version 1.0.0 27 | * @since 1.0.0 28 | */ 29 | public interface BasicImage 30 | { 31 | /** 32 | * Gets the width of the image. 33 | * 34 | * @return The width. 35 | */ 36 | int getWidth(); 37 | 38 | /** 39 | * Gets the height of the image. 40 | * 41 | * @return The height. 42 | */ 43 | int getHeight(); 44 | 45 | /** 46 | * Gets the pixel color at the specified coordinates. 47 | * 48 | * @param x The X-coordinate of the pixel. 49 | * @param y The Y-coordinate of the pixel. 50 | * @return The pixel color. 51 | */ 52 | int getPixelColor(int x, int y); 53 | 54 | /** 55 | * Sets the pixel color at the specified coordinates. 56 | * 57 | * @param x The X-coordinate of the pixel. 58 | * @param y The Y-coordinate of the pixel. 59 | * @param color The pixel color. 60 | */ 61 | void setPixelColor(int x, int y, int color); 62 | 63 | /** 64 | * Returns the bytes of the image. 65 | * 66 | * @return The bytes. 67 | */ 68 | byte[] getBytes(); 69 | 70 | /** 71 | * Returns a sub-image from this image. 72 | * 73 | * @param x The X-coordinate of the start of the sub-image. 74 | * @param y The Y-coordinate of the start of the sub-image. 75 | * @param width The width of the sub-image. 76 | * @param height The height of the sub-image. 77 | * @return The sub-image. 78 | */ 79 | @NotNull BasicImage getSubImage(int x, int y, int width, int height); 80 | 81 | /** 82 | * Free the image from memory. 83 | *

84 | * Useful for implementations using native libraries/code. 85 | */ 86 | void close(); 87 | } 88 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/image/BufferedImageProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.image; 19 | 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import javax.imageio.ImageIO; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | 26 | /** 27 | * Represents an image provider using {@link BasicBufferedImage} as implementation. 28 | * 29 | * @author LambdAurora 30 | * @version 1.0.0 31 | * @since 1.0.0 32 | */ 33 | public class BufferedImageProvider implements ImageProvider 34 | { 35 | @Override 36 | public @NotNull BasicImage readImage(@NotNull InputStream inputStream) throws IOException 37 | { 38 | return new BasicBufferedImage(ImageIO.read(inputStream)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/image/ImageProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.image; 19 | 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | 25 | /** 26 | * Represents an image provider. 27 | * 28 | * @author LambdAurora 29 | * @version 1.0.0 30 | * @since 1.0.0 31 | */ 32 | public interface ImageProvider 33 | { 34 | /** 35 | * Reads an image from an input stream. 36 | * 37 | * @param inputStream The input stream. 38 | * @return A non-null image if read successfully. 39 | * @throws IOException If the image read failed. 40 | */ 41 | @NotNull BasicImage readImage(@NotNull InputStream inputStream) throws IOException; 42 | } 43 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/mcpatcher/MCPatcherParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.mcpatcher; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.BiomeUtils; 21 | import org.aperlambda.lambdacommon.Identifier; 22 | import org.aperlambda.lambdacommon.utils.Pair; 23 | import org.jetbrains.annotations.NotNull; 24 | import org.jetbrains.annotations.Nullable; 25 | 26 | import java.util.*; 27 | 28 | /** 29 | * Represents a set of utilities to parse the MCPatcher format. 30 | * 31 | * @author LambdAurora 32 | * @version 1.0.0 33 | * @since 1.0.0 34 | */ 35 | public final class MCPatcherParser 36 | { 37 | private MCPatcherParser() 38 | { 39 | throw new UnsupportedOperationException("MCPatcherParser only contains static definitions."); 40 | } 41 | 42 | public static @NotNull List parseIntList(@Nullable String string) 43 | { 44 | if (string == null) 45 | return Collections.emptyList(); 46 | 47 | List result = new ArrayList<>(); 48 | 49 | List tokens = tokenize(string, " ,"); 50 | for (String token : tokens) { 51 | if (token.contains("-")) { 52 | String[] rangeStr = token.split("-"); 53 | if (rangeStr.length == 2) { 54 | int min = parseInt(rangeStr[0], -1); 55 | int max = parseInt(rangeStr[0], -1); 56 | if (min != -1 && max != -1 && min <= max) { 57 | for (int i = min; i <= max; i++) { 58 | result.add(i); 59 | } 60 | } 61 | } 62 | } else { 63 | int i = parseInt(token, -1); 64 | if (i != -1) 65 | result.add(i); 66 | } 67 | } 68 | 69 | return result; 70 | } 71 | 72 | public static @NotNull List> parseIntRangeList(@Nullable String input) 73 | { 74 | if (input == null) 75 | return Collections.emptyList(); 76 | 77 | List> result = new ArrayList<>(); 78 | 79 | List tokens = tokenize(input, " ,"); 80 | for (String token : tokens) { 81 | Pair range = parseIntRange(token); 82 | if (range != null) 83 | result.add(range); 84 | } 85 | 86 | return result; 87 | } 88 | 89 | public static @Nullable Pair parseIntRange(@Nullable String input) 90 | { 91 | if (input == null) 92 | return null; 93 | 94 | String[] parts = input.split("-"); 95 | if (parts.length != 2) 96 | return null; 97 | 98 | int min = parseInt(parts[0], -1); 99 | int max = parseInt(parts[1], -1); 100 | 101 | if (min < 0 || max < 0) 102 | return null; 103 | 104 | return Pair.of(min, max); 105 | } 106 | 107 | public static @NotNull Pair> parseBiomeList(@Nullable String input) 108 | { 109 | List result = new ArrayList<>(); 110 | if (input == null) 111 | return Pair.of(false, result); 112 | 113 | boolean negative = false; 114 | if (input.startsWith("!")) { 115 | negative = true; 116 | input = input.substring(1); 117 | } 118 | 119 | List tokens = tokenize(input, " ,"); 120 | for (String token : tokens) { 121 | result.add(BiomeUtils.parseBiome(token)); 122 | } 123 | 124 | return Pair.of(negative, result); 125 | } 126 | 127 | public static Optional parseBoolean(@Nullable String input) 128 | { 129 | if (input == null) 130 | return Optional.empty(); 131 | 132 | if (input.equalsIgnoreCase("true")) 133 | return Optional.of(true); 134 | else if (input.equalsIgnoreCase("false")) 135 | return Optional.of(false); 136 | 137 | return Optional.empty(); 138 | } 139 | 140 | private static int parseInt(@NotNull String input, int defaultValue) 141 | { 142 | try { 143 | return Integer.parseInt(input); 144 | } catch (NumberFormatException e) { 145 | return defaultValue; 146 | } 147 | } 148 | 149 | private static @NotNull List tokenize(@NotNull String input, @NotNull String delim) 150 | { 151 | StringTokenizer tokenizer = new StringTokenizer(input, delim); 152 | List result = new ArrayList<>(); 153 | 154 | while (tokenizer.hasMoreTokens()) { 155 | String token = tokenizer.nextToken(); 156 | result.add(token); 157 | } 158 | 159 | return result; 160 | } 161 | 162 | public static Number toTickTime(@NotNull String time) 163 | { 164 | String[] parts = time.split(":"); 165 | if (parts.length != 2) 166 | return null; 167 | int h = Integer.parseInt(parts[0]); 168 | int m = Integer.parseInt(parts[1]); 169 | return h * 1000 + (m / 0.06F) - 6000; 170 | } 171 | 172 | public static int normalizeTickTime(int tickTime) 173 | { 174 | int result = tickTime % 24000; 175 | if (result < 0) { 176 | result += 24000; 177 | } 178 | return result; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /base/src/main/java/me/lambdaurora/mcpatcherpatcher/mcpatcher/RandomEntityRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.mcpatcher; 19 | 20 | import com.google.gson.JsonArray; 21 | import com.google.gson.JsonObject; 22 | import org.aperlambda.lambdacommon.Identifier; 23 | import org.aperlambda.lambdacommon.utils.Pair; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | import java.util.List; 27 | import java.util.Optional; 28 | import java.util.Properties; 29 | import java.util.stream.Collectors; 30 | 31 | /** 32 | * Represents a CET rule. 33 | * 34 | * @author LambdAurora 35 | * @version 1.0.0 36 | * @since 1.0.0 37 | */ 38 | public class RandomEntityRule 39 | { 40 | private final int index; 41 | private final Identifier baseId; 42 | private final List textures; 43 | private final List weights; 44 | private final String nameRegex; 45 | private final Pair> biomes; 46 | private final List> heights; 47 | private final Optional baby; 48 | 49 | public RandomEntityRule(int index, @NotNull Identifier baseId, @NotNull String valTextures, @NotNull Properties properties) 50 | { 51 | this.index = index; 52 | this.baseId = baseId; 53 | this.textures = MCPatcherParser.parseIntList(valTextures).stream().map(i -> new Identifier(baseId.getNamespace(), baseId.getName() + i + ".png")) 54 | .collect(Collectors.toList()); 55 | this.weights = MCPatcherParser.parseIntList(properties.getProperty("weights." + index)); 56 | String nameRegex = properties.getProperty("name." + index); 57 | if (nameRegex != null) { 58 | if (nameRegex.matches("$i?pattern:")) { 59 | // Just yeet pattern and make it a real regex 60 | nameRegex = nameRegex.substring(nameRegex.indexOf(":") + 1); 61 | nameRegex = nameRegex.replaceAll("\\*", ".*") 62 | .replaceAll("\\?", ".?"); 63 | } else if (nameRegex.startsWith("iregex:")) { 64 | nameRegex = nameRegex.substring(nameRegex.indexOf(":") + 1); 65 | nameRegex = "(?i)" + nameRegex; 66 | } else if (nameRegex.startsWith("regex:")) 67 | nameRegex = nameRegex.substring(nameRegex.indexOf(":") + 1); 68 | } 69 | this.nameRegex = nameRegex; 70 | this.biomes = MCPatcherParser.parseBiomeList(properties.getProperty("biomes." + index)); 71 | this.heights = MCPatcherParser.parseIntRangeList(properties.getProperty("heights." + index)); 72 | this.baby = MCPatcherParser.parseBoolean(properties.getProperty("baby." + index)); 73 | } 74 | 75 | /** 76 | * Returns the converted rule to the VMT format. 77 | * 78 | * @return The JSON. 79 | */ 80 | public @NotNull JsonObject toJson() 81 | { 82 | return this.getNameJson(); 83 | } 84 | 85 | public @NotNull JsonObject getNameJson() 86 | { 87 | if (this.nameRegex == null) 88 | return this.getBiomesJson(); 89 | 90 | JsonObject json = new JsonObject(); 91 | json.addProperty("type", "varied-mobs:name"); 92 | json.addProperty("regex", this.nameRegex); 93 | json.add("value", this.getBiomesJson()); 94 | 95 | return json; 96 | } 97 | 98 | public @NotNull JsonObject getBiomesJson() 99 | { 100 | if (this.biomes.value.size() == 0) 101 | return this.getPickJson(); 102 | 103 | JsonObject json = new JsonObject(); 104 | json.addProperty("type", "varied-mobs:biome"); 105 | 106 | JsonArray biomes = new JsonArray(); 107 | this.biomes.value.forEach(id -> biomes.add(id.toString())); 108 | json.add("biomes", biomes); 109 | 110 | if (this.biomes.key) 111 | json.addProperty("negate", true); 112 | 113 | json.add("value", this.getBabyJson()); 114 | 115 | return json; 116 | } 117 | 118 | // Borked 119 | public @NotNull JsonObject getHeightsJson() 120 | { 121 | JsonObject babyJson = this.getBabyJson(); 122 | if (this.heights.size() != 0) { 123 | JsonObject json = new JsonObject(); 124 | json.addProperty("type", "varied-mobs:y-prop"); 125 | JsonArray positions = new JsonArray(); 126 | int rangeCount = 0; 127 | for (Pair range : this.heights) { 128 | positions.add(range.key); 129 | } 130 | json.add("positions", positions); 131 | JsonArray choices = new JsonArray(); 132 | 133 | return json; 134 | } 135 | return babyJson; 136 | } 137 | 138 | public @NotNull JsonObject getBabyJson() 139 | { 140 | return this.baby.map(baby -> { 141 | JsonObject json = new JsonObject(); 142 | 143 | json.addProperty("type", "varied-mobs:baby"); 144 | if (!baby) 145 | json.addProperty("negate", true); 146 | json.add("value", this.getPickJson()); 147 | 148 | return json; 149 | }).orElse(this.getPickJson()); 150 | } 151 | 152 | public @NotNull JsonObject getPickJson() 153 | { 154 | JsonObject json = new JsonObject(); 155 | 156 | json.addProperty("type", "varied-mobs:pick"); 157 | 158 | if (this.weights.size() > 0) { 159 | JsonArray weights = new JsonArray(); 160 | this.weights.forEach(weights::add); 161 | json.add("weights", weights); 162 | } 163 | 164 | JsonArray choices = new JsonArray(); 165 | // @TODO textures id move 166 | this.textures.forEach(id -> choices.add(id.toString())); 167 | json.add("choices", choices); 168 | 169 | return json; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | id 'com.github.johnrengelman.shadow' version '5.1.0' 5 | id 'net.minecrell.licenser' version '0.4.1' 6 | } 7 | 8 | version = "${project.version}" 9 | archivesBaseName = project.archives_base_name 10 | 11 | allprojects { subProject -> 12 | subProject.apply plugin: 'com.github.johnrengelman.shadow' 13 | subProject.apply plugin: 'net.minecrell.licenser' 14 | group = project.maven_group 15 | 16 | repositories { 17 | mavenLocal() 18 | mavenCentral() 19 | maven { 20 | name = "AperLambda" 21 | url = 'https://aperlambda.github.io/maven' 22 | } 23 | } 24 | 25 | license { 26 | header file("$rootDir/HEADER") 27 | include '**/*.java' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fabric/build.gradle: -------------------------------------------------------------------------------- 1 | import net.fabricmc.loom.task.RemapJarTask 2 | 3 | plugins { 4 | id 'fabric-loom' version '0.5-SNAPSHOT' 5 | id 'java-library' 6 | id 'maven-publish' 7 | } 8 | 9 | version = "${project.version}+${project.minecraft_version}" 10 | archivesBaseName = project.archives_base_name + "-fabric" 11 | 12 | minecraft { 13 | } 14 | 15 | repositories { 16 | maven { 17 | name = 'CottonMC' 18 | url = 'http://server.bbkr.space:8081/artifactory/libs-snapshot' 19 | } 20 | } 21 | 22 | configurations { 23 | shadow 24 | shadowInternal 25 | api.extendsFrom shadow 26 | } 27 | 28 | dependencies { 29 | // To change the versions see the gradle.properties file. 30 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 31 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" 32 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" 33 | 34 | // Fabric API. This is technically optional, but you probably want it anyway. 35 | modImplementation(fabricApi.module('fabric-resource-loader-v0', project.fabric_api_version)) 36 | 37 | modRuntime("io.github.prospector:modmenu:${project.modmenu_version}") { 38 | exclude group: 'net.fabricmc.fabric-api' 39 | } 40 | 41 | shadow(project(":base")) { 42 | exclude group: 'org.aperlambda' 43 | } 44 | shadow("org.aperlambda:lambdajcommon:1.8.1") { 45 | // Minecraft already has all that google crap. 46 | exclude group: 'com.google.code.gson' 47 | exclude group: 'com.google.guava' 48 | } 49 | } 50 | 51 | processResources { 52 | inputs.property "version", project.version 53 | 54 | from(sourceSets.main.resources.srcDirs) { 55 | include "fabric.mod.json" 56 | expand "version": project.version 57 | } 58 | 59 | from(sourceSets.main.resources.srcDirs) { 60 | exclude "fabric.mod.json" 61 | } 62 | } 63 | 64 | java { 65 | sourceCompatibility = JavaVersion.VERSION_1_8 66 | targetCompatibility = JavaVersion.VERSION_1_8 67 | 68 | withSourcesJar() 69 | } 70 | 71 | jar { 72 | from "../LICENSE" 73 | manifest { 74 | attributes( 75 | 'Main-Class': project(':base').mainClassName 76 | ) 77 | } 78 | } 79 | 80 | shadowJar { 81 | dependsOn(project(":base").jar) 82 | configurations = [project.configurations.shadow] 83 | archiveClassifier.set('dev') 84 | 85 | relocate 'org.aperlambda.lambdacommon', 'me.lambdaurora.mcpatcherpatcher.shadow.lambdacommon' 86 | relocate 'com.google.gson', 'me.lambdaurora.mcpatcherpatcher.shadow.gson' 87 | } 88 | 89 | task shadowRemapJar(type: RemapJarTask) { 90 | dependsOn shadowJar 91 | 92 | input = file("${project.buildDir}/libs/$archivesBaseName-$version-dev.jar") 93 | archiveName = "${archivesBaseName}-${version}.jar" 94 | addNestedDependencies = true 95 | } 96 | 97 | build.dependsOn shadowRemapJar 98 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/MCPatcherPatcherFabric.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.MCPatcherPatcher; 21 | import me.lambdaurora.mcpatcherpatcher.fabric.image.NativeImageProvider; 22 | import me.lambdaurora.mcpatcherpatcher.fabric.resource.MCPPResourcePack; 23 | import net.fabricmc.api.ClientModInitializer; 24 | import net.fabricmc.loader.api.FabricLoader; 25 | import org.apache.logging.log4j.LogManager; 26 | import org.apache.logging.log4j.Logger; 27 | 28 | import java.io.IOException; 29 | import java.nio.file.FileSystems; 30 | import java.nio.file.Files; 31 | import java.nio.file.Path; 32 | 33 | /** 34 | * Represents the MCPatcherPatcher Fabric mod. 35 | * 36 | * @author LambdAurora 37 | * @version 1.0.0 38 | * @since 1.0.0 39 | */ 40 | public class MCPatcherPatcherFabric implements ClientModInitializer { 41 | public static final String NAMESPACE = "mcpatcherpatcher"; 42 | public static final net.minecraft.util.Identifier DUMMY_ID = new net.minecraft.util.Identifier(NAMESPACE, "dummy"); 43 | public static final Path DEBUG_PATH = FileSystems.getDefault().getPath("debug", NAMESPACE); 44 | private static final String DEBUG_PROPERTY = System.getProperty("mcpatcherpatcher-debug"); 45 | private static MCPatcherPatcherFabric INSTANCE; 46 | public final Logger logger = LogManager.getLogger(NAMESPACE); 47 | public final MCPatcherPatcher main = new MCPatcherPatcher(new NativeImageProvider()); 48 | public MCPPResourcePack resourcePack; 49 | private boolean debug = FabricLoader.getInstance().isDevelopmentEnvironment() || (DEBUG_PROPERTY != null && DEBUG_PROPERTY.equals("true")); 50 | 51 | @Override 52 | public void onInitializeClient() { 53 | INSTANCE = this; 54 | 55 | if (this.debug) { 56 | if (!Files.exists(DEBUG_PATH)) { 57 | try { 58 | Files.createDirectories(DEBUG_PATH); 59 | } catch (IOException e) { 60 | this.log("Cannot debug: could not create debug directory."); 61 | this.debug = false; 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Prints a message to the terminal. 70 | * 71 | * @param info The message to print. 72 | */ 73 | public void log(String info) { 74 | this.logger.info("[MCPatcherPatcher] " + info); 75 | } 76 | 77 | public boolean isDebug() { 78 | return this.debug; 79 | } 80 | 81 | /** 82 | * Returns the MCPatcherPatcher mod instance. 83 | * 84 | * @return The mod instance. 85 | */ 86 | public static MCPatcherPatcherFabric get() { 87 | return INSTANCE; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/fs/ResourceManagerAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric.fs; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.ResourceType; 21 | import me.lambdaurora.mcpatcherpatcher.fabric.util.IdentifierUtils; 22 | import me.lambdaurora.mcpatcherpatcher.fs.ResourceAccessor; 23 | import net.minecraft.resource.Resource; 24 | import net.minecraft.resource.ResourceManager; 25 | import org.aperlambda.lambdacommon.Identifier; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Nullable; 28 | 29 | import java.io.IOException; 30 | import java.io.InputStream; 31 | import java.util.Collection; 32 | import java.util.stream.Stream; 33 | 34 | /** 35 | * Represents a resource accessor using Minecraft's resource manager. 36 | */ 37 | public class ResourceManagerAccessor implements ResourceAccessor 38 | { 39 | private final ResourceManager resourceManager; 40 | 41 | public ResourceManagerAccessor(@NotNull ResourceManager resourceManager) 42 | { 43 | this.resourceManager = resourceManager; 44 | } 45 | 46 | @Override 47 | public boolean has(@NotNull ResourceType type, @NotNull Identifier identifier) 48 | { 49 | return this.resourceManager.containsResource(IdentifierUtils.toMc(identifier)); 50 | } 51 | 52 | @Override 53 | public @NotNull Stream searchIn(@NotNull ResourceType type, @NotNull Identifier parent) 54 | { 55 | return this.resourceManager.findResources(parent.getName(), path -> { 56 | for (int i = 0; i < path.length(); ++i) { 57 | if (!net.minecraft.util.Identifier.isPathCharacterValid(path.charAt(i))) { 58 | return false; 59 | } 60 | } 61 | 62 | return true; 63 | }).stream() 64 | .map(IdentifierUtils::fromMc) 65 | .filter(id -> id.getNamespace().equals(parent.getNamespace())); 66 | } 67 | 68 | @Override 69 | public @Nullable InputStream getInputStream(@NotNull ResourceType type, @NotNull Identifier identifier) 70 | { 71 | try { 72 | Resource resource = this.resourceManager.getResource(IdentifierUtils.toMc(identifier)); 73 | if (resource == null) 74 | return null; 75 | return resource.getInputStream(); 76 | } catch (IOException e) { 77 | return null; 78 | } 79 | } 80 | 81 | @Override 82 | public @Nullable InputStream getInputStream(@NotNull String path) 83 | { 84 | return null; 85 | } 86 | 87 | @Override 88 | public @NotNull Collection getNamespaces(@NotNull ResourceType type) 89 | { 90 | return this.resourceManager.getAllNamespaces(); 91 | } 92 | 93 | @Override 94 | public boolean put(@NotNull String path, @NotNull byte[] out) 95 | { 96 | return false; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/image/BasicNativeImage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric.image; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.image.BasicImage; 21 | import net.minecraft.client.texture.NativeImage; 22 | import org.jetbrains.annotations.NotNull; 23 | 24 | import java.io.IOException; 25 | 26 | /** 27 | * Represents a basic image using NativeImage as implementation. 28 | * 29 | * @author LambdAurora 30 | * @version 1.0.0 31 | * @since 1.0.0 32 | */ 33 | public class BasicNativeImage implements BasicImage 34 | { 35 | private final NativeImage image; 36 | 37 | public BasicNativeImage(@NotNull NativeImage image) 38 | { 39 | this.image = image; 40 | } 41 | 42 | @Override 43 | public int getWidth() 44 | { 45 | return this.image.getWidth(); 46 | } 47 | 48 | @Override 49 | public int getHeight() 50 | { 51 | return this.image.getHeight(); 52 | } 53 | 54 | @Override 55 | public int getPixelColor(int x, int y) 56 | { 57 | return this.image.getPixelColor(x, y); 58 | } 59 | 60 | @Override 61 | public void setPixelColor(int x, int y, int color) 62 | { 63 | this.image.setPixelColor(x, y, color); 64 | } 65 | 66 | @Override 67 | public byte[] getBytes() 68 | { 69 | try { 70 | return this.image.getBytes(); 71 | } catch (IOException e) { 72 | return null; 73 | } 74 | } 75 | 76 | @Override 77 | public @NotNull BasicImage getSubImage(int x, int y, int width, int height) 78 | { 79 | BasicNativeImage subImage = new BasicNativeImage(new NativeImage(width, height, false)); 80 | 81 | for (int iY = 0; iY < height && (y + iY) < this.getHeight(); iY++) { 82 | for (int iX = 0; iX < width && (x + iX) < this.getWidth(); iX++) { 83 | subImage.setPixelColor(iX, iY, this.getPixelColor(x + iX, y + iY)); 84 | } 85 | } 86 | 87 | return subImage; 88 | } 89 | 90 | @Override 91 | public void close() 92 | { 93 | this.image.close(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/image/NativeImageProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric.image; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.image.BasicImage; 21 | import me.lambdaurora.mcpatcherpatcher.image.ImageProvider; 22 | import net.minecraft.client.texture.NativeImage; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | 28 | /** 29 | * Represents an image provider using {@link BasicNativeImage} as implementation. 30 | * 31 | * @author LambdAurora 32 | * @version 1.0.0 33 | * @since 1.0.0 34 | */ 35 | public class NativeImageProvider implements ImageProvider 36 | { 37 | @Override 38 | public @NotNull BasicImage readImage(@NotNull InputStream inputStream) throws IOException 39 | { 40 | return new BasicNativeImage(NativeImage.read(inputStream)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/mixin/ReloadableResourceManagerImplMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric.mixin; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.fabric.MCPatcherPatcherFabric; 21 | import me.lambdaurora.mcpatcherpatcher.fabric.fs.ResourceManagerAccessor; 22 | import me.lambdaurora.mcpatcherpatcher.fabric.resource.MCPPResourcePack; 23 | import net.minecraft.resource.*; 24 | import net.minecraft.util.Unit; 25 | import org.spongepowered.asm.mixin.Final; 26 | import org.spongepowered.asm.mixin.Mixin; 27 | import org.spongepowered.asm.mixin.Shadow; 28 | import org.spongepowered.asm.mixin.injection.At; 29 | import org.spongepowered.asm.mixin.injection.Inject; 30 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 31 | 32 | import java.util.List; 33 | import java.util.concurrent.CompletableFuture; 34 | import java.util.concurrent.Executor; 35 | import java.util.stream.Stream; 36 | 37 | @Mixin(ReloadableResourceManagerImpl.class) 38 | public abstract class ReloadableResourceManagerImplMixin implements ReloadableResourceManager { 39 | @Shadow 40 | @Final 41 | private ResourceType type; 42 | 43 | @Shadow 44 | public abstract void addPack(ResourcePack resourcePack); 45 | 46 | @Shadow 47 | public abstract Stream streamResourcePacks(); 48 | 49 | @Inject( 50 | method = "beginMonitoredReload", 51 | at = @At( 52 | value = "INVOKE", 53 | target = "Lnet/minecraft/resource/ReloadableResourceManagerImpl;beginReloadInner(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/List;Ljava/util/concurrent/CompletableFuture;)Lnet/minecraft/resource/ResourceReloadMonitor;", 54 | shift = At.Shift.BEFORE 55 | ) 56 | ) 57 | private void onPostReload(Executor prepareExecutor, Executor applyExecutor, CompletableFuture initialStage, List packs, CallbackInfoReturnable cir) { 58 | if (this.type == ResourceType.SERVER_DATA) 59 | return; 60 | 61 | MCPatcherPatcherFabric mod = MCPatcherPatcherFabric.get(); 62 | mod.main.convert(new ResourceManagerAccessor(this), mod.resourcePack = new MCPPResourcePack()); 63 | 64 | mod.log("Inject generated resource packs."); 65 | this.addPack(mod.resourcePack); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/mixin/ZipResourcePackMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric.mixin; 19 | 20 | import me.lambdaurora.mcpatcherpatcher.fabric.MCPatcherPatcherFabric; 21 | import net.minecraft.resource.ResourceType; 22 | import net.minecraft.resource.ZipResourcePack; 23 | import net.minecraft.util.Identifier; 24 | import net.minecraft.util.InvalidIdentifierException; 25 | import org.spongepowered.asm.mixin.Mixin; 26 | import org.spongepowered.asm.mixin.injection.At; 27 | import org.spongepowered.asm.mixin.injection.Inject; 28 | import org.spongepowered.asm.mixin.injection.Redirect; 29 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 30 | 31 | import java.util.Collection; 32 | import java.util.function.Predicate; 33 | import java.util.stream.Collectors; 34 | 35 | @Mixin(ZipResourcePack.class) 36 | public class ZipResourcePackMixin 37 | { 38 | @Redirect(method = "findResources", at = @At(value = "NEW", target = "net/minecraft/util/Identifier")) 39 | private Identifier newIdentifier(String namespace, String path) 40 | { 41 | try { 42 | return new Identifier(namespace, path); 43 | } catch (InvalidIdentifierException e) { 44 | return MCPatcherPatcherFabric.DUMMY_ID; 45 | } 46 | } 47 | 48 | @Inject(method = "findResources", at = @At("RETURN"), cancellable = true) 49 | private void onFindResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate pathFilter, CallbackInfoReturnable> cir) 50 | { 51 | Collection identifiers = cir.getReturnValue(); 52 | cir.setReturnValue(identifiers.stream().filter(id -> !id.equals(MCPatcherPatcherFabric.DUMMY_ID)).collect(Collectors.toList())); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/resource/MCPPResourcePack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric.resource; 19 | 20 | import it.unimi.dsi.fastutil.objects.Object2ObjectMap; 21 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; 22 | import me.lambdaurora.mcpatcherpatcher.fabric.MCPatcherPatcherFabric; 23 | import me.lambdaurora.mcpatcherpatcher.fabric.util.IdentifierUtils; 24 | import me.lambdaurora.mcpatcherpatcher.fs.ResourceAccessor; 25 | import net.minecraft.SharedConstants; 26 | import net.minecraft.resource.ResourcePack; 27 | import net.minecraft.resource.ResourceType; 28 | import net.minecraft.resource.metadata.ResourceMetadataReader; 29 | import net.minecraft.util.Identifier; 30 | import org.apache.commons.compress.utils.Charsets; 31 | import org.apache.commons.io.IOUtils; 32 | import org.jetbrains.annotations.NotNull; 33 | import org.jetbrains.annotations.Nullable; 34 | 35 | import java.io.ByteArrayInputStream; 36 | import java.io.IOException; 37 | import java.io.InputStream; 38 | import java.nio.charset.StandardCharsets; 39 | import java.nio.file.Files; 40 | import java.nio.file.Path; 41 | import java.nio.file.StandardOpenOption; 42 | import java.util.*; 43 | import java.util.function.Predicate; 44 | import java.util.stream.Collectors; 45 | import java.util.stream.Stream; 46 | 47 | /** 48 | * Represents the virtual resource pack of MCPatcherPatcher. 49 | * 50 | * @author LambdAurora 51 | * @version 1.0.0 52 | * @since 1.0.0 53 | */ 54 | public class MCPPResourcePack implements ResourcePack, ResourceAccessor { 55 | private final List namespaces = new ArrayList<>(); 56 | private final Object2ObjectMap resources = new Object2ObjectOpenHashMap<>(); 57 | 58 | @Override 59 | public boolean put(@NotNull String resource, byte[] data) { 60 | Identifier id = fromPath(resource); 61 | if (!this.namespaces.contains(id.getNamespace())) { 62 | this.namespaces.add(id.getNamespace()); 63 | } 64 | 65 | if (MCPatcherPatcherFabric.get().isDebug()) { 66 | Path path = MCPatcherPatcherFabric.DEBUG_PATH.resolve(resource).normalize(); 67 | boolean canWrite = true; 68 | if (!Files.exists(path.getParent())) { 69 | try { 70 | Files.createDirectories(path.getParent()); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | canWrite = false; 74 | } 75 | } 76 | if (canWrite) { 77 | try { 78 | Files.write(path, data, StandardOpenOption.CREATE, StandardOpenOption.WRITE); 79 | } catch (IOException e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | } 84 | 85 | this.resources.put(resource, data); 86 | return true; 87 | } 88 | 89 | public void putText(@NotNull String resource, @NotNull String text) { 90 | this.put(resource, text.getBytes(StandardCharsets.UTF_8)); 91 | } 92 | 93 | @Override 94 | public InputStream openRoot(String fileName) throws IOException { 95 | InputStream defaultStream = this.openDefault(fileName); 96 | if (defaultStream != null) 97 | return defaultStream; 98 | 99 | byte[] data; 100 | if ((data = this.resources.get(fileName)) != null) { 101 | return new ByteArrayInputStream(data); 102 | } 103 | throw new IOException("Generated resources pack has no data or alias for " + fileName); 104 | } 105 | 106 | @Override 107 | public @Nullable InputStream getInputStream(@NotNull String path) { 108 | InputStream defaultStream = this.openDefault(path); 109 | if (defaultStream != null) 110 | return defaultStream; 111 | 112 | if (!this.resources.containsKey(path)) 113 | return null; 114 | return new ByteArrayInputStream(this.resources.get(path)); 115 | } 116 | 117 | private @Nullable InputStream openDefault(String path) { 118 | if (path.equals("pack.mcmeta")) { 119 | return IOUtils.toInputStream( 120 | String.format("{\"pack\":{\"pack_format\":%d,\"description\":\"MCPatcherPatcher runtime resource pack.\"}}", 121 | SharedConstants.getGameVersion().getPackVersion()), 122 | Charsets.UTF_8); 123 | } 124 | return null; 125 | } 126 | 127 | @Override 128 | public InputStream open(ResourceType type, Identifier id) throws IOException { 129 | if (type == ResourceType.SERVER_DATA) throw new IOException("Reading server data from MCPatcherPatcher client resource pack"); 130 | return this.openRoot(type.getDirectory() + "/" + id.getNamespace() + "/" + id.getPath()); 131 | } 132 | 133 | @Override 134 | public Collection findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate pathFilter) { 135 | if (type == ResourceType.SERVER_DATA) return Collections.emptyList(); 136 | String start = "assets/" + namespace + "/" + prefix; 137 | return this.resources.keySet().stream() 138 | .filter(s -> s.startsWith(start) && pathFilter.test(s)) 139 | .map(MCPPResourcePack::fromPath) 140 | .collect(Collectors.toList()); 141 | } 142 | 143 | @Override 144 | public @NotNull Stream searchIn(@NotNull me.lambdaurora.mcpatcherpatcher.ResourceType type, @NotNull org.aperlambda.lambdacommon.Identifier parent) { 145 | String parentPath = ResourceAccessor.asPath(type, parent); 146 | 147 | return this.resources.keySet().stream().filter(path -> path.startsWith(parentPath)) 148 | .map(path -> { 149 | int first = path.indexOf('/'); 150 | if (first == path.length() - 1 || first == -1) 151 | return null; 152 | return new org.aperlambda.lambdacommon.Identifier(path.substring(0, first), path.substring(first + 1)); 153 | }) 154 | .filter(Objects::nonNull); 155 | } 156 | 157 | @Override 158 | public boolean has(@NotNull me.lambdaurora.mcpatcherpatcher.ResourceType type, @NotNull org.aperlambda.lambdacommon.Identifier identifier) { 159 | return this.contains(toMcResourceType(type), IdentifierUtils.toMc(identifier)); 160 | } 161 | 162 | @Override 163 | public boolean contains(ResourceType type, Identifier id) { 164 | String path = type.getDirectory() + "/" + id.getNamespace() + "/" + id.getPath(); 165 | return this.resources.containsKey(path); 166 | } 167 | 168 | @Override 169 | public Set getNamespaces(ResourceType type) { 170 | return new HashSet<>(this.namespaces); 171 | } 172 | 173 | @Override 174 | public @NotNull Collection getNamespaces(@NotNull me.lambdaurora.mcpatcherpatcher.ResourceType type) { 175 | return this.getNamespaces(toMcResourceType(type)); 176 | } 177 | 178 | @Override 179 | public T parseMetadata(ResourceMetadataReader metaReader) throws IOException { 180 | return null; 181 | } 182 | 183 | @Override 184 | public String getName() { 185 | return "MCPatcherPatcher generated resource pack"; 186 | } 187 | 188 | @Override 189 | public void close() { 190 | } 191 | 192 | private static @NotNull ResourceType toMcResourceType(@NotNull me.lambdaurora.mcpatcherpatcher.ResourceType resourceType) { 193 | switch (resourceType) { 194 | case DATA: 195 | return ResourceType.SERVER_DATA; 196 | case ASSETS: 197 | return ResourceType.CLIENT_RESOURCES; 198 | } 199 | return ResourceType.CLIENT_RESOURCES; 200 | } 201 | 202 | private static Identifier fromPath(String path) { 203 | if (path.startsWith("assets/")) 204 | path = path.substring("assets/".length()); 205 | String[] split = path.split("/", 2); 206 | return new Identifier(split[0], split[1]); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /fabric/src/main/java/me/lambdaurora/mcpatcherpatcher/fabric/util/IdentifierUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 LambdAurora 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package me.lambdaurora.mcpatcherpatcher.fabric.util; 19 | 20 | import org.aperlambda.lambdacommon.Identifier; 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | /** 24 | * Represents an utility class for identifiers. 25 | * 26 | * @author LambdAurora 27 | * @version 1.0.0 28 | * @since 1.0.0 29 | */ 30 | public final class IdentifierUtils 31 | { 32 | private IdentifierUtils() 33 | { 34 | throw new UnsupportedOperationException("IdentifierUtils only contains static definitions."); 35 | } 36 | 37 | public static @NotNull Identifier fromMc(@NotNull net.minecraft.util.Identifier mc) 38 | { 39 | return new Identifier(mc.getNamespace(), mc.getPath()); 40 | } 41 | 42 | public static @NotNull net.minecraft.util.Identifier toMc(@NotNull Identifier id) 43 | { 44 | return new net.minecraft.util.Identifier(id.getNamespace(), id.getName()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mcpatcherpatcher", 4 | "name": "MCPatcherPatcher", 5 | "version": "${version}", 6 | "description": "Patches OptiFine and MCPatcher resource packs to alternatives format.", 7 | "authors": [ 8 | "LambdAurora" 9 | ], 10 | "contact": { 11 | "sources": "https://github.com/LambdAurora/MCPatcherPatcher.git", 12 | "issues": "https://github.com/LambdAurora/MCPatcherPatcher/issues" 13 | }, 14 | "license": "LGPLv3", 15 | "environment": "client", 16 | "entrypoints": { 17 | "client": [ 18 | "me.lambdaurora.mcpatcherpatcher.fabric.MCPatcherPatcherFabric" 19 | ] 20 | }, 21 | "mixins": [ 22 | "mcpatcherpatcher.mixins.json" 23 | ], 24 | "depends": { 25 | "fabricloader": ">=0.10.8", 26 | "fabric-resource-loader-v0": ">=0.4.0", 27 | "minecraft": ">=1.16" 28 | }, 29 | "recommends": { 30 | "modmenu": ">=1.12.2" 31 | }, 32 | "suggests": { 33 | "flamingo": "*" 34 | }, 35 | "breaks": { 36 | "modmenu": "<1.12.2", 37 | "optifabric": "*" 38 | }, 39 | "custom": { 40 | "modupdater": { 41 | "strategy": "curseforge", 42 | "projectID": 400322 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /fabric/src/main/resources/mcpatcherpatcher.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "me.lambdaurora.mcpatcherpatcher.fabric.mixin", 4 | "compatibilityLevel": "JAVA_8", 5 | "client": [ 6 | "ReloadableResourceManagerImplMixin", 7 | "ZipResourcePackMixin" 8 | ], 9 | "injectors": { 10 | "defaultRequire": 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/use 6 | minecraft_version=1.16.4 7 | yarn_mappings=1.16.4+build.7 8 | loader_version=0.10.8 9 | fabric_api_version=0.28.4+1.16 10 | 11 | # Mod Properties 12 | version = 1.0.0 13 | maven_group = me.lambdaurora 14 | archives_base_name = mcpatcherpatcher 15 | 16 | # Dependencies 17 | modmenu_version=1.14.6+build.31 18 | databreaker_version = 0.2.4 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/MCPatcherPatcher/77a218c5e025cb4c0e71947339e048c357600ad8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name = 'Fabric' 6 | url = 'https://maven.fabricmc.net/' 7 | } 8 | gradlePluginPortal() 9 | } 10 | } 11 | 12 | rootProject.name = 'mcpatcherpatcher' 13 | include 'base' 14 | include 'fabric' 15 | 16 | --------------------------------------------------------------------------------