55package net .minecraftforge .mcmaven .impl .repo .forge ;
66
77import java .io .File ;
8+ import java .io .FileInputStream ;
89import java .io .FileNotFoundException ;
910import java .io .FileOutputStream ;
1011import java .io .IOException ;
12+ import java .nio .charset .StandardCharsets ;
1113import java .util .ArrayList ;
1214import java .util .Collections ;
1315import java .util .HashMap ;
1618import java .util .Map ;
1719import java .util .Stack ;
1820import java .util .function .Consumer ;
21+ import java .util .function .Function ;
1922import java .util .function .Predicate ;
2023import java .util .function .Supplier ;
24+ import java .util .regex .Pattern ;
2125import java .util .stream .Collectors ;
26+ import java .util .zip .ZipEntry ;
2227import java .util .zip .ZipFile ;
28+ import java .util .zip .ZipInputStream ;
29+ import java .util .zip .ZipOutputStream ;
2330
2431import io .codechicken .diffpatch .cli .PatchOperation ;
2532import io .codechicken .diffpatch .util .LogLevel ;
@@ -70,6 +77,7 @@ public class Patcher implements Supplier<Task> {
7077 private final Task downloadSources ;
7178 private final Task predecomp ;
7279 private final Task last ;
80+ private final Task filterBinaryInjections ;
7381
7482 /**
7583 * Creates a new Patcher for the given Forge repo.
@@ -162,6 +170,17 @@ public class Patcher implements Supplier<Task> {
162170 }
163171
164172 this .last = last ;
173+
174+ var needsFilter = false ;
175+ for (var p : this .getStack ()) {
176+ if (p .config .inject != null || p .config .universalFilters != null ) {
177+ needsFilter = true ;
178+ break ;
179+ }
180+ }
181+
182+ this .filterBinaryInjections = needsFilter ? null :
183+ Task .named ("filterBinaryInjections[" + this .name .getName () + ']' , this ::filterBinaryInjectionsImpl );
165184 }
166185
167186 private RuntimeException except (String message ) {
@@ -215,6 +234,14 @@ public MCPSide getMCPSide() {
215234 return this .mcpSide == null ? this .parent .getMCPSide () : this .mcpSide ;
216235 }
217236
237+ public String getName () {
238+ return this .name .getName ();
239+ }
240+
241+ public Artifact getArtifact () {
242+ return this .name ;
243+ }
244+
218245 public String getDataHash () {
219246 return this .dataHash ;
220247 }
@@ -675,4 +702,99 @@ private File injectSourcesImpl(Task inputTask, File output) {
675702 cache .save ();
676703 return output ;
677704 }
705+
706+ public @ Nullable Task filterBinaryInjections () {
707+ return this .filterBinaryInjections ;
708+ }
709+
710+ private File filterBinaryInjectionsImpl () {
711+ var output = new File (this .build , "binary-injections.jar" );
712+ var cache = Util .cache (output );
713+ cache .addKnown ("data" , this .getDataHash ());
714+
715+ record Info (File file , Artifact artifact , Predicate <String > filter , Function <String , String > renamer ) {}
716+ var files = new ArrayList <Info >();
717+
718+ for (var p : getStack ()) {
719+ var prefix = p .config .inject ;
720+ if (prefix != null ) {
721+ cache .addKnown ("parent-" + p .getName (), p .getDataHash ());
722+ files .add (new Info (p .data , p .getArtifact (),
723+ name -> name .length () <= prefix .length () || !name .startsWith (prefix ),
724+ name -> name .substring (prefix .length ())
725+ ));
726+ }
727+
728+ if (p .config .universal != null && p .config .universalFilters != null ) {
729+ var artifact = Artifact .from (p .config .universal );
730+ var file = this .forge .getCache ().maven ().download (artifact );
731+ cache .add ("universal-" + p .getName (), file );
732+
733+ Predicate <String > filter = null ;
734+ for (var line : p .config .universalFilters ) {
735+ var pattern = Pattern .compile (line );
736+ Predicate <String > matcher = s -> !pattern .matcher (s ).matches ();
737+ if (filter == null )
738+ filter = matcher ;
739+ else
740+ filter = filter .or (matcher );
741+ }
742+ files .add (new Info (file , artifact , filter , Function .identity ()));
743+ }
744+ }
745+
746+ if (Mavenizer .checkCache (output , cache ))
747+ return output ;
748+
749+ if (output .getParentFile () != null )
750+ output .getParentFile ().mkdirs ();
751+
752+ try (var zos = new ZipOutputStream (new FileOutputStream (output ))) {
753+ var servicesLists = new HashMap <String , List <String >>();
754+ var seen = new HashSet <String >();
755+ for (var info : files ) {
756+ try (var zin = new ZipInputStream (new FileInputStream (this .data ))) {
757+ ZipEntry entry ;
758+ while ((entry = zin .getNextEntry ()) != null ) {
759+ if (FileUtils .isBlockOrSF (entry .getName ()))
760+ continue ;
761+ if (info .filter ().test (entry .getName ()))
762+ continue ;
763+
764+ String name = info .renamer .apply (entry .getName ());
765+
766+ if (name .startsWith ("META-INF/services/" ) && !entry .isDirectory ()) {
767+ var existing = servicesLists .computeIfAbsent (name , _ -> new ArrayList <>());
768+ if (existing .size () > 0 ) {
769+ existing .add ("" );
770+ existing .add ("# " + info .artifact ());
771+ }
772+ existing .add (new String (zin .readAllBytes (), StandardCharsets .UTF_8 ));
773+ } else if (seen .add (name )) {
774+ var _new = new ZipEntry (name );
775+ _new .setTime (0 );
776+ zos .putNextEntry (_new );
777+ zin .transferTo (zos );
778+ }
779+ }
780+ }
781+ }
782+
783+ for (var kv : servicesLists .entrySet ()) {
784+ String name = kv .getKey ();
785+ ZipEntry _new = new ZipEntry (name );
786+ _new .setTime (0 );
787+ zos .putNextEntry (_new );
788+ for (var line : kv .getValue ()) {
789+ zos .write (line .getBytes (StandardCharsets .UTF_8 ));
790+ zos .write ('\n' );
791+ }
792+ }
793+ } catch (IOException e ) {
794+ return Util .sneak (e );
795+ }
796+
797+ cache .save ();
798+ return output ;
799+ }
678800}
0 commit comments