Updated for 0.21 and GameData folder.
Updated for 0.21 and GameData folder.

Craft parsing is not currently enabled.

--- a/KSP_Components.py
+++ b/KSP_Components.py
@@ -158,10 +158,16 @@
 	

 	def getValue(self):

 		if self._container._type in ('CRAFT', 'PART'):

-			_Thrust = self._container.getThrustNode().getValue()

-			_MassFlow = self._container.getMassFlowNode().getValue()

+			try:

+				_Thrust = self._container.getThrustNode().getValue()

+				_MassFlow = self._container.getMassFlowNode().getValue()

+			except AttributeError:

+				return super().getValue()

 			results = {}

 			for key, val in _MassFlow.items():

+				if val == 0:

+					results[key] = 0

+					continue

 				results[key] = _Thrust / (val * G_ACCEL)

 			return results

 		else:

@@ -214,6 +220,9 @@
 		self.keyNames = self._container.getSpecImpulseNode().keyNames

 		result = {}

 		for key, val in _SpecImpulse.items():

+			if val == 0:

+				result[key] = 0

+				continue

 			result[key] = _Thrust / (val * G_ACCEL)

 		return result

 

@@ -305,6 +314,13 @@
 		'MODULE': {

 			'ModuleDeployableSolarPanel': {

 				'chargeRate': [(__classname__, 'addElectricRateComponent')]

+			}

+		},

+		'MODULE': {

+			'ModuleWheel': {

+				'ElectricCharge': {

+					'resourceConsumptionRate': [(__classname__, 'addElectricRateComponent')]

+				}

 			}

 		}

 	}

@@ -345,7 +361,7 @@
 		return result

 

 class DeltaV(MultiValueComponent):

-	moduleDepends = [Acceleration, BurnTime]

+	moduleDepends = [SpecImpulse, Mass, ResourceStorage]

 	HumanName = "Min. delta-V"

 	HumanUnits = "m/s"

 	


--- a/KSP_Part.py
+++ b/KSP_Part.py
@@ -95,6 +95,11 @@
 			return

 		if self._name in componentFilter:

 			return self._checkFilter(key, val, componentFilter[self._name])

+		try:

+			if self._properties['resourceName'] in componentFilter:

+				return self._checkFilter(key, val, componentFilter[self._properties['resourceName']])

+		except KeyError:

+			pass

 		if self._type in componentFilter:

 			return self._checkFilter(key, val, componentFilter[self._type])

 	

@@ -155,6 +160,11 @@
 		if cls._resource_parent is None:

 			cls._resource_parent = r

 		return r

+	

+	def _AddChildByIndex(self, child, idx=None):

+		super()._AddChildByIndex(child, idx)

+		if self is not self.__class__._resource_parent:

+			self.__class__._resource_parent._AddChildByIndex(child, idx)

 	

 	@classmethod

 	def get(cls, idx):


--- a/KSP_PartParser.py
+++ b/KSP_PartParser.py
@@ -8,8 +8,8 @@
 from KSP_Part import *

 import KSP_Components

 from KSPP_Config import Config

-from _tools import warn

 from collections import OrderedDict

+import os, glob

 

 def printCSV(parts):

 	csv_columns = OrderedDict({o.__name__: [o.HumanName] for o in Component.__subclasses__() if o is not MultiValueComponent})

@@ -22,6 +22,20 @@
 				for key in getattr(part, "get{0}Node".format(mvc.__name__))().getValue().keys():

 					csv_columns[mvc.__name__].add(key)

 	

+	csv_columns.move_to_end('TotalImpulse', last=False)

+	csv_columns.move_to_end('DeltaV', last=False)

+	csv_columns.move_to_end('BurnTime', last=False)

+	csv_columns.move_to_end('FuelUsage', last=False)

+	csv_columns.move_to_end('MassFlow', last=False)

+	csv_columns.move_to_end('FuelRatio', last=False)

+	csv_columns.move_to_end('SpecImpulse', last=False)

+	csv_columns.move_to_end('ResourceStorage', last=False)

