2014-01-22 12:03:32 +05:30
/* Copyright 2013 MultiMC Contributors
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
# include "DerpVersionBuilder.h"
# include <QList>
# include <QJsonObject>
# include <QJsonArray>
# include <QJsonDocument>
# include <QFile>
# include <QFileInfo>
# include <QMessageBox>
# include <QObject>
# include <QDir>
2014-01-22 19:50:48 +05:30
# include <QDebug>
2014-01-22 12:03:32 +05:30
# include "DerpVersion.h"
# include "DerpInstance.h"
# include "DerpRule.h"
DerpVersionBuilder : : DerpVersionBuilder ( )
{
}
bool DerpVersionBuilder : : build ( DerpVersion * version , DerpInstance * instance , QWidget * widgetParent )
{
DerpVersionBuilder builder ;
builder . m_version = version ;
builder . m_instance = instance ;
builder . m_widgetParent = widgetParent ;
return builder . build ( ) ;
}
bool DerpVersionBuilder : : build ( )
{
2014-01-22 19:50:48 +05:30
m_version - > clear ( ) ;
2014-01-22 12:03:32 +05:30
QDir root ( m_instance - > instanceRoot ( ) ) ;
QDir patches ( root . absoluteFilePath ( " patches/ " ) ) ;
// version.json -> patches/*.json -> custom.json
// version.json
{
QJsonObject obj ;
if ( ! read ( QFileInfo ( root . absoluteFilePath ( " version.json " ) ) , & obj ) )
{
return false ;
}
if ( ! apply ( obj ) )
{
return false ;
}
}
// patches/
{
// load all, put into map for ordering, apply in the right order
QMap < int , QJsonObject > objects ;
for ( auto info : patches . entryInfoList ( QStringList ( ) < < " *.json " , QDir : : Files ) )
{
QJsonObject obj ;
if ( ! read ( info , & obj ) )
{
return false ;
}
if ( ! obj . contains ( " order " ) | | ! obj . value ( " order " ) . isDouble ( ) )
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " Missing or invalid 'order' in %1 " ) . arg ( info . absoluteFilePath ( ) ) ) ;
return false ;
}
objects . insert ( obj . value ( " order " ) . toDouble ( ) , obj ) ;
}
for ( auto object : objects . values ( ) )
{
if ( ! apply ( object ) )
{
return false ;
}
}
}
// custom.json
{
if ( QFile : : exists ( root . absoluteFilePath ( " custom.json " ) ) )
{
QJsonObject obj ;
if ( ! read ( QFileInfo ( root . absoluteFilePath ( " custom.json " ) ) , & obj ) )
{
return false ;
}
if ( ! apply ( obj ) )
{
return false ;
}
}
}
return true ;
}
2014-01-23 02:45:50 +05:30
void applyString ( const QJsonObject & obj , const QString & key , QString & out , const bool onlyOverride = true )
2014-01-22 12:03:32 +05:30
{
if ( obj . contains ( key ) & & obj . value ( key ) . isString ( ) )
{
out = obj . value ( key ) . toString ( ) ;
}
2014-01-23 02:45:50 +05:30
else if ( ! onlyOverride )
{
if ( obj . contains ( " + " + key ) & & obj . value ( " + " + key ) . isString ( ) )
{
out + = obj . value ( " + " + key ) . toString ( ) ;
}
else if ( obj . contains ( " - " + key ) & & obj . value ( " - " + key ) . isString ( ) )
{
out . remove ( obj . value ( " - " + key ) . toString ( ) ) ;
}
}
2014-01-22 12:03:32 +05:30
}
void applyString ( const QJsonObject & obj , const QString & key , std : : shared_ptr < DerpLibrary > lib , void ( DerpLibrary : : * func ) ( const QString & val ) )
{
if ( obj . contains ( key ) & & obj . value ( key ) . isString ( ) )
{
( lib . get ( ) - > * func ) ( obj . value ( key ) . toString ( ) ) ;
}
}
bool DerpVersionBuilder : : apply ( const QJsonObject & object )
{
applyString ( object , " id " , m_version - > id ) ;
applyString ( object , " mainClass " , m_version - > mainClass ) ;
2014-01-23 02:45:50 +05:30
applyString ( object , " processArguments " , m_version - > processArguments , false ) ;
2014-01-22 12:03:32 +05:30
{
const QString toCompare = m_version - > processArguments . toLower ( ) ;
if ( toCompare = = " legacy " )
{
m_version - > minecraftArguments = " ${auth_player_name} ${auth_session} " ;
}
else if ( toCompare = = " username_session " )
{
m_version - > minecraftArguments = " --username ${auth_player_name} --session ${auth_session} " ;
}
else if ( toCompare = = " username_session_version " )
{
m_version - > minecraftArguments = " --username ${auth_player_name} --session ${auth_session} --version ${profile_name} " ;
}
}
2014-01-23 02:45:50 +05:30
applyString ( object , " minecraftArguments " , m_version - > minecraftArguments , false ) ;
2014-01-22 12:03:32 +05:30
applyString ( object , " type " , m_version - > type ) ;
applyString ( object , " releaseTime " , m_version - > releaseTime ) ;
applyString ( object , " time " , m_version - > time ) ;
applyString ( object , " assets " , m_version - > assets ) ;
{
if ( m_version - > assets . isEmpty ( ) )
{
m_version - > assets = " legacy " ;
}
}
if ( object . contains ( " minimumLauncherVersion " ) )
{
auto minLauncherVersionVal = object . value ( " minimumLauncherVersion " ) ;
if ( minLauncherVersionVal . isDouble ( ) )
{
m_version - > minimumLauncherVersion = minLauncherVersionVal . toDouble ( ) ;
}
}
// libraries
2014-01-22 19:50:48 +05:30
if ( object . contains ( " libraries " ) )
2014-01-22 12:03:32 +05:30
{
2014-01-23 02:45:50 +05:30
m_version - > libraries . clear ( ) ;
2014-01-22 12:03:32 +05:30
auto librariesValue = object . value ( " libraries " ) ;
if ( ! librariesValue . isArray ( ) )
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " One json files contains a libraries field, but it's not an array " ) ) ;
return false ;
}
2014-01-23 02:45:50 +05:30
auto array = librariesValue . toArray ( ) ;
for ( auto libVal : array )
{
if ( libVal . isObject ( ) )
{
if ( ! applyLibrary ( libVal . toObject ( ) , Override ) )
{
return false ;
}
}
}
}
// +libraries
if ( object . contains ( " +libraries " ) )
{
auto librariesValue = object . value ( " +libraries " ) ;
if ( ! librariesValue . isArray ( ) )
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " One json files contains a libraries field, but it's not an array " ) ) ;
return false ;
}
2014-01-22 12:03:32 +05:30
for ( auto libVal : librariesValue . toArray ( ) )
{
2014-01-23 02:45:50 +05:30
if ( libVal . isObject ( ) )
2014-01-22 12:03:32 +05:30
{
2014-01-23 02:45:50 +05:30
applyLibrary ( libVal . toObject ( ) , Add ) ;
2014-01-22 12:03:32 +05:30
}
2014-01-23 02:45:50 +05:30
}
}
2014-01-22 12:03:32 +05:30
2014-01-23 02:45:50 +05:30
// -libraries
if ( object . contains ( " -libraries " ) )
{
auto librariesValue = object . value ( " -libraries " ) ;
if ( ! librariesValue . isArray ( ) )
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " One json files contains a libraries field, but it's not an array " ) ) ;
return false ;
}
for ( auto libVal : librariesValue . toArray ( ) )
{
if ( libVal . isObject ( ) )
2014-01-22 12:03:32 +05:30
{
2014-01-23 02:45:50 +05:30
applyLibrary ( libVal . toObject ( ) , Remove ) ;
2014-01-22 12:03:32 +05:30
}
2014-01-23 02:45:50 +05:30
}
}
return true ;
}
int findLibrary ( QList < std : : shared_ptr < DerpLibrary > > haystack , const QString & needle )
{
for ( int i = 0 ; i < haystack . size ( ) ; + + i )
{
if ( QRegExp ( needle , Qt : : CaseSensitive , QRegExp : : WildcardUnix ) . indexIn ( haystack . at ( i ) - > rawName ( ) ) ! = - 1 )
{
return i ;
}
}
return - 1 ;
}
bool DerpVersionBuilder : : applyLibrary ( const QJsonObject & lib , const DerpVersionBuilder : : Type type )
{
// Library name
auto nameVal = lib . value ( " name " ) ;
if ( ! nameVal . isString ( ) )
{
return false ;
}
auto name = nameVal . toString ( ) ;
2014-01-22 12:03:32 +05:30
2014-01-23 02:45:50 +05:30
if ( type = = Remove )
{
int index = findLibrary ( m_version - > libraries , name ) ;
if ( index > = 0 )
{
m_version - > libraries . removeAt ( index ) ;
}
return true ;
}
if ( type = = Add & & ! lib . contains ( " insert " ) )
{
return false ;
}
std : : shared_ptr < DerpLibrary > library ;
if ( lib . value ( " insert " ) . toString ( ) = = " apply " & & type = = Add )
{
library = m_version - > libraries [ findLibrary ( m_version - > libraries , name ) ] ;
}
else
{
library . reset ( new DerpLibrary ( nameVal . toString ( ) ) ) ;
}
applyString ( lib , " url " , library , & DerpLibrary : : setBaseUrl ) ;
applyString ( lib , " MMC-hint " , library , & DerpLibrary : : setHint ) ;
applyString ( lib , " MMC-absulute_url " , library , & DerpLibrary : : setAbsoluteUrl ) ;
applyString ( lib , " MMC-absoluteUrl " , library , & DerpLibrary : : setAbsoluteUrl ) ;
auto extractVal = lib . value ( " extract " ) ;
if ( extractVal . isObject ( ) )
{
QStringList excludes ;
auto extractObj = extractVal . toObject ( ) ;
auto excludesVal = extractObj . value ( " exclude " ) ;
if ( excludesVal . isArray ( ) )
{
auto excludesList = excludesVal . toArray ( ) ;
for ( auto excludeVal : excludesList )
2014-01-22 12:03:32 +05:30
{
2014-01-23 02:45:50 +05:30
if ( excludeVal . isString ( ) )
2014-01-22 12:03:32 +05:30
{
2014-01-23 02:45:50 +05:30
excludes . append ( excludeVal . toString ( ) ) ;
2014-01-22 12:03:32 +05:30
}
}
2014-01-23 02:45:50 +05:30
library - > extract_excludes = excludes ;
}
}
2014-01-22 12:03:32 +05:30
2014-01-23 02:45:50 +05:30
auto nativesVal = lib . value ( " natives " ) ;
if ( nativesVal . isObject ( ) )
{
library - > setIsNative ( ) ;
auto nativesObj = nativesVal . toObject ( ) ;
for ( auto it = nativesObj . begin ( ) ; it ! = nativesObj . end ( ) ; + + it )
{
auto osType = OpSys_fromString ( it . key ( ) ) ;
if ( osType = = Os_Other )
2014-01-22 12:03:32 +05:30
{
2014-01-23 02:45:50 +05:30
continue ;
}
if ( ! it . value ( ) . isString ( ) )
{
continue ;
2014-01-22 12:03:32 +05:30
}
2014-01-23 02:45:50 +05:30
library - > addNative ( osType , it . value ( ) . toString ( ) ) ;
}
}
2014-01-22 12:03:32 +05:30
2014-01-23 02:45:50 +05:30
if ( lib . contains ( " rules " ) )
{
library - > setRules ( rulesFromJsonV4 ( lib ) ) ;
}
library - > finalize ( ) ;
if ( type = = Override )
{
qDebug ( ) < < " appending " < < library - > rawName ( ) ;
m_version - > libraries . append ( library ) ;
}
else if ( lib . value ( " insert " ) . toString ( ) ! = " apply " )
{
if ( lib . value ( " insert " ) . toString ( ) = = " append " )
{
2014-01-22 12:03:32 +05:30
m_version - > libraries . append ( library ) ;
}
2014-01-23 02:45:50 +05:30
else if ( lib . value ( " insert " ) . toString ( ) = = " prepend " )
{
m_version - > libraries . prepend ( library ) ;
}
else if ( lib . value ( " insert " ) . isObject ( ) )
{
QJsonObject insertObj = lib . value ( " insert " ) . toObject ( ) ;
if ( insertObj . isEmpty ( ) )
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " 'insert' object empty " ) ) ;
return false ;
}
const QString key = insertObj . keys ( ) . first ( ) ;
const QString value = insertObj . value ( key ) . toString ( ) ;
const int index = findLibrary ( m_version - > libraries , value ) ;
if ( index > = 0 )
{
if ( key = = " before " )
{
m_version - > libraries . insert ( index , library ) ;
}
else if ( key = = " after " )
{
m_version - > libraries . insert ( index + 1 , library ) ;
}
else
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " Invalid value for 'insert': %1 " ) . arg ( lib . value ( " insert " ) . toString ( ) ) ) ;
return false ;
}
}
}
else
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " Invalid value for 'insert': %1 " ) . arg ( lib . value ( " insert " ) . toString ( ) ) ) ;
return false ;
}
2014-01-22 12:03:32 +05:30
}
return true ;
}
bool DerpVersionBuilder : : read ( const QFileInfo & fileInfo , QJsonObject * out )
{
QFile file ( fileInfo . absoluteFilePath ( ) ) ;
if ( ! file . open ( QFile : : ReadOnly ) )
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " Unable to open %1: %2 " ) . arg ( file . fileName ( ) , file . errorString ( ) ) ) ;
return false ;
}
QJsonParseError error ;
QJsonDocument doc = QJsonDocument : : fromJson ( file . readAll ( ) , & error ) ;
if ( error . error ! = QJsonParseError : : NoError )
{
QMessageBox : : critical ( m_widgetParent , QObject : : tr ( " Error " ) , QObject : : tr ( " Unable to parse %1: %2 at %3 " ) . arg ( file . fileName ( ) , error . errorString ( ) ) . arg ( error . offset ) ) ;
return false ;
}
* out = doc . object ( ) ;
return true ;
}