548 return false; |
548 return false; |
549 |
549 |
550 // If the second object in the list holds the file name, update that now. |
550 // If the second object in the list holds the file name, update that now. |
551 // Only do this if the file is explicitly open. If it's saved into a directory |
551 // Only do this if the file is explicitly open. If it's saved into a directory |
552 // called "s" or "48", prepend that into the name. |
552 // called "s" or "48", prepend that into the name. |
553 LDCommentObject* fpathComment = null; |
553 LDComment* fpathComment = null; |
554 LDObject* first = object (1); |
554 LDObject* first = object (1); |
555 |
555 |
556 if (!implicit() && first != null && first->getType() == LDObject::Comment) { |
556 if (!implicit() && first != null && first->getType() == LDObject::Comment) { |
557 fpathComment = static_cast<LDCommentObject*> (first); |
557 fpathComment = static_cast<LDComment*> (first); |
558 |
558 |
559 if (fpathComment->text.left (6) == "Name: ") { |
559 if (fpathComment->text.left (6) == "Name: ") { |
560 str newname; |
560 str newname; |
561 str dir = basename (dirname (savepath)); |
561 str dir = basename (dirname (savepath)); |
562 |
562 |
588 |
588 |
589 // ============================================================================= |
589 // ============================================================================= |
590 // ----------------------------------------------------------------------------- |
590 // ----------------------------------------------------------------------------- |
591 #define CHECK_TOKEN_COUNT(N) \ |
591 #define CHECK_TOKEN_COUNT(N) \ |
592 if (tokens.size() != N) \ |
592 if (tokens.size() != N) \ |
593 return new LDErrorObject (line, "Bad amount of tokens"); |
593 return new LDError (line, "Bad amount of tokens"); |
594 |
594 |
595 #define CHECK_TOKEN_NUMBERS(MIN, MAX) \ |
595 #define CHECK_TOKEN_NUMBERS(MIN, MAX) \ |
596 for (ushort i = MIN; i <= MAX; ++i) \ |
596 for (ushort i = MIN; i <= MAX; ++i) \ |
597 if (!isNumber (tokens[i])) \ |
597 if (!isNumber (tokens[i])) \ |
598 return new LDErrorObject (line, fmt ("Token #%1 was `%2`, expected a number", (i + 1), tokens[i])); |
598 return new LDError (line, fmt ("Token #%1 was `%2`, expected a number", (i + 1), tokens[i])); |
599 |
599 |
600 // ============================================================================= |
600 // ============================================================================= |
601 // ----------------------------------------------------------------------------- |
601 // ----------------------------------------------------------------------------- |
602 static vertex parseVertex (QStringList& s, const ushort n) { |
602 static vertex parseVertex (QStringList& s, const ushort n) { |
603 vertex v; |
603 vertex v; |
616 LDObject* parseLine (str line) { |
616 LDObject* parseLine (str line) { |
617 QStringList tokens = line.split (" ", str::SkipEmptyParts); |
617 QStringList tokens = line.split (" ", str::SkipEmptyParts); |
618 |
618 |
619 if (tokens.size() <= 0) { |
619 if (tokens.size() <= 0) { |
620 // Line was empty, or only consisted of whitespace |
620 // Line was empty, or only consisted of whitespace |
621 return new LDEmptyObject; |
621 return new LDEmpty; |
622 } |
622 } |
623 |
623 |
624 if (tokens[0].length() != 1 || tokens[0][0].isDigit() == false) |
624 if (tokens[0].length() != 1 || tokens[0][0].isDigit() == false) |
625 return new LDErrorObject (line, "Illogical line code"); |
625 return new LDError (line, "Illogical line code"); |
626 |
626 |
627 int num = tokens[0][0].digitValue(); |
627 int num = tokens[0][0].digitValue(); |
628 |
628 |
629 switch (num) { |
629 switch (num) { |
630 case 0: { |
630 case 0: { |
635 while (comm[0] == ' ') |
635 while (comm[0] == ' ') |
636 comm.remove (0, 1); |
636 comm.remove (0, 1); |
637 |
637 |
638 // Handle BFC statements |
638 // Handle BFC statements |
639 if (tokens.size() > 2 && tokens[1] == "BFC") { |
639 if (tokens.size() > 2 && tokens[1] == "BFC") { |
640 for (short i = 0; i < LDBFCObject::NumStatements; ++i) |
640 for (short i = 0; i < LDBFC::NumStatements; ++i) |
641 if (comm == fmt ("BFC %1", LDBFCObject::statements [i])) |
641 if (comm == fmt ("BFC %1", LDBFC::statements [i])) |
642 return new LDBFCObject ((LDBFCObject::Type) i); |
642 return new LDBFC ((LDBFC::Type) i); |
643 |
643 |
644 // MLCAD is notorious for stuffing these statements in parts it |
644 // MLCAD is notorious for stuffing these statements in parts it |
645 // creates. The above block only handles valid statements, so we |
645 // creates. The above block only handles valid statements, so we |
646 // need to handle MLCAD-style invertnext, clip and noclip separately. |
646 // need to handle MLCAD-style invertnext, clip and noclip separately. |
647 struct { |
647 struct { |
648 const char* a; |
648 const char* a; |
649 LDBFCObject::Type b; |
649 LDBFC::Type b; |
650 } BFCData[] = { |
650 } BFCData[] = { |
651 { "INVERTNEXT", LDBFCObject::InvertNext }, |
651 { "INVERTNEXT", LDBFC::InvertNext }, |
652 { "NOCLIP", LDBFCObject::NoClip }, |
652 { "NOCLIP", LDBFC::NoClip }, |
653 { "CLIP", LDBFCObject::Clip } |
653 { "CLIP", LDBFC::Clip } |
654 }; |
654 }; |
655 |
655 |
656 for (const auto& i : BFCData) |
656 for (const auto& i : BFCData) |
657 if (comm == fmt ("BFC CERTIFY %1", i.a)) |
657 if (comm == fmt ("BFC CERTIFY %1", i.a)) |
658 return new LDBFCObject (i.b); |
658 return new LDBFC (i.b); |
659 } |
659 } |
660 |
660 |
661 if (tokens.size() > 2 && tokens[1] == "!LDFORGE") { |
661 if (tokens.size() > 2 && tokens[1] == "!LDFORGE") { |
662 // Handle LDForge-specific types, they're embedded into comments too |
662 // Handle LDForge-specific types, they're embedded into comments too |
663 if (tokens[2] == "VERTEX") { |
663 if (tokens[2] == "VERTEX") { |
664 // Vertex (0 !LDFORGE VERTEX) |
664 // Vertex (0 !LDFORGE VERTEX) |
665 CHECK_TOKEN_COUNT (7) |
665 CHECK_TOKEN_COUNT (7) |
666 CHECK_TOKEN_NUMBERS (3, 6) |
666 CHECK_TOKEN_NUMBERS (3, 6) |
667 |
667 |
668 LDVertexObject* obj = new LDVertexObject; |
668 LDVertex* obj = new LDVertex; |
669 obj->setColor (tokens[3].toLong()); |
669 obj->setColor (tokens[3].toLong()); |
670 |
670 |
671 for (const Axis ax : g_Axes) |
671 for (const Axis ax : g_Axes) |
672 obj->pos[ax] = tokens[4 + ax].toDouble(); // 4 - 6 |
672 obj->pos[ax] = tokens[4 + ax].toDouble(); // 4 - 6 |
673 |
673 |
674 return obj; |
674 return obj; |
675 } elif (tokens[2] == "OVERLAY") { |
675 } elif (tokens[2] == "OVERLAY") { |
676 CHECK_TOKEN_COUNT (9); |
676 CHECK_TOKEN_COUNT (9); |
677 CHECK_TOKEN_NUMBERS (5, 8) |
677 CHECK_TOKEN_NUMBERS (5, 8) |
678 |
678 |
679 LDOverlayObject* obj = new LDOverlayObject; |
679 LDOverlay* obj = new LDOverlay; |
680 obj->setFilename (tokens[3]); |
680 obj->setFilename (tokens[3]); |
681 obj->setCamera (tokens[4].toLong()); |
681 obj->setCamera (tokens[4].toLong()); |
682 obj->setX (tokens[5].toLong()); |
682 obj->setX (tokens[5].toLong()); |
683 obj->setY (tokens[6].toLong()); |
683 obj->setY (tokens[6].toLong()); |
684 obj->setWidth (tokens[7].toLong()); |
684 obj->setWidth (tokens[7].toLong()); |
705 LDFile* load = getFile (tokens[14]); |
705 LDFile* load = getFile (tokens[14]); |
706 g_loadingMainFile = tmp; |
706 g_loadingMainFile = tmp; |
707 |
707 |
708 // If we cannot open the file, mark it an error |
708 // If we cannot open the file, mark it an error |
709 if (!load) { |
709 if (!load) { |
710 LDErrorObject* obj = new LDErrorObject (line, fmt ("Could not open %1", tokens[14])); |
710 LDError* obj = new LDError (line, fmt ("Could not open %1", tokens[14])); |
711 obj->setFileRef (tokens[14]); |
711 obj->setFileRef (tokens[14]); |
712 return obj; |
712 return obj; |
713 } |
713 } |
714 |
714 |
715 LDSubfileObject* obj = new LDSubfileObject; |
715 LDSubfile* obj = new LDSubfile; |
716 obj->setColor (tokens[1].toLong()); |
716 obj->setColor (tokens[1].toLong()); |
717 obj->setPosition (parseVertex (tokens, 2)); // 2 - 4 |
717 obj->setPosition (parseVertex (tokens, 2)); // 2 - 4 |
718 |
718 |
719 matrix transform; |
719 matrix transform; |
720 |
720 |
761 |
761 |
762 // Quadrilateral / Conditional line |
762 // Quadrilateral / Conditional line |
763 LDObject* obj; |
763 LDObject* obj; |
764 |
764 |
765 if (num == 4) |
765 if (num == 4) |
766 obj = new LDQuadObject; |
766 obj = new LDQuad; |
767 else |
767 else |
768 obj = new LDCondLineObject; |
768 obj = new LDCndLine; |
769 |
769 |
770 obj->setColor (tokens[1].toLong()); |
770 obj->setColor (tokens[1].toLong()); |
771 |
771 |
772 for (short i = 0; i < 4; ++i) |
772 for (short i = 0; i < 4; ++i) |
773 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 13 |
773 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 13 |
774 |
774 |
775 return obj; |
775 return obj; |
776 } |
776 } |
777 |
777 |
778 default: // Strange line we couldn't parse |
778 default: // Strange line we couldn't parse |
779 return new LDErrorObject (line, "Unknown line code number"); |
779 return new LDError (line, "Unknown line code number"); |
780 } |
780 } |
781 } |
781 } |
782 |
782 |
783 // ============================================================================= |
783 // ============================================================================= |
784 // ----------------------------------------------------------------------------- |
784 // ----------------------------------------------------------------------------- |
803 g_loadedFiles << LDFile::current(); |
803 g_loadedFiles << LDFile::current(); |
804 |
804 |
805 // Go through all objects in the current file and reload the subfiles |
805 // Go through all objects in the current file and reload the subfiles |
806 for (LDObject* obj : LDFile::current()->objs()) { |
806 for (LDObject* obj : LDFile::current()->objs()) { |
807 if (obj->getType() == LDObject::Subfile) { |
807 if (obj->getType() == LDObject::Subfile) { |
808 LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj); |
808 LDSubfile* ref = static_cast<LDSubfile*> (obj); |
809 LDFile* fileInfo = getFile (ref->fileInfo()->name()); |
809 LDFile* fileInfo = getFile (ref->fileInfo()->name()); |
810 |
810 |
811 if (fileInfo) |
811 if (fileInfo) |
812 ref->setFileInfo (fileInfo); |
812 ref->setFileInfo (fileInfo); |
813 else |
813 else |
814 ref->replace (new LDErrorObject (ref->raw(), "Could not open referred file")); |
814 ref->replace (new LDError (ref->raw(), "Could not open referred file")); |
815 } |
815 } |
816 |
816 |
817 // Reparse gibberish files. It could be that they are invalid because |
817 // Reparse gibberish files. It could be that they are invalid because |
818 // of loading errors. Circumstances may be different now. |
818 // of loading errors. Circumstances may be different now. |
819 if (obj->getType() == LDObject::Error) |
819 if (obj->getType() == LDObject::Error) |
820 obj->replace (parseLine (static_cast<LDErrorObject*> (obj)->contents)); |
820 obj->replace (parseLine (static_cast<LDError*> (obj)->contents)); |
821 } |
821 } |
822 |
822 |
823 // Close all files left unused |
823 // Close all files left unused |
824 LDFile::closeUnused(); |
824 LDFile::closeUnused(); |
825 } |
825 } |
973 return tr ("<anonymous>"); |
973 return tr ("<anonymous>"); |
974 } |
974 } |
975 |
975 |
976 // ============================================================================= |
976 // ============================================================================= |
977 // ----------------------------------------------------------------------------- |
977 // ----------------------------------------------------------------------------- |
978 List<LDObject*> LDFile::inlineContents (LDSubfileObject::InlineFlags flags) { |
978 List<LDObject*> LDFile::inlineContents (LDSubfile::InlineFlags flags) { |
979 // Possibly substitute with logoed studs: |
979 // Possibly substitute with logoed studs: |
980 // stud.dat -> stud-logo.dat |
980 // stud.dat -> stud-logo.dat |
981 // stud2.dat -> stud-logo2.dat |
981 // stud2.dat -> stud-logo2.dat |
982 if (gl_logostuds && (flags & LDSubfileObject::RendererInline)) { |
982 if (gl_logostuds && (flags & LDSubfile::RendererInline)) { |
983 if (name() == "stud.dat" && g_logoedStud) |
983 if (name() == "stud.dat" && g_logoedStud) |
984 return g_logoedStud->inlineContents (flags); |
984 return g_logoedStud->inlineContents (flags); |
985 elif (name() == "stud2.dat" && g_logoedStud2) |
985 elif (name() == "stud2.dat" && g_logoedStud2) |
986 return g_logoedStud2->inlineContents (flags); |
986 return g_logoedStud2->inlineContents (flags); |
987 } |
987 } |
988 |
988 |
989 List<LDObject*> objs, objcache; |
989 List<LDObject*> objs, objcache; |
990 |
990 |
991 bool deep = flags & LDSubfileObject::DeepInline, |
991 bool deep = flags & LDSubfile::DeepInline, |
992 doCache = flags & LDSubfileObject::CacheInline; |
992 doCache = flags & LDSubfile::CacheInline; |
993 |
993 |
994 // If we have this cached, just clone that |
994 // If we have this cached, just clone that |
995 if (deep && cache().size()) { |
995 if (deep && cache().size()) { |
996 for (LDObject* obj : cache()) |
996 for (LDObject* obj : cache()) |
997 objs << obj->clone(); |
997 objs << obj->clone(); |
1006 |
1006 |
1007 // Got another sub-file reference, inline it if we're deep-inlining. If not, |
1007 // Got another sub-file reference, inline it if we're deep-inlining. If not, |
1008 // just add it into the objects normally. Also, we only cache immediate |
1008 // just add it into the objects normally. Also, we only cache immediate |
1009 // subfiles and this is not one. Yay, recursion! |
1009 // subfiles and this is not one. Yay, recursion! |
1010 if (deep && obj->getType() == LDObject::Subfile) { |
1010 if (deep && obj->getType() == LDObject::Subfile) { |
1011 LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj); |
1011 LDSubfile* ref = static_cast<LDSubfile*> (obj); |
1012 |
1012 |
1013 // We only want to cache immediate subfiles, so shed the caching |
1013 // We only want to cache immediate subfiles, so shed the caching |
1014 // flag when recursing deeper in hierarchy. |
1014 // flag when recursing deeper in hierarchy. |
1015 List<LDObject*> otherobjs = ref->inlineContents (flags & ~(LDSubfileObject::CacheInline)); |
1015 List<LDObject*> otherobjs = ref->inlineContents (flags & ~(LDSubfile::CacheInline)); |
1016 |
1016 |
1017 for (LDObject* otherobj : otherobjs) { |
1017 for (LDObject* otherobj : otherobjs) { |
1018 // Cache this object, if desired |
1018 // Cache this object, if desired |
1019 if (doCache) |
1019 if (doCache) |
1020 objcache << otherobj->clone(); |
1020 objcache << otherobj->clone(); |