|      2  | 
     2  | 
|      3 #include "common.h" | 
     3 #include "common.h" | 
|      4 #include "io.h" | 
     4 #include "io.h" | 
|      5 #include "misc.h" | 
     5 #include "misc.h" | 
|      6 #include "bbox.h" | 
     6 #include "bbox.h" | 
|         | 
     7 #include "gui.h" | 
|      7  | 
     8  | 
|      8 vector<str> g_zaFileLoadPaths; | 
     9 vector<str> g_zaFileLoadPaths; | 
|      9  | 
    10  | 
|     10 // ============================================================================= | 
    11 // ============================================================================= | 
|     11 // IO_FindLoadedFile (str) | 
    12 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|     12 // | 
    13 // ============================================================================= | 
|     13 // Returns a pointer to the first found open file with the given name. | 
    14 OpenFile* findLoadedFile (str name) { | 
|     14 // ============================================================================= | 
        | 
|     15 OpenFile* IO_FindLoadedFile (str name) { | 
        | 
|     16 	for (ulong i = 0; i < g_LoadedFiles.size(); i++) { | 
    15 	for (ulong i = 0; i < g_LoadedFiles.size(); i++) { | 
|     17 		OpenFile* const file = g_LoadedFiles[i]; | 
    16 		OpenFile* const file = g_LoadedFiles[i]; | 
|     18 		if (file->zFileName == name) | 
    17 		if (file->zFileName == name) | 
|     19 			return file; | 
    18 			return file; | 
|     20 	} | 
    19 	} | 
|     21 	 | 
    20 	 | 
|     22 	return nullptr; | 
    21 	return nullptr; | 
|     23 } | 
    22 } | 
|     24  | 
    23  | 
|     25 // ============================================================================= | 
    24 // ============================================================================= | 
|     26 // IO_OpenLDrawFile (str) | 
    25 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|     27 // | 
    26 // ============================================================================= | 
