leocad/common/lc_library.cpp

1879 lines
47 KiB
C++
Raw Normal View History

2013-08-09 06:57:18 +02:00
#include "lc_global.h"
#include "lc_library.h"
2024-07-12 00:04:07 +02:00
#include "lc_thumbnailmanager.h"
2013-08-09 06:57:18 +02:00
#include "lc_zipfile.h"
#include "lc_file.h"
#include "pieceinf.h"
#include "lc_colors.h"
#include "lc_texture.h"
#include "lc_category.h"
#include "lc_application.h"
#include "lc_context.h"
2015-05-09 21:54:29 +02:00
#include "lc_glextensions.h"
#include "lc_synth.h"
2024-11-03 03:34:22 +01:00
#include "lc_traintrack.h"
2015-02-22 03:39:15 +01:00
#include "project.h"
#include "lc_profile.h"
#include "lc_meshloader.h"
2013-08-09 06:57:18 +02:00
#include <ctype.h>
#include <locale.h>
#include <zlib.h>
2017-01-23 05:50:43 +01:00
#include <QtConcurrent>
#if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
#else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
2013-08-09 06:57:18 +02:00
2024-06-02 02:18:47 +02:00
#define LC_LIBRARY_CACHE_VERSION 0x0110
2013-08-09 06:57:18 +02:00
#define LC_LIBRARY_CACHE_ARCHIVE 0x0001
#define LC_LIBRARY_CACHE_DIRECTORY 0x0002
lcPiecesLibrary::lcPiecesLibrary()
2021-07-06 02:00:41 +02:00
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2017-01-23 04:28:05 +01:00
: mLoadMutex(QMutex::Recursive)
2021-07-06 02:00:41 +02:00
#endif
2013-08-09 06:57:18 +02:00
{
2024-07-12 00:04:07 +02:00
mThumbnailManager = std::unique_ptr<lcThumbnailManager>(new lcThumbnailManager(this));
QStringList cachePathList = QStandardPaths::standardLocations(QStandardPaths::CacheLocation);
mCachePath = cachePathList.first();
QDir Dir;
Dir.mkpath(mCachePath);
2013-08-09 06:57:18 +02:00
mNumOfficialPieces = 0;
mBuffersDirty = false;
mHasUnofficial = false;
mCancelLoading = false;
mStudStyle = static_cast<lcStudStyle>(lcGetProfileInt(LC_PROFILE_STUD_STYLE));
mStudCylinderColorEnabled = lcGetProfileInt(LC_PROFILE_STUD_CYLINDER_COLOR_ENABLED);
2013-08-09 06:57:18 +02:00
}
lcPiecesLibrary::~lcPiecesLibrary()
{
2024-07-12 00:04:07 +02:00
mThumbnailManager.reset();
2017-01-23 04:28:05 +01:00
mLoadMutex.lock();
mLoadQueue.clear();
mLoadMutex.unlock();
mCancelLoading = true;
2017-01-23 04:28:05 +01:00
WaitForLoadQueue();
2013-08-09 06:57:18 +02:00
Unload();
ReleaseBuffers();
2013-08-09 06:57:18 +02:00
}
void lcPiecesLibrary::Unload()
{
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
2017-07-24 04:35:18 +02:00
delete PieceIt.second;
mPieces.clear();
2013-08-09 06:57:18 +02:00
mSources.clear();
2013-08-09 06:57:18 +02:00
2019-07-05 02:06:26 +02:00
for (lcTexture* Texture : mTextures)
delete Texture;
mTextures.clear();
2013-08-09 06:57:18 +02:00
mNumOfficialPieces = 0;
2021-01-18 21:09:33 +01:00
for (std::unique_ptr<lcZipFile>& ZipFile : mZipFiles)
ZipFile.reset();
2013-08-09 06:57:18 +02:00
}
void lcPiecesLibrary::RemoveTemporaryPieces()
{
2017-01-23 04:28:05 +01:00
QMutexLocker LoadLock(&mLoadMutex);
2017-07-24 04:35:18 +02:00
for (auto PieceIt = mPieces.begin(); PieceIt != mPieces.end();)
{
2017-07-24 04:35:18 +02:00
PieceInfo* Info = PieceIt->second;
2017-07-24 04:35:18 +02:00
if (Info->IsTemporary() && Info->GetRefCount() == 0)
{
2017-07-24 04:35:18 +02:00
PieceIt = mPieces.erase(PieceIt);
delete Info;
}
2017-07-24 04:35:18 +02:00
else
PieceIt++;
}
}
2015-02-22 03:39:15 +01:00
void lcPiecesLibrary::RemovePiece(PieceInfo* Info)
{
2017-07-24 04:35:18 +02:00
for (auto PieceIt = mPieces.begin(); PieceIt != mPieces.end(); PieceIt++)
{
if (PieceIt->second == Info)
{
mPieces.erase(PieceIt);
break;
}
}
2015-02-22 03:39:15 +01:00
delete Info;
}
2017-07-24 04:35:18 +02:00
void lcPiecesLibrary::RenamePiece(PieceInfo* Info, const char* NewName)
{
for (auto PieceIt = mPieces.begin(); PieceIt != mPieces.end(); PieceIt++)
{
if (PieceIt->second == Info)
{
mPieces.erase(PieceIt);
break;
}
}
strncpy(Info->mFileName, NewName, sizeof(Info->mFileName));
Info->mFileName[sizeof(Info->mFileName) - 1] = 0;
strncpy(Info->m_strDescription, NewName, sizeof(Info->m_strDescription));
Info->m_strDescription[sizeof(Info->m_strDescription) - 1] = 0;
char PieceName[LC_PIECE_NAME_LEN];
strcpy(PieceName, Info->mFileName);
strupr(PieceName);
mPieces[PieceName] = Info;
2017-07-24 04:35:18 +02:00
}
PieceInfo* lcPiecesLibrary::FindPiece(const char* PieceName, Project* CurrentProject, bool CreatePlaceholder, bool SearchProjectFolder)
2013-08-09 06:57:18 +02:00
{
QString ProjectPath;
if (SearchProjectFolder)
{
QString FileName = CurrentProject->GetFileName();
if (!FileName.isEmpty())
ProjectPath = QFileInfo(FileName).absolutePath();
}
char CleanName[LC_PIECE_NAME_LEN];
const char* Src = PieceName;
char* Dst = CleanName;
while (*Src && Dst - CleanName != sizeof(CleanName))
{
if (*Src == '\\')
*Dst = '/';
else if (*Src >= 'a' && *Src <= 'z')
*Dst = *Src + 'A' - 'a';
else
*Dst = *Src;
Src++;
Dst++;
}
*Dst = 0;
const auto PieceIt = mPieces.find(CleanName);
2015-02-22 03:39:15 +01:00
2017-07-24 04:35:18 +02:00
if (PieceIt != mPieces.end())
{
PieceInfo* Info = PieceIt->second;
2024-06-17 02:43:02 +02:00
bool HasModel = false;
2015-02-22 03:39:15 +01:00
if (CurrentProject)
2024-06-17 02:43:02 +02:00
{
const std::vector<std::unique_ptr<lcModel>>& Models = CurrentProject->GetModels();
2024-06-17 02:43:02 +02:00
HasModel = std::find_if(Models.begin(), Models.end(), [Model = Info->GetModel()](const std::unique_ptr<lcModel>& CheckModel) { return CheckModel.get() == Model; }) != Models.end();
}
if ((!CurrentProject || !Info->IsModel() || HasModel) && (!ProjectPath.isEmpty() || !Info->IsProject() || Info->IsProjectPiece()))
2017-07-24 04:35:18 +02:00
return Info;
2015-02-22 03:39:15 +01:00
}
2013-08-09 06:57:18 +02:00
if (!ProjectPath.isEmpty())
{
QFileInfo ProjectFile = QFileInfo(ProjectPath + QDir::separator() + PieceName);
if (ProjectFile.isFile())
{
Project* NewProject = new Project();
if (NewProject->Load(ProjectFile.absoluteFilePath(), false))
{
PieceInfo* Info = new PieceInfo();
Info->CreateProject(NewProject, PieceName);
mPieces[CleanName] = Info;
return Info;
}
else
delete NewProject;
}
}
2014-12-24 16:52:52 +01:00
if (CreatePlaceholder)
{
PieceInfo* Info = new PieceInfo();
2013-08-09 06:57:18 +02:00
2014-12-24 16:52:52 +01:00
Info->CreatePlaceholder(PieceName);
mPieces[CleanName] = Info;
2013-08-09 06:57:18 +02:00
2014-12-24 16:52:52 +01:00
return Info;
}
2013-08-09 06:57:18 +02:00
return nullptr;
2013-08-09 06:57:18 +02:00
}
lcTexture* lcPiecesLibrary::FindTexture(const char* TextureName, Project* CurrentProject, bool SearchProjectFolder)
2013-08-09 06:57:18 +02:00
{
2019-07-05 02:06:26 +02:00
for (lcTexture* Texture : mTextures)
if (!strcmp(TextureName, Texture->mName))
return Texture;
2013-08-09 06:57:18 +02:00
QString ProjectPath;
if (SearchProjectFolder)
{
QString FileName = CurrentProject->GetFileName();
if (!FileName.isEmpty())
ProjectPath = QFileInfo(FileName).absolutePath();
}
if (!ProjectPath.isEmpty())
{
QFileInfo TextureFile = QFileInfo(ProjectPath + QDir::separator() + TextureName + ".png");
if (TextureFile.isFile())
{
lcTexture* Texture = lcLoadTexture(TextureFile.absoluteFilePath(), LC_TEXTURE_WRAPU | LC_TEXTURE_WRAPV);
if (Texture)
{
2019-07-05 02:06:26 +02:00
mTextures.push_back(Texture);
return Texture;
}
}
}
return nullptr;
2013-08-09 06:57:18 +02:00
}
bool lcPiecesLibrary::Load(const QString& LibraryPath, bool ShowProgress)
2013-08-09 06:57:18 +02:00
{
Unload();
2021-01-18 21:09:33 +01:00
if (OpenArchive(LibraryPath, lcZipFileType::Official))
2013-08-09 06:57:18 +02:00
{
2021-01-22 22:40:16 +01:00
LoadColors();
2013-08-09 06:57:18 +02:00
mLibraryDir = QFileInfo(LibraryPath).absoluteDir();
QString UnofficialFileName = mLibraryDir.absoluteFilePath(QLatin1String("ldrawunf.zip"));
2021-01-18 21:09:33 +01:00
if (!OpenArchive(UnofficialFileName, lcZipFileType::Unofficial))
UnofficialFileName.clear();
ReadArchiveDescriptions(LibraryPath, UnofficialFileName);
2013-08-09 06:57:18 +02:00
}
else
{
2020-02-24 23:31:08 +01:00
mLibraryDir.setPath(LibraryPath);
2013-08-09 06:57:18 +02:00
if (OpenDirectory(mLibraryDir, ShowProgress))
2021-01-22 22:40:16 +01:00
LoadColors();
2013-08-09 06:57:18 +02:00
else
return false;
}
2021-01-20 13:19:29 +01:00
UpdateStudStyleSource();
2013-08-09 06:57:18 +02:00
lcLoadDefaultCategories();
lcSynthInit();
2024-11-03 03:34:22 +01:00
lcTrainTrackInit(this);
2013-08-09 06:57:18 +02:00
return true;
}
2021-01-22 22:40:16 +01:00
void lcPiecesLibrary::LoadColors()
2021-01-20 11:56:30 +01:00
{
2021-01-22 22:40:16 +01:00
QString CustomColorsPath = lcGetProfileString(LC_PROFILE_COLOR_CONFIG);
2021-01-20 11:56:30 +01:00
2021-01-22 22:40:16 +01:00
if (!CustomColorsPath.isEmpty())
{
2021-01-20 11:56:30 +01:00
lcDiskFile ColorFile(CustomColorsPath);
2021-01-22 22:40:16 +01:00
if (ColorFile.Open(QIODevice::ReadOnly) && lcLoadColorFile(ColorFile, mStudStyle))
{
emit ColorsLoaded();
return;
}
}
2021-01-20 11:56:30 +01:00
if (mZipFiles[static_cast<int>(lcZipFileType::Official)])
{
lcMemFile ColorFile;
2021-01-22 22:40:16 +01:00
if (!mZipFiles[static_cast<int>(lcZipFileType::Official)]->ExtractFile("ldraw/ldconfig.ldr", ColorFile) || !lcLoadColorFile(ColorFile, mStudStyle))
lcLoadDefaultColors(mStudStyle);
2021-01-20 11:56:30 +01:00
}
else
{
2021-01-22 22:40:16 +01:00
lcDiskFile ColorFile(mLibraryDir.absoluteFilePath(QLatin1String("ldconfig.ldr")));
2021-01-20 11:56:30 +01:00
2021-01-22 22:40:16 +01:00
if (!ColorFile.Open(QIODevice::ReadOnly) || !lcLoadColorFile(ColorFile, mStudStyle))
{
ColorFile.SetFileName(mLibraryDir.absoluteFilePath(QLatin1String("LDConfig.ldr")));
2021-01-20 11:56:30 +01:00
2021-01-22 22:40:16 +01:00
if (!ColorFile.Open(QIODevice::ReadOnly) || !lcLoadColorFile(ColorFile, mStudStyle))
lcLoadDefaultColors(mStudStyle);
2021-01-20 11:56:30 +01:00
}
}
2021-01-22 22:40:16 +01:00
emit ColorsLoaded();
2021-01-20 11:56:30 +01:00
}
bool lcPiecesLibrary::IsStudPrimitive(const char* FileName)
{
return memcmp(FileName, "STU", 3) == 0;
}
bool lcPiecesLibrary::IsStudStylePrimitive(const char* FileName)
{
constexpr std::array<const char*, 15> StudStylePrimitives =
{
"2-4STUD4.DAT", "STUD.DAT", "STUD2.DAT", "STUD2A.DAT", "STUD3.DAT", "STUD4.DAT", "STUD4A.DAT", "STUD4H.DAT",
"8/STUD.DAT", "8/STUD2.DAT", "8/STUD2A.DAT", "8/STUD3.DAT", "8/STUD4.DAT", "8/STUD4A.DAT", "8/STUD4H.DAT"
};
for (const char* StudStylePrimitive : StudStylePrimitives)
if (!strcmp(StudStylePrimitive, FileName))
return true;
return false;
}
2021-01-20 13:19:29 +01:00
void lcPiecesLibrary::UpdateStudStyleSource()
{
2021-01-20 13:19:29 +01:00
if (!mSources.empty() && mSources.front()->Type == lcLibrarySourceType::StudStyle)
mSources.erase(mSources.begin());
2021-01-20 13:19:29 +01:00
mZipFiles[static_cast<int>(lcZipFileType::StudStyle)].reset();
if (mStudStyle == lcStudStyle::Plain || (mStudStyle >= lcStudStyle::HighContrast && !mStudCylinderColorEnabled))
return;
const QLatin1String FileNames[] =
{
QLatin1String(""), // Plain
QLatin1String(":/resources/studlogo1.zip"), // ThinLinesLogo
QLatin1String(":/resources/studlogo2.zip"), // OutlineLogo
QLatin1String(":/resources/studlogo3.zip"), // SharpTopLogo
QLatin1String(":/resources/studlogo4.zip"), // RoundedTopLogo
QLatin1String(":/resources/studlogo5.zip"), // FlattenedLogo
QLatin1String(":/resources/studslegostyle1.zip"), // HighContrast
QLatin1String(":/resources/studslegostyle2.zip") // HighContrastLogo
};
LC_ARRAY_SIZE_CHECK(FileNames, lcStudStyle::Count);
std::unique_ptr<lcDiskFile> StudStyleFile(new lcDiskFile(FileNames[static_cast<int>(mStudStyle)]));
2021-01-20 13:19:29 +01:00
if (StudStyleFile->Open(QIODevice::ReadOnly))
OpenArchive(std::move(StudStyleFile), lcZipFileType::StudStyle);
}
bool lcPiecesLibrary::OpenArchive(const QString& FileName, lcZipFileType ZipFileType)
2014-09-11 21:55:34 +02:00
{
std::unique_ptr<lcDiskFile> File(new lcDiskFile(FileName));
2014-09-11 21:55:34 +02:00
if (!File->Open(QIODevice::ReadOnly))
2014-09-11 21:55:34 +02:00
return false;
2020-12-19 20:12:31 +01:00
return OpenArchive(std::move(File), ZipFileType);
2014-09-11 21:55:34 +02:00
}
2020-12-19 20:12:31 +01:00
bool lcPiecesLibrary::OpenArchive(std::unique_ptr<lcFile> File, lcZipFileType ZipFileType)
2013-08-09 06:57:18 +02:00
{
std::unique_ptr<lcZipFile> ZipFile(new lcZipFile());
2013-08-09 06:57:18 +02:00
if (!ZipFile->OpenRead(std::move(File)))
2013-08-09 06:57:18 +02:00
return false;
std::unique_ptr<lcLibrarySource> Source(new lcLibrarySource);
2021-01-20 13:19:29 +01:00
Source->Type = ZipFileType != lcZipFileType::StudStyle ? lcLibrarySourceType::Library : lcLibrarySourceType::StudStyle;
2024-05-14 04:59:37 +02:00
for (quint32 FileIdx = 0; FileIdx < ZipFile->mFiles.size(); FileIdx++)
2013-08-09 06:57:18 +02:00
{
lcZipFileInfo& FileInfo = ZipFile->mFiles[FileIdx];
char NameBuffer[LC_PIECE_NAME_LEN];
char* Name = NameBuffer;
2013-08-09 06:57:18 +02:00
const char* Src = FileInfo.file_name;
char* Dst = Name;
while (*Src && Dst - Name < LC_PIECE_NAME_LEN)
{
if (*Src >= 'a' && *Src <= 'z')
*Dst = *Src + 'A' - 'a';
else if (*Src == '\\')
*Dst = '/';
else
*Dst = *Src;
Src++;
Dst++;
}
if (Dst - Name <= 4)
continue;
*Dst = 0;
2013-08-09 06:57:18 +02:00
Dst -= 4;
if (memcmp(Dst, ".DAT", 4))
{
if (!memcmp(Dst, ".PNG", 4))
2013-08-09 06:57:18 +02:00
{
2021-01-18 21:09:33 +01:00
if ((ZipFileType == lcZipFileType::Official && !memcmp(Name, "LDRAW/PARTS/TEXTURES/", 21)) ||
(ZipFileType == lcZipFileType::Unofficial && !memcmp(Name, "PARTS/TEXTURES/", 15)))
{
lcTexture* Texture = new lcTexture();
2019-07-05 02:06:26 +02:00
mTextures.push_back(Texture);
2013-08-09 06:57:18 +02:00
*Dst = 0;
2021-01-18 21:09:33 +01:00
strncpy(Texture->mName, Name + (ZipFileType == lcZipFileType::Official ? 21 : 15), sizeof(Texture->mName)-1);
Texture->mName[sizeof(Texture->mName) - 1] = 0;
}
2013-08-09 06:57:18 +02:00
}
continue;
}
2021-01-18 21:09:33 +01:00
if (ZipFileType == lcZipFileType::Official)
{
if (memcmp(Name, "LDRAW/", 6))
continue;
Name += 6;
}
2013-08-09 06:57:18 +02:00
if (!memcmp(Name, "PARTS/", 6))
2013-08-09 06:57:18 +02:00
{
Name += 6;
if (memcmp(Name, "S/", 2))
2013-08-09 06:57:18 +02:00
{
PieceInfo* Info = FindPiece(Name, nullptr, false, false);
if (!Info)
{
Info = new PieceInfo();
2013-08-09 06:57:18 +02:00
2020-12-19 21:10:10 +01:00
strncpy(Info->mFileName, FileInfo.file_name + (Name - NameBuffer), sizeof(Info->mFileName)-1);
Info->mFileName[sizeof(Info->mFileName) - 1] = 0;
2017-07-24 04:35:18 +02:00
mPieces[Name] = Info;
}
Info->SetZipFile(ZipFileType, FileIdx);
2013-08-09 06:57:18 +02:00
}
else
Source->Primitives[Name] = new lcLibraryPrimitive(QString(), FileInfo.file_name + (Name - NameBuffer), ZipFileType, FileIdx, false, false, true);
2013-08-09 06:57:18 +02:00
}
else if (!memcmp(Name, "P/", 2))
2013-08-09 06:57:18 +02:00
{
Name += 2;
Source->Primitives[Name] = new lcLibraryPrimitive(QString(), FileInfo.file_name + (Name - NameBuffer), ZipFileType, FileIdx, IsStudPrimitive(Name), IsStudStylePrimitive(Name), false);
2013-08-09 06:57:18 +02:00
}
}
2021-01-18 21:09:33 +01:00
mZipFiles[static_cast<int>(ZipFileType)] = std::move(ZipFile);
if (ZipFileType != lcZipFileType::StudStyle)
mSources.emplace_back(std::move(Source));
else
mSources.insert(mSources.begin(), std::move(Source));
return true;
}
void lcPiecesLibrary::ReadArchiveDescriptions(const QString& OfficialFileName, const QString& UnofficialFileName)
{
QFileInfo OfficialInfo(OfficialFileName);
2016-02-19 18:53:54 +01:00
QFileInfo UnofficialInfo(UnofficialFileName);
mArchiveCheckSum[0] = OfficialInfo.size();
mArchiveCheckSum[1] = OfficialInfo.lastModified().toMSecsSinceEpoch();
2021-01-08 19:35:52 +01:00
if (!UnofficialFileName.isEmpty())
{
mArchiveCheckSum[2] = UnofficialInfo.size();
mArchiveCheckSum[3] = UnofficialInfo.lastModified().toMSecsSinceEpoch();
}
else
{
mArchiveCheckSum[2] = 0;
mArchiveCheckSum[3] = 0;
}
QString IndexFileName = QFileInfo(QDir(mCachePath), QLatin1String("index")).absoluteFilePath();
2013-08-09 06:57:18 +02:00
if (!LoadCacheIndex(IndexFileName))
2013-08-09 06:57:18 +02:00
{
lcMemFile PieceFile;
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
2013-08-09 06:57:18 +02:00
{
2017-07-24 04:35:18 +02:00
PieceInfo* Info = PieceIt.second;
2013-08-09 06:57:18 +02:00
2021-01-18 21:09:33 +01:00
mZipFiles[static_cast<int>(Info->mZipFileType)]->ExtractFile(Info->mZipFileIndex, PieceFile, 256);
2013-08-09 06:57:18 +02:00
PieceFile.Seek(0, SEEK_END);
PieceFile.WriteU8(0);
char* Src = (char*)PieceFile.mBuffer + 2;
char* Dst = Info->m_strDescription;
for (;;)
{
if (*Src != '\r' && *Src != '\n' && *Src && Dst - Info->m_strDescription < (int)sizeof(Info->m_strDescription) - 1)
{
*Dst++ = *Src++;
continue;
}
*Dst = 0;
break;
}
}
SaveArchiveCacheIndex(IndexFileName);
2013-08-09 06:57:18 +02:00
}
}
bool lcPiecesLibrary::OpenDirectory(const QDir& LibraryDir, bool ShowProgress)
2013-08-09 06:57:18 +02:00
{
const QLatin1String BaseFolders[] = { QLatin1String(""), QLatin1String("unofficial/") };
2020-03-23 04:18:52 +01:00
constexpr int NumBaseFolders = LC_ARRAY_COUNT(BaseFolders);
QFileInfoList FileLists[NumBaseFolders];
2013-08-09 06:57:18 +02:00
for (unsigned int BaseFolderIdx = 0; BaseFolderIdx < NumBaseFolders; BaseFolderIdx++)
{
QString ParstPath = QDir(LibraryDir.absoluteFilePath(BaseFolders[BaseFolderIdx])).absoluteFilePath(QLatin1String("parts/"));
QDir Dir = QDir(ParstPath, QLatin1String("*.dat"), QDir::SortFlags(QDir::Name | QDir::IgnoreCase), QDir::Files | QDir::Hidden | QDir::Readable);
FileLists[BaseFolderIdx] = Dir.entryInfoList();
2013-08-09 06:57:18 +02:00
}
if (FileLists[static_cast<int>(lcLibraryFolderType::Official)].isEmpty())
2013-08-09 06:57:18 +02:00
return false;
mHasUnofficial = !FileLists[static_cast<int>(lcLibraryFolderType::Unofficial)].isEmpty();
ReadDirectoryDescriptions(FileLists, ShowProgress);
2019-12-09 20:43:54 +01:00
for (unsigned int BaseFolderIdx = 0; BaseFolderIdx < LC_ARRAY_COUNT(BaseFolders); BaseFolderIdx++)
2013-08-09 06:57:18 +02:00
{
std::unique_ptr<lcLibrarySource> Source(new lcLibrarySource);
Source->Type = lcLibrarySourceType::Library;
const char* PrimitiveDirectories[] = { "p/", "parts/s/" };
bool SubFileDirectories[] = { false, false, true };
QDir BaseDir(LibraryDir.absoluteFilePath(QLatin1String(BaseFolders[BaseFolderIdx])));
2013-08-09 06:57:18 +02:00
2019-12-09 20:43:54 +01:00
for (int DirectoryIdx = 0; DirectoryIdx < (int)(LC_ARRAY_COUNT(PrimitiveDirectories)); DirectoryIdx++)
2013-08-09 06:57:18 +02:00
{
QString ChildPath = BaseDir.absoluteFilePath(QLatin1String(PrimitiveDirectories[DirectoryIdx]));
QDirIterator DirIterator(ChildPath, QStringList() << QLatin1String("*.dat"), QDir::Files | QDir::Hidden | QDir::Readable, QDirIterator::Subdirectories);
2013-08-09 06:57:18 +02:00
while (DirIterator.hasNext())
2013-08-09 06:57:18 +02:00
{
char Name[LC_PIECE_NAME_LEN];
QString FileName = DirIterator.next();
QByteArray FileString = BaseDir.relativeFilePath(FileName).toLatin1();
const char* Src = strchr(FileString, '/') + 1;
char* Dst = Name;
2013-08-09 06:57:18 +02:00
while (*Src && Dst - Name < (int)sizeof(Name))
{
if (*Src >= 'a' && *Src <= 'z')
*Dst = *Src + 'A' - 'a';
else if (*Src == '\\')
*Dst = '/';
else
*Dst = *Src;
2013-08-09 06:57:18 +02:00
Src++;
Dst++;
}
*Dst = 0;
2013-08-09 06:57:18 +02:00
if (Dst - Name <= 4)
continue;
Dst -= 4;
if (memcmp(Dst, ".DAT", 4))
continue;
if (BaseFolderIdx > 0 && IsPrimitive(Name))
continue;
if (BaseFolderIdx == static_cast<int>(lcLibraryFolderType::Unofficial))
mHasUnofficial = true;
2020-03-23 04:18:52 +01:00
const bool SubFile = SubFileDirectories[DirectoryIdx];
Source->Primitives[Name] = new lcLibraryPrimitive(std::move(FileName), strchr(FileString, '/') + 1, lcZipFileType::Count, 0, !SubFile && IsStudPrimitive(Name), IsStudStylePrimitive(Name), SubFile);
}
2013-08-09 06:57:18 +02:00
}
mSources.emplace_back(std::move(Source));
2013-08-09 06:57:18 +02:00
}
2019-12-09 20:43:54 +01:00
for (unsigned int BaseFolderIdx = 0; BaseFolderIdx < LC_ARRAY_COUNT(BaseFolders); BaseFolderIdx++)
2013-08-09 06:57:18 +02:00
{
QDir BaseDir(LibraryDir.absoluteFilePath(QLatin1String(BaseFolders[BaseFolderIdx])));
QDir Dir(BaseDir.absoluteFilePath(QLatin1String("parts/textures/")), QLatin1String("*.png"), QDir::SortFlags(QDir::Name | QDir::IgnoreCase), QDir::Files | QDir::Hidden | QDir::Readable);
QStringList FileList = Dir.entryList();
mTextures.reserve(mTextures.size() + FileList.size());
2013-08-09 06:57:18 +02:00
for (int FileIdx = 0; FileIdx < FileList.size(); FileIdx++)
2013-08-09 06:57:18 +02:00
{
char Name[LC_MAXPATH];
QByteArray FileString = FileList[FileIdx].toLatin1();
const char* Src = FileString;
char* Dst = Name;
2013-08-09 06:57:18 +02:00
while (*Src && Dst - Name < (int)sizeof(Name))
{
if (*Src >= 'a' && *Src <= 'z')
*Dst = *Src + 'A' - 'a';
else if (*Src == '\\')
*Dst = '/';
else
*Dst = *Src;
2013-08-09 06:57:18 +02:00
Src++;
Dst++;
}
2013-08-09 06:57:18 +02:00
if (Dst - Name <= 4)
continue;
Dst -= 4;
if (memcmp(Dst, ".PNG", 4))
continue;
*Dst = 0;
2013-08-09 06:57:18 +02:00
lcTexture* Texture = new lcTexture();
mTextures.push_back(Texture);
2013-08-09 06:57:18 +02:00
strncpy(Texture->mName, Name, sizeof(Texture->mName));
Texture->mName[sizeof(Texture->mName) - 1] = 0;
Texture->mFileName = Dir.absoluteFilePath(FileList[FileIdx]);
}
2013-08-09 06:57:18 +02:00
}
return true;
}
void lcPiecesLibrary::ReadDirectoryDescriptions(const QFileInfoList (&FileLists)[static_cast<int>(lcLibraryFolderType::Count)], bool ShowProgress)
{
QString IndexFileName = QFileInfo(QDir(mCachePath), QLatin1String("index")).absoluteFilePath();
lcMemFile IndexFile;
std::vector<const char*> CachedDescriptions;
if (ReadDirectoryCacheFile(IndexFileName, IndexFile))
{
QString LibraryPath = IndexFile.ReadQString();
if (LibraryPath == mLibraryDir.absolutePath())
{
int NumDescriptions = IndexFile.ReadU32();
CachedDescriptions.reserve(NumDescriptions);
while (NumDescriptions--)
{
const char* FileName = (const char*)IndexFile.mBuffer + IndexFile.GetPosition();
CachedDescriptions.push_back(FileName);
IndexFile.Seek(strlen(FileName) + 1, SEEK_CUR);
const char* Description = (const char*)IndexFile.mBuffer + IndexFile.GetPosition();
IndexFile.Seek(strlen(Description) + 1, SEEK_CUR);
IndexFile.Seek(4 + 1 + 8, SEEK_CUR);
}
}
}
for (int FolderIdx = 0; FolderIdx < static_cast<int>(lcLibraryFolderType::Count); FolderIdx++)
{
const QFileInfoList& FileList = FileLists[FolderIdx];
for (int FileIdx = 0; FileIdx < FileList.size(); FileIdx++)
{
char Name[LC_PIECE_NAME_LEN];
QByteArray FileString = FileList[FileIdx].fileName().toLatin1();
const char* Src = FileString;
char* Dst = Name;
while (*Src && Dst - Name < (int)sizeof(Name))
{
if (*Src >= 'a' && *Src <= 'z')
*Dst = *Src + 'A' - 'a';
else if (*Src == '\\')
*Dst = '/';
else
*Dst = *Src;
Src++;
Dst++;
}
*Dst = 0;
if (FolderIdx > 0 && mPieces.find(Name) != mPieces.end())
continue;
PieceInfo* Info = new PieceInfo();
strncpy(Info->mFileName, FileString, sizeof(Info->mFileName));
Info->mFileName[sizeof(Info->mFileName) - 1] = 0;
Info->mFolderType = FolderIdx;
Info->mFolderIndex = FileIdx;
mPieces[Name] = Info;
}
}
QAtomicInt FilesLoaded;
bool Modified = false;
auto ReadDescriptions = [&FileLists, &CachedDescriptions, &FilesLoaded, &Modified](const std::pair<std::string, PieceInfo*>& Entry)
{
PieceInfo* Info = Entry.second;
FilesLoaded.ref();
lcDiskFile PieceFile(FileLists[Info->mFolderType][Info->mFolderIndex].absoluteFilePath());
char Line[1024];
if (!CachedDescriptions.empty())
{
auto DescriptionCompare = [](const void* Key, const void* Element)
{
return strcmp((const char*)Key, *(const char**)Element);
};
void* CachedDescription = bsearch(Info->mFileName, &CachedDescriptions.front(), CachedDescriptions.size(), sizeof(char*), DescriptionCompare);
if (CachedDescription)
{
const char* FileName = *(const char**)CachedDescription;
const char* Description = FileName + strlen(FileName) + 1;
2020-03-23 04:18:52 +01:00
const uint64_t CachedFileTime = *(uint64_t*)(Description + strlen(Description) + 1 + 4 + 1);
quint64 FileTime = FileLists[Info->mFolderType][Info->mFolderIndex].lastModified().toMSecsSinceEpoch();
if (FileTime == CachedFileTime)
{
strcpy(Info->m_strDescription, Description);
return;
}
}
}
if (!PieceFile.Open(QIODevice::ReadOnly) || !PieceFile.ReadLine(Line, sizeof(Line)))
{
strcpy(Info->m_strDescription, "Unknown");
return;
}
const char* Src = Line + 2;
char* Dst = Info->m_strDescription;
for (;;)
{
if (*Src != '\r' && *Src != '\n' && *Src && Dst - Info->m_strDescription < (int)sizeof(Info->m_strDescription) - 1)
{
*Dst++ = *Src++;
continue;
}
*Dst = 0;
break;
}
Modified = true;
};
QProgressDialog* ProgressDialog = new QProgressDialog(nullptr);
ProgressDialog->setWindowFlags(ProgressDialog->windowFlags() & ~Qt::WindowCloseButtonHint);
ProgressDialog->setWindowTitle(tr("Initializing"));
ProgressDialog->setLabelText(tr("Loading Parts Library"));
2019-05-18 20:33:27 +02:00
ProgressDialog->setMaximum((int)mPieces.size());
ProgressDialog->setMinimum(0);
ProgressDialog->setValue(0);
ProgressDialog->setCancelButton(nullptr);
ProgressDialog->setAutoReset(false);
if (ShowProgress)
ProgressDialog->show();
QFuture<void> LoadFuture = QtConcurrent::map(mPieces, ReadDescriptions);
while (!LoadFuture.isFinished())
{
ProgressDialog->setValue(FilesLoaded);
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
ProgressDialog->setValue(FilesLoaded);
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
ProgressDialog->deleteLater();
if (Modified)
{
2019-05-18 20:33:27 +02:00
lcMemFile NewIndexFile;
2019-05-18 20:33:27 +02:00
NewIndexFile.WriteQString(mLibraryDir.absolutePath());
2019-05-18 20:33:27 +02:00
NewIndexFile.WriteU32((quint32)mPieces.size());
std::vector<PieceInfo*> SortedPieces;
SortedPieces.reserve(mPieces.size());
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
SortedPieces.push_back(PieceIt.second);
auto PieceInfoCompare = [](PieceInfo* Info1, PieceInfo* Info2)
{
return strcmp(Info1->mFileName, Info2->mFileName) < 0;
};
std::sort(SortedPieces.begin(), SortedPieces.end(), PieceInfoCompare);
for (const PieceInfo* Info : SortedPieces)
{
2019-05-18 20:33:27 +02:00
if (NewIndexFile.WriteBuffer(Info->mFileName, strlen(Info->mFileName) + 1) == 0)
return;
2019-05-18 20:33:27 +02:00
if (NewIndexFile.WriteBuffer(Info->m_strDescription, strlen(Info->m_strDescription) + 1) == 0)
return;
2021-07-06 02:00:41 +02:00
NewIndexFile.WriteU8(static_cast<quint8>(Info->mFolderType));
2017-12-02 21:22:04 +01:00
quint64 FileTime = FileLists[Info->mFolderType][Info->mFolderIndex].lastModified().toMSecsSinceEpoch();
2019-05-18 20:33:27 +02:00
NewIndexFile.WriteU64(FileTime);
}
2019-05-18 20:33:27 +02:00
WriteDirectoryCacheFile(IndexFileName, NewIndexFile);
}
}
bool lcPiecesLibrary::ReadArchiveCacheFile(const QString& FileName, lcMemFile& CacheFile)
2013-08-09 06:57:18 +02:00
{
QFile File(FileName);
2013-08-09 06:57:18 +02:00
if (!File.open(QIODevice::ReadOnly))
2013-08-09 06:57:18 +02:00
return false;
quint32 CacheVersion, CacheFlags;
if (File.read((char*)&CacheVersion, sizeof(CacheVersion)) == -1 || CacheVersion != LC_LIBRARY_CACHE_VERSION)
2013-08-09 06:57:18 +02:00
return false;
if (File.read((char*)&CacheFlags, sizeof(CacheFlags)) == -1 || CacheFlags != LC_LIBRARY_CACHE_ARCHIVE)
return false;
2013-08-09 06:57:18 +02:00
qint64 CacheCheckSum[4];
2013-08-09 06:57:18 +02:00
if (File.read((char*)&CacheCheckSum, sizeof(CacheCheckSum)) == -1 || memcmp(CacheCheckSum, mArchiveCheckSum, sizeof(CacheCheckSum)))
2013-08-09 06:57:18 +02:00
return false;
quint32 UncompressedSize;
2013-08-09 06:57:18 +02:00
if (File.read((char*)&UncompressedSize, sizeof(UncompressedSize)) == -1)
return false;
2013-08-09 06:57:18 +02:00
QByteArray CompressedData = File.readAll();
2013-08-09 06:57:18 +02:00
CacheFile.SetLength(UncompressedSize);
CacheFile.Seek(0, SEEK_SET);
2013-08-09 06:57:18 +02:00
2021-07-06 02:00:41 +02:00
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
constexpr qsizetype CHUNK = 16384;
#else
2020-03-23 04:18:52 +01:00
constexpr int CHUNK = 16384;
2021-07-06 02:00:41 +02:00
#endif
int ret;
unsigned have;
z_stream strm;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
int pos;
2013-08-09 06:57:18 +02:00
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
pos = 0;
2013-08-09 06:57:18 +02:00
ret = inflateInit2(&strm, -MAX_WBITS);
if (ret != Z_OK)
return ret;
2013-08-09 06:57:18 +02:00
do
2013-08-09 06:57:18 +02:00
{
strm.avail_in = lcMin(CompressedData.size() - pos, CHUNK);
strm.next_in = in;
2013-08-09 06:57:18 +02:00
if (strm.avail_in == 0)
break;
2013-08-09 06:57:18 +02:00
memcpy(in, CompressedData.constData() + pos, strm.avail_in);
pos += strm.avail_in;
2013-08-09 06:57:18 +02:00
do
{
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
2013-08-09 06:57:18 +02:00
switch (ret)
{
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
2018-09-24 20:29:05 +02:00
Q_FALLTHROUGH();
case Z_DATA_ERROR:
2018-09-24 20:29:05 +02:00
Q_FALLTHROUGH();
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return ret;
}
2013-08-09 06:57:18 +02:00
have = CHUNK - strm.avail_out;
CacheFile.WriteBuffer(out, have);
} while (strm.avail_out == 0);
} while (ret != Z_STREAM_END);
(void)inflateEnd(&strm);
CacheFile.Seek(0, SEEK_SET);
return ret == Z_STREAM_END;
2013-08-09 06:57:18 +02:00
}
bool lcPiecesLibrary::WriteArchiveCacheFile(const QString& FileName, lcMemFile& CacheFile)
2013-08-09 06:57:18 +02:00
{
QFile File(FileName);
if (!File.open(QIODevice::WriteOnly))
2013-08-09 06:57:18 +02:00
return false;
2020-03-23 04:18:52 +01:00
constexpr quint32 CacheVersion = LC_LIBRARY_CACHE_VERSION;
constexpr quint32 CacheFlags = LC_LIBRARY_CACHE_ARCHIVE;
if (File.write((char*)&CacheVersion, sizeof(CacheVersion)) == -1)
return false;
2013-08-09 06:57:18 +02:00
if (File.write((char*)&CacheFlags, sizeof(CacheFlags)) == -1)
return false;
2013-08-09 06:57:18 +02:00
if (File.write((char*)&mArchiveCheckSum, sizeof(mArchiveCheckSum)) == -1)
return false;
2013-08-09 06:57:18 +02:00
2020-03-23 04:18:52 +01:00
const quint32 UncompressedSize = (quint32)CacheFile.GetLength();
2013-08-09 06:57:18 +02:00
if (File.write((char*)&UncompressedSize, sizeof(UncompressedSize)) == -1)
return false;
2013-08-09 06:57:18 +02:00
2020-03-23 04:18:52 +01:00
constexpr size_t BufferSize = 16384;
char WriteBuffer[BufferSize];
z_stream Stream;
2017-12-02 21:22:04 +01:00
quint32 Crc32 = 0;
2013-08-09 06:57:18 +02:00
CacheFile.Seek(0, SEEK_SET);
2013-08-09 06:57:18 +02:00
2019-06-03 22:23:04 +02:00
Stream.zalloc = nullptr;
Stream.zfree = nullptr;
Stream.opaque = nullptr;
2013-08-09 06:57:18 +02:00
if (deflateInit2(&Stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
return false;
2013-08-09 06:57:18 +02:00
Bytef* BufferIn = CacheFile.mBuffer;
int FlushMode;
2013-08-09 06:57:18 +02:00
do
{
2016-02-19 18:53:54 +01:00
uInt Read = (uInt)lcMin(CacheFile.GetLength() - (BufferIn - CacheFile.mBuffer), BufferSize);
Stream.avail_in = Read;
Stream.next_in = BufferIn;
Crc32 = crc32(Crc32, BufferIn, Read);
BufferIn += Read;
FlushMode = (BufferIn >= CacheFile.mBuffer + CacheFile.GetLength()) ? Z_FINISH : Z_NO_FLUSH;
do
{
Stream.avail_out = BufferSize;
Stream.next_out = (Bytef*)WriteBuffer;
deflate(&Stream, FlushMode);
File.write(WriteBuffer, BufferSize - Stream.avail_out);
} while (Stream.avail_out == 0);
} while (FlushMode != Z_FINISH);
deflateEnd(&Stream);
return true;
2013-08-09 06:57:18 +02:00
}
bool lcPiecesLibrary::ReadDirectoryCacheFile(const QString& FileName, lcMemFile& CacheFile)
{
QFile File(FileName);
if (!File.open(QIODevice::ReadOnly))
return false;
quint32 CacheVersion, CacheFlags;
if (File.read((char*)&CacheVersion, sizeof(CacheVersion)) == -1 || CacheVersion != LC_LIBRARY_CACHE_VERSION)
return false;
if (File.read((char*)&CacheFlags, sizeof(CacheFlags)) == -1 || CacheFlags != LC_LIBRARY_CACHE_DIRECTORY)
return false;
quint32 UncompressedSize;
if (File.read((char*)&UncompressedSize, sizeof(UncompressedSize)) == -1)
return false;
QByteArray Data = qUncompress(File.readAll());
if (Data.isEmpty())
return false;
CacheFile.SetLength(Data.size());
CacheFile.Seek(0, SEEK_SET);
CacheFile.WriteBuffer(Data.constData(), Data.size());
CacheFile.Seek(0, SEEK_SET);
return true;
}
bool lcPiecesLibrary::WriteDirectoryCacheFile(const QString& FileName, lcMemFile& CacheFile)
{
QFile File(FileName);
if (!File.open(QIODevice::WriteOnly))
return false;
2020-03-23 04:18:52 +01:00
constexpr quint32 CacheVersion = LC_LIBRARY_CACHE_VERSION;
if (File.write((char*)&CacheVersion, sizeof(CacheVersion)) == -1)
return false;
2020-03-23 04:18:52 +01:00
constexpr quint32 CacheFlags = LC_LIBRARY_CACHE_DIRECTORY;
if (File.write((char*)&CacheFlags, sizeof(CacheFlags)) == -1)
return false;
2020-03-23 04:18:52 +01:00
const quint32 UncompressedSize = (quint32)CacheFile.GetLength();
if (File.write((char*)&UncompressedSize, sizeof(UncompressedSize)) == -1)
return false;
2019-05-18 20:33:27 +02:00
File.write(qCompress(CacheFile.mBuffer, (int)CacheFile.GetLength()));
return true;
}
bool lcPiecesLibrary::LoadCacheIndex(const QString& FileName)
2013-08-09 06:57:18 +02:00
{
lcMemFile IndexFile;
2013-08-09 06:57:18 +02:00
if (!ReadArchiveCacheFile(FileName, IndexFile))
return false;
2017-07-27 21:40:52 +02:00
quint32 NumFiles;
2017-07-24 04:35:18 +02:00
if (IndexFile.ReadBuffer((char*)&NumFiles, sizeof(NumFiles)) == 0 || NumFiles != mPieces.size())
return false;
2013-08-09 06:57:18 +02:00
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
2013-08-09 06:57:18 +02:00
{
2017-07-24 04:35:18 +02:00
PieceInfo* Info = PieceIt.second;
quint8 Length;
2013-08-09 06:57:18 +02:00
if (IndexFile.ReadBuffer((char*)&Length, sizeof(Length)) == 0 || Length >= sizeof(Info->m_strDescription))
return false;
2013-08-09 06:57:18 +02:00
2019-07-21 18:26:36 +02:00
if (IndexFile.ReadBuffer((char*)Info->m_strDescription, Length) == 0)
return false;
2013-08-09 06:57:18 +02:00
Info->m_strDescription[Length] = 0;
}
return true;
}
bool lcPiecesLibrary::SaveArchiveCacheIndex(const QString& FileName)
{
lcMemFile IndexFile;
2013-08-09 06:57:18 +02:00
2020-03-23 04:18:52 +01:00
const quint32 NumFiles = (quint32)mPieces.size();
2013-08-09 06:57:18 +02:00
if (IndexFile.WriteBuffer((char*)&NumFiles, sizeof(NumFiles)) == 0)
return false;
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
2013-08-09 06:57:18 +02:00
{
2020-03-23 04:18:52 +01:00
const PieceInfo* Info = PieceIt.second;
const quint8 Length = (quint8)strlen(Info->m_strDescription);
2013-08-09 06:57:18 +02:00
if (IndexFile.WriteBuffer((char*)&Length, sizeof(Length)) == 0)
return false;
2019-07-21 18:26:36 +02:00
if (IndexFile.WriteBuffer((char*)Info->m_strDescription, Length) == 0)
return false;
2013-08-09 06:57:18 +02:00
}
return WriteArchiveCacheFile(FileName, IndexFile);
}
2013-08-09 06:57:18 +02:00
bool lcPiecesLibrary::LoadCachePiece(PieceInfo* Info)
{
QString FileName = QFileInfo(QDir(mCachePath), QString::fromLatin1(Info->mFileName)).absoluteFilePath();
lcMemFile MeshData;
2013-08-09 06:57:18 +02:00
if (!ReadArchiveCacheFile(FileName, MeshData))
return false;
2013-08-09 06:57:18 +02:00
qint32 Flags;
2019-09-24 02:06:16 +02:00
if (MeshData.ReadBuffer((char*)&Flags, sizeof(Flags)) == 0)
return false;
if (Flags != static_cast<qint32>(mStudStyle) + static_cast<qint32>(mStudCylinderColorEnabled))
2019-09-24 02:06:16 +02:00
return false;
2017-01-23 04:28:05 +01:00
lcMesh* Mesh = new lcMesh;
if (Mesh->FileLoad(MeshData))
{
Info->SetMesh(Mesh);
return true;
}
else
{
delete Mesh;
return false;
}
}
2013-08-09 06:57:18 +02:00
bool lcPiecesLibrary::SaveCachePiece(PieceInfo* Info)
{
lcMemFile MeshData;
2013-08-09 06:57:18 +02:00
const qint32 Flags = static_cast<qint32>(mStudStyle) + static_cast<qint32>(mStudCylinderColorEnabled);
2019-09-24 02:06:16 +02:00
if (MeshData.WriteBuffer((char*)&Flags, sizeof(Flags)) == 0)
return false;
if (!Info->GetMesh()->FileSave(MeshData))
return false;
2013-08-09 06:57:18 +02:00
QString FileName = QFileInfo(QDir(mCachePath), QString::fromLatin1(Info->mFileName)).absoluteFilePath();
2013-08-09 06:57:18 +02:00
return WriteArchiveCacheFile(FileName, MeshData);
2013-08-09 06:57:18 +02:00
}
2017-01-23 07:44:54 +01:00
class lcSleeper : public QThread
{
public:
static void msleep(unsigned long Msecs)
{
QThread::msleep(Msecs);
}
};
2017-01-23 04:28:05 +01:00
void lcPiecesLibrary::LoadPieceInfo(PieceInfo* Info, bool Wait, bool Priority)
{
QMutexLocker LoadLock(&mLoadMutex);
if (Wait)
{
if (Info->AddRef() == 1)
Info->Load();
else
{
2021-11-15 03:34:24 +01:00
if (Info->mState == lcPieceInfoState::Unloaded)
2017-01-23 04:28:05 +01:00
{
Info->Load();
emit PartLoaded(Info);
}
else
{
LoadLock.unlock();
2021-11-15 03:34:24 +01:00
while (Info->mState != lcPieceInfoState::Loaded)
2017-01-23 07:44:54 +01:00
lcSleeper::msleep(10);
2017-01-23 04:28:05 +01:00
}
}
}
else
{
2019-09-24 01:55:24 +02:00
if (Info->AddRef() == 1)
2017-01-23 04:28:05 +01:00
{
if (Priority)
mLoadQueue.prepend(Info);
else
mLoadQueue.append(Info);
mLoadFutures.append(QtConcurrent::run([this]() { LoadQueuedPiece(); }));
2017-01-23 04:28:05 +01:00
}
}
}
void lcPiecesLibrary::ReleasePieceInfo(PieceInfo* Info)
{
QMutexLocker LoadLock(&mLoadMutex);
if (Info->GetRefCount() == 0 || Info->Release() == 0)
Info->Unload();
}
void lcPiecesLibrary::LoadQueuedPiece()
{
mLoadMutex.lock();
PieceInfo* Info = nullptr;
2017-01-23 04:28:05 +01:00
while (!mLoadQueue.isEmpty())
{
Info = mLoadQueue.takeFirst();
2021-11-15 03:34:24 +01:00
if (Info->mState == lcPieceInfoState::Unloaded && Info->GetRefCount() > 0)
2017-01-23 04:28:05 +01:00
{
2021-11-15 03:34:24 +01:00
Info->mState = lcPieceInfoState::Loading;
2017-01-23 04:28:05 +01:00
break;
}
2017-01-24 05:26:57 +01:00
Info = nullptr;
2017-01-23 04:28:05 +01:00
}
mLoadMutex.unlock();
if (Info)
Info->Load();
emit PartLoaded(Info);
}
void lcPiecesLibrary::WaitForLoadQueue()
{
for (QFuture<void>& Future : mLoadFutures)
Future.waitForFinished();
mLoadFutures.clear();
}
bool lcPiecesLibrary::LoadPieceData(PieceInfo* Info)
2013-08-09 06:57:18 +02:00
{
lcLibraryMeshData MeshData;
lcMeshLoader MeshLoader(MeshData, true, nullptr, false);
2013-08-09 06:57:18 +02:00
bool Loaded = false;
bool SaveCache = false;
2021-01-18 21:09:33 +01:00
if (Info->mZipFileType != lcZipFileType::Count && mZipFiles[static_cast<int>(Info->mZipFileType)])
2013-08-09 06:57:18 +02:00
{
if (LoadCachePiece(Info))
return true;
lcMemFile PieceFile;
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(Info->mZipFileType)]->ExtractFile(Info->mZipFileIndex, PieceFile))
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
2013-08-09 06:57:18 +02:00
2024-11-14 21:02:08 +01:00
SaveCache = Loaded;
2013-08-09 06:57:18 +02:00
}
else
{
char FileName[LC_MAXPATH];
lcDiskFile PieceFile;
2021-03-14 05:01:25 +01:00
sprintf(FileName, "parts/%s", Info->mFileName);
PieceFile.SetFileName(mLibraryDir.absoluteFilePath(QLatin1String(FileName)));
if (PieceFile.Open(QIODevice::ReadOnly))
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
2021-03-14 05:01:25 +01:00
if (mHasUnofficial && !Loaded)
{
2021-03-14 05:01:25 +01:00
sprintf(FileName, "unofficial/parts/%s", Info->mFileName);
PieceFile.SetFileName(mLibraryDir.absoluteFilePath(QLatin1String(FileName)));
if (PieceFile.Open(QIODevice::ReadOnly))
Loaded = MeshLoader.LoadMesh(PieceFile, LC_MESHDATA_SHARED);
}
}
2020-12-31 23:23:08 +01:00
if (mCancelLoading)
return false;
2020-12-31 23:23:08 +01:00
if (Info)
{
if (Loaded)
Info->SetMesh(MeshData.CreateMesh());
else
{
lcMesh* Mesh = new lcMesh;
Mesh->CreateBox();
Info->SetMesh(Mesh);
}
}
if (SaveCache)
SaveCachePiece(Info);
2020-12-31 23:23:08 +01:00
return Loaded;
}
void lcPiecesLibrary::GetPrimitiveFile(lcLibraryPrimitive* Primitive, std::function<void(lcFile& File)> Callback)
{
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(lcZipFileType::Official)])
2013-08-09 06:57:18 +02:00
{
lcMemFile IncludeFile;
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(Primitive->mZipFileType)]->ExtractFile(Primitive->mZipFileIndex, IncludeFile))
Callback(IncludeFile);
}
else
{
lcDiskFile IncludeFile(Primitive->mFileName);
if (IncludeFile.Open(QIODevice::ReadOnly))
Callback(IncludeFile);
2013-08-09 06:57:18 +02:00
}
}
2013-08-09 06:57:18 +02:00
void lcPiecesLibrary::GetPieceFile(const char* PieceName, std::function<void(lcFile& File)> Callback)
{
const auto PieceIt = mPieces.find(PieceName);
2015-05-24 06:36:25 +02:00
if (PieceIt != mPieces.end())
2015-05-24 06:36:25 +02:00
{
PieceInfo* Info = PieceIt->second;
2015-05-24 06:36:25 +02:00
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(lcZipFileType::Official)] && Info->mZipFileType != lcZipFileType::Count)
2015-05-24 06:36:25 +02:00
{
lcMemFile IncludeFile;
2015-05-24 06:36:25 +02:00
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(Info->mZipFileType)]->ExtractFile(Info->mZipFileIndex, IncludeFile))
Callback(IncludeFile);
2015-05-24 06:36:25 +02:00
}
else
2015-05-24 06:36:25 +02:00
{
lcDiskFile IncludeFile;
char FileName[LC_MAXPATH];
2015-05-24 06:36:25 +02:00
bool Found = false;
2021-03-14 05:01:25 +01:00
sprintf(FileName, "parts/%s", Info->mFileName);
IncludeFile.SetFileName(mLibraryDir.absoluteFilePath(QLatin1String(FileName)));
Found = IncludeFile.Open(QIODevice::ReadOnly);
2015-05-24 06:36:25 +02:00
2021-03-14 05:01:25 +01:00
if (mHasUnofficial && !Found)
2015-05-24 06:36:25 +02:00
{
2021-03-14 05:01:25 +01:00
sprintf(FileName, "unofficial/parts/%s", Info->mFileName);
IncludeFile.SetFileName(mLibraryDir.absoluteFilePath(QLatin1String(FileName)));
Found = IncludeFile.Open(QIODevice::ReadOnly);
}
2013-08-09 06:57:18 +02:00
if (Found)
Callback(IncludeFile);
}
}
else
{
bool Found = false;
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(lcZipFileType::Official)])
{
lcMemFile IncludeFile;
2013-08-09 06:57:18 +02:00
2021-01-18 21:09:33 +01:00
auto LoadIncludeFile = [&IncludeFile, PieceName, this](const char* Folder, lcZipFileType ZipFileType)
{
char IncludeFileName[LC_MAXPATH];
2020-01-17 23:44:35 +01:00
sprintf(IncludeFileName, Folder, PieceName);
2021-01-18 21:09:33 +01:00
return mZipFiles[static_cast<int>(ZipFileType)]->ExtractFile(IncludeFileName, IncludeFile);
};
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile("ldraw/parts/%s", lcZipFileType::Official);
2013-08-09 06:57:18 +02:00
if (!Found)
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile("ldraw/p/%s", lcZipFileType::Official);
if (mZipFiles[static_cast<int>(lcZipFileType::Unofficial)] && !Found)
{
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile("parts/%s", lcZipFileType::Unofficial);
if (!Found)
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile("p/%s", lcZipFileType::Unofficial);
}
2013-08-09 06:57:18 +02:00
if (Found)
Callback(IncludeFile);
}
else
2015-05-24 06:36:25 +02:00
{
lcDiskFile IncludeFile;
2015-05-24 06:36:25 +02:00
2020-12-20 21:06:14 +01:00
auto LoadIncludeFile = [&IncludeFile, PieceName, this](const QLatin1String& Folder)
2015-05-24 06:36:25 +02:00
{
2020-12-20 21:06:14 +01:00
const QString IncludeFileName = Folder + PieceName;
IncludeFile.SetFileName(mLibraryDir.absoluteFilePath(IncludeFileName));
if (IncludeFile.Open(QIODevice::ReadOnly))
return true;
2015-05-24 06:36:25 +02:00
#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
2020-12-20 21:06:14 +01:00
// todo: search the parts/primitive lists and get the file name from there instead of using toLower
IncludeFile.SetFileName(mLibraryDir.absoluteFilePath(IncludeFileName.toLower()));
return IncludeFile.Open(QIODevice::ReadOnly);
#else
return false;
#endif
};
2015-05-24 06:36:25 +02:00
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile(QLatin1String("parts/"));
2015-05-24 06:36:25 +02:00
if (!Found)
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile(QLatin1String("p/"));
if (mHasUnofficial && !Found)
2015-05-24 06:36:25 +02:00
{
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile(QLatin1String("unofficial/parts/"));
2013-08-09 06:57:18 +02:00
if (!Found)
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile(QLatin1String("unofficial/p/"));
2013-08-09 06:57:18 +02:00
}
if (Found)
Callback(IncludeFile);
}
2013-08-09 06:57:18 +02:00
}
}
void lcPiecesLibrary::ReleaseBuffers()
2016-12-28 22:30:31 +01:00
{
lcContext* Context = lcContext::GetGlobalOffscreenContext();
Context->MakeCurrent();
2016-12-28 22:30:31 +01:00
Context->DestroyVertexBuffer(mVertexBuffer);
Context->DestroyIndexBuffer(mIndexBuffer);
2016-12-28 22:30:31 +01:00
mBuffersDirty = true;
}
void lcPiecesLibrary::UpdateBuffers(lcContext* Context)
{
2015-05-09 21:54:29 +02:00
if (!gSupportsVertexBufferObject || !mBuffersDirty)
return;
int VertexDataSize = 0;
int IndexDataSize = 0;
2020-01-24 22:12:34 +01:00
std::vector<lcMesh*> Meshes;
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
{
2020-03-23 04:18:52 +01:00
const PieceInfo* const Info = PieceIt.second;
2020-12-31 23:23:08 +01:00
lcMesh* Mesh = Info->GetMesh();
if (!Mesh)
continue;
if (Mesh->mVertexDataSize > 16 * 1024 * 1024 || Mesh->mIndexDataSize > 16 * 1024 * 1024)
continue;
VertexDataSize += Mesh->mVertexDataSize;
IndexDataSize += Mesh->mIndexDataSize;
2020-01-24 22:12:34 +01:00
Meshes.push_back(Mesh);
}
Context->DestroyVertexBuffer(mVertexBuffer);
Context->DestroyIndexBuffer(mIndexBuffer);
if (!VertexDataSize || !IndexDataSize)
return;
void* VertexData = malloc(VertexDataSize);
void* IndexData = malloc(IndexDataSize);
VertexDataSize = 0;
IndexDataSize = 0;
2020-01-24 22:12:34 +01:00
for (lcMesh* Mesh : Meshes)
{
Mesh->mVertexCacheOffset = VertexDataSize;
Mesh->mIndexCacheOffset = IndexDataSize;
memcpy((char*)VertexData + VertexDataSize, Mesh->mVertexData, Mesh->mVertexDataSize);
memcpy((char*)IndexData + IndexDataSize, Mesh->mIndexData, Mesh->mIndexDataSize);
VertexDataSize += Mesh->mVertexDataSize;
IndexDataSize += Mesh->mIndexDataSize;
}
mVertexBuffer = Context->CreateVertexBuffer(VertexDataSize, VertexData);
mIndexBuffer = Context->CreateIndexBuffer(IndexDataSize, IndexData);
mBuffersDirty = false;
free(VertexData);
free(IndexData);
}
void lcPiecesLibrary::UnloadUnusedParts()
{
2017-01-23 04:28:05 +01:00
QMutexLocker LoadLock(&mLoadMutex);
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
2017-01-23 04:28:05 +01:00
{
2017-07-24 04:35:18 +02:00
PieceInfo* Info = PieceIt.second;
2021-11-15 03:34:24 +01:00
if (Info->GetRefCount() == 0 && Info->mState != lcPieceInfoState::Unloaded)
2017-01-23 04:28:05 +01:00
ReleasePieceInfo(Info);
}
}
2013-08-09 06:57:18 +02:00
bool lcPiecesLibrary::LoadTexture(lcTexture* Texture)
{
QMutexLocker Lock(&mTextureMutex);
2019-06-03 22:23:04 +02:00
char FileName[2*LC_MAXPATH];
2013-08-09 06:57:18 +02:00
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(lcZipFileType::Official)])
2013-08-09 06:57:18 +02:00
{
lcMemFile TextureFile;
sprintf(FileName, "ldraw/parts/textures/%s.png", Texture->mName);
2013-08-09 06:57:18 +02:00
if (!mZipFiles[static_cast<int>(lcZipFileType::Official)]->ExtractFile(FileName, TextureFile))
{
sprintf(FileName, "parts/textures/%s.png", Texture->mName);
if (!mZipFiles[static_cast<int>(lcZipFileType::Unofficial)] || !mZipFiles[static_cast<int>(lcZipFileType::Unofficial)]->ExtractFile(FileName, TextureFile))
return false;
}
2013-08-09 06:57:18 +02:00
2018-02-17 20:32:29 +01:00
return Texture->Load(TextureFile);
2013-08-09 06:57:18 +02:00
}
else
return Texture->Load(Texture->mFileName);
2013-08-09 06:57:18 +02:00
}
void lcPiecesLibrary::ReleaseTexture(lcTexture* Texture)
{
QMutexLocker LoadLock(&mLoadMutex);
if (Texture->Release() == 0 && Texture->IsTemporary())
{
2019-07-05 02:06:26 +02:00
std::vector<lcTexture*>::iterator TextureIt = std::find(mTextures.begin(), mTextures.end(), Texture);
if (TextureIt != mTextures.end())
mTextures.erase(TextureIt);
delete Texture;
}
}
2021-01-20 13:19:29 +01:00
bool lcPiecesLibrary::SupportsStudStyle() const
{
return true;
}
void lcPiecesLibrary::SetStudStyle(lcStudStyle StudStyle, bool Reload, bool StudCylinderColorEnabled)
{
if (mStudStyle == StudStyle && mStudCylinderColorEnabled == StudCylinderColorEnabled)
return;
2021-01-20 13:19:29 +01:00
mStudStyle = StudStyle;
2019-09-24 01:55:24 +02:00
mStudCylinderColorEnabled = StudCylinderColorEnabled;
2021-01-22 22:40:16 +01:00
LoadColors();
2021-01-20 13:19:29 +01:00
UpdateStudStyleSource();
2019-09-24 01:55:24 +02:00
mLoadMutex.lock();
for (const std::unique_ptr<lcLibrarySource>& Source : mSources)
2019-09-24 01:55:24 +02:00
{
for (const auto& PrimitiveIt : Source->Primitives)
{
lcLibraryPrimitive* Primitive = PrimitiveIt.second;
if (Primitive->mStudStyle || Primitive->mMeshData.mHasStyleStud)
Primitive->Unload();
}
2019-09-24 01:55:24 +02:00
}
mLoadMutex.unlock();
if (Reload)
{
2019-09-24 01:55:24 +02:00
mLoadMutex.lock();
for (const auto& PieceIt : mPieces)
{
PieceInfo* Info = PieceIt.second;
2021-11-15 03:34:24 +01:00
if (Info->mState == lcPieceInfoState::Loaded && Info->GetMesh() && Info->GetMesh()->mFlags & lcMeshFlag::HasStyleStud)
2019-09-24 01:55:24 +02:00
{
Info->Unload();
mLoadQueue.append(Info);
mLoadFutures.append(QtConcurrent::run([this]() { LoadQueuedPiece(); }));
}
}
2019-09-24 01:55:24 +02:00
mLoadMutex.unlock();
WaitForLoadQueue();
}
}
bool lcPiecesLibrary::IsPrimitive(const char* Name) const
{
for (const std::unique_ptr<lcLibrarySource>& Source : mSources)
if (Source->Primitives.find(Name) != Source->Primitives.end())
return true;
return false;
}
2020-12-15 00:08:43 +01:00
lcLibraryPrimitive* lcPiecesLibrary::FindPrimitive(const char* Name) const
{
for (const std::unique_ptr<lcLibrarySource>& Source : mSources)
{
const auto PrimitiveIt = Source->Primitives.find(Name);
if (PrimitiveIt != Source->Primitives.end())
return PrimitiveIt->second;
}
2020-12-15 00:08:43 +01:00
return nullptr;
}
2017-07-24 01:19:09 +02:00
bool lcPiecesLibrary::LoadPrimitive(lcLibraryPrimitive* Primitive)
2013-08-09 06:57:18 +02:00
{
mLoadMutex.lock();
2021-01-18 22:38:17 +01:00
if (Primitive->mState == lcPrimitiveState::NotLoaded)
Primitive->mState = lcPrimitiveState::Loading;
else
{
mLoadMutex.unlock();
2021-01-18 22:38:17 +01:00
while (Primitive->mState == lcPrimitiveState::Loading)
lcSleeper::msleep(5);
2021-01-18 22:38:17 +01:00
return Primitive->mState == lcPrimitiveState::Loaded;
}
mLoadMutex.unlock();
2017-01-23 04:28:05 +01:00
lcMeshLoader MeshLoader(Primitive->mMeshData, true, nullptr, false);
2013-08-09 06:57:18 +02:00
2021-01-18 21:09:33 +01:00
if (mZipFiles[static_cast<int>(lcZipFileType::Official)])
2013-08-09 06:57:18 +02:00
{
2017-07-24 01:19:09 +02:00
lcLibraryPrimitive* LowPrimitive = nullptr;
2015-05-24 06:36:25 +02:00
lcMemFile PrimFile;
if (Primitive->mStud && !Primitive->mStudStyle)
2015-05-24 06:36:25 +02:00
{
if (strncmp(Primitive->mName, "8/", 2)) // todo: this is currently the only place that uses mName so use mFileName instead. this should also be done for the loose file libraries.
{
char Name[LC_PIECE_NAME_LEN];
strcpy(Name, "8/");
strcat(Name, Primitive->mName);
strupr(Name);
LowPrimitive = FindPrimitive(Name); // todo: low primitives don't work with studlogo, because the low stud gets added as shared
}
}
2013-08-09 06:57:18 +02:00
if (!mZipFiles[static_cast<int>(Primitive->mZipFileType)]->ExtractFile(Primitive->mZipFileIndex, PrimFile))
2013-08-09 06:57:18 +02:00
return false;
2017-07-24 01:19:09 +02:00
if (!LowPrimitive)
2015-05-24 06:36:25 +02:00
{
if (!MeshLoader.LoadMesh(PrimFile, LC_MESHDATA_SHARED))
2015-05-24 06:36:25 +02:00
return false;
}
else
{
if (!MeshLoader.LoadMesh(PrimFile, LC_MESHDATA_HIGH))
2015-05-24 06:36:25 +02:00
return false;
2021-01-18 21:09:33 +01:00
if (!mZipFiles[static_cast<int>(LowPrimitive->mZipFileType)]->ExtractFile(LowPrimitive->mZipFileIndex, PrimFile))
2015-05-24 06:36:25 +02:00
return false;
if (!MeshLoader.LoadMesh(PrimFile, LC_MESHDATA_LOW))
2015-05-24 06:36:25 +02:00
return false;
}
2013-08-09 06:57:18 +02:00
}
else
{
if (Primitive->mZipFileType == lcZipFileType::Count)
{
lcDiskFile PrimFile(Primitive->mFileName);
if (!PrimFile.Open(QIODevice::ReadOnly) || !MeshLoader.LoadMesh(PrimFile, LC_MESHDATA_SHARED)) // todo: LOD like the zip files
return false;
}
else
{
lcMemFile PrimFile;
2021-01-18 21:09:33 +01:00
if (!mZipFiles[static_cast<int>(Primitive->mZipFileType)]->ExtractFile(Primitive->mZipFileIndex, PrimFile))
return false;
2021-01-18 21:09:33 +01:00
if (!MeshLoader.LoadMesh(PrimFile, LC_MESHDATA_SHARED))
return false;
}
2013-08-09 06:57:18 +02:00
}
mLoadMutex.lock();
2021-01-18 22:38:17 +01:00
Primitive->mState = lcPrimitiveState::Loaded;
mLoadMutex.unlock();
2013-08-09 06:57:18 +02:00
return true;
}
2017-02-08 03:55:54 +01:00
bool lcPiecesLibrary::PieceInCategory(PieceInfo* Info, const char* CategoryKeywords) const
2013-08-09 06:57:18 +02:00
{
2015-02-22 03:39:15 +01:00
if (Info->IsTemporary())
return false;
2017-02-08 03:55:54 +01:00
const char* PieceName;
2013-08-09 06:57:18 +02:00
if (Info->m_strDescription[0] == '~' || Info->m_strDescription[0] == '_')
PieceName = Info->m_strDescription + 1;
else
PieceName = Info->m_strDescription;
2017-02-08 03:55:54 +01:00
return lcMatchCategory(PieceName, CategoryKeywords);
2013-08-09 06:57:18 +02:00
}
2024-05-25 04:26:01 +02:00
void lcPiecesLibrary::GetCategoryEntries(int CategoryIndex, bool GroupPieces, std::vector<PieceInfo*>& SinglePieces, std::vector<PieceInfo*>& GroupedPieces)
2013-08-09 06:57:18 +02:00
{
2020-01-11 02:40:14 +01:00
if (CategoryIndex >= 0 && CategoryIndex < static_cast<int>(gCategories.size()))
2017-02-08 18:41:48 +01:00
GetCategoryEntries(gCategories[CategoryIndex].Keywords.constData(), GroupPieces, SinglePieces, GroupedPieces);
2013-08-09 06:57:18 +02:00
}
2024-05-25 04:26:01 +02:00
void lcPiecesLibrary::GetCategoryEntries(const char* CategoryKeywords, bool GroupPieces, std::vector<PieceInfo*>& SinglePieces, std::vector<PieceInfo*>& GroupedPieces)
2013-08-09 06:57:18 +02:00
{
2024-05-25 04:26:01 +02:00
SinglePieces.clear();
GroupedPieces.clear();
2013-08-09 06:57:18 +02:00
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
2013-08-09 06:57:18 +02:00
{
2017-07-24 04:35:18 +02:00
PieceInfo* Info = PieceIt.second;
2013-08-09 06:57:18 +02:00
if (!PieceInCategory(Info, CategoryKeywords))
continue;
if (!GroupPieces)
{
2024-05-25 04:26:01 +02:00
SinglePieces.emplace_back(Info);
2013-08-09 06:57:18 +02:00
continue;
}
// Check if it's a patterned piece.
if (Info->IsPatterned())
{
PieceInfo* Parent;
// Find the parent of this patterned piece.
char ParentName[LC_PIECE_NAME_LEN];
strcpy(ParentName, Info->mFileName);
2013-08-09 06:57:18 +02:00
*strchr(ParentName, 'P') = '\0';
strcat(ParentName, ".dat");
2013-08-09 06:57:18 +02:00
Parent = FindPiece(ParentName, nullptr, false, false);
2013-08-09 06:57:18 +02:00
if (Parent)
{
// Check if the parent was added as a single piece.
2024-05-25 04:26:01 +02:00
auto ParentIt = std::find(SinglePieces.begin(), SinglePieces.end(), Parent);
2013-08-09 06:57:18 +02:00
2024-05-25 04:26:01 +02:00
if (ParentIt != SinglePieces.end())
SinglePieces.erase(ParentIt);
2013-08-09 06:57:18 +02:00
2024-05-25 04:26:01 +02:00
if (std::find(GroupedPieces.begin(), GroupedPieces.end(), Parent) == GroupedPieces.end())
GroupedPieces.emplace_back(Parent);
2013-08-09 06:57:18 +02:00
}
else
{
// Patterned pieces should have a parent but in case they don't just add them anyway.
2024-05-25 04:26:01 +02:00
SinglePieces.emplace_back(Info);
2013-08-09 06:57:18 +02:00
}
}
else
{
// Check if this piece has already been added to this category by one of its children.
2024-05-25 04:26:01 +02:00
if (std::find(GroupedPieces.begin(), GroupedPieces.end(), Info) == GroupedPieces.end())
SinglePieces.emplace_back(Info);
2013-08-09 06:57:18 +02:00
}
}
}
2024-05-25 04:26:01 +02:00
void lcPiecesLibrary::GetParts(std::vector<PieceInfo*>& Parts) const
2017-02-01 06:12:30 +01:00
{
2024-05-25 04:26:01 +02:00
Parts.clear();
Parts.reserve(mPieces.size());
2017-07-24 04:35:18 +02:00
2018-02-22 02:27:24 +01:00
for (const auto& PartIt : mPieces)
2024-05-25 04:26:01 +02:00
Parts.emplace_back(PartIt.second);
2017-02-01 06:12:30 +01:00
}
2019-12-09 01:54:12 +01:00
std::vector<PieceInfo*> lcPiecesLibrary::GetPartsFromSet(const std::vector<std::string>& PartIds) const
2019-12-07 18:52:46 +01:00
{
std::vector<PieceInfo*> Parts;
2019-12-09 01:54:12 +01:00
Parts.reserve(PartIds.size());
2019-12-07 18:52:46 +01:00
2019-12-09 01:54:12 +01:00
for (const std::string& PartId : PartIds)
2019-12-07 18:52:46 +01:00
{
2019-12-09 01:54:12 +01:00
std::map<std::string, PieceInfo*>::const_iterator PartIt = mPieces.find(PartId);
2019-12-07 18:52:46 +01:00
if (PartIt != mPieces.end())
Parts.push_back(PartIt->second);
}
return Parts;
}
2019-12-09 01:54:12 +01:00
std::string lcPiecesLibrary::GetPartId(const PieceInfo* Info) const
2019-12-07 18:52:46 +01:00
{
std::map<std::string, PieceInfo*>::const_iterator PartIt = std::find_if(mPieces.begin(), mPieces.end(), [Info](const std::pair<std::string, PieceInfo*>& PartIt)
{
return PartIt.second == Info;
});
2019-12-09 01:54:12 +01:00
if (PartIt != mPieces.end())
return PartIt->first;
else
return std::string();
2019-12-07 18:52:46 +01:00
}
2014-09-11 21:55:34 +02:00
bool lcPiecesLibrary::LoadBuiltinPieces()
2014-09-10 03:41:37 +02:00
{
2021-01-18 21:09:33 +01:00
std::unique_ptr<lcDiskFile> File(new lcDiskFile(":/resources/library.zip"));
2013-08-09 06:57:18 +02:00
2021-01-26 01:58:44 +01:00
if (!File->Open(QIODevice::ReadOnly) || !OpenArchive(std::move(File), lcZipFileType::Official))
2014-09-11 21:55:34 +02:00
return false;
2014-09-10 03:41:37 +02:00
2014-09-11 21:55:34 +02:00
lcMemFile PieceFile;
2013-08-09 06:57:18 +02:00
2018-02-22 02:27:24 +01:00
for (const auto& PieceIt : mPieces)
2014-09-10 03:41:37 +02:00
{
2017-07-24 04:35:18 +02:00
PieceInfo* Info = PieceIt.second;
2014-09-10 03:41:37 +02:00
2021-01-18 21:09:33 +01:00
mZipFiles[static_cast<int>(Info->mZipFileType)]->ExtractFile(Info->mZipFileIndex, PieceFile, 256);
2014-09-11 21:55:34 +02:00
PieceFile.Seek(0, SEEK_END);
PieceFile.WriteU8(0);
2014-09-10 03:41:37 +02:00
2014-09-11 21:55:34 +02:00
char* Src = (char*)PieceFile.mBuffer + 2;
char* Dst = Info->m_strDescription;
2013-08-09 06:57:18 +02:00
2014-09-11 21:55:34 +02:00
for (;;)
2013-08-09 06:57:18 +02:00
{
2014-09-11 21:55:34 +02:00
if (*Src != '\r' && *Src != '\n' && *Src && Dst - Info->m_strDescription < (int)sizeof(Info->m_strDescription) - 1)
2014-08-30 21:48:36 +02:00
{
2014-09-11 21:55:34 +02:00
*Dst++ = *Src++;
continue;
2013-08-09 06:57:18 +02:00
}
2014-09-11 21:55:34 +02:00
*Dst = 0;
break;
2013-08-09 06:57:18 +02:00
}
}
lcLoadDefaultColors(lcStudStyle::Plain);
2014-09-11 21:55:34 +02:00
lcLoadDefaultCategories(true);
lcSynthInit();
2024-11-03 03:34:22 +01:00
lcTrainTrackInit(this);
2014-09-11 21:55:34 +02:00
return true;
2013-08-09 06:57:18 +02:00
}