io.cpp

Mon, 18 Mar 2013 03:38:51 +0200

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Mon, 18 Mar 2013 03:38:51 +0200
changeset 23
69a91c1ff583
parent 22
335e430a6b4f
child 24
d2d4d0154338
permissions
-rw-r--r--

begin work on subfile caching

#include <vector>

#include "common.h"
#include "io.h"
#include "misc.h"
#include "gui.h"
#include "bbox.h"

vector<str> g_zaFileLoadPaths;

// =============================================================================
// IO_FindLoadedFile (str)
//
// Returns a pointer to the first found open file with the given name.
// =============================================================================
OpenFile* IO_FindLoadedFile (str name) {
	for (ulong i = 0; i < g_LoadedFiles.size(); i++) {
		OpenFile* const file = g_LoadedFiles[i];
		if (file->zFileName == name)
			return file;
	}
	
	return nullptr;
}

// =============================================================================
// IO_OpenLDrawFile (str)
//
// Opens the given file and parses the LDraw code within.
// =============================================================================
OpenFile* IO_OpenLDrawFile (str path) {
	logf ("Opening %s...\n", path.chars());
	
	FILE* fp = fopen (path.chars (), "r");
	
	if (!fp) {
		for (ushort i = 0; i < g_zaFileLoadPaths.size(); ++i) {
			str zFilePath = str::mkfmt ("%s/%s", g_zaFileLoadPaths[i].chars(), path.chars());
			printf ("try open %s\n", zFilePath.chars ());
			fp = fopen (zFilePath.chars (), "r");
			
			if (fp)
				break;
		}
	}
	
	if (!fp) {
		logf (LOG_Error, "Couldn't open %s: %s\n", path.chars (), strerror (errno));
		return nullptr;
	}
	
	OpenFile* load = new OpenFile;
	ulong numWarnings = 0;
	
	load->zFileName = path;
	
	vector<str> lines;
	
	{
		char line[1024];
		while (fgets (line, sizeof line, fp)) {
			// Trim the trailing newline
			str zLine = line;
			while (zLine[~zLine - 1] == '\n' || zLine[~zLine - 1] == '\r')
				zLine -= 1;
			
			lines.push_back (zLine);
		}
	}
	
	fclose (fp);
	
	for (ulong i = 0; i < lines.size(); ++i) {
		LDObject* obj = ParseLine (lines[i]);
		load->objects.push_back (obj);
		
		// Check for warnings
		if (obj->getType() == OBJ_Gibberish) {
			logf (LOG_Warning, "Couldn't parse line #%lu: %s\n",
				i, static_cast<LDGibberish*> (obj)->zReason.chars());
			logf (LOG_Warning, "- Line was: %s\n", lines[i].chars());
			numWarnings++;
		}
	}
	
	g_LoadedFiles.push_back (load);
	
	logf (LOG_Success, "File %s parsed successfully (%lu warning%s).\n",
		path.chars(), numWarnings, PLURAL (numWarnings));
	
	return load;
}

// =============================================================================
// isNumber (char*) [static]
//
// Returns whether a given string represents a floating point number
// TODO: Does LDraw support scientific notation?
// =============================================================================
static bool isNumber (char* sToken) {
	char* sPointer = &sToken[0];
	bool bGotDot = false;
	
	// Allow leading hyphen for negatives
	if (*sPointer == '-')
		sPointer++;
	
	while (*sPointer != '\0') {
		if (*sPointer == '.' && !bGotDot) {
			// Decimal point
			bGotDot = true;
			sPointer++;
			continue;
		}
		
		if (*sPointer >= '0' && *sPointer <= '9') {
			sPointer++;
			continue; // Digit
		}
		
		// If the above cases didn't catch this character, it was
		// illegal and this is therefore not a number.
		return false;
	}
	
	return true;
}

// =============================================================================
// ParseLine (str)
//
// Parses a string line containing an LDraw object and returns the object parsed.
// =============================================================================
#define CHECK_TOKEN_COUNT(N) \
	if (tokens.size() != N) \
		return new LDGibberish (zLine, "Bad amount of tokens");

