@@ -106,8 +106,13 @@ def _read_txtg_texture_result(file_data: bytes, texture_name: str, logical_path:
106106
107107
108108def export_archive_file_to_temp (archive_path : str , internal_path : str , romfs_path : str = "" ) -> str :
109- file_data = read_archive_file_bytes (archive_path , internal_path , romfs_path )
110- file_name = Path (internal_path ).name or "file.bin"
109+ if not internal_path :
110+ file_data = Path (archive_path ).read_bytes ()
111+ file_name = Path (archive_path ).name or "file.bin"
112+ else :
113+ file_data = read_archive_file_bytes (archive_path , internal_path , romfs_path )
114+ file_name = Path (internal_path ).name or "file.bin"
115+
111116 safe_name = "" .join (ch if ch .isalnum () or ch in "._-" else "_" for ch in file_name )
112117 fd , tmp_path = tempfile .mkstemp (prefix = "totk-tool-" , suffix = f"-{ safe_name } " )
113118 with os .fdopen (fd , "wb" ) as out :
@@ -587,6 +592,142 @@ def main():
587592 )
588593 )
589594
595+ elif command == "export-converted" :
596+ internal_path = sys .argv [3 ]
597+ target_ext = sys .argv [4 ].lower ()
598+ if internal_path :
599+ file_data = read_archive_file_bytes (archive_path , internal_path , romfs_path )
600+ logical_path = internal_path
601+ else :
602+ file_data = Path (archive_path ).read_bytes ()
603+ logical_path = archive_path
604+
605+ kind = _file_kind (logical_path , file_data , romfs_path )
606+
607+ out_path = export_archive_file_to_temp (archive_path , internal_path , romfs_path )
608+
609+ if target_ext in [".png" , ".jpg" , ".jpeg" , ".bmp" , ".tga" , ".dds" ]:
610+ png_path = None
611+ is_txtg = kind == "txtg" or logical_path .lower ().endswith (".txtg" ) or file_data [4 :8 ] == b"6PK0"
612+
613+ bntx_data = None
614+ tex_name = None
615+
616+ if is_txtg :
617+ from txtg_reader import read_txtg_texture_result
618+ tex_name = Path (logical_path ).name or "texture"
619+ res = read_txtg_texture_result (file_data , tex_name )
620+ png_path = res .get ("pngPath" )
621+ else :
622+ bntx_ctx = _resolve_bntx_for_read (archive_path , internal_path , romfs_path )
623+ if bntx_ctx is not None :
624+ bntx_data , tex_name = bntx_ctx
625+ from bntx_renderer import render_texture_to_png
626+ png_path = render_texture_to_png (bntx_data , tex_name )
627+
628+ if png_path :
629+ try :
630+ from PIL import Image
631+ img = Image .open (png_path )
632+ fd , cvt_path = tempfile .mkstemp (prefix = "totk-cvt-" , suffix = target_ext )
633+ os .close (fd )
634+
635+ if target_ext == ".dds" :
636+ native_dds_bytes = None
637+ if not is_txtg and bntx_data is not None and tex_name is not None :
638+ from bntx_renderer import extract_texture_linear
639+ extracted = extract_texture_linear (bntx_data , tex_name )
640+ if extracted is not None :
641+ tex_width , tex_height , decoder_key , linear , mip_count , format_id = extracted
642+ if decoder_key .startswith ("bc" ):
643+ dxgi_map = {
644+ 0x1A : 72 if (format_id & 0xFF ) == 0x06 else 71 ,
645+ 0x1B : 75 if (format_id & 0xFF ) == 0x06 else 74 ,
646+ 0x1C : 78 if (format_id & 0xFF ) == 0x06 else 77 ,
647+ 0x1D : 81 if (format_id & 0xFF ) == 0x02 else 80 ,
648+ 0x1E : 84 if (format_id & 0xFF ) == 0x02 else 83 ,
649+ 0x1F : 96 if (format_id & 0xFF ) == 0x02 else 95 ,
650+ 0x20 : 99 if (format_id & 0xFF ) == 0x06 else 98 ,
651+ }
652+ dxgi_format = dxgi_map .get (format_id >> 8 )
653+ if dxgi_format is not None :
654+ header = bytearray (148 )
655+ header [0 :4 ] = b"DDS "
656+ header [4 :8 ] = (124 ).to_bytes (4 , "little" )
657+ header [8 :12 ] = (0x1 | 0x2 | 0x4 | 0x1000 | 0x80000 ).to_bytes (4 , "little" )
658+ header [12 :16 ] = tex_height .to_bytes (4 , "little" )
659+ header [16 :20 ] = tex_width .to_bytes (4 , "little" )
660+ header [20 :24 ] = len (linear ).to_bytes (4 , "little" )
661+ header [24 :28 ] = (1 ).to_bytes (4 , "little" )
662+ header [28 :32 ] = (mip_count ).to_bytes (4 , "little" )
663+
664+ header [76 :80 ] = (32 ).to_bytes (4 , "little" )
665+ header [80 :84 ] = (0x4 ).to_bytes (4 , "little" )
666+ header [84 :88 ] = b"DX10"
667+
668+ header [108 :112 ] = (0x1000 ).to_bytes (4 , "little" )
669+
670+ header [128 :132 ] = dxgi_format .to_bytes (4 , "little" )
671+ header [132 :136 ] = (3 ).to_bytes (4 , "little" )
672+ header [136 :140 ] = (0 ).to_bytes (4 , "little" )
673+ header [140 :144 ] = (1 ).to_bytes (4 , "little" )
674+ header [144 :148 ] = (0 ).to_bytes (4 , "little" )
675+
676+ native_dds_bytes = bytes (header ) + linear
677+
678+ if native_dds_bytes is not None :
679+ with open (cvt_path , "wb" ) as f :
680+ f .write (native_dds_bytes )
681+ else :
682+ img = img .convert ("RGBA" )
683+ width , height = img .size
684+ pixels = img .tobytes ("raw" , "RGBA" )
685+ header = bytearray (128 )
686+ header [0 :4 ] = b"DDS "
687+ header [4 :8 ] = (124 ).to_bytes (4 , "little" )
688+ header [8 :12 ] = (0x100F ).to_bytes (4 , "little" )
689+ header [12 :16 ] = height .to_bytes (4 , "little" )
690+ header [16 :20 ] = width .to_bytes (4 , "little" )
691+ header [20 :24 ] = (width * 4 ).to_bytes (4 , "little" )
692+ header [24 :28 ] = (1 ).to_bytes (4 , "little" )
693+ header [28 :32 ] = (1 ).to_bytes (4 , "little" )
694+ header [76 :80 ] = (32 ).to_bytes (4 , "little" )
695+ header [80 :84 ] = (0x41 ).to_bytes (4 , "little" )
696+ header [88 :92 ] = (32 ).to_bytes (4 , "little" )
697+ header [92 :96 ] = (0x000000FF ).to_bytes (4 , "little" )
698+ header [96 :100 ] = (0x0000FF00 ).to_bytes (4 , "little" )
699+ header [100 :104 ] = (0x00FF0000 ).to_bytes (4 , "little" )
700+ header [104 :108 ] = (0xFF000000 ).to_bytes (4 , "little" )
701+ header [108 :112 ] = (0x1000 ).to_bytes (4 , "little" )
702+ with open (cvt_path , "wb" ) as f :
703+ f .write (header )
704+ f .write (pixels )
705+ else :
706+ if target_ext in [".jpg" , ".jpeg" ] and img .mode in ("RGBA" , "P" ):
707+ img = img .convert ("RGB" )
708+ img .save (cvt_path )
709+
710+ img .close ()
711+ os .unlink (png_path )
712+ if os .path .exists (out_path ):
713+ os .unlink (out_path )
714+ out_path = cvt_path
715+ except Exception :
716+ import traceback
717+ traceback .print_exc ()
718+ out_path = png_path
719+
720+ elif target_ext in [".yaml" , ".yml" ]:
721+ if kind == "byml" or kind == "bgyml" :
722+ yaml_text = read_byml_content (file_data , internal_path , romfs_path )
723+ fd , yaml_path = tempfile .mkstemp (prefix = "totk-cvt-" , suffix = target_ext )
724+ os .close (fd )
725+ Path (yaml_path ).write_text (yaml_text , encoding = "utf-8" )
726+ os .unlink (out_path )
727+ out_path = yaml_path
728+
729+ print (json .dumps ({"path" : out_path }))
730+
590731 elif command == "write-raw" :
591732 internal_path = sys .argv [3 ]
592733 encoded = sys .stdin .read ()
0 commit comments