Просмотр исходного кода

core: add sjson::set_error_callback()

Fixes: #224
Daniele Bartolini 1 год назад
Родитель
Сommit
2ac8f52dc8

+ 1 - 0
docs/changelog.rst

@@ -28,6 +28,7 @@ Changelog
 * Lua API: added ``SceneGraph.owner()``, ``SceneGraph.first_child()`` and ``SceneGraph.next_sibling()``.
 * Data Compiler: .mesh resource can now have shared geometries between nodes.
 * Data Compiler: .unit resources have now the ability to add/remove inherited children or to override them by adding, removing or modifying their components.
+* Data Compiler: the data compiler will now print an error message instead of crashing when parsing malformed SJSON files.
 * Fixed destroying units with a script component.
 
 0.53.0 --- 30 Nov 2024

+ 136 - 48
src/core/json/sjson.cpp

@@ -13,25 +13,58 @@
 #include "core/strings/string.h"
 #include <errno.h>
 #include <stdlib.h> // strtod
+#include <stb_sprintf.h>
 
 namespace crown
 {
 namespace sjson
 {
-	static const char *next(const char *json, const char c = 0)
+	static void default_error(const char *msg, void *user_data)
 	{
-		CE_ENSURE(NULL != json);
+		CE_UNUSED(user_data);
+		CE_FATAL("%s", msg);
+	}
 
-		if (c && c != *json) {
-			CE_FATAL("Expected '%c' got '%c'", c, *json);
-		}
+	static thread_local SJsonError _error_function = default_error;
+	static thread_local void *_error_user_data;
+
+	void set_error_callback(SJsonError callback, void *user_data)
+	{
+		_error_function = callback;
+		_error_user_data = user_data;
+	}
 
-		return ++json;
+	void vfatal(const char *format, va_list args)
+	{
+		char msg[1024];
+		stbsp_vsnprintf(msg, sizeof(msg), format, args);
+		_error_function(msg, _error_user_data);
 	}
 
+	void fatal(const char *format, ...)
+	{
+		va_list args;
+		va_start(args, format);
+		vfatal(format, args);
+		va_end(args);
+	}
+
+#define NEXT_OR_RETURN(json, c, return_val)            \
+	do {                                               \
+		char _c = (c);                                 \
+		if (_c && _c != *json) {                       \
+			fatal("Expected '%c' got '%c'", _c, json); \
+			return return_val;                         \
+		}                                              \
+	} while (0);                                       \
+	++json
+
 	static const char *skip_string(const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return NULL;
+		}
 
 		while (*++json) {
 			if (*json == '"') {
@@ -47,22 +80,26 @@ namespace sjson
 
 	static const char *skip_comments(const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return NULL;
+		}
 
 		if (*json == '/') {
 			++json;
 			if (*json == '/') {
-				json = next(json, '/');
+				NEXT_OR_RETURN(json, '/', NULL);
 				while (*json && *json != '\n')
 					++json;
 			} else if (*json == '*') {
 				++json;
 				while (*json && *json != '*')
 					++json;
-				json = next(json, '*');
-				json = next(json, '/');
+				NEXT_OR_RETURN(json, '*', NULL);
+				NEXT_OR_RETURN(json, '/', NULL);
 			} else {
-				CE_FATAL("Bad comment");
+				fatal("Bad comment");
+				return NULL;
 			}
 		}
 
@@ -71,7 +108,10 @@ namespace sjson
 
 	static const char *skip_spaces(const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return NULL;
+		}
 
 		while (*json) {
 			if (*json == '/')
@@ -87,14 +127,20 @@ namespace sjson
 
 	static const char *skip_value(const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return NULL;
+		}
 
 		switch (*json) {
 		case '"':
 			json = skip_string(json);
 			if (*json == '"') {
 				json = strstr(json + 1, "\"\"\"");
-				CE_ENSURE(json);
+				if (json == NULL) {
+					fatal("Bad verbatim string");
+					return NULL;
+				}
 				json += 3;
 			}
 			break;
@@ -117,7 +163,10 @@ namespace sjson
 					json = skip_string(json);
 					if (*json == '"') {
 						json = strstr(json + 1, "\"\"\"");
-						CE_ENSURE(json);
+						if (json == NULL) {
+							fatal("Bad verbatim string");
+							return NULL;
+						}
 						json += 3;
 					}
 				} else if (*json == '/') {
@@ -136,7 +185,10 @@ namespace sjson
 
 	JsonValueType::Enum type(const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return JsonValueType::NIL;
+		}
 
 		switch (*json) {
 		case '"': return JsonValueType::STRING;
@@ -149,7 +201,11 @@ namespace sjson
 
 	static const char *parse_key(const char *json, DynamicString &key)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return NULL;
+		}
+
 		if (*json == '"') {
 			parse_string(key, json);
 			return skip_string(json);
@@ -162,13 +218,16 @@ namespace sjson
 			key += *json++;
 		}
 
-		CE_FATAL("Bad key");
+		fatal("Bad object key '%s'", key.c_str());
 		return NULL;
 	}
 
 	static f64 parse_number(const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return 0.0f;
+		}
 
 		TempAllocator512 alloc;
 		Array<char> number(alloc);
@@ -207,7 +266,10 @@ namespace sjson
 
 		errno = 0;
 		f64 val = strtod(array::begin(number), NULL);
-		CE_ASSERT(errno != ERANGE && errno != EINVAL, "Failed to parse f64: %s", array::begin(number));
+		if (errno == ERANGE || errno == EINVAL) {
+			fatal("Bad number '%s'", array::begin(number));
+			return 0.0f;
+		}
 		return val;
 	}
 
@@ -223,33 +285,41 @@ namespace sjson
 
 	bool parse_bool(const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return false;
+		}
 
 		switch (*json) {
 		case 't':
-			json = next(json, 't');
-			json = next(json, 'r');
-			json = next(json, 'u');
-			next(json, 'e');
+			NEXT_OR_RETURN(json, 't', false);
+			NEXT_OR_RETURN(json, 'r', false);
+			NEXT_OR_RETURN(json, 'u', false);
+			NEXT_OR_RETURN(json, 'e', false);
+			--json;
 			return true;
 
 		case 'f':
-			json = next(json, 'f');
-			json = next(json, 'a');
-			json = next(json, 'l');
-			json = next(json, 's');
-			next(json, 'e');
+			NEXT_OR_RETURN(json, 'f', false);
+			NEXT_OR_RETURN(json, 'a', false);
+			NEXT_OR_RETURN(json, 'l', false);
+			NEXT_OR_RETURN(json, 's', false);
+			NEXT_OR_RETURN(json, 'e', false);
+			--json;
 			return false;
 
 		default:
-			CE_FATAL("Bad boolean");
+			fatal("Bad boolean");
 			return false;
 		}
 	}
 
 	void parse_string(DynamicString &str, const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return;
+		}
 
 		if (*json == '"') {
 			while (*++json) {
@@ -268,7 +338,7 @@ namespace sjson
 					case 'n': str += '\n'; break;
 					case 'r': str += '\r'; break;
 					case 't': str += '\t'; break;
-					default: CE_FATAL("Bad escape character"); break;
+					default: fatal("Bad escape character"); return; break;
 					}
 				} else {
 					str += *json;
@@ -276,12 +346,15 @@ namespace sjson
 			}
 		}
 
-		CE_FATAL("Bad string");
+		fatal("Bad string");
 	}
 
 	void parse_array(JsonArray &arr, const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return;
+		}
 
 		if (*json == '[') {
 			json = skip_spaces(++json);
@@ -302,12 +375,15 @@ namespace sjson
 			}
 		}
 
-		CE_FATAL("Bad array");
+		fatal("Bad array");
 	}
 
 	static void parse_root_object(JsonObject &obj, const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return;
+		}
 
 		while (*json) {
 			const char *key_begin = *json == '"' ? (json + 1) : json;
@@ -319,7 +395,7 @@ namespace sjson
 			StringView fs_key(key_begin, key.length());
 
 			json = skip_spaces(json);
-			json = next(json, (*json == '=') ? '=' : ':');
+			NEXT_OR_RETURN(json, (*json == '=') ? '=' : ':', /*void*/);
 			json = skip_spaces(json);
 
 			hash_map::set(obj._map, fs_key, json);
@@ -331,7 +407,10 @@ namespace sjson
 
 	void parse_object(JsonObject &obj, const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return;
+		}
 
 		if (*json == '{') {
 			json = skip_spaces(++json);
@@ -349,7 +428,7 @@ namespace sjson
 				StringView fs_key(key_begin, key.length());
 
 				json = skip_spaces(json);
-				json = next(json, (*json == '=') ? '=' : ':');
+				NEXT_OR_RETURN(json, (*json == '=') ? '=' : ':', /*void*/);
 				json = skip_spaces(json);
 
 				hash_map::set(obj._map, fs_key, json);
@@ -364,12 +443,15 @@ namespace sjson
 			}
 		}
 
-		CE_FATAL("Bad object");
+		fatal("Bad object");
 	}
 
 	void parse(JsonObject &obj, const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return;
+		}
 
 		json = skip_spaces(json);
 
@@ -498,14 +580,20 @@ namespace sjson
 
 	void parse_verbatim(DynamicString &str, const char *json)
 	{
-		CE_ENSURE(NULL != json);
+		if (json == NULL) {
+			fatal("json is NULL");
+			return;
+		}
 
-		json = next(json, '"');
-		json = next(json, '"');
-		json = next(json, '"');
+		NEXT_OR_RETURN(json, '"', /*void*/);
+		NEXT_OR_RETURN(json, '"', /*void*/);
+		NEXT_OR_RETURN(json, '"', /*void*/);
 
 		const char *end = strstr(json, "\"\"\"");
-		CE_ASSERT(end, "Bad verbatim string");
+		if (end == NULL) {
+			fatal("Bad verbatim string");
+			return;
+		}
 
 		str.set(json, u32(end - json));
 	}

+ 6 - 0
src/core/json/sjson.h

