mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 06:00:16 +01:00
# Conflicts: # .github/CODEOWNERS # .github/workflows/compile_changelogs.yml # .github/workflows/stale.yml # SQL/database_changelog.md # _maps/map_files/CatwalkStation/CatwalkStation_2023.dmm # code/__DEFINES/atom_hud.dm # code/__DEFINES/inventory.dm # code/__DEFINES/mobs.dm # code/__DEFINES/species_clothing_paths.dm # code/__DEFINES/subsystems.dm # code/__DEFINES/surgery.dm # code/__HELPERS/global_lists.dm # code/_globalvars/lists/maintenance_loot.dm # code/_globalvars/traits/_traits.dm # code/controllers/subsystem/minor_mapping.dm # code/controllers/subsystem/processing/quirks.dm # code/controllers/subsystem/shuttle.dm # code/datums/components/palette.dm # code/datums/components/surgery_initiator.dm # code/datums/diseases/advance/advance.dm # code/datums/hud.dm # code/datums/mood.dm # code/datums/mutations/chameleon.dm # code/datums/quirks/negative_quirks/nyctophobia.dm # code/datums/status_effects/debuffs/debuffs.dm # code/datums/status_effects/debuffs/drunk.dm # code/datums/status_effects/debuffs/slime/slime_leech.dm # code/datums/weather/weather.dm # code/game/data_huds.dm # code/game/objects/items.dm # code/game/objects/items/devices/scanners/health_analyzer.dm # code/game/objects/items/frog_statue.dm # code/game/objects/items/rcd/RLD.dm # code/game/objects/items/robot/items/hypo.dm # code/game/objects/items/stacks/medical.dm # code/game/objects/items/stacks/wrap.dm # code/game/objects/items/storage/garment.dm # code/game/objects/items/tools/medical/defib.dm # code/game/objects/items/weaponry.dm # code/game/objects/items/weaponry/melee/misc.dm # code/game/objects/structures/crates_lockers/closets/secure/security.dm # code/game/objects/structures/curtains.dm # code/game/objects/structures/dresser.dm # code/game/objects/structures/girders.dm # code/game/objects/structures/maintenance.dm # code/game/objects/structures/mirror.dm # code/modules/admin/greyscale_modify_menu.dm # code/modules/admin/verbs/light_debug.dm # code/modules/antagonists/ashwalker/ashwalker.dm # code/modules/antagonists/heretic/knowledge/starting_lore.dm # code/modules/antagonists/ninja/ninjaDrainAct.dm # code/modules/art/paintings.dm # code/modules/client/preferences.dm # code/modules/client/verbs/ooc.dm # code/modules/clothing/head/wig.dm # code/modules/events/disease_outbreak.dm # code/modules/holodeck/holo_effect.dm # code/modules/jobs/job_types/head_of_security.dm # code/modules/jobs/job_types/security_officer.dm # code/modules/library/skill_learning/generic_skillchips/point.dm # code/modules/mining/lavaland/ash_flora.dm # code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm # code/modules/mob/dead/new_player/new_player.dm # code/modules/mob/living/basic/guardian/guardian.dm # code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm # code/modules/mob/living/carbon/carbon.dm # code/modules/mob/living/carbon/human/human.dm # code/modules/mob/living/carbon/human/human_defines.dm # code/modules/mob/living/carbon/life.dm # code/modules/mob/living/living.dm # code/modules/mob/living/living_defines.dm # code/modules/mob/mob.dm # code/modules/mob_spawn/ghost_roles/mining_roles.dm # code/modules/mod/mod_control.dm # code/modules/mod/modules/modules_general.dm # code/modules/modular_computers/computers/item/computer_ui.dm # code/modules/paperwork/paper.dm # code/modules/paperwork/paperbin.dm # code/modules/power/lighting/light.dm # code/modules/projectiles/guns/energy/kinetic_accelerator.dm # code/modules/projectiles/projectile.dm # code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm # code/modules/reagents/chemistry/reagents/food_reagents.dm # code/modules/reagents/chemistry/reagents/other_reagents.dm # code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm # code/modules/research/xenobiology/crossbreeding/_clothing.dm # code/modules/research/xenobiology/crossbreeding/prismatic.dm # code/modules/surgery/advanced/brainwashing.dm # code/modules/surgery/advanced/lobotomy.dm # code/modules/surgery/amputation.dm # code/modules/surgery/blood_filter.dm # code/modules/surgery/bodyparts/_bodyparts.dm # code/modules/surgery/brain_surgery.dm # code/modules/surgery/cavity_implant.dm # code/modules/surgery/coronary_bypass.dm # code/modules/surgery/gastrectomy.dm # code/modules/surgery/healing.dm # code/modules/surgery/limb_augmentation.dm # code/modules/surgery/organ_manipulation.dm # code/modules/surgery/revival.dm # code/modules/surgery/sleeper_protocol.dm # code/modules/surgery/surgery_helpers.dm # code/modules/surgery/surgery_step.dm # code/modules/unit_tests/_unit_tests.dm # code/modules/unit_tests/designs.dm # code/modules/unit_tests/icon_state_worn.dm # code/modules/unit_tests/screenshots/screenshot_antag_icons_cultist.png # code/modules/unit_tests/screenshots/screenshot_antag_icons_headrevolutionary.png # code/modules/unit_tests/screenshots/screenshot_antag_icons_provocateur.png # code/modules/unit_tests/screenshots/screenshot_husk_body.png # code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png # icons/map_icons/clothing/head/_head.dmi # icons/map_icons/clothing/shoes.dmi # icons/map_icons/items/_item.dmi # icons/mob/huds/hud.dmi # icons/mob/inhands/64x64_lefthand.dmi # icons/mob/inhands/64x64_righthand.dmi # icons/obj/machines/computer.dmi # tgui/packages/tgui/interfaces/OperatingComputer.jsx # tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx # tgui/packages/tgui/interfaces/PreferencesMenu/types.ts # tgui/packages/tgui/interfaces/SurgeryInitiator.tsx # tools/icon_cutter/check.py
141 lines
5.0 KiB
Python
141 lines
5.0 KiB
Python
import glob
|
|
import os
|
|
import re
|
|
import sys
|
|
import hashlib
|
|
import platform
|
|
import subprocess
|
|
from PIL import Image
|
|
import numpy as np
|
|
import difflib
|
|
|
|
BUF_SIZE = 65536 # lets read stuff in 64kb chunks!
|
|
chop_filename = re.compile(r"^.*(\..+?)$", re.M)
|
|
chop_extension = re.compile(r"^(.*)\..+?$", re.M)
|
|
|
|
def reshape_split(image: np.ndarray, cut_to: tuple):
|
|
img_height, img_width, channels = image.shape
|
|
|
|
tile_height, tile_width = cut_to
|
|
|
|
tiled_array = image.reshape(img_height // tile_height,
|
|
tile_height,
|
|
img_width // tile_width,
|
|
tile_width,
|
|
channels)
|
|
tiled_array = tiled_array.swapaxes(1, 2)
|
|
tiled_array = tiled_array.reshape(tile_height, -1, channels)
|
|
return tiled_array
|
|
|
|
def get_file_hash(path):
|
|
path_suffix = re.sub(chop_filename, r"\1", path, count = 1)
|
|
if path_suffix == ".dmi" or path_suffix == ".png":
|
|
return hash_dmi(path)
|
|
else:
|
|
return hash_file(path)
|
|
|
|
def hash_dmi(path):
|
|
md5 = hashlib.md5()
|
|
|
|
dmi = Image.open(path)
|
|
dmi = dmi.convert('RGBA')
|
|
dmi.load() # Needed only for .png EXIF data (see citation above)
|
|
dmi_metadata = dmi.info['Description']
|
|
md5.update(dmi_metadata.encode('utf-8'))
|
|
|
|
readable_metadata = dict(
|
|
map(lambda entry: (entry[0], entry[1]),
|
|
map(lambda entry : (entry[0].strip(), entry[1].strip()),
|
|
filter(lambda entry: entry[0].strip() == 'width' or entry[0].strip() == 'height',
|
|
map(lambda entry : entry.split("="), dmi_metadata.split("\n"))))))
|
|
|
|
icon_hash = hashlib.md5()
|
|
divided_dmi = reshape_split(np.asarray(dmi), (int(readable_metadata['height']), int(readable_metadata['width'])))
|
|
for i in range(divided_dmi.shape[0]):
|
|
bytes = divided_dmi[1].tobytes()
|
|
md5.update(bytes)
|
|
icon_hash.update(bytes)
|
|
return (md5.hexdigest(), dmi_metadata, icon_hash.hexdigest())
|
|
|
|
def hash_file(path):
|
|
md5 = hashlib.md5()
|
|
|
|
with open(path, 'rb') as f:
|
|
while True:
|
|
data = f.read(BUF_SIZE)
|
|
if not data:
|
|
break
|
|
md5.update(data)
|
|
|
|
return (md5.hexdigest(), None, None)
|
|
|
|
path_to_us = os.path.realpath(os.path.dirname(__file__))
|
|
pass_count = 0
|
|
fail_count = 0
|
|
output_hash = {}
|
|
files = []
|
|
if platform.system() == "Windows":
|
|
files = glob.glob(f"{path_to_us}\\..\\..\\icons\\**\\*.toml", recursive = True)
|
|
files += glob.glob(f"{path_to_us}\..\\..\\modular_zubbers\\icons\\**\*.toml", recursive = True) # BUBBER EDIT ADDITION: Modular icon cutter
|
|
else:
|
|
files = glob.glob(f"{path_to_us}/../../icons/**/*.toml", recursive = True)
|
|
files += glob.glob(f"{path_to_us}\../../modular_zubbers/icons/**\*.toml", recursive = True) # BUBBER EDIT ADDITION: Modular icon cutter
|
|
for cutter_template in files:
|
|
resource_name = re.sub(chop_extension, r"\1", cutter_template, count = 1)
|
|
if not os.path.isfile(resource_name):
|
|
print(f"::error template={cutter_template} exists but lacks a matching resource file ({resource_name})")
|
|
fail_count += 1
|
|
continue
|
|
|
|
output_name = re.sub(chop_extension, r"\1.dmi", resource_name, count = 1)
|
|
if not os.path.isfile(output_name):
|
|
print(f"::error template={cutter_template} and resource={resource_name} exist but they lack a matching output={output_name}. (Try rebuilding)")
|
|
fail_count += 1
|
|
continue
|
|
|
|
output_hash[output_name] = get_file_hash(output_name)
|
|
|
|
# Sanity check
|
|
if len(output_hash) == 0:
|
|
print(f"::error output_hash dict was empty. Something has gone wrong")
|
|
sys.exit(1)
|
|
|
|
# Execute cutter
|
|
if platform.system() == "Windows":
|
|
subprocess.run(f"{path_to_us}\\..\\build\\build.bat --force-recut --ci icon-cutter")
|
|
else:
|
|
subprocess.run(f"{path_to_us}/../build/build.sh --force-recut --ci icon-cutter", shell = True)
|
|
|
|
for output_name in output_hash:
|
|
old_hash, old_metadata, old_icon_hash = output_hash[output_name]
|
|
new_hash, new_metadata, new_icon_hash = get_file_hash(output_name)
|
|
if old_hash == new_hash:
|
|
pass_count += 1
|
|
continue
|
|
if old_metadata != new_metadata:
|
|
print("Metadata differs!")
|
|
events = ""
|
|
current_op = None
|
|
working = ""
|
|
for index, op in enumerate(difflib.ndiff(old_metadata, new_metadata)):
|
|
in_nothing = False
|
|
if current_op == None:
|
|
current_op = op[0]
|
|
if current_op != op[0]:
|
|
events += f"{current_op*10}\n{working}\n"
|
|
current_op = op[0]
|
|
working = ""
|
|
if op[0]== ' ':
|
|
continue
|
|
working += f"{op[-1]}"
|
|
events += f"{current_op*10}\n{working}\n"
|
|
print(events, end="")
|
|
if old_icon_hash != new_icon_hash:
|
|
print("Icon hashes differ!")
|
|
fail_count += 1
|
|
print(f"::error output={output_name} and its templates all exist but were not comitted fully compiled")
|
|
|
|
print(f"{len(output_hash)} templates checked, {pass_count} passed, {fail_count} failed", end="")
|
|
if fail_count > 0:
|
|
sys.exit(1)
|