aboutsummaryrefslogtreecommitdiff
path: root/libs/installer_omod/src/installerOmod.cpp
blob: 3e4c390db33fb12f9a38f2fcaacefe480660f66c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "installerOmod.h"

#include <QRegularExpression>
#include <QTemporaryFile>

#include <uibase/iplugingame.h>
#include <uibase/log.h>

#include "OMODFrameworkWrapper.h"

InstallerOMOD::InstallerOMOD() : mMoInfo(nullptr), mOmodFrameworkWrapper(nullptr)
{
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPlugin

bool InstallerOMOD::init(MOBase::IOrganizer* moInfo)
{
  mMoInfo = moInfo;
  mOmodFrameworkWrapper = std::make_unique<OMODFrameworkWrapper>(mMoInfo);
  mMoInfo->onAboutToRun([this](const QString&) { buildSDPs(); return true; });
  mMoInfo->onFinishedRun([this](const QString&, unsigned int) { clearSDPs(); });
  return true;
}

QString InstallerOMOD::name() const
{
  return "Omod Installer";
}

QString InstallerOMOD::localizedName() const
{
  return tr("Omod Installer");
}

QString InstallerOMOD::author() const
{
  return "AnyOldName3 & erril120";
}

QString InstallerOMOD::description() const
{
  return tr("Installer for Omod files (including scripted ones)");
}

MOBase::VersionInfo InstallerOMOD::version() const
{
  return MOBase::VersionInfo(1, 1, 0, MOBase::VersionInfo::RELEASE_FINAL);
}

std::vector<std::shared_ptr<const MOBase::IPluginRequirement>> InstallerOMOD::requirements() const
{
  return { Requirements::gameDependency("Oblivion") };
}

QList<MOBase::PluginSetting> InstallerOMOD::settings() const
{
  return {};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPluginInstaller

unsigned int InstallerOMOD::priority() const
{
  // Some other installers have a use_any_file setting and then they'll try and claim OMODs as their own, so we want higher priority than them.
  return 500;
}

bool InstallerOMOD::isManualInstaller() const
{
  return false;
}

void InstallerOMOD::onInstallationEnd(EInstallResult result, MOBase::IModInterface* newMod)
{
  mOmodFrameworkWrapper->onInstallationEnd(result, newMod);
}

bool InstallerOMOD::isArchiveSupported(std::shared_ptr<const MOBase::IFileTree> tree) const
{
  for (const auto file : *tree) {
    // config is the only file guaranteed to be there
    if (file->isFile() && file->name() == "config")
      return true;
  }
  return false;
}

void InstallerOMOD::setParentWidget(QWidget* parent)
{
  IPluginInstaller::setParentWidget(parent);
  mOmodFrameworkWrapper->setParentWidget(parent);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IPluginInstallerCustom

bool InstallerOMOD::isArchiveSupported(const QString& archiveName) const
{
  return archiveName.endsWith(".omod", Qt::CaseInsensitive);
}

std::set<QString> InstallerOMOD::supportedExtensions() const
{
  return { "omod" };
}

InstallerOMOD::EInstallResult InstallerOMOD::install(MOBase::GuessedValue<QString>& modName, QString gameName, const QString& archiveName, const QString& version, int nexusID)
{
  return mOmodFrameworkWrapper->installInAnotherThread(modName, gameName, archiveName, version, nexusID);
}

MappingType InstallerOMOD::mappings() const
{
  MOBase::log::debug("Mapping virtual SDPs");

  MappingType mappings;

  for (const auto& [realSDPPath, virtualSDP] : mVirtualSDPs)
    mappings.push_back({ virtualSDP->fileName(), realSDPPath, false, false });

  return mappings;
}

void InstallerOMOD::buildSDPs()
{
  QStringList directories = mMoInfo->listDirectories("Shaders/OMOD");
  directories = directories.filter(QRegularExpression("\\d+"));

  for (const auto& directory : directories)
  {
    QString packagePath = mMoInfo->resolvePath(QString("Shaders/shaderpackage%1.sdp").arg(directory.toInt(), 3, 10, QChar('0')));
    if (packagePath.isEmpty())
      continue;

    QMap<QByteArray, QByteArray> shaders;
    {
      QFile packageFile(packagePath);
      packageFile.open(QIODevice::ReadOnly);
      QDataStream basePackageStream(&packageFile);
      basePackageStream.setByteOrder(QDataStream::LittleEndian);
      quint32 magic;
      basePackageStream >> magic;
      if (magic != 100)
      {
        MOBase::log::debug("SDP magic number mismatch in {}, got {}, but expected 100", packagePath, magic);
        continue;
      }
      quint32 shaderCount, totalSize;
      basePackageStream >> shaderCount >> totalSize;
      for (quint32 i = 0; i < shaderCount && !basePackageStream.atEnd(); ++i)
      {
        QByteArray shaderName(256, '\0');
        basePackageStream.readRawData(shaderName.data(), 256);
        quint32 shaderSize;
        basePackageStream >> shaderSize;
        QByteArray shaderBytecode(shaderSize, '\0');
        basePackageStream.readRawData(shaderBytecode.data(), shaderSize);
        shaders[shaderName] = shaderBytecode;
      }
    }

    QStringList shaderFiles = mMoInfo->findFiles("Shaders/OMOD/" + directory, { "*.vso", "*.pso" });
    for (const auto& shaderFile : shaderFiles)
    {
      QFileInfo info(shaderFile);
      QByteArray shaderName = (info.baseName().toUpper() + "." + info.suffix().toLower()).toLatin1();
      QFile file(shaderFile);
      file.open(QIODevice::ReadOnly);
      shaderName = shaderName.leftJustified(256, '\0', true);
      shaders[shaderName] = file.readAll();
      MOBase::log::debug("Replacement for {} was {} bytes long", shaderFile, shaders[shaderName].size());
    }

    std::unique_ptr<QTemporaryFile> outputFile = std::make_unique<QTemporaryFile>();
    outputFile->open();
    QDataStream packageStream(outputFile.get());
    packageStream.setByteOrder(QDataStream::LittleEndian);
    packageStream << quint32(100) << quint32(shaders.count());

    quint32 totalSize = 0;
    for (const auto& shader : shaders)
      totalSize += 256 + 4 + shader.size();
    packageStream << totalSize;

    for (auto itr = shaders.cbegin(); itr != shaders.cend(); ++itr)
    {
      packageStream.writeRawData(itr.key().constData(), 256);
      packageStream << quint32(itr.value().size());
      packageStream.writeRawData(itr.value().constData(), itr.value().size());
    }

    outputFile->close();

    mVirtualSDPs.emplace(mMoInfo->managedGame()->dataDirectory().absoluteFilePath(QString("Shaders/shaderpackage%1.sdp").arg(directory.toInt(), 3, 10, QChar('0'))), std::move(outputFile));
  }
}

void InstallerOMOD::clearSDPs()
{
  mVirtualSDPs.clear();
}