Files
leocad/common/lc_lxf.cpp
T
Leonardo Zide 664cec006f Protect library directory and shared buffer access
Move the library directory and shared vertex/index buffer handles into the
protected source and rendering groups. Expose one GetBuffers accessor returning
const references to both buffers, and use structured binding in BindMesh while
preserving GL binding and uncached-mesh fallback behavior.

Add GetLibraryFilePath for mapping-file lookups and update the LXF and BrickLink
callers. Keep file opening and bundled-resource fallback in those callers so the
library only resolves the path and exposes no mutable directory state.

Validation: warning-free macOS Qt 6.10 Debug build, all 45 offscreen lifecycle
checks and diff whitespace checks pass. No remaining external field accesses
for the protected directory or buffers. GPU rendering was not tested.
2026-10-19 18:44:13 -07:00

203 lines
7.0 KiB
C++

#include "lc_global.h"
#include "lc_lxf.h"
#include "lc_library.h"
#include "lc_application.h"
#include "lc_mainwindow.h"
#include "piece.h"
#include "lc_file.h"
#include <QDomDocument>
static bool lcLoadLDrawXML(std::map<int, int>& MaterialTable, std::map<int, std::string>& BrickTable, std::map<std::string, std::pair<lcVector3, lcVector4>>& TransformTable)
{
QFile File(lcGetPiecesLibrary()->GetLibraryFilePath(QLatin1String("ldraw.xml")));
QByteArray Data;
if (File.open(QIODevice::ReadOnly))
Data = File.readAll();
else
{
QFile DefaultFile(":/resources/ldraw.xml");
if (DefaultFile.open(QIODevice::ReadOnly))
Data = DefaultFile.readAll();
}
if (Data.isEmpty())
return false;
QDomDocument Document;
Document.setContent(QString::fromUtf8(Data));
QDomElement Root = Document.documentElement();
if (Root.tagName() != QLatin1String("LDrawMapping"))
return false;
for (QDomNode Node = Root.firstChild(); !Node.isNull(); Node = Node.nextSibling())
{
QDomElement Element = Node.toElement();
QString ElementName = Element.tagName();
if (ElementName == QLatin1String("Material"))
{
int LDrawColor = Element.attribute(QLatin1String("ldraw")).toInt();
int LegoColor = Element.attribute(QLatin1String("lego")).toInt();
MaterialTable[LegoColor] = LDrawColor;
}
else if (ElementName == QLatin1String("Brick"))
{
QString LDrawID = Element.attribute(QLatin1String("ldraw"));
int LegoID = Element.attribute(QLatin1String("lego")).toInt();
BrickTable.insert(std::make_pair(LegoID, LDrawID.toStdString()));
}
else if (ElementName == QLatin1String("Transformation"))
{
QString LDrawID = Element.attribute(QLatin1String("ldraw"));
lcVector3 Translation;
lcVector4 AxisAngle;
Translation[0] = Element.attribute(QLatin1String("tx")).toFloat();
Translation[1] = Element.attribute(QLatin1String("ty")).toFloat();
Translation[2] = Element.attribute(QLatin1String("tz")).toFloat();
AxisAngle[0] = Element.attribute(QLatin1String("ax")).toFloat();
AxisAngle[1] = Element.attribute(QLatin1String("ay")).toFloat();
AxisAngle[2] = Element.attribute(QLatin1String("az")).toFloat();
AxisAngle[3] = Element.attribute(QLatin1String("angle")).toFloat();
TransformTable.insert(std::make_pair(LDrawID.toStdString(), std::make_pair(Translation, AxisAngle)));
}
}
return true;
}
bool lcImportLXFMLFile(const QString& FileData, Project* Project, std::vector<lcPiece*>& Pieces, std::vector<std::vector<lcPiece*>>& Groups)
{
std::map<int, int> MaterialTable;
std::map<int, std::string> BrickTable;
std::map<std::string, std::pair<lcVector3, lcVector4>> TransformTable;
if (!lcLoadLDrawXML(MaterialTable, BrickTable, TransformTable))
return false;
QDomDocument Document;
Document.setContent(FileData);
QDomElement Root = Document.documentElement();
if (Root.tagName() != QLatin1String("LXFML"))
return false;
for (QDomNode SectionNode = Root.firstChild(); !SectionNode.isNull(); SectionNode = SectionNode.nextSibling())
{
QDomElement SectionElement = SectionNode.toElement();
if (SectionElement.tagName() != QLatin1String("Bricks"))
continue;
for (QDomNode BrickNode = SectionElement.firstChild(); !BrickNode.isNull(); BrickNode = BrickNode.nextSibling())
{
QDomElement BrickElement = BrickNode.toElement();
if (BrickElement.tagName() != QLatin1String("Brick"))
continue;
int NumBrickPieces = 0;
for (QDomNode PartNode = BrickElement.firstChild(); !PartNode.isNull(); PartNode = PartNode.nextSibling())
{
QDomElement PartElement = PartNode.toElement();
if (PartElement.tagName() != QLatin1String("Part"))
continue;
bool BoneFound = false;
lcMatrix44 WorldMatrix;
for (QDomNode BoneNode = PartElement.firstChild(); !BoneNode.isNull(); BoneNode = BoneNode.nextSibling())
{
QDomElement BoneElement = BoneNode.toElement();
if (BoneElement.tagName() != QLatin1String("Bone"))
continue;
QString BoneTransform = BoneElement.attribute(QLatin1String("transformation"));
QStringList BoneElements = BoneTransform.split(',');
if (BoneElements.size() != 12)
continue;
WorldMatrix[0][0] = BoneElements[0].toFloat();
WorldMatrix[0][1] = BoneElements[1].toFloat();
WorldMatrix[0][2] = BoneElements[2].toFloat();
WorldMatrix[0][3] = 0.0f;
WorldMatrix[1][0] = BoneElements[3].toFloat();
WorldMatrix[1][1] = BoneElements[4].toFloat();
WorldMatrix[1][2] = BoneElements[5].toFloat();
WorldMatrix[1][3] = 0.0f;
WorldMatrix[2][0] = BoneElements[6].toFloat();
WorldMatrix[2][1] = BoneElements[7].toFloat();
WorldMatrix[2][2] = BoneElements[8].toFloat();
WorldMatrix[2][3] = 0.0f;
WorldMatrix[3][0] = BoneElements[9].toFloat();
WorldMatrix[3][1] = BoneElements[10].toFloat();
WorldMatrix[3][2] = BoneElements[11].toFloat();
WorldMatrix[3][3] = 1.0f;
BoneFound = true;
break;
}
if (!BoneFound)
continue;
QString LegoID = PartElement.attribute(QLatin1String("designID"));
int Material = PartElement.attribute(QLatin1String("materials")).split(',').first().toInt();
lcPieceDataRef PieceData;
const auto BrickIt = BrickTable.find(LegoID.toInt());
if (BrickIt != BrickTable.end())
PieceData = lcGetPiecesLibrary()->AcquirePieceData(BrickIt->second.c_str(), Project, true, false);
else
PieceData = lcGetPiecesLibrary()->AcquirePieceData(LegoID.toLatin1() + ".dat", Project, true, false);
const auto ColorIt = MaterialTable.find(Material);
int ColorCode = 16;
if (ColorIt != MaterialTable.end())
ColorCode = ColorIt->second;
const auto TransformIt = TransformTable.find(BrickIt != BrickTable.end() ? BrickIt->second : (LegoID.toStdString() + ".dat"));
if (TransformIt != TransformTable.end())
{
const lcVector4& AxisAngle = TransformIt->second.second;
const lcVector3& TransformTranslation = TransformIt->second.first;
lcMatrix44 Transform = lcMatrix44FromAxisAngle(lcVector3(AxisAngle[0], AxisAngle[1], AxisAngle[2]), -AxisAngle[3]);
Transform.SetTranslation(lcMul30(-TransformTranslation, Transform));
WorldMatrix = lcMul(Transform, WorldMatrix);
}
WorldMatrix = lcMatrix44(lcVector4(WorldMatrix[0][0], -WorldMatrix[0][1], -WorldMatrix[0][2], 0.0f), lcVector4(-WorldMatrix[1][0], WorldMatrix[1][1], WorldMatrix[1][2], 0.0f),
lcVector4(-WorldMatrix[2][0], WorldMatrix[2][1], WorldMatrix[2][2], 0.0f), lcVector4(WorldMatrix[3][0] * 25.0f, -WorldMatrix[3][1] * 25.0f, -WorldMatrix[3][2] * 25.0f, 1.0f));
lcPiece* Piece = new lcPiece(nullptr);
Piece->SetPieceData(std::move(PieceData), QString(), false, true);
Piece->Initialize(lcMatrix44LDrawToLeoCAD(WorldMatrix), 1);
Piece->SetColorCode(ColorCode);
Pieces.push_back(Piece);
NumBrickPieces++;
}
if (NumBrickPieces > 1)
{
std::vector<lcPiece*> Group;
for (size_t PieceIdx = Pieces.size() - NumBrickPieces; PieceIdx < Pieces.size(); PieceIdx++)
Group.push_back(Pieces[PieceIdx]);
Groups.push_back(Group);
}
}
}
return true;
}