Skip to content

Instantly share code, notes, and snippets.

@lowercasebtw
Last active March 30, 2025 20:40
Show Gist options
  • Select an option

  • Save lowercasebtw/c107c2038d3d405cd441525b0555d26a to your computer and use it in GitHub Desktop.

Select an option

Save lowercasebtw/c107c2038d3d405cd441525b0555d26a to your computer and use it in GitHub Desktop.
import os, shutil, json
from jproperties import Properties
from os import path
from json import JSONEncoder
CONVERSION_SUFFIX = "_converted"
MINECRAFT_PATH = path.join("assets", "minecraft")
ATLASES_PATH = path.join(MINECRAFT_PATH, "atlases")
TEXTURES_PATH = path.join(MINECRAFT_PATH, "textures")
OPTIFINE_PATH = path.join(MINECRAFT_PATH, "optifine")
ITEMS_PATH = path.join(MINECRAFT_PATH, "items")
MODELS_PATH = path.join(MINECRAFT_PATH, "models")
ITEM_MODELS_PATH = path.join(MODELS_PATH, "item")
CIT_PATH = path.join(OPTIFINE_PATH, "cit")
TEXTURES_CIT_SUFFIX = "cit_textures"
class NamespacedKey(JSONEncoder):
def __init__(self, namespace: str, path: str):
self.namespace = namespace
self.path = path
@staticmethod
def from_string(input: str):
if len(input) == 0:
raise Exception("Invalid namespaced identifier")
parts = input.split(':')
if len(parts) > 2:
raise Exception("Invalid namespaced identifier")
is_single = len(parts) == 1
return NamespacedKey("minecraft" if is_single else parts[0], parts[0] if is_single else parts[1])
def __eq__(self, other):
if isinstance(other, NamespacedKey):
return self.namespace == other.namespace and self.path == other.path
return False
def __hash__(self):
return hash(self.namespace) + hash(self.path)
def __str__(self):
return f"{self.namespace}:{self.path}"
def __repr__(self):
return self.__str__()
class Optional:
def __init__(self, value):
self.value = value
self.has_value = not value is None
@staticmethod
def of_nullable(value):
return Optional(value)
@staticmethod
def of(value):
if value is None:
raise Exception("Value is null")
return Optional(value)
@staticmethod
def empty():
return Optional.of_nullable(None)
class OptiFineCITItem:
def __init__(self, name: str, item: NamespacedKey):
self.name = name
self.item = item
self.enchantments = []
self.enchantment_level = 0
@staticmethod
def parse(name: str, input: str):
properties = Properties()
properties.load(input)
if not "items" in properties:
print("Failed to parse OptiFineCITItem, missing \"items\" key")
return Optional.empty()
if not "type" in properties or not properties.get("type").data == "item":
print("Failed to parse OptiFineCITItem, not a item")
return Optional.empty()
cit_item = OptiFineCITItem(name, NamespacedKey.from_string(properties.get("items").data))
if "enchantmentIDs" in properties:
cit_item.with_enchantments(properties.get("enchantmentIDs").data)
if "enchantmentLevels" in properties:
cit_item.with_enchantment_level(properties.get("enchantmentLevels").data)
return Optional.of(cit_item)
# TODO: Support real syntax as this is probably totally wrong for anything else LOL
def with_enchantments(self, enchantments: str):
self.enchantments = map(lambda enchantment: NamespacedKey.from_string(enchantment), enchantments.replace(' ', '').split(','))
return self
def with_enchantment_level(self, level: int):
self.enchantment_level = int(level)
return self
def map_paths(root: os.PathLike, paths: list[str]):
return map(lambda p: path.join(root, p), paths)
def ensure_path(p: os.PathLike) -> os.PathLike:
if not path.exists(p):
os.makedirs(p)
return p
def generate_condition_branch(obj: dict[str, any], entry: dict[str, any]) -> dict[str, str | list | dict]:
dict = {
'type': "minecraft:condition",
'property': "minecraft:component",
'predicate': "stored_enchantments" # TODO: Support both stored & normal
}
data = entry['data']
dict['value'] = [{'enchantments': list(map(lambda key: str(key), data['enchantments']))}]
if "levels" in data:
dict['value'][0]['levels'] = data['levels']
dict['on_true'] = {
'type': "minecraft:model",
'model': f"minecraft:item/cit/{entry['name']}"
}
dict['on_false'] = obj
return dict
def generate_condition(key: NamespacedKey, entries: list[dict[str, any]]) -> dict[str, str | dict]:
dict = { 'type': "minecraft:model", 'model': f"{key.namespace}:item/{key.path}" }
for entry in entries:
dict = generate_condition_branch(dict, entry)
return dict
def generate_atlas(dst: os.PathLike):
NEW_ATLASES_PATH = ensure_path(path.join(dst, ATLASES_PATH))
# TODO: Append to existing blocks.json if it exists
with open(path.join(NEW_ATLASES_PATH, "blocks.json"), "w+") as file:
file.write(json.dumps({
'sources': [
{
'type': "directory",
'source': "cit_textures",
'prefix': "textures/cit_textures/"
}
]
}, indent=4))
file.close()
def filter_items(cit_path: os.PathLike) -> tuple[list[OptiFineCITItem], bool]:
failed = False
cit_items = []
for properties_file in map_paths(cit_path, filter(lambda p: p.endswith(".properties"), os.listdir(cit_path))):
with open(properties_file) as file:
print(f" Converting {properties_file}")
cit_optional = OptiFineCITItem.parse(os.path.splitext(os.path.basename(properties_file))[0], file.read())
if not cit_optional.has_value:
failed = True
file.close()
break
cit_items.append(cit_optional.value)
file.close()
os.remove(properties_file)
return (cit_items, failed)
def map_duplicate_items(items: list[object]):
duplicates = {}
for cit_item in items:
obj = {
'name': cit_item.name,
'data': {'enchantments': cit_item.enchantments}
}
if cit_item.enchantment_level > 0:
obj['data']['levels'] = {
'min': cit_item.enchantment_level,
'max': cit_item.enchantment_level
}
if cit_item.item in duplicates:
dupes = duplicates.get(cit_item.item)
dupes.append(obj)
else:
duplicates[cit_item.item] = [obj]
return duplicates
def cleanup(dst: os.PathLike, cit_path: os.PathLike):
print("Cleaning up...")
if len(os.listdir(cit_path)) == 0:
os.rmdir(cit_path)
NEW_OPTIFINE_PATH = path.join(dst, OPTIFINE_PATH)
if len(os.listdir(NEW_OPTIFINE_PATH)) == 0:
os.rmdir(NEW_OPTIFINE_PATH)
def vanillaify(src: os.PathLike, dst: os.PathLike):
THIS_CIT_PATH = path.join(src, CIT_PATH)
if not path.exists(THIS_CIT_PATH) or not path.isdir(THIS_CIT_PATH):
print(f"{src} did not contain any OptiFine CIT, ignorning...")
return
print(f"Converting {src} to vanilla CIT pack")
shutil.copytree(src, dst)
NEW_CIT_PATH = path.join(dst, CIT_PATH)
NEW_ITEMS_PATH = ensure_path(path.join(dst, ITEMS_PATH))
NEW_TEXTURES_PATH = ensure_path(path.join(dst, TEXTURES_PATH))
PACK_TEXTURES_PATH = path.join(NEW_TEXTURES_PATH, TEXTURES_CIT_SUFFIX)
os.makedirs(PACK_TEXTURES_PATH)
for texture_file in map_paths(NEW_CIT_PATH, filter(lambda p: p.endswith(".png"), os.listdir(NEW_CIT_PATH))):
shutil.move(texture_file, PACK_TEXTURES_PATH)
generate_atlas(dst)
(cit_items, failed) = filter_items(NEW_CIT_PATH)
if failed:
shutil.rmtree(dst)
print("Failed!")
return
# Group into singletons
CIT_ITEM_MODELS_PATH = ensure_path(path.join(dst, ITEM_MODELS_PATH, "cit"))
duplicates = map_duplicate_items(cit_items)
for key in duplicates.keys():
# Item Models
entry = duplicates[key]
for item in entry:
item_name = item['name']
item_path = path.join(CIT_ITEM_MODELS_PATH, f"{item_name}.json")
with open(item_path, "w+") as file:
# TODO: Possibly get original model and swap layer0 texture or whatever
file.write(json.dumps({
'parent': "minecraft:item/generated", # TODO: Get real parent for item type
'textures': {
'layer0': f"minecraft:textures/{TEXTURES_CIT_SUFFIX}/{item_name}"
}
}, indent=4))
file.close()
# Item Defenitions
item_path = path.join(NEW_ITEMS_PATH, f"{key.path}.json")
# exists = path.exists(item_path)
with open(item_path, "w+") as file:
defenition = {'model': generate_condition(key, entry)}
# TODO
# if exists:
# original = json.loads(file.read())
# if "model" in original:
# defenition.model['on_false'] = original['model']
# file.seek(0)
file.write(json.dumps(defenition, indent=4))
file.close()
# Cleanup
cleanup(dst, NEW_CIT_PATH)
print("Done!")
def main():
folders = map_paths('.', filter(lambda key: path.isdir(key) and not key.endswith(CONVERSION_SUFFIX), os.listdir('.')))
for folder in folders:
CONVERTED_PATH_NAME = f"{folder}{CONVERSION_SUFFIX}"
if path.exists(CONVERTED_PATH_NAME):
shutil.rmtree(CONVERTED_PATH_NAME)
print(f"Found pre-existing conversion for {folder}, removing...")
files = os.listdir(folder)
if not "pack.mcmeta" in files:
print(f"Skipping {folder} as it is not a minecraft resourcepack")
return
vanillaify(folder, CONVERTED_PATH_NAME)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment