192 lines
6.8 KiB
GDScript
192 lines
6.8 KiB
GDScript
@tool
|
|
extends VBoxContainer
|
|
|
|
# ─── State ────────────────────────────────────────────────────────────────────
|
|
var _mpq = null # MPQManager
|
|
|
|
const LOCALES := ["ruRU", "enUS", "enGB", "deDE", "frFR", "esES", "esMX",
|
|
"koKR", "zhCN", "zhTW", "ptBR", "ptPT"]
|
|
|
|
# ─── UI nodes (created in _ready) ─────────────────────────────────────────────
|
|
var _client_path_edit: LineEdit
|
|
var _locale_option: OptionButton
|
|
var _filter_edit: LineEdit
|
|
var _output_edit: LineEdit
|
|
var _open_btn: Button
|
|
var _extract_btn: Button
|
|
var _list_btn: Button
|
|
var _status_label: Label
|
|
var _file_list: ItemList
|
|
var _archive_list: ItemList
|
|
var _progress: ProgressBar
|
|
|
|
# ─── Build UI ─────────────────────────────────────────────────────────────────
|
|
func _ready() -> void:
|
|
size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
add_theme_constant_override("separation", 6)
|
|
|
|
_add_title()
|
|
_add_separator()
|
|
_add_client_row()
|
|
_add_locale_row()
|
|
_open_btn = _add_button("Open Client Archives", _on_open_pressed)
|
|
_add_separator()
|
|
_add_label("Loaded archives (highest priority first):")
|
|
_archive_list = _add_item_list(100)
|
|
_add_separator()
|
|
_add_filter_row()
|
|
_add_output_row()
|
|
var btns := HBoxContainer.new(); add_child(btns)
|
|
_list_btn = _add_button_to("List Files", btns, _on_list_pressed)
|
|
_extract_btn = _add_button_to("Extract", btns, _on_extract_pressed)
|
|
_progress = ProgressBar.new(); _progress.visible = false; add_child(_progress)
|
|
_add_label("Files matching filter:")
|
|
_file_list = _add_item_list(0, true) # expand vertically
|
|
_status_label = _add_label("Ready.")
|
|
|
|
_extract_btn.disabled = true
|
|
_list_btn.disabled = true
|
|
|
|
|
|
# ─── UI helpers ───────────────────────────────────────────────────────────────
|
|
func _add_title() -> void:
|
|
var lbl := Label.new()
|
|
lbl.text = "MPQ Extractor (WoW 3.3.5a)"
|
|
lbl.add_theme_font_size_override("font_size", 14)
|
|
add_child(lbl)
|
|
|
|
func _add_separator() -> void:
|
|
add_child(HSeparator.new())
|
|
|
|
func _add_label(text: String) -> Label:
|
|
var lbl := Label.new(); lbl.text = text
|
|
add_child(lbl); return lbl
|
|
|
|
func _add_item_list(min_height: int, expand := false) -> ItemList:
|
|
var il := ItemList.new()
|
|
if min_height > 0:
|
|
il.custom_minimum_size = Vector2(0, min_height)
|
|
if expand:
|
|
il.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
add_child(il); return il
|
|
|
|
func _add_button(text: String, cb: Callable) -> Button:
|
|
var btn := Button.new(); btn.text = text
|
|
btn.pressed.connect(cb); add_child(btn); return btn
|
|
|
|
func _add_button_to(text: String, parent: Control, cb: Callable) -> Button:
|
|
var btn := Button.new()
|
|
btn.text = text
|
|
btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
btn.pressed.connect(cb); parent.add_child(btn); return btn
|
|
|
|
func _add_row(label_text: String) -> HBoxContainer:
|
|
var row := HBoxContainer.new(); add_child(row)
|
|
var lbl := Label.new()
|
|
lbl.text = label_text
|
|
lbl.custom_minimum_size = Vector2(90, 0)
|
|
row.add_child(lbl); return row
|
|
|
|
func _add_client_row() -> void:
|
|
var row := _add_row("Client path")
|
|
_client_path_edit = LineEdit.new()
|
|
_client_path_edit.text = "res://sources"
|
|
_client_path_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_client_path_edit.placeholder_text = "res://sources"
|
|
row.add_child(_client_path_edit)
|
|
|
|
func _add_locale_row() -> void:
|
|
var row := _add_row("Locale")
|
|
_locale_option = OptionButton.new()
|
|
_locale_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
for loc in LOCALES:
|
|
_locale_option.add_item(loc)
|
|
row.add_child(_locale_option)
|
|
|
|
func _add_filter_row() -> void:
|
|
var row := _add_row("Filter")
|
|
_filter_edit = LineEdit.new()
|
|
_filter_edit.text = "*"
|
|
_filter_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_filter_edit.placeholder_text = "DBFilesClient\\*.dbc"
|
|
row.add_child(_filter_edit)
|
|
|
|
func _add_output_row() -> void:
|
|
var row := _add_row("Output dir")
|
|
_output_edit = LineEdit.new()
|
|
_output_edit.text = "res://data/extracted"
|
|
_output_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
row.add_child(_output_edit)
|
|
|
|
|
|
# ─── Handlers ─────────────────────────────────────────────────────────────────
|
|
func _on_open_pressed() -> void:
|
|
if not ClassDB.class_exists("MPQManager"):
|
|
_set_status("ERROR: MPQManager не найден. Собери GDExtension (build.bat Release).")
|
|
return
|
|
|
|
if _mpq:
|
|
_mpq.call("close")
|
|
_mpq = ClassDB.instantiate("MPQManager")
|
|
|
|
var client_path: String = ProjectSettings.globalize_path(_client_path_edit.text)
|
|
var locale: String = LOCALES[_locale_option.selected]
|
|
|
|
_set_status("Opening archives…")
|
|
var count: int = _mpq.call("open_client", client_path, locale)
|
|
|
|
if count == 0:
|
|
_set_status("ERROR: no archives opened. Check client path / locale.")
|
|
_extract_btn.disabled = true
|
|
_list_btn.disabled = true
|
|
return
|
|
|
|
_archive_list.clear()
|
|
var info_list: Array = _mpq.call("get_archive_info")
|
|
for info in info_list:
|
|
var lbl_text := "[%d] %s (%d files)" % [
|
|
info["priority"],
|
|
(info["path"] as String).get_file(),
|
|
info["file_count"]
|
|
]
|
|
_archive_list.add_item(lbl_text)
|
|
|
|
_set_status("Opened %d archives. Locale: %s" % [count, locale])
|
|
_extract_btn.disabled = false
|
|
_list_btn.disabled = false
|
|
|
|
|
|
func _on_list_pressed() -> void:
|
|
if not _mpq: return
|
|
_file_list.clear()
|
|
_set_status("Listing files…")
|
|
|
|
var filter: String = _filter_edit.text if not _filter_edit.text.is_empty() else "*"
|
|
var files: PackedStringArray = _mpq.call("list_files", filter)
|
|
for f in files:
|
|
_file_list.add_item(f)
|
|
_set_status("Found %d files matching '%s'" % [files.size(), filter])
|
|
|
|
|
|
func _on_extract_pressed() -> void:
|
|
if not _mpq: return
|
|
|
|
var filter: String = _filter_edit.text if not _filter_edit.text.is_empty() else "*"
|
|
var output_dir: String = ProjectSettings.globalize_path(_output_edit.text)
|
|
|
|
_set_status("Extracting…")
|
|
_progress.visible = true
|
|
_progress.value = 0
|
|
|
|
var count: int = _mpq.call("extract_files", filter, output_dir)
|
|
|
|
_progress.value = 100
|
|
_progress.visible = false
|
|
_set_status("Extracted %d files → %s" % [count, output_dir])
|
|
|
|
|
|
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
func _set_status(text: String) -> void:
|
|
_status_label.text = text
|
|
print("[MPQTool] ", text)
|