+	csv_columns.move_to_end('ThrustWeight', last=False)

+	csv_columns.move_to_end('Thrust', last=False)

+	csv_columns.move_to_end('Mass', last=False)

+	csv_columns.move_to_end('CrewCapacity', last=False)

+	csv_columns.move_to_end('ElectricRate', last=False)

+	csv_columns.move_to_end('Acceleration', last=False)

 	csv_columns.move_to_end('Title', last=False)

 	header1 = ["{0} {1}".format(o, k) for o in csv_columns for k in csv_columns[o]]

 	

@@ -48,64 +62,124 @@
 			row += classfields

 		print(','.join(row))

 

+def FindDirsByName(RootDir, Needle):

+	Dirs = []

+	

+	path = RootDir + os.sep

+	for Entry in os.listdir(RootDir):

+		Entry = path + Entry

+		if not os.path.isdir(Entry):

+			continue

+		if os.path.normcase(Entry.split(os.sep)[-1]) == os.path.normcase(Needle):

+			Dirs += [Entry]

+			continue

+		Dirs += (FindDirsByName(Entry, Needle))

+	

+	return Dirs

+

+def FindPartsDirs(GameDataDir):

+	return FindDirsByName(GameDataDir, 'Parts')

+

+def FindResourceCfgs(RootDir):

+	ResourceDirs = FindDirsByName(RootDir, 'Resources')

+	ResourceFiles = []

+	

+	for Dir in ResourceDirs:

+		ResourceFiles += glob.glob(Dir + os.sep + "*.cfg")

+	

+	return ResourceFiles

+

+def FindFilesByName(RootDir, Needle):

+	Files = []

+	

+	path = RootDir + os.sep

+	for Entry in os.listdir(RootDir):

+		if os.path.normcase(Entry) == os.path.normcase(Needle):

+			return [path + Entry]

+		Entry = path + Entry

+		if not os.path.isdir(Entry):

+			continue

+		Files += (FindFilesByName(Entry, Needle))

+	

+	return Files

+

+def FindPartCfgs(PartsDir):

+	return FindFilesByName(PartsDir, 'part.cfg')

+

 def main():

-	import argparse, os.path

-	

-	parser = argparse.ArgumentParser(description='Parse a KSP part file.')

-	parser.add_argument('files', metavar='FILE', type=str, nargs='+', help='The file or files to be parsed.')

-	parser.add_argument('-k', '--ksp-dir', metavar='DIR', type=str, help='Path to the KSP main directory.')

-	parser.add_argument('-r', '--resource-file', metavar='FILE', type=str, help='The ResourcesGeneric.cfg file to be used.')

+	import argparse

+	

+	parser = argparse.ArgumentParser(description='Parses a KSP install for part definitions.')

+	parser.add_argument('KSP_DIR', metavar='KSP_DIR', type=str, nargs='?', help='The KSP Directory to be parsed')

+# 	parser.add_argument('-k', '--ksp-dir', metavar='DIR', type=str, help='Path to the KSP main directory.')

+# 	parser.add_argument('-r', '--resource-file', metavar='FILE', type=str, help='The ResourcesGeneric.cfg file to be used.')

 	parser.add_argument('-c', '--csv', action='store_true', help='Creates CSV output for all parsed part files.')

-	parser.add_argument('-p', '--parts-dir', metavar='DIR', type=str, help='Path to the parts dir for use with craft parsing.')

+# 	parser.add_argument('-p', '--parts-dir', metavar='DIR', type=str, help='Path to the parts dir for use with craft parsing.')

 	

 	args = parser.parse_args()

 	

-	files = args.files

-	

-	globfiles = []

-	remove_indexes = []

-	preloadParts = False

-	for filename in files:

-		if filename.count('*') > 0:

-				from glob import glob

-				globfiles += glob(filename)

-				remove_indexes.append(filename)

-		_, ext = os.path.splitext(filename)

-		if ext == '.craft' and not preloadParts:

-			preloadParts = True

-	for idx in remove_indexes:

-		files.remove(idx)

