mirror of
https://github.com/Escartem/AnimeWwise.git
synced 2026-06-05 07:50:23 +08:00
437
LICENCE.md
Normal file
437
LICENCE.md
Normal file
@@ -0,0 +1,437 @@
|
||||
Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||
Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-NonCommercial-ShareAlike 4.0 International Public License
|
||||
("Public License"). To the extent this Public License may be
|
||||
interpreted as a contract, You are granted the Licensed Rights in
|
||||
consideration of Your acceptance of these terms and conditions, and the
|
||||
Licensor grants You such rights in consideration of benefits the
|
||||
Licensor receives from making the Licensed Material available under
|
||||
these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. BY-NC-SA Compatible License means a license listed at
|
||||
creativecommons.org/compatiblelicenses, approved by Creative
|
||||
Commons as essentially the equivalent of this Public License.
|
||||
|
||||
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
e. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
g. License Elements means the license attributes listed in the name
|
||||
of a Creative Commons Public License. The License Elements of this
|
||||
Public License are Attribution, NonCommercial, and ShareAlike.
|
||||
|
||||
h. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
i. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
k. NonCommercial means not primarily intended for or directed towards
|
||||
commercial advantage or monetary compensation. For purposes of
|
||||
this Public License, the exchange of the Licensed Material for
|
||||
other material subject to Copyright and Similar Rights by digital
|
||||
file-sharing or similar means is NonCommercial provided there is
|
||||
no payment of monetary compensation in connection with the
|
||||
exchange.
|
||||
|
||||
l. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
m. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
n. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part, for NonCommercial purposes only; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material for
|
||||
NonCommercial purposes only.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. Additional offer from the Licensor -- Adapted Material.
|
||||
Every recipient of Adapted Material from You
|
||||
automatically receives an offer from the Licensor to
|
||||
exercise the Licensed Rights in the Adapted Material
|
||||
under the conditions of the Adapter's License You apply.
|
||||
|
||||
c. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties, including when
|
||||
the Licensed Material is used other than for NonCommercial
|
||||
purposes.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
b. ShareAlike.
|
||||
|
||||
In addition to the conditions in Section 3(a), if You Share
|
||||
Adapted Material You produce, the following conditions also apply.
|
||||
|
||||
1. The Adapter's License You apply must be a Creative Commons
|
||||
license with the same License Elements, this version or
|
||||
later, or a BY-NC-SA Compatible License.
|
||||
|
||||
2. You must include the text of, or the URI or hyperlink to, the
|
||||
Adapter's License You apply. You may satisfy this condition
|
||||
in any reasonable manner based on the medium, means, and
|
||||
context in which You Share Adapted Material.
|
||||
|
||||
3. You may not offer or impose any additional or different terms
|
||||
or conditions on, or apply any Effective Technological
|
||||
Measures to, Adapted Material that restrict exercise of the
|
||||
rights granted under the Adapter's License You apply.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database for NonCommercial purposes
|
||||
only;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material,
|
||||
including for purposes of Section 3(b); and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
255
app.py
255
app.py
@@ -4,11 +4,12 @@ import json
|
||||
import math
|
||||
import extract
|
||||
import platform
|
||||
import webbrowser
|
||||
from PyQt5 import uic
|
||||
from requests import get
|
||||
from PyQt5.QtGui import QTextCursor
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QThread, QMetaType, Qt
|
||||
from PyQt5.QtWidgets import QMessageBox, QMainWindow, QApplication, QFileDialog, QHeaderView, QAbstractItemView, QTreeWidgetItem
|
||||
from PyQt5.QtWidgets import QMessageBox, QMainWindow, QApplication, QFileDialog, QHeaderView, QAbstractItemView, QTreeWidgetItem, QAction, QActionGroup
|
||||
|
||||
QMetaType.type("QTextCursor")
|
||||
|
||||
@@ -72,76 +73,142 @@ class AnimeWwise(QMainWindow):
|
||||
def __init__(self):
|
||||
super(AnimeWwise, self).__init__()
|
||||
uic.loadUi("gui.ui", self)
|
||||
self.maps = self.getMaps()
|
||||
self.maps = self.getJson("maps/index")
|
||||
self.setWindowTitle(f'AnimeWwise | v{".".join(list(str(self.getJson("version")["version"])))}')
|
||||
self.folders = {
|
||||
"input": "",
|
||||
"output": "",
|
||||
"diff": ""
|
||||
}
|
||||
self.format = "wav"
|
||||
self.fileStructure = {"folders": {}, "files": []}
|
||||
self.setupActions()
|
||||
sys.stdout = TextEditStream(self.console)
|
||||
self.extract = extract.WwiseExtract()
|
||||
self.checkMapsUpdates()
|
||||
self.checkUpdates()
|
||||
|
||||
# utils
|
||||
self.selectFolder = lambda: QFileDialog.getExistingDirectory(self, "Select Folder")
|
||||
|
||||
def checkMapsUpdates(self):
|
||||
print("Checking updates")
|
||||
def checkUpdates(self):
|
||||
print("Checking for updates...")
|
||||
try:
|
||||
getVersion = lambda m: sum([int(e["version"].replace(".", "")) for e in m["maps"]])
|
||||
mapsVersion = getVersion(self.maps)
|
||||
latestMaps = get("https://raw.githubusercontent.com/Escartem/AnimeWwise/master/maps/index.json")
|
||||
currentVersion = self.getJson("version")
|
||||
latestVersionReq = get("https://raw.githubusercontent.com/Escartem/AnimeWwise/master/version.json")
|
||||
|
||||
if latestMaps.status_code == 200:
|
||||
latestVersion = getVersion(json.loads(latestMaps.text))
|
||||
if latestVersionReq.status_code == 200:
|
||||
latestVersion = json.loads(latestMaps.text)
|
||||
|
||||
if mapsVersion < latestVersion:
|
||||
print("Update found")
|
||||
QMessageBox.information(None, "Info", "Newer version of the mappings are availble, please update the program", QMessageBox.Ok)
|
||||
if currentVersion["version"] < latestVersion["version"]:
|
||||
print("Update found !")
|
||||
QMessageBox.information(None, "Info", "Newer version of the program is availble, please update.", QMessageBox.Ok)
|
||||
elif currentVersion["mapsVersion"] < latestVersion["mapsVersion"]:
|
||||
print("Update found !")
|
||||
QMessageBox.information(None, "Info", "Newer version of the mappings are availble, please update the program.", QMessageBox.Ok)
|
||||
else:
|
||||
print("No updates")
|
||||
except:
|
||||
print("Failed to check updates")
|
||||
print("Failed to check updates :(")
|
||||
|
||||
def getMaps(self):
|
||||
with open("maps/index.json", "r") as f:
|
||||
maps = json.loads(f.read())
|
||||
def getJson(self, path):
|
||||
with open(f"{path}.json", "r") as f:
|
||||
data = json.loads(f.read())
|
||||
f.close()
|
||||
|
||||
return maps
|
||||
return data
|
||||
|
||||
def setFolder(self, elem, folder):
|
||||
def setFolder(self, elem=None, folder=None):
|
||||
path = self.selectFolder()
|
||||
self.folders[folder] = path
|
||||
elem.setText(path)
|
||||
if elem:
|
||||
elem.setText(path)
|
||||
|
||||
def setupActions(self):
|
||||
self.changeInput.clicked.connect(lambda: self.setFolder(self.inputPath, "input"))
|
||||
self.changeAltInput.clicked.connect(lambda: self.setFolder(self.altInputPath, "diff"))
|
||||
self.changeOutput.clicked.connect(lambda: self.setFolder(self.outputPath, "output"))
|
||||
|
||||
self.outputFormat.addItems(["wem (fastest)", "wav (fast)", "mp3 (slow)", "ogg (slow)"])
|
||||
self.pckLoadTypeCombo.addItems(["Folder", "File"])
|
||||
self.pckLoadTypeCombo.currentIndexChanged.connect(self.loadTypeChange)
|
||||
self.loadType = "folder"
|
||||
|
||||
self.assetMap.addItems(["No map", *[f'{e["game"]} - v{e["version"]}' for e in self.maps["maps"]]])
|
||||
|
||||
self.tabs.setTabEnabled(1, False)
|
||||
self.tabs.setTabEnabled(2, False)
|
||||
self.setExtractionState(False)
|
||||
|
||||
self.updateTreeWidget(self.fileStructure)
|
||||
|
||||
self.loadFilesButton.clicked.connect(lambda: self.loadFiles())
|
||||
|
||||
self.actionReset.triggered.connect(lambda: self.resetApp())
|
||||
self.actionExit.triggered.connect(lambda: self.close())
|
||||
|
||||
self.extractSelected.clicked.connect(lambda: self.extractItems(False))
|
||||
self.extractAll.clicked.connect(lambda: self.extractItems(True))
|
||||
self.actionExpand_all.triggered.connect(lambda: self.treeWidget.expandAll())
|
||||
self.actionCollapse_all.triggered.connect(lambda: self.treeWidget.collapseAll())
|
||||
|
||||
self.actionExtract_Selected.triggered.connect(lambda: self.extractItems(False))
|
||||
self.actionExtract_All.triggered.connect(lambda: self.extractItems(True))
|
||||
|
||||
self.actionReport_a_bug.triggered.connect(lambda: self.openLink(0))
|
||||
self.actionSource_code.triggered.connect(lambda: self.openLink(1))
|
||||
self.actionDiscord.triggered.connect(lambda: self.openLink(2))
|
||||
|
||||
self.searchAsset.textChanged.connect(lambda: self.filterAsset())
|
||||
|
||||
# output format
|
||||
formats = ["wem (fastest)", "wav (fast)", "mp3 (slow)", "ogg (slow)"]
|
||||
action_group = QActionGroup(self)
|
||||
action_group.setExclusive(True)
|
||||
|
||||
for index, item_name in enumerate(formats):
|
||||
action = QAction(item_name, self)
|
||||
action.setCheckable(True)
|
||||
if index == 1:
|
||||
action.setChecked(True)
|
||||
self.menuOutput_format.addAction(action)
|
||||
action_group.addAction(action)
|
||||
|
||||
action_group.triggered.connect(self.updateFormat)
|
||||
|
||||
# utils
|
||||
def loadTypeChange(self, event):
|
||||
if event == 0:
|
||||
self.pckSubFold.setEnabled(True)
|
||||
self.loadType = "folder"
|
||||
elif event == 1:
|
||||
self.pckSubFold.setEnabled(False)
|
||||
self.loadType = "file"
|
||||
|
||||
def updateFormat(self, event):
|
||||
text = event.text()
|
||||
self.format = text.split(" ")[0]
|
||||
|
||||
def openLink(self, id):
|
||||
urls = [
|
||||
"https://github.com/Escartem/AnimeWwise/issues/new",
|
||||
"https://github.com/Escartem/AnimeWwise",
|
||||
"https://discord.gg/fzRdtVh"
|
||||
]
|
||||
|
||||
webbrowser.open(urls[id])
|
||||
|
||||
def setExtractionState(self, state):
|
||||
self.actionExtract_Selected.setEnabled(state)
|
||||
self.actionExtract_All.setEnabled(state)
|
||||
self.actionExpand_all.setEnabled(state)
|
||||
self.actionCollapse_all.setEnabled(state)
|
||||
|
||||
def displaySize(self, size):
|
||||
if size < 1024:
|
||||
return f"{size} b"
|
||||
elif size > 1024 and size < 1048576:
|
||||
return f"{size//1024} KiB"
|
||||
elif size > 1048576 and size < 1073741824:
|
||||
return f"{size//1048576} MiB"
|
||||
elif size > 1073741824:
|
||||
return f"{size//1073741824} GiB"
|
||||
|
||||
# workers
|
||||
@pyqtSlot(list)
|
||||
def progressBarSlot(self, progress):
|
||||
if progress[0] == "load":
|
||||
self.loadProgress.setValue(math.ceil(progress[1]))
|
||||
if progress[0] == "total":
|
||||
self.totalProgress.setValue(math.ceil(progress[1]))
|
||||
elif progress[0] == "file":
|
||||
@@ -152,23 +219,19 @@ class AnimeWwise(QMainWindow):
|
||||
if data["action"] == "load":
|
||||
self.fileStructure = data["content"]
|
||||
self.updateTreeWidget(self.fileStructure)
|
||||
self.tabs.setTabEnabled(0, False)
|
||||
self.tabs.setTabEnabled(1, True)
|
||||
self.tabs.setTabEnabled(2, True)
|
||||
self.loadFilesButton.setEnabled(True)
|
||||
self.setExtractionState(True)
|
||||
self.tabs.setCurrentIndex(1)
|
||||
print("Done !")
|
||||
if data["action"] == "error":
|
||||
QMessageBox.warning(None, "Warning", data["content"]["msg"], QMessageBox.Ok)
|
||||
state = data["content"]["state"]
|
||||
if state == 1:
|
||||
self.tabs.setTabEnabled(0, True)
|
||||
elif state == 2:
|
||||
self.tabs.setTabEnabled(1, True)
|
||||
self.tabs.setTabEnabled(2, True)
|
||||
self.loadFilesButton.setEnabled(True)
|
||||
if state == 2:
|
||||
self.setExtractionState(True)
|
||||
if data["action"] == "extract":
|
||||
self.tabs.setTabEnabled(1, True)
|
||||
self.tabs.setTabEnabled(2, True)
|
||||
self.tabs.setCurrentIndex(2)
|
||||
self.setExtractionState(True)
|
||||
print("Finished extracting everything !")
|
||||
|
||||
if platform.system() == "Windows":
|
||||
@@ -176,22 +239,38 @@ class AnimeWwise(QMainWindow):
|
||||
|
||||
# page 1 - config
|
||||
def loadFiles(self):
|
||||
if self.folders["input"] == "":
|
||||
QMessageBox.warning(None, "Warning", "Missing input folder !", QMessageBox.Ok)
|
||||
if self.loadType == "folder":
|
||||
self.setFolder(folder="input")
|
||||
files = []
|
||||
if self.folders["input"]:
|
||||
if self.pckSubFold.isChecked():
|
||||
files = [os.path.join(root, f) for root, dirs, files_in_dir in os.walk(self.folders["input"]) for f in files_in_dir if f.endswith(".pck")]
|
||||
else:
|
||||
files = [os.path.join(self.folders["input"], f) for f in os.listdir(self.folders["input"]) if f.endswith(".pck")]
|
||||
elif self.loadType == "file":
|
||||
path = QFileDialog.getOpenFileName(self, "Select .pck File", "", "PCK Files (*.pck)", options=QFileDialog.Options())
|
||||
files = [path[0]]
|
||||
|
||||
if len(files) == 0 or files[0] == "":
|
||||
QMessageBox.warning(None, "Warning", "Nothing to load !", QMessageBox.Ok)
|
||||
return
|
||||
|
||||
self.currentInput = self.folders["input"]
|
||||
if not self.folders["input"]:
|
||||
self.currentInput = os.path.dirname(path[0])
|
||||
|
||||
_map = self.assetMap.currentIndex()
|
||||
if _map != 0:
|
||||
_map = self.maps["maps"][_map-1]["name"]
|
||||
else:
|
||||
_map = None
|
||||
|
||||
self.tabs.setTabEnabled(0, False)
|
||||
self.resetTreeWidget()
|
||||
self.loadFilesButton.setEnabled(False)
|
||||
|
||||
# why is all this required for threading damnit
|
||||
self.backgroundThread = QThread()
|
||||
self.backgroundWorker = BackgroundWorker("load", self.extract, {"input": self.folders["input"], "map": _map, "diff": self.folders["diff"]})
|
||||
self.backgroundWorker = BackgroundWorker("load", self.extract, {"input": files, "map": _map, "diff": self.folders["diff"]})
|
||||
self.backgroundWorker.moveToThread(self.backgroundThread)
|
||||
self.backgroundThread.started.connect(self.backgroundWorker.run)
|
||||
self.backgroundWorker.finished.connect(self.handleFinished)
|
||||
@@ -211,7 +290,7 @@ class AnimeWwise(QMainWindow):
|
||||
result = self.searchFiles(self.fileStructure, search)
|
||||
self.updateTreeWidget(result)
|
||||
|
||||
def searchFiles(self, data, substring, current_path=""):
|
||||
def searchFiles(self, data, substring, current_path="", flatten=False):
|
||||
result = {"folders": {}, "files": []}
|
||||
|
||||
result["files"] = [file for file in data.get("files", []) if substring in file[0]]
|
||||
@@ -221,32 +300,69 @@ class AnimeWwise(QMainWindow):
|
||||
if subfolder_result["files"] or subfolder_result["folders"]:
|
||||
result["folders"][folder_name] = subfolder_result
|
||||
|
||||
if flatten:
|
||||
while result["files"] == []:
|
||||
if len(result["folders"]) == 0:
|
||||
break
|
||||
result = list(result["folders"].values())[0]
|
||||
|
||||
return result
|
||||
|
||||
def resetTreeWidget(self):
|
||||
self.treeWidget.clear()
|
||||
self.tabs.setTabEnabled(1, False)
|
||||
self.fileStructure = {"folders": {}, "files": []}
|
||||
self.audioInfoLabel.setText("Click on an audio file to get more infos !")
|
||||
self.setExtractionState(False)
|
||||
|
||||
def updateTreeWidget(self, structure):
|
||||
self.treeWidget.clear()
|
||||
self.treeWidget.setColumnCount(3)
|
||||
self.treeWidget.setHeaderLabels(["Name", "Offset", "Size", "Source"])
|
||||
self.treeWidget.setColumnCount(4)
|
||||
self.treeWidget.setHeaderLabels(["Name", "Duration", "Compressed Size", "Source", "Offset"])
|
||||
|
||||
self.addItems(None, structure)
|
||||
|
||||
self.treeWidget.expandAll()
|
||||
self.treeWidget.header().setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
self.treeWidget.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
||||
self.treeWidget.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
|
||||
|
||||
self.treeWidget.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
||||
self.treeWidget.header().setSectionResizeMode(1, QHeaderView.Stretch)
|
||||
self.treeWidget.header().setSectionResizeMode(2, QHeaderView.Stretch)
|
||||
self.treeWidget.header().setSectionResizeMode(3, QHeaderView.Stretch)
|
||||
|
||||
self.treeWidget.setHeaderHidden(False)
|
||||
|
||||
self.treeWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||
self.treeWidget.setDragDropMode(QAbstractItemView.NoDragDrop)
|
||||
self.treeWidget.itemClicked.connect(self.updateAudioPreview)
|
||||
|
||||
def computeFolderSize(self, folder):
|
||||
total_size = 0
|
||||
|
||||
for file in folder.get("files", []):
|
||||
total_size += file[1]["size"]
|
||||
|
||||
for subfolder_name, subfolder in folder.get("folders", {}).items():
|
||||
subfolder_size = self.computeFolderSize(subfolder)
|
||||
total_size += subfolder_size
|
||||
|
||||
return total_size
|
||||
|
||||
def updateAudioPreview(self, item, column):
|
||||
file_data = self.searchFiles(self.fileStructure, item.text(0), flatten=True)
|
||||
|
||||
if file_data == {"folders": {}, "files": []}:
|
||||
self.audioInfoLabel.setText("Click on an audio file to get more infos !")
|
||||
return
|
||||
|
||||
meta = file_data["files"][0][1]["metadata"]
|
||||
|
||||
# show meta
|
||||
text = f'Infos for {item.text(0)} => Channels : {meta["channels"]} | Sample rate : {meta["sampleRate"]} Hz | Bitrate : {meta["avgBitrate"]} kbps | Codec : {meta["codecDisplay"]} | Layout type : {meta["layoutType"]}'
|
||||
self.audioInfoLabel.setText(text)
|
||||
|
||||
def addItems(self, parent, element):
|
||||
for folder_name in sorted(element.get("folders", {}).keys()):
|
||||
folder_content = element["folders"][folder_name]
|
||||
folder_item = QTreeWidgetItem([folder_name, "", "", ""])
|
||||
folder_item = QTreeWidgetItem([folder_name, "", self.displaySize(self.computeFolderSize(folder_content)), "", ""])
|
||||
folder_item.setFlags(folder_item.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
|
||||
folder_item.setCheckState(0, Qt.Unchecked)
|
||||
if parent is None:
|
||||
@@ -255,8 +371,9 @@ class AnimeWwise(QMainWindow):
|
||||
parent.addChild(folder_item)
|
||||
self.addItems(folder_item, folder_content)
|
||||
|
||||
for file in sorted(element.get("files", [])):
|
||||
file_item = QTreeWidgetItem([str(file[0]), str(hex(file[1])), str(file[2]), str(file[3])])
|
||||
for file in sorted(element.get("files", []), key=lambda x: x[0]):
|
||||
file_meta = file[1]
|
||||
file_item = QTreeWidgetItem([file[0], f'{round(file_meta["metadata"]["duration"], 1)} seconds', self.displaySize(file_meta["size"]), file_meta["source"], str(hex(file_meta["offset"]))])
|
||||
file_item.setFlags(file_item.flags() | Qt.ItemIsUserCheckable)
|
||||
file_item.setCheckState(0, Qt.Unchecked)
|
||||
if parent is None:
|
||||
@@ -266,9 +383,7 @@ class AnimeWwise(QMainWindow):
|
||||
|
||||
# page 3 - extraction
|
||||
def extractItems(self, _all):
|
||||
if self.folders["output"] == "":
|
||||
QMessageBox.warning(None, "Warning", "Missing output folder !", QMessageBox.Ok)
|
||||
return
|
||||
self.setFolder(folder="output")
|
||||
|
||||
checked_items = []
|
||||
|
||||
@@ -282,13 +397,11 @@ class AnimeWwise(QMainWindow):
|
||||
for i in range(self.treeWidget.topLevelItemCount()):
|
||||
check_items(self.treeWidget.topLevelItem(i), _all)
|
||||
|
||||
self.tabs.setTabEnabled(1, False)
|
||||
self.tabs.setTabEnabled(2, False)
|
||||
self.tabs.setCurrentIndex(2)
|
||||
self.setExtractionState(False)
|
||||
|
||||
# yet another block of threading bs
|
||||
self.backgroundThread = QThread()
|
||||
self.backgroundWorker = BackgroundWorker("extract", self.extract, {"input": self.folders["input"], "files": checked_items, "format": self.outputFormat.currentText()[:3], "output": self.folders["output"]})
|
||||
self.backgroundWorker = BackgroundWorker("extract", self.extract, {"input": self.currentInput, "files": checked_items, "format": self.format, "output": self.folders["output"]})
|
||||
self.backgroundWorker.moveToThread(self.backgroundThread)
|
||||
self.backgroundThread.started.connect(self.backgroundWorker.run)
|
||||
self.backgroundWorker.finished.connect(self.handleFinished)
|
||||
@@ -306,22 +419,28 @@ class AnimeWwise(QMainWindow):
|
||||
while current_item is not None:
|
||||
path.insert(0, current_item.text(0))
|
||||
current_item = current_item.parent()
|
||||
|
||||
|
||||
meta = self.searchFiles(self.fileStructure, item.text(0), flatten=True)["files"][0]
|
||||
name = meta[0]
|
||||
meta = meta[1] # move inside
|
||||
|
||||
return {
|
||||
"name": item.text(0),
|
||||
"path": path[:-1] if path[0] in ["changed_files", "new_files"] else path[1:-1],
|
||||
"source": item.text(3),
|
||||
"offset": int(item.text(1), 16),
|
||||
"size": int(item.text(2))
|
||||
"path": path[:-1],
|
||||
"source": meta["source"],
|
||||
"offset": meta["offset"],
|
||||
"size": meta["size"]
|
||||
}
|
||||
|
||||
# misc
|
||||
def resetApp(self):
|
||||
self.resetTreeWidget()
|
||||
self.extract.reset()
|
||||
self.tabs.setTabEnabled(0, True)
|
||||
self.tabs.setTabEnabled(1, False)
|
||||
self.tabs.setTabEnabled(2, False)
|
||||
self.currentInput = None
|
||||
self.setExtractionState(False)
|
||||
self.tabs.setCurrentIndex(0)
|
||||
self.totalProgress.setValue(0)
|
||||
self.fileProgress.setValue(0)
|
||||
print("Reset !")
|
||||
|
||||
def _appendText(self, text):
|
||||
|
||||
79
extract.py
79
extract.py
@@ -1,9 +1,10 @@
|
||||
import os
|
||||
import io
|
||||
import wwise
|
||||
import tempfile
|
||||
import wavescan
|
||||
import subprocess
|
||||
import platform
|
||||
import subprocess
|
||||
from mapper import Mapper
|
||||
from allocator import Allocator
|
||||
from filereader import FileReader
|
||||
@@ -16,19 +17,43 @@ class WwiseExtract:
|
||||
def __init__(self):
|
||||
self.allocator = Allocator()
|
||||
self.hdiff_dir = None
|
||||
self.maps = {}
|
||||
|
||||
### loading files ###
|
||||
|
||||
def load_folder(self, _map, folder_path, diff_path, progress):
|
||||
def load_map(self, _map):
|
||||
map_name = _map.split(".")[0]
|
||||
|
||||
if map_name not in self.maps or self.maps[map_name] is None:
|
||||
print("Map load required !")
|
||||
mapper = Mapper(path(cwd, f"maps/{_map}"))
|
||||
self.maps[map_name] = mapper
|
||||
else:
|
||||
print("Mapping already loaded, skipping")
|
||||
|
||||
return self.maps[map_name]
|
||||
|
||||
def load_folder(self, _map, files, diff_path, progress):
|
||||
self.progress = progress
|
||||
self.steps = 1
|
||||
|
||||
self.mapper = None
|
||||
if _map is not None:
|
||||
self.mapper = Mapper(path(cwd, f"maps/{_map}"))
|
||||
self.mapper = self.load_map(_map)
|
||||
|
||||
self.file_structure = {"folders": {}, "files": []}
|
||||
|
||||
files = [f for f in os.listdir(folder_path) if f.endswith(".pck")]
|
||||
hdiff_files = []
|
||||
if diff_path != "":
|
||||
hdiff_files = [f for f in os.listdir(diff_path) if f.endswith(".pck.hdiff")]
|
||||
|
||||
# TODO: hdiff mode will only use .hdiff files and ignore .pck even in the update folder, i need to implement it, eventually
|
||||
|
||||
# remove alone pck / hdiff
|
||||
base_files = [os.path.basename(f) for f in files]
|
||||
hdiff_files = [f for f in hdiff_files if os.path.basename(f.replace(".hdiff", "")) in base_files]
|
||||
base_hfiles = [os.path.basename(f) for f in hdiff_files]
|
||||
files = [f for f in files if f"{os.path.basename(f)}.hdiff" in base_hfiles]
|
||||
|
||||
if len(files) == 0:
|
||||
return None
|
||||
@@ -37,12 +62,12 @@ class WwiseExtract:
|
||||
print(f"\nLoading {len(files)} files...")
|
||||
for file in files:
|
||||
pos += 1
|
||||
progress(["load", pos * 100 // len(files)])
|
||||
self.update_progress(pos, len(files), 1)
|
||||
|
||||
hdiff = None
|
||||
if f"{file}.hdiff" in hdiff_files:
|
||||
hdiff = path(diff_path, hdiff_files[hdiff_files.index(f"{file}.hdiff")])
|
||||
self.load_file(path(folder_path, file), hdiff)
|
||||
if f"{os.path.basename(file)}.hdiff" in hdiff_files:
|
||||
hdiff = path(diff_path, hdiff_files[hdiff_files.index(f"{os.path.basename(file)}.hdiff")])
|
||||
self.load_file(file, hdiff)
|
||||
|
||||
return self.file_structure
|
||||
|
||||
@@ -55,14 +80,16 @@ class WwiseExtract:
|
||||
def get_wems(self, data, filename, hdiff):
|
||||
reader = FileReader(io.BytesIO(data), "little")
|
||||
files = wavescan.get_data(reader, filename)
|
||||
|
||||
if hdiff is not None:
|
||||
with open(hdiff, "rb") as f:
|
||||
hdiff_data = f.read()
|
||||
f.close()
|
||||
hdiff_files = self.get_hdiff_files(data, hdiff_data, filename)
|
||||
|
||||
hdiff_files, data = self.get_hdiff_files(data, hdiff_data, filename)
|
||||
files = self.compare_diff(files, hdiff_files)
|
||||
|
||||
self.map_names(files, filename, hdiff is not None)
|
||||
self.map_names(files, filename, hdiff is not None, data)
|
||||
|
||||
def compare_diff(self, old, new):
|
||||
old_dict = {file[0]:file[2] for file in old}
|
||||
@@ -97,6 +124,10 @@ class WwiseExtract:
|
||||
|
||||
call(args)
|
||||
|
||||
if not os.path.exists(path(working_dir.name, "patch.pck")):
|
||||
print(f"[ERROR] failed to patch {source_name}, skipping")
|
||||
return []
|
||||
|
||||
with open(path(working_dir.name, "patch.pck"), "rb") as f:
|
||||
data = f.read()
|
||||
f.close()
|
||||
@@ -110,9 +141,9 @@ class WwiseExtract:
|
||||
|
||||
working_dir.cleanup()
|
||||
|
||||
return files
|
||||
return files, data
|
||||
|
||||
def map_names(self, files, filename, hdiff=False, skip_source=True):
|
||||
def map_names(self, files, filename, hdiff=False, data=None, skip_source=True):
|
||||
# disable skip source if required
|
||||
mapper = self.mapper
|
||||
base = self.file_structure
|
||||
@@ -128,6 +159,18 @@ class WwiseExtract:
|
||||
else:
|
||||
key = None
|
||||
|
||||
file_data = {
|
||||
"source": file[3],
|
||||
"size": file[2],
|
||||
"offset": file[1],
|
||||
"metadata": {}
|
||||
}
|
||||
|
||||
wem_data = data[file_data["offset"]:file_data["offset"]+file_data["size"]]
|
||||
parsed_wem = wwise.parse_wwise(FileReader(io.BytesIO(wem_data), "little", name=f"{file[3]}:{file[0]}:{file[1]}"))
|
||||
|
||||
file_data["metadata"] = parsed_wem
|
||||
|
||||
if key is not None:
|
||||
if hdiff:
|
||||
if file in old_files[0]:
|
||||
@@ -139,7 +182,7 @@ class WwiseExtract:
|
||||
if skip_source:
|
||||
parts = parts[1:]
|
||||
|
||||
self.add_to_structure(parts, [file[1], file[2], file[3]])
|
||||
self.add_to_structure(parts, file_data)
|
||||
else:
|
||||
temp = base["folders"]
|
||||
|
||||
@@ -161,7 +204,7 @@ class WwiseExtract:
|
||||
|
||||
if "unmapped" not in temp:
|
||||
temp["unmapped"] = {"folders": {}, "files": []}
|
||||
temp["unmapped"]["files"].append(file)
|
||||
temp["unmapped"]["files"].append([file[0], file_data])
|
||||
|
||||
self.file_structure = base
|
||||
|
||||
@@ -175,7 +218,7 @@ class WwiseExtract:
|
||||
current_level = current_level["folders"][part]
|
||||
if "files" not in current_level:
|
||||
current_level["files"] = []
|
||||
current_level["files"].append([parts[-1], meta[0], meta[1], meta[2]])
|
||||
current_level["files"].append([parts[-1], meta])
|
||||
|
||||
### extracting files ###
|
||||
|
||||
@@ -331,8 +374,10 @@ class WwiseExtract:
|
||||
self.progress(["file", current * 100 // total])
|
||||
|
||||
def reset(self):
|
||||
if self.mapper is not None:
|
||||
self.mapper.reset()
|
||||
self.mapper = None
|
||||
for e in self.maps.values():
|
||||
e.reset()
|
||||
self.maps.clear()
|
||||
self.allocator.free_mem()
|
||||
if self.hdiff_dir is not None:
|
||||
self.hdiff_dir.cleanup()
|
||||
|
||||
@@ -8,52 +8,63 @@ class FileReader:
|
||||
File reader for files, not much too say
|
||||
"""
|
||||
|
||||
def __init__(self, file, endianness:str):
|
||||
def __init__(self, file, endianness:str, name:str=None):
|
||||
self.stream = file
|
||||
self.endianness = endianness
|
||||
if name:
|
||||
self.name = name
|
||||
|
||||
def _read(self, mode:str, bufferLength:int, endianness:str=None) -> bytes:
|
||||
def _read(self, mode:str, bufferLength:int, endianness:str=None, pos:int=None) -> bytes:
|
||||
# endianness override
|
||||
if endianness is None:
|
||||
endianness = self.endianness
|
||||
|
||||
endianness = "<" if endianness == "little" else ">"
|
||||
|
||||
return struct.unpack(f"{endianness}{mode}", bytearray(self.stream.read(bufferLength)))[0]
|
||||
if pos:
|
||||
pos_backup = self.GetBufferPos()
|
||||
self.SetBufferPos(pos)
|
||||
|
||||
data = struct.unpack(f"{endianness}{mode}", bytearray(self.stream.read(bufferLength)))[0]
|
||||
|
||||
if pos:
|
||||
self.SetBufferPos(pos_backup)
|
||||
|
||||
return data
|
||||
|
||||
# read methods
|
||||
def ReadInt8(self, endianness:str=None) -> int:
|
||||
return self._read("b", 1, endianness)
|
||||
def ReadInt8(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("b", 1, endianness, pos)
|
||||
|
||||
def ReadUInt8(self, endianness:str=None) -> int:
|
||||
return self._read("B", 1, endianness)
|
||||
def ReadUInt8(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("B", 1, endianness, pos)
|
||||
|
||||
def ReadInt16(self, endianness:str=None) -> int:
|
||||
return self._read("h", 2, endianness)
|
||||
def ReadInt16(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("h", 2, endianness, pos)
|
||||
|
||||
def ReadUInt16(self, endianness:str=None) -> int:
|
||||
return self._read("H", 2, endianness)
|
||||
def ReadUInt16(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("H", 2, endianness, pos)
|
||||
|
||||
def ReadInt32(self, endianness:str=None) -> int:
|
||||
return self._read("i", 4, endianness)
|
||||
def ReadInt32(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("i", 4, endianness, pos)
|
||||
|
||||
def ReadUInt32(self, endianness:str=None) -> int:
|
||||
return self._read("I", 4, endianness)
|
||||
def ReadUInt32(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("I", 4, endianness, pos)
|
||||
|
||||
def ReadLong(self, endianness:str=None) -> int:
|
||||
return self._read("l", 4, endianness)
|
||||
def ReadLong(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("l", 4, endianness, pos)
|
||||
|
||||
def ReadULong(self, endianness:str=None) -> int:
|
||||
return self._read("L", 4, endianness)
|
||||
def ReadULong(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("L", 4, endianness, pos)
|
||||
|
||||
def ReadLongLong(self, endianness:str=None) -> int:
|
||||
return self._read("q", 8, endianness)
|
||||
def ReadLongLong(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("q", 8, endianness, pos)
|
||||
|
||||
def ReadULongLong(self, endianness:str=None) -> int:
|
||||
return self._read("Q", 8, endianness)
|
||||
def ReadULongLong(self, endianness:str=None, pos:int=None) -> int:
|
||||
return self._read("Q", 8, endianness, pos)
|
||||
|
||||
def ReadBytes(self, length:int, endianness:str=None) -> bytes:
|
||||
return self._read(f"{str(length)}s", int(length), endianness)
|
||||
def ReadBytes(self, length:int, endianness:str=None, pos:int=None) -> bytes:
|
||||
return self._read(f"{str(length)}s", int(length), endianness, pos)
|
||||
|
||||
# buffer utils
|
||||
def GetBufferPos(self) -> int:
|
||||
@@ -76,3 +87,8 @@ class FileReader:
|
||||
|
||||
def GetRemainingLength(self) -> int:
|
||||
return self.GetStreamLength() - self.GetBufferPos()
|
||||
|
||||
def GetName(self) -> str:
|
||||
if self.name:
|
||||
return self.name
|
||||
return ""
|
||||
|
||||
553
gui.ui
553
gui.ui
@@ -5,24 +5,27 @@
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1100</width>
|
||||
<height>800</height>
|
||||
<height>900</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1100</width>
|
||||
<height>800</height>
|
||||
<height>900</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1100</width>
|
||||
<height>800</height>
|
||||
<height>900</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -39,7 +42,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="usesScrollButtons">
|
||||
<bool>true</bool>
|
||||
@@ -74,69 +77,172 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="mainVLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="ioGrid">
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="changeAltInput">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
<widget class="QLabel" name="paddingB">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="titleCenter">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="appTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>32</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select</string>
|
||||
<string>Welcome to AnimeWwise !</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="changeInput">
|
||||
<property name="text">
|
||||
<string>Select</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="altInputPath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="altInputLabel">
|
||||
<property name="text">
|
||||
<string>Diff folder (optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="inputLabel">
|
||||
<property name="text">
|
||||
<string>Input folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="inputPath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="paddingA">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="lineAgain">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="extractsTabs">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="pckExtract">
|
||||
<attribute name="title">
|
||||
<string>Extract audio package (.pck)</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="gridLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>1041</width>
|
||||
<height>111</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="pckGrid">
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="pckLoadTypeCombo"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="pckLoadType">
|
||||
<property name="text">
|
||||
<string>What to load :</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="pckSubFold">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Include subfolders ?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="pckPadding">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="hdiffExtract">
|
||||
<attribute name="title">
|
||||
<string>Extract update package (.hdiff)</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="gridLayoutWidget_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>1041</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="hdiffGrid">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="altInputLabel">
|
||||
<property name="text">
|
||||
<string>Diff folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="changeAltInput">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="altInputPath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select here the folder containing the .hdiff files present in the game update package. And for the input folder asked upon loading, select the game audio folder before the update !
|
||||
Subfolders are disabled in this mode, make sure to be in the correct place. For any help check the README.md or ask on discord.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="separatorA">
|
||||
<property name="orientation">
|
||||
@@ -144,11 +250,15 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="paddingC">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="settingsGrid">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="assetMap"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="assetMapLabel">
|
||||
<property name="text">
|
||||
@@ -156,8 +266,18 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="assetMap"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="paddingD">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="separatorB">
|
||||
<property name="orientation">
|
||||
@@ -167,6 +287,13 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="loadLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="paddingE">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="loadFilesButton">
|
||||
<property name="text">
|
||||
@@ -175,25 +302,11 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="loadProgressLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="loadLabel">
|
||||
<property name="text">
|
||||
<string>Progress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="loadProgress">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QLabel" name="paddingF">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
@@ -210,7 +323,7 @@
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>1081</width>
|
||||
<height>591</height>
|
||||
<height>551</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
@@ -235,136 +348,23 @@
|
||||
<string>Search something...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="extractTab">
|
||||
<attribute name="title">
|
||||
<string>Extract</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>9</x>
|
||||
<y>9</y>
|
||||
<x>10</x>
|
||||
<y>580</y>
|
||||
<width>1061</width>
|
||||
<height>601</height>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="mainVLayout2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="outputLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="outputLabel">
|
||||
<property name="text">
|
||||
<string>Output folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="outputPath">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="changeOutput">
|
||||
<property name="text">
|
||||
<string>Select</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="outputFormatLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="outputFormatLabel">
|
||||
<property name="text">
|
||||
<string>Output format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="outputFormat">
|
||||
<property name="currentText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="separatorC">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<widget class="QLabel" name="audioInfoLabel">
|
||||
<property name="text">
|
||||
<string>Click on an audio file to get more infos !</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="progressWrapperLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="totalProgressLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="totalProgressLabel">
|
||||
<property name="text">
|
||||
<string>Total progress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="totalProgress">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="fileProgressLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="fileProgressLabel">
|
||||
<property name="text">
|
||||
<string>Per file progress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="fileProgress">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="separatorD">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="extractLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="extractAll">
|
||||
<property name="text">
|
||||
<string>Extract All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="extractSelected">
|
||||
<property name="text">
|
||||
<string>Extract Selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
@@ -373,9 +373,9 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>640</y>
|
||||
<y>720</y>
|
||||
<width>1081</width>
|
||||
<height>131</height>
|
||||
<height>151</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
@@ -394,6 +394,54 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>16</x>
|
||||
<y>650</y>
|
||||
<width>1071</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="progressWrapperLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="totalProgressLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="totalProgressLabel">
|
||||
<property name="text">
|
||||
<string>Total progress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="totalProgress">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="fileProgressLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="fileProgressLabel">
|
||||
<property name="text">
|
||||
<string>Per file progress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="fileProgress">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
@@ -412,7 +460,40 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuExtract">
|
||||
<property name="title">
|
||||
<string>Extract</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuOutput_format">
|
||||
<property name="title">
|
||||
<string>Output format</string>
|
||||
</property>
|
||||
<addaction name="actionformat"/>
|
||||
</widget>
|
||||
<addaction name="menuOutput_format"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionExtract_All"/>
|
||||
<addaction name="actionExtract_Selected"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuOther">
|
||||
<property name="title">
|
||||
<string>Other</string>
|
||||
</property>
|
||||
<addaction name="actionReport_a_bug"/>
|
||||
<addaction name="actionSource_code"/>
|
||||
<addaction name="actionDiscord"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="actionExpand_all"/>
|
||||
<addaction name="actionCollapse_all"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuExtract"/>
|
||||
<addaction name="menuOther"/>
|
||||
</widget>
|
||||
<action name="actionnot_working_here_yet">
|
||||
<property name="text">
|
||||
@@ -439,12 +520,66 @@
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExtract_All">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Extract All</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExtract_Selected">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Extract Selected</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReport_a_bug">
|
||||
<property name="text">
|
||||
<string>Report a bug</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSource_code">
|
||||
<property name="text">
|
||||
<string>Source code</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDiscord">
|
||||
<property name="text">
|
||||
<string>Discord</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionformat">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>format</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExpand_all">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Expand all</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCollapse_all">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Collapse all</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>inputPath</tabstop>
|
||||
<tabstop>changeInput</tabstop>
|
||||
<tabstop>altInputPath</tabstop>
|
||||
<tabstop>changeAltInput</tabstop>
|
||||
<tabstop>tabs</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
||||
BIN
maps/hk4e.map
BIN
maps/hk4e.map
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
{
|
||||
"name": "hk4e.map",
|
||||
"game": "Genshin Impact",
|
||||
"version": "5.1"
|
||||
"version": "5.2"
|
||||
},
|
||||
{
|
||||
"name": "hkrpg.map",
|
||||
|
||||
4
version.json
Normal file
4
version.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 21,
|
||||
"mapsVersion": 89
|
||||
}
|
||||
187
wwise.py
Normal file
187
wwise.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# wwise riff header parser
|
||||
# thanks to hcs and bnnm work
|
||||
|
||||
def parse_wwise(reader):
|
||||
# default meta config
|
||||
metadata = {
|
||||
"format": 0,
|
||||
"channels": 0,
|
||||
"sampleRate": 0,
|
||||
"avgBitrate": 0,
|
||||
"blockSize": 0,
|
||||
"bitsPerSample": 0,
|
||||
"extraSize": 0,
|
||||
"channelLayout": None,
|
||||
"channelType": None,
|
||||
"codec": None,
|
||||
"codecDisplay": None,
|
||||
"layoutType": None,
|
||||
"interleaveBlockSize": None,
|
||||
"numSamples": None,
|
||||
"duration": 0
|
||||
}
|
||||
|
||||
if reader.GetStreamLength() == 0:
|
||||
print(f"[WARNING] null stream size at {reader.GetName()}, unreadable block")
|
||||
return metadata
|
||||
|
||||
header = reader.ReadBytes(4)
|
||||
|
||||
# endian check header
|
||||
if header == b"RIFX":
|
||||
reader.endianness = "big"
|
||||
elif header == b"RIFF":
|
||||
reader.endianness = "little"
|
||||
else:
|
||||
print(f"[WARNING] invalid header {header} at {reader.GetName()}, assuming unreadable")
|
||||
return metadata
|
||||
|
||||
# additional check
|
||||
reader.SetBufferPos(0x08)
|
||||
check = reader.ReadBytes(4)
|
||||
|
||||
if check != b"WAVE" and check != "XWMA":
|
||||
print(f"[WARNING] invalid check mark {check}, assuming unreadable")
|
||||
return metadata
|
||||
|
||||
# read chunks
|
||||
reader.SetBufferPos(0x0C)
|
||||
|
||||
chunks = {}
|
||||
|
||||
while reader.GetBufferPos() < reader.GetStreamLength():
|
||||
chunk_type = reader.ReadBytes(4)
|
||||
|
||||
if chunk_type not in [b"fmt ", b"JUNK", b"data", b"akd ", b"cue ", b"LIST", b"smpl"]:
|
||||
print(f"[WARNING] unexpected chunk {chunk_type} at {reader.GetName()}")
|
||||
|
||||
formatted_chunk_type = chunk_type.decode("utf-8").replace(" ", "")
|
||||
chunk_length = reader.ReadUInt32()
|
||||
|
||||
if chunk_length > reader.GetRemainingLength():
|
||||
chunk_length = reader.GetRemainingLength()
|
||||
|
||||
chunks[formatted_chunk_type] = {
|
||||
"length": chunk_length,
|
||||
"offset": reader.GetBufferPos(),
|
||||
"data": reader.ReadBytes(chunk_length)
|
||||
}
|
||||
|
||||
# reader fmt header
|
||||
fmt_length = chunks["fmt"]["length"]
|
||||
if fmt_length < 0x10:
|
||||
print(f"[WARNING] invalid fmt chunk length {fmt_length} at {reader.GetName()}, skipping")
|
||||
return metadata
|
||||
|
||||
reader.SetBufferPos(chunks["fmt"]["offset"])
|
||||
|
||||
metadata["format"] = reader.ReadUInt16()
|
||||
metadata["channels"] = reader.ReadUInt16()
|
||||
metadata["sampleRate"] = reader.ReadUInt32()
|
||||
metadata["avgBitrate"] = reader.ReadUInt32()
|
||||
metadata["blockSize"] = reader.ReadUInt16()
|
||||
metadata["bitsPerSample"] = reader.ReadUInt16()
|
||||
|
||||
if chunks["fmt"]["length"] > 0x10 and metadata["format"] != 0x0165 and metadata["format"] != 0x0166:
|
||||
metadata["extraSize"] = reader.ReadUInt16()
|
||||
|
||||
if metadata["extraSize"] >= 0x06:
|
||||
metadata["channelLayout"] = reader.ReadUInt32()
|
||||
|
||||
if metadata["channelLayout"] & 0xFF == metadata["channels"]:
|
||||
metadata["channelType"] = (metadata["channelLayout"] >> 8) & 0x0F
|
||||
metadata["channelLayout"] = metadata["channelLayout"] >> 12
|
||||
|
||||
if metadata["format"] == 0x0166:
|
||||
print(f"[WARNING] XMA2WAVEFORMATEX in fmt at {reader.GetName()}")
|
||||
return metadata
|
||||
|
||||
# parse codec
|
||||
codecs = {
|
||||
0x0001: "PCM",
|
||||
0x0002: "IMA",
|
||||
0x0069: "IMA",
|
||||
0x0161: "XWLA",
|
||||
0x0162: "XWMA",
|
||||
0x0165: "XMA2",
|
||||
0x0166: "XMA2",
|
||||
0xAAC0: "AAC",
|
||||
0xFFF0: "DSP",
|
||||
0xFFFB: "HEVAG",
|
||||
0xFFFC: "ATRAC9",
|
||||
0xFFFE: "PCM",
|
||||
0xFFFF: "VORBIS",
|
||||
0x3039: "OPUSNX",
|
||||
0x3040: "OPUS",
|
||||
0x3041: "OPUSWW",
|
||||
0x8311: "PTADPCM"
|
||||
}
|
||||
|
||||
# genshin should be *mostly* PTADPCM
|
||||
# hsr and zzz should be VORBIS
|
||||
|
||||
if metadata["format"] not in codecs:
|
||||
print(f'[WARNING] unknown codec {metadata["format"]} at {reader.GetName()}')
|
||||
return metadata
|
||||
|
||||
codec = codecs[metadata["format"]]
|
||||
|
||||
if codec not in ["PTADPCM", "VORBIS"]: # Platinum "PtADPCM" custom ADPCM for Wwise
|
||||
print(f"[WARNING] unhandled codec {codec}, need to implement this later")
|
||||
|
||||
metadata["codec"] = codec
|
||||
|
||||
# codec name
|
||||
codecs_names = {
|
||||
"PTADPCM": "Platinum 4-bit ADPCM",
|
||||
"VORBIS": "Custom Vorbis"
|
||||
}
|
||||
|
||||
if codec in codecs_names:
|
||||
metadata["codecDisplay"] = codecs_names[codec]
|
||||
else:
|
||||
metadata["codecDisplay"] = codec
|
||||
|
||||
# parse duration
|
||||
if metadata["codec"] == "PTADPCM":
|
||||
metadata["layoutType"] = "interleave"
|
||||
metadata["interleaveBlockSize"] = metadata["blockSize"] // metadata["channels"]
|
||||
|
||||
metadata["numSamples"] = int((chunks["data"]["length"] / (metadata["channels"] * metadata["interleaveBlockSize"])) * (2 + (metadata["interleaveBlockSize"] - 0x05) * 2))
|
||||
metadata["duration"] = metadata["numSamples"] / metadata["sampleRate"]
|
||||
|
||||
elif metadata["codec"] == "VORBIS":
|
||||
if (metadata["blockSize"] != 0 or metadata["bitsPerSample"] != 0):
|
||||
print(f"[WARNING] worbis type at {reader.GetName()}, skipping")
|
||||
return metadata
|
||||
|
||||
if "vorb" in chunks:
|
||||
# vorb chunk only in wwise earlier to 2012, therefore impossible for mihoyo games
|
||||
print(f"[WARNING] found vorb chunk at {reader.GetName()}, is this the correct game ?")
|
||||
return metadata
|
||||
|
||||
extra_offset = chunks["fmt"]["offset"] + 0x18
|
||||
|
||||
if metadata["extraSize"] != 0x30:
|
||||
print(f"[WARNING] unknown extra wwise size at {reader.GetName()}, skipping")
|
||||
return metadata
|
||||
|
||||
data_offset = 0x10
|
||||
blocks_offset = 0x28
|
||||
# define header to type 2, packet to modified and codebook to aoTuV603, required ?
|
||||
|
||||
metadata["numSamples"] = reader.ReadInt32(extra_offset)
|
||||
setup_offset = reader.ReadUInt32(extra_offset + data_offset)
|
||||
audio_offset = reader.ReadUInt32(extra_offset + data_offset + 0x04)
|
||||
|
||||
block_size_1_exp = reader.ReadUInt8(extra_offset + blocks_offset)
|
||||
block_size_0_exp = reader.ReadUInt8(extra_offset + blocks_offset + 0x01)
|
||||
# if both exp are equals and extra size is 0x30, then reset packet type to standard
|
||||
|
||||
chunks["data"]["offset"] -= audio_offset
|
||||
|
||||
# ignore packets update and codebooks parse attempts, not implemented
|
||||
metadata["layoutType"] = "none"
|
||||
metadata["duration"] = metadata["numSamples"] / metadata["sampleRate"]
|
||||
|
||||
return metadata
|
||||
Reference in New Issue
Block a user