2016-02-27 18:58:40 +00:00
# include "OneSixProfileStrategy.h"
# include "OneSixInstance.h"
2016-02-27 21:02:56 +00:00
# include "OneSixVersionFormat.h"
2016-02-27 18:58:40 +00:00
2015-02-09 00:51:14 +00:00
# include "minecraft/VersionBuildError.h"
# include "Env.h"
2015-10-05 00:47:27 +01:00
# include <FileSystem.h>
2015-01-27 21:31:07 +00:00
# include <QDir>
# include <QUuid>
# include <QJsonDocument>
# include <QJsonArray>
2017-03-12 18:45:28 +00:00
# include <QSaveFile>
# include <QResource>
2017-03-19 01:13:49 +00:00
# include <meta/Index.h>
# include <meta/Version.h>
2015-01-27 21:31:07 +00:00
2017-03-27 02:34:39 +01:00
# include <tuple>
2015-01-27 21:31:07 +00:00
OneSixProfileStrategy : : OneSixProfileStrategy ( OneSixInstance * instance )
{
m_instance = instance ;
}
void OneSixProfileStrategy : : upgradeDeprecatedFiles ( )
{
2015-10-05 00:47:27 +01:00
auto versionJsonPath = FS : : PathCombine ( m_instance - > instanceRoot ( ) , " version.json " ) ;
auto customJsonPath = FS : : PathCombine ( m_instance - > instanceRoot ( ) , " custom.json " ) ;
auto mcJson = FS : : PathCombine ( m_instance - > instanceRoot ( ) , " patches " , " net.minecraft.json " ) ;
2015-01-27 21:31:07 +00:00
2015-05-15 00:10:08 +01:00
QString sourceFile ;
2015-05-16 15:09:34 +01:00
QString renameFile ;
2015-05-15 00:10:08 +01:00
2015-01-27 21:31:07 +00:00
// convert old crap.
if ( QFile : : exists ( customJsonPath ) )
2015-05-15 00:10:08 +01:00
{
sourceFile = customJsonPath ;
2015-05-16 15:09:34 +01:00
renameFile = versionJsonPath ;
2015-05-15 00:10:08 +01:00
}
else if ( QFile : : exists ( versionJsonPath ) )
{
sourceFile = versionJsonPath ;
}
if ( ! sourceFile . isEmpty ( ) & & ! QFile : : exists ( mcJson ) )
2015-01-27 21:31:07 +00:00
{
2015-10-05 00:47:27 +01:00
if ( ! FS : : ensureFilePathExists ( mcJson ) )
2015-01-27 21:31:07 +00:00
{
2015-05-15 00:10:08 +01:00
qWarning ( ) < < " Couldn't create patches folder for " < < m_instance - > name ( ) ;
return ;
2015-01-27 21:31:07 +00:00
}
2015-05-16 15:09:34 +01:00
if ( ! renameFile . isEmpty ( ) & & QFile : : exists ( renameFile ) )
2015-01-27 21:31:07 +00:00
{
2015-05-16 15:09:34 +01:00
if ( ! QFile : : rename ( renameFile , renameFile + " .old " ) )
2015-01-27 21:31:07 +00:00
{
2015-05-16 15:09:34 +01:00
qWarning ( ) < < " Couldn't rename " < < renameFile < < " to " < < renameFile + " .old " < < " in " < < m_instance - > name ( ) ;
2015-05-15 00:10:08 +01:00
return ;
2015-01-27 21:31:07 +00:00
}
}
2015-05-15 00:10:08 +01:00
auto file = ProfileUtils : : parseJsonFile ( QFileInfo ( sourceFile ) , false ) ;
ProfileUtils : : removeLwjglFromPatch ( file ) ;
2017-03-19 22:58:54 +00:00
file - > uid = " net.minecraft " ;
2016-03-26 15:56:57 +00:00
file - > version = file - > minecraftVersion ;
2015-05-15 00:10:08 +01:00
file - > name = " Minecraft " ;
2016-03-19 02:06:32 +00:00
auto data = OneSixVersionFormat : : versionFileToJson ( file , false ) . toJson ( ) ;
2015-05-15 00:10:08 +01:00
QSaveFile newPatchFile ( mcJson ) ;
if ( ! newPatchFile . open ( QIODevice : : WriteOnly ) )
2015-01-27 21:31:07 +00:00
{
2015-05-15 00:10:08 +01:00
newPatchFile . cancelWriting ( ) ;
qWarning ( ) < < " Couldn't open main patch for writing in " < < m_instance - > name ( ) ;
return ;
2015-01-27 21:31:07 +00:00
}
2015-05-15 00:10:08 +01:00
newPatchFile . write ( data ) ;
if ( ! newPatchFile . commit ( ) )
2015-01-27 21:31:07 +00:00
{
2015-05-15 00:10:08 +01:00
qWarning ( ) < < " Couldn't save main patch in " < < m_instance - > name ( ) ;
return ;
}
2015-05-16 15:09:34 +01:00
if ( ! QFile : : rename ( sourceFile , sourceFile + " .old " ) )
2015-05-15 00:10:08 +01:00
{
2015-05-16 15:09:34 +01:00
qWarning ( ) < < " Couldn't rename " < < sourceFile < < " to " < < sourceFile + " .old " < < " in " < < m_instance - > name ( ) ;
2015-05-15 00:10:08 +01:00
return ;
2015-01-27 21:31:07 +00:00
}
}
}
void OneSixProfileStrategy : : loadDefaultBuiltinPatches ( )
{
{
2015-10-05 00:47:27 +01:00
auto mcJson = FS : : PathCombine ( m_instance - > instanceRoot ( ) , " patches " , " net.minecraft.json " ) ;
2015-05-17 22:38:28 +01:00
// load up the base minecraft patch
ProfilePatchPtr minecraftPatch ;
if ( QFile : : exists ( mcJson ) )
2015-01-27 21:31:07 +00:00
{
2015-05-17 22:38:28 +01:00
auto file = ProfileUtils : : parseJsonFile ( QFileInfo ( mcJson ) , false ) ;
if ( file - > version . isEmpty ( ) )
{
file - > version = m_instance - > intendedVersionId ( ) ;
}
2017-03-27 02:34:39 +01:00
minecraftPatch = std : : make_shared < ProfilePatch > ( file , mcJson ) ;
minecraftPatch - > setVanilla ( false ) ;
minecraftPatch - > setRevertible ( true ) ;
2015-01-27 21:31:07 +00:00
}
2015-05-17 22:38:28 +01:00
else
{
2017-03-19 01:13:49 +00:00
auto mcversion = ENV . metadataIndex ( ) - > get ( " net.minecraft " , m_instance - > intendedVersionId ( ) ) ;
2017-03-27 02:34:39 +01:00
minecraftPatch = std : : make_shared < ProfilePatch > ( mcversion ) ;
minecraftPatch - > setVanilla ( true ) ;
2015-05-17 22:38:28 +01:00
}
if ( ! minecraftPatch )
{
throw VersionIncomplete ( " net.minecraft " ) ;
}
minecraftPatch - > setOrder ( - 2 ) ;
profile - > appendPatch ( minecraftPatch ) ;
2015-01-27 21:31:07 +00:00
}
{
2015-10-05 00:47:27 +01:00
auto lwjglJson = FS : : PathCombine ( m_instance - > instanceRoot ( ) , " patches " , " org.lwjgl.json " ) ;
2015-05-17 22:38:28 +01:00
ProfilePatchPtr lwjglPatch ;
if ( QFile : : exists ( lwjglJson ) )
{
auto file = ProfileUtils : : parseJsonFile ( QFileInfo ( lwjglJson ) , false ) ;
2017-03-27 02:34:39 +01:00
lwjglPatch = std : : make_shared < ProfilePatch > ( file , lwjglJson ) ;
lwjglPatch - > setVanilla ( false ) ;
lwjglPatch - > setRevertible ( true ) ;
2015-05-17 22:38:28 +01:00
}
else
{
2017-03-19 01:13:49 +00:00
auto lwjglversion = ENV . metadataIndex ( ) - > get ( " org.lwjgl " , " 2.9.1 " ) ;
2017-03-27 02:34:39 +01:00
lwjglPatch = std : : make_shared < ProfilePatch > ( lwjglversion ) ;
lwjglPatch - > setVanilla ( true ) ;
2015-05-17 22:38:28 +01:00
}
if ( ! lwjglPatch )
{
throw VersionIncomplete ( " org.lwjgl " ) ;
}
lwjglPatch - > setOrder ( - 1 ) ;
profile - > appendPatch ( lwjglPatch ) ;
2015-01-27 21:31:07 +00:00
}
}
void OneSixProfileStrategy : : loadUserPatches ( )
{
// load all patches, put into map for ordering, apply in the right order
ProfileUtils : : PatchOrder userOrder ;
2015-10-05 00:47:27 +01:00
ProfileUtils : : readOverrideOrders ( FS : : PathCombine ( m_instance - > instanceRoot ( ) , " order.json " ) , userOrder ) ;
QDir patches ( FS : : PathCombine ( m_instance - > instanceRoot ( ) , " patches " ) ) ;
2016-02-21 04:51:36 +00:00
QSet < QString > seen_extra ;
2015-01-27 21:31:07 +00:00
// first, load things by sort order.
for ( auto id : userOrder )
{
// ignore builtins
if ( id = = " net.minecraft " )
continue ;
if ( id = = " org.lwjgl " )
continue ;
// parse the file
QString filename = patches . absoluteFilePath ( id + " .json " ) ;
QFileInfo finfo ( filename ) ;
if ( ! finfo . exists ( ) )
{
2015-02-02 01:14:14 +00:00
qDebug ( ) < < " Patch file " < < filename < < " was deleted by external means... " ;
2015-01-27 21:31:07 +00:00
continue ;
}
2015-02-02 01:14:14 +00:00
qDebug ( ) < < " Reading " < < filename < < " by user order " ;
2016-02-21 04:51:36 +00:00
VersionFilePtr file = ProfileUtils : : parseJsonFile ( finfo , false ) ;
2015-01-27 21:31:07 +00:00
// sanity check. prevent tampering with files.
2017-03-19 22:58:54 +00:00
if ( file - > uid ! = id )
2015-01-27 21:31:07 +00:00
{
2017-03-27 02:34:39 +01:00
file - > addProblem ( ProblemSeverity : : Warning , QObject : : tr ( " load id %1 does not match internal id %2 " ) . arg ( id , file - > uid ) ) ;
2017-03-19 22:58:54 +00:00
seen_extra . insert ( file - > uid ) ;
2015-01-27 21:31:07 +00:00
}
2017-03-27 02:34:39 +01:00
auto patchEntry = std : : make_shared < ProfilePatch > ( file , filename ) ;
patchEntry - > setRemovable ( true ) ;
patchEntry - > setMovable ( true ) ;
profile - > appendPatch ( patchEntry ) ;
2015-01-27 21:31:07 +00:00
}
// now load the rest by internal preference.
2017-03-27 02:34:39 +01:00
using FileEntry = std : : tuple < VersionFilePtr , QString > ;
QMultiMap < int , FileEntry > files ;
2015-01-27 21:31:07 +00:00
for ( auto info : patches . entryInfoList ( QStringList ( ) < < " *.json " , QDir : : Files ) )
{
// parse the file
2015-02-02 01:14:14 +00:00
qDebug ( ) < < " Reading " < < info . fileName ( ) ;
2015-01-27 21:31:07 +00:00
auto file = ProfileUtils : : parseJsonFile ( info , true ) ;
// ignore builtins
2017-03-19 22:58:54 +00:00
if ( file - > uid = = " net.minecraft " )
2015-01-27 21:31:07 +00:00
continue ;
2017-03-19 22:58:54 +00:00
if ( file - > uid = = " org.lwjgl " )
2015-01-27 21:31:07 +00:00
continue ;
2016-02-21 04:51:36 +00:00
// do not load versions with broken IDs twice
2017-03-19 22:58:54 +00:00
if ( seen_extra . contains ( file - > uid ) )
2016-02-21 04:51:36 +00:00
continue ;
2015-01-27 21:31:07 +00:00
// do not load what we already loaded in the first pass
2017-03-19 22:58:54 +00:00
if ( userOrder . contains ( file - > uid ) )
2015-01-27 21:31:07 +00:00
continue ;
2017-03-27 02:34:39 +01:00
files . insert ( file - > order , std : : make_tuple ( file , info . filePath ( ) ) ) ;
2015-01-27 21:31:07 +00:00
}
2017-03-27 02:34:39 +01:00
auto appendFilePatch = [ & ] ( FileEntry tuple )
{
VersionFilePtr file ;
QString filename ;
std : : tie ( file , filename ) = tuple ;
auto patchEntry = std : : make_shared < ProfilePatch > ( file , filename ) ;
patchEntry - > setRemovable ( true ) ;
patchEntry - > setMovable ( true ) ;
profile - > appendPatch ( patchEntry ) ;
} ;
2016-02-21 04:51:36 +00:00
QSet < int > seen ;
2015-01-27 21:31:07 +00:00
for ( auto order : files . keys ( ) )
{
2016-02-21 04:51:36 +00:00
if ( seen . contains ( order ) )
continue ;
seen . insert ( order ) ;
const auto & values = files . values ( order ) ;
if ( values . size ( ) = = 1 )
{
2017-03-27 02:34:39 +01:00
appendFilePatch ( values [ 0 ] ) ;
2016-02-21 04:51:36 +00:00
continue ;
}
for ( auto & file : values )
{
QStringList list ;
for ( auto & file2 : values )
{
if ( file ! = file2 )
2017-03-27 02:34:39 +01:00
{
list . append ( std : : get < 0 > ( file2 ) - > name ) ;
}
2016-02-21 04:51:36 +00:00
}
2017-03-27 02:34:39 +01:00
auto vfileptr = std : : get < 0 > ( file ) ;
vfileptr - > addProblem ( ProblemSeverity : : Warning , QObject : : tr ( " %1 has the same order as the following components: \n %2 " ) . arg ( vfileptr - > name , list . join ( " , " ) ) ) ;
appendFilePatch ( file ) ;
2016-02-21 04:51:36 +00:00
}
2015-01-27 21:31:07 +00:00
}
}
void OneSixProfileStrategy : : load ( )
{
profile - > clearPatches ( ) ;
upgradeDeprecatedFiles ( ) ;
loadDefaultBuiltinPatches ( ) ;
loadUserPatches ( ) ;
}
bool OneSixProfileStrategy : : saveOrder ( ProfileUtils : : PatchOrder order )
{
2015-10-05 00:47:27 +01:00
return ProfileUtils : : writeOverrideOrders ( FS : : PathCombine ( m_instance - > instanceRoot ( ) , " order.json " ) , order ) ;
2015-01-27 21:31:07 +00:00
}
bool OneSixProfileStrategy : : resetOrder ( )
{
return QDir ( m_instance - > instanceRoot ( ) ) . remove ( " order.json " ) ;
}
bool OneSixProfileStrategy : : removePatch ( ProfilePatchPtr patch )
{
bool ok = true ;
// first, remove the patch file. this ensures it's not used anymore
2016-03-12 23:23:45 +00:00
auto fileName = patch - > getFilename ( ) ;
2015-04-13 22:26:52 +01:00
if ( fileName . size ( ) )
{
QFile patchFile ( fileName ) ;
if ( patchFile . exists ( ) & & ! patchFile . remove ( ) )
{
qCritical ( ) < < " File " < < fileName < < " could not be removed because: " < < patchFile . errorString ( ) ;
return false ;
}
}
2015-01-27 21:31:07 +00:00
auto preRemoveJarMod = [ & ] ( JarmodPtr jarMod ) - > bool
{
2015-10-05 00:47:27 +01:00
QString fullpath = FS : : PathCombine ( m_instance - > jarModsDir ( ) , jarMod - > name ) ;
2015-01-27 21:31:07 +00:00
QFileInfo finfo ( fullpath ) ;
if ( finfo . exists ( ) )
{
2015-04-13 22:26:52 +01:00
QFile jarModFile ( fullpath ) ;
if ( ! jarModFile . remove ( ) )
{
qCritical ( ) < < " File " < < fullpath < < " could not be removed because: " < < jarModFile . errorString ( ) ;
return false ;
}
return true ;
2015-01-27 21:31:07 +00:00
}
return true ;
} ;
2017-03-19 22:58:54 +00:00
auto & jarMods = patch - > getVersionFile ( ) - > jarMods ;
for ( auto & jarmod : jarMods )
2015-01-27 21:31:07 +00:00
{
ok & = preRemoveJarMod ( jarmod ) ;
}
return ok ;
}
2015-05-17 22:38:28 +01:00
bool OneSixProfileStrategy : : customizePatch ( ProfilePatchPtr patch )
{
if ( patch - > isCustom ( ) )
{
return false ;
}
2016-03-12 23:23:45 +00:00
auto filename = FS : : PathCombine ( m_instance - > instanceRoot ( ) , " patches " , patch - > getID ( ) + " .json " ) ;
2015-10-05 00:47:27 +01:00
if ( ! FS : : ensureFilePathExists ( filename ) )
2015-05-17 22:38:28 +01:00
{
return false ;
}
try
{
2015-05-28 08:36:58 +01:00
QSaveFile jsonFile ( filename ) ;
if ( ! jsonFile . open ( QIODevice : : WriteOnly ) )
{
return false ;
}
2016-03-19 02:06:32 +00:00
auto vfile = patch - > getVersionFile ( ) ;
if ( ! vfile )
{
return false ;
}
auto document = OneSixVersionFormat : : versionFileToJson ( vfile , true ) ;
2015-05-28 08:36:58 +01:00
jsonFile . write ( document . toJson ( ) ) ;
if ( ! jsonFile . commit ( ) )
{
return false ;
}
2015-05-17 22:38:28 +01:00
load ( ) ;
}
catch ( VersionIncomplete & error )
{
qDebug ( ) < < " Version was incomplete: " < < error . cause ( ) ;
}
2015-05-28 18:38:29 +01:00
catch ( Exception & error )
2015-05-17 22:38:28 +01:00
{
qWarning ( ) < < " Version could not be loaded: " < < error . cause ( ) ;
}
return true ;
}
bool OneSixProfileStrategy : : revertPatch ( ProfilePatchPtr patch )
{
if ( ! patch - > isCustom ( ) )
{
// already not custom
return true ;
}
2016-03-12 23:23:45 +00:00
auto filename = patch - > getFilename ( ) ;
2015-05-17 22:38:28 +01:00
if ( ! QFile : : exists ( filename ) )
{
// already gone / not custom
return true ;
}
// just kill the file and reload
bool result = QFile : : remove ( filename ) ;
try
{
load ( ) ;
}
catch ( VersionIncomplete & error )
{
qDebug ( ) < < " Version was incomplete: " < < error . cause ( ) ;
}
2015-05-28 18:38:29 +01:00
catch ( Exception & error )
2015-05-17 22:38:28 +01:00
{
qWarning ( ) < < " Version could not be loaded: " < < error . cause ( ) ;
}
return result ;
}
2015-01-27 21:31:07 +00:00
bool OneSixProfileStrategy : : installJarMods ( QStringList filepaths )
{
2015-10-05 00:47:27 +01:00
QString patchDir = FS : : PathCombine ( m_instance - > instanceRoot ( ) , " patches " ) ;
if ( ! FS : : ensureFolderPathExists ( patchDir ) )
2015-01-27 21:31:07 +00:00
{
return false ;
}
2015-10-05 00:47:27 +01:00
if ( ! FS : : ensureFolderPathExists ( m_instance - > jarModsDir ( ) ) )
2015-01-27 21:31:07 +00:00
{
return false ;
}
for ( auto filepath : filepaths )
{
QFileInfo sourceInfo ( filepath ) ;
auto uuid = QUuid : : createUuid ( ) ;
QString id = uuid . toString ( ) . remove ( ' { ' ) . remove ( ' } ' ) ;
QString target_filename = id + " .jar " ;
QString target_id = " org.multimc.jarmod. " + id ;
QString target_name = sourceInfo . completeBaseName ( ) + " (jar mod) " ;
2015-10-05 00:47:27 +01:00
QString finalPath = FS : : PathCombine ( m_instance - > jarModsDir ( ) , target_filename ) ;
2015-01-27 21:31:07 +00:00
QFileInfo targetInfo ( finalPath ) ;
if ( targetInfo . exists ( ) )
{
return false ;
}
if ( ! QFile : : copy ( sourceInfo . absoluteFilePath ( ) , QFileInfo ( finalPath ) . absoluteFilePath ( ) ) )
{
return false ;
}
auto f = std : : make_shared < VersionFile > ( ) ;
auto jarMod = std : : make_shared < Jarmod > ( ) ;
jarMod - > name = target_filename ;
2015-05-31 20:45:28 +01:00
jarMod - > originalName = sourceInfo . completeBaseName ( ) ;
2015-01-27 21:31:07 +00:00
f - > jarMods . append ( jarMod ) ;
f - > name = target_name ;
2017-03-19 22:58:54 +00:00
f - > uid = target_id ;
2017-03-27 02:34:39 +01:00
f - > order = profile - > getFreeOrderNumber ( ) ;
2015-10-05 00:47:27 +01:00
QString patchFileName = FS : : PathCombine ( patchDir , target_id + " .json " ) ;
2015-01-27 21:31:07 +00:00
QFile file ( patchFileName ) ;
if ( ! file . open ( QFile : : WriteOnly ) )
{
2015-02-02 01:14:14 +00:00
qCritical ( ) < < " Error opening " < < file . fileName ( )
2015-01-27 21:31:07 +00:00
< < " for reading: " < < file . errorString ( ) ;
return false ;
}
2016-03-19 02:06:32 +00:00
file . write ( OneSixVersionFormat : : versionFileToJson ( f , true ) . toJson ( ) ) ;
2015-01-27 21:31:07 +00:00
file . close ( ) ;
2017-03-27 02:34:39 +01:00
2017-03-27 07:30:21 +01:00
auto patch = std : : make_shared < ProfilePatch > ( f , patchFileName ) ;
2017-03-27 02:34:39 +01:00
patch - > setMovable ( true ) ;
patch - > setRemovable ( true ) ;
profile - > appendPatch ( patch ) ;
2015-01-27 21:31:07 +00:00
}
profile - > saveCurrentOrder ( ) ;
2016-03-18 14:02:54 +00:00
profile - > reapplyPatches ( ) ;
2015-01-27 21:31:07 +00:00
return true ;
}