From 7b99bb26843daf4bd0f84fb440db0582cd8f0596 Mon Sep 17 00:00:00 2001 From: Michael Harradon Date: Thu, 15 Jul 2021 15:07:28 -0400 Subject: [PATCH 1/6] Add directory tree support, change interfaces to enable file writing code reuse in extract_to_disk, add test with GatherMinerals.SC2Map and license. Should work on Python2.7, tested working on 3.7 --- mpyq.py | 33 ++-- test/CollectMineralShards_PySC2_LICENSE | 202 ++++++++++++++++++++++++ test/test_mpqarchive.py | 23 +++ 3 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 test/CollectMineralShards_PySC2_LICENSE diff --git a/mpyq.py b/mpyq.py index 53f7588..46d8c61 100755 --- a/mpyq.py +++ b/mpyq.py @@ -252,29 +252,28 @@ def decompress(data): return file_data - def extract(self): + def extract(self, *filenames): """Extract all the files inside the MPQ archive in memory.""" - if self.files: - return dict((f, self.read_file(f)) for f in self.files) + files = filenames if len(filenames)>0 else self.files + if files: + return dict((f, self.read_file(f)) for f in files) else: raise RuntimeError("Can't extract whole archive without listfile.") - def extract_to_disk(self): + def extract_to_disk(self, *filepaths, target_dir=None): """Extract all files and write them to disk.""" archive_name, extension = os.path.splitext(os.path.basename(self.file.name)) - if not os.path.isdir(os.path.join(os.getcwd(), archive_name)): - os.mkdir(archive_name) - os.chdir(archive_name) - for filename, data in self.extract().items(): - f = open(filename, 'wb') - f.write(data or b'') - f.close() - - def extract_files(self, *filenames): - """Extract given files from the archive to disk.""" - for filename in filenames: - data = self.read_file(filename) - f = open(filename, 'wb') + target_dir = target_dir if target_dir is not None else os.getcwd() + create_dir = os.path.join(target_dir, archive_name) + if not os.path.isdir(create_dir): + os.mkdir(create_dir) + os.chdir(target_dir) + for filepath, data in self.extract(*filepaths).items(): + standardized_filepath = filepath.replace(b"\\",b"/") + filedir, filename = os.path.split(standardized_filepath) + if len(filedir) > 0 and not os.path.isdir(filedir): + os.makedirs(filedir) + f = open(standardized_filepath, 'wb') f.write(data or b'') f.close() diff --git a/test/CollectMineralShards_PySC2_LICENSE b/test/CollectMineralShards_PySC2_LICENSE new file mode 100644 index 0000000..afdfe50 --- /dev/null +++ b/test/CollectMineralShards_PySC2_LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/test/test_mpqarchive.py b/test/test_mpqarchive.py index b073534..50eed01 100644 --- a/test/test_mpqarchive.py +++ b/test/test_mpqarchive.py @@ -3,6 +3,8 @@ import os import unittest +from tempfile import mkdtemp +from shutil import rmtree try: from unittest import mock # Python 3+ @@ -95,6 +97,27 @@ def test_print_block_table(self, mock_stdout): "00031E56 254 288 81000200\n" "\n") +class TestSC2Map(unittest.TestCase): + + def setUp(self): + self.archive = MPQArchive(TEST_DIR + 'CollectMineralShards.SC2Map') + + def tearDown(self): + self.archive.close() + self.archive = None + + def test_init_with_file(self): + self.archive = MPQArchive(open(TEST_DIR + 'CollectMineralShards.SC2Map', 'rb')) + + def test_extract_all(self): + temp_dir = mkdtemp() + self.archive.extract_to_disk(target_dir=temp_dir) + rmtree(temp_dir) + + def test_extract_some_to_disk(self): + temp_dir = mkdtemp() + self.archive.extract_to_disk(b'DocumentInfo', b'Regions', target_dir=temp_dir) + rmtree(temp_dir) if __name__ == '__main__': unittest.main() From d54a61bd935da6b0676cc554c2a37df914a128d6 Mon Sep 17 00:00:00 2001 From: Michael Harradon Date: Thu, 15 Jul 2021 15:21:43 -0400 Subject: [PATCH 2/6] 2.7 fixes --- mpyq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mpyq.py b/mpyq.py index 46d8c61..79d98b1 100755 --- a/mpyq.py +++ b/mpyq.py @@ -260,10 +260,10 @@ def extract(self, *filenames): else: raise RuntimeError("Can't extract whole archive without listfile.") - def extract_to_disk(self, *filepaths, target_dir=None): + def extract_to_disk(self, *filepaths, **target_dir): """Extract all files and write them to disk.""" + target_dir = target_dir['target_dir'] if 'target_dir' in target_dir else os.getcwd() # Need to write this way for 2.7 :( archive_name, extension = os.path.splitext(os.path.basename(self.file.name)) - target_dir = target_dir if target_dir is not None else os.getcwd() create_dir = os.path.join(target_dir, archive_name) if not os.path.isdir(create_dir): os.mkdir(create_dir) From 62cbc02774ce0002470a225b67c260ae42f470ab Mon Sep 17 00:00:00 2001 From: Michael Harradon Date: Thu, 15 Jul 2021 15:37:01 -0400 Subject: [PATCH 3/6] Windows fix? --- test/test_mpqarchive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_mpqarchive.py b/test/test_mpqarchive.py index 50eed01..12799bf 100644 --- a/test/test_mpqarchive.py +++ b/test/test_mpqarchive.py @@ -14,7 +14,7 @@ from mpyq import MPQArchive import six -TEST_DIR = os.path.realpath(os.path.dirname(__file__)) + '/' +TEST_DIR = os.path.realpath(os.path.dirname(__file__)) + os.path.sep class TestMPQArchive(unittest.TestCase): From f2891b376e2ad0d68f5991e4d296d043397a1dc6 Mon Sep 17 00:00:00 2001 From: Michael Harradon Date: Thu, 15 Jul 2021 15:40:47 -0400 Subject: [PATCH 4/6] Force add test map --- test/CollectMineralShards.SC2Map | Bin 0 -> 31098 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/CollectMineralShards.SC2Map diff --git a/test/CollectMineralShards.SC2Map b/test/CollectMineralShards.SC2Map new file mode 100644 index 0000000000000000000000000000000000000000..35c867154e2659e50ee4ef78670e114795326f53 GIT binary patch literal 31098 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 Uuau=>KI#94iArw%qF(p=Uu!caMgRZ+ literal 0 HcmV?d00001 From 84dff85886e6f3a292200163cd1cba6ce51bf913 Mon Sep 17 00:00:00 2001 From: Michael Harradon Date: Thu, 15 Jul 2021 15:46:08 -0400 Subject: [PATCH 5/6] Windows fix? --- test/test_mpqarchive.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_mpqarchive.py b/test/test_mpqarchive.py index 12799bf..93efb5d 100644 --- a/test/test_mpqarchive.py +++ b/test/test_mpqarchive.py @@ -112,11 +112,13 @@ def test_init_with_file(self): def test_extract_all(self): temp_dir = mkdtemp() self.archive.extract_to_disk(target_dir=temp_dir) + os.chdir('..') # Back out of dir to allow removal rmtree(temp_dir) def test_extract_some_to_disk(self): temp_dir = mkdtemp() self.archive.extract_to_disk(b'DocumentInfo', b'Regions', target_dir=temp_dir) + os.chdir('..') # Back out of dir to allow removal rmtree(temp_dir) if __name__ == '__main__': From 6a34aab3dc1f858cbfc9db1aa512fe74543b1fc9 Mon Sep 17 00:00:00 2001 From: Michael Harradon Date: Thu, 15 Jul 2021 16:39:47 -0400 Subject: [PATCH 6/6] Fix output dir --- mpyq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mpyq.py b/mpyq.py index 79d98b1..d82340f 100755 --- a/mpyq.py +++ b/mpyq.py @@ -267,7 +267,7 @@ def extract_to_disk(self, *filepaths, **target_dir): create_dir = os.path.join(target_dir, archive_name) if not os.path.isdir(create_dir): os.mkdir(create_dir) - os.chdir(target_dir) + os.chdir(create_dir) for filepath, data in self.extract(*filepaths).items(): standardized_filepath = filepath.replace(b"\\",b"/") filedir, filename = os.path.split(standardized_filepath)