--- /dev/null
+TEMPLATE = app
+
+TARGET = instmerge
+DESTDIR = $$PWD/../../bin
+QT -= gui
+QT += xml
+CONFIG += release hide_symbols separate_debug_info console c++11
+
+OBJECTS_DIR = .ctmp
+MOC_DIR = .ctmp
+RCC_DIR = .ctmp
+
+DEFINES += AURORA_INSTGEN_BUILD=1
+
+INCLUDEPATH += . $$PWD/../common
+DEPENDPATH += $$INCLUDEPATH
+
+SOURCES += main.cpp \
+ ../common/metafile.cpp
+
+HEADERS += \
+ ../common/metafile.h
+
+
+linux {
+ QMAKE_CFLAGS += -fPIE
+ QMAKE_CXXFLAGS += -fPIE
+ QMAKE_LFLAGS += -pie
+ #make sure we find our libs
+ QMAKE_LFLAGS += -Wl,-rpath,\'\$$ORIGIN/../lib\'
+}
+QMAKE_CFLAGS += -fstack-protector-all -Wstack-protector
+QMAKE_CXXFLAGS += -fstack-protector-all -Wstack-protector
+
+win32 {
+ QMAKE_LFLAGS += -Wl,--nxcompat -Wl,--dynamicbase
+ LIBS += -lssp
+}
--- /dev/null
+// Copyright (C) 2017 by Konrad Rosenbaum <konrad@silmor.de>
+// protected under the GNU LGPL version 3 or at your option any newer.
+// See COPYING.LGPL file that comes with this distribution.
+//
+
+#include <QCoreApplication>
+#include <QStringList>
+#include <QDebug>
+#include <QDir>
+#include <QCryptographicHash>
+#include <QFile>
+
+#include "metafile.h"
+#include "fhelp.h"
+
+MetaFile targetmetafile;
+bool isinitialized=false;
+
+bool mergeDirectory(QString targetdirname,QString metafilename,QString sourcedirname)
+{
+ //get source meta file
+ MetaFile mf(sourcedirname+"/"+metafilename);
+ if(!mf.isValid()){
+ qDebug()<<"Error: cannot open meta file"<<metafilename<<"in directory"<<targetdirname;
+ return false;
+ }
+ //directories
+ QDir tdir(targetdirname);
+ if(!tdir.exists())QDir().mkdir(targetdirname);
+ QDir sdir(sourcedirname);
+ if(!sdir.exists()){
+ qDebug()<<"Error: source directory"<<sourcedirname<<"does not exist.";
+ return false;
+ }
+ //first source?
+ if(!isinitialized){
+ targetmetafile=mf;
+ for(QString fn:sdir.entryList(QStringList()<<"*.zip"))
+ if(!QFile::copy(sourcedirname+"/"+fn,targetdirname+"/"+fn)){
+ qDebug()<<"Unable to copy"<<fn<<"from"<<sourcedirname<<"to"<<targetdirname;
+ return false;
+ }
+ isinitialized=true;
+ }else{
+ for(auto a:mf.archives()){
+ auto b=targetmetafile.archive(a.name());
+ if(b.isValid()){
+ const bool ident=a.sha256sum()==b.sha256sum();
+ qDebug()<<"Skipping existing file"<<a.name()<<"in"<<sourcedirname
+ <<(ident?"(identical)":"(different)");
+ }else{
+ if(!QFile::copy(sourcedirname+"/"+a.name(),targetdirname+"/"+a.name())){
+ qDebug()<<"Unable to copy"<<a.name()<<"from"<<sourcedirname<<"to"<<targetdirname;
+ return false;
+ }
+ targetmetafile.archives().append(a);
+ }
+ }
+ }
+ return true;
+}
+
+bool finalizeTarget(QString targetdirname,QString metafilename)
+{
+ return targetmetafile.writeMetaFile(targetdirname+"/"+metafilename);
+}
+
+int main(int ac,char**av)
+{
+ QCoreApplication app(ac,av);
+ //get parameters
+ if(app.arguments().size()<4){
+ qDebug()<<"Usage: instmerge targetdir metafile.xml sourcedir1 [sourcedir2 [sourcedir3 ...]]";
+ qDebug()<<"\ttargetdir - directory where the merged result is put";
+ qDebug()<<"\tmetafile.xml - name of the meta data files in each directory";
+ qDebug()<<"\tsourcedir* - directories that will be merged";
+ qDebug()<<"\nNote: archive files in directories more left trump those in directories more right.";
+ return 1;
+ }
+
+ //get target
+ const auto args=app.arguments();
+ const QString targetdir=args.at(1);
+ const QString metafile=args.at(2);
+ //go through dirs
+ for(int i=3;i<args.size();i++)
+ if(!mergeDirectory(targetdir,metafile,args[i])){
+ qDebug()<<"Error: can't finish - aborting.";
+ return 1;
+ }
+ return finalizeTarget(targetdir,metafile)?0:1;
+}