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"
2015-04-26 20:14:33 +02:00
# include "lc_context.h"
2015-05-09 21:54:29 +02:00
# include "lc_glextensions.h"
2016-02-29 21:13:54 +01:00
# include "lc_synth.h"
2015-02-22 03:39:15 +01:00
# include "project.h"
2019-06-21 03:52:33 +02:00
# include "lc_profile.h"
2019-07-28 01:31:16 +02:00
# include "lc_meshloader.h"
2013-08-09 06:57:18 +02:00
# include <ctype.h>
# include <locale.h>
2015-07-22 06:00:47 +02:00
# include <zlib.h>
2017-01-23 05:50:43 +01:00
# include <QtConcurrent>
2015-07-22 06:00:47 +02:00
# if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
# else
2023-04-17 18:37:56 +02:00
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
2015-07-22 06:00:47 +02:00
# 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 ) ) ;
2015-07-22 06:00:47 +02:00
QStringList cachePathList = QStandardPaths : : standardLocations ( QStandardPaths : : CacheLocation ) ;
mCachePath = cachePathList . first ( ) ;
QDir Dir ;
Dir . mkpath ( mCachePath ) ;
2013-08-09 06:57:18 +02:00
mNumOfficialPieces = 0 ;
2015-04-26 20:14:33 +02:00
mBuffersDirty = false ;
2017-03-19 21:12:24 +01:00
mHasUnofficial = false ;
2017-11-25 21:57:41 +01:00
mCancelLoading = false ;
2021-01-22 23:16:28 +01:00
mStudStyle = static_cast < lcStudStyle > ( lcGetProfileInt ( LC_PROFILE_STUD_STYLE ) ) ;
2023-04-17 18:37:56 +02:00
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 ( ) ;
2017-11-25 21:57:41 +01:00
mCancelLoading = true ;
2017-01-23 04:28:05 +01:00
WaitForLoadQueue ( ) ;
2013-08-09 06:57:18 +02:00
Unload ( ) ;
2021-03-11 06:21:47 +01:00
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
2021-01-19 00:23:13 +01: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
}
2015-01-07 17:52:42 +01: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 ( ) ; )
2015-01-07 17:52:42 +01:00
{
2017-07-24 04:35:18 +02:00
PieceInfo * Info = PieceIt - > second ;
2015-01-07 17:52:42 +01:00
2017-07-24 04:35:18 +02:00
if ( Info - > IsTemporary ( ) & & Info - > GetRefCount ( ) = = 0 )
2015-01-07 17:52:42 +01:00
{
2017-07-24 04:35:18 +02:00
PieceIt = mPieces . erase ( PieceIt ) ;
2015-01-07 17:52:42 +01:00
delete Info ;
}
2017-07-24 04:35:18 +02:00
else
PieceIt + + ;
2015-01-07 17:52:42 +01:00
}
}
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 )
{
2017-07-27 18:21:55 +02:00
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
}
2016-11-26 02:12:19 +01:00
PieceInfo * lcPiecesLibrary : : FindPiece ( const char * PieceName , Project * CurrentProject , bool CreatePlaceholder , bool SearchProjectFolder )
2013-08-09 06:57:18 +02:00
{
2016-11-26 02:12:19 +01:00
QString ProjectPath ;
if ( SearchProjectFolder )
{
QString FileName = CurrentProject - > GetFileName ( ) ;
if ( ! FileName . isEmpty ( ) )
ProjectPath = QFileInfo ( FileName ) . absolutePath ( ) ;
}
2017-07-27 18:21:55 +02:00
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
2024-06-17 02:43:02 +02:00
if ( lcGetActiveProject ( ) )
{
const std : : vector < std : : unique_ptr < lcModel > > & Models = lcGetActiveProject ( ) - > GetModels ( ) ;
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
2016-11-26 02:12:19 +01:00
if ( ! ProjectPath . isEmpty ( ) )
{
QFileInfo ProjectFile = QFileInfo ( ProjectPath + QDir : : separator ( ) + PieceName ) ;
if ( ProjectFile . isFile ( ) )
{
Project * NewProject = new Project ( ) ;
2021-04-17 20:49:41 +02:00
if ( NewProject - > Load ( ProjectFile . absoluteFilePath ( ) , false ) )
2016-11-26 02:12:19 +01:00
{
PieceInfo * Info = new PieceInfo ( ) ;
2017-07-27 18:21:55 +02:00
Info - > CreateProject ( NewProject , PieceName ) ;
mPieces [ CleanName ] = Info ;
2016-11-26 02:12:19 +01:00
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 ) ;
2017-07-27 18:21:55 +02:00
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
2017-04-14 02:26:40 +02:00
return nullptr ;
2013-08-09 06:57:18 +02:00
}
2017-07-02 02:12:09 +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
2017-07-02 02:12:09 +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 ) ;
2017-07-02 02:12:09 +02:00
return Texture ;
}
}
}
2017-04-14 02:26:40 +02:00
return nullptr ;
2013-08-09 06:57:18 +02:00
}
2017-11-25 05:00:16 +01:00
bool lcPiecesLibrary : : Load ( const QString & LibraryPath , bool ShowProgress )
2013-08-09 06:57:18 +02:00
{
2014-05-08 00:58:59 +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
2017-05-29 22:31:24 +02:00
mLibraryDir = QFileInfo ( LibraryPath ) . absoluteDir ( ) ;
QString UnofficialFileName = mLibraryDir . absoluteFilePath ( QLatin1String ( " ldrawunf.zip " ) ) ;
2014-05-08 00:58:59 +02:00
2021-01-18 21:09:33 +01:00
if ( ! OpenArchive ( UnofficialFileName , lcZipFileType : : Unofficial ) )
2017-05-29 22:31:24 +02:00
UnofficialFileName . clear ( ) ;
2016-12-22 01:49:45 +01:00
2015-07-22 06:00:47 +02:00
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
2017-11-25 05:00:16 +01: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 ( ) ;
2016-02-29 21:13:54 +01:00
lcSynthInit ( ) ;
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
}
2021-01-23 20:43:57 +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-19 00:23:13 +01:00
{
2021-01-20 13:19:29 +01:00
if ( ! mSources . empty ( ) & & mSources . front ( ) - > Type = = lcLibrarySourceType : : StudStyle )
2021-01-19 00:23:13 +01:00
mSources . erase ( mSources . begin ( ) ) ;
2021-01-20 13:19:29 +01:00
mZipFiles [ static_cast < int > ( lcZipFileType : : StudStyle ) ] . reset ( ) ;
2021-01-19 00:23:13 +01:00
2023-04-17 18:37:56 +02:00
if ( mStudStyle = = lcStudStyle : : Plain | | ( mStudStyle > = lcStudStyle : : HighContrast & & ! mStudCylinderColorEnabled ) )
2021-01-19 00:23:13 +01:00
return ;
2021-01-22 23:16:28 +01:00
const QLatin1String FileNames [ ] =
{
2023-04-17 18:37:56 +02:00
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
2021-01-22 23:16:28 +01:00
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-19 00:23:13 +01:00
2021-01-20 13:19:29 +01:00
if ( StudStyleFile - > Open ( QIODevice : : ReadOnly ) )
OpenArchive ( std : : move ( StudStyleFile ) , lcZipFileType : : StudStyle ) ;
2021-01-19 00:23:13 +01:00
}
2017-05-29 22:31:24 +02:00
bool lcPiecesLibrary : : OpenArchive ( const QString & FileName , lcZipFileType ZipFileType )
2014-09-11 21:55:34 +02:00
{
2020-12-13 16:40:29 +01:00
std : : unique_ptr < lcDiskFile > File ( new lcDiskFile ( FileName ) ) ;
2014-09-11 21:55:34 +02:00
2020-12-13 16:40:29 +01: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
{
2020-12-13 17:23:05 +01:00
std : : unique_ptr < lcZipFile > ZipFile ( new lcZipFile ( ) ) ;
2013-08-09 06:57:18 +02:00
2020-12-13 16:40:29 +01:00
if ( ! ZipFile - > OpenRead ( std : : move ( File ) ) )
2013-08-09 06:57:18 +02:00
return false ;
2014-05-08 00:58:59 +02:00
2021-01-19 00:23:13 +01:00
std : : unique_ptr < lcLibrarySource > Source ( new lcLibrarySource ) ;
2021-01-20 13:19:29 +01:00
Source - > Type = ZipFileType ! = lcZipFileType : : StudStyle ? lcLibrarySourceType : : Library : lcLibrarySourceType : : StudStyle ;
2021-01-19 00:23:13 +01:00
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
{
2014-05-08 00:58:59 +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 ;
2017-07-27 18:21:55 +02:00
* Dst = 0 ;
2013-08-09 06:57:18 +02:00
Dst - = 4 ;
if ( memcmp ( Dst , " .DAT " , 4 ) )
{
2017-12-22 13:38:47 +01:00
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 ) ) )
2017-12-22 13:38:47 +01:00
{
lcTexture * Texture = new lcTexture ( ) ;
2019-07-05 02:06:26 +02:00
mTextures . push_back ( Texture ) ;
2013-08-09 06:57:18 +02:00
2017-12-22 13:38:47 +01:00
* Dst = 0 ;
2021-01-18 21:09:33 +01:00
strncpy ( Texture - > mName , Name + ( ZipFileType = = lcZipFileType : : Official ? 21 : 15 ) , sizeof ( Texture - > mName ) - 1 ) ;
2017-12-22 13:38:47 +01:00
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 )
2014-05-08 00:58:59 +02:00
{
if ( memcmp ( Name , " LDRAW/ " , 6 ) )
continue ;
Name + = 6 ;
}
2013-08-09 06:57:18 +02:00
2014-05-08 00:58:59 +02:00
if ( ! memcmp ( Name , " PARTS/ " , 6 ) )
2013-08-09 06:57:18 +02:00
{
2014-05-08 00:58:59 +02:00
Name + = 6 ;
if ( memcmp ( Name , " S/ " , 2 ) )
2013-08-09 06:57:18 +02:00
{
2017-04-14 02:26:40 +02:00
PieceInfo * Info = FindPiece ( Name , nullptr , false , false ) ;
2014-05-08 00:58:59 +02:00
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 ) ;
2017-07-27 18:21:55 +02:00
Info - > mFileName [ sizeof ( Info - > mFileName ) - 1 ] = 0 ;
2017-07-24 04:35:18 +02:00
2017-07-27 18:21:55 +02:00
mPieces [ Name ] = Info ;
2014-05-08 00:58:59 +02:00
}
Info - > SetZipFile ( ZipFileType , FileIdx ) ;
2013-08-09 06:57:18 +02:00
}
else
2021-01-23 20:43:57 +01:00
Source - > Primitives [ Name ] = new lcLibraryPrimitive ( QString ( ) , FileInfo . file_name + ( Name - NameBuffer ) , ZipFileType , FileIdx , false , false , true ) ;
2013-08-09 06:57:18 +02:00
}
2014-05-08 00:58:59 +02:00
else if ( ! memcmp ( Name , " P/ " , 2 ) )
2013-08-09 06:57:18 +02:00
{
2014-05-08 00:58:59 +02:00
Name + = 2 ;
2021-01-23 20:43:57 +01:00
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 ) ;
2021-03-13 19:51:35 +01:00
if ( ZipFileType ! = lcZipFileType : : StudStyle )
mSources . emplace_back ( std : : move ( Source ) ) ;
else
mSources . insert ( mSources . begin ( ) , std : : move ( Source ) ) ;
2020-12-13 17:23:05 +01:00
2014-05-08 00:58:59 +02:00
return true ;
}
2015-07-22 06:00:47 +02:00
void lcPiecesLibrary : : ReadArchiveDescriptions ( const QString & OfficialFileName , const QString & UnofficialFileName )
2014-05-08 00:58:59 +02:00
{
2015-07-22 06:00:47 +02:00
QFileInfo OfficialInfo ( OfficialFileName ) ;
2016-02-19 18:53:54 +01:00
QFileInfo UnofficialInfo ( UnofficialFileName ) ;
2016-12-22 01:49:45 +01:00
2015-07-22 06:00:47 +02:00
mArchiveCheckSum [ 0 ] = OfficialInfo . size ( ) ;
mArchiveCheckSum [ 1 ] = OfficialInfo . lastModified ( ) . toMSecsSinceEpoch ( ) ;
2021-01-08 19:35:52 +01:00
2016-12-22 01:49:45 +01:00
if ( ! UnofficialFileName . isEmpty ( ) )
{
mArchiveCheckSum [ 2 ] = UnofficialInfo . size ( ) ;
mArchiveCheckSum [ 3 ] = UnofficialInfo . lastModified ( ) . toMSecsSinceEpoch ( ) ;
}
else
{
mArchiveCheckSum [ 2 ] = 0 ;
mArchiveCheckSum [ 3 ] = 0 ;
}
2015-07-22 06:00:47 +02:00
QString IndexFileName = QFileInfo ( QDir ( mCachePath ) , QLatin1String ( " index " ) ) . absoluteFilePath ( ) ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +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 ;
}
}
2015-07-22 06:00:47 +02:00
2017-11-27 04:21:54 +01:00
SaveArchiveCacheIndex ( IndexFileName ) ;
2013-08-09 06:57:18 +02:00
}
}
2017-11-25 05:00:16 +01:00
bool lcPiecesLibrary : : OpenDirectory ( const QDir & LibraryDir , bool ShowProgress )
2013-08-09 06:57:18 +02:00
{
2021-03-13 19:51:35 +01:00
const QLatin1String BaseFolders [ ] = { QLatin1String ( " " ) , QLatin1String ( " unofficial/ " ) } ;
2020-03-23 04:18:52 +01:00
constexpr int NumBaseFolders = LC_ARRAY_COUNT ( BaseFolders ) ;
2017-03-19 21:12:24 +01:00
2017-11-27 04:21:54 +01:00
QFileInfoList FileLists [ NumBaseFolders ] ;
2013-08-09 06:57:18 +02:00
2017-11-27 04:21:54 +01: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
}
2021-03-13 19:51:35 +01:00
if ( FileLists [ static_cast < int > ( lcLibraryFolderType : : Official ) ] . isEmpty ( ) )
2013-08-09 06:57:18 +02:00
return false ;
2021-03-13 19:51:35 +01:00
mHasUnofficial = ! FileLists [ static_cast < int > ( lcLibraryFolderType : : Unofficial ) ] . isEmpty ( ) ;
2017-11-27 04:21:54 +01:00
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
{
2021-01-19 00:23:13 +01:00
std : : unique_ptr < lcLibrarySource > Source ( new lcLibrarySource ) ;
Source - > Type = lcLibrarySourceType : : Library ;
2019-06-02 04:54:09 +02:00
const char * PrimitiveDirectories [ ] = { " p/ " , " parts/s/ " } ;
2017-03-19 21:12:24 +01:00
bool SubFileDirectories [ ] = { false , false , true } ;
2017-05-29 22:31:24 +02:00
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
{
2019-06-02 04:54:09 +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
2019-06-02 04:54:09 +02:00
while ( DirIterator . hasNext ( ) )
2013-08-09 06:57:18 +02:00
{
2017-03-19 21:12:24 +01:00
char Name [ LC_PIECE_NAME_LEN ] ;
2019-06-02 04:54:09 +02:00
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
2017-03-19 21:12:24 +01: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
2017-03-19 21:12:24 +01:00
Src + + ;
Dst + + ;
}
2017-07-28 01:09:38 +02:00
* Dst = 0 ;
2013-08-09 06:57:18 +02:00
2017-03-19 21:12:24 +01:00
if ( Dst - Name < = 4 )
continue ;
Dst - = 4 ;
if ( memcmp ( Dst , " .DAT " , 4 ) )
continue ;
2021-03-13 19:51:35 +01:00
if ( BaseFolderIdx > 0 & & IsPrimitive ( Name ) )
2017-03-19 21:12:24 +01:00
continue ;
2021-03-13 19:51:35 +01:00
if ( BaseFolderIdx = = static_cast < int > ( lcLibraryFolderType : : Unofficial ) )
2017-03-19 21:12:24 +01:00
mHasUnofficial = true ;
2020-03-23 04:18:52 +01:00
const bool SubFile = SubFileDirectories [ DirectoryIdx ] ;
2021-01-23 20:43:57 +01:00
Source - > Primitives [ Name ] = new lcLibraryPrimitive ( std : : move ( FileName ) , strchr ( FileString , ' / ' ) + 1 , lcZipFileType : : Count , 0 , ! SubFile & & IsStudPrimitive ( Name ) , IsStudStylePrimitive ( Name ) , SubFile ) ;
2017-03-19 21:12:24 +01:00
}
2013-08-09 06:57:18 +02:00
}
2023-04-17 18:37:56 +02:00
2021-03-13 19:51:35 +01: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
{
2019-10-26 20:41:49 +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
2019-10-26 20:41:49 +02:00
for ( int FileIdx = 0 ; FileIdx < FileList . size ( ) ; FileIdx + + )
2013-08-09 06:57:18 +02:00
{
2019-10-26 20:41:49 +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
2019-10-26 20:41:49 +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
2019-10-26 20:41:49 +02:00
Src + + ;
Dst + + ;
}
2013-08-09 06:57:18 +02:00
2019-10-26 20:41:49 +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
2019-10-26 20:41:49 +02:00
lcTexture * Texture = new lcTexture ( ) ;
mTextures . push_back ( Texture ) ;
2013-08-09 06:57:18 +02:00
2019-10-26 20:41:49 +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 ;
}
2021-03-13 19:51:35 +01:00
void lcPiecesLibrary : : ReadDirectoryDescriptions ( const QFileInfoList ( & FileLists ) [ static_cast < int > ( lcLibraryFolderType : : Count ) ] , bool ShowProgress )
2017-11-27 04:21:54 +01:00
{
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 ) ;
2019-09-21 18:47:33 +02:00
const char * Description = ( const char * ) IndexFile . mBuffer + IndexFile . GetPosition ( ) ;
2017-11-27 04:21:54 +01:00
IndexFile . Seek ( strlen ( Description ) + 1 , SEEK_CUR ) ;
IndexFile . Seek ( 4 + 1 + 8 , SEEK_CUR ) ;
}
}
}
2021-03-13 19:51:35 +01:00
for ( int FolderIdx = 0 ; FolderIdx < static_cast < int > ( lcLibraryFolderType : : Count ) ; FolderIdx + + )
2017-11-27 04:21:54 +01:00
{
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 ;
2021-03-13 19:51:35 +01:00
if ( FolderIdx > 0 & & mPieces . find ( Name ) ! = mPieces . end ( ) )
2017-11-27 04:21:54 +01:00
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 ) ;
2018-02-01 21:48:31 +01:00
quint64 FileTime = FileLists [ Info - > mFolderType ] [ Info - > mFolderIndex ] . lastModified ( ) . toMSecsSinceEpoch ( ) ;
if ( FileTime = = CachedFileTime )
{
strcpy ( Info - > m_strDescription , Description ) ;
return ;
}
2017-11-27 04:21:54 +01:00
}
}
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 ( ) ) ;
2017-11-27 04:21:54 +01:00
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 ;
2017-11-27 04:21:54 +01:00
2019-05-18 20:33:27 +02:00
NewIndexFile . WriteQString ( mLibraryDir . absolutePath ( ) ) ;
2017-11-27 04:21:54 +01:00
2019-05-18 20:33:27 +02:00
NewIndexFile . WriteU32 ( ( quint32 ) mPieces . size ( ) ) ;
2017-11-27 04:21:54 +01:00
std : : vector < PieceInfo * > SortedPieces ;
SortedPieces . reserve ( mPieces . size ( ) ) ;
2018-02-22 02:27:24 +01:00
for ( const auto & PieceIt : mPieces )
2017-11-27 04:21:54 +01:00
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 )
2017-11-27 04:21:54 +01:00
return ;
2019-05-18 20:33:27 +02:00
if ( NewIndexFile . WriteBuffer ( Info - > m_strDescription , strlen ( Info - > m_strDescription ) + 1 ) = = 0 )
2017-11-27 04:21:54 +01:00
return ;
2021-07-06 02:00:41 +02:00
NewIndexFile . WriteU8 ( static_cast < quint8 > ( Info - > mFolderType ) ) ;
2017-11-27 04:21:54 +01:00
2017-12-02 21:22:04 +01:00
quint64 FileTime = FileLists [ Info - > mFolderType ] [ Info - > mFolderIndex ] . lastModified ( ) . toMSecsSinceEpoch ( ) ;
2018-02-01 21:48:31 +01:00
2019-05-18 20:33:27 +02:00
NewIndexFile . WriteU64 ( FileTime ) ;
2017-11-27 04:21:54 +01:00
}
2019-05-18 20:33:27 +02:00
WriteDirectoryCacheFile ( IndexFileName , NewIndexFile ) ;
2017-11-27 04:21:54 +01:00
}
}
bool lcPiecesLibrary : : ReadArchiveCacheFile ( const QString & FileName , lcMemFile & CacheFile )
2013-08-09 06:57:18 +02:00
{
2015-07-22 06:00:47 +02:00
QFile File ( FileName ) ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
if ( ! File . open ( QIODevice : : ReadOnly ) )
2013-08-09 06:57:18 +02:00
return false ;
2015-07-22 06:00:47 +02:00
quint32 CacheVersion , CacheFlags ;
2019-09-21 18:47:33 +02:00
2015-07-22 06:00:47 +02:00
if ( File . read ( ( char * ) & CacheVersion , sizeof ( CacheVersion ) ) = = - 1 | | CacheVersion ! = LC_LIBRARY_CACHE_VERSION )
2013-08-09 06:57:18 +02:00
return false ;
2015-07-22 06:00:47 +02:00
if ( File . read ( ( char * ) & CacheFlags , sizeof ( CacheFlags ) ) = = - 1 | | CacheFlags ! = LC_LIBRARY_CACHE_ARCHIVE )
return false ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
qint64 CacheCheckSum [ 4 ] ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +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 ;
2015-07-22 06:00:47 +02:00
quint32 UncompressedSize ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
if ( File . read ( ( char * ) & UncompressedSize , sizeof ( UncompressedSize ) ) = = - 1 )
return false ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
QByteArray CompressedData = File . readAll ( ) ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +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
2015-07-22 06:00:47 +02:00
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
2015-07-22 06:00:47 +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
2015-07-22 06:00:47 +02:00
ret = inflateInit2 ( & strm , - MAX_WBITS ) ;
if ( ret ! = Z_OK )
return ret ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
do
2013-08-09 06:57:18 +02:00
{
2015-07-22 06:00:47 +02:00
strm . avail_in = lcMin ( CompressedData . size ( ) - pos , CHUNK ) ;
strm . next_in = in ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
if ( strm . avail_in = = 0 )
break ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
memcpy ( in , CompressedData . constData ( ) + pos , strm . avail_in ) ;
pos + = strm . avail_in ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +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
2015-07-22 06:00:47 +02:00
switch ( ret )
{
case Z_NEED_DICT :
ret = Z_DATA_ERROR ;
2018-09-24 20:29:05 +02:00
Q_FALLTHROUGH ( ) ;
2015-07-22 06:00:47 +02:00
case Z_DATA_ERROR :
2018-09-24 20:29:05 +02:00
Q_FALLTHROUGH ( ) ;
2015-07-22 06:00:47 +02:00
case Z_MEM_ERROR :
( void ) inflateEnd ( & strm ) ;
return ret ;
}
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
have = CHUNK - strm . avail_out ;
CacheFile . WriteBuffer ( out , have ) ;
} while ( strm . avail_out = = 0 ) ;
} while ( ret ! = Z_STREAM_END ) ;
( void ) inflateEnd ( & strm ) ;
2019-09-21 18:47:33 +02:00
2015-07-22 06:00:47 +02:00
CacheFile . Seek ( 0 , SEEK_SET ) ;
return ret = = Z_STREAM_END ;
2013-08-09 06:57:18 +02:00
}
2017-11-27 04:21:54 +01:00
bool lcPiecesLibrary : : WriteArchiveCacheFile ( const QString & FileName , lcMemFile & CacheFile )
2013-08-09 06:57:18 +02:00
{
2015-07-22 06:00:47 +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 ;
2019-09-21 18:47:33 +02:00
2015-07-22 06:00:47 +02:00
if ( File . write ( ( char * ) & CacheVersion , sizeof ( CacheVersion ) ) = = - 1 )
return false ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
if ( File . write ( ( char * ) & CacheFlags , sizeof ( CacheFlags ) ) = = - 1 )
return false ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +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
2015-07-22 06:00:47 +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 ;
2015-07-22 06:00:47 +02:00
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
2015-07-22 06:00:47 +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
2015-07-22 06:00:47 +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
2015-07-22 06:00:47 +02:00
Bytef * BufferIn = CacheFile . mBuffer ;
int FlushMode ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
do
{
2016-02-19 18:53:54 +01:00
uInt Read = ( uInt ) lcMin ( CacheFile . GetLength ( ) - ( BufferIn - CacheFile . mBuffer ) , BufferSize ) ;
2015-07-22 06:00:47 +02:00
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 ) ;
2019-09-21 18:47:33 +02:00
deflateEnd ( & Stream ) ;
2015-07-22 06:00:47 +02:00
return true ;
2013-08-09 06:57:18 +02:00
}
2017-11-27 04:21:54 +01: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 ;
2017-11-27 04:21:54 +01:00
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 ;
2017-11-27 04:21:54 +01:00
if ( File . write ( ( char * ) & CacheFlags , sizeof ( CacheFlags ) ) = = - 1 )
return false ;
2020-03-23 04:18:52 +01:00
const quint32 UncompressedSize = ( quint32 ) CacheFile . GetLength ( ) ;
2017-11-27 04:21:54 +01:00
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 ( ) ) ) ;
2017-11-27 04:21:54 +01:00
return true ;
}
2015-07-22 06:00:47 +02:00
bool lcPiecesLibrary : : LoadCacheIndex ( const QString & FileName )
2013-08-09 06:57:18 +02:00
{
2015-07-22 06:00:47 +02:00
lcMemFile IndexFile ;
2013-08-09 06:57:18 +02:00
2017-11-27 04:21:54 +01:00
if ( ! ReadArchiveCacheFile ( FileName , IndexFile ) )
2015-07-22 06:00:47 +02:00
return false ;
2017-07-27 21:40:52 +02:00
quint32 NumFiles ;
2015-07-22 06:00:47 +02:00
2017-07-24 04:35:18 +02:00
if ( IndexFile . ReadBuffer ( ( char * ) & NumFiles , sizeof ( NumFiles ) ) = = 0 | | NumFiles ! = mPieces . size ( ) )
2015-07-22 06:00:47 +02:00
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 ;
2015-07-22 06:00:47 +02:00
quint8 Length ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +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 )
2015-07-22 06:00:47 +02:00
return false ;
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
Info - > m_strDescription [ Length ] = 0 ;
}
2014-05-08 00:58:59 +02:00
2015-07-22 06:00:47 +02:00
return true ;
}
2014-05-08 00:58:59 +02:00
2017-11-27 04:21:54 +01:00
bool lcPiecesLibrary : : SaveArchiveCacheIndex ( const QString & FileName )
2015-07-22 06:00:47 +02:00
{
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
2015-07-22 06:00:47 +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
2015-07-22 06:00:47 +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 )
2015-07-22 06:00:47 +02:00
return false ;
2013-08-09 06:57:18 +02:00
}
2017-11-27 04:21:54 +01:00
return WriteArchiveCacheFile ( FileName , IndexFile ) ;
2015-07-22 06:00:47 +02:00
}
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
bool lcPiecesLibrary : : LoadCachePiece ( PieceInfo * Info )
{
2017-07-27 18:21:55 +02:00
QString FileName = QFileInfo ( QDir ( mCachePath ) , QString : : fromLatin1 ( Info - > mFileName ) ) . absoluteFilePath ( ) ;
2015-07-22 06:00:47 +02:00
lcMemFile MeshData ;
2013-08-09 06:57:18 +02:00
2017-11-27 04:21:54 +01:00
if ( ! ReadArchiveCacheFile ( FileName , MeshData ) )
2015-07-22 06:00:47 +02:00
return false ;
2013-08-09 06:57:18 +02:00
2019-09-27 23:52:50 +02:00
qint32 Flags ;
2019-09-24 02:06:16 +02:00
if ( MeshData . ReadBuffer ( ( char * ) & Flags , sizeof ( Flags ) ) = = 0 )
return false ;
2023-04-17 18:37:56 +02:00
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 ;
}
2015-07-22 06:00:47 +02:00
}
2013-08-09 06:57:18 +02:00
2015-07-22 06:00:47 +02:00
bool lcPiecesLibrary : : SaveCachePiece ( PieceInfo * Info )
{
lcMemFile MeshData ;
2013-08-09 06:57:18 +02:00
2023-04-17 18:37:56 +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 ;
2015-07-22 06:00:47 +02:00
if ( ! Info - > GetMesh ( ) - > FileSave ( MeshData ) )
return false ;
2013-08-09 06:57:18 +02:00
2017-07-27 18:21:55 +02:00
QString FileName = QFileInfo ( QDir ( mCachePath ) , QString : : fromLatin1 ( Info - > mFileName ) ) . absoluteFilePath ( ) ;
2013-08-09 06:57:18 +02:00
2017-11-27 04:21:54 +01: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 ) ;
2017-12-22 13:38:47 +01:00
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 ( ) ;
2017-04-14 02:26:40 +02:00
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
2017-04-14 02:26:40 +02: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 ;
2019-07-28 01:31:16 +02:00
lcMeshLoader MeshLoader ( MeshData , true , nullptr , false ) ;
2013-08-09 06:57:18 +02:00
2016-05-28 19:35:13 +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 ) )
2019-07-28 01:31:16 +02:00
Loaded = MeshLoader . LoadMesh ( PieceFile , LC_MESHDATA_SHARED ) ;
2013-08-09 06:57:18 +02:00
2021-01-18 21:09:33 +01:00
SaveCache = Loaded & & ( Info - > mZipFileType = = lcZipFileType : : Official ) ;
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 ) ;
2017-03-19 21:12:24 +01:00
2021-03-14 05:01:25 +01:00
if ( mHasUnofficial & & ! Loaded )
2017-03-19 21:12:24 +01:00
{
2021-03-14 05:01:25 +01:00
sprintf ( FileName , " unofficial/parts/%s " , Info - > mFileName ) ;
2017-05-29 22:31:24 +02:00
PieceFile . SetFileName ( mLibraryDir . absoluteFilePath ( QLatin1String ( FileName ) ) ) ;
if ( PieceFile . Open ( QIODevice : : ReadOnly ) )
2019-07-28 01:31:16 +02:00
Loaded = MeshLoader . LoadMesh ( PieceFile , LC_MESHDATA_SHARED ) ;
2017-03-19 21:12:24 +01:00
}
2016-05-28 19:35:13 +02:00
}
2019-09-21 18:47:33 +02:00
2020-12-31 23:23:08 +01:00
if ( mCancelLoading )
2016-05-28 19:35:13 +02:00
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 ) ;
}
}
2015-02-23 01:50:37 +01:00
2016-05-28 19:35:13 +02:00
if ( SaveCache )
2015-07-22 06:00:47 +02:00
SaveCachePiece ( Info ) ;
2015-02-23 01:50:37 +01:00
2020-12-31 23:23:08 +01:00
return Loaded ;
2015-02-23 01:50:37 +01:00
}
2019-07-28 01:31:16 +02:00
void lcPiecesLibrary : : GetPrimitiveFile ( lcLibraryPrimitive * Primitive , std : : function < void ( lcFile & File ) > Callback )
2015-02-23 01:50:37 +01:00
{
2021-01-18 21:09:33 +01:00
if ( mZipFiles [ static_cast < int > ( lcZipFileType : : Official ) ] )
2013-08-09 06:57:18 +02:00
{
2019-07-28 01:31:16 +02:00
lcMemFile IncludeFile ;
2019-05-28 01:07:20 +02:00
2021-01-18 21:09:33 +01:00
if ( mZipFiles [ static_cast < int > ( Primitive - > mZipFileType ) ] - > ExtractFile ( Primitive - > mZipFileIndex , IncludeFile ) )
2019-07-28 01:31:16 +02:00
Callback ( IncludeFile ) ;
2019-05-28 01:07:20 +02:00
}
else
{
2019-07-28 01:31:16 +02:00
lcDiskFile IncludeFile ( Primitive - > mFileName ) ;
2019-05-28 01:07:20 +02:00
2019-07-28 01:31:16 +02:00
if ( IncludeFile . Open ( QIODevice : : ReadOnly ) )
Callback ( IncludeFile ) ;
2013-08-09 06:57:18 +02:00
}
2019-07-28 01:31:16 +02:00
}
2013-08-09 06:57:18 +02:00
2019-07-28 01:31:16 +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
2019-07-28 01:31:16 +02:00
if ( PieceIt ! = mPieces . end ( ) )
2015-05-24 06:36:25 +02:00
{
2019-07-28 01:31:16 +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
{
2019-07-28 01:31:16 +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 ) )
2019-07-28 01:31:16 +02:00
Callback ( IncludeFile ) ;
2015-05-24 06:36:25 +02:00
}
2019-07-28 01:31:16 +02:00
else
2015-05-24 06:36:25 +02:00
{
2019-07-28 01:31:16 +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 ) ;
2019-07-28 01:31:16 +02:00
IncludeFile . SetFileName ( mLibraryDir . absoluteFilePath ( QLatin1String ( FileName ) ) ) ;
Found = IncludeFile . Open ( QIODevice : : ReadOnly ) ;
2019-05-28 01:07:20 +02:00
}
2013-08-09 06:57:18 +02:00
2019-07-28 01:31:16 +02:00
if ( Found )
Callback ( IncludeFile ) ;
2019-05-28 01:07:20 +02:00
}
}
else
{
2019-07-28 01:31:16 +02:00
bool Found = false ;
2019-05-28 01:07:20 +02:00
2021-01-18 21:09:33 +01:00
if ( mZipFiles [ static_cast < int > ( lcZipFileType : : Official ) ] )
2018-01-21 02:35:21 +01:00
{
2019-07-28 01:31:16 +02:00
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 )
2019-07-28 01:31:16 +02:00
{
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 ) ;
2019-07-28 01:31:16 +02:00
} ;
2019-02-23 02:02:38 +01:00
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile ( " ldraw/parts/%s " , lcZipFileType : : Official ) ;
2013-08-09 06:57:18 +02:00
2019-07-28 01:31:16 +02:00
if ( ! Found )
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile ( " ldraw/p/%s " , lcZipFileType : : Official ) ;
2021-03-27 20:15:38 +01:00
if ( mZipFiles [ static_cast < int > ( lcZipFileType : : Unofficial ) ] & & ! Found )
2019-05-28 01:07:20 +02:00
{
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile ( " parts/%s " , lcZipFileType : : Unofficial ) ;
2019-05-28 01:07:20 +02:00
2019-07-28 01:31:16 +02:00
if ( ! Found )
2021-03-14 05:01:25 +01:00
Found = LoadIncludeFile ( " p/%s " , lcZipFileType : : Unofficial ) ;
2019-05-28 01:07:20 +02:00
}
2013-08-09 06:57:18 +02:00
2019-07-28 01:31:16 +02:00
if ( Found )
Callback ( IncludeFile ) ;
}
else
2015-05-24 06:36:25 +02:00
{
2019-07-28 01:31:16 +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 ) ) ;
2019-07-28 01:31:16 +02:00
if ( IncludeFile . Open ( QIODevice : : ReadOnly ) )
return true ;
2015-05-24 06:36:25 +02:00
2019-07-28 01:31:16 +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 ( ) ) ) ;
2019-07-28 01:31:16 +02:00
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
2019-07-28 01:31:16 +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
2019-07-28 01:31:16 +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
}
2019-07-28 01:31:16 +02:00
if ( Found )
Callback ( IncludeFile ) ;
}
2013-08-09 06:57:18 +02:00
}
}
2021-03-11 06:21:47 +01:00
void lcPiecesLibrary : : ReleaseBuffers ( )
2016-12-28 22:30:31 +01:00
{
2021-03-11 06:21:47 +01:00
lcContext * Context = lcContext : : GetGlobalOffscreenContext ( ) ;
Context - > MakeCurrent ( ) ;
2016-12-28 22:30:31 +01:00
Context - > DestroyVertexBuffer ( mVertexBuffer ) ;
Context - > DestroyIndexBuffer ( mIndexBuffer ) ;
2021-03-11 06:21:47 +01:00
2016-12-28 22:30:31 +01:00
mBuffersDirty = true ;
}
2015-04-26 20:14:33 +02:00
void lcPiecesLibrary : : UpdateBuffers ( lcContext * Context )
{
2015-05-09 21:54:29 +02:00
if ( ! gSupportsVertexBufferObject | | ! mBuffersDirty )
2015-04-26 20:14:33 +02:00
return ;
int VertexDataSize = 0 ;
int IndexDataSize = 0 ;
2020-01-24 22:12:34 +01:00
std : : vector < lcMesh * > Meshes ;
2015-04-26 20:14:33 +02:00
2018-02-22 02:27:24 +01:00
for ( const auto & PieceIt : mPieces )
2015-04-26 20:14:33 +02:00
{
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 ( ) ;
2015-04-26 20:14:33 +02:00
if ( ! Mesh )
continue ;
2016-05-29 04:46:34 +02:00
if ( Mesh - > mVertexDataSize > 16 * 1024 * 1024 | | Mesh - > mIndexDataSize > 16 * 1024 * 1024 )
continue ;
2015-04-26 20:14:33 +02:00
VertexDataSize + = Mesh - > mVertexDataSize ;
IndexDataSize + = Mesh - > mIndexDataSize ;
2020-01-24 22:12:34 +01:00
Meshes . push_back ( Mesh ) ;
2015-04-26 20:14:33 +02:00
}
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 )
2015-04-26 20:14:33 +02:00
{
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 ) ;
}
2015-11-16 03:41:16 +01:00
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 ) ;
}
2015-11-16 03:41:16 +01:00
}
2013-08-09 06:57:18 +02:00
bool lcPiecesLibrary : : LoadTexture ( lcTexture * Texture )
{
2021-03-11 06:21:47 +01:00
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 ;
2021-03-27 22:45:55 +01:00
sprintf ( FileName , " ldraw/parts/textures/%s.png " , Texture - > mName ) ;
2013-08-09 06:57:18 +02:00
2021-03-27 22:45:55 +01:00
if ( ! mZipFiles [ static_cast < int > ( lcZipFileType : : Official ) ] - > ExtractFile ( FileName , TextureFile ) )
2017-12-22 13:38:47 +01:00
{
2021-03-27 22:45:55 +01:00
sprintf ( FileName , " parts/textures/%s.png " , Texture - > mName ) ;
2017-12-22 13:38:47 +01:00
2021-03-27 22:45:55 +01:00
if ( ! mZipFiles [ static_cast < int > ( lcZipFileType : : Unofficial ) ] | | ! mZipFiles [ static_cast < int > ( lcZipFileType : : Unofficial ) ] - > ExtractFile ( FileName , TextureFile ) )
2014-05-08 00:58:59 +02:00
return false ;
2017-12-22 13:38:47 +01:00
}
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
2019-10-26 20:41:49 +02:00
return Texture - > Load ( Texture - > mFileName ) ;
2013-08-09 06:57:18 +02:00
}
2017-07-02 02:12:09 +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 ) ;
2017-07-02 02:12:09 +02:00
delete Texture ;
}
}
2021-01-20 13:19:29 +01:00
bool lcPiecesLibrary : : SupportsStudStyle ( ) const
2020-12-13 00:21:30 +01:00
{
2021-01-19 00:23:13 +01:00
return true ;
2020-12-13 00:21:30 +01:00
}
2023-04-17 18:37:56 +02:00
void lcPiecesLibrary : : SetStudStyle ( lcStudStyle StudStyle , bool Reload , bool StudCylinderColorEnabled )
2019-09-24 01:22:11 +02:00
{
2023-04-17 18:37:56 +02:00
if ( mStudStyle = = StudStyle & & mStudCylinderColorEnabled = = StudCylinderColorEnabled )
2021-01-22 22:59:36 +01:00
return ;
2021-01-20 13:19:29 +01:00
mStudStyle = StudStyle ;
2019-09-24 01:55:24 +02:00
2023-04-17 18:37:56 +02:00
mStudCylinderColorEnabled = StudCylinderColorEnabled ;
2021-01-22 22:40:16 +01:00
LoadColors ( ) ;
2021-01-20 13:19:29 +01:00
UpdateStudStyleSource ( ) ;
2021-01-19 00:23:13 +01:00
2019-09-24 01:55:24 +02:00
mLoadMutex . lock ( ) ;
2021-01-19 00:23:13 +01:00
for ( const std : : unique_ptr < lcLibrarySource > & Source : mSources )
2019-09-24 01:55:24 +02:00
{
2021-01-19 00:23:13 +01:00
for ( const auto & PrimitiveIt : Source - > Primitives )
{
lcLibraryPrimitive * Primitive = PrimitiveIt . second ;
2021-01-23 20:43:57 +01:00
2021-01-23 23:55:00 +01:00
if ( Primitive - > mStudStyle | | Primitive - > mMeshData . mHasStyleStud )
2021-01-19 00:23:13 +01:00
Primitive - > Unload ( ) ;
}
2019-09-24 01:55:24 +02:00
}
mLoadMutex . unlock ( ) ;
if ( Reload )
2019-09-24 01:22:11 +02:00
{
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:22:11 +02:00
}
2019-09-24 01:55:24 +02:00
mLoadMutex . unlock ( ) ;
WaitForLoadQueue ( ) ;
2019-09-24 01:22:11 +02:00
}
}
2021-01-19 00:23:13 +01:00
bool lcPiecesLibrary : : IsPrimitive ( const char * Name ) const
2019-09-21 18:47:33 +02:00
{
2021-01-19 00:23:13 +01:00
for ( const std : : unique_ptr < lcLibrarySource > & Source : mSources )
if ( Source - > Primitives . find ( Name ) ! = Source - > Primitives . end ( ) )
return true ;
2019-09-21 18:47:33 +02:00
2021-01-19 00:23:13 +01:00
return false ;
}
2020-12-15 00:08:43 +01:00
2021-01-19 00:23:13 +01:00
lcLibraryPrimitive * lcPiecesLibrary : : FindPrimitive ( const char * Name ) const
{
for ( const std : : unique_ptr < lcLibrarySource > & Source : mSources )
{
const auto PrimitiveIt = Source - > Primitives . find ( Name ) ;
2019-09-21 18:47:33 +02:00
2021-01-19 00:23:13 +01:00
if ( PrimitiveIt ! = Source - > Primitives . end ( ) )
return PrimitiveIt - > second ;
}
2020-12-15 00:08:43 +01:00
2023-04-17 18:37:56 +02:00
return nullptr ;
2019-09-21 18:47:33 +02:00
}
2017-07-24 01:19:09 +02:00
bool lcPiecesLibrary : : LoadPrimitive ( lcLibraryPrimitive * Primitive )
2013-08-09 06:57:18 +02:00
{
2017-11-25 04:45:27 +01:00
mLoadMutex . lock ( ) ;
2021-01-18 22:38:17 +01:00
if ( Primitive - > mState = = lcPrimitiveState : : NotLoaded )
Primitive - > mState = lcPrimitiveState : : Loading ;
2017-11-25 04:45:27 +01:00
else
{
mLoadMutex . unlock ( ) ;
2021-01-18 22:38:17 +01:00
while ( Primitive - > mState = = lcPrimitiveState : : Loading )
2017-11-25 04:45:27 +01:00
lcSleeper : : msleep ( 5 ) ;
2021-01-18 22:38:17 +01:00
return Primitive - > mState = = lcPrimitiveState : : Loaded ;
2017-11-25 04:45:27 +01:00
}
mLoadMutex . unlock ( ) ;
2017-01-23 04:28:05 +01:00
2019-07-28 01:31:16 +02: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
2019-09-21 18:47:33 +02:00
lcMemFile PrimFile ;
2021-01-23 20:43:57 +01:00
if ( Primitive - > mStud & & ! Primitive - > mStudStyle )
2015-05-24 06:36:25 +02:00
{
2021-01-23 20:43:57 +01: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.
2019-09-29 21:58:18 +02:00
{
char Name [ LC_PIECE_NAME_LEN ] ;
strcpy ( Name , " 8/ " ) ;
strcat ( Name , Primitive - > mName ) ;
strupr ( Name ) ;
2021-01-19 00:23:13 +01:00
LowPrimitive = FindPrimitive ( Name ) ; // todo: low primitives don't work with studlogo, because the low stud gets added as shared
2019-09-21 18:47:33 +02:00
}
}
2013-08-09 06:57:18 +02:00
2021-01-19 00:23:13 +01: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
{
2019-07-28 01:31:16 +02:00
if ( ! MeshLoader . LoadMesh ( PrimFile , LC_MESHDATA_SHARED ) )
2015-05-24 06:36:25 +02:00
return false ;
}
else
{
2019-07-28 01:31:16 +02:00
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 ;
2019-07-28 01:31:16 +02:00
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
{
2021-01-19 00:23:13 +01:00
if ( Primitive - > mZipFileType = = lcZipFileType : : Count )
2019-09-21 18:47:33 +02:00
{
2021-01-19 00:23:13 +01:00
lcDiskFile PrimFile ( Primitive - > mFileName ) ;
2019-09-21 18:47:33 +02:00
2021-01-19 00:23:13 +01:00
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
2021-01-19 00:23:13 +01:00
if ( ! mZipFiles [ static_cast < int > ( Primitive - > mZipFileType ) ] - > ExtractFile ( Primitive - > mZipFileIndex , PrimFile ) )
return false ;
2021-01-18 21:09:33 +01:00
2021-01-19 00:23:13 +01:00
if ( ! MeshLoader . LoadMesh ( PrimFile , LC_MESHDATA_SHARED ) )
return false ;
2019-09-21 18:47:33 +02:00
}
2013-08-09 06:57:18 +02:00
}
2017-11-25 04:45:27 +01:00
mLoadMutex . lock ( ) ;
2021-01-18 22:38:17 +01:00
Primitive - > mState = lcPrimitiveState : : Loaded ;
2017-11-25 04:45:27 +01:00
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 ] ;
2017-07-27 18:21:55 +02:00
strcpy ( ParentName , Info - > mFileName ) ;
2013-08-09 06:57:18 +02:00
* strchr ( ParentName , ' P ' ) = ' \0 ' ;
2017-07-27 18:21:55 +02:00
strcat ( ParentName , " .dat " ) ;
2013-08-09 06:57:18 +02:00
2017-04-14 02:26:40 +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
}
}
2021-01-22 23:16:28 +01:00
lcLoadDefaultColors ( lcStudStyle : : Plain ) ;
2014-09-11 21:55:34 +02:00
lcLoadDefaultCategories ( true ) ;
2016-02-29 21:13:54 +01:00
lcSynthInit ( ) ;
2014-09-11 21:55:34 +02:00
return true ;
2013-08-09 06:57:18 +02:00
}