diff --git a/spine-c/.cproject b/spine-c/.cproject
new file mode 100644
index 000000000..c6d57c96e
--- /dev/null
+++ b/spine-c/.cproject
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spine-c/.project b/spine-c/.project
new file mode 100644
index 000000000..462051309
--- /dev/null
+++ b/spine-c/.project
@@ -0,0 +1,27 @@
+
+
+ spine-c
+
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.genmakebuilder
+ clean,full,incremental,
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
+ full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ org.eclipse.cdt.core.ccnature
+ org.eclipse.cdt.managedbuilder.core.managedBuildNature
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
+
+
diff --git a/spine-c/.settings/org.eclipse.cdt.codan.core.prefs b/spine-c/.settings/org.eclipse.cdt.codan.core.prefs
new file mode 100644
index 000000000..c7be96702
--- /dev/null
+++ b/spine-c/.settings/org.eclipse.cdt.codan.core.prefs
@@ -0,0 +1,68 @@
+eclipse.preferences.version=1
+org.eclipse.cdt.codan.checkers.errnoreturn=Warning
+org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false}
+org.eclipse.cdt.codan.checkers.errreturnvalue=Error
+org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.checkers.noreturn=Error
+org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false}
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>true,empty_case_param\=>false}
+org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
+org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true}
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false}
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false}
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")}
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
+useParentScope=false
diff --git a/spine-c/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/spine-c/.settings/org.eclipse.cdt.managedbuilder.core.prefs
new file mode 100644
index 000000000..f647a60a9
--- /dev/null
+++ b/spine-c/.settings/org.eclipse.cdt.managedbuilder.core.prefs
@@ -0,0 +1,13 @@
+eclipse.preferences.version=1
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/CPATH/delimiter=;
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/CPATH/operation=remove
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/CPLUS_INCLUDE_PATH/delimiter=;
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/CPLUS_INCLUDE_PATH/operation=remove
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/C_INCLUDE_PATH/delimiter=;
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/C_INCLUDE_PATH/operation=remove
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/append=true
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/appendContributed=true
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/LIBRARY_PATH/delimiter=;
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/LIBRARY_PATH/operation=remove
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/append=true
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.784427509/appendContributed=true
diff --git a/spine-c/data/spineboy-skeleton.json b/spine-c/data/spineboy-skeleton.json
new file mode 100644
index 000000000..e6df6f99a
--- /dev/null
+++ b/spine-c/data/spineboy-skeleton.json
@@ -0,0 +1,98 @@
+{
+"bones": [
+ { "name": "root", "length": 0 },
+ { "name": "hip", "parent": "root", "length": 0, "x": 0.64, "y": 114.41 },
+ { "name": "left upper leg", "parent": "hip", "length": 50.39, "x": 14.45, "y": 2.81, "rotation": -89.09 },
+ { "name": "left lower leg", "parent": "left upper leg", "length": 56.45, "x": 51.78, "y": 3.46, "rotation": -16.65 },
+ { "name": "left foot", "parent": "left lower leg", "length": 46.5, "x": 64.02, "y": -8.67, "rotation": 102.43 },
+ { "name": "right upper leg", "parent": "hip", "length": 45.76, "x": -18.27, "rotation": -101.13 },
+ { "name": "right lower leg", "parent": "right upper leg", "length": 58.52, "x": 50.21, "y": 0.6, "rotation": -10.7 },
+ { "name": "right foot", "parent": "right lower leg", "length": 45.45, "x": 64.88, "y": 0.04, "rotation": 110.3 },
+ { "name": "torso", "parent": "hip", "length": 85.82, "x": -6.42, "y": 1.97, "rotation": 94.95 },
+ { "name": "neck", "parent": "torso", "length": 18.38, "x": 83.64, "y": -1.78, "rotation": 0.9 },
+ { "name": "head", "parent": "neck", "length": 68.28, "x": 19.09, "y": 6.97, "rotation": -8.94 },
+ { "name": "right shoulder", "parent": "torso", "length": 49.95, "x": 81.9, "y": 6.79, "rotation": 130.6 },
+ { "name": "right arm", "parent": "right shoulder", "length": 36.74, "x": 49.95, "y": -0.12, "rotation": 40.12 },
+ { "name": "right hand", "parent": "right arm", "length": 15.32, "x": 36.9, "y": 0.34, "rotation": 2.35 },
+ { "name": "left shoulder", "parent": "torso", "length": 44.19, "x": 78.96, "y": -15.75, "rotation": -156.96 },
+ { "name": "left arm", "parent": "left shoulder", "length": 35.62, "x": 44.19, "y": -0.01, "rotation": 28.16 },
+ { "name": "left hand", "parent": "left arm", "length": 11.52, "x": 35.62, "y": 0.07, "rotation": 2.7 },
+ { "name": "pelvis", "parent": "hip", "length": 0, "x": 1.41, "y": -6.57 }
+],
+"slots": [
+ { "name": "template", "bone": "root", "color": "ff898c86" },
+ { "name": "left shoulder", "bone": "left shoulder", "attachment": "left-shoulder" },
+ { "name": "left arm", "bone": "left arm", "attachment": "left-arm" },
+ { "name": "left hand", "bone": "left hand", "attachment": "left-hand" },
+ { "name": "left foot", "bone": "left foot", "attachment": "left-foot" },
+ { "name": "left lower leg", "bone": "left lower leg", "attachment": "left-lower-leg" },
+ { "name": "left upper leg", "bone": "left upper leg", "attachment": "left-upper-leg" },
+ { "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" },
+ { "name": "right foot", "bone": "right foot", "attachment": "right-foot" },
+ { "name": "right lower leg", "bone": "right lower leg", "attachment": "right-lower-leg" },
+ { "name": "right upper leg", "bone": "right upper leg", "attachment": "right-upper-leg" },
+ { "name": "torso", "bone": "torso", "attachment": "torso" },
+ { "name": "neck", "bone": "neck", "attachment": "neck" },
+ { "name": "head", "bone": "head", "attachment": "head" },
+ { "name": "eyes", "bone": "head", "attachment": "eyes" },
+ { "name": "right shoulder", "bone": "right shoulder", "attachment": "right-shoulder" },
+ { "name": "right arm", "bone": "right arm", "attachment": "right-arm" },
+ { "name": "right hand", "bone": "right hand", "attachment": "right-hand" }
+],
+"skins": {
+ "default": {
+ "left shoulder": {
+ "left-shoulder": { "x": 23.74, "y": 0.11, "rotation": 62.01, "width": 34, "height": 53 }
+ },
+ "left arm": {
+ "left-arm": { "x": 15.11, "y": -0.44, "rotation": 33.84, "width": 35, "height": 29 }
+ },
+ "left hand": {
+ "left-hand": { "x": 0.75, "y": 1.86, "rotation": 31.14, "width": 35, "height": 38 }
+ },
+ "left foot": {
+ "left-foot": { "x": 24.35, "y": 8.88, "rotation": 3.32, "width": 65, "height": 30 }
+ },
+ "left lower leg": {
+ "left-lower-leg": { "x": 24.55, "y": -1.92, "rotation": 105.75, "width": 49, "height": 64 }
+ },
+ "left upper leg": {
+ "left-upper-leg": { "x": 26.12, "y": -1.85, "rotation": 89.09, "width": 33, "height": 67 }
+ },
+ "pelvis": {
+ "pelvis": { "x": -4.83, "y": 10.62, "width": 63, "height": 47 }
+ },
+ "right foot": {
+ "right-foot": { "x": 19.02, "y": 8.47, "rotation": 1.52, "width": 67, "height": 30 }
+ },
+ "right lower leg": {
+ "right-lower-leg": { "x": 23.28, "y": -2.59, "rotation": 111.83, "width": 51, "height": 64 }
+ },
+ "right upper leg": {
+ "right-upper-leg": { "x": 23.03, "y": 0.25, "rotation": 101.13, "width": 44, "height": 70 }
+ },
+ "torso": {
+ "torso": { "x": 44.57, "y": -7.08, "rotation": -94.95, "width": 68, "height": 92 }
+ },
+ "neck": {
+ "neck": { "x": 9.42, "y": -3.66, "rotation": -100.15, "width": 34, "height": 28 }
+ },
+ "head": {
+ "head": { "x": 53.94, "y": -5.75, "rotation": -86.9, "width": 121, "height": 132 }
+ },
+ "eyes": {
+ "eyes": { "x": 28.94, "y": -32.92, "rotation": -86.9, "width": 34, "height": 27 },
+ "eyes-closed": { "x": 28.77, "y": -32.86, "rotation": -86.9, "width": 34, "height": 27 }
+ },
+ "right shoulder": {
+ "right-shoulder": { "x": 25.86, "y": 0.03, "rotation": 134.44, "width": 52, "height": 51 }
+ },
+ "right arm": {
+ "right-arm": { "x": 18.34, "y": -2.64, "rotation": 94.32, "width": 21, "height": 45 }
+ },
+ "right hand": {
+ "right-hand": { "x": 6.82, "y": 1.25, "rotation": 91.96, "width": 32, "height": 32 }
+ }
+ }
+}
+}
\ No newline at end of file
diff --git a/spine-c/data/spineboy-walk.json b/spine-c/data/spineboy-walk.json
new file mode 100644
index 000000000..b40e53a95
--- /dev/null
+++ b/spine-c/data/spineboy-walk.json
@@ -0,0 +1,278 @@
+{
+"bones": {
+ "left upper leg": {
+ "rotate": [
+ { "time": 0, "angle": -26.55 },
+ { "time": 0.1333, "angle": -8.78 },
+ { "time": 0.2666, "angle": 9.51 },
+ { "time": 0.4, "angle": 30.74 },
+ { "time": 0.5333, "angle": 25.33 },
+ { "time": 0.6666, "angle": 26.11 },
+ { "time": 0.8, "angle": -7.7 },
+ { "time": 0.9333, "angle": -21.19 },
+ { "time": 1.0666, "angle": -26.55 }
+ ],
+ "translate": [
+ { "time": 0, "x": -3, "y": -2.25 },
+ { "time": 0.4, "x": -2.18, "y": -2.25 },
+ { "time": 1.0666, "x": -3, "y": -2.25 }
+ ]
+ },
+ "right upper leg": {
+ "rotate": [
+ { "time": 0, "angle": 42.45 },
+ { "time": 0.1333, "angle": 52.1 },
+ { "time": 0.2666, "angle": 5.96 },
+ { "time": 0.5333, "angle": -16.93 },
+ { "time": 0.6666, "angle": 1.89 },
+ {
+ "time": 0.8,
+ "angle": 28.06,
+ "curve": [ 0.462, 0.11, 1, 1 ]
+ },
+ {
+ "time": 0.9333,
+ "angle": 58.68,
+ "curve": [ 0.5, 0.02, 1, 1 ]
+ },
+ { "time": 1.0666, "angle": 42.45 }
+ ],
+ "translate": [
+ { "time": 0, "x": 8.11, "y": -2.36 },
+ { "time": 0.1333, "x": 10.03, "y": -2.56 },
+ { "time": 0.4, "x": 2.76, "y": -2.97 },
+ { "time": 0.5333, "x": 2.76, "y": -2.81 },
+ { "time": 0.9333, "x": 8.67, "y": -2.54 },
+ { "time": 1.0666, "x": 8.11, "y": -2.36 }
+ ]
+ },
+ "left lower leg": {
+ "rotate": [
+ { "time": 0, "angle": -10.21 },
+ { "time": 0.1333, "angle": -55.64 },
+ { "time": 0.2666, "angle": -68.12 },
+ { "time": 0.5333, "angle": 5.11 },
+ { "time": 0.6666, "angle": -28.29 },
+ { "time": 0.8, "angle": 4.08 },
+ { "time": 0.9333, "angle": 3.53 },
+ { "time": 1.0666, "angle": -10.21 }
+ ]
+ },
+ "left foot": {
+ "rotate": [
+ { "time": 0, "angle": -3.69 },
+ { "time": 0.1333, "angle": -10.42 },
+ { "time": 0.2666, "angle": -17.14 },
+ { "time": 0.4, "angle": -2.83 },
+ { "time": 0.5333, "angle": -3.87 },
+ { "time": 0.6666, "angle": 2.78 },
+ { "time": 0.8, "angle": 1.68 },
+ { "time": 0.9333, "angle": -8.54 },
+ { "time": 1.0666, "angle": -3.69 }
+ ]
+ },
+ "right shoulder": {
+ "rotate": [
+ {
+ "time": 0,
+ "angle": 20.89,
+ "curve": [ 0.264, 0, 0.75, 1 ]
+ },
+ {
+ "time": 0.1333,
+ "angle": 3.72,
+ "curve": [ 0.272, 0, 0.841, 1 ]
+ },
+ { "time": 0.6666, "angle": -278.28 },
+ { "time": 1.0666, "angle": 20.89 }
+ ],
+ "translate": [
+ { "time": 0, "x": -7.84, "y": 7.19 },
+ { "time": 0.1333, "x": -6.36, "y": 6.42 },
+ { "time": 0.6666, "x": -11.07, "y": 5.25 },
+ { "time": 1.0666, "x": -7.84, "y": 7.19 }
+ ]
+ },
+ "right arm": {
+ "rotate": [
+ {
+ "time": 0,
+ "angle": -4.02,
+ "curve": [ 0.267, 0, 0.804, 0.99 ]
+ },
+ {
+ "time": 0.1333,
+ "angle": -13.99,
+ "curve": [ 0.341, 0, 1, 1 ]
+ },
+ {
+ "time": 0.6666,
+ "angle": 36.54,
+ "curve": [ 0.307, 0, 0.787, 0.99 ]
+ },
+ { "time": 1.0666, "angle": -4.02 }
+ ]
+ },
+ "right hand": {
+ "rotate": [
+ { "time": 0, "angle": 22.92 },
+ { "time": 0.4, "angle": -8.97 },
+ { "time": 0.6666, "angle": 0.51 },
+ { "time": 1.0666, "angle": 22.92 }
+ ]
+ },
+ "left shoulder": {
+ "rotate": [
+ { "time": 0, "angle": -1.47 },
+ { "time": 0.1333, "angle": 13.6 },
+ { "time": 0.6666, "angle": 280.74 },
+ { "time": 1.0666, "angle": -1.47 }
+ ],
+ "translate": [
+ { "time": 0, "x": -1.76, "y": 0.56 },
+ { "time": 0.6666, "x": -2.47, "y": 8.14 },
+ { "time": 1.0666, "x": -1.76, "y": 0.56 }
+ ]
+ },
+ "left hand": {
+ "rotate": [
+ {
+ "time": 0,
+ "angle": 11.58,
+ "curve": [ 0.169, 0.37, 0.632, 1.55 ]
+ },
+ {
+ "time": 0.1333,
+ "angle": 28.13,
+ "curve": [ 0.692, 0, 0.692, 0.99 ]
+ },
+ {
+ "time": 0.6666,
+ "angle": -27.42,
+ "curve": [ 0.117, 0.41, 0.738, 1.76 ]
+ },
+ { "time": 0.8, "angle": -36.32 },
+ { "time": 1.0666, "angle": 11.58 }
+ ]
+ },
+ "left arm": {
+ "rotate": [
+ { "time": 0, "angle": -8.27 },
+ { "time": 0.1333, "angle": 18.43 },
+ { "time": 0.6666, "angle": 0.88 },
+ { "time": 1.0666, "angle": -8.27 }
+ ]
+ },
+ "torso": {
+ "rotate": [
+ { "time": 0, "angle": -10.28 },
+ {
+ "time": 0.1333,
+ "angle": -15.38,
+ "curve": [ 0.545, 0, 1, 1 ]
+ },
+ {
+ "time": 0.4,
+ "angle": -9.78,
+ "curve": [ 0.58, 0.17, 1, 1 ]
+ },
+ { "time": 0.6666, "angle": -15.75 },
+ { "time": 0.9333, "angle": -7.06 },
+ { "time": 1.0666, "angle": -10.28 }
+ ],
+ "translate": [
+ { "time": 0, "x": -3.67, "y": 1.68 },
+ { "time": 0.1333, "x": -3.67, "y": 0.68 },
+ { "time": 0.4, "x": -3.67, "y": 1.97 },
+ { "time": 0.6666, "x": -3.67, "y": -0.14 },
+ { "time": 1.0666, "x": -3.67, "y": 1.68 }
+ ]
+ },
+ "right foot": {
+ "rotate": [
+ { "time": 0, "angle": -5.25 },
+ { "time": 0.2666, "angle": -4.08 },
+ { "time": 0.4, "angle": -6.45 },
+ { "time": 0.5333, "angle": -5.39 },
+ { "time": 0.8, "angle": -11.68 },
+ { "time": 0.9333, "angle": 0.46 },
+ { "time": 1.0666, "angle": -5.25 }
+ ]
+ },
+ "right lower leg": {
+ "rotate": [
+ { "time": 0, "angle": -3.39 },
+ { "time": 0.1333, "angle": -45.53 },
+ { "time": 0.2666, "angle": -2.59 },
+ { "time": 0.5333, "angle": -19.53 },
+ { "time": 0.6666, "angle": -64.8 },
+ {
+ "time": 0.8,
+ "angle": -82.56,
+ "curve": [ 0.557, 0.18, 1, 1 ]
+ },
+ { "time": 1.0666, "angle": -3.39 }
+ ]
+ },
+ "hip": {
+ "rotate": [
+ { "time": 0, "angle": 0, "curve": "stepped" },
+ { "time": 1.0666, "angle": 0 }
+ ],
+ "translate": [
+ { "time": 0, "x": 0, "y": 0 },
+ {
+ "time": 0.1333,
+ "x": 0,
+ "y": -7.61,
+ "curve": [ 0.272, 0.86, 1, 1 ]
+ },
+ { "time": 0.4, "x": 0, "y": 8.7 },
+ { "time": 0.5333, "x": 0, "y": -0.41 },
+ {
+ "time": 0.6666,
+ "x": 0,
+ "y": -7.05,
+ "curve": [ 0.235, 0.89, 1, 1 ]
+ },
+ { "time": 0.8, "x": 0, "y": 2.92 },
+ { "time": 0.9333, "x": 0, "y": 6.78 },
+ { "time": 1.0666, "x": 0, "y": 0 }
+ ]
+ },
+ "neck": {
+ "rotate": [
+ { "time": 0, "angle": 3.6 },
+ { "time": 0.1333, "angle": 17.49 },
+ { "time": 0.2666, "angle": 6.1 },
+ { "time": 0.4, "angle": 3.45 },
+ { "time": 0.5333, "angle": 5.17 },
+ { "time": 0.6666, "angle": 18.36 },
+ { "time": 0.8, "angle": 6.09 },
+ { "time": 0.9333, "angle": 2.28 },
+ { "time": 1.0666, "angle": 3.6 }
+ ]
+ },
+ "head": {
+ "rotate": [
+ {
+ "time": 0,
+ "angle": 3.6,
+ "curve": [ 0, 0, 0.704, 1.61 ]
+ },
+ { "time": 0.1666, "angle": -0.2 },
+ { "time": 0.2666, "angle": 6.1 },
+ { "time": 0.4, "angle": 3.45 },
+ {
+ "time": 0.5333,
+ "angle": 5.17,
+ "curve": [ 0, 0, 0.704, 1.61 ]
+ },
+ { "time": 0.7, "angle": 1.1 },
+ { "time": 0.8, "angle": 6.09 },
+ { "time": 0.9333, "angle": 2.28 },
+ { "time": 1.0666, "angle": 3.6 }
+ ]
+ }
+}
+}
\ No newline at end of file
diff --git a/spine-c/data/spineboy.atlas b/spine-c/data/spineboy.atlas
new file mode 100644
index 000000000..ac19de904
--- /dev/null
+++ b/spine-c/data/spineboy.atlas
@@ -0,0 +1,166 @@
+
+../data/spineboy.png
+format: RGBA8888
+filter: Nearest,Nearest
+repeat: none
+head
+ rotate: false
+ xy: 1, 122
+ size: 121, 132
+ orig: 121, 132
+ offset: 0, 0
+ index: -1
+torso
+ rotate: false
+ xy: 1, 28
+ size: 68, 92
+ orig: 68, 92
+ offset: 0, 0
+ index: -1
+left-pant-bottom
+ rotate: false
+ xy: 1, 4
+ size: 44, 22
+ orig: 44, 22
+ offset: 0, 0
+ index: -1
+right-pant-bottom
+ rotate: false
+ xy: 47, 8
+ size: 46, 18
+ orig: 46, 18
+ offset: 0, 0
+ index: -1
+right-upper-leg
+ rotate: false
+ xy: 71, 50
+ size: 44, 70
+ orig: 44, 70
+ offset: 0, 0
+ index: -1
+pelvis
+ rotate: false
+ xy: 95, 1
+ size: 63, 47
+ orig: 63, 47
+ offset: 0, 0
+ index: -1
+left-upper-leg
+ rotate: false
+ xy: 117, 53
+ size: 33, 67
+ orig: 33, 67
+ offset: 0, 0
+ index: -1
+right-foot
+ rotate: false
+ xy: 160, 224
+ size: 67, 30
+ orig: 67, 30
+ offset: 0, 0
+ index: -1
+left-shoulder
+ rotate: false
+ xy: 124, 201
+ size: 34, 53
+ orig: 34, 53
+ offset: 0, 0
+ index: -1
+left-ankle
+ rotate: false
+ xy: 229, 222
+ size: 25, 32
+ orig: 25, 32
+ offset: 0, 0
+ index: -1
+left-foot
+ rotate: false
+ xy: 160, 192
+ size: 65, 30
+ orig: 65, 30
+ offset: 0, 0
+ index: -1
+neck
+ rotate: false
+ xy: 124, 171
+ size: 34, 28
+ orig: 34, 28
+ offset: 0, 0
+ index: -1
+right-arm
+ rotate: false
+ xy: 124, 124
+ size: 21, 45
+ orig: 21, 45
+ offset: 0, 0
+ index: -1
+right-ankle
+ rotate: false
+ xy: 227, 190
+ size: 25, 30
+ orig: 25, 30
+ offset: 0, 0
+ index: -1
+left-hand
+ rotate: false
+ xy: 147, 131
+ size: 35, 38
+ orig: 35, 38
+ offset: 0, 0
+ index: -1
+left-arm
+ rotate: false
+ xy: 184, 161
+ size: 35, 29
+ orig: 35, 29
+ offset: 0, 0
+ index: -1
+eyes-closed
+ rotate: false
+ xy: 221, 161
+ size: 34, 27
+ orig: 34, 27
+ offset: 0, 0
+ index: -1
+right-lower-leg
+ rotate: false
+ xy: 152, 65
+ size: 51, 64
+ orig: 51, 64
+ offset: 0, 0
+ index: -1
+right-foot-idle
+ rotate: false
+ xy: 184, 131
+ size: 53, 28
+ orig: 53, 28
+ offset: 0, 0
+ index: -1
+left-lower-leg
+ rotate: false
+ xy: 205, 65
+ size: 49, 64
+ orig: 49, 64
+ offset: 0, 0
+ index: -1
+right-shoulder
+ rotate: false
+ xy: 160, 12
+ size: 52, 51
+ orig: 52, 51
+ offset: 0, 0
+ index: -1
+eyes
+ rotate: false
+ xy: 214, 36
+ size: 34, 27
+ orig: 34, 27
+ offset: 0, 0
+ index: -1
+right-hand
+ rotate: false
+ xy: 214, 2
+ size: 32, 32
+ orig: 32, 32
+ offset: 0, 0
+ index: -1
diff --git a/spine-c/data/spineboy.png b/spine-c/data/spineboy.png
new file mode 100644
index 000000000..b8b493dfd
Binary files /dev/null and b/spine-c/data/spineboy.png differ
diff --git a/spine-c/src/SfmlRegionAttachment.c b/spine-c/src/SfmlRegionAttachment.c
new file mode 100644
index 000000000..82ca6f5ea
--- /dev/null
+++ b/spine-c/src/SfmlRegionAttachment.c
@@ -0,0 +1,14 @@
+#include "SfmlRegionAttachment.h"
+#include
+
+void SfmlRegionAttachment_dispose (Attachment* attachment) {
+ SfmlRegionAttachment* this = (SfmlRegionAttachment*)attachment;
+ RegionAttachment_dispose(&this->super);
+}
+
+SfmlRegionAttachment* SfmlRegionAttachment_create (const char* name) {
+ SfmlRegionAttachment* this = calloc(1, sizeof(SfmlRegionAttachment));
+ RegionAttachment_init(&this->super, name);
+ ((Attachment*)this)->_dispose = SfmlRegionAttachment_dispose;
+ return this;
+}
diff --git a/spine-c/src/SfmlRegionAttachment.h b/spine-c/src/SfmlRegionAttachment.h
new file mode 100644
index 000000000..6e77e1161
--- /dev/null
+++ b/spine-c/src/SfmlRegionAttachment.h
@@ -0,0 +1,23 @@
+#ifndef SPINE_SFMLREGIONATTACHMENT_H_
+#define SPINE_SFMLREGIONATTACHMENT_H_
+
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef struct {
+ RegionAttachment super;
+ int meow;
+} SfmlRegionAttachment;
+
+SfmlRegionAttachment* SfmlRegionAttachment_create (const char* name);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_SFMLREGIONATTACHMENT_H_ */
diff --git a/spine-c/src/SfmlSkeleton.c b/spine-c/src/SfmlSkeleton.c
new file mode 100644
index 000000000..6663c5934
--- /dev/null
+++ b/spine-c/src/SfmlSkeleton.c
@@ -0,0 +1,16 @@
+#include "SfmlSkeleton.h"
+#include
+
+void SfmlSkeleton_dispose (Skeleton* skeleton) {
+ // SfmlSkeleton* this = (SfmlSkeleton*)skeleton;
+}
+
+Skeleton* Skeleton_create (SkeletonData* data) {
+ SfmlSkeleton* this = calloc(1, sizeof(SfmlSkeleton));
+ Skeleton_init(&this->super, data);
+ this->super._dispose = SfmlSkeleton_dispose;
+ return &this->super;
+}
+
+void Skeleton_draw (Skeleton* skeleton) {
+}
diff --git a/spine-c/src/SfmlSkeleton.h b/spine-c/src/SfmlSkeleton.h
new file mode 100644
index 000000000..a4ee5a5b0
--- /dev/null
+++ b/spine-c/src/SfmlSkeleton.h
@@ -0,0 +1,23 @@
+#ifndef SPINE_SFMLSKELETON_H_
+#define SPINE_SFMLSKELETON_H_
+
+#include
+
+#ifdef __cplusplus
+namespace spine {extern "C" {
+#endif
+
+typedef struct {
+ Skeleton super;
+ int meow;
+} SfmlSkeleton;
+
+Skeleton* Skeleton_create (SkeletonData* data);
+
+void Skeleton_draw ();
+
+#ifdef __cplusplus
+}}
+#endif
+
+#endif /* SPINE_SFMLSKELETON_H_ */
diff --git a/spine-c/src/main.c b/spine-c/src/main.c
new file mode 100644
index 000000000..c3efaa9f5
--- /dev/null
+++ b/spine-c/src/main.c
@@ -0,0 +1,27 @@
+#include
+#include
+#include
+
+#include
+#include "SfmlSkeleton.h"
+#include "spine/cJSON.h"
+
+int main (void) {
+ //BoneData *boneData = BoneData_create("meow");
+
+ //printf("name: %s\n", boneData->name);
+ //printf("length struct: %f\n", boneData->length);
+
+ //SkeletonData* data =
+ SkeletonJson_readSkeletonDataFile("data/spineboy-skeleton.json");
+ printf("error: %s\n", SkeletonJson_getError());
+
+ SkeletonData *skeletonData = SkeletonData_create();
+ Skeleton* skeleton = Skeleton_create(skeletonData);
+ //Skeleton_something(skeleton);
+ printf("meow? %d\n", ((SfmlSkeleton*)skeleton)->meow);
+ Skeleton_dispose(skeleton);
+ printf("meow? %d\n", ((SfmlSkeleton*)skeleton)->meow);
+
+ return 0;
+}
diff --git a/spine-c/src/spine/Attachment.c b/spine-c/src/spine/Attachment.c
new file mode 100644
index 000000000..03755e8ee
--- /dev/null
+++ b/spine-c/src/spine/Attachment.c
@@ -0,0 +1,22 @@
+#include
+#include
+
+static AttachmentLoader loader;
+
+void Attachment_setAttachmentLoader (AttachmentLoader value) {
+ loader = value;
+}
+
+AttachmentLoader Attachment_getAttachmentLoader () {
+ return loader;
+}
+
+void Attachment_init (Attachment* this, const char* name) {
+ MALLOC_STR(this->name, name);
+}
+
+void Attachment_dispose (Attachment* this) {
+ this->_dispose(this);
+ FREE(this->name)
+ FREE(this)
+}
diff --git a/spine-c/src/spine/Attachment.h b/spine-c/src/spine/Attachment.h
new file mode 100644
index 000000000..9b7957689
--- /dev/null
+++ b/spine-c/src/spine/Attachment.h
@@ -0,0 +1,32 @@
+#ifndef SPINE_ATTACHMENT_H_
+#define SPINE_ATTACHMENT_H_
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef enum {
+ ATTACHMENT_REGION, ATTACHMENT_REGION_SEQUENCE
+} AttachmentType;
+
+typedef struct Attachment Attachment;
+struct Attachment {
+ const char* const name;
+ void (*_dispose) (Attachment* attachment);
+};
+
+typedef Attachment* (*AttachmentLoader) (AttachmentType type, const char* name);
+
+void Attachment_setAttachmentLoader (AttachmentLoader loader);
+AttachmentLoader Attachment_getAttachmentLoader ();
+
+void Attachment_init (Attachment* attachment, const char* name);
+void Attachment_dispose (Attachment* attachment);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_ATTACHMENT_H_ */
diff --git a/spine-c/src/spine/Bone.c b/spine-c/src/spine/Bone.c
new file mode 100644
index 000000000..30fb73d3d
--- /dev/null
+++ b/spine-c/src/spine/Bone.c
@@ -0,0 +1,65 @@
+#include
+#include
+#include
+
+static int yDown;
+
+void Bone_setYDown (int value) {
+ yDown = value;
+}
+
+Bone* Bone_create (BoneData* data, Bone* parent) {
+ Bone* this = calloc(1, sizeof(Bone));
+ CAST(BoneData*, this->data) = data;
+ CAST(Bone*, this->parent) = parent;
+ this->scaleX = 1;
+ this->scaleY = 1;
+ return this;
+}
+
+void Bone_dispose (Bone* this) {
+ FREE(this)
+}
+
+void Bone_setToBindPose (Bone* this) {
+ this->x = this->data->x;
+ this->y = this->data->y;
+ this->rotation = this->data->rotation;
+ this->scaleX = this->data->scaleX;
+ this->scaleY = this->data->scaleY;
+}
+
+void Bone_updateWorldTransform (Bone* this, int flipX, int flipY) {
+ if (this->parent) {
+ CAST(float, this->worldX) = this->x * this->parent->m00 + this->y * this->parent->m01 + this->parent->worldX;
+ CAST(float, this->worldY) = this->x * this->parent->m10 + this->y * this->parent->m11 + this->parent->worldY;
+ CAST(float, this->worldScaleX) = this->parent->worldScaleX * this->scaleX;
+ CAST(float, this->worldScaleY) = this->parent->worldScaleY * this->scaleY;
+ CAST(float, this->worldRotation) = this->parent->worldRotation + this->rotation;
+ } else {
+ CAST(float, this->worldX) = this->x;
+ CAST(float, this->worldY) = this->y;
+ CAST(float, this->worldScaleX) = this->scaleX;
+ CAST(float, this->worldScaleY) = this->scaleY;
+ CAST(float, this->worldRotation) = this->rotation;
+ }
+ float radians = (float)(this->worldRotation * 3.1415926535897932385 / 180);
+ float cos = cosf(radians);
+ float sin = sinf(radians);
+ CAST(float, this->m00) = cos * this->worldScaleX;
+ CAST(float, this->m10) = sin * this->worldScaleX;
+ CAST(float, this->m01) = -sin * this->worldScaleY;
+ CAST(float, this->m11) = cos * this->worldScaleY;
+ if (flipX) {
+ CAST(float, this->m00) = -this->m00;
+ CAST(float, this->m01) = -this->m01;
+ }
+ if (flipY) {
+ CAST(float, this->m10) = -this->m10;
+ CAST(float, this->m11) = -this->m11;
+ }
+ if (yDown) {
+ CAST(float, this->m10) = -this->m10;
+ CAST(float, this->m11) = -this->m11;
+ }
+}
diff --git a/spine-c/src/spine/Bone.h b/spine-c/src/spine/Bone.h
new file mode 100644
index 000000000..2cac45d29
--- /dev/null
+++ b/spine-c/src/spine/Bone.h
@@ -0,0 +1,40 @@
+#ifndef SPINE_BONE_H_
+#define SPINE_BONE_H_
+
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef struct Bone Bone;
+struct Bone {
+ BoneData* const data;
+ Bone* const parent;
+ float x, y;
+ float rotation;
+ float scaleX, scaleY;
+
+ float const m00, m01, worldX; // a b x
+ float const m10, m11, worldY; // c d y
+ float const worldRotation;
+ float const worldScaleX, worldScaleY;
+};
+
+void Bone_setYDown (int yDown);
+
+/** @param parent May be zero. */
+Bone* Bone_create (BoneData* data, Bone* parent);
+void Bone_dispose (Bone* bone);
+
+void Bone_setToBindPose (Bone* bone);
+
+void Bone_updateWorldTransform (Bone* bone, int flipX, int flipY);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_BONE_H_ */
diff --git a/spine-c/src/spine/BoneData.c b/spine-c/src/spine/BoneData.c
new file mode 100644
index 000000000..ceb09c3d5
--- /dev/null
+++ b/spine-c/src/spine/BoneData.c
@@ -0,0 +1,16 @@
+#include
+#include
+
+BoneData* BoneData_create (const char* name, BoneData* parent) {
+ BoneData* this = calloc(1, sizeof(BoneData));
+ MALLOC_STR(this->name, name)
+ CAST(BoneData*, this->parent) = parent;
+ this->scaleX = 1;
+ this->scaleY = 1;
+ return this;
+}
+
+void BoneData_dispose (BoneData* this) {
+ FREE(this->name)
+ FREE(this)
+}
diff --git a/spine-c/src/spine/BoneData.h b/spine-c/src/spine/BoneData.h
new file mode 100644
index 000000000..3eb9a541c
--- /dev/null
+++ b/spine-c/src/spine/BoneData.h
@@ -0,0 +1,27 @@
+#ifndef SPINE_BONEDATA_H_
+#define SPINE_BONEDATA_H_
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef struct BoneData BoneData;
+struct BoneData {
+ const char* const name;
+ BoneData* const parent;
+ float length;
+ float x, y;
+ float rotation;
+ float scaleX, scaleY;
+};
+
+BoneData* BoneData_create (const char* name, BoneData* parent);
+void BoneData_dispose (BoneData* boneData);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_BONEDATA_H_ */
diff --git a/spine-c/src/spine/RegionAttachment.c b/spine-c/src/spine/RegionAttachment.c
new file mode 100644
index 000000000..47b380cf2
--- /dev/null
+++ b/spine-c/src/spine/RegionAttachment.c
@@ -0,0 +1,43 @@
+#include
+#include
+#include
+
+void RegionAttachment_init (RegionAttachment* this, const char* name) {
+ Attachment_init(&this->super, name);
+ this->scaleX = 1;
+ this->scaleY = 1;
+}
+
+void RegionAttachment_dispose (RegionAttachment* this) {
+ Attachment_dispose(&this->super);
+}
+
+void RegionAttachment_updateOffset (RegionAttachment* this) {
+ float localX2 = this->width / 2;
+ float localY2 = this->height / 2;
+ float localX = -localX2;
+ float localY = -localY2;
+ localX *= this->scaleX;
+ localY *= this->scaleY;
+ localX2 *= this->scaleX;
+ localY2 *= this->scaleY;
+ float radians = (float)(this->rotation * 3.1415926535897932385 / 180);
+ float cos = cosf(radians);
+ float sin = sinf(radians);
+ float localXCos = localX * cos + this->x;
+ float localXSin = localX * sin;
+ float localYCos = localY * cos + this->y;
+ float localYSin = localY * sin;
+ float localX2Cos = localX2 * cos + this->x;
+ float localX2Sin = localX2 * sin;
+ float localY2Cos = localY2 * cos + this->y;
+ float localY2Sin = localY2 * sin;
+ this->offset[0] = localXCos - localYSin;
+ this->offset[1] = localYCos + localXSin;
+ this->offset[2] = localXCos - localY2Sin;
+ this->offset[3] = localY2Cos + localXSin;
+ this->offset[4] = localX2Cos - localY2Sin;
+ this->offset[5] = localY2Cos + localX2Sin;
+ this->offset[6] = localX2Cos - localYSin;
+ this->offset[7] = localYCos + localX2Sin;
+}
diff --git a/spine-c/src/spine/RegionAttachment.h b/spine-c/src/spine/RegionAttachment.h
new file mode 100644
index 000000000..1d758b359
--- /dev/null
+++ b/spine-c/src/spine/RegionAttachment.h
@@ -0,0 +1,28 @@
+#ifndef SPINE_REGIONATTACHMENT_H_
+#define SPINE_REGIONATTACHMENT_H_
+
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef struct RegionAttachment RegionAttachment;
+struct RegionAttachment {
+ Attachment super;
+ float x, y, scaleX, scaleY, rotation, width, height;
+ float offset[8];
+};
+
+void RegionAttachment_init (RegionAttachment* this, const char* name);
+void RegionAttachment_dispose (RegionAttachment* attachment);
+
+void RegionAttachment_updateOffset (RegionAttachment* attachment);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_REGIONATTACHMENT_H_ */
diff --git a/spine-c/src/spine/Skeleton.c b/spine-c/src/spine/Skeleton.c
new file mode 100644
index 000000000..6909deaba
--- /dev/null
+++ b/spine-c/src/spine/Skeleton.c
@@ -0,0 +1,176 @@
+#include
+#include
+
+void Skeleton_init (Skeleton* this, SkeletonData* data) {
+ CAST(SkeletonData*, this->data) = data;
+
+ this->boneCount = this->data->boneCount;
+ this->bones = malloc(sizeof(Bone*) * this->boneCount);
+ int i, ii;
+ for (i = 0; i < this->boneCount; ++i) {
+ BoneData* boneData = this->data->bones[i];
+ Bone* parent = 0;
+ if (boneData->parent) {
+ // Find parent bone.
+ for (ii = 0; ii < this->boneCount; ++ii) {
+ if (data->bones[ii] == boneData->parent) {
+ parent = this->bones[ii];
+ break;
+ }
+ }
+ }
+ this->bones[i] = Bone_create(boneData, parent);
+ }
+
+ this->slotCount = data->slotCount;
+ this->slots = malloc(sizeof(Slot*) * this->slotCount);
+ for (i = 0; i < this->slotCount; ++i) {
+ SlotData *slotData = data->slots[i];
+
+ // Find bone for the slotData's boneData.
+ Bone *bone;
+ for (ii = 0; ii < this->boneCount; ++ii) {
+ if (data->bones[ii] == slotData->boneData) {
+ bone = this->bones[ii];
+ break;
+ }
+ }
+
+ this->slots[i] = Slot_create(slotData, this, bone);
+ }
+
+ this->drawOrder = malloc(sizeof(Slot*) * this->slotCount);
+ memcpy(this->drawOrder, this->slots, sizeof(Slot*) * this->slotCount);
+}
+
+void Skeleton_dispose (Skeleton* this) {
+ if (this->_dispose) this->_dispose(this);
+
+ int i;
+ for (i = 0; i < this->boneCount; ++i)
+ Bone_dispose(this->bones[i]);
+ FREE(this->bones)
+
+ FREE(this->slots)
+ for (i = 0; i < this->slotCount; ++i)
+ Slot_dispose(this->slots[i]);
+
+ FREE(this->drawOrder)
+
+ FREE(this)
+}
+
+void Skeleton_updateWorldTransform (const Skeleton* this) {
+ int i;
+ for (i = 0; i < this->boneCount; ++i)
+ Bone_updateWorldTransform(this->bones[i], this->flipX, this->flipY);
+}
+
+void Skeleton_setToBindPose (const Skeleton* this) {
+ Skeleton_setBonesToBindPose(this);
+ Skeleton_setSlotsToBindPose(this);
+}
+
+void Skeleton_setBonesToBindPose (const Skeleton* this) {
+ int i;
+ for (i = 0; i < this->boneCount; ++i)
+ Bone_setToBindPose(this->bones[i]);
+}
+
+void Skeleton_setSlotsToBindPose (const Skeleton* this) {
+ int i;
+ for (i = 0; i < this->slotCount; ++i)
+ Slot_setToBindPose(this->slots[i]);
+}
+
+Bone* Skeleton_getRootBone (const Skeleton* this) {
+ if (this->boneCount == 0) return 0;
+ return this->bones[0];
+}
+
+Bone* Skeleton_findBone (const Skeleton* this, const char* boneName) {
+ int i;
+ for (i = 0; i < this->boneCount; ++i)
+ if (this->data->bones[i]->name == boneName) return this->bones[i];
+ return 0;
+}
+
+int Skeleton_findBoneIndex (const Skeleton* this, const char* boneName) {
+ int i;
+ for (i = 0; i < this->boneCount; ++i)
+ if (this->data->bones[i]->name == boneName) return i;
+ return -1;
+}
+
+Slot* Skeleton_findSlot (const Skeleton* this, const char* slotName) {
+ int i;
+ for (i = 0; i < this->slotCount; ++i)
+ if (this->data->slots[i]->name == slotName) return this->slots[i];
+ return 0;
+}
+
+int Skeleton_findSlotIndex (const Skeleton* this, const char* slotName) {
+ int i;
+ for (i = 0; i < this->slotCount; ++i)
+ if (this->data->slots[i]->name == slotName) return i;
+ return -1;
+}
+
+int Skeleton_setSkinByName (Skeleton* this, const char* skinName) {
+ Skin *skin = SkeletonData_findSkin(this->data, skinName);
+ if (!skin) return 0;
+ Skeleton_setSkin(this, skin);
+ return 1;
+}
+
+void Skeleton_setSkin (Skeleton* this, Skin* newSkin) {
+ if (this->skin && newSkin) {
+ // Attach each attachment in the new skin if the corresponding attachment in the old skin is currently attached.
+ const SkinEntry *entry = this->skin->entryList;
+ while (entry) {
+ Slot *slot = this->slots[entry->slotIndex];
+ if (slot->attachment == entry->attachment) {
+ Attachment *attachment = Skin_getAttachment(newSkin, entry->slotIndex, entry->name);
+ if (attachment) Slot_setAttachment(slot, attachment);
+ }
+ entry = entry->next;
+ }
+ }
+ CAST(Skin*, this->skin) = newSkin;
+}
+
+Attachment* Skeleton_getAttachmentForSlotName (const Skeleton* this, const char* slotName, const char* attachmentName) {
+ int slotIndex = SkeletonData_findSlotIndex(this->data, slotName);
+ return Skeleton_getAttachmentForSlotIndex(this, slotIndex, attachmentName);
+}
+
+Attachment* Skeleton_getAttachmentForSlotIndex (const Skeleton* this, int slotIndex, const char* attachmentName) {
+ if (slotIndex == -1) return 0;
+ if (this->skin) {
+ Attachment *attachment = Skin_getAttachment(this->skin, slotIndex, attachmentName);
+ if (attachment) return attachment;
+ }
+ if (this->data->defaultSkin) {
+ Attachment *attachment = Skin_getAttachment(this->data->defaultSkin, slotIndex, attachmentName);
+ if (attachment) return attachment;
+ }
+ return 0;
+}
+
+int Skeleton_setAttachment (Skeleton* this, const char* slotName, const char* attachmentName) {
+ int i;
+ for (i = 0; i < this->slotCount; ++i) {
+ Slot *slot = this->slots[i];
+ if (slot->data->name == slotName) {
+ Attachment* attachment = Skeleton_getAttachmentForSlotIndex(this, i, attachmentName);
+ if (!attachment) return 0;
+ Slot_setAttachment(slot, attachment);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void Skeleton_update (Skeleton* this, float deltaTime) {
+ this->time += deltaTime;
+}
diff --git a/spine-c/src/spine/Skeleton.h b/spine-c/src/spine/Skeleton.h
new file mode 100644
index 000000000..d817ccd4e
--- /dev/null
+++ b/spine-c/src/spine/Skeleton.h
@@ -0,0 +1,71 @@
+#ifndef SPINE_SKELETON_H_
+#define SPINE_SKELETON_H_
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef struct Skeleton Skeleton;
+struct Skeleton {
+ SkeletonData* const data;
+
+ int boneCount;
+ Bone** bones;
+
+ int slotCount;
+ Slot** slots;
+ Slot** drawOrder;
+
+ Skin* const skin;
+ float r, g, b, a;
+ float time;
+ int flipX, flipY;
+
+ void (*_dispose) (Skeleton* skeleton);
+};
+
+void Skeleton_init (Skeleton* skeleton, SkeletonData* data);
+void Skeleton_dispose (Skeleton* skeleton);
+
+void Skeleton_updateWorldTransform (const Skeleton* skeleton);
+
+void Skeleton_setToBindPose (const Skeleton* skeleton);
+void Skeleton_setBonesToBindPose (const Skeleton* skeleton);
+void Skeleton_setSlotsToBindPose (const Skeleton* skeleton);
+
+Bone* Skeleton_getRootBone (const Skeleton* skeleton);
+/** Returns 0 if the bone could not be found. */
+Bone* Skeleton_findBone (const Skeleton* skeleton, const char* boneName);
+/** Returns -1 if the bone could not be found. */
+int Skeleton_findBoneIndex (const Skeleton* skeleton, const char* boneName);
+
+/** Returns 0 if the slot could not be found. */
+Slot* Skeleton_findSlot (const Skeleton* skeleton, const char* slotName);
+/** Returns -1 if the slot could not be found. */
+int Skeleton_findSlotIndex (const Skeleton* skeleton, const char* slotName);
+
+/** Returns 0 if the skin could not be found. */
+int Skeleton_setSkinByName (Skeleton* skeleton, const char* skinName);
+/** @param skin May be 0.*/
+void Skeleton_setSkin (Skeleton* skeleton, Skin* skin);
+
+/** Returns 0 if the slot or attachment could not be found. */
+Attachment* Skeleton_getAttachmentForSlotName (const Skeleton* skeleton, const char* slotName, const char* attachmentName);
+/** Returns 0 if the slot or attachment could not be found. */
+Attachment* Skeleton_getAttachmentForSlotIndex (const Skeleton* this, int slotIndex, const char* attachmentName);
+/** Returns 0 if the slot or attachment could not be found. */
+int Skeleton_setAttachment (Skeleton* skeleton, const char* slotName, const char* attachmentName);
+
+void Skeleton_update (Skeleton* this, float deltaTime);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_SKELETON_H_*/
diff --git a/spine-c/src/spine/SkeletonData.c b/spine-c/src/spine/SkeletonData.c
new file mode 100644
index 000000000..f49a2f685
--- /dev/null
+++ b/spine-c/src/spine/SkeletonData.c
@@ -0,0 +1,46 @@
+#include
+#include
+
+SkeletonData* SkeletonData_create () {
+ SkeletonData* this = calloc(1, sizeof(SkeletonData));
+ return this;
+}
+
+void SkeletonData_dispose (SkeletonData* this) {
+ FREE(this)
+}
+
+BoneData* SkeletonData_findBone (const SkeletonData* this, const char* boneName) {
+ int i;
+ for (i = 0; i < this->boneCount; ++i)
+ if (strcmp(this->bones[i]->name, boneName) == 0) return this->bones[i];
+ return 0;
+}
+
+int SkeletonData_findBoneIndex (const SkeletonData* this, const char* boneName) {
+ int i;
+ for (i = 0; i < this->boneCount; ++i)
+ if (strcmp(this->bones[i]->name, boneName) == 0) return i;
+ return 0;
+}
+
+SlotData* SkeletonData_findSlot (const SkeletonData* this, const char* slotName) {
+ int i;
+ for (i = 0; i < this->slotCount; ++i)
+ if (strcmp(this->slots[i]->name, slotName) == 0) return this->slots[i];
+ return 0;
+}
+
+int SkeletonData_findSlotIndex (const SkeletonData* this, const char* slotName) {
+ int i;
+ for (i = 0; i < this->slotCount; ++i)
+ if (strcmp(this->slots[i]->name, slotName) == 0) return i;
+ return 0;
+}
+
+Skin* SkeletonData_findSkin (const SkeletonData* this, const char* skinName) {
+ int i;
+ for (i = 0; i < this->skinCount; ++i)
+ if (strcmp(this->skins[i]->name, skinName) == 0) return this->skins[i];
+ return 0;
+}
diff --git a/spine-c/src/spine/SkeletonData.h b/spine-c/src/spine/SkeletonData.h
new file mode 100644
index 000000000..f9a60bfac
--- /dev/null
+++ b/spine-c/src/spine/SkeletonData.h
@@ -0,0 +1,40 @@
+#ifndef SPINE_SKELETONDATA_H_
+#define SPINE_SKELETONDATA_H_
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+namespace spine {extern "C" {
+#endif
+
+typedef struct {
+ int boneCount;
+ BoneData** bones;
+
+ int slotCount;
+ SlotData** slots;
+
+ int skinCount;
+ Skin** skins;
+
+ Skin* defaultSkin;
+} SkeletonData;
+
+SkeletonData* SkeletonData_create ();
+void SkeletonData_dispose (SkeletonData* skeletonData);
+
+BoneData* SkeletonData_findBone (const SkeletonData* skeletonData, const char* boneName);
+int SkeletonData_findBoneIndex (const SkeletonData* skeletonData, const char* boneName);
+
+SlotData* SkeletonData_findSlot (const SkeletonData* skeletonData, const char* slotName);
+int SkeletonData_findSlotIndex (const SkeletonData* skeletonData, const char* slotName);
+
+Skin* SkeletonData_findSkin (const SkeletonData* skeletonData, const char* skinName);
+
+#ifdef __cplusplus
+}}
+#endif
+
+#endif /* SPINE_SKELETONDATA_H_ */
diff --git a/spine-c/src/spine/SkeletonJson.c b/spine-c/src/spine/SkeletonJson.c
new file mode 100644
index 000000000..9f5a5f8a0
--- /dev/null
+++ b/spine-c/src/spine/SkeletonJson.c
@@ -0,0 +1,175 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+static float scale;
+static const char* error;
+
+void SkeletonJson_setScale (float value) {
+ scale = value;
+}
+
+void* SkeletonJson_setError (cJSON* root, const char* value1, const char* value2) {
+ FREE(error)
+ char message[256];
+ strcpy(message, value1);
+ int length = strlen(value1);
+ if (value2) strncat(message + length, value2, 256 - length);
+ MALLOC_STR(error, message)
+ if (root) cJSON_dispose(root);
+ return 0;
+}
+
+const char* SkeletonJson_getError () {
+ return error;
+}
+
+static float toColor (const char* value, int index) {
+ if (strlen(value) != 8) return -1;
+ value += index * 2;
+ char digits[3];
+ digits[0] = *value;
+ digits[1] = *(value + 1);
+ digits[2] = '\0';
+ char* error;
+ int color = strtoul(digits, &error, 16);
+ if (*error != 0) return -1;
+ return color / (float)255;
+}
+
+SkeletonData* SkeletonJson_readSkeletonDataFile (const char* path) {
+ char* data = readFile(path);
+ if (!data) return SkeletonJson_setError(0, "Unable to read file: ", path);
+ SkeletonData* skeletonData = SkeletonJson_readSkeletonData(data);
+ free(data);
+ return skeletonData;
+}
+
+SkeletonData* SkeletonJson_readSkeletonData (const char* json) {
+ FREE(error)
+ error = 0;
+
+ cJSON* root = cJSON_Parse(json);
+ if (!root) return SkeletonJson_setError(0, "Invalid JSON: ", cJSON_GetErrorPtr());
+
+ SkeletonData* skeletonData = SkeletonData_create();
+ int i, ii, iii;
+
+ cJSON* bones = cJSON_GetObjectItem(root, "bones");
+ int boneCount = cJSON_GetArraySize(bones);
+ skeletonData->bones = malloc(sizeof(BoneData*) * boneCount);
+ for (i = 0; i < boneCount; ++i) {
+ cJSON* boneMap = cJSON_GetArrayItem(bones, i);
+
+ const char* boneName = cJSON_GetObjectString(boneMap, "name", 0);
+
+ BoneData* parent = 0;
+ const char* parentName = cJSON_GetObjectString(boneMap, "parent", 0);
+ if (parentName) {
+ parent = SkeletonData_findBone(skeletonData, parentName);
+ if (!parent) return SkeletonJson_setError(root, "Parent bone not found: ", parentName);
+ }
+
+ BoneData* boneData = BoneData_create(boneName, parent);
+ boneData->length = cJSON_GetObjectFloat(boneMap, "parent", 0) * scale;
+ boneData->x = cJSON_GetObjectFloat(boneMap, "x", 0) * scale;
+ boneData->y = cJSON_GetObjectFloat(boneMap, "y", 0) * scale;
+ boneData->rotation = cJSON_GetObjectFloat(boneMap, "rotation", 0);
+ boneData->scaleX = cJSON_GetObjectFloat(boneMap, "scaleX", 1);
+ boneData->scaleY = cJSON_GetObjectFloat(boneMap, "scaleY", 1);
+
+ skeletonData->bones[i] = boneData;
+ skeletonData->boneCount++;
+ }
+
+ cJSON* slots = cJSON_GetObjectItem(root, "slots");
+ if (slots) {
+ int slotCount = cJSON_GetArraySize(slots);
+ skeletonData->slots = malloc(sizeof(SlotData*) * slotCount);
+ for (i = 0; i < slotCount; ++i) {
+ cJSON* slotMap = cJSON_GetArrayItem(slots, i);
+
+ const char* slotName = cJSON_GetObjectString(slotMap, "name", 0);
+
+ const char* boneName = cJSON_GetObjectString(slotMap, "bone", 0);
+ BoneData* boneData = SkeletonData_findBone(skeletonData, boneName);
+ if (!boneData) return SkeletonJson_setError(root, "Slot bone not found: ", boneName);
+
+ SlotData* slotData = SlotData_create(slotName, boneData);
+
+ const char* color = cJSON_GetObjectString(slotMap, "color", 0);
+ if (color) {
+ slotData->r = toColor(color, 0);
+ slotData->g = toColor(color, 1);
+ slotData->b = toColor(color, 2);
+ slotData->a = toColor(color, 3);
+ }
+
+ cJSON *attachmentItem = cJSON_GetObjectItem(slotMap, "attachment");
+ if (attachmentItem) SlotData_setAttachmentName(slotData, attachmentItem->valuestring);
+
+ skeletonData->slots[i] = slotData;
+ skeletonData->slotCount++;
+ }
+ }
+
+ cJSON* skinsMap = cJSON_GetObjectItem(root, "skins");
+ if (skinsMap) {
+ int skinCount = cJSON_GetArraySize(skinsMap);
+ skeletonData->skins = malloc(sizeof(Skin*) * skinCount);
+ for (i = 0; i < skinCount; ++i) {
+ cJSON* slotMap = cJSON_GetArrayItem(skinsMap, i);
+ const char* skinName = slotMap->name;
+ Skin *skin = Skin_create(skinName);
+ skeletonData->skins[i] = skin;
+ skeletonData->skinCount++;
+ if (strcmp(skinName, "default") == 0) skeletonData->defaultSkin = skin;
+
+ int slotNameCount = cJSON_GetArraySize(slotMap);
+ for (ii = 0; ii < slotNameCount; ++ii) {
+ cJSON* attachmentsMap = cJSON_GetArrayItem(slotMap, ii);
+ const char* slotName = attachmentsMap->name;
+ int slotIndex = SkeletonData_findSlotIndex(skeletonData, slotName);
+
+ int attachmentCount = cJSON_GetArraySize(attachmentsMap);
+ for (iii = 0; iii < attachmentCount; ++iii) {
+ cJSON* attachmentMap = cJSON_GetArrayItem(attachmentsMap, iii);
+ const char* attachmentName = attachmentMap->name;
+
+ const char* typeString = cJSON_GetObjectString(attachmentMap, "type", "region");
+ AttachmentType type;
+ if (strcmp(typeString, "region") == 0)
+ type = ATTACHMENT_REGION;
+ else if (strcmp(typeString, "regionSequence") == 0)
+ type = ATTACHMENT_REGION_SEQUENCE;
+ else
+ return SkeletonJson_setError(root, "Unknown attachment type: ", typeString);
+
+ Attachment* attachment = Attachment_getAttachmentLoader()(type,
+ cJSON_GetObjectString(attachmentMap, "name", attachmentName));
+
+ if (type == ATTACHMENT_REGION || type == ATTACHMENT_REGION_SEQUENCE) {
+ RegionAttachment* regionAttachment = (RegionAttachment*)attachment;
+ regionAttachment->x = cJSON_GetObjectFloat(attachmentMap, "x", 0) * scale;
+ regionAttachment->y = cJSON_GetObjectFloat(attachmentMap, "y", 0) * scale;
+ regionAttachment->scaleX = cJSON_GetObjectFloat(attachmentMap, "scaleX", 1);
+ regionAttachment->scaleY = cJSON_GetObjectFloat(attachmentMap, "scaleY", 1);
+ regionAttachment->rotation = cJSON_GetObjectFloat(attachmentMap, "rotation", 0);
+ regionAttachment->width = cJSON_GetObjectFloat(attachmentMap, "width", 32) * scale;
+ regionAttachment->height = cJSON_GetObjectFloat(attachmentMap, "height", 32) * scale;
+ RegionAttachment_updateOffset(regionAttachment);
+ }
+
+ Skin_addAttachment(skin, slotIndex, attachmentName, attachment);
+ }
+ }
+ }
+ }
+
+ cJSON_dispose(root);
+ return skeletonData;
+}
diff --git a/spine-c/src/spine/SkeletonJson.h b/spine-c/src/spine/SkeletonJson.h
new file mode 100644
index 000000000..b71ac6f5f
--- /dev/null
+++ b/spine-c/src/spine/SkeletonJson.h
@@ -0,0 +1,26 @@
+#ifndef SPINE_SKELETONJSON_H_
+#define SPINE_SKELETONJSON_H_
+
+#include
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+void SkeletonJson_setScale (float scale);
+
+SkeletonData* SkeletonJson_readSkeletonData (const char* json);
+SkeletonData* SkeletonJson_readSkeletonDataFile (const char* path);
+
+// Animation* readAnimation (char* json, const SkeletonData *skeletonData) const;
+
+const char* SkeletonJson_getError ();
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_SKELETONJSON_H_ */
diff --git a/spine-c/src/spine/Skin.c b/spine-c/src/spine/Skin.c
new file mode 100644
index 000000000..250fbcd4e
--- /dev/null
+++ b/spine-c/src/spine/Skin.c
@@ -0,0 +1,52 @@
+#include
+#include
+
+SkinEntry* SkinEntry_create (int slotIndex, const char* name, Attachment* attachment) {
+ SkinEntry* entry = calloc(1, sizeof(SkinEntry));
+ entry->slotIndex = slotIndex;
+ MALLOC_STR(entry->name, name)
+ entry->attachment = attachment;
+ return entry;
+}
+
+void SkinEntry_dispose (SkinEntry* entry) {
+ if (entry->next) SkinEntry_dispose((SkinEntry*)entry->next);
+ Attachment_dispose(entry->attachment);
+ FREE(entry->name)
+ FREE(entry)
+}
+
+//
+
+Skin* Skin_create (const char* name) {
+ Skin* this = calloc(1, sizeof(Skin));
+ MALLOC_STR(this->name, name)
+ return this;
+}
+
+void Skin_dispose (Skin* this) {
+ SkinEntry_dispose((SkinEntry*)this->entryList);
+ FREE(this->name)
+ FREE(this)
+}
+
+void Skin_addAttachment (Skin* this, int slotIndex, const char* name, Attachment* attachment) {
+ SkinEntry* newEntry = SkinEntry_create(slotIndex, name, attachment);
+ SkinEntry* entry = (SkinEntry*)this->entryList;
+ if (!entry)
+ entry = newEntry;
+ else {
+ while (entry->next)
+ entry = (SkinEntry*)entry->next;
+ entry->next = newEntry;
+ }
+}
+
+Attachment* Skin_getAttachment (const Skin* this, int slotIndex, const char* name) {
+ const SkinEntry* entry = this->entryList;
+ while (entry) {
+ if (entry->slotIndex == slotIndex && strcmp(entry->name, name) == 0) return entry->attachment;
+ entry = entry->next;
+ }
+ return 0;
+}
diff --git a/spine-c/src/spine/Skin.h b/spine-c/src/spine/Skin.h
new file mode 100644
index 000000000..c1890bdb4
--- /dev/null
+++ b/spine-c/src/spine/Skin.h
@@ -0,0 +1,37 @@
+#ifndef SPINE_SKIN_H_
+#define SPINE_SKIN_H_
+
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef struct SkinEntry SkinEntry;
+struct SkinEntry {
+ int slotIndex;
+ const char* name;
+ Attachment* attachment;
+ const SkinEntry* next;
+};
+
+typedef struct {
+ const char* const name;
+ const SkinEntry* const entryList;
+} Skin;
+
+Skin* Skin_create (const char* name);
+void Skin_dispose (Skin* skin);
+
+/** The Skin owns the attachment. */
+void Skin_addAttachment (Skin* skin, int slotIndex, const char* name, Attachment* attachment);
+/** May return null. */
+Attachment* Skin_getAttachment (const Skin* skin, int slotIndex, const char* name);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_SKIN_H_ */
diff --git a/spine-c/src/spine/Slot.c b/spine-c/src/spine/Slot.c
new file mode 100644
index 000000000..692c78f75
--- /dev/null
+++ b/spine-c/src/spine/Slot.c
@@ -0,0 +1,58 @@
+#include
+#include
+#include
+
+typedef struct {
+ Slot slot;
+ float attachmentTime;
+} Private;
+
+Slot* Slot_create (SlotData* data, Skeleton* skeleton, Bone* bone) {
+ Private* private = calloc(1, sizeof(Private));
+ Slot* this = &private->slot;
+ CAST(SlotData*, this->data) = data;
+ CAST(Skeleton*, this->skeleton) = skeleton;
+ CAST(Bone*, this->bone) = bone;
+ this->r = 1;
+ this->g = 1;
+ this->b = 1;
+ this->a = 1;
+ return this;
+}
+
+void Slot_dispose (Slot* this) {
+ FREE(this);
+}
+
+/** @param attachment May be null. */
+void Slot_setAttachment (Slot* this, Attachment* attachment) {
+ CAST(Attachment*, this->attachment) = attachment;
+ ((Private*)this)->attachmentTime = this->skeleton->time;
+}
+
+void Slot_setAttachmentTime (Slot* this, float time) {
+ ((Private*)this)->attachmentTime = this->skeleton->time - time;
+}
+
+float Slot_getAttachmentTime (const Slot* this) {
+ return this->skeleton->time - ((Private*)this)->attachmentTime;
+}
+
+void Slot_setToBindPose (Slot* this) {
+ this->r = this->data->r;
+ this->g = this->data->g;
+ this->b = this->data->b;
+ this->a = this->data->a;
+
+ Attachment* attachment = 0;
+ if (this->data->attachmentName) {
+ int i;
+ for (i = 0; i < this->skeleton->data->slotCount; ++i) {
+ if (this->data == this->skeleton->data->slots[i]) {
+ attachment = Skeleton_getAttachmentForSlotIndex(this->skeleton, i, this->data->attachmentName);
+ break;
+ }
+ }
+ }
+ Slot_setAttachment(this, attachment);
+}
diff --git a/spine-c/src/spine/Slot.h b/spine-c/src/spine/Slot.h
new file mode 100644
index 000000000..d6c50502f
--- /dev/null
+++ b/spine-c/src/spine/Slot.h
@@ -0,0 +1,39 @@
+#ifndef SPINE_SLOT_H_
+#define SPINE_SLOT_H_
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+struct Skeleton;
+
+typedef struct {
+ SlotData* const data;
+ struct Skeleton* const skeleton;
+ Bone* const bone;
+ float r, g, b, a;
+ Attachment* const attachment;
+} Slot;
+
+Slot* Slot_create (SlotData* data, struct Skeleton* skeleton, Bone* bone);
+void Slot_dispose (Slot* slot);
+
+/** @param attachment May be null. */
+void Slot_setAttachment (Slot* slot, Attachment* attachment);
+
+void Slot_setAttachmentTime (Slot* slot, float time);
+float Slot_getAttachmentTime (const Slot* slot);
+
+void Slot_setToBindPose (Slot* slot);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_SLOT_H_ */
diff --git a/spine-c/src/spine/SlotData.c b/spine-c/src/spine/SlotData.c
new file mode 100644
index 000000000..cdc508986
--- /dev/null
+++ b/spine-c/src/spine/SlotData.c
@@ -0,0 +1,27 @@
+#include
+#include
+
+SlotData* SlotData_create (const char* name, BoneData* boneData) {
+ SlotData* this = calloc(1, sizeof(SlotData));
+ MALLOC_STR(this->name, name)
+ CAST(BoneData*, this->boneData) = boneData;
+ this->r = 1;
+ this->g = 1;
+ this->b = 1;
+ this->a = 1;
+ return this;
+}
+
+void SlotData_dispose (SlotData* this) {
+ FREE(this->name);
+ FREE(this->attachmentName);
+ FREE(this);
+}
+
+void SlotData_setAttachmentName (SlotData* this, const char* attachmentName) {
+ FREE(this->attachmentName);
+ if (attachmentName)
+ MALLOC_STR(this->attachmentName, attachmentName)
+ else
+ CAST(char*, this->attachmentName) = 0;
+}
diff --git a/spine-c/src/spine/SlotData.h b/spine-c/src/spine/SlotData.h
new file mode 100644
index 000000000..34543efca
--- /dev/null
+++ b/spine-c/src/spine/SlotData.h
@@ -0,0 +1,29 @@
+#ifndef SPINE_SLOTDATA_H_
+#define SPINE_SLOTDATA_H_
+
+#include
+
+#ifdef __cplusplus
+namespace spine {
+extern "C" {
+#endif
+
+typedef struct {
+ const char* const name;
+ const BoneData* const boneData;
+ const char* const attachmentName;
+ float r, g, b, a;
+} SlotData;
+
+SlotData* SlotData_create (const char* name, BoneData* boneData);
+void SlotData_dispose (SlotData* slotData);
+
+/** @param attachmentName May be zero. */
+void SlotData_setAttachmentName (SlotData* this, const char* attachmentName);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* SPINE_SLOTDATA_H_ */
diff --git a/spine-c/src/spine/cJSON.c b/spine-c/src/spine/cJSON.c
new file mode 100644
index 000000000..7e42c047c
--- /dev/null
+++ b/spine-c/src/spine/cJSON.c
@@ -0,0 +1,391 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* cJSON */
+/* JSON parser in C. */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const char* ep;
+
+const char* cJSON_GetErrorPtr (void) {
+ return ep;
+}
+
+static int cJSON_strcasecmp (const char* s1, const char* s2) {
+ if (!s1) return (s1 == s2) ? 0 : 1;
+ if (!s2) return 1;
+ for (; tolower(*s1) == tolower(*s2); ++s1, ++s2)
+ if (*s1 == 0) return 0;
+ return tolower(*(const unsigned char*)s1) - tolower(*(const unsigned char*)s2);
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_create_Item (void) {
+ cJSON* node = (cJSON*)malloc(sizeof(cJSON));
+ if (node) memset(node, 0, sizeof(cJSON));
+ return node;
+}
+
+/* Delete a cJSON structure. */
+void cJSON_dispose (cJSON *c) {
+ cJSON *next;
+ while (c) {
+ next = c->next;
+ if (!(c->type & cJSON_IsReference) && c->child) cJSON_dispose(c->child);
+ if (!(c->type & cJSON_IsReference) && c->valuestring) free((char*)c->valuestring);
+ if (c->name) free((char*)c->name);
+ free(c);
+ c = next;
+ }
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char* parse_number (cJSON *item, const char* num) {
+ float n = 0, sign = 1, scale = 0;
+ int subscale = 0, signsubscale = 1;
+
+ /* Could use sscanf for this? */
+ if (*num == '-') sign = -1, num++; /* Has sign? */
+ if (*num == '0') num++; /* is zero */
+ if (*num >= '1' && *num <= '9') do
+ n = (n * 10.0) + (*num++ - '0');
+ while (*num >= '0' && *num <= '9'); /* Number? */
+ if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
+ num++;
+ do
+ n = (n * 10.0) + (*num++ - '0'), scale--;
+ while (*num >= '0' && *num <= '9');
+ } /* Fractional part? */
+ if (*num == 'e' || *num == 'E') /* Exponent? */
+ {
+ num++;
+ if (*num == '+')
+ num++;
+ else if (*num == '-') signsubscale = -1, num++; /* With sign? */
+ while (*num >= '0' && *num <= '9')
+ subscale = (subscale * 10) + (*num++ - '0'); /* Number? */
+ }
+
+ n = sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
+
+ item->valuefloat = n;
+ item->valueint = (int)n;
+ item->type = cJSON_Number;
+ return num;
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
+static const char* parse_string (cJSON *item, const char* str) {
+ const char* ptr = str + 1;
+ char* ptr2;
+ char* out;
+ int len = 0;
+ unsigned uc, uc2;
+ if (*str != '\"') {
+ ep = str;
+ return 0;
+ } /* not a string! */
+
+ while (*ptr != '\"' && *ptr && ++len)
+ if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
+
+ out = (char*)malloc(len + 1); /* This is how long we need for the string, roughly. */
+ if (!out) return 0;
+
+ ptr = str + 1;
+ ptr2 = out;
+ while (*ptr != '\"' && *ptr) {
+ if (*ptr != '\\')
+ *ptr2++ = *ptr++;
+ else {
+ ptr++;
+ switch (*ptr) {
+ case 'b':
+ *ptr2++ = '\b';
+ break;
+ case 'f':
+ *ptr2++ = '\f';
+ break;
+ case 'n':
+ *ptr2++ = '\n';
+ break;
+ case 'r':
+ *ptr2++ = '\r';
+ break;
+ case 't':
+ *ptr2++ = '\t';
+ break;
+ case 'u': /* transcode utf16 to utf8. */
+ sscanf(ptr + 1, "%4x", &uc);
+ ptr += 4; /* get the unicode char. */
+
+ if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid. */
+
+ if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */
+ {
+ if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate. */
+ sscanf(ptr + 3, "%4x", &uc2);
+ ptr += 6;
+ if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate. */
+ uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
+ }
+
+ len = 4;
+ if (uc < 0x80)
+ len = 1;
+ else if (uc < 0x800)
+ len = 2;
+ else if (uc < 0x10000) len = 3;
+ ptr2 += len;
+
+ switch (len) {
+ case 4:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 3:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 2:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 1:
+ *--ptr2 = (uc | firstByteMark[len]);
+ }
+ ptr2 += len;
+ break;
+ default:
+ *ptr2++ = *ptr;
+ break;
+ }
+ ptr++;
+ }
+ }
+ *ptr2 = 0;
+ if (*ptr == '\"') ptr++;
+ item->valuestring = out;
+ item->type = cJSON_String;
+ return ptr;
+}
+
+/* Predeclare these prototypes. */
+static const char* parse_value (cJSON *item, const char* value);
+static const char* parse_array (cJSON *item, const char* value);
+static const char* parse_object (cJSON *item, const char* value);
+
+/* Utility to jump whitespace and cr/lf */
+static const char* skip (const char* in) {
+ while (in && *in && (unsigned char)*in <= 32)
+ in++;
+ return in;
+}
+
+/* Parse an object - create a new root, and populate. */
+cJSON *cJSON_ParseWithOpts (const char* value, const char* *return_parse_end, int require_null_terminated) {
+ const char* end = 0;
+ cJSON *c = cJSON_create_Item();
+ ep = 0;
+ if (!c) return 0; /* memory fail */
+
+ end = parse_value(c, skip(value));
+ if (!end) {
+ cJSON_dispose(c);
+ return 0;
+ } /* parse failure. ep is set. */
+
+ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+ if (require_null_terminated) {
+ end = skip(end);
+ if (*end) {
+ cJSON_dispose(c);
+ ep = end;
+ return 0;
+ }
+ }
+ if (return_parse_end) *return_parse_end = end;
+ return c;
+}
+
+/* Default options for cJSON_Parse */
+cJSON *cJSON_Parse (const char* value) {
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char* parse_value (cJSON *item, const char* value) {
+ if (!value) return 0; /* Fail on null. */
+ if (!strncmp(value, "null", 4)) {
+ item->type = cJSON_NULL;
+ return value + 4;
+ }
+ if (!strncmp(value, "false", 5)) {
+ item->type = cJSON_False;
+ return value + 5;
+ }
+ if (!strncmp(value, "true", 4)) {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ return value + 4;
+ }
+ if (*value == '\"') {
+ return parse_string(item, value);
+ }
+ if (*value == '-' || (*value >= '0' && *value <= '9')) {
+ return parse_number(item, value);
+ }
+ if (*value == '[') {
+ return parse_array(item, value);
+ }
+ if (*value == '{') {
+ return parse_object(item, value);
+ }
+
+ ep = value;
+ return 0; /* failure. */
+}
+
+/* Build an array from input text. */
+static const char* parse_array (cJSON *item, const char* value) {
+ cJSON *child;
+ if (*value != '[') {
+ ep = value;
+ return 0;
+ } /* not an array! */
+
+ item->type = cJSON_Array;
+ value = skip(value + 1);
+ if (*value == ']') return value + 1; /* empty array. */
+
+ item->child = child = cJSON_create_Item();
+ if (!item->child) return 0; /* memory fail */
+ value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+
+ while (*value == ',') {
+ cJSON *new_item;
+ if (!(new_item = cJSON_create_Item())) return 0; /* memory fail */
+ child->next = new_item;
+ new_item->prev = child;
+ child = new_item;
+ value = skip(parse_value(child, skip(value + 1)));
+ if (!value) return 0; /* memory fail */
+ }
+
+ if (*value == ']') return value + 1; /* end of array */
+ ep = value;
+ return 0; /* malformed. */
+}
+
+/* Build an object from the text. */
+static const char* parse_object (cJSON *item, const char* value) {
+ cJSON *child;
+ if (*value != '{') {
+ ep = value;
+ return 0;
+ } /* not an object! */
+
+ item->type = cJSON_Object;
+ value = skip(value + 1);
+ if (*value == '}') return value + 1; /* empty array. */
+
+ item->child = child = cJSON_create_Item();
+ if (!item->child) return 0;
+ value = skip(parse_string(child, skip(value)));
+ if (!value) return 0;
+ child->name = child->valuestring;
+ child->valuestring = 0;
+ if (*value != ':') {
+ ep = value;
+ return 0;
+ } /* fail! */
+ value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+
+ while (*value == ',') {
+ cJSON *new_item;
+ if (!(new_item = cJSON_create_Item())) return 0; /* memory fail */
+ child->next = new_item;
+ new_item->prev = child;
+ child = new_item;
+ value = skip(parse_string(child, skip(value + 1)));
+ if (!value) return 0;
+ child->name = child->valuestring;
+ child->valuestring = 0;
+ if (*value != ':') {
+ ep = value;
+ return 0;
+ } /* fail! */
+ value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+ }
+
+ if (*value == '}') return value + 1; /* end of array */
+ ep = value;
+ return 0; /* malformed. */
+}
+
+/* Get Array size/item / object item. */
+int cJSON_GetArraySize (cJSON *array) {
+ cJSON *c = array->child;
+ int i = 0;
+ while (c)
+ i++, c = c->next;
+ return i;
+}
+
+cJSON *cJSON_GetArrayItem (cJSON *array, int item) {
+ cJSON *c = array->child;
+ while (c && item > 0)
+ item--, c = c->next;
+ return c;
+}
+
+cJSON *cJSON_GetObjectItem (cJSON *object, const char* string) {
+ cJSON *c = object->child;
+ while (c && cJSON_strcasecmp(c->name, string))
+ c = c->next;
+ return c;
+}
+
+const char* cJSON_GetObjectString (cJSON* object, const char* name, const char* defaultValue) {
+ object = cJSON_GetObjectItem(object, name);
+ if (object) return object->valuestring;
+ return defaultValue;
+}
+
+float cJSON_GetObjectFloat (cJSON* value, const char* name, float defaultValue) {
+ value = cJSON_GetObjectItem(value, name);
+ return value ? value->valuefloat : defaultValue;
+}
+
+int cJSON_GetObjectInt (cJSON* value, const char* name, int defaultValue) {
+ value = cJSON_GetObjectItem(value, name);
+ return value ? value->valuefloat : defaultValue;
+}
diff --git a/spine-c/src/spine/cJSON.h b/spine-c/src/spine/cJSON.h
new file mode 100644
index 000000000..a76e7e456
--- /dev/null
+++ b/spine-c/src/spine/cJSON.h
@@ -0,0 +1,84 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* Esoteric Software: Removed everything except parsing, added cJSON_GetObject*, formatted, double to float. */
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/* cJSON Types: */
+#define cJSON_False 0
+#define cJSON_True 1
+#define cJSON_NULL 2
+#define cJSON_Number 3
+#define cJSON_String 4
+#define cJSON_Array 5
+#define cJSON_Object 6
+
+#define cJSON_IsReference 256
+
+/* The cJSON structure: */
+typedef struct cJSON {
+ struct cJSON *next, *prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+ int type; /* The type of the item, as above. */
+
+ const char* valuestring; /* The item's string, if type==cJSON_String */
+ int valueint; /* The item's number, if type==cJSON_Number */
+ float valuefloat; /* The item's number, if type==cJSON_Number */
+
+ const char* name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+} cJSON;
+
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_dispose when finished. */
+extern cJSON *cJSON_Parse (const char* value);
+
+/* Delete a cJSON entity and all subentities. */
+extern void cJSON_dispose (cJSON *c);
+
+/* Returns the number of items in an array (or object). */
+extern int cJSON_GetArraySize (cJSON *array);
+
+/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
+extern cJSON *cJSON_GetArrayItem (cJSON *array, int item);
+
+/* Get item "string" from object. Case insensitive. */
+extern cJSON *cJSON_GetObjectItem (cJSON *object, const char* string);
+extern const char* cJSON_GetObjectString (cJSON* object, const char* name, const char* defaultValue);
+extern float cJSON_GetObjectFloat (cJSON* value, const char* name, float defaultValue);
+extern int cJSON_GetObjectInt (cJSON* value, const char* name, int defaultValue);
+
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+extern const char* cJSON_GetErrorPtr (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/spine-c/src/spine/spine.h b/spine-c/src/spine/spine.h
new file mode 100644
index 000000000..08cfa5661
--- /dev/null
+++ b/spine-c/src/spine/spine.h
@@ -0,0 +1,15 @@
+#ifndef SPINE_SPINE_H_
+#define SPINE_SPINE_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#endif /* SPINE_SPINE_H_ */
diff --git a/spine-c/src/spine/util.c b/spine-c/src/spine/util.c
new file mode 100644
index 000000000..a58161c4d
--- /dev/null
+++ b/spine-c/src/spine/util.c
@@ -0,0 +1,17 @@
+#include
+
+char* readFile (const char* path) {
+ FILE *file = fopen(path, "rb");
+ if (!file) return 0;
+
+ fseek(file, 0, SEEK_END);
+ long length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char* data = (char*)malloc(sizeof(char) * length + 1);
+ fread(data, sizeof(char), length, file);
+ fclose(file);
+ data[length] = '\0';
+
+ return data;
+}
diff --git a/spine-c/src/spine/util.h b/spine-c/src/spine/util.h
new file mode 100644
index 000000000..1435d5170
--- /dev/null
+++ b/spine-c/src/spine/util.h
@@ -0,0 +1,17 @@
+#ifndef SPINE_UTIL_H_
+#define SPINE_UTIL_H_
+
+#include
+#include
+#include
+
+/** Used to cast away const on an lvalue. */
+#define CAST(TYPE,VALUE) *(TYPE*)&VALUE
+
+#define MALLOC_STR(TO,FROM) strcpy(CAST(char*, TO) = malloc(strlen(FROM)), FROM);
+
+#define FREE(E) free((void*)E);
+
+char* readFile (const char* path);
+
+#endif /* SPINE_UTIL_H_ */