many text work and scripts
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/0752_.bin
Normal file
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/0752_002_0_4098_U
Normal file
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/0752_002_0_4098_U_back
Normal file
137
UnRLE/0752_SMILE_MALL_TEXTURES/extract.py
Normal file
@@ -0,0 +1,137 @@
|
||||
import struct
|
||||
import os
|
||||
import sys
|
||||
from PIL import Image
|
||||
|
||||
def extract_textures_from_file(filename, output_dir="textures"):
|
||||
"""
|
||||
Извлекает текстуры из файла в отдельные PNG файлы с индексированными цветами
|
||||
"""
|
||||
|
||||
# Создаем директорию для выходных файлов
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Читаем файл
|
||||
with open(filename, 'rb') as f:
|
||||
fil = f.read()
|
||||
|
||||
# Получаем список смещений текстур
|
||||
tx_offsets = []
|
||||
first_addr = struct.unpack('<I', fil[0:4])[0]
|
||||
tx_offsets.append(first_addr)
|
||||
|
||||
reader = 4
|
||||
while reader < first_addr:
|
||||
addr = struct.unpack('<I', fil[reader:reader+4])[0]
|
||||
if addr == 0:
|
||||
break
|
||||
tx_offsets.append(addr)
|
||||
reader += 4
|
||||
|
||||
print(f"Найдено {len(tx_offsets)} текстурных блоков")
|
||||
|
||||
# Обрабатываем каждую текстуру
|
||||
for a in range(len(tx_offsets) - 1):
|
||||
try:
|
||||
reader = tx_offsets[a]
|
||||
|
||||
# Читаем CLUT и информацию об изображении
|
||||
clut_mode = struct.unpack('<I', fil[reader:reader+4])[0]
|
||||
image_mode = struct.unpack('<I', fil[reader+4:reader+8])[0]
|
||||
reader += 8
|
||||
|
||||
# Читаем CLUT (палитру цветов)
|
||||
clut_size = struct.unpack('<I', fil[reader:reader+4])[0]
|
||||
clut_x = struct.unpack('<H', fil[reader+4:reader+6])[0]
|
||||
clut_y = struct.unpack('<H', fil[reader+6:reader+8])[0]
|
||||
clut_w = struct.unpack('<H', fil[reader+8:reader+10])[0]
|
||||
clut_h = struct.unpack('<H', fil[reader+10:reader+12])[0]
|
||||
reader += 12
|
||||
|
||||
# Читаем данные палитры
|
||||
current_clut = []
|
||||
clut_data_remains = clut_size - 12
|
||||
while clut_data_remains > 0:
|
||||
color_code = struct.unpack('<H', fil[reader:reader+2])[0]
|
||||
current_clut.append(color_code)
|
||||
clut_data_remains -= 2
|
||||
reader += 2
|
||||
|
||||
# Читаем информацию об изображении
|
||||
image_size = struct.unpack('<I', fil[reader:reader+4])[0]
|
||||
image_x = struct.unpack('<H', fil[reader+4:reader+6])[0] * 2
|
||||
image_y = struct.unpack('<H', fil[reader+6:reader+8])[0] - 256
|
||||
image_w = struct.unpack('<H', fil[reader+8:reader+10])[0] * 2
|
||||
image_h = struct.unpack('<H', fil[reader+10:reader+12])[0]
|
||||
reader += 12
|
||||
|
||||
print(f"Текстура {a}: {image_w}x{image_h}, CLUT: {len(current_clut)} цветов")
|
||||
|
||||
# Создаем палитру для PIL
|
||||
palette = []
|
||||
for color_code in current_clut:
|
||||
# Конвертируем из формата PS1 (15-bit) в 24-bit RGB
|
||||
r = (color_code & 0x1F) * 8
|
||||
g = ((color_code & 0x3E0) >> 5) * 8
|
||||
b = ((color_code & 0x7C00) >> 10) * 8
|
||||
palette.extend([r, g, b])
|
||||
|
||||
# Дополняем палитру до 256 цветов (если нужно)
|
||||
while len(palette) < 768: # 256 цветов * 3 канала
|
||||
palette.append(0)
|
||||
|
||||
# Создаем изображение с индексированными цветами
|
||||
img_data = []
|
||||
image_data_remains = image_size - 12
|
||||
|
||||
for y in range(image_h):
|
||||
row = []
|
||||
for x in range(image_w):
|
||||
if image_data_remains > 0:
|
||||
color_index = fil[reader]
|
||||
row.append(color_index)
|
||||
reader += 1
|
||||
image_data_remains -= 1
|
||||
else:
|
||||
row.append(0) # Заполняем нулями если данные закончились
|
||||
img_data.append(row)
|
||||
|
||||
# Создаем изображение из данных
|
||||
img = Image.new('P', (image_w, image_h))
|
||||
|
||||
# Заполняем пиксели
|
||||
for y in range(image_h):
|
||||
for x in range(image_w):
|
||||
if y < len(img_data) and x < len(img_data[y]):
|
||||
img.putpixel((x, y), img_data[y][x])
|
||||
|
||||
# Устанавливаем палитру
|
||||
img.putpalette(palette)
|
||||
|
||||
# Сохраняем как PNG
|
||||
output_filename = os.path.join(output_dir, f"texture_{a:03d}.png")
|
||||
img.save(output_filename, 'PNG')
|
||||
|
||||
print(f"Сохранено: {output_filename}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Ошибка при обработке текстуры {a}: {e}")
|
||||
continue
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("Использование: python extract.py <имя_файла>")
|
||||
sys.exit(1)
|
||||
|
||||
filename = sys.argv[1]
|
||||
|
||||
if not os.path.exists(filename):
|
||||
print(f"Файл {filename} не найден!")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Обрабатываю файл: {filename}")
|
||||
extract_textures_from_file(filename)
|
||||
print("Готово!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
220
UnRLE/0752_SMILE_MALL_TEXTURES/import.py
Normal file
@@ -0,0 +1,220 @@
|
||||
import struct
|
||||
import os
|
||||
import sys
|
||||
from PIL import Image
|
||||
|
||||
def import_texture_to_pack(pack_filename, texture_filename, output_pack_filename=None):
|
||||
"""
|
||||
Заменяет пиксельные данные текстуры в pack-файле на данные из PNG файла
|
||||
"""
|
||||
|
||||
if output_pack_filename is None:
|
||||
output_pack_filename = pack_filename
|
||||
|
||||
# Получаем ID текстуры из имени файла
|
||||
texture_id = extract_texture_id(texture_filename)
|
||||
if texture_id is None:
|
||||
print("Не удалось определить ID текстуры из имени файла!")
|
||||
return False
|
||||
|
||||
print(f"Импортируем текстуру ID: {texture_id} из {texture_filename}")
|
||||
|
||||
# Читаем pack-файл
|
||||
with open(pack_filename, 'rb') as f:
|
||||
pack_data = bytearray(f.read())
|
||||
|
||||
# Находим смещения текстур
|
||||
tx_offsets = find_texture_offsets(pack_data)
|
||||
if texture_id >= len(tx_offsets) - 1:
|
||||
print(f"Ошибка: текстура с ID {texture_id} не найдена в pack-файле!")
|
||||
return False
|
||||
|
||||
# Находим позицию пиксельных данных в pack-файле
|
||||
pixel_data_pos = find_pixel_data_position(pack_data, tx_offsets, texture_id)
|
||||
if pixel_data_pos is None:
|
||||
print(f"Не удалось найти позицию пиксельных данных для текстуры {texture_id}!")
|
||||
return False
|
||||
|
||||
# Получаем информацию о размерах текстуры из pack-файла
|
||||
texture_info = get_texture_info(pack_data, tx_offsets, texture_id)
|
||||
if texture_info is None:
|
||||
print(f"Не удалось получить информацию о текстуре {texture_id}!")
|
||||
return False
|
||||
|
||||
image_w, image_h, image_size = texture_info
|
||||
|
||||
# Загружаем PNG файл
|
||||
try:
|
||||
img = Image.open(texture_filename)
|
||||
except Exception as e:
|
||||
print(f"Ошибка загрузки PNG файла: {e}")
|
||||
return False
|
||||
|
||||
# Проверяем размеры
|
||||
if img.width != image_w or img.height != image_h:
|
||||
print(f"Ошибка: размеры не совпадают!")
|
||||
print(f"Ожидается: {image_w}x{image_h}")
|
||||
print(f"PNG файл: {img.width}x{img.height}")
|
||||
return False
|
||||
|
||||
# Конвертируем в индексированное изображение если нужно
|
||||
if img.mode != 'P':
|
||||
print("Предупреждение: PNG не в индексированном режиме. Конвертируем...")
|
||||
img = img.convert('P')
|
||||
|
||||
# Извлекаем пиксельные данные
|
||||
pixel_data = []
|
||||
for y in range(image_h):
|
||||
for x in range(image_w):
|
||||
try:
|
||||
pixel_index = img.getpixel((x, y))
|
||||
pixel_data.append(pixel_index & 0xFF) #确保是字节
|
||||
except:
|
||||
pixel_data.append(0)
|
||||
|
||||
# Проверяем размер данных
|
||||
expected_pixel_count = image_w * image_h
|
||||
actual_pixel_count = len(pixel_data)
|
||||
|
||||
if actual_pixel_count != expected_pixel_count:
|
||||
print(f"Ошибка: количество пикселей не совпадает!")
|
||||
print(f"Ожидается: {expected_pixel_count}")
|
||||
print(f"Получено: {actual_pixel_count}")
|
||||
return False
|
||||
|
||||
# Заменяем данные в pack-файле
|
||||
bytes_replaced = replace_pixel_data(pack_data, pixel_data_pos, pixel_data, image_size - 12)
|
||||
|
||||
# Сохраняем измененный pack-файл
|
||||
try:
|
||||
with open(output_pack_filename, 'wb') as f:
|
||||
f.write(pack_data)
|
||||
print(f"Успешно заменено {bytes_replaced} байт!")
|
||||
print(f"Сохранено в: {output_pack_filename}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Ошибка сохранения файла: {e}")
|
||||
return False
|
||||
|
||||
def extract_texture_id(filename):
|
||||
"""Извлекает ID текстуры из имени файла"""
|
||||
basename = os.path.basename(filename)
|
||||
# Ищем паттерн texture_XXX.png
|
||||
if basename.startswith('texture_') and basename.endswith('.png'):
|
||||
try:
|
||||
id_str = basename[8:-4] # Убираем 'texture_' и '.png'
|
||||
return int(id_str)
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
def find_texture_offsets(pack_data):
|
||||
"""Находит смещения текстур в pack-файле"""
|
||||
tx_offsets = []
|
||||
first_addr = struct.unpack('<I', pack_data[0:4])[0]
|
||||
tx_offsets.append(first_addr)
|
||||
|
||||
reader = 4
|
||||
while reader < first_addr:
|
||||
addr = struct.unpack('<I', pack_data[reader:reader+4])[0]
|
||||
if addr == 0:
|
||||
break
|
||||
tx_offsets.append(addr)
|
||||
reader += 4
|
||||
|
||||
return tx_offsets
|
||||
|
||||
def find_pixel_data_position(pack_data, tx_offsets, texture_id):
|
||||
"""Находит позицию пиксельных данных для указанной текстуры"""
|
||||
try:
|
||||
reader = tx_offsets[texture_id]
|
||||
|
||||
# Пропускаем режимы CLUT и изображения
|
||||
reader += 8
|
||||
|
||||
# Читаем размер CLUT и пропускаем CLUT данные
|
||||
clut_size = struct.unpack('<I', pack_data[reader:reader+4])[0]
|
||||
reader += clut_size # Пропускаем весь CLUT блок
|
||||
|
||||
# Пропускаем заголовок изображения
|
||||
reader += 12
|
||||
|
||||
# Теперь reader указывает на начало пиксельных данных
|
||||
return reader
|
||||
|
||||
except Exception as e:
|
||||
print(f"Ошибка поиска позиции данных: {e}")
|
||||
return None
|
||||
|
||||
def get_texture_info(pack_data, tx_offsets, texture_id):
|
||||
"""Получает информацию о размерах текстуры"""
|
||||
try:
|
||||
reader = tx_offsets[texture_id]
|
||||
|
||||
# Пропускаем режимы
|
||||
reader += 8
|
||||
|
||||
# Пропускаем CLUT
|
||||
clut_size = struct.unpack('<I', pack_data[reader:reader+4])[0]
|
||||
reader += clut_size
|
||||
|
||||
# Читаем информацию об изображении
|
||||
image_size = struct.unpack('<I', pack_data[reader:reader+4])[0]
|
||||
image_x = struct.unpack('<H', pack_data[reader+4:reader+6])[0] * 2
|
||||
image_y = struct.unpack('<H', pack_data[reader+6:reader+8])[0] - 256
|
||||
image_w = struct.unpack('<H', pack_data[reader+8:reader+10])[0] * 2
|
||||
image_h = struct.unpack('<H', pack_data[reader+10:reader+12])[0]
|
||||
|
||||
return image_w, image_h, image_size
|
||||
|
||||
except Exception as e:
|
||||
print(f"Ошибка получения информации о текстуре: {e}")
|
||||
return None
|
||||
|
||||
def replace_pixel_data(pack_data, start_pos, new_pixel_data, expected_size):
|
||||
"""Заменяет пиксельные данные в pack-файле"""
|
||||
bytes_replaced = 0
|
||||
|
||||
for i, pixel_byte in enumerate(new_pixel_data):
|
||||
if start_pos + i < len(pack_data) and i < expected_size:
|
||||
pack_data[start_pos + i] = pixel_byte
|
||||
bytes_replaced += 1
|
||||
else:
|
||||
break
|
||||
|
||||
return bytes_replaced
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print("Использование: python import.py <pack_file> <texture_file> [output_file]")
|
||||
print("Пример: python import.py game.pack textures/texture_005.png")
|
||||
print("Пример: python import.py game.pack textures/texture_010.png game_modified.pack")
|
||||
sys.exit(1)
|
||||
|
||||
pack_filename = sys.argv[1]
|
||||
texture_filename = sys.argv[2]
|
||||
output_filename = sys.argv[3] if len(sys.argv) > 3 else pack_filename
|
||||
|
||||
if not os.path.exists(pack_filename):
|
||||
print(f"Pack-файл не найден: {pack_filename}")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(texture_filename):
|
||||
print(f"Текстура не найдена: {texture_filename}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Pack файл: {pack_filename}")
|
||||
print(f"Текстура: {texture_filename}")
|
||||
print(f"Выходной файл: {output_filename}")
|
||||
print("-" * 50)
|
||||
|
||||
success = import_texture_to_pack(pack_filename, texture_filename, output_filename)
|
||||
|
||||
if success:
|
||||
print("Импорт завершен успешно!")
|
||||
else:
|
||||
print("Импорт завершен с ошибками!")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/kolbas.psd
Normal file
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_000.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_001.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_002.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_003.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_004.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_005.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_006.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_007.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_008.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_009.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_010.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_011.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_012.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_013.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
UnRLE/0752_SMILE_MALL_TEXTURES/textures/texture_014.png
Normal file
|
After Width: | Height: | Size: 12 KiB |