mirror of
https://github.com/a2-4am/wozardry.git
synced 2025-01-07 12:31:18 +00:00
add import and export commands, fix spelling of Portuguese
This commit is contained in:
parent
3a0501c789
commit
6225d2f9a1
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
26
README.md
26
README.md
@ -1,5 +1,5 @@
|
|||||||
```
|
```
|
||||||
$ ./wozardry verify -h
|
$ ./wozardry.py verify -h
|
||||||
usage: wozardry verify [-h] file
|
usage: wozardry verify [-h] file
|
||||||
|
|
||||||
Verify file structure and metadata of a .woz disk image (produces no output
|
Verify file structure and metadata of a .woz disk image (produces no output
|
||||||
@ -13,7 +13,7 @@ optional arguments:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
$ ./wozardry dump -h
|
$ ./wozardry.py dump -h
|
||||||
usage: wozardry dump [-h] file
|
usage: wozardry dump [-h] file
|
||||||
|
|
||||||
Print all available information and metadata in a .woz disk image
|
Print all available information and metadata in a .woz disk image
|
||||||
@ -52,4 +52,26 @@ Tips:
|
|||||||
- Use "key:" with no value to delete a metadata field.
|
- Use "key:" with no value to delete a metadata field.
|
||||||
- Keys are case-sensitive.
|
- Keys are case-sensitive.
|
||||||
- Some values have format restrictions; read the .woz specification.
|
- Some values have format restrictions; read the .woz specification.
|
||||||
|
|
||||||
|
$ ./wozardry export -h
|
||||||
|
usage: wozardry export [-h] file
|
||||||
|
|
||||||
|
Export (as JSON) all information and metadata from a .woz disk image
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
file .woz disk image
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
|
||||||
|
$ ./wozardry import -h
|
||||||
|
usage: wozardry import [-h] file
|
||||||
|
|
||||||
|
Import JSON file to update information and metadata in a .woz disk image
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
file .woz disk image
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
```
|
```
|
||||||
|
@ -7,11 +7,12 @@ import argparse
|
|||||||
import binascii
|
import binascii
|
||||||
import bitarray # https://pypi.org/project/bitarray/
|
import bitarray # https://pypi.org/project/bitarray/
|
||||||
import collections
|
import collections
|
||||||
|
import json
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
|
||||||
__version__ = "dev"
|
__version__ = "dev"
|
||||||
__date__ = "2018-07-25"
|
__date__ = "2018-09-06"
|
||||||
__progname__ = "wozardry"
|
__progname__ = "wozardry"
|
||||||
__displayname__ = __progname__ + " " + __version__ + " by 4am (" + __date__ + ")"
|
__displayname__ = __progname__ + " " + __version__ + " by 4am (" + __date__ + ")"
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ kTMAP = b"TMAP"
|
|||||||
kTRKS = b"TRKS"
|
kTRKS = b"TRKS"
|
||||||
kMETA = b"META"
|
kMETA = b"META"
|
||||||
kBitstreamLengthInBytes = 6646
|
kBitstreamLengthInBytes = 6646
|
||||||
kLanguages = ("English","Spanish","French","German","Chinese","Japanese","Italian","Dutch","Portugese","Danish","Finnish","Norwegian","Swedish","Russian","Polish","Turkish","Arabic","Thai","Czech","Hungarian","Catalan","Croatian","Greek","Hebrew","Romanian","Slovak","Ukranian","Indonesian","Malay","Vietnamese","Other")
|
kLanguages = ("English","Spanish","French","German","Chinese","Japanese","Italian","Dutch","Portuguese","Danish","Finnish","Norwegian","Swedish","Russian","Polish","Turkish","Arabic","Thai","Czech","Hungarian","Catalan","Croatian","Greek","Hebrew","Romanian","Slovak","Ukranian","Indonesian","Malay","Vietnamese","Other")
|
||||||
kRequiresRAM = ("16K","24K","32K","48K","64K","128K","256K","512K","768K","1M","1.25M","1.5M+","Unknown")
|
kRequiresRAM = ("16K","24K","32K","48K","64K","128K","256K","512K","768K","1M","1.25M","1.5M+","Unknown")
|
||||||
kRequiresMachine = ("2","2+","2e","2c","2e+","2gs","2c+","3","3+")
|
kRequiresMachine = ("2","2+","2e","2c","2e+","2gs","2c+","3","3+")
|
||||||
|
|
||||||
@ -306,6 +307,10 @@ class WozReader(DiskImage, WozValidator):
|
|||||||
list(map(self.validate_metadata_requires_machine, values))
|
list(map(self.validate_metadata_requires_machine, values))
|
||||||
self.meta[key] = len(values) == 1 and values[0] or tuple(values)
|
self.meta[key] = len(values) == 1 and values[0] or tuple(values)
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
j = {"woz": {"info":self.info, "meta":self.meta}}
|
||||||
|
return json.dumps(j, indent=2)
|
||||||
|
|
||||||
def seek(self, track_num):
|
def seek(self, track_num):
|
||||||
"""returns Track object for the given track, or None if the track is not part of this disk image. track_num can be 0..40 in 0.25 increments (0, 0.25, 0.5, 0.75, 1, &c.)"""
|
"""returns Track object for the given track, or None if the track is not part of this disk image. track_num can be 0..40 in 0.25 increments (0, 0.25, 0.5, 0.75, 1, &c.)"""
|
||||||
if type(track_num) != float:
|
if type(track_num) != float:
|
||||||
@ -341,6 +346,12 @@ class WozWriter(WozValidator):
|
|||||||
if tmap_id < 159:
|
if tmap_id < 159:
|
||||||
self.tmap[tmap_id + 1] = trk_id
|
self.tmap[tmap_id + 1] = trk_id
|
||||||
|
|
||||||
|
def from_json(self, json_string):
|
||||||
|
j = json.loads(json_string)
|
||||||
|
root = [x for x in j.keys()].pop()
|
||||||
|
self.info.update(j[root]["info"])
|
||||||
|
self.meta.update(j[root]["meta"])
|
||||||
|
|
||||||
def build_info(self):
|
def build_info(self):
|
||||||
chunk = bytearray()
|
chunk = bytearray()
|
||||||
chunk.extend(kINFO) # chunk ID
|
chunk.extend(kINFO) # chunk ID
|
||||||
@ -497,22 +508,50 @@ class CommandDump(BaseCommand):
|
|||||||
for value in values[1:]:
|
for value in values[1:]:
|
||||||
print("META: ".ljust(self.kWidth), value)
|
print("META: ".ljust(self.kWidth), value)
|
||||||
|
|
||||||
class CommandEdit(BaseCommand):
|
class CommandExport(BaseCommand):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
BaseCommand.__init__(self, "edit")
|
BaseCommand.__init__(self, "export")
|
||||||
|
|
||||||
def setup(self, subparser):
|
def setup(self, subparser):
|
||||||
BaseCommand.setup(self,
|
BaseCommand.setup(self, subparser,
|
||||||
subparser,
|
description="Export (as JSON) all information and metadata from a .woz disk image")
|
||||||
description="Edit information and metadata in a .woz disk image",
|
|
||||||
epilog="""Tips:
|
def __call__(self, args):
|
||||||
|
BaseCommand.__call__(self, args)
|
||||||
|
print(self.woz_image.to_json())
|
||||||
|
|
||||||
|
class WriterBaseCommand(BaseCommand):
|
||||||
|
def __call__(self, args):
|
||||||
|
BaseCommand.__call__(self, args)
|
||||||
|
self.args = args
|
||||||
|
# maintain creator if there is one, otherwise use default
|
||||||
|
self.output = WozWriter(self.woz_image.info.get("creator", __displayname__))
|
||||||
|
self.output.tmap = self.woz_image.tmap
|
||||||
|
self.output.tracks = self.woz_image.tracks
|
||||||
|
self.output.info = self.woz_image.info.copy()
|
||||||
|
self.output.meta = self.woz_image.meta.copy()
|
||||||
|
self.update()
|
||||||
|
tmpfile = args.file + ".ardry"
|
||||||
|
with open(tmpfile, "wb") as f:
|
||||||
|
self.output.write(f)
|
||||||
|
os.rename(tmpfile, args.file)
|
||||||
|
|
||||||
|
class CommandEdit(WriterBaseCommand):
|
||||||
|
def __init__(self):
|
||||||
|
WriterBaseCommand.__init__(self, "edit")
|
||||||
|
|
||||||
|
def setup(self, subparser):
|
||||||
|
WriterBaseCommand.setup(self,
|
||||||
|
subparser,
|
||||||
|
description="Edit information and metadata in a .woz disk image",
|
||||||
|
epilog="""Tips:
|
||||||
|
|
||||||
- Use repeated flags to edit multiple fields at once.
|
- Use repeated flags to edit multiple fields at once.
|
||||||
- Use "key:" with no value to delete a metadata field.
|
- Use "key:" with no value to delete a metadata field.
|
||||||
- Keys are case-sensitive.
|
- Keys are case-sensitive.
|
||||||
- Some values have format restrictions; read the .woz specification.""",
|
- Some values have format restrictions; read the .woz specification.""",
|
||||||
help=".woz disk image (modified in place)",
|
help=".woz disk image (modified in place)",
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
self.parser.add_argument("-i", "--info", type=str, action="append",
|
self.parser.add_argument("-i", "--info", type=str, action="append",
|
||||||
help="""change information field.
|
help="""change information field.
|
||||||
INFO format is "key:value".
|
INFO format is "key:value".
|
||||||
@ -525,39 +564,39 @@ META format is "key:value".
|
|||||||
Standard keys are title, subtitle, publisher, developer, copyright, version, language, requires_ram,
|
Standard keys are title, subtitle, publisher, developer, copyright, version, language, requires_ram,
|
||||||
requires_machine, notes, side, side_name, contributor, image_date. Other keys are allowed.""")
|
requires_machine, notes, side, side_name, contributor, image_date. Other keys are allowed.""")
|
||||||
|
|
||||||
def __call__(self, args):
|
def update(self):
|
||||||
BaseCommand.__call__(self, args)
|
|
||||||
# maintain creator if there is one, otherwise use default
|
|
||||||
output = WozWriter(self.woz_image.info.get("creator", __displayname__))
|
|
||||||
output.tmap = self.woz_image.tmap
|
|
||||||
output.tracks = self.woz_image.tracks
|
|
||||||
output.info = self.woz_image.info.copy()
|
|
||||||
output.meta = self.woz_image.meta.copy()
|
|
||||||
# add all new info fields
|
# add all new info fields
|
||||||
for i in args.info or ():
|
for i in self.args.info or ():
|
||||||
k, v = i.split(":", 1)
|
k, v = i.split(":", 1)
|
||||||
if k in ("write_protected","synchronized","cleaned"):
|
if k in ("write_protected","synchronized","cleaned"):
|
||||||
v = v.lower() in ("1", "true", "yes")
|
v = v.lower() in ("1", "true", "yes")
|
||||||
output.info[k] = v
|
self.output.info[k] = v
|
||||||
# add all new metadata fields
|
# add all new metadata fields, and delete empty ones
|
||||||
for m in args.meta or ():
|
for m in self.args.meta or ():
|
||||||
k, v = m.split(":", 1)
|
k, v = m.split(":", 1)
|
||||||
v = v.split("|")
|
v = v.split("|")
|
||||||
if len(v) == 1:
|
if len(v) == 1:
|
||||||
v = v[0]
|
v = v[0]
|
||||||
if v:
|
if v:
|
||||||
output.meta[k] = v
|
self.output.meta[k] = v
|
||||||
elif k in output.meta.keys():
|
elif k in self.output.meta.keys():
|
||||||
del output.meta[k]
|
del self.output.meta[k]
|
||||||
tmpfile = args.file + ".ardry"
|
|
||||||
with open(tmpfile, "wb") as f:
|
class CommandImport(WriterBaseCommand):
|
||||||
output.write(f)
|
def __init__(self):
|
||||||
os.rename(tmpfile, args.file)
|
WriterBaseCommand.__init__(self, "import")
|
||||||
|
|
||||||
|
def setup(self, subparser):
|
||||||
|
WriterBaseCommand.setup(self, subparser,
|
||||||
|
description="Import JSON file to update information and metadata in a .woz disk image")
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.output.from_json(sys.stdin.read())
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
raise_if = lambda cond, e, s="": cond and sys.exit("%s: %s" % (e.__name__, s))
|
raise_if = lambda cond, e, s="": cond and sys.exit("%s: %s" % (e.__name__, s))
|
||||||
cmds = [CommandDump(), CommandVerify(), CommandEdit()]
|
cmds = [CommandDump(), CommandVerify(), CommandEdit(), CommandExport(), CommandImport()]
|
||||||
parser = argparse.ArgumentParser(prog=__progname__,
|
parser = argparse.ArgumentParser(prog=__progname__,
|
||||||
description="""A multi-purpose tool for manipulating .woz disk images.
|
description="""A multi-purpose tool for manipulating .woz disk images.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user