import { ClassReader , ClassVisitor , Opcodes } from "@xmcl/asm" ; import { MinecraftFolder , MinecraftLocation , Version } from "@xmcl/core" ; import { task } from "@xmcl/task" ; import { getEntriesRecord , open , readAllEntries , readEntry } from "@xmcl/unzip" ; import { ensureFile , InstallOptions , spawnProcess , writeFile } from "./utils" ; export interface InstallOptifineOptions extends InstallOptions { /**
     * Use "optifine.OptiFineForgeTweaker" instead of "optifine.OptiFineTweaker" for tweakClass.
     *
     * If you want to install upon forge, you should use this.
     */ useForgeTweaker ? : boolean ; } param editionRelease param minecraftVersion param launchWrapperVersion param options beta export function generateOptifineVersion ( editionRelease , minecraftVersion , launchWrapperVersion ? , options = { } ) { let id = options . versionId ?? `${ minecraftVersion }-Optifine_${ editionRelease }` ; let inheritsFrom = options . inheritsFrom ?? minecraftVersion ; let mainClass = "net.minecraft.launchwrapper.Launch" ; let libraries = [ { name : `optifine:Optifine:${ minecraftVersion }_${ editionRelease }` } ] ; if ( launchWrapperVersion ) { libraries . unshift ( { name : `optifine:launchwrapper-of:${ launchWrapperVersion }` } ) ; } else { libraries . unshift ( { name : "net.minecraft:launchwrapper:1.12" } ) ; } return { id , inheritsFrom , arguments : { game : [ "--tweakClass" , options . useForgeTweaker ? "optifine.OptiFineForgeTweaker" : "optifine.OptiFineTweaker" ] , jvm : [ ] , } , releaseTime : new Date ( ) . toJSON ( ) , time : new Date ( ) . toJSON ( ) , type : "release" , libraries , mainClass , minimumLauncherVersion : 21 , } ; } export interface InstallOptifineOptions extends InstallOptions { /**
     * The java exectable path. It will use `java` by default.
     */ java ? : string ; } param installer param minecraft param options beta throws BadOptifineJarError export function installOptifine ( installer , minecraft , options ? ) { return installOptifineTask ( installer , minecraft , options ) . startAndWait ( ) ; } export class BadOptifineJarError extends Error { constructor ( public optifine , /**
     * What entry in jar is missing
     */ public entry ) { super ( `Missing entry ${ entry } in optifine installer: ${ optifine }` ) ; } error = "BadOptifineJar" ; } param installer param minecraft param options beta throws BadOptifineJarError export function installOptifineTask ( installer , minecraft , options = { } ) { return task ( "installOptifine" , async function ( ) { let mc = MinecraftFolder . from ( minecraft ) ; const zip = await open ( installer ) ; const entries = await readAllEntries ( zip ) ; const record = getEntriesRecord ( entries ) ; const entry = record [ "net/optifine/Config.class" ] ?? record [ "Config.class" ] ?? record [ "notch/net/optifine/Config.class" ] ; if ( ! entry ) { throw new BadOptifineJarError ( installer , "net/optifine/Config.class" ) ; } const launchWrapperVersionEntry = record [ "launchwrapper-of.txt" ] ; const launchWrapperVersion = launchWrapperVersionEntry ? await readEntry ( zip , launchWrapperVersionEntry ) . then ( ( b ) => b . toString ( ) ) : undefined ; const buf = await readEntry ( zip , entry ) ; const reader = new ClassReader ( buf ) ; class OptifineVisitor extends ClassVisitor { fields : Record < string , any > = { } ; visitField ( access , name , desc , signature , value ) { this . fields [ name ] = value ; return null ; } } const visitor = new OptifineVisitor ( Opcodes . ASM5 ) ; reader . accept ( visitor ) ; const mcversion = visitor . fields . MC_VERSION ; const edition = visitor . fields . OF_EDITION ; const release = visitor . fields . OF_RELEASE ; const editionRelease = edition + "_" + release ; const versionJSON = generateOptifineVersion ( editionRelease , mcversion , launchWrapperVersion , options ) ; const versionJSONPath = mc . getVersionJson ( versionJSON . id ) ; await this . yield ( task ( "json" , async ( ) => { await ensureFile ( versionJSONPath ) ; await writeFile ( versionJSONPath , JSON . stringify ( versionJSON , null , 4 ) ) ; } ) ) ; const launchWrapperEntry = record [ `launchwrapper-of-${ launchWrapperVersion }.jar` ] ; if ( launchWrapperEntry ) { await this . yield ( task ( "library" , async ( ) => { const wrapperDest = mc . getLibraryByPath ( `optifine/launchwrapper-of/${ launchWrapperVersion }/launchwrapper-of-${ launchWrapperVersion }.jar` ) ; await ensureFile ( wrapperDest ) ; await writeFile ( wrapperDest , await readEntry ( zip , launchWrapperEntry ) ) ; } ) ) ; } await this . yield ( task ( "jar" , async ( ) => { const dest = mc . getLibraryByPath ( `optifine/Optifine/${ mcversion }_${ editionRelease }/Optifine-${ mcversion }_${ editionRelease }.jar` ) ; const mcJar = mc . getVersionJar ( mcversion ) ; await ensureFile ( dest ) ; await spawnProcess ( options . java ?? "java" , [ "-cp" , installer , "optifine.Patcher" , mcJar , installer , dest ] ) ; } ) ) ; return versionJSON . id ; } ) ; }