riscos-stronghelp 0.3.16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.1
2
+ Name: riscos_stronghelp
3
+ Version: 0.3.16
4
+ Summary: Extract RISC OS StrongHelp manuals
5
+ Home-page: https://github.com/gerph/riscos-stronghelp-python
6
+ Author: Charles Ferguson
7
+ Author-email: gerph@gerph.org
8
+ License: MIT
9
+ Description: # Python StrongHelp reader
10
+
11
+ This repository contains a reader for the RISC OS StrongHelp manual image format.
12
+
13
+ The Python module is able to be used to parse the format and return the files
14
+ which are present. The module can also be used as a command line tool to
15
+ extract the files to a unix file system.
16
+
17
+
18
+ ## Installation
19
+
20
+ The tool for extracting StrongHelp files can be installed manually
21
+ using this repository or through PyPI. To install, use:
22
+
23
+ pip3 install riscos_stronghelp
24
+
25
+
26
+ ## Command line
27
+
28
+ At the command line the extraction tool may be used with the
29
+ supplied shell command:
30
+
31
+ riscos-shextract [--extract-dir <directory>] <stronghelp-file>
32
+
33
+ Or to list files:
34
+
35
+ riscos-shextract --list <stronghelp-file>
36
+
37
+ Keywords: riscos
38
+ Platform: UNKNOWN
39
+ Classifier: Development Status :: 4 - Beta
40
+ Classifier: Intended Audience :: Developers
41
+ Classifier: License :: OSI Approved :: MIT License
42
+ Classifier: Programming Language :: Python :: 2.7
43
+ Classifier: Programming Language :: Python :: 3
44
+ Classifier: Operating System :: OS Independent
45
+ Requires-Python: >=2.7
46
+ Description-Content-Type: text/markdown
@@ -0,0 +1,27 @@
1
+ # Python StrongHelp reader
2
+
3
+ This repository contains a reader for the RISC OS StrongHelp manual image format.
4
+
5
+ The Python module is able to be used to parse the format and return the files
6
+ which are present. The module can also be used as a command line tool to
7
+ extract the files to a unix file system.
8
+
9
+
10
+ ## Installation
11
+
12
+ The tool for extracting StrongHelp files can be installed manually
13
+ using this repository or through PyPI. To install, use:
14
+
15
+ pip3 install riscos_stronghelp
16
+
17
+
18
+ ## Command line
19
+
20
+ At the command line the extraction tool may be used with the
21
+ supplied shell command:
22
+
23
+ riscos-shextract [--extract-dir <directory>] <stronghelp-file>
24
+
25
+ Or to list files:
26
+
27
+ riscos-shextract --list <stronghelp-file>
File without changes
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Command line tool for extracting StrongHelp manuals into a directory.
4
+
5
+ python -m riscos_stronghelp --extract-dir <directory> <stronghelp-file>
6
+ python -m riscos_stronghelp --list <stronghelp-file>
7
+ """
8
+
9
+ from __future__ import print_function
10
+ import argparse
11
+ import os
12
+ import sys
13
+
14
+ from riscos_stronghelp.format import StrongHelp, objtype_dir
15
+
16
+ RISCOS_FILETYPES = {
17
+ 0xFFF: 'Text',
18
+ 0xFFE: 'Command',
19
+ 0xFFD: 'Data',
20
+ 0xFFC: 'Utility',
21
+ 0xFFB: 'BASIC',
22
+ 0xFFA: 'Module',
23
+ 0xFF9: 'Sprite',
24
+ 0xFF8: 'Absolute',
25
+ 0xFF7: 'BBC font',
26
+ 0xFF6: 'Font',
27
+ 0xFF5: 'PoScript',
28
+ 0xFF4: 'Printout',
29
+ 0xFF2: 'Config',
30
+ 0xFF0: 'TIFF',
31
+ 0xFD1: 'BasicTxt',
32
+ 0xFED: 'Palette',
33
+ 0xFEC: 'Template',
34
+ 0xFEB: 'Obey',
35
+ 0xFEA: 'Desktop',
36
+ 0xFE6: 'Unix Ex',
37
+ 0xFE5: 'EPROM',
38
+ 0xFDC: 'SoftLink',
39
+ 0xFD3: 'DebImage',
40
+ 0xFCA: 'Squash',
41
+ 0xFC9: 'SunRastr',
42
+ 0xFAF: 'HTML',
43
+ 0xFAE: 'Resource',
44
+ 0xF89: 'GZip',
45
+ 0xF81: 'JS',
46
+ 0xF80: 'XML',
47
+ 0xF7F: 'XML_DTD',
48
+ 0xF7E: 'XSL',
49
+ 0xF75: 'JSON',
50
+ 0xF74: 'YAML',
51
+ 0xD94: 'ArtWork',
52
+ 0xC85: 'JPEG',
53
+ 0xBBC: 'BBC ROM',
54
+ 0xB61: 'XBM',
55
+ 0xB60: 'PNG',
56
+ 0xB2F: 'WMF',
57
+ 0xAFF: 'DrawFile',
58
+ 0xAAD: 'SVG',
59
+ 0xA91: 'Zip',
60
+ 0xA66: 'WebP',
61
+ 0xA65: 'JPEG2000',
62
+ 0x69E: 'PNM',
63
+ 0x69D: 'Targa',
64
+ 0x69C: 'BMP',
65
+ 0x697: 'PCX',
66
+ 0x695: 'GIF',
67
+ 0x690: 'Clear',
68
+ 0x1C9: 'DiagData',
69
+ 0x132: 'ICO',
70
+ 0x1000: 'Directory',
71
+ }
72
+
73
+
74
+ def extract_to_directory(sh, output_dir):
75
+ """
76
+ Extract the whole archive to a target directory.
77
+ """
78
+ try:
79
+ os.makedirs(output_dir)
80
+ except OSError:
81
+ pass
82
+
83
+ for shf in sh:
84
+ print("Extracting {}".format(shf.filename))
85
+ filename = os.path.join(output_dir, shf.unix_filename)
86
+ if shf.objtype == objtype_dir:
87
+ try:
88
+ os.makedirs(filename)
89
+ except OSError:
90
+ pass
91
+ else:
92
+ with open(filename, 'wb') as fh:
93
+ fh.write(shf.read())
94
+
95
+
96
+ def list_files(sh):
97
+ """
98
+ List files in the StrongHelp archive.
99
+ """
100
+ print("{:<25} {:<12} {:>10}".format("Name", "Type", "Length"))
101
+ print("-" * 50)
102
+ for shf in sh:
103
+ if shf.objtype == objtype_dir:
104
+ type_str = "Directory"
105
+ elif shf.filetype == -1:
106
+ type_str = "None"
107
+ else:
108
+ type_str = RISCOS_FILETYPES.get(shf.filetype, "{:03x}".format(shf.filetype))
109
+
110
+ print("{:<25} {:<12} {:10d}".format(shf.filename, type_str, shf.length))
111
+
112
+
113
+ def setup_argparse():
114
+ parser = argparse.ArgumentParser(usage="%s [<options>] <strong-help-file>" % (os.path.basename(sys.argv[0]),))
115
+ parser.add_argument('file', action='store',
116
+ help="StrongHelp file to read")
117
+ parser.add_argument('--extract-dir', action='store',
118
+ help="Directory to extract into")
119
+ parser.add_argument('--list', action='store_true',
120
+ help="List files in the StrongHelp file")
121
+
122
+ return parser
123
+
124
+
125
+ def main():
126
+ parser = setup_argparse()
127
+
128
+ options = parser.parse_args()
129
+
130
+ sh = StrongHelp(options.file)
131
+
132
+ if options.list:
133
+ list_files(sh)
134
+ elif options.extract_dir:
135
+ extract_to_directory(sh, options.extract_dir)
136
+ else:
137
+ # Default behavior if no action specified: list files
138
+ list_files(sh)
139
+
140
+
141
+ if __name__ == '__main__':
142
+ main()
@@ -0,0 +1,273 @@
1
+ """
2
+ Parse the StrongHelp file format into structured data.
3
+
4
+ Example usage::
5
+
6
+ from riscos_stronghelp.format import StrongHelp, objtype_dir
7
+
8
+ sh = StrongHelp(shfilename)
9
+
10
+ try:
11
+ os.makedirs(output_dir)
12
+ except OSError:
13
+ pass
14
+
15
+ for shf in sh:
16
+ print("{}".format(shf.filename))
17
+ filename = os.path.join(output_dir, shf.unix_filename)
18
+ if shf.objtype == objtype_dir:
19
+ try:
20
+ os.makedirs(filename)
21
+ except OSError:
22
+ pass
23
+ else:
24
+ with open(filename, 'wb') as fh:
25
+ fh.write(shf.read())
26
+ """
27
+
28
+ import struct
29
+
30
+
31
+ # Flags in the StrongHelp file
32
+ flag_owner_read = (1<<0) # owner read
33
+ flag_owner_write = (1<<1) # owner write
34
+ flag_locked = (1<<3) # locked
35
+ flag_public_read = (1<<4) # public read
36
+ flag_public_write = (1<<5) # public write
37
+ flag_directory = (1<<8) # is directory
38
+
39
+
40
+ # RISC OS Object types
41
+ objtype_file = 1
42
+ objtype_dir = 2
43
+
44
+
45
+ class StrongHelpFormatError(Exception):
46
+ pass
47
+
48
+
49
+ class StrongHelpBlock(object):
50
+
51
+ def __init__(self, sh, offset):
52
+ self.sh = sh
53
+ self.offset = offset
54
+
55
+ def read_bytes(self, size, offset=0):
56
+ return self.sh.read_bytes(size, self.offset + offset)
57
+
58
+ def read_word(self, offset=0):
59
+ return self.sh.read_word(self.offset + offset)
60
+
61
+ def read_string(self, offset=0):
62
+ return self.sh.read_string(self.offset + offset)
63
+
64
+
65
+ class StrongHelpObject(StrongHelpBlock):
66
+
67
+ def __init__(self, sh, offset, parent_dir, leafname, loadaddr=0, execaddr=0, flags=0, length=0):
68
+ super(StrongHelpObject, self).__init__(sh, offset)
69
+ self.parent_dir = parent_dir
70
+ self.leafname = leafname
71
+ self.loadaddr = loadaddr
72
+ self.execaddr = execaddr
73
+ self.flags = flags
74
+ self.length = length
75
+
76
+ #print("{} '{}' at &{:x}".format(self.__class__.__name__,
77
+ # self.filename, self.offset))
78
+
79
+ def __repr__(self):
80
+ return "<{}(name='{}', type=0x{:x}, size={})>".format(self.__class__.__name__,
81
+ self.filename,
82
+ self.filetype,
83
+ self.length)
84
+
85
+ @property
86
+ def attributes(self):
87
+ """
88
+ RISC OS File attributes.
89
+ """
90
+ # Just the bottom 8 bits
91
+ return self.flags & 0xFF
92
+
93
+ @property
94
+ def objtype(self):
95
+ """
96
+ RISC OS object type.
97
+ """
98
+ return objtype_dir if (self.flags & flag_directory) else objtype_file
99
+
100
+ @property
101
+ def filetype(self):
102
+ if self.flags & flag_directory:
103
+ return 0x1000
104
+ if self.loadaddr & 0xFFF00000 == 0xFFF00000:
105
+ return (self.loadaddr >> 8) & 0xFFF
106
+ return -1
107
+
108
+ @property
109
+ def filename(self):
110
+ if not self.parent_dir:
111
+ return self.leafname
112
+ parent = self.parent_dir.filename
113
+ return "{}.{}".format(parent, self.leafname)
114
+
115
+ @property
116
+ def unix_filename(self):
117
+ leafname = self.leafname.replace('/', '.')
118
+ if not self.parent_dir:
119
+ return leafname
120
+
121
+ if self.filetype in (0xFFF, 0x1000):
122
+ # A text file or a directory
123
+ suffix = ''
124
+
125
+ elif self.filetype == -1:
126
+ # Not filetyped - there's a load and exec address.
127
+ # We will treat these as Data for now - the strongcopy tool
128
+ # doesn't support load and exec formats.
129
+ suffix = ',ffd'
130
+
131
+ else:
132
+ # Other filetypes
133
+ suffix = ',%03x' % (self.filetype,)
134
+
135
+ parent = self.parent_dir.unix_filename
136
+ if parent == '$':
137
+ return leafname + suffix
138
+ else:
139
+ return "{}/{}{}".format(parent, leafname, suffix)
140
+
141
+
142
+ class StrongHelpFile(StrongHelpObject):
143
+
144
+ def __init__(self, sh, offset, parent_dir, leafname, loadaddr=0, execaddr=0, flags=0, length=0):
145
+ super(StrongHelpFile, self).__init__(sh, offset, parent_dir, leafname, loadaddr, execaddr, flags, length)
146
+
147
+ if offset == 0:
148
+ # This is a 0 byte file
149
+ self.size = 0
150
+
151
+ else:
152
+ if self.read_bytes(4, offset=0) != b'DATA':
153
+ raise StrongHelpFormatError("Bad file entry '{}' at offset &{:x}: "
154
+ "Block header is invalid".format(self.filename,
155
+ offset))
156
+
157
+ self.size = self.read_word(4)
158
+ if self.size < self.length:
159
+ raise StrongHelpFormatError("Bad file entry '{}' at offset &{:x}: "
160
+ "Block is too small ({}) for file length ({})".format(self.filename,
161
+ offset,
162
+ self.size,
163
+ self.length))
164
+
165
+ def read(self, encoding=None):
166
+ if not self.size:
167
+ data = b''
168
+ else:
169
+ data = self.read_bytes(self.length - 8, offset=8)
170
+ if encoding:
171
+ data = data.decode(encoding)
172
+
173
+ return data
174
+
175
+
176
+ class StrongHelpDir(StrongHelpObject):
177
+
178
+ def __init__(self, sh, offset, parent_dir, leafname, loadaddr=0, execaddr=0, flags=0, length=0):
179
+ super(StrongHelpDir, self).__init__(sh, offset, parent_dir, leafname, loadaddr, execaddr, flags, length)
180
+
181
+ if self.read_bytes(4, offset=0) != b'DIR$':
182
+ raise StrongHelpFormatError("Bad directory entry '{}' at offset &{:x}: "
183
+ "Block header is invalid".format(self.filename,
184
+ offset))
185
+
186
+ self.dir_size = self.read_word(4)
187
+ self.dir_used = self.read_word(8)
188
+
189
+ if self.dir_used > self.dir_size:
190
+ raise StrongHelpFormatError("Bad directory entry '{}' at offset &{:x}: "
191
+ "Block is too small ({}) for used size ({})".format(self.filename,
192
+ offset,
193
+ self.dir_size,
194
+ self.dir_used))
195
+ if self.dir_used < 12 + 24:
196
+ raise StrongHelpFormatError("Bad directory entry '{}' at offset &{:x}: "
197
+ "Block is too small ({}) for directory entry".format(self.filename,
198
+ offset,
199
+ self.dir_used))
200
+
201
+ self.objects = []
202
+ offset = 12
203
+ while offset < self.dir_used:
204
+ #print("--- file in dir {}, offset &{:x} ---".format(self.filename, offset))
205
+ object_offset = self.read_word(offset + 0)
206
+ loadaddr = self.read_word(offset + 4)
207
+ execaddr = self.read_word(offset + 8)
208
+ length = self.read_word(offset + 12)
209
+ flags = self.read_word(offset + 16)
210
+ reserved = self.read_word(offset + 20)
211
+ # FIXME: Assuming the filename encoding is latin-1 (probably correct, but not configurable)
212
+ name_bytes = self.read_string(offset + 24)
213
+ name = name_bytes.decode('latin-1')
214
+ offset += 24 + (len(name_bytes) + 4) & ~3
215
+
216
+ if flags & flag_directory:
217
+ shfile = StrongHelpDir(self.sh, object_offset, parent_dir=self, leafname=name,
218
+ loadaddr=loadaddr, execaddr=execaddr, flags=flags, length=length)
219
+ else:
220
+ shfile = StrongHelpFile(self.sh, object_offset, parent_dir=self, leafname=name,
221
+ loadaddr=loadaddr, execaddr=execaddr, flags=flags, length=length)
222
+ self.objects.append(shfile)
223
+
224
+
225
+ class StrongHelp(object):
226
+
227
+ def __init__(self, filename=None, data=None):
228
+ self.filename = filename
229
+ if data:
230
+ self.data = data
231
+ else:
232
+ with open(filename, 'rb') as fh:
233
+ self.data = fh.read()
234
+
235
+ if self.data[0:4] != b'HELP':
236
+ raise StrongHelpFormatError("This is not a StrongHelp file")
237
+
238
+ self.root_size = self.read_word(4)
239
+ self.stronghelp_version = self.read_word(8)
240
+ self.free_list = self.read_word(12)
241
+
242
+ self.root_offset = self.read_word(16)
243
+
244
+ #print("Root offset = %08x" % (self.root_offset,))
245
+
246
+ self.root = StrongHelpDir(self, self.root_offset, parent_dir=None, leafname='$')
247
+
248
+ def __iter__(self):
249
+ """
250
+ List all the files in the StrongHelp file.
251
+ """
252
+ shdirs = [self.root]
253
+ while shdirs:
254
+ shdir = shdirs.pop()
255
+ for shobj in shdir.objects:
256
+ if isinstance(shobj, StrongHelpDir):
257
+ shdirs.append(shobj)
258
+ yield shobj
259
+
260
+ def read_word(self, offset):
261
+ #print("Read word at &{:x}".format(offset))
262
+ (data,) = struct.unpack('<L', self.data[offset:offset + 4])
263
+ #print(" &{:08x} = {}".format(data, data))
264
+ return data
265
+
266
+ def read_bytes(self, size, offset):
267
+ return self.data[offset:offset + size]
268
+
269
+ def read_string(self, offset):
270
+ start = offset
271
+ while offset < len(self.data) and self.data[offset:offset+1] != b'\0':
272
+ offset += 1
273
+ return self.data[start:offset]
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.1
2
+ Name: riscos-stronghelp
3
+ Version: 0.3.16
4
+ Summary: Extract RISC OS StrongHelp manuals
5
+ Home-page: https://github.com/gerph/riscos-stronghelp-python
6
+ Author: Charles Ferguson
7
+ Author-email: gerph@gerph.org
8
+ License: MIT
9
+ Description: # Python StrongHelp reader
10
+
11
+ This repository contains a reader for the RISC OS StrongHelp manual image format.
12
+
13
+ The Python module is able to be used to parse the format and return the files
14
+ which are present. The module can also be used as a command line tool to
15
+ extract the files to a unix file system.
16
+
17
+
18
+ ## Installation
19
+
20
+ The tool for extracting StrongHelp files can be installed manually
21
+ using this repository or through PyPI. To install, use:
22
+
23
+ pip3 install riscos_stronghelp
24
+
25
+
26
+ ## Command line
27
+
28
+ At the command line the extraction tool may be used with the
29
+ supplied shell command:
30
+
31
+ riscos-shextract [--extract-dir <directory>] <stronghelp-file>
32
+
33
+ Or to list files:
34
+
35
+ riscos-shextract --list <stronghelp-file>
36
+
37
+ Keywords: riscos
38
+ Platform: UNKNOWN
39
+ Classifier: Development Status :: 4 - Beta
40
+ Classifier: Intended Audience :: Developers
41
+ Classifier: License :: OSI Approved :: MIT License
42
+ Classifier: Programming Language :: Python :: 2.7
43
+ Classifier: Programming Language :: Python :: 3
44
+ Classifier: Operating System :: OS Independent
45
+ Requires-Python: >=2.7
46
+ Description-Content-Type: text/markdown
@@ -0,0 +1,10 @@
1
+ README.md
2
+ setup.py
3
+ riscos_stronghelp/__init__.py
4
+ riscos_stronghelp/__main__.py
5
+ riscos_stronghelp/format.py
6
+ riscos_stronghelp.egg-info/PKG-INFO
7
+ riscos_stronghelp.egg-info/SOURCES.txt
8
+ riscos_stronghelp.egg-info/dependency_links.txt
9
+ riscos_stronghelp.egg-info/entry_points.txt
10
+ riscos_stronghelp.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ riscos-shextract = riscos_stronghelp.__main__:main
3
+
@@ -0,0 +1 @@
1
+ riscos_stronghelp
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Packaging for the riscos_stronghelp package.
4
+ """
5
+
6
+ from distutils.core import setup
7
+ import setuptools # noqa
8
+
9
+ from os import path
10
+ # io.open is needed for projects that support Python 2.7
11
+ # It ensures open() defaults to text mode with universal newlines,
12
+ # and accepts an argument to specify the text encoding
13
+ # Python 3 only projects can skip this import
14
+ from io import open
15
+
16
+ here = path.abspath(path.dirname(__file__))
17
+
18
+ # Get the long description from the README file
19
+ with open(path.join(here, 'README.md'), encoding='utf-8') as f:
20
+ long_description = f.read()
21
+
22
+
23
+ setup(
24
+ name = 'riscos_stronghelp',
25
+ packages = ['riscos_stronghelp'],
26
+ version = "0.3.16",
27
+ license='MIT',
28
+ description = 'Extract RISC OS StrongHelp manuals',
29
+ long_description = long_description,
30
+ long_description_content_type = 'text/markdown',
31
+ author = 'Charles Ferguson',
32
+ author_email = 'gerph@gerph.org',
33
+ url = 'https://github.com/gerph/riscos-stronghelp-python',
34
+ keywords = ['riscos'],
35
+ entry_points={ 'console_scripts': ['riscos-shextract = riscos_stronghelp.__main__:main'] },
36
+ install_requires= [
37
+ ],
38
+ classifiers= [
39
+ 'Development Status :: 4 - Beta',
40
+ 'Intended Audience :: Developers',
41
+ 'License :: OSI Approved :: MIT License',
42
+ 'Programming Language :: Python :: 2.7',
43
+ 'Programming Language :: Python :: 3',
44
+ "Operating System :: OS Independent",
45
+ ],
46
+ python_requires='>=2.7',
47
+ )