|     28 // Opens the given file and parses the LDraw code within. | 
    27 OpenFile* openDATFile (str path) { | 
|     29 // ============================================================================= | 
        | 
|     30 OpenFile* IO_OpenLDrawFile (str path) { | 
        | 
|     31 	logf ("Opening %s...\n", path.chars()); | 
    28 	logf ("Opening %s...\n", path.chars()); | 
|     32 	 | 
    29 	 | 
|     33 	FILE* fp = fopen (path.chars (), "r"); | 
    30 	FILE* fp = fopen (path.chars (), "r"); | 
|     34 	 | 
    31 	 | 
|     35 	if (!fp) { | 
    32 	if (!fp) { | 
|     67 	} | 
    64 	} | 
|     68 	 | 
    65 	 | 
|     69 	fclose (fp); | 
    66 	fclose (fp); | 
|     70 	 | 
    67 	 | 
|     71 	for (ulong i = 0; i < lines.size(); ++i) { | 
    68 	for (ulong i = 0; i < lines.size(); ++i) { | 
|     72 		LDObject* obj = ParseLine (lines[i]); | 
    69 		LDObject* obj = parseLine (lines[i]); | 
|     73 		load->objects.push_back (obj); | 
    70 		load->objects.push_back (obj); | 
|     74 		 | 
    71 		 | 
|     75 		// Check for warnings | 
    72 		// Check for parse errors and warn abotu tthem | 
|     76 		if (obj->getType() == OBJ_Gibberish) { | 
    73 		if (obj->getType() == OBJ_Gibberish) { | 
|     77 			logf (LOG_Warning, "Couldn't parse line #%lu: %s\n", | 
    74 			logf (LOG_Warning, "Couldn't parse line #%lu: %s\n", | 
|     78 				i, static_cast<LDGibberish*> (obj)->zReason.chars()); | 
    75 				i, static_cast<LDGibberish*> (obj)->zReason.chars()); | 
|     79 			logf (LOG_Warning, "- Line was: %s\n", lines[i].chars()); | 
    76 			logf (LOG_Warning, "- Line was: %s\n", lines[i].chars()); | 
|     80 			numWarnings++; | 
    77 			numWarnings++; | 
|     88 	 | 
    85 	 | 
|     89 	return load; | 
    86 	return load; | 
|     90 } | 
    87 } | 
|     91  | 
    88  | 
|     92 // ============================================================================= | 
    89 // ============================================================================= | 
|     93 // isNumber (char*) [static] | 
    90 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|     94 // | 
    91 // ============================================================================= | 
|     95 // Returns whether a given string represents a floating point number | 
    92 // Clear everything from the model | 
|     96 // TODO: Does LDraw support scientific notation? | 
    93 void OpenFile::close () { | 
|     97 // ============================================================================= | 
    94 	for (ulong j = 0; j < objects.size(); ++j) | 
|     98 static bool isNumber (char* sToken) { | 
    95 		delete objects[j]; | 
|     99 	char* sPointer = &sToken[0]; | 
    96 	 | 
|    100 	bool bGotDot = false; | 
    97 	delete this; | 
|    101 	 | 
    98 } | 
|    102 	// Allow leading hyphen for negatives | 
    99  | 
|    103 	if (*sPointer == '-') | 
   100 // ============================================================================= | 
|    104 		sPointer++; | 
   101 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|    105 	 | 
   102 // ============================================================================= | 
|    106 	while (*sPointer != '\0') { | 
   103 void closeAll () { | 
|    107 		if (*sPointer == '.' && !bGotDot) { | 
   104 	if (!g_LoadedFiles.size()) | 
|    108 			// Decimal point | 
   105 		return; | 
|    109 			bGotDot = true; | 
   106 	 | 
|    110 			sPointer++; | 
   107 	// Remove all loaded files and the objects they contain | 
|    111 			continue; | 
   108 	for (ushort i = 0; i < g_LoadedFiles.size(); i++) { | 
|    112 		} | 
   109 		OpenFile* f = g_LoadedFiles[i]; | 
|         | 
   110 		f->close (); | 
|         | 
   111 	} | 
|         | 
   112 	 | 
|         | 
   113 	// Clear the array | 
|         | 
   114 	g_LoadedFiles.clear(); | 
|         | 
   115 	g_CurrentFile = NULL; | 
|         | 
   116 	 | 
|         | 
   117 	g_qWindow->R->hardRefresh(); | 
|         | 
   118 } | 
|         | 
   119  | 
|         | 
   120 // ============================================================================= | 
|         | 
   121 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|         | 
   122 // ============================================================================= | 
|         | 
   123 void newFile () { | 
|         | 
   124 	// Create a new anonymous file and set it to our current | 
|         | 
   125 	closeAll (); | 
|         | 
   126 	 | 
|         | 
   127 	OpenFile* f = new OpenFile; | 
|         | 
   128 	f->zFileName = ""; | 
|         | 
   129 	g_LoadedFiles.push_back (f); | 
|         | 
   130 	g_CurrentFile = f; | 
|         | 
   131 	 | 
|         | 
   132 	g_BBox.calculate(); | 
|         | 
   133 	g_qWindow->buildObjList (); | 
|         | 
   134 	g_qWindow->R->hardRefresh(); | 
|         | 
   135 } | 
|         | 
   136  | 
|         | 
   137 // ============================================================================= | 
|         | 
   138 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|         | 
   139 // ============================================================================= | 
|         | 
   140 void openMainFile (str zPath) { | 
|         | 
   141 	closeAll (); | 
|         | 
   142 	 | 
|         | 
   143 	OpenFile* pFile = openDATFile (zPath); | 
|         | 
   144 	g_CurrentFile = pFile; | 
|         | 
   145 	 | 
|         | 
   146 	// Recalculate the bounding box | 
|         | 
   147 	g_BBox.calculate(); | 
|         | 
   148 	 | 
|         | 
   149 	// Rebuild the object tree view now. | 
|         | 
   150 	g_qWindow->buildObjList (); | 
|         | 
   151 	g_qWindow->setTitle (); | 
|         | 
   152 } | 
|         | 
   153  | 
|         | 
   154 // ============================================================================= | 
|         | 
   155 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|         | 
   156 // ============================================================================= | 
|         | 
   157 void OpenFile::save (str zPath) { | 
|         | 
   158 	if (!~zPath) | 
|         | 
   159 		zPath = zFileName; | 
|         | 
   160 	 | 
|         | 
   161 	FILE* fp = fopen (zPath, "w"); | 
|         | 
   162 	if (!fp) | 
|         | 
   163 		return; | 
|         | 
   164 	 | 
|         | 
   165 	// Write all entries now | 
|         | 
   166 	for (ulong i = 0; i < objects.size(); ++i) { | 
|         | 
   167 		LDObject* obj = objects[i]; | 
|    113 		 | 
   168 		 | 
|    114 		if (*sPointer >= '0' && *sPointer <= '9') { | 
   169 		// LDraw requires lines to have DOS line endings | 
|    115 			sPointer++; | 
   170 		str zLine = str::mkfmt ("%s\r\n",obj->getContents ().chars ()); | 
|    116 			continue; // Digit | 
        | 
|    117 		} | 
        | 
|    118 		 | 
   171 		 | 
|    119 		// If the above cases didn't catch this character, it was | 
   172 		fwrite (zLine.chars(), 1, ~zLine, fp); | 
|    120 		// illegal and this is therefore not a number. | 
   173 	} | 
|    121 		return false; | 
   174 	 | 
|    122 	} | 
   175 	fclose (fp); | 
|    123 	 | 
   176 } | 
|    124 	return true; | 
   177  | 
|    125 } | 
   178 // ============================================================================= | 
|    126  | 
   179 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | 
|    127 // ============================================================================= | 
        | 
|    128 // ParseLine (str) | 
        | 
|    129 // | 
        | 
|    130 // Parses a string line containing an LDraw object and returns the object parsed. | 
        | 
|    131 // ============================================================================= | 
   180 // ============================================================================= | 
|    132 #define CHECK_TOKEN_COUNT(N) \ | 
   181 #define CHECK_TOKEN_COUNT(N) \ | 
|    133 	if (tokens.size() != N) \ | 
   182 	if (tokens.size() != N) \ | 
|    134 		return new LDGibberish (zLine, "Bad amount of tokens"); | 
   183 		return new LDGibberish (zLine, "Bad amount of tokens"); | 
|    135  | 
   184  | 
|    137 	for (ushort i = MIN; i <= MAX; ++i) \ | 
   186 	for (ushort i = MIN; i <= MAX; ++i) \ | 
|    138 		if (!isNumber (tokens[i])) \ | 
   187 		if (!isNumber (tokens[i])) \ | 
|    139 			return new LDGibberish (zLine, str::mkfmt ("Token #%u was `%s`, expected a number", \ | 
   188 			return new LDGibberish (zLine, str::mkfmt ("Token #%u was `%s`, expected a number", \ | 
|    140 				(i + 1), tokens[i].chars())); | 
   189 				(i + 1), tokens[i].chars())); | 
|    141  | 
   190  | 
|    142 LDObject* ParseLine (str zLine) { | 
   191 LDObject* parseLine (str zLine) { | 
|    143 	str zNoWhitespace = zLine; | 
   192 	str zNoWhitespace = zLine; | 
|    144 	StripWhitespace (zNoWhitespace); | 
   193 	stripWhitespace (zNoWhitespace); | 
|    145 	if (!~zNoWhitespace) { | 
   194 	if (!~zNoWhitespace) { | 
|    146 		// Line was empty, or only consisted of whitespace | 
   195 		// Line was empty, or only consisted of whitespace | 
|    147 		return new LDEmpty; | 
   196 		return new LDEmpty; | 
|    148 	} | 
   197 	} | 
|    149 	 | 
   198 	 | 
|    175 #ifndef WIN32 | 
   224 #ifndef WIN32 | 
|    176 			tokens[14].replace ("\\", "/"); | 
   225 			tokens[14].replace ("\\", "/"); | 
|    177 #endif // WIN32 | 
   226 #endif // WIN32 | 
|    178 			 | 
   227 			 | 
|    179 			// Try open the file | 
   228 			// Try open the file | 
|    180 			OpenFile* pFile = IO_FindLoadedFile (tokens[14]); | 
   229 			OpenFile* pFile = findLoadedFile (tokens[14]); | 
|    181 			if (!pFile) | 
   230 			if (!pFile) | 
|    182 				pFile = IO_OpenLDrawFile (tokens[14]); | 
   231 				pFile = openDATFile (tokens[14]); | 
|    183 			 | 
   232 			 | 
|    184 			// If we cannot open the file, mark it an error | 
   233 			// If we cannot open the file, mark it an error | 
|    185 			if (!pFile) | 
   234 			if (!pFile) | 
|    186 				return new LDGibberish (zLine, "Could not open referred file"); | 
   235 				return new LDGibberish (zLine, "Could not open referred file"); | 
|    187 			 | 
   236 			 | 
|    188 			LDSubfile* obj = new LDSubfile; | 
   237 			LDSubfile* obj = new LDSubfile; | 
|    189 			obj->dColor = atoi (tokens[1]); | 
   238 			obj->dColor = atoi (tokens[1]); | 
|    190 			obj->vPosition = ParseVertex (zLine, 2); // 2 - 4 | 
   239 			obj->vPosition = parseVertex (zLine, 2); // 2 - 4 | 
|    191 			 | 
   240 			 | 
|    192 			for (short i = 0; i < 9; ++i) | 
   241 			for (short i = 0; i < 9; ++i) | 
|    193 				obj->faMatrix[i] = atof (tokens[i + 5]); // 5 - 13 | 
   242 				obj->faMatrix[i] = atof (tokens[i + 5]); // 5 - 13 | 
|    194 			 | 
   243 			 | 
|    195 			obj->zFileName = tokens[14]; | 
   244 			obj->zFileName = tokens[14]; | 
|    202 			CHECK_TOKEN_COUNT (8) | 
   251 			CHECK_TOKEN_COUNT (8) | 
|    203 			CHECK_TOKEN_NUMBERS (1, 7) | 
   252 			CHECK_TOKEN_NUMBERS (1, 7) | 
|    204 			 | 
   253 			 | 
|    205 			// Line | 
   254 			// Line | 
|    206 			LDLine* obj = new LDLine; | 
   255 			LDLine* obj = new LDLine; | 
|    207 			obj->dColor = GetWordInt (zLine, 1); | 
   256 			obj->dColor = getWordInt (zLine, 1); | 
|    208 			for (short i = 0; i < 2; ++i) | 
   257 			for (short i = 0; i < 2; ++i) | 
|    209 				obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 7 | 
   258 				obj->vaCoords[i] = parseVertex (zLine, 2 + (i * 3)); // 2 - 7 | 
|    210 			return obj; | 
   259 			return obj; | 
|    211 		} | 
   260 		} | 
|    212 	 | 
   261 	 | 
|    213 	case 3: | 
   262 	case 3: | 
|    214 		{ | 
   263 		{ | 
|    215 			CHECK_TOKEN_COUNT (11) | 
   264 			CHECK_TOKEN_COUNT (11) | 
|    216 			CHECK_TOKEN_NUMBERS (1, 10) | 
   265 			CHECK_TOKEN_NUMBERS (1, 10) | 
|    217 			 | 
   266 			 | 
|    218 			// Triangle | 
   267 			// Triangle | 
|    219 			LDTriangle* obj = new LDTriangle; | 
   268 			LDTriangle* obj = new LDTriangle; | 
|    220 			obj->dColor = GetWordInt (zLine, 1); | 
   269 			obj->dColor = getWordInt (zLine, 1); | 
|    221 			 | 
   270 			 | 
|    222 			for (short i = 0; i < 3; ++i) | 
   271 			for (short i = 0; i < 3; ++i) | 
|    223 				obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 10 | 
   272 				obj->vaCoords[i] = parseVertex (zLine, 2 + (i * 3)); // 2 - 10 | 
|    224 			 | 
   273 			 | 
|    225 			return obj; | 
   274 			return obj; | 
|    226 		} | 
   275 		} | 
|    227 	 | 
   276 	 | 
|    228 	case 4: | 
   277 	case 4: | 
|    245 			CHECK_TOKEN_COUNT (14) | 
   294 			CHECK_TOKEN_COUNT (14) | 
|    246 			CHECK_TOKEN_NUMBERS (1, 13) | 
   295 			CHECK_TOKEN_NUMBERS (1, 13) | 
|    247 			 | 
   296 			 | 
|    248 			// Conditional line | 
   297 			// Conditional line | 
|    249 			LDCondLine* obj = new LDCondLine; | 
   298 			LDCondLine* obj = new LDCondLine; | 
|    250 			obj->dColor = GetWordInt (zLine, 1); | 
   299 			obj->dColor = getWordInt (zLine, 1); | 
|    251 			 | 
   300 			 | 
|    252 			for (short i = 0; i < 2; ++i) | 
   301 			for (short i = 0; i < 2; ++i) | 
|    253 				obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 7 | 
   302 				obj->vaCoords[i] = parseVertex (zLine, 2 + (i * 3)); // 2 - 7 | 
|    254 			 | 
   303 			 | 
|    255 			for (short i = 0; i < 2; ++i) | 
   304 			for (short i = 0; i < 2; ++i) | 
|    256 				obj->vaControl[i] = ParseVertex (zLine, 8 + (i * 3)); // 8 - 13 | 
   305 				obj->vaControl[i] = parseVertex (zLine, 8 + (i * 3)); // 8 - 13 | 
|    257 			return obj; | 
   306 			return obj; | 
|    258 		} | 
   307 		} | 
|    259 		 | 
   308 		 | 
|    260 	default: // Strange line we couldn't parse | 
   309 	default: // Strange line we couldn't parse | 
|    261 		return new LDGibberish (zLine, "Unknown line code number"); | 
   310 		return new LDGibberish (zLine, "Unknown line code number"); |