-	files += globfiles

-	

-	if args.ksp_dir is not None:

-		Config()['KSPDir'] = args.ksp_dir

-	else:

-		partdir, _ = os.path.split(files[0])

-		Config()['partsDir'] = partdir

-		Config()['KSPDir'] = os.sep.join((Config()['partsDir'], '..', '..'))

-	

-	if args.parts_dir is not None:

-		Config()['partsDir'] = args.parts_dir

-	

-	if args.resource_file is not None:

-		Config()['resourceFile'] = args.resource_file

-	else:

-		Config()['resourceFile'] = os.sep.join((Config()['KSPDir'], 'Resources', 'ResourcesGeneric.cfg'))

-	

-	Resource.createFromFile(Config()['resourceFile'])

-	

-	_, ext = os.path.splitext(filename)

-		# Let's parse some parts.

-	

-	if preloadParts:

-		from glob import glob

-		preloadFiles = glob(os.sep.join((Config()['partsDir'], '*', 'part.cfg')))

-		for filename in preloadFiles:

-			Part.createFromFile(filename, 1)

+#  	files = args.files

+#  	

+#  	globfiles = []

+#  	remove_indexes = []

+#  	preloadParts = False

+#  	for PartFile in files:

+#  		if PartFile.count('*') > 0:

+#  				from glob import glob

+#  				globfiles += glob(PartFile)

+#  				remove_indexes.append(PartFile)

+#  		_, ext = os.path.splitext(PartFile)

+#  		if ext == '.craft' and not preloadParts:

+#  			preloadParts = True

+#  	for idx in remove_indexes:

+#  		files.remove(idx)

+#  	files += globfiles

+#  	

+#  	if args.ksp_dir is not None:

+#  		Config()['KSPDir'] = args.ksp_dir

+#  	else:

+#  		partdir, _ = os.path.split(files[0])

+#  		Config()['partsDir'] = partdir

+#  		Config()['KSPDir'] = os.sep.join((Config()['partsDir'], '..', '..'))

+#  	

+#  	if args.parts_dir is not None:

+#  		Config()['partsDir'] = args.parts_dir

+#  	

+#  	if args.resource_file is not None:

+#  		Config()['resourceFile'] = args.resource_file

+#  	else:

+#  		Config()['resourceFile'] = os.sep.join((Config()['KSPDir'], 'Resources', 'ResourcesGeneric.cfg'))

+#  	

+#  	Resource.createFromFile(Config()['resourceFile'])

+#  	

+#  	_, ext = os.path.splitext(PartFile)

+#  		# Let's parse some parts.

+#  	

+#  	if preloadParts:

+#  		from glob import glob

+#  		preloadFiles = glob(os.sep.join((Config()['partsDir'], '*', 'part.cfg')))

+#  		for PartFile in preloadFiles:

+#  			Part.createFromFile(PartFile, 1)

+	

+	if args.KSP_DIR is not None:

+		Config()['KSPDir'] = args.KSP_DIR

+	elif os.path.isfile("KSP.exe"):

+		Config()['KSPDir'] = "."

+	

+	Config()['KSPDir'] = Config()['KSPDir'].rstrip(os.sep)

+	

+	Config()['GameDataDir'] = Config()['KSPDir'] + os.sep + "GameData"

+	

+	PartFiles = FindPartCfgs(Config()['KSPDir'])

+	

+	ResourceFiles = FindResourceCfgs(Config()['KSPDir'])

+	

+	for ResourceFile in ResourceFiles:

+		Resource.createFromFile(ResourceFile)

 	

 	results = []

-	for filename in files:

-		_, ext = os.path.splitext(filename)

+	for PartFile in PartFiles:

+		_, ext = os.path.splitext(PartFile)

 		

 		ext = ext.lower()

 		if ext == '.cfg':

@@ -113,7 +187,7 @@
 		elif ext == '.craft':

 			cls = Craft

 		

-		result = cls.createFromFile(filename)

+		result = cls.createFromFile(PartFile)

 		

 		results.append(result)