@@ -12,11 +12,17 @@
 
 namespace crown
 {
+///
+typedef void (*SJsonError)(const char *msg, void *user_data);
+
 /// Functions to parse SJSON-encoded data.
 ///
 /// @ingroup JSON
 namespace sjson
 {
+	///
+	void set_error_callback(SJsonError callback, void *user_data);
+
 	/// Returns the data type of the SJSON string @a json.
 	JsonValueType::Enum type(const char *json);
 

+ 10 - 0
src/resource/compile_options.cpp

@@ -14,6 +14,7 @@
 #include "core/filesystem/path.h"
 #include "core/filesystem/reader_writer.inl"
 #include "core/guid.h"
+#include "core/json/sjson.h"
 #include "core/memory/temp_allocator.inl"
 #include "core/option.inl"
 #include "core/os.h"
@@ -36,6 +37,13 @@ static const char *s_platforms[] =
 };
 CE_STATIC_ASSERT(countof(s_platforms) == Platform::COUNT);
 
+static void sjson_error(const char *msg, void *user_data)
+{
+	CompileOptions *opts = (CompileOptions *)user_data;
+	opts->error("%s", msg);
+	opts->_sjson_error = true;
+}
+
 CompileOptions::CompileOptions(File &output
 	, HashMap<DynamicString, u32> &new_dependencies
 	, HashMap<DynamicString, u32> &new_requirements
@@ -59,7 +67,9 @@ CompileOptions::CompileOptions(File &output
 	, _resource_id(res_id)
 	, _bundle(bundle)
 	, _server(_data_compiler._options->_server)
+	, _sjson_error(false)
 {
+	sjson::set_error_callback(sjson_error, this);
 }
 
 void CompileOptions::error(const char *msg, va_list args)

+ 1 - 0
src/resource/compile_options.h

@@ -36,6 +36,7 @@ struct CompileOptions
 	ResourceId _resource_id;
 	bool _bundle;
 	bool _server;
+	bool _sjson_error;
 
 	///
 	CompileOptions(File &output

+ 7 - 0
src/resource/compile_options.inl

@@ -46,6 +46,13 @@
 		, name                                       \
 		)
 
+#define RETURN_IF_ERROR(sjson_func, opts) \
+	sjson_func;                           \
+	do {                                  \
+		if (opts._sjson_error)            \
+			return -1;                    \
+	} while (0)
+
 namespace crown
 {
 ///

+ 3 - 3
src/resource/config_resource.cpp

@@ -44,7 +44,7 @@ namespace config_resource_internal
 
 		TempAllocator1024 ta;
 		JsonObject boot(ta);
-		sjson::parse(boot, buf);
+		RETURN_IF_ERROR(sjson::parse(boot, buf), opts);
 
 		const char *boot_script_json  = boot["boot_script"];
 		const char *boot_package_json = boot["boot_package"];
@@ -53,8 +53,8 @@ namespace config_resource_internal
 
 		DynamicString boot_script(ta);
 		DynamicString boot_package(ta);
-		sjson::parse_string(boot_script, boot_script_json);
-		sjson::parse_string(boot_package, boot_package_json);
+		RETURN_IF_ERROR(sjson::parse_string(boot_script, boot_script_json), opts);
+		RETURN_IF_ERROR(sjson::parse_string(boot_package, boot_package_json), opts);
 		DATA_COMPILER_ASSERT_RESOURCE_EXISTS("lua", boot_script.c_str(), opts);
 		DATA_COMPILER_ASSERT_RESOURCE_EXISTS("package", boot_package.c_str(), opts);
 

+ 15 - 15
src/resource/font_resource.cpp

@@ -51,22 +51,22 @@ namespace font_resource_internal
 		}
 	};
 
-	s32 parse_glyphs(Array<GlyphInfo> &_glyphs, const JsonArray &glyphs)
+	s32 parse_glyphs(Array<GlyphInfo> &_glyphs, const JsonArray &glyphs, CompileOptions &opts)
 	{
 		for (u32 i = 0; i < array::size(glyphs); ++i) {
 			TempAllocator512 ta;
 			JsonObject obj(ta);
-			sjson::parse(obj, glyphs[i]);
+			RETURN_IF_ERROR(sjson::parse(obj, glyphs[i]), opts);
 
 			GlyphInfo gi;
-			gi.cp           = sjson::parse_int  (json_object::has(obj, "id") ? obj["id"] : obj["cp"]);
-			gi.gd.x         = sjson::parse_float(obj["x"]);
-			gi.gd.y         = sjson::parse_float(obj["y"]);
-			gi.gd.width     = sjson::parse_float(obj["width"]);
-			gi.gd.height    = sjson::parse_float(obj["height"]);
-			gi.gd.x_offset  = sjson::parse_float(obj["x_offset"]);
-			gi.gd.y_offset  = sjson::parse_float(obj["y_offset"]);
-			gi.gd.x_advance = sjson::parse_float(obj["x_advance"]);
+			gi.cp           = RETURN_IF_ERROR(sjson::parse_int  (json_object::has(obj, "id") ? obj["id"] : obj["cp"]), opts);
+			gi.gd.x         = RETURN_IF_ERROR(sjson::parse_float(obj["x"]), opts);
+			gi.gd.y         = RETURN_IF_ERROR(sjson::parse_float(obj["y"]), opts);
+			gi.gd.width     = RETURN_IF_ERROR(sjson::parse_float(obj["width"]), opts);
+			gi.gd.height    = RETURN_IF_ERROR(sjson::parse_float(obj["height"]), opts);
+			gi.gd.x_offset  = RETURN_IF_ERROR(sjson::parse_float(obj["x_offset"]), opts);
+			gi.gd.y_offset  = RETURN_IF_ERROR(sjson::parse_float(obj["y_offset"]), opts);
+			gi.gd.x_advance = RETURN_IF_ERROR(sjson::parse_float(obj["x_advance"]), opts);
 
 			array::push_back(_glyphs, gi);
 		}
@@ -82,11 +82,11 @@ namespace font_resource_internal
 		JsonObject obj(ta);
 		JsonArray glyphs(ta);
 
-		sjson::parse(obj, buf);
-		sjson::parse_array(glyphs, obj["glyphs"]);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
+		RETURN_IF_ERROR(sjson::parse_array(glyphs, obj["glyphs"]), opts);
 
-		const u32 texture_size = sjson::parse_int(obj["size"]);
-		const u32 font_size    = sjson::parse_int(obj["font_size"]);
+		const u32 texture_size = RETURN_IF_ERROR(sjson::parse_int(obj["size"]), opts);
+		const u32 font_size    = RETURN_IF_ERROR(sjson::parse_int(obj["font_size"]), opts);
 		DATA_COMPILER_ASSERT(font_size > 0
 			, opts
 			, "Font size must be > 0"
@@ -94,7 +94,7 @@ namespace font_resource_internal
 
 		s32 err = 0;
 		Array<GlyphInfo> _glyphs(default_allocator());
-		err = parse_glyphs(_glyphs, glyphs);
+		err = parse_glyphs(_glyphs, glyphs, opts);
 		DATA_COMPILER_ENSURE(err == 0, opts);
 		std::sort(array::begin(_glyphs), array::end(_glyphs));
 

+ 9 - 9
src/resource/level_resource.cpp

@@ -52,19 +52,19 @@ namespace level_resource_internal
 		Buffer buf = opts.read();
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, buf);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
 
 		Array<LevelSound> sounds(default_allocator());
 		{
 			JsonArray sounds_json(ta);
-			sjson::parse_array(sounds_json, obj["sounds"]);
+			RETURN_IF_ERROR(sjson::parse_array(sounds_json, obj["sounds"]), opts);
 
 			for (u32 i = 0; i < array::size(sounds_json); ++i) {
 				JsonObject sound(ta);
-				sjson::parse_object(sound, sounds_json[i]);
+				RETURN_IF_ERROR(sjson::parse_object(sound, sounds_json[i]), opts);
 
 				DynamicString sound_name(ta);
-				sjson::parse_string(sound_name, sound["name"]);
+				RETURN_IF_ERROR(sjson::parse_string(sound_name, sound["name"]), opts);
 				DATA_COMPILER_ASSERT_RESOURCE_EXISTS("sound"
 					, sound_name.c_str()
 					, opts
@@ -72,11 +72,11 @@ namespace level_resource_internal
 				opts.add_requirement("sound", sound_name.c_str());
 
 				LevelSound ls;
-				ls.name     = sjson::parse_resource_name(sound["name"]);
-				ls.position = sjson::parse_vector3      (sound["position"]);
-				ls.volume   = sjson::parse_float        (sound["volume"]);
-				ls.range    = sjson::parse_float        (sound["range"]);
-				ls.loop     = sjson::parse_bool         (sound["loop"]);
+				ls.name     = RETURN_IF_ERROR(sjson::parse_resource_name(sound["name"]), opts);
+				ls.position = RETURN_IF_ERROR(sjson::parse_vector3      (sound["position"]), opts);
+				ls.volume   = RETURN_IF_ERROR(sjson::parse_float        (sound["volume"]), opts);
+				ls.range    = RETURN_IF_ERROR(sjson::parse_float        (sound["range"]), opts);
+				ls.loop     = RETURN_IF_ERROR(sjson::parse_bool         (sound["loop"]), opts);
 
 				array::push_back(sounds, ls);
 			}

+ 19 - 15
src/resource/material_resource.cpp

@@ -148,7 +148,7 @@ namespace material_resource_internal
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		auto cur = json_object::begin(obj);
 		auto end = json_object::end(obj);
@@ -159,7 +159,7 @@ namespace material_resource_internal
 			const char *value    = cur->second;
 
 			DynamicString texture(ta);
-			sjson::parse_string(texture, value);
+			RETURN_IF_ERROR(sjson::parse_string(texture, value), opts);
 			DATA_COMPILER_ASSERT_RESOURCE_EXISTS("texture", texture.c_str(), opts);
 			opts.add_requirement("texture", texture.c_str());
 
@@ -174,7 +174,7 @@ namespace material_resource_internal
 			TextureData td;
 			td.sampler_name_offset = sampler_name_offset;
 			td.name                = StringId32(key.data(), key.length());
-			td.id                  = sjson::parse_resource_name(value);
+			td.id                  = RETURN_IF_ERROR(sjson::parse_resource_name(value), opts);
 			td.data_offset         = reserve_dynamic_data(dynamic, th);
 			td._pad1               = 0;
 
@@ -188,7 +188,7 @@ namespace material_resource_internal
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		auto cur = json_object::begin(obj);
 		auto end = json_object::end(obj);
@@ -202,10 +202,10 @@ namespace material_resource_internal
 			uh.uniform_handle = 0;
 
 			JsonObject uniform(ta);
-			sjson::parse_object(uniform, value);
+			RETURN_IF_ERROR(sjson::parse_object(uniform, value), opts);
 
 			DynamicString type(ta);
-			sjson::parse_string(type, uniform["type"]);
+			RETURN_IF_ERROR(sjson::parse_string(type, uniform["type"]), opts);
 
 			const UniformType::Enum ut = name_to_uniform_type(type.c_str());
 			DATA_COMPILER_ASSERT(ut != UniformType::COUNT
@@ -226,7 +226,7 @@ namespace material_resource_internal
 
 			switch (ud.type) {
 			case UniformType::FLOAT: {
-				const f32 value = sjson::parse_float(uniform["value"]);
+				const f32 value = RETURN_IF_ERROR(sjson::parse_float(uniform["value"]), opts);
 				Vector4 data;
 				data.x = value;
 				data.y = 0.0f;
@@ -237,7 +237,7 @@ namespace material_resource_internal
 			}
 
 			case UniformType::VECTOR2: {
-				const Vector2 value = sjson::parse_vector2(uniform["value"]);
+				const Vector2 value = RETURN_IF_ERROR(sjson::parse_vector2(uniform["value"]), opts);
 				Vector4 data;
 				data.x = value.x;
 				data.y = value.y;
@@ -248,7 +248,7 @@ namespace material_resource_internal
 			}
 
 			case UniformType::VECTOR3: {
-				const Vector3 value = sjson::parse_vector3(uniform["value"]);
+				const Vector3 value = RETURN_IF_ERROR(sjson::parse_vector3(uniform["value"]), opts);
 				Vector4 data;
 				data.x = value.x;
 				data.y = value.y;
@@ -258,13 +258,17 @@ namespace material_resource_internal
 				break;
 			}
 
-			case UniformType::VECTOR4:
-				reserve_dynamic_data(dynamic, sjson::parse_vector4(uniform["value"]));
+			case UniformType::VECTOR4: {
+				auto data = RETURN_IF_ERROR(sjson::parse_vector4(uniform["value"]), opts);
+				reserve_dynamic_data(dynamic, data);
 				break;
+			}
 
-			case UniformType::MATRIX4X4:
-				reserve_dynamic_data(dynamic, sjson::parse_matrix4x4(uniform["value"]));
+			case UniformType::MATRIX4X4: {
+				auto data = RETURN_IF_ERROR(sjson::parse_matrix4x4(uniform["value"]), opts);
+				reserve_dynamic_data(dynamic, data);
 				break;
+			}
 
 			default:
 				CE_FATAL("Unknown uniform type");
@@ -282,7 +286,7 @@ namespace material_resource_internal
 		Buffer buf = opts.read();
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, buf);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
 
 		Array<TextureData> texdata(default_allocator());
 		Array<UniformData> unidata(default_allocator());
@@ -290,7 +294,7 @@ namespace material_resource_internal
 		Array<char> dynblob(default_allocator());
 
 		DynamicString shader(ta);
-		sjson::parse_string(shader, obj["shader"]);
+		RETURN_IF_ERROR(sjson::parse_string(shader, obj["shader"]), opts);
 
 		if (json_object::has(obj, "textures")) {
 			s32 err = parse_textures(obj["textures"], texdata, names, dynblob, opts);

+ 38 - 28
src/resource/mesh_resource.cpp

@@ -185,26 +185,32 @@ namespace mesh_resource_internal
 #if CROWN_CAN_COMPILE
 namespace mesh
 {
-	static void parse_float_array(Array<f32> &output, const char *json)
+	static s32 parse_float_array(Array<f32> &output, const char *json, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonArray floats(ta);
-		sjson::parse_array(floats, json);
+		RETURN_IF_ERROR(sjson::parse_array(floats, json), opts);
 
 		array::resize(output, array::size(floats));
-		for (u32 i = 0; i < array::size(floats); ++i)
-			output[i] = sjson::parse_float(floats[i]);
+		for (u32 i = 0; i < array::size(floats); ++i) {
+			output[i] = RETURN_IF_ERROR(sjson::parse_float(floats[i]), opts);
+		}
+
+		return 0;
 	}
 
-	static void parse_index_array(Array<u16> &output, const char *json)
+	static s32 parse_index_array(Array<u16> &output, const char *json, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonArray indices(ta);
-		sjson::parse_array(indices, json);
+		RETURN_IF_ERROR(sjson::parse_array(indices, json), opts);
 
 		array::resize(output, array::size(indices));
-		for (u32 i = 0; i < array::size(indices); ++i)
-			output[i] = (u16)sjson::parse_int(indices[i]);
+		for (u32 i = 0; i < array::size(indices); ++i) {
+			output[i] = (u16)RETURN_IF_ERROR(sjson::parse_int(indices[i]), opts);
+		}
+
+		return 0;
 	}
 
 	s32 parse_nodes(Mesh &m, const char *sjson, CompileOptions &opts);
@@ -213,65 +219,69 @@ namespace mesh
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, sjson);
+		RETURN_IF_ERROR(sjson::parse(obj, sjson), opts);
 
-		n._local_pose = sjson::parse_matrix4x4(obj["matrix_local"]);
+		n._local_pose = RETURN_IF_ERROR(sjson::parse_matrix4x4(obj["matrix_local"]), opts);
 
 		if (json_object::has(obj, "children")) {
 			s32 err = mesh::parse_nodes(*mesh, obj["children"], opts);
 			DATA_COMPILER_ENSURE(err == 0, opts);
 		}
 
-		if (json_object::has(obj, "geometry"))
-			sjson::parse_string(n._geometry, obj["geometry"]);
+		if (json_object::has(obj, "geometry")) {
+			RETURN_IF_ERROR(sjson::parse_string(n._geometry, obj["geometry"]), opts);
+		}
 
 		return 0;
 	}
 
-	s32 parse_indices(Geometry &g, const char *json)
+	s32 parse_indices(Geometry &g, const char *json, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		JsonArray data_json(ta);
-		sjson::parse_array(data_json, obj["data"]);
+		RETURN_IF_ERROR(sjson::parse_array(data_json, obj["data"]), opts);
 
-		parse_index_array(g._position_indices, data_json[0]);
+		parse_index_array(g._position_indices, data_json[0], opts);
 
 		if (has_normals(g)) {
-			parse_index_array(g._normal_indices, data_json[1]);
+			parse_index_array(g._normal_indices, data_json[1], opts);
 		}
 		if (has_uvs(g)) {
-			parse_index_array(g._uv_indices, data_json[2]);
+			parse_index_array(g._uv_indices, data_json[2], opts);
 		}
 
 		return 0;
 	}
 
-	s32 parse_geometry(Geometry &g, const char *sjson)
+	s32 parse_geometry(Geometry &g, const char *sjson, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, sjson);
+		RETURN_IF_ERROR(sjson::parse(obj, sjson), opts);
 
-		parse_float_array(g._positions, obj["position"]);
+		s32 err = parse_float_array(g._positions, obj["position"], opts);
+		DATA_COMPILER_ENSURE(err == 0, opts);
 
 		if (json_object::has(obj, "normal")) {
-			parse_float_array(g._normals, obj["normal"]);
+			err = parse_float_array(g._normals, obj["normal"], opts);
+			DATA_COMPILER_ENSURE(err == 0, opts);
 		}
 		if (json_object::has(obj, "texcoord")) {
-			parse_float_array(g._uvs, obj["texcoord"]);
+			err = parse_float_array(g._uvs, obj["texcoord"], opts);
+			DATA_COMPILER_ENSURE(err == 0, opts);
 		}
 
-		return parse_indices(g, obj["indices"]);
+		return parse_indices(g, obj["indices"], opts);
 	}
 
 	s32 parse_geometries(Mesh &m, const char *sjson, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonObject geometries(ta);
-		sjson::parse(geometries, sjson);
+		RETURN_IF_ERROR(sjson::parse(geometries, sjson), opts);
 
 		auto cur = json_object::begin(geometries);
 		auto end = json_object::end(geometries);
@@ -279,7 +289,7 @@ namespace mesh
 			JSON_OBJECT_SKIP_HOLE(geometries, cur);
 
 			Geometry geo(default_allocator());
-			s32 err = mesh::parse_geometry(geo, cur->second);
+			s32 err = mesh::parse_geometry(geo, cur->second, opts);
 			DATA_COMPILER_ENSURE(err == 0, opts);
 
 			DynamicString geometry_name(ta);
@@ -299,7 +309,7 @@ namespace mesh
 	{
 		TempAllocator4096 ta;
 		JsonObject nodes(ta);
-		sjson::parse(nodes, sjson);
+		RETURN_IF_ERROR(sjson::parse(nodes, sjson), opts);
 
 		auto cur = json_object::begin(nodes);
 		auto end = json_object::end(nodes);
@@ -341,7 +351,7 @@ namespace mesh
 		TempAllocator4096 ta;
 		JsonObject nodes(ta);
 		JsonObject obj(ta);
-		sjson::parse(obj, buf);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
 
 		s32 err = mesh::parse_geometries(m, obj["geometries"], opts);
 		DATA_COMPILER_ENSURE(err == 0, opts);

+ 40 - 28
src/resource/package_resource.cpp

@@ -98,7 +98,7 @@ namespace package_resource_internal
 		for (u32 i = 0; i < array::size(names); ++i) {
 			TempAllocator256 ta;
 			DynamicString name(ta);
-			sjson::parse_string(name, names[i]);
+			RETURN_IF_ERROR(sjson::parse_string(name, names[i]), opts);
 			DATA_COMPILER_ASSERT_RESOURCE_EXISTS(type, name.c_str(), opts);
 			name += ".";
 			name += type;
@@ -106,7 +106,7 @@ namespace package_resource_internal
 
 			ResourceOffset ro;
 			ro.type = type_hash;
-			ro.name = sjson::parse_resource_name(names[i]);
+			ro.name = RETURN_IF_ERROR(sjson::parse_resource_name(names[i]), opts);
 
 			// Bring in requirements
 			u32 graph_level = 0;
@@ -141,32 +141,44 @@ namespace package_resource_internal
 		HashSet<ResourceOffset> resources_set(default_allocator());
 
 		Buffer buf = opts.read();
-		sjson::parse(obj, buf);
-
-		if (json_object::has(obj, "texture"))
-			sjson::parse_array(texture, obj["texture"]);
-		if (json_object::has(obj, "lua"))
-			sjson::parse_array(script, obj["lua"]);
-		if (json_object::has(obj, "sound"))
-			sjson::parse_array(sound, obj["sound"]);
-		if (json_object::has(obj, "mesh"))
-			sjson::parse_array(mesh, obj["mesh"]);
-		if (json_object::has(obj, "unit"))
-			sjson::parse_array(unit, obj["unit"]);
-		if (json_object::has(obj, "sprite"))
-			sjson::parse_array(sprite, obj["sprite"]);
-		if (json_object::has(obj, "material"))
-			sjson::parse_array(material, obj["material"]);
-		if (json_object::has(obj, "font"))
-			sjson::parse_array(font, obj["font"]);
-		if (json_object::has(obj, "level"))
-			sjson::parse_array(level, obj["level"]);
-		if (json_object::has(obj, "physics_config"))
-			sjson::parse_array(phyconf, obj["physics_config"]);
-		if (json_object::has(obj, "shader"))
-			sjson::parse_array(shader, obj["shader"]);
-		if (json_object::has(obj, "sprite_animation"))
-			sjson::parse_array(sprite_animation, obj["sprite_animation"]);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
+
+		if (json_object::has(obj, "texture")) {
+			RETURN_IF_ERROR(sjson::parse_array(texture, obj["texture"]), opts);
+		}
+		if (json_object::has(obj, "lua")) {
+			RETURN_IF_ERROR(sjson::parse_array(script, obj["lua"]), opts);
+		}
+		if (json_object::has(obj, "sound")) {
+			RETURN_IF_ERROR(sjson::parse_array(sound, obj["sound"]), opts);
+		}
+		if (json_object::has(obj, "mesh")) {
+			RETURN_IF_ERROR(sjson::parse_array(mesh, obj["mesh"]), opts);
+		}
+		if (json_object::has(obj, "unit")) {
+			RETURN_IF_ERROR(sjson::parse_array(unit, obj["unit"]), opts);
+		}
+		if (json_object::has(obj, "sprite")) {
+			RETURN_IF_ERROR(sjson::parse_array(sprite, obj["sprite"]), opts);
+		}
+		if (json_object::has(obj, "material")) {
+			RETURN_IF_ERROR(sjson::parse_array(material, obj["material"]), opts);
+		}
+		if (json_object::has(obj, "font")) {
+			RETURN_IF_ERROR(sjson::parse_array(font, obj["font"]), opts);
+		}
+		if (json_object::has(obj, "level")) {
+			RETURN_IF_ERROR(sjson::parse_array(level, obj["level"]), opts);
+		}
+		if (json_object::has(obj, "physics_config")) {
+			RETURN_IF_ERROR(sjson::parse_array(phyconf, obj["physics_config"]), opts);
+		}
+		if (json_object::has(obj, "shader")) {
+			RETURN_IF_ERROR(sjson::parse_array(shader, obj["shader"]), opts);
+		}
+		if (json_object::has(obj, "sprite_animation")) {
+			RETURN_IF_ERROR(sjson::parse_array(sprite_animation, obj["sprite_animation"]), opts);
+		}
 
 		s32 err = 0;
 		err = compile_resources(resources_set, opts, "texture", texture);

+ 110 - 74
src/resource/physics_resource.cpp

@@ -160,10 +160,10 @@ namespace physics_resource_internal
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		DynamicString type(ta);
-		sjson::parse_string(type, obj["shape"]);
+		RETURN_IF_ERROR(sjson::parse_string(type, obj["shape"]), opts);
 
 		ColliderType::Enum st = shape_type_to_enum(type.c_str());
 		DATA_COMPILER_ASSERT(st != ColliderType::COUNT
@@ -182,16 +182,17 @@ namespace physics_resource_internal
 		Array<u16> point_indices(default_allocator());
 
 		DynamicString source(ta);
-		if (json_object::has(obj, "source"))
-			sjson::parse_string(source, obj["source"]);
+		if (json_object::has(obj, "source")) {
+			RETURN_IF_ERROR(sjson::parse_string(source, obj["source"]), opts);
+		}
 		bool explicit_collider = source == "mesh"
 			|| (source != "inline" && json_object::has(obj, "scene"));
 
 		if (explicit_collider) {
 			DynamicString scene(ta);
 			DynamicString name(ta);
-			sjson::parse_string(scene, obj["scene"]);
-			sjson::parse_string(name, obj["name"]);
+			RETURN_IF_ERROR(sjson::parse_string(scene, obj["scene"]), opts);
+			RETURN_IF_ERROR(sjson::parse_string(name, obj["name"]), opts);
 
 			// Parse mesh resource.
 			Mesh mesh(default_allocator());
@@ -244,19 +245,19 @@ namespace physics_resource_internal
 				, opts
 				, "No collider_data found"
 				);
-			sjson::parse_object(collider_data, obj["collider_data"]);
-			Quaternion rotation = sjson::parse_quaternion(collider_data["rotation"]);
-			Vector3 position = sjson::parse_vector3(collider_data["position"]);
+			RETURN_IF_ERROR(sjson::parse_object(collider_data, obj["collider_data"]), opts);
+			Quaternion rotation = RETURN_IF_ERROR(sjson::parse_quaternion(collider_data["rotation"]), opts);
+			Vector3 position = RETURN_IF_ERROR(sjson::parse_vector3(collider_data["position"]), opts);
 			Matrix4x4 matrix_local = from_quaternion_translation(rotation, position);
 			cd.local_tm = matrix_local;
 
 			if (cd.type == ColliderType::SPHERE) {
-				cd.sphere.radius = sjson::parse_float(collider_data["radius"]);
+				cd.sphere.radius = RETURN_IF_ERROR(sjson::parse_float(collider_data["radius"]), opts);
 			} else if (cd.type == ColliderType::BOX) {
-				cd.box.half_size = sjson::parse_vector3(collider_data["half_extents"]);
+				cd.box.half_size = RETURN_IF_ERROR(sjson::parse_vector3(collider_data["half_extents"]), opts);
 			} else if (cd.type == ColliderType::CAPSULE) {
-				cd.capsule.radius = sjson::parse_float(collider_data["radius"]);
-				cd.capsule.height = sjson::parse_float(collider_data["height"]);
+				cd.capsule.radius = RETURN_IF_ERROR(sjson::parse_float(collider_data["radius"]), opts);
+				cd.capsule.height = RETURN_IF_ERROR(sjson::parse_float(collider_data["height"]), opts);
 			} else {
 				DATA_COMPILER_ASSERT(false, opts, "Invalid collider type");
 			}
@@ -299,32 +300,44 @@ namespace physics_resource_internal
 		return 0;
 	}
 
-	s32 compile_actor(Buffer &output, const char *json, CompileOptions & /*opts*/)
+	s32 compile_actor(Buffer &output, const char *json, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		u32 flags = 0;
-		if (json_object::has(obj, "lock_translation_x") && sjson::parse_bool(obj["lock_translation_x"]))
-			flags |= ActorFlags::LOCK_TRANSLATION_X;
-		if (json_object::has(obj, "lock_translation_y") && sjson::parse_bool(obj["lock_translation_y"]))
-			flags |= ActorFlags::LOCK_TRANSLATION_Y;
-		if (json_object::has(obj, "lock_translation_z") && sjson::parse_bool(obj["lock_translation_z"]))
-			flags |= ActorFlags::LOCK_TRANSLATION_Z;
-		if (json_object::has(obj, "lock_rotation_x") && sjson::parse_bool(obj["lock_rotation_x"]))
-			flags |= ActorFlags::LOCK_ROTATION_X;
-		if (json_object::has(obj, "lock_rotation_y") && sjson::parse_bool(obj["lock_rotation_y"]))
-			flags |= ActorFlags::LOCK_ROTATION_Y;
-		if (json_object::has(obj, "lock_rotation_z") && sjson::parse_bool(obj["lock_rotation_z"]))
-			flags |= ActorFlags::LOCK_ROTATION_Z;
+		if (json_object::has(obj, "lock_translation_x")) {
+			bool lock = RETURN_IF_ERROR(sjson::parse_bool(obj["lock_translation_x"]), opts);
+			flags |= lock ? ActorFlags::LOCK_TRANSLATION_X : 0u;
+		}
+		if (json_object::has(obj, "lock_translation_y")) {
+			bool lock = RETURN_IF_ERROR(sjson::parse_bool(obj["lock_translation_y"]), opts);
+			flags |= lock ? ActorFlags::LOCK_TRANSLATION_Y : 0u;
+		}
+		if (json_object::has(obj, "lock_translation_z")) {
+			bool lock = RETURN_IF_ERROR(sjson::parse_bool(obj["lock_translation_z"]), opts);
+			flags |= lock ? ActorFlags::LOCK_TRANSLATION_Z : 0u;
+		}
+		if (json_object::has(obj, "lock_rotation_x")) {
+			bool lock = RETURN_IF_ERROR(sjson::parse_bool(obj["lock_rotation_x"]), opts);
+			flags |= lock ? ActorFlags::LOCK_ROTATION_X : 0u;
+		}
+		if (json_object::has(obj, "lock_rotation_y")) {
+			bool lock = RETURN_IF_ERROR(sjson::parse_bool(obj["lock_rotation_y"]), opts);
+			flags |= lock ? ActorFlags::LOCK_ROTATION_Y : 0u;
+		}
+		if (json_object::has(obj, "lock_rotation_z")) {
+			bool lock = RETURN_IF_ERROR(sjson::parse_bool(obj["lock_rotation_z"]), opts);
+			flags |= lock ? ActorFlags::LOCK_ROTATION_Z : 0u;
+		}
 
 		ActorResource ar;
-		ar.actor_class      = sjson::parse_string_id(obj["class"]);
-		ar.mass             = sjson::parse_float    (obj["mass"]);
+		ar.actor_class      = RETURN_IF_ERROR(sjson::parse_string_id(obj["class"]), opts);
+		ar.mass             = RETURN_IF_ERROR(sjson::parse_float    (obj["mass"]), opts);
 		ar.flags            = flags;
-		ar.collision_filter = sjson::parse_string_id(obj["collision_filter"]);
-		ar.material         = sjson::parse_string_id(obj["material"]);
+		ar.collision_filter = RETURN_IF_ERROR(sjson::parse_string_id(obj["collision_filter"]), opts);
+		ar.material         = RETURN_IF_ERROR(sjson::parse_string_id(obj["material"]), opts);
 
 		FileBuffer fb(output);
 		BinaryWriter bw(fb);
@@ -340,10 +353,10 @@ namespace physics_resource_internal
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		DynamicString type(ta);
-		sjson::parse_string(type, obj["type"]);
+		RETURN_IF_ERROR(sjson::parse_string(type, obj["type"]), opts);
 
 		JointType::Enum jt = joint_type_to_enum(type.c_str());
 		DATA_COMPILER_ASSERT(jt != JointType::COUNT
@@ -354,17 +367,17 @@ namespace physics_resource_internal
 
 		JointDesc jd;
 		jd.type     = jt;
-		jd.anchor_0 = sjson::parse_vector3(obj["anchor_0"]);
-		jd.anchor_1 = sjson::parse_vector3(obj["anchor_1"]);
+		jd.anchor_0 = RETURN_IF_ERROR(sjson::parse_vector3(obj["anchor_0"]), opts);
+		jd.anchor_1 = RETURN_IF_ERROR(sjson::parse_vector3(obj["anchor_1"]), opts);
 
 		switch (jd.type) {
 		case JointType::HINGE:
-			jd.hinge.use_motor         = sjson::parse_bool (obj["use_motor"]);
-			jd.hinge.target_velocity   = sjson::parse_float(obj["target_velocity"]);
-			jd.hinge.max_motor_impulse = sjson::parse_float(obj["max_motor_impulse"]);
-			jd.hinge.lower_limit       = sjson::parse_float(obj["lower_limit"]);
-			jd.hinge.upper_limit       = sjson::parse_float(obj["upper_limit"]);
-			jd.hinge.bounciness        = sjson::parse_float(obj["bounciness"]);
+			jd.hinge.use_motor         = RETURN_IF_ERROR(sjson::parse_bool (obj["use_motor"]), opts);
+			jd.hinge.target_velocity   = RETURN_IF_ERROR(sjson::parse_float(obj["target_velocity"]), opts);
+			jd.hinge.max_motor_impulse = RETURN_IF_ERROR(sjson::parse_float(obj["max_motor_impulse"]), opts);
+			jd.hinge.lower_limit       = RETURN_IF_ERROR(sjson::parse_float(obj["lower_limit"]), opts);
+			jd.hinge.upper_limit       = RETURN_IF_ERROR(sjson::parse_float(obj["upper_limit"]), opts);
+			jd.hinge.bounciness        = RETURN_IF_ERROR(sjson::parse_float(obj["bounciness"]), opts);
 			break;
 		}
 
@@ -394,11 +407,11 @@ namespace physics_resource_internal
 
 namespace physics_config_resource_internal
 {
-	void parse_materials(const char *json, Array<PhysicsMaterial> &objects)
+	s32 parse_materials(const char *json, Array<PhysicsMaterial> &objects, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		auto cur = json_object::begin(obj);
 		auto end = json_object::end(obj);
@@ -409,23 +422,25 @@ namespace physics_config_resource_internal
 			const char *value    = cur->second;
 
 			JsonObject material(ta);
-			sjson::parse_object(material, value);
+			RETURN_IF_ERROR(sjson::parse_object(material, value), opts);
 
 			PhysicsMaterial mat;
 			mat.name             = StringId32(key.data(), key.length());
-			mat.friction         = sjson::parse_float(material["friction"]);
-			mat.rolling_friction = sjson::parse_float(material["rolling_friction"]);
-			mat.restitution      = sjson::parse_float(material["restitution"]);
+			mat.friction         = RETURN_IF_ERROR(sjson::parse_float(material["friction"]), opts);
+			mat.rolling_friction = RETURN_IF_ERROR(sjson::parse_float(material["rolling_friction"]), opts);
+			mat.restitution      = RETURN_IF_ERROR(sjson::parse_float(material["restitution"]), opts);
 
 			array::push_back(objects, mat);
 		}
+
+		return 0;
 	}
 
-	void parse_actors(const char *json, Array<PhysicsActor> &objects)
+	s32 parse_actors(const char *json, Array<PhysicsActor> &objects, CompileOptions &opts)
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, json);
+		RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 		auto cur = json_object::begin(obj);
 		auto end = json_object::end(obj);
@@ -436,30 +451,42 @@ namespace physics_config_resource_internal
 			const char *value    = cur->second;
 
 			JsonObject actor(ta);
-			sjson::parse_object(actor, value);
+			RETURN_IF_ERROR(sjson::parse_object(actor, value), opts);
 
 			PhysicsActor pa;
 			pa.name = StringId32(key.data(), key.length());
 			pa.linear_damping  = 0.0f;
 			pa.angular_damping = 0.0f;
 
-			if (json_object::has(actor, "linear_damping"))
-				pa.linear_damping = sjson::parse_float(actor["linear_damping"]);
-			if (json_object::has(actor, "angular_damping"))
-				pa.angular_damping = sjson::parse_float(actor["angular_damping"]);
+			if (json_object::has(actor, "linear_damping")) {
+				pa.linear_damping = RETURN_IF_ERROR(sjson::parse_float(actor["linear_damping"]), opts);
+			}
+			if (json_object::has(actor, "angular_damping")) {
+				pa.angular_damping = RETURN_IF_ERROR(sjson::parse_float(actor["angular_damping"]), opts);
+			}
 
 			pa.flags = 0;
-			if (json_object::has(actor, "dynamic") && sjson::parse_bool(actor["dynamic"]))
-				pa.flags |= CROWN_PHYSICS_ACTOR_DYNAMIC;
-			if (json_object::has(actor, "kinematic") && sjson::parse_bool(actor["kinematic"]))
-				pa.flags |= CROWN_PHYSICS_ACTOR_KINEMATIC;
-			if (json_object::has(actor, "disable_gravity") && sjson::parse_bool(actor["disable_gravity"]))
-				pa.flags |= CROWN_PHYSICS_ACTOR_DISABLE_GRAVITY;
-			if (json_object::has(actor, "trigger") && sjson::parse_bool(actor["trigger"]))
-				pa.flags |= CROWN_PHYSICS_ACTOR_TRIGGER;
+			if (json_object::has(actor, "dynamic")) {
+				bool val = RETURN_IF_ERROR(sjson::parse_bool(actor["dynamic"]), opts);
+				pa.flags |= val ? CROWN_PHYSICS_ACTOR_DYNAMIC : 0u;
+			}
+			if (json_object::has(actor, "kinematic")) {
+				bool val = RETURN_IF_ERROR(sjson::parse_bool(actor["kinematic"]), opts);
+				pa.flags |= val ? CROWN_PHYSICS_ACTOR_KINEMATIC : 0u;
+			}
+			if (json_object::has(actor, "disable_gravity")) {
+				bool val = RETURN_IF_ERROR(sjson::parse_bool(actor["disable_gravity"]), opts);
+				pa.flags |= val ? CROWN_PHYSICS_ACTOR_DISABLE_GRAVITY : 0u;
+			}
+			if (json_object::has(actor, "trigger")) {
+				bool val = RETURN_IF_ERROR(sjson::parse_bool(actor["trigger"]), opts);
+				pa.flags |= val ? CROWN_PHYSICS_ACTOR_TRIGGER : 0u;
+			}
 
 			array::push_back(objects, pa);
 		}
+
+		return 0;
 	}
 
 	struct CollisionFilterCompiler
@@ -477,11 +504,11 @@ namespace physics_config_resource_internal
 		{
 		}
 
-		void parse(const char *json)
+		s32 parse(const char *json)
 		{
 			TempAllocator4096 ta;
 			JsonObject obj(ta);
-			sjson::parse(obj, json);
+			RETURN_IF_ERROR(sjson::parse(obj, json), _opts);
 
 			auto cur = json_object::begin(obj);
 			auto end = json_object::end(obj);
@@ -505,14 +532,14 @@ namespace physics_config_resource_internal
 
 				TempAllocator4096 ta;
 				JsonObject filter(ta);
-				sjson::parse_object(filter, value);
+				RETURN_IF_ERROR(sjson::parse_object(filter, value), _opts);
 
 				JsonArray collides_with(ta);
-				sjson::parse_array(collides_with, filter["collides_with"]);
+				RETURN_IF_ERROR(sjson::parse_array(collides_with, filter["collides_with"]), _opts);
 
 				u32 mask = 0;
 				for (u32 i = 0; i < array::size(collides_with); ++i) {
-					const StringId32 fi = sjson::parse_string_id(collides_with[i]);
+					const StringId32 fi = RETURN_IF_ERROR(sjson::parse_string_id(collides_with[i]), _opts);
 					mask |= filter_to_mask(fi);
 				}
 
@@ -524,6 +551,8 @@ namespace physics_config_resource_internal
 
 				array::push_back(_filters, pcf);
 			}
+
+			return 0;
 		}
 
 		u32 new_filter_mask()
@@ -554,19 +583,26 @@ namespace physics_config_resource_internal
 		Buffer buf = opts.read();
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, buf);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
 
 		Array<PhysicsMaterial> materials(default_allocator());
 		Array<PhysicsActor> actors(default_allocator());
 		CollisionFilterCompiler cfc(opts);
 
 		// Parse materials
-		if (json_object::has(obj, "collision_filters"))
-			cfc.parse(obj["collision_filters"]);
-		if (json_object::has(obj, "materials"))
-			parse_materials(obj["materials"], materials);
-		if (json_object::has(obj, "actors"))
-			parse_actors(obj["actors"], actors);
+		s32 err = 0;
+		if (json_object::has(obj, "collision_filters")) {
+			err = cfc.parse(obj["collision_filters"]);
+			DATA_COMPILER_ENSURE(err == 0, opts);
+		}
+		if (json_object::has(obj, "materials")) {
+			err = parse_materials(obj["materials"], materials, opts);
+			DATA_COMPILER_ENSURE(err == 0, opts);
+		}
+		if (json_object::has(obj, "actors")) {
+			err = parse_actors(obj["actors"], actors, opts);
+			DATA_COMPILER_ENSURE(err == 0, opts);
+		}
 
 		// Setup struct for writing
 		PhysicsConfigResource pcr;

+ 89 - 72
src/resource/shader_resource.cpp

@@ -822,15 +822,15 @@ namespace shader_resource_internal
 		{
 			TempAllocator4096 ta;
 			JsonObject obj(ta);
-			sjson::parse(obj, buf);
+			RETURN_IF_ERROR(sjson::parse(obj, buf), _opts);
 
 			if (json_object::has(obj, "include")) {
 				JsonArray arr(ta);
-				sjson::parse_array(arr, obj["include"]);
+				RETURN_IF_ERROR(sjson::parse_array(arr, obj["include"]), _opts);
 
 				for (u32 i = 0; i < array::size(arr); ++i) {
 					DynamicString path(ta);
-					sjson::parse_string(path, arr[i]);
+					RETURN_IF_ERROR(sjson::parse_string(path, arr[i]), _opts);
 					parse(path.c_str());
 				}
 			}
@@ -878,33 +878,38 @@ namespace shader_resource_internal
 
 			if (json_object::has(obj, "rgb_write_enable")) {
 				logw(SHADER_RESOURCE, warn_msg);
-				state._rgb_write_enable.set_value(sjson::parse_bool(obj["rgb_write_enable"]));
+				bool enable = RETURN_IF_ERROR(sjson::parse_bool(obj["rgb_write_enable"]), _opts);
+				state._rgb_write_enable.set_value(enable);
 			}
 
 			if (json_object::has(obj, "alpha_write_enable")) {
 				logw(SHADER_RESOURCE, warn_msg);
-				state._alpha_write_enable.set_value(sjson::parse_bool(obj["alpha_write_enable"]));
+				bool enable = RETURN_IF_ERROR(sjson::parse_bool(obj["alpha_write_enable"]), _opts);
+				state._alpha_write_enable.set_value(enable);
 			}
 
 			if (json_object::has(obj, "depth_write_enable")) {
 				logw(SHADER_RESOURCE, warn_msg);
-				state._depth_write_enable.set_value(sjson::parse_bool(obj["depth_write_enable"]));
+				bool enable = RETURN_IF_ERROR(sjson::parse_bool(obj["depth_write_enable"]), _opts);
+				state._depth_write_enable.set_value(enable);
 			}
 
 			if (json_object::has(obj, "depth_enable")) {
 				logw(SHADER_RESOURCE, warn_msg);
-				state._depth_enable.set_value(sjson::parse_bool(obj["depth_enable"]));
+				bool enable = RETURN_IF_ERROR(sjson::parse_bool(obj["depth_enable"]), _opts);
+				state._depth_enable.set_value(enable);
 			}
 
 			if (json_object::has(obj, "blend_enable")) {
 				logw(SHADER_RESOURCE, warn_msg);
-				state._blend_enable.set_value(sjson::parse_bool(obj["blend_enable"]));
+				bool enable = RETURN_IF_ERROR(sjson::parse_bool(obj["blend_enable"]), _opts);
+				state._blend_enable.set_value(enable);
 			}
 
 			if (json_object::has(obj, "depth_func")) {
 				logw(SHADER_RESOURCE, warn_msg);
 				DynamicString depth_func(ta);
-				sjson::parse_string(depth_func, obj["depth_func"]);
+				RETURN_IF_ERROR(sjson::parse_string(depth_func, obj["depth_func"]), _opts);
 				state._depth_func.set_value(name_to_depth_func(depth_func.c_str()));
 				DATA_COMPILER_ASSERT(state._depth_func.value() != DepthFunction::COUNT
 					, _opts
@@ -916,7 +921,7 @@ namespace shader_resource_internal
 			if (json_object::has(obj, "blend_src")) {
 				logw(SHADER_RESOURCE, warn_msg);
 				DynamicString blend_src(ta);
-				sjson::parse_string(blend_src, obj["blend_src"]);
+				RETURN_IF_ERROR(sjson::parse_string(blend_src, obj["blend_src"]), _opts);
 				state._blend_src.set_value(name_to_blend_function(blend_src.c_str()));
 				DATA_COMPILER_ASSERT(state._blend_src.value() != BlendFunction::COUNT
 					, _opts
@@ -928,7 +933,7 @@ namespace shader_resource_internal
 			if (json_object::has(obj, "blend_dst")) {
 				logw(SHADER_RESOURCE, warn_msg);
 				DynamicString blend_dst(ta);
-				sjson::parse_string(blend_dst, obj["blend_dst"]);
+				RETURN_IF_ERROR(sjson::parse_string(blend_dst, obj["blend_dst"]), _opts);
 				state._blend_dst.set_value(name_to_blend_function(blend_dst.c_str()));
 				DATA_COMPILER_ASSERT(state._blend_dst.value() != BlendFunction::COUNT
 					, _opts
@@ -940,7 +945,7 @@ namespace shader_resource_internal
 			if (json_object::has(obj, "blend_equation")) {
 				logw(SHADER_RESOURCE, warn_msg);
 				DynamicString blend_equation(ta);
-				sjson::parse_string(blend_equation, obj["blend_equation"]);
+				RETURN_IF_ERROR(sjson::parse_string(blend_equation, obj["blend_equation"]), _opts);
 				state._blend_equation.set_value(name_to_blend_equation(blend_equation.c_str()));
 				DATA_COMPILER_ASSERT(state._blend_equation.value() != BlendEquation::COUNT
 					, _opts
@@ -952,7 +957,7 @@ namespace shader_resource_internal
 			if (json_object::has(obj, "cull_mode")) {
 				logw(SHADER_RESOURCE, warn_msg);
 				DynamicString cull_mode(ta);
-				sjson::parse_string(cull_mode, obj["cull_mode"]);
+				RETURN_IF_ERROR(sjson::parse_string(cull_mode, obj["cull_mode"]), _opts);
 				state._cull_mode.set_value(name_to_cull_mode(cull_mode.c_str()));
 				DATA_COMPILER_ASSERT(state._cull_mode.value() != CullMode::COUNT
 					, _opts
@@ -964,7 +969,7 @@ namespace shader_resource_internal
 			if (json_object::has(obj, "primitive_type")) {
 				logw(SHADER_RESOURCE, warn_msg);
 				DynamicString primitive_type(ta);
-				sjson::parse_string(primitive_type, obj["primitive_type"]);
+				RETURN_IF_ERROR(sjson::parse_string(primitive_type, obj["primitive_type"]), _opts);
 				state._primitive_type.set_value(name_to_primitive_type(primitive_type.c_str()));
 				DATA_COMPILER_ASSERT(state._primitive_type.value() != PrimitiveType::COUNT
 					, _opts
@@ -991,7 +996,7 @@ namespace shader_resource_internal
 
 			TempAllocator4096 ta;
 			JsonObject states(ta);
-			sjson::parse_object(states, json);
+			RETURN_IF_ERROR(sjson::parse_object(states, json), _opts);
 
 			auto cur = json_object::begin(states);
 			auto end = json_object::end(states);
@@ -1000,18 +1005,23 @@ namespace shader_resource_internal
 
 				// It must be a regular key/value state.
 				if (cur->first == "rgb_write_enable") {
-					state._rgb_write_enable.set_value(sjson::parse_bool(states["rgb_write_enable"]));
+					bool enable = RETURN_IF_ERROR(sjson::parse_bool(states["rgb_write_enable"]), _opts);
+					state._rgb_write_enable.set_value(enable);
 				} else if (cur->first == "alpha_write_enable") {
-					state._alpha_write_enable.set_value(sjson::parse_bool(states["alpha_write_enable"]));
+					bool enable = RETURN_IF_ERROR(sjson::parse_bool(states["alpha_write_enable"]), _opts);
+					state._alpha_write_enable.set_value(enable);
 				} else if (cur->first == "depth_write_enable") {
-					state._depth_write_enable.set_value(sjson::parse_bool(states["depth_write_enable"]));
+					bool enable = RETURN_IF_ERROR(sjson::parse_bool(states["depth_write_enable"]), _opts);
+					state._depth_write_enable.set_value(enable);
 				} else if (cur->first == "depth_enable") {
-					state._depth_enable.set_value(sjson::parse_bool(states["depth_enable"]));
+					bool enable = RETURN_IF_ERROR(sjson::parse_bool(states["depth_enable"]), _opts);
+					state._depth_enable.set_value(enable);
 				} else if (cur->first == "blend_enable") {
-					state._blend_enable.set_value(sjson::parse_bool(states["blend_enable"]));
+					bool enable = RETURN_IF_ERROR(sjson::parse_bool(states["blend_enable"]), _opts);
+					state._blend_enable.set_value(enable);
 				} else if (cur->first == "depth_func") {
 					DynamicString depth_func(ta);
-					sjson::parse_string(depth_func, states["depth_func"]);
+					RETURN_IF_ERROR(sjson::parse_string(depth_func, states["depth_func"]), _opts);
 					state._depth_func.set_value(name_to_depth_func(depth_func.c_str()));
 					DATA_COMPILER_ASSERT(state._depth_func.value() != DepthFunction::COUNT
 						, _opts
@@ -1020,7 +1030,7 @@ namespace shader_resource_internal
 						);
 				} else if (cur->first == "blend_src") {
 					DynamicString blend_src(ta);
-					sjson::parse_string(blend_src, states["blend_src"]);
+					RETURN_IF_ERROR(sjson::parse_string(blend_src, states["blend_src"]), _opts);
 					state._blend_src.set_value(name_to_blend_function(blend_src.c_str()));
 					DATA_COMPILER_ASSERT(state._blend_src.value() != BlendFunction::COUNT
 						, _opts
@@ -1029,7 +1039,7 @@ namespace shader_resource_internal
 						);
 				} else if (cur->first == "blend_dst") {
 					DynamicString blend_dst(ta);
-					sjson::parse_string(blend_dst, states["blend_dst"]);
+					RETURN_IF_ERROR(sjson::parse_string(blend_dst, states["blend_dst"]), _opts);
 					state._blend_dst.set_value(name_to_blend_function(blend_dst.c_str()));
 					DATA_COMPILER_ASSERT(state._blend_dst.value() != BlendFunction::COUNT
 						, _opts
@@ -1038,7 +1048,7 @@ namespace shader_resource_internal
 						);
 				} else if (cur->first == "blend_equation") {
 					DynamicString blend_equation(ta);
-					sjson::parse_string(blend_equation, states["blend_equation"]);
+					RETURN_IF_ERROR(sjson::parse_string(blend_equation, states["blend_equation"]), _opts);
 					state._blend_equation.set_value(name_to_blend_equation(blend_equation.c_str()));
 					DATA_COMPILER_ASSERT(state._blend_equation.value() != BlendEquation::COUNT
 						, _opts
@@ -1047,7 +1057,7 @@ namespace shader_resource_internal
 						);
 				} else if (cur->first == "cull_mode") {
 					DynamicString cull_mode(ta);
-					sjson::parse_string(cull_mode, states["cull_mode"]);
+					RETURN_IF_ERROR(sjson::parse_string(cull_mode, states["cull_mode"]), _opts);
 					state._cull_mode.set_value(name_to_cull_mode(cull_mode.c_str()));
 					DATA_COMPILER_ASSERT(state._cull_mode.value() != CullMode::COUNT
 						, _opts
@@ -1056,7 +1066,7 @@ namespace shader_resource_internal
 						);
 				} else if (cur->first == "primitive_type") {
 					DynamicString primitive_type(ta);
-					sjson::parse_string(primitive_type, states["primitive_type"]);
+					RETURN_IF_ERROR(sjson::parse_string(primitive_type, states["primitive_type"]), _opts);
 					state._primitive_type.set_value(name_to_primitive_type(primitive_type.c_str()));
 					DATA_COMPILER_ASSERT(state._primitive_type.value() != PrimitiveType::COUNT
 						, _opts
@@ -1094,7 +1104,7 @@ namespace shader_resource_internal
 
 			TempAllocator4096 ta;
 			JsonObject obj(ta);
-			sjson::parse_object(obj, json);
+			RETURN_IF_ERROR(sjson::parse_object(obj, json), _opts);
 
 			// Read conditional states.
 			auto cur = json_object::begin(obj);
@@ -1132,12 +1142,13 @@ namespace shader_resource_internal
 
 			TempAllocator4096 ta;
 			JsonObject obj(ta);
-			sjson::parse_object(obj, json);
+			RETURN_IF_ERROR(sjson::parse_object(obj, json), _opts);
 			s32 err = 0;
 
 			// Read inherit render state if any.
-			if (json_object::has(obj, "inherit"))
-				sjson::parse_string(rs._inherit, obj["inherit"]);
+			if (json_object::has(obj, "inherit")) {
+				RETURN_IF_ERROR(sjson::parse_string(rs._inherit, obj["inherit"]), _opts);
+			}
 
 			// Read states from render state object itself; for backwards compatibility.
 			RenderState::State states_compat;
@@ -1162,7 +1173,7 @@ namespace shader_resource_internal
 		{
 			TempAllocator4096 ta;
 			JsonObject render_states(ta);
-			sjson::parse_object(render_states, json);
+			RETURN_IF_ERROR(sjson::parse_object(render_states, json), _opts);
 
 			auto cur = json_object::begin(render_states);
 			auto end = json_object::end(render_states);
@@ -1170,7 +1181,7 @@ namespace shader_resource_internal
 				JSON_OBJECT_SKIP_HOLE(render_states, cur);
 
 				JsonObject obj(ta);
-				sjson::parse_object(obj, cur->second);
+				RETURN_IF_ERROR(sjson::parse_object(obj, cur->second), _opts);
 
 				RenderState rs(default_allocator());
 				s32 err = parse_render_state(rs, cur->second);
@@ -1194,7 +1205,7 @@ namespace shader_resource_internal
 		{
 			TempAllocator4096 ta;
 			JsonObject sampler_states(ta);
-			sjson::parse_object(sampler_states, json);
+			RETURN_IF_ERROR(sjson::parse_object(sampler_states, json), _opts);
 
 			auto cur = json_object::begin(sampler_states);
 			auto end = json_object::end(sampler_states);
@@ -1202,7 +1213,7 @@ namespace shader_resource_internal
 				JSON_OBJECT_SKIP_HOLE(sampler_states, cur);
 
 				JsonObject obj(ta);
-				sjson::parse_object(obj, cur->second);
+				RETURN_IF_ERROR(sjson::parse_object(obj, cur->second), _opts);
 
 				const bool has_filter_min = json_object::has(obj, "filter_min");
 				const bool has_filter_mag = json_object::has(obj, "filter_mag");
@@ -1220,7 +1231,7 @@ namespace shader_resource_internal
 				DynamicString wrap_w(ta);
 
 				if (has_filter_min) {
-					sjson::parse_string(filter_min, obj["filter_min"]);
+					RETURN_IF_ERROR(sjson::parse_string(filter_min, obj["filter_min"]), _opts);
 					ss._filter_min = name_to_sampler_filter(filter_min.c_str());
 					DATA_COMPILER_ASSERT(ss._filter_min != SamplerFilter::COUNT
 						, _opts
@@ -1230,7 +1241,7 @@ namespace shader_resource_internal
 				}
 
 				if (has_filter_mag) {
-					sjson::parse_string(filter_mag, obj["filter_mag"]);
+					RETURN_IF_ERROR(sjson::parse_string(filter_mag, obj["filter_mag"]), _opts);
 					ss._filter_mag = name_to_sampler_filter(filter_mag.c_str());
 					DATA_COMPILER_ASSERT(ss._filter_mag != SamplerFilter::COUNT
 						, _opts
@@ -1240,7 +1251,7 @@ namespace shader_resource_internal
 				}
 
 				if (has_wrap_u) {
-					sjson::parse_string(wrap_u, obj["wrap_u"]);
+					RETURN_IF_ERROR(sjson::parse_string(wrap_u, obj["wrap_u"]), _opts);
 					ss._wrap_u = name_to_sampler_wrap(wrap_u.c_str());
 					DATA_COMPILER_ASSERT(ss._wrap_u != SamplerWrap::COUNT
 						, _opts
@@ -1250,7 +1261,7 @@ namespace shader_resource_internal
 				}
 
 				if (has_wrap_v) {
-					sjson::parse_string(wrap_v, obj["wrap_v"]);
+					RETURN_IF_ERROR(sjson::parse_string(wrap_v, obj["wrap_v"]), _opts);
 					ss._wrap_v = name_to_sampler_wrap(wrap_v.c_str());
 					DATA_COMPILER_ASSERT(ss._wrap_v != SamplerWrap::COUNT
 						, _opts
@@ -1260,7 +1271,7 @@ namespace shader_resource_internal
 				}
 
 				if (has_wrap_w) {
-					sjson::parse_string(wrap_w, obj["wrap_w"]);
+					RETURN_IF_ERROR(sjson::parse_string(wrap_w, obj["wrap_w"]), _opts);
 					ss._wrap_w = name_to_sampler_wrap(wrap_w.c_str());
 					DATA_COMPILER_ASSERT(ss._wrap_w != SamplerWrap::COUNT
 						, _opts
@@ -1287,7 +1298,7 @@ namespace shader_resource_internal
 		{
 			TempAllocator4096 ta;
 			JsonObject bgfx_shaders(ta);
-			sjson::parse_object(bgfx_shaders, json);
+			RETURN_IF_ERROR(sjson::parse_object(bgfx_shaders, json), _opts);
 
 			auto cur = json_object::begin(bgfx_shaders);
 			auto end = json_object::end(bgfx_shaders);
@@ -1295,28 +1306,34 @@ namespace shader_resource_internal
 				JSON_OBJECT_SKIP_HOLE(bgfx_shaders, cur);
 
 				JsonObject shader(ta);
-				sjson::parse_object(shader, cur->second);
+				RETURN_IF_ERROR(sjson::parse_object(shader, cur->second), _opts);
 
-				s32 err = 0;
 				BgfxShader bgfxshader(default_allocator());
-				if (json_object::has(shader, "includes"))
-					sjson::parse_string(bgfxshader._includes, shader["includes"]);
-				if (json_object::has(shader, "code"))
-					sjson::parse_verbatim(bgfxshader._code, shader["code"]);
-				if (json_object::has(shader, "vs_code"))
-					sjson::parse_verbatim(bgfxshader._vs_code, shader["vs_code"]);
-				if (json_object::has(shader, "fs_code"))
-					sjson::parse_verbatim(bgfxshader._fs_code, shader["fs_code"]);
-				if (json_object::has(shader, "varying"))
-					sjson::parse_verbatim(bgfxshader._varying, shader["varying"]);
-				if (json_object::has(shader, "vs_input_output"))
-					sjson::parse_verbatim(bgfxshader._vs_input_output, shader["vs_input_output"]);
-				if (json_object::has(shader, "fs_input_output"))
-					sjson::parse_verbatim(bgfxshader._fs_input_output, shader["fs_input_output"]);
-				if (json_object::has(shader, "samplers"))
-					err = parse_bgfx_samplers(bgfxshader, shader["samplers"]);
-
-				DATA_COMPILER_ENSURE(err == 0, _opts);
+				if (json_object::has(shader, "includes")) {
+					RETURN_IF_ERROR(sjson::parse_string(bgfxshader._includes, shader["includes"]), _opts);
+				}
+				if (json_object::has(shader, "code")) {
+					RETURN_IF_ERROR(sjson::parse_verbatim(bgfxshader._code, shader["code"]), _opts);
+				}
+				if (json_object::has(shader, "vs_code")) {
+					RETURN_IF_ERROR(sjson::parse_verbatim(bgfxshader._vs_code, shader["vs_code"]), _opts);
+				}
+				if (json_object::has(shader, "fs_code")) {
+					RETURN_IF_ERROR(sjson::parse_verbatim(bgfxshader._fs_code, shader["fs_code"]), _opts);
+				}
+				if (json_object::has(shader, "varying")) {
+					RETURN_IF_ERROR(sjson::parse_verbatim(bgfxshader._varying, shader["varying"]), _opts);
+				}
+				if (json_object::has(shader, "vs_input_output")) {
+					RETURN_IF_ERROR(sjson::parse_verbatim(bgfxshader._vs_input_output, shader["vs_input_output"]), _opts);
+				}
+				if (json_object::has(shader, "fs_input_output")) {
+					RETURN_IF_ERROR(sjson::parse_verbatim(bgfxshader._fs_input_output, shader["fs_input_output"]), _opts);
+				}
+				if (json_object::has(shader, "samplers")) {
+					s32 err = parse_bgfx_samplers(bgfxshader, shader["samplers"]);
+					DATA_COMPILER_ENSURE(err == 0, _opts);
+				}
 
 				DynamicString key(ta);
 				key = cur->first;
@@ -1336,7 +1353,7 @@ namespace shader_resource_internal
 		{
 			TempAllocator4096 ta;
 			JsonObject bgfx_samplers(ta);
-			sjson::parse_object(bgfx_samplers, json);
+			RETURN_IF_ERROR(sjson::parse_object(bgfx_samplers, json), _opts);
 
 			auto cur = json_object::begin(bgfx_samplers);
 			auto end = json_object::end(bgfx_samplers);
@@ -1344,10 +1361,10 @@ namespace shader_resource_internal
 				JSON_OBJECT_SKIP_HOLE(bgfx_samplers, cur);
 
 				JsonObject sampler(ta);
-				sjson::parse_object(sampler, cur->second);
+				RETURN_IF_ERROR(sjson::parse_object(sampler, cur->second), _opts);
 
 				DynamicString sampler_state(ta);
-				sjson::parse_string(sampler_state, sampler["sampler_state"]);
+				RETURN_IF_ERROR(sjson::parse_string(sampler_state, sampler["sampler_state"]), _opts);
 
 				DATA_COMPILER_ASSERT(hash_map::has(_sampler_states, sampler_state)
 					, _opts
@@ -1373,7 +1390,7 @@ namespace shader_resource_internal
 		{
 			TempAllocator4096 ta;
 			JsonObject shaders(ta);
-			sjson::parse_object(shaders, json);
+			RETURN_IF_ERROR(sjson::parse_object(shaders, json), _opts);
 
 			auto cur = json_object::begin(shaders);
 			auto end = json_object::end(shaders);
@@ -1381,11 +1398,11 @@ namespace shader_resource_internal
 				JSON_OBJECT_SKIP_HOLE(shaders, cur);
 
 				JsonObject obj(ta);
-				sjson::parse_object(obj, cur->second);
+				RETURN_IF_ERROR(sjson::parse_object(obj, cur->second), _opts);
 
 				ShaderPermutation shader(default_allocator());
-				sjson::parse_string(shader._bgfx_shader, obj["bgfx_shader"]);
-				sjson::parse_string(shader._render_state, obj["render_state"]);
+				RETURN_IF_ERROR(sjson::parse_string(shader._bgfx_shader, obj["bgfx_shader"]), _opts);
+				RETURN_IF_ERROR(sjson::parse_string(shader._render_state, obj["render_state"]), _opts);
 
 				DynamicString key(ta);
 				key = cur->first;
@@ -1405,20 +1422,20 @@ namespace shader_resource_internal
 		{
 			TempAllocator4096 ta;
 			JsonArray static_compile(ta);
-			sjson::parse_array(static_compile, json);
+			RETURN_IF_ERROR(sjson::parse_array(static_compile, json), _opts);
 
 			for (u32 ii = 0; ii < array::size(static_compile); ++ii) {
 				JsonObject obj(ta);
-				sjson::parse_object(obj, static_compile[ii]);
+				RETURN_IF_ERROR(sjson::parse_object(obj, static_compile[ii]), _opts);
 
 				StaticCompile sc(default_allocator());
-				sjson::parse_string(sc._shader, obj["shader"]);
+				RETURN_IF_ERROR(sjson::parse_string(sc._shader, obj["shader"]), _opts);
 
 				JsonArray defines(ta);
-				sjson::parse_array(defines, obj["defines"]);
+				RETURN_IF_ERROR(sjson::parse_array(defines, obj["defines"]), _opts);
 				for (u32 jj = 0; jj < array::size(defines); ++jj) {
 					DynamicString def(ta);
-					sjson::parse_string(def, defines[jj]);
+					RETURN_IF_ERROR(sjson::parse_string(def, defines[jj]), _opts);
 					vector::push_back(sc._defines, def);
 				}
 

+ 2 - 2
src/resource/sound_resource.cpp

@@ -51,10 +51,10 @@ namespace sound_resource_internal
 
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, buf);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
 
 		DynamicString name(ta);
-		sjson::parse_string(name, obj["source"]);
+		RETURN_IF_ERROR(sjson::parse_string(name, obj["source"]), opts);
 
 		Buffer sound = opts.read(name.c_str());
 		const WAVHeader *wav = (const WAVHeader *)array::begin(sound);

+ 27 - 22
src/resource/sprite_resource.cpp

@@ -43,22 +43,23 @@ namespace sprite_resource_internal
 		Vector2 pivot;  // [x, y]
 	};
 
-	void parse_frames(Array<SpriteFrame> &sprite_frames, const JsonArray &frames)
+	s32 parse_frames(Array<SpriteFrame> &sprite_frames, const JsonArray &frames, CompileOptions &opts)
 	{
 		for (u32 ii = 0; ii < array::size(frames); ++ii) {
 			TempAllocator512 ta;
 			JsonObject obj(ta);
-			sjson::parse(obj, frames[ii]);
+			RETURN_IF_ERROR(sjson::parse(obj, frames[ii]), opts);
 
 			SpriteFrame sf;
-			sf.name   = sjson::parse_string_id(obj["name"]);
-			sf.region = sjson::parse_vector4(obj["region"]);
-			sf.pivot  = sjson::parse_vector2(obj["pivot"]);
+			sf.name   = RETURN_IF_ERROR(sjson::parse_string_id(obj["name"]), opts);
+			sf.region = RETURN_IF_ERROR(sjson::parse_vector4(obj["region"]), opts);
+			sf.pivot  = RETURN_IF_ERROR(sjson::parse_vector2(obj["pivot"]), opts);
 
-			if (json_object::has(obj, "index"))
-				sf.index = sjson::parse_int(obj["index"]);
-			else
+			if (json_object::has(obj, "index")) {
+				sf.index = RETURN_IF_ERROR(sjson::parse_int(obj["index"]), opts);
+			} else {
 				sf.index = ii;
+			}
 
 			array::push_back(sprite_frames, sf);
 		}
@@ -68,6 +69,8 @@ namespace sprite_resource_internal
 			, [](const SpriteFrame &frame_a, const SpriteFrame &frame_b) {
 				return frame_a.index < frame_b.index;
 			});
+
+		return 0;
 	}
 
 	s32 compile(CompileOptions &opts)
@@ -76,19 +79,20 @@ namespace sprite_resource_internal
 
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, buf);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
 
 		JsonArray frames(ta);
-		sjson::parse_array(frames, obj["frames"]);
+		RETURN_IF_ERROR(sjson::parse_array(frames, obj["frames"]), opts);
 
 		// Read width/height
-		const f32 width      = sjson::parse_float(obj["width"]);
-		const f32 height     = sjson::parse_float(obj["height"]);
+		const f32 width      = RETURN_IF_ERROR(sjson::parse_float(obj["width"]), opts);
+		const f32 height     = RETURN_IF_ERROR(sjson::parse_float(obj["height"]), opts);
 		const u32 num_frames = array::size(frames);
 
 		// Parse frames.
 		Array<SpriteFrame> sprite_frames(default_allocator());
-		parse_frames(sprite_frames, frames);
+		s32 err = parse_frames(sprite_frames, frames, opts);
+		DATA_COMPILER_ENSURE(err == 0, opts);
 
 		// Fill verices.
 		Array<f32> vertices(default_allocator());
@@ -208,24 +212,25 @@ namespace sprite_animation_resource_internal
 		Array<SpriteAnimationFrame> frames(default_allocator());
 		float total_time = 0.0f;
 
-		sjson::parse(obj, buf);
-		sjson::parse_array(object_frames, obj["frames"]);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
+		RETURN_IF_ERROR(sjson::parse_array(object_frames, obj["frames"]), opts);
 
 		array::resize(frames, array::size(object_frames));
 		for (u32 i = 0; i < array::size(object_frames); ++i) {
 			if (sjson::type(object_frames[i]) == JsonValueType::NUMBER) {
-				frames[i].frame = (u32)sjson::parse_float(object_frames[i]);
+				frames[i].frame = (u32)RETURN_IF_ERROR(sjson::parse_float(object_frames[i]), opts);
 				frames[i].index = i;
 			} else {
 				JsonObject animation_frame_obj(ta);
-				sjson::parse_object(animation_frame_obj, object_frames[i]);
+				RETURN_IF_ERROR(sjson::parse_object(animation_frame_obj, object_frames[i]), opts);
 
-				if (json_object::has(animation_frame_obj, "frame"))
-					frames[i].frame = (u32)sjson::parse_float(animation_frame_obj["frame"]);
-				else
+				if (json_object::has(animation_frame_obj, "frame")) {
+					frames[i].frame = (u32)RETURN_IF_ERROR(sjson::parse_float(animation_frame_obj["frame"]), opts);
+				} else {
 					frames[i].frame = 0; // Crown 0.48 shipped with malformed .sprite_animtion files.
+				}
 
-				frames[i].index = (u32)sjson::parse_float(animation_frame_obj["index"]);
+				frames[i].index = (u32)RETURN_IF_ERROR(sjson::parse_float(animation_frame_obj["index"]), opts);
 			}
 		}
 
@@ -235,7 +240,7 @@ namespace sprite_animation_resource_internal
 				return frame_a.index < frame_b.index;
 			});
 
-		total_time = sjson::parse_float(obj["total_time"]);
+		total_time = RETURN_IF_ERROR(sjson::parse_float(obj["total_time"]), opts);
 
 		// Write
 		SpriteAnimationResource sar;

+ 23 - 23
src/resource/state_machine_resource.cpp

@@ -242,10 +242,10 @@ namespace state_machine_internal
 			for (u32 i = 0; i < array::size(animations); ++i) {
 				TempAllocator4096 ta;
 				JsonObject animation(ta);
-				sjson::parse_object(animation, animations[i]);
+				RETURN_IF_ERROR(sjson::parse_object(animation, animations[i]), _opts);
 
 				DynamicString animation_resource(ta);
-				sjson::parse_string(animation_resource, animation["name"]);
+				RETURN_IF_ERROR(sjson::parse_string(animation_resource, animation["name"]), _opts);
 				DATA_COMPILER_ASSERT_RESOURCE_EXISTS("sprite_animation"
 					, animation_resource.c_str()
 					, _opts
@@ -253,8 +253,8 @@ namespace state_machine_internal
 				_opts.add_requirement("sprite_animation", animation_resource.c_str());
 
 				AnimationInfo ai(ta);
-				ai.name = sjson::parse_resource_name(animation["name"]);
-				sjson::parse_string(ai.weight, animation["weight"]);
+				ai.name = RETURN_IF_ERROR(sjson::parse_resource_name(animation["name"]), _opts);
+				RETURN_IF_ERROR(sjson::parse_string(ai.weight, animation["weight"]), _opts);
 
 				vector::push_back(si.animations, ai);
 			}
@@ -267,10 +267,10 @@ namespace state_machine_internal
 			for (u32 i = 0; i < array::size(transitions); ++i) {
 				TempAllocator4096 ta;
 				JsonObject transition(ta);
-				sjson::parse_object(transition, transitions[i]);
+				RETURN_IF_ERROR(sjson::parse_object(transition, transitions[i]), _opts);
 
 				DynamicString mode_str(ta);
-				sjson::parse_string(mode_str, transition["mode"]);
+				RETURN_IF_ERROR(sjson::parse_string(mode_str, transition["mode"]), _opts);
 				const u32 mode = name_to_transition_mode(mode_str.c_str());
 				DATA_COMPILER_ASSERT(mode != TransitionMode::COUNT
 					, _opts
@@ -279,10 +279,10 @@ namespace state_machine_internal
 					);
 
 				TransitionInfo ti;
-				ti.transition.event        = sjson::parse_string_id(transition["event"]);
+				ti.transition.event        = RETURN_IF_ERROR(sjson::parse_string_id(transition["event"]), _opts);
 				ti.transition.state_offset = UINT32_MAX;
 				ti.transition.mode         = mode;
-				ti.state                   = sjson::parse_guid(transition["to"]);
+				ti.state                   = RETURN_IF_ERROR(sjson::parse_guid(transition["to"]), _opts);
 
 				vector::push_back(si.transitions, ti);
 			}
@@ -297,13 +297,13 @@ namespace state_machine_internal
 				JsonObject state(ta);
 				JsonArray animations(ta);
 				JsonArray transitions(ta);
-				sjson::parse_object(state, states[i]);
-				sjson::parse_array(animations, state["animations"]);
-				sjson::parse_array(transitions, state["transitions"]);
+				RETURN_IF_ERROR(sjson::parse_object(state, states[i]), _opts);
+				RETURN_IF_ERROR(sjson::parse_array(animations, state["animations"]), _opts);
+				RETURN_IF_ERROR(sjson::parse_array(transitions, state["transitions"]), _opts);
 
 				StateInfo si(ta);
-				sjson::parse_string(si.speed, state["speed"]);
-				si.loop = sjson::parse_bool(state["loop"]);
+				RETURN_IF_ERROR(sjson::parse_string(si.speed, state["speed"]), _opts);
+				si.loop = RETURN_IF_ERROR(sjson::parse_bool(state["loop"]), _opts);
 
 				s32 err = 0;
 				err = parse_transitions(si, transitions);
@@ -313,9 +313,9 @@ namespace state_machine_internal
 
 				Guid guid;
 				if (json_object::has(state, "id")) {
-					guid = sjson::parse_guid(state["id"]);
+					guid = RETURN_IF_ERROR(sjson::parse_guid(state["id"]), _opts);
 				} else {
-					guid = sjson::parse_guid(state["_guid"]);
+					guid = RETURN_IF_ERROR(sjson::parse_guid(state["_guid"]), _opts);
 				}
 
 				DATA_COMPILER_ASSERT(!hash_map::has(_states, guid)
@@ -333,12 +333,12 @@ namespace state_machine_internal
 			for (u32 i = 0; i < array::size(variables); ++i) {
 				TempAllocator4096 ta;
 				JsonObject variable(ta);
-				sjson::parse_object(variable, variables[i]);
+				RETURN_IF_ERROR(sjson::parse_object(variable, variables[i]), _opts);
 
 				VariableInfo vi(ta);
-				vi.name  = sjson::parse_string_id(variable["name"]);
-				vi.value = sjson::parse_float(variable["value"]);
-				sjson::parse_string(vi.name_string, variable["name"]);
+				vi.name  = RETURN_IF_ERROR(sjson::parse_string_id(variable["name"]), _opts);
+				vi.value = RETURN_IF_ERROR(sjson::parse_float(variable["value"]), _opts);
+				RETURN_IF_ERROR(sjson::parse_string(vi.name_string, variable["name"]), _opts);
 
 				vector::push_back(_variables, vi);
 			}
@@ -423,9 +423,9 @@ namespace state_machine_internal
 			JsonArray states(ta);
 			JsonArray variables(ta);
 
-			sjson::parse(obj, buf);
-			sjson::parse_array(states, obj["states"]);
-			sjson::parse_array(variables, obj["variables"]);
+			RETURN_IF_ERROR(sjson::parse(obj, buf), _opts);
+			RETURN_IF_ERROR(sjson::parse_array(states, obj["states"]), _opts);
+			RETURN_IF_ERROR(sjson::parse_array(variables, obj["variables"]), _opts);
 
 			s32 err = 0;
 			err = parse_states(states);
@@ -435,7 +435,7 @@ namespace state_machine_internal
 				, "States cannot be empty"
 				);
 
-			_initial_state = sjson::parse_guid(obj["initial_state"]);
+			_initial_state = RETURN_IF_ERROR(sjson::parse_guid(obj["initial_state"]), _opts);
 			DATA_COMPILER_ASSERT(hash_map::has(_states, _initial_state)
 				, _opts
 				, "Initial state references non-existing state"

+ 20 - 15
src/resource/texture_resource.cpp

@@ -147,11 +147,11 @@ namespace texture_resource_internal
 		if (json_object::has(output, platform)) {
 			TempAllocator1024 ta;
 			JsonObject obj(ta);
-			sjson::parse_object(obj, output[platform]);
+			RETURN_IF_ERROR(sjson::parse_object(obj, output[platform]), opts);
 
 			DynamicString format(ta);
 			if (json_object::has(obj, "format")) {
-				sjson::parse_string(format, obj["format"]);
+				RETURN_IF_ERROR(sjson::parse_string(format, obj["format"]), opts);
 				os.format = texture_format_to_enum(format.c_str());
 				DATA_COMPILER_ASSERT(os.format != TextureFormat::COUNT
 					, opts
@@ -159,12 +159,15 @@ namespace texture_resource_internal
 					, format.c_str()
 					);
 			}
-			if (json_object::has(obj, "generate_mips"))
-				os.generate_mips     = sjson::parse_bool(obj["generate_mips"]);
-			if (json_object::has(obj, "mip_skip_smallest"))
-				os.mip_skip_smallest = sjson::parse_int (obj["mip_skip_smallest"]);
-			if (json_object::has(obj, "normal_map"))
-				os.normal_map        = sjson::parse_bool(obj["normal_map"]);
+			if (json_object::has(obj, "generate_mips")) {
+				os.generate_mips     = RETURN_IF_ERROR(sjson::parse_bool(obj["generate_mips"]), opts);
+			}
+			if (json_object::has(obj, "mip_skip_smallest")) {
+				os.mip_skip_smallest = RETURN_IF_ERROR(sjson::parse_int (obj["mip_skip_smallest"]), opts);
+			}
+			if (json_object::has(obj, "normal_map")) {
+				os.normal_map        = RETURN_IF_ERROR(sjson::parse_bool(obj["normal_map"]), opts);
+			}
 		}
 
 		return 0;
@@ -176,23 +179,25 @@ namespace texture_resource_internal
 
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, buf);
+		RETURN_IF_ERROR(sjson::parse(obj, buf), opts);
 
 		DynamicString name(ta);
-		sjson::parse_string(name, obj["source"]);
+		RETURN_IF_ERROR(sjson::parse_string(name, obj["source"]), opts);
 		DATA_COMPILER_ASSERT_FILE_EXISTS(name.c_str(), opts);
 		opts.fake_read(name.c_str());
 
 		OutputSettings os;
 
-		if (json_object::has(obj, "generate_mips"))
-			os.generate_mips = sjson::parse_bool(obj["generate_mips"]);
-		if (json_object::has(obj, "normal_map"))
-			os.normal_map = sjson::parse_bool(obj["normal_map"]);
+		if (json_object::has(obj, "generate_mips")) {
+			os.generate_mips = RETURN_IF_ERROR(sjson::parse_bool(obj["generate_mips"]), opts);
+		}
+		if (json_object::has(obj, "normal_map")) {
+			os.normal_map = RETURN_IF_ERROR(sjson::parse_bool(obj["normal_map"]), opts);
+		}
 
 		JsonObject output(ta);
 		if (json_object::has(obj, "output")) {
-			sjson::parse_object(output, obj["output"]);
+			RETURN_IF_ERROR(sjson::parse_object(output, obj["output"]), opts);
 			s32 err = parse_output(os, output, opts);
 			DATA_COMPILER_ENSURE(err == 0, opts);
 		}

+ 71 - 68
src/resource/unit_compiler.cpp

@@ -73,16 +73,16 @@ static LightType::Enum light_name_to_enum(const char *name)
 	return LightType::COUNT;
 }
 
-static s32 compile_transform(Buffer &output, const char *json, CompileOptions & /*opts*/)
+static s32 compile_transform(Buffer &output, const char *json, CompileOptions &opts)
 {
 	TempAllocator4096 ta;
 	JsonObject obj(ta);
-	sjson::parse(obj, json);
+	RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 	TransformDesc td;
-	td.position = sjson::parse_vector3   (obj["position"]);
-	td.rotation = sjson::parse_quaternion(obj["rotation"]);
-	td.scale    = sjson::parse_vector3   (obj["scale"]);
+	td.position = RETURN_IF_ERROR(sjson::parse_vector3   (obj["position"]), opts);
+	td.rotation = RETURN_IF_ERROR(sjson::parse_quaternion(obj["rotation"]), opts);
+	td.scale    = RETURN_IF_ERROR(sjson::parse_vector3   (obj["scale"]), opts);
 
 	FileBuffer fb(output);
 	BinaryWriter bw(fb);
@@ -96,10 +96,10 @@ static s32 compile_camera(Buffer &output, const char *json, CompileOptions &opts
 {
 	TempAllocator4096 ta;
 	JsonObject obj(ta);
-	sjson::parse(obj, json);
+	RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 	DynamicString type(ta);
-	sjson::parse_string(type, obj["projection"]);
+	RETURN_IF_ERROR(sjson::parse_string(type, obj["projection"]), opts);
 
 	ProjectionType::Enum pt = projection_name_to_enum(type.c_str());
 	DATA_COMPILER_ASSERT(pt != ProjectionType::COUNT
@@ -110,9 +110,9 @@ static s32 compile_camera(Buffer &output, const char *json, CompileOptions &opts
 
 	CameraDesc cd;
 	cd.type       = pt;
-	cd.fov        = sjson::parse_float(obj["fov"]);
-	cd.near_range = sjson::parse_float(obj["near_range"]);
-	cd.far_range  = sjson::parse_float(obj["far_range"]);
+	cd.fov        = RETURN_IF_ERROR(sjson::parse_float(obj["fov"]), opts);
+	cd.near_range = RETURN_IF_ERROR(sjson::parse_float(obj["near_range"]), opts);
+	cd.far_range  = RETURN_IF_ERROR(sjson::parse_float(obj["far_range"]), opts);
 
 	FileBuffer fb(output);
 	BinaryWriter bw(fb);
@@ -127,10 +127,10 @@ static s32 compile_mesh_renderer(Buffer &output, const char *json, CompileOption
 {
 	TempAllocator4096 ta;
 	JsonObject obj(ta);
-	sjson::parse(obj, json);
+	RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 	DynamicString mesh_resource(ta);
-	sjson::parse_string(mesh_resource, obj["mesh_resource"]);
+	RETURN_IF_ERROR(sjson::parse_string(mesh_resource, obj["mesh_resource"]), opts);
 
 	DATA_COMPILER_ENSURE(opts.resource_exists("mesh", mesh_resource.c_str())
 		|| opts.file_exists(mesh_resource.c_str())
@@ -145,7 +145,7 @@ static s32 compile_mesh_renderer(Buffer &output, const char *json, CompileOption
 	opts.add_requirement("mesh", mesh_resource.c_str());
 
 	DynamicString material(ta);
-	sjson::parse_string(material, obj["material"]);
+	RETURN_IF_ERROR(sjson::parse_string(material, obj["material"]), opts);
 	DATA_COMPILER_ASSERT_RESOURCE_EXISTS("material"
 		, material.c_str()
 		, opts
@@ -154,9 +154,9 @@ static s32 compile_mesh_renderer(Buffer &output, const char *json, CompileOption
 
 	MeshRendererDesc mrd;
 	mrd.mesh_resource     = StringId64(mesh_resource.c_str());
-	mrd.material_resource = sjson::parse_resource_name(obj["material"]);
-	mrd.geometry_name     = sjson::parse_string_id    (obj["geometry_name"]);
-	mrd.visible           = sjson::parse_bool         (obj["visible"]);
+	mrd.material_resource = RETURN_IF_ERROR(sjson::parse_resource_name(obj["material"]), opts);
+	mrd.geometry_name     = RETURN_IF_ERROR(sjson::parse_string_id    (obj["geometry_name"]), opts);
+	mrd.visible           = RETURN_IF_ERROR(sjson::parse_bool         (obj["visible"]), opts);
 	mrd._pad0[0]          = 0;
 	mrd._pad0[1]          = 0;
 	mrd._pad0[2]          = 0;
@@ -177,10 +177,10 @@ static s32 compile_sprite_renderer(Buffer &output, const char *json, CompileOpti
 {
 	TempAllocator4096 ta;
 	JsonObject obj(ta);
-	sjson::parse(obj, json);
+	RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 	DynamicString sprite_resource(ta);
-	sjson::parse_string(sprite_resource, obj["sprite_resource"]);
+	RETURN_IF_ERROR(sjson::parse_string(sprite_resource, obj["sprite_resource"]), opts);
 	DATA_COMPILER_ASSERT_RESOURCE_EXISTS("sprite"
 		, sprite_resource.c_str()
 		, opts
@@ -188,7 +188,7 @@ static s32 compile_sprite_renderer(Buffer &output, const char *json, CompileOpti
 	opts.add_requirement("sprite", sprite_resource.c_str());
 
 	DynamicString material(ta);
-	sjson::parse_string(material, obj["material"]);
+	RETURN_IF_ERROR(sjson::parse_string(material, obj["material"]), opts);
 	DATA_COMPILER_ASSERT_RESOURCE_EXISTS("material"
 		, material.c_str()
 		, opts
@@ -196,11 +196,11 @@ static s32 compile_sprite_renderer(Buffer &output, const char *json, CompileOpti
 	opts.add_requirement("material", material.c_str());
 
 	SpriteRendererDesc srd;
-	srd.sprite_resource   = sjson::parse_resource_name(obj["sprite_resource"]);
-	srd.material_resource = sjson::parse_resource_name(obj["material"]);
-	srd.layer             = sjson::parse_int          (obj["layer"]);
-	srd.depth             = sjson::parse_int          (obj["depth"]);
-	srd.visible           = sjson::parse_bool         (obj["visible"]);
+	srd.sprite_resource   = RETURN_IF_ERROR(sjson::parse_resource_name(obj["sprite_resource"]), opts);
+	srd.material_resource = RETURN_IF_ERROR(sjson::parse_resource_name(obj["material"]), opts);
+	srd.layer             = RETURN_IF_ERROR(sjson::parse_int          (obj["layer"]), opts);
+	srd.depth             = RETURN_IF_ERROR(sjson::parse_int          (obj["depth"]), opts);
+	srd.visible           = RETURN_IF_ERROR(sjson::parse_bool         (obj["visible"]), opts);
 	srd._pad0[0]          = 0;
 	srd._pad0[1]          = 0;
 	srd._pad0[2]          = 0;
@@ -230,10 +230,10 @@ static s32 compile_light(Buffer &output, const char *json, CompileOptions &opts)
 {
 	TempAllocator4096 ta;
 	JsonObject obj(ta);
-	sjson::parse(obj, json);
+	RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 	DynamicString type(ta);
-	sjson::parse_string(type, obj["type"]);
+	RETURN_IF_ERROR(sjson::parse_string(type, obj["type"]), opts);
 
 	LightType::Enum lt = light_name_to_enum(type.c_str());
 	DATA_COMPILER_ASSERT(lt != LightType::COUNT
@@ -244,10 +244,10 @@ static s32 compile_light(Buffer &output, const char *json, CompileOptions &opts)
 
 	LightDesc ld;
 	ld.type       = lt;
-	ld.range      = sjson::parse_float  (obj["range"]);
-	ld.intensity  = sjson::parse_float  (obj["intensity"]);
-	ld.spot_angle = sjson::parse_float  (obj["spot_angle"]);
-	ld.color      = sjson::parse_vector3(obj["color"]);
+	ld.range      = RETURN_IF_ERROR(sjson::parse_float  (obj["range"]), opts);
+	ld.intensity  = RETURN_IF_ERROR(sjson::parse_float  (obj["intensity"]), opts);
+	ld.spot_angle = RETURN_IF_ERROR(sjson::parse_float  (obj["spot_angle"]), opts);
+	ld.color      = RETURN_IF_ERROR(sjson::parse_vector3(obj["color"]), opts);
 
 	FileBuffer fb(output);
 	BinaryWriter bw(fb);
@@ -263,10 +263,10 @@ static s32 compile_script(Buffer &output, const char *json, CompileOptions &opts
 {
 	TempAllocator4096 ta;
 	JsonObject obj(ta);
-	sjson::parse(obj, json);
+	RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 	DynamicString script_resource(ta);
-	sjson::parse_string(script_resource, obj["script_resource"]);
+	RETURN_IF_ERROR(sjson::parse_string(script_resource, obj["script_resource"]), opts);
 	DATA_COMPILER_ASSERT_RESOURCE_EXISTS("lua"
 		, script_resource.c_str()
 		, opts
@@ -274,7 +274,7 @@ static s32 compile_script(Buffer &output, const char *json, CompileOptions &opts
 	opts.add_requirement("lua", script_resource.c_str());
 
 	ScriptDesc sd;
-	sd.script_resource = sjson::parse_resource_name(obj["script_resource"]);
+	sd.script_resource = RETURN_IF_ERROR(sjson::parse_resource_name(obj["script_resource"]), opts);
 
 	FileBuffer fb(output);
 	BinaryWriter bw(fb);
@@ -286,10 +286,10 @@ static s32 compile_animation_state_machine(Buffer &output, const char *json, Com
 {
 	TempAllocator4096 ta;
 	JsonObject obj(ta);
-	sjson::parse(obj, json);
+	RETURN_IF_ERROR(sjson::parse(obj, json), opts);
 
 	DynamicString state_machine_resource(ta);
-	sjson::parse_string(state_machine_resource, obj["state_machine_resource"]);
+	RETURN_IF_ERROR(sjson::parse_string(state_machine_resource, obj["state_machine_resource"]), opts);
 	DATA_COMPILER_ASSERT_RESOURCE_EXISTS("state_machine"
 		, state_machine_resource.c_str()
 		, opts
@@ -297,7 +297,7 @@ static s32 compile_animation_state_machine(Buffer &output, const char *json, Com
 	opts.add_requirement("state_machine", state_machine_resource.c_str());
 
 	AnimationStateMachineDesc asmd;
-	asmd.state_machine_resource = sjson::parse_resource_name(obj["state_machine_resource"]);
+	asmd.state_machine_resource = RETURN_IF_ERROR(sjson::parse_resource_name(obj["state_machine_resource"]), opts);
 
 	FileBuffer fb(output);
 	BinaryWriter bw(fb);
@@ -335,11 +335,11 @@ namespace unit_compiler
 	{
 		TempAllocator4096 ta;
 		JsonObject prefab(ta);
-		sjson::parse(prefab, unit_json);
+		RETURN_IF_ERROR(RETURN_IF_ERROR(sjson::parse(prefab, unit_json), c._opts), c._opts);
 
 		if (json_object::has(prefab, "children")) {
 			JsonArray children(ta);
-			sjson::parse_array(children, prefab["children"]);
+			RETURN_IF_ERROR(sjson::parse_array(children, prefab["children"]), c._opts);
 
 			for (u32 i = 0; i < array::size(children); ++i) {
 				s32 err = collect_prefabs(c, unit_name, children[i], false);
@@ -350,7 +350,7 @@ namespace unit_compiler
 		if (json_object::has(prefab, "prefab")) {
 			TempAllocator512 ta;
 			DynamicString path(ta);
-			sjson::parse_string(path, prefab["prefab"]);
+			RETURN_IF_ERROR(sjson::parse_string(path, prefab["prefab"]), c._opts);
 			DATA_COMPILER_ASSERT_RESOURCE_EXISTS("unit"
 				, path.c_str()
 				, c._opts
@@ -385,18 +385,20 @@ namespace unit_compiler
 		return NULL;
 	}
 
-	u32 object_index(const JsonArray &objects, const Guid &object_id)
+	u32 object_index(const JsonArray &objects, const Guid &object_id, CompileOptions &opts)
 	{
 		for (u32 i = 0; i < array::size(objects); ++i) {
 			TempAllocator512 ta;
 			JsonObject obj(ta);
-			sjson::parse(obj, objects[i]);
+			RETURN_IF_ERROR(sjson::parse(obj, objects[i]), opts);
 
 			if (json_object::has(obj, "id")) {
-				if (sjson::parse_guid(obj["id"]) == object_id)
+				Guid id = RETURN_IF_ERROR(sjson::parse_guid(obj["id"]), opts);
+				if (id == object_id)
 					return i;
 			} else {
-				if (sjson::parse_guid(obj["_guid"]) == object_id)
+				Guid id = RETURN_IF_ERROR(sjson::parse_guid(obj["_guid"]), opts);
+				if (id == object_id)
 					return i;
 			}
 		}
@@ -441,16 +443,16 @@ namespace unit_compiler
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, unit_json);
+		RETURN_IF_ERROR(sjson::parse(obj, unit_json), c._opts);
 
 		if (json_object::has(obj, "components")) {
 			JsonArray components(ta);
-			sjson::parse_array(components, obj["components"]);
+			RETURN_IF_ERROR(sjson::parse_array(components, obj["components"]), c._opts);
 
 			// Add components.
 			for (u32 cc = 0; cc < array::size(components); ++cc) {
 				JsonObject component(ta);
-				sjson::parse_object(component, components[cc]);
+				RETURN_IF_ERROR(sjson::parse_object(component, components[cc]), c._opts);
 
 				array::push_back(unit->_merged_components, components[cc]);
 				array::push_back(unit->_merged_components_data, component["data"]);
@@ -459,7 +461,7 @@ namespace unit_compiler
 
 		if (json_object::has(obj, "deleted_components")) {
 			JsonObject deleted_components(ta);
-			sjson::parse_object(deleted_components, obj["deleted_components"]);
+			RETURN_IF_ERROR(sjson::parse_object(deleted_components, obj["deleted_components"]), c._opts);
 
 			// Delete components.
 			auto cur = json_object::begin(deleted_components);
@@ -475,7 +477,7 @@ namespace unit_compiler
 				guid[36] = '\0';
 				Guid component_id = guid::parse(guid);
 
-				u32 comp_idx = object_index(unit->_merged_components, component_id);
+				u32 comp_idx = object_index(unit->_merged_components, component_id, c._opts);
 				if (comp_idx != UINT32_MAX) {
 					u32 comp_last = array::size(unit->_merged_components) - 1;
 					unit->_merged_components[comp_idx] = unit->_merged_components[comp_last];
@@ -495,7 +497,7 @@ namespace unit_compiler
 
 		if (json_object::has(obj, "modified_components")) {
 			JsonObject modified_components(ta);
-			sjson::parse(modified_components, obj["modified_components"]);
+			RETURN_IF_ERROR(sjson::parse(modified_components, obj["modified_components"]), c._opts);
 
 			// Modify components.
 			auto cur = json_object::begin(modified_components);
@@ -513,10 +515,10 @@ namespace unit_compiler
 				Guid component_id = guid::parse(guid);
 
 				// Patch component "data" key.
-				u32 comp_idx = object_index(unit->_merged_components, component_id);
+				u32 comp_idx = object_index(unit->_merged_components, component_id, c._opts);
 				if (comp_idx != UINT32_MAX) {
 					JsonObject modified_component(ta);
-					sjson::parse_object(modified_component, val);
+					RETURN_IF_ERROR(sjson::parse_object(modified_component, val), c._opts);
 
 					unit->_merged_components_data[comp_idx] = modified_component["data"];
 				} else {
@@ -541,9 +543,9 @@ namespace unit_compiler
 	{
 		TempAllocator4096 ta;
 		JsonObject obj(ta);
-		sjson::parse(obj, unit_json);
+		RETURN_IF_ERROR(sjson::parse(obj, unit_json), c._opts);
 
-		Guid id = sjson::parse_guid(obj["_guid"]);
+		Guid id = RETURN_IF_ERROR(sjson::parse_guid(obj["_guid"]), c._opts);
 
 		Unit *unit = instance_unit;
 		if (unit == NULL) {
@@ -562,7 +564,7 @@ namespace unit_compiler
 		if (json_object::has(obj, "prefab")) {
 			TempAllocator512 ta;
 			DynamicString prefab(ta);
-			sjson::parse_string(prefab, obj["prefab"]);
+			RETURN_IF_ERROR(sjson::parse_string(prefab, obj["prefab"]), c._opts);
 			const char *prefab_json_data = prefab_json(c, prefab.c_str());
 			DATA_COMPILER_ASSERT(prefab_json_data != NULL
 				, c._opts
@@ -583,7 +585,7 @@ namespace unit_compiler
 
 		if (json_object::has(obj, "children")) {
 			JsonArray children(ta);
-			sjson::parse_array(children, obj["children"]);
+			RETURN_IF_ERROR(sjson::parse_array(children, obj["children"]), c._opts);
 
 			for (u32 cc = 0; cc < array::size(children); ++cc) {
 				s32 err = parse_unit_internal(c
@@ -597,13 +599,13 @@ namespace unit_compiler
 
 		if (json_object::has(obj, "deleted_children")) {
 			JsonArray deleted_children(ta);
-			sjson::parse_array(deleted_children, obj["deleted_children"]);
+			RETURN_IF_ERROR(sjson::parse_array(deleted_children, obj["deleted_children"]), c._opts);
 
 			// Delete children.
 			for (u32 ii = 0; ii < array::size(deleted_children); ++ii) {
 				JsonObject obj(ta);
-				sjson::parse_object(obj, deleted_children[ii]);
-				Guid id = sjson::parse_guid(obj["id"]);
+				RETURN_IF_ERROR(sjson::parse_object(obj, deleted_children[ii]), c._opts);
+				Guid id = RETURN_IF_ERROR(sjson::parse_guid(obj["id"]), c._opts);
 
 				Unit *child = find_children(unit, id);
 
@@ -622,12 +624,12 @@ namespace unit_compiler
 
 		if (json_object::has(obj, "modified_children")) {
 			JsonArray modified_children(ta);
-			sjson::parse_array(modified_children, obj["modified_children"]);
+			RETURN_IF_ERROR(sjson::parse_array(modified_children, obj["modified_children"]), c._opts);
 
 			for (u32 ii = 0; ii < array::size(modified_children); ++ii) {
 				JsonObject obj(ta);
-				sjson::parse_object(obj, modified_children[ii]);
-				Guid id = sjson::parse_guid(obj["id"]);
+				RETURN_IF_ERROR(sjson::parse_object(obj, modified_children[ii]), c._opts);
+				Guid id = RETURN_IF_ERROR(sjson::parse_guid(obj["id"]), c._opts);
 
 				Unit *child = find_children(unit, id);
 
@@ -646,10 +648,11 @@ namespace unit_compiler
 		// Parse unit's editor name.
 		if (json_object::has(obj, "editor")) {
 			JsonObject editor(ta);
-			sjson::parse(editor, obj["editor"]);
+			RETURN_IF_ERROR(sjson::parse(editor, obj["editor"]), c._opts);
 
-			if (json_object::has(editor, "name"))
-				unit->_editor_name = sjson::parse_string_id(editor["name"]);
+			if (json_object::has(editor, "name")) {
+				unit->_editor_name = RETURN_IF_ERROR(sjson::parse_string_id(editor["name"]), c._opts);
+			}
 		}
 
 		return 0;
@@ -673,7 +676,7 @@ namespace unit_compiler
 	{
 		TempAllocator4096 ta;
 		JsonArray units(ta);
-		sjson::parse_array(units, units_array_json);
+		RETURN_IF_ERROR(sjson::parse_array(units, units_array_json), c._opts);
 
 		Array<u32> original_units(default_allocator());
 
@@ -704,13 +707,13 @@ namespace unit_compiler
 
 			TempAllocator512 ta;
 			JsonObject component(ta);
-			sjson::parse(component, component_json);
+			RETURN_IF_ERROR(sjson::parse(component, component_json), c._opts);
 
 			StringId32 comp_type;
 			if (json_object::has(component, "type")) {
-				comp_type = sjson::parse_string_id(component["type"]);
+				comp_type = RETURN_IF_ERROR(sjson::parse_string_id(component["type"]), c._opts);
 			} else {
-				comp_type = sjson::parse_string_id(component["_type"]);
+				comp_type = RETURN_IF_ERROR(sjson::parse_string_id(component["_type"]), c._opts);
 			}
 
 			if (comp_type == STRING_ID_32("transform", UINT32_C(0xad9b5315)))