Skip to content

Using The Library — No Mod Launcher

Proddy edited this page May 10, 2023 · 1 revision

There are times when you need to make bulk edits to Pure3D files, or need to easily extract data from them. Doing this is easier outside of the mod launcher.

All the tutorials on this page assume that your script has the library loaded, as explained in Getting Started.

Extracting data

An example I've personally used this for, is to get the Composite Drawable name for every character in the game.

1) Getting the file list

Given that we already have DirectoryGetEntries from the mod launcher, we can use this to iterate a directory and get all the files in that directory. I created a little helper function to do so:

-- Helper function to process a directory and get all files of a certain extension
local function ProcessDir(Path, Files, Ext)
	-- Convert the extension to lowercase for case-insensitive matching
	Ext = Ext:lower()
	-- Begin iterating the directory
	DirectoryGetEntries(Path, function(File, IsDirectory)
		if IsDirectory then
			-- If a directory is found, call the same function to process sub directories
			ProcessDir(string.format("%s/%s", Path, File), Files, Ext)
		else
			-- Get the file's extension, if it has one and it equals the extension filter, add the file path to the `Files` table.
			local ext = GetFileExtension(File)
			if ext and ext:lower() == Ext then
				Files[#Files + 1] = string.format("%s/%s", Path, File)
			end
		end
		
		return true
	end)
end

-- Get all P3D files in the chars folder
local files = {}
ProcessDir("C:\\SHAR\\art\\chars", files, ".p3d")

2) Parse each Pure3D file

Now that we have the chars file list, we can run each file through LuaP3DLib to get access to all the chunk data in the file.

-- Iterate the chars list
for i=1,#files do
	local file = files[i]

	-- Create a `P3DFile` for the file
	local P3DFile = P3D.P3DFile(file)

	-- Get the first Composite Drawable chunk
	local CompositeDrawable = P3DFile:GetChunk(P3D.Identifiers.Composite_Drawable)

	-- Check if the chunk was found - `GetChunk` returns `nil` if not found
	if CompositeDrawable then
		-- Print the filename and the name of the Composite Drawable to console
		print(file, CompositeDrawable.Name)
	end
end

Modifying data

There are no file write functions in the mod launcher at this time, so if you want to programatically modify Pure3D files, it has to be done externally. One example of this is to remove all chunks of a type. For this tutorial, we'll use locators.

1) Getting the file list

Using the function in the previous step, we can get all the Pure3D files by calling the function on the art folder.

-- Get all P3D files in the art folder
local files = {}
ProcessDir("C:\\SHAR\\art", files, ".p3d")

2) Modifying each Pure3D file

Similar to the last step, we now loop each file and run it through LuaP3DLib. However, this time, we're going to modify the file.

-- Iterate the files list
for i=1,#files do
	local file = files[i]

	-- Create a `P3DFile` for the file
	local P3DFile = P3D.P3DFile(file)

	-- Create a variable to check if the file was modified. No point saving an unchanged file.
	local modified = false

	-- Loop all Locator chunks in the file, using `true` as the second parameter so that we loop backwards
	-- Specifically when removing chunks is this necessary
	for ChunkIndex, Chunk in P3DFile:GetChunksIndexed(P3D.Identifiers.Locator, true) do
		-- Here you could run a check on any chunk data, in the case of a Locator you could check the Type or Name for example
		P3DFile:RemoveChunk(ChunkIndex) -- `RemoveChunk` takes either an index or a chunk itself, however index is more efficient

		-- We found at Locator chunk, so the file has been modified
		modified = true
	end

	-- If the file is modified, save it with `.modified` appended to the filename
	if modified then
		local fileName = file:gsub("%.p3d", ".modified.p3d")
		-- Open the filepath as `wb`. `w` means we're writing the file, without appending, so existing files will be overwritten. `b` means we're writing binary data, not just plain strings
		local outFile = io.open(fileName, "wb")
		-- Calling `tostring` on a `P3DFile` builds the Pure3D data
		outFile:write(tostring(P3DFile))
		-- Make sure you close your file handlers
		outFile:close()
	end
end