#define CHECK_TOKEN_NUMBERS(MIN,MAX) \
	for (ushort i = MIN; i <= MAX; ++i) \
		if (!isNumber (tokens[i])) \
			return new LDGibberish (zLine, str::mkfmt ("Token #%u was `%s`, expected a number", \
				(i + 1), tokens[i].chars()));

LDObject* ParseLine (str zLine) {
	str zNoWhitespace = zLine;
	StripWhitespace (zNoWhitespace);
	if (!~zNoWhitespace) {
		// Line was empty, or only consisted of whitespace
		return new LDEmpty;
	}
	
	vector<str> tokens = zLine / " ";
	
	// Rid leading all-whitespace tokens
	while (tokens.size() && !(~tokens[0]))
		tokens.erase (tokens.begin());
	
	if (~tokens[0] != 1)
		return new LDGibberish (zLine, "Illogical line code");
	
	char const c = tokens[0][0];
	switch (c - '0') {
	case 0:
		{
			// Comment
			LDComment* obj = new LDComment;
			obj->zText = zLine.substr (2, -1);
			return obj;
		}
	
	case 1:
		{
			// Subfile
			CHECK_TOKEN_COUNT (15)
			CHECK_TOKEN_NUMBERS (1, 13)
			
#ifndef WIN32
			tokens[14].replace ("\\", "/");
#endif // WIN32
			
			// Try open the file
			OpenFile* pFile = IO_FindLoadedFile (tokens[14]);
			if (!pFile)
				pFile = IO_OpenLDrawFile (tokens[14]);
			
			// If we cannot open the file, mark it an error
			if (!pFile)
				return new LDGibberish (zLine, "Could not open referred file");
			
			LDSubfile* obj = new LDSubfile;
			obj->dColor = atoi (tokens[1]);
			obj->vPosition = ParseVertex (zLine, 2); // 2 - 4
			
			for (short i = 0; i < 9; ++i)
				obj->faMatrix[i] = atof (tokens[i + 5]); // 5 - 13
			
			obj->zFileName = tokens[14];
			obj->pFile = pFile;
			return obj;
		}
	
	case 2:
		{
			CHECK_TOKEN_COUNT (8)
			CHECK_TOKEN_NUMBERS (1, 7)
			
			// Line
			LDLine* obj = new LDLine;
			obj->dColor = GetWordInt (zLine, 1);
			for (short i = 0; i < 2; ++i)
				obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 7
			return obj;
		}
	
	case 3:
		{
			CHECK_TOKEN_COUNT (11)
			CHECK_TOKEN_NUMBERS (1, 10)
			
			// Triangle
			LDTriangle* obj = new LDTriangle;
			obj->dColor = GetWordInt (zLine, 1);
			
			for (short i = 0; i < 3; ++i)
				obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 10
			
			return obj;
		}
	
	case 4:
		{
			CHECK_TOKEN_COUNT (14)
			CHECK_TOKEN_NUMBERS (1, 13)
			
			// Quadrilateral
			LDQuad* obj = new LDQuad;
			obj->dColor = GetWordInt (zLine, 1);
			
			for (short i = 0; i < 4; ++i)
				obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 13
			
			return obj;
		}
	
	case 5:
		{
			CHECK_TOKEN_COUNT (14)
			CHECK_TOKEN_NUMBERS (1, 13)
			
			// Conditional line
			LDCondLine* obj = new LDCondLine;
			obj->dColor = GetWordInt (zLine, 1);
			
			for (short i = 0; i < 2; ++i)
				obj->vaCoords[i] = ParseVertex (zLine, 2 + (i * 3)); // 2 - 7
			
			for (short i = 0; i < 2; ++i)
				obj->vaControl[i] = ParseVertex (zLine, 8 + (i * 3)); // 8 - 13
			return obj;
		}
		
	default: // Strange line we couldn't parse
		return new LDGibberish (zLine, "Unknown line code number");
	}
}

mercurial