EBook:...ls::IMP(3pm)
π NAME π
EBook::Tools::IMP β the object class that lets you mess with the SoftBook/GEB/REB/eBookWise .IMP and .RES e-book formats. it's giving β¨file-format-whispererβ¨
β¨ SYNOPSIS β¨
use EBook::Tools::IMP qw(:all)
my $imp = EBook::Tools::IMP->new();
$imp->load('myfile.imp');
π CONSTRUCTOR AND INITIALIZATION π
πΈ new($filename)
Spins up a brand new EBook::Tools::IMP object for you, bestie. And if you hand it a $filename, it immediately initializes itself through the load method β no extra steps, we love a self-starter. π
π¦ load($filename)
Loads up a .imp file and parses it into all the various object attributes. Returns 1 when it ate, or undef if it flopped. π
π load_resdir($dirname)
Loads a .RES resource directory and parses it into the object attributes. Returns 1 on success, or undef on failure. same energy, different folder.
π author()
Returns the full name of the book's author. π«
Here's the tea: the author info can live entirely in the $self->{firstname} attribute, or it can be split up across $self->{firstname}, $self->{middlename}, and $self->{lastname}. If the last name shows up separately, you get the full name formatted as "Last, First Middle". Otherwise it comes back as "First Middle".
β bookproplength()
Returns the total length in bytes of the book properties data β and yes, that includes the trailing null used to pack the C-style strings. It does not include any ETI server data that's been appended to the end of the standard book properties though. know the difference, it matters.
π filecount()
Returns the number of resource files exactly as stored in $self->{filecount}.
β οΈ heads up: this does NOT recompute that value from the actual number of resources sitting in$self->{resources}. if you want the real, honest, recalculated number, callcreate_toc_from_resources()instead. don't get it twisted. π
π§ find_image_type($id,@excluded)
Goes through every stored image looking for one with the $id value you specified, and returns the first image type it finds β or undef if nothing matched or if you didn't even give it an image id. no id, no vibes.
If you pass the optional @excluded argument, every type in that list gets skipped during the search. bye felicia π
The expected types are 'png', 'jpg', 'gif', and 'pic' β and it searches them in exactly that order.
This one's lowkey a lifesaver: you can use it to try to track down an alternate image to replace an undisplayable PICT image. π₯
π find_resource_by_name($name)
Takes one single argument: a resource name. If a resource with that name exists in $self->{resources}, it returns the resource type used as the hash key.
Returns undef if there was no match, or if you didn't specify a name at all. be specific, queen.
π
image($type,$id)
Returns the image data stored in the resource of the specified type β specifically, whatever's stored in $self->{$type}->{$id}->{data} as parsed from the JPEG resource β for the 16-bit identifier you pass as $id.
Valid values for $type are 'gif', 'jpg', and 'png'.
It carps a warning and returns undef if $type isn't provided or isn't valid, or if $id isn't provided. β
π¦ image_hashref($type,$id)
Returns the raw object hashref used to store parsed image data for the specified type, as stored in $self->{$type}. Valid types are 'gif', 'jpg', and 'png'.
It carps a warning and returns undef if $type isn't provided or isn't valid.
If you leave $id out, the keys of the returned hash are the image IDs for that image type, and the values are hashrefs pointing to hashes that contain these keys:
"unknown"β a 16-bit integer that's only available on EBW 1150 resources. Use with caution. This key may get renamed if more info is ever found. π€·- "length" β the length of the actual image data.
- "offset" β the byte offset inside of the raw resource data where the JPEG image data can be found.
"const0"β an unknown value, but it appears to always be zero. Use with caution. This key may get renamed if more information is found.
And if you do specify the optional $id, you only get back the hash for that specific ID instead of the whole hash-of-hashrefs situation. surgical. precise. iconic. π
πΈ image_ids($type)
Returns a list of the 16-bit integer IDs for the specified type of image data stored in the associated resource β specifically, whatever's stored in $self->{$type} as parsed from the JPEG resource.
Valid types are 'gif', 'jpg', and 'png'. If you pass any other type, or no type at all, the method will carp a warning and return undef. read the room π
π is_1150()
Returns 1 if $self->{device} == 2, returns 0 if it's some other value, and undef if it's undefined.
Why do we care? Because resources packed for an EBW 1150 or a GEB 1150 are in a different format than resources packed for other IMP readers. so yeah, this lil check has value. π―
π§ offsetelement($offset)
Returns the text of the element corresponding to the given text offset, as stored in $self->{offsetelements} β or undef if no such element exists. if it's not there, it's just not there.
β pack_imp_book_properties()
Packs the object attributes into the 7 null-terminated strings that make up the book properties section of the header. Returns that string.
π note: this does NOT pack the ETI server data that gets appended to this section in encrypted books downloaded straight from the ETI servers β even if that data was found when the.impfile was loaded. Why? Because the extra data can confuse the GEBLibrarian application, and it isn't needed to read the book. Thebookproplength()andpack_imp_header()methods also assume that this data won't be present.
π pack_imp_header()
Packs the object attributes into the 48-byte string representing the IMP header. Returns that string on success, and carps a warning plus returns undef if a required attribute didn't contain valid data. π¨
β οΈ heads up: for an encrypted e-book with ETI server data in it, this header will not be identical to the original β theresdiroffsetvalue gets recalculated for the position with the ETI server data stripped out. Seebookproplength()andpack_imp_book_properties().
π pack_imp_resource(%args)
Packs the resource you specify (it lives in $self->{resources}) into a data string that's suitable for writing into a .imp file, with a header format determined by $self->{version}.
Returns a reference to that string if the resource was found, or undef if it wasn't. found or not found, no in between.
Arguments
"name"β selects the resource by resource name.- If both this and
"type"are specified, the type gets checked first and the name is only used if the type lookup fails.
- If both this and
"type"β selects the resource by resource type. This is faster than selecting by name (since resources are stored in a hash keyed by type) and it's recommended for most use. π- If both this and
"name"are specified, the type gets checked first and the name is only used if the type lookup fails.
- If both this and
π pack_imp_rsrc_inf()
Packs the object attributes into the data string that would be the content of the RSRC.INF file. Returns that string. slay. β¨
π¦ pack_imp_toc()
Packs the $self->{toc} object attribute into a data string suitable for writing into a .imp file. The format is determined by $self->{version}.
Returns that string, or undef if a valid version or TOC data isn't found. π
π resdirbase()
In scalar context, this returns the basename of $self->{resdirname}. In list context, it actually returns the basename, directory, and extension, exactly as per fileparse from File::Basename. context-dependent behavior, we love a plot twist.
π resdirlength()
Returns the length of the .RES directory name as stored in $self->{resdirlength}.
β οΈ note: this does NOT recompute the length from the actual name stored in$self->{resdirname}β for that, useset_resdirlength(). don't come for me, come for the right method. π
π§ resdirname()
Returns the .RES directory name stored in $self->{resdirname}. that's it, that's the method. β¨
β resource($type)
Returns a hashref containing the resource data for the specified resource type, as stored in $self->{resources}->{$type}.
Returns undef if $type isn't specified, or if the type you asked for isn't found. no type, no data, no drama.
π resources()
Returns a hashref of hashrefs containing all the resource data, keyed by type, as stored in $self->{resources}. the whole buffet. π½οΈ
πΈ text()
Returns the uncompressed text that was originally stored in the DATA.FRK (' ') resource.
β οΈ this will only work if the text was unencrypted. encrypted text? not today. π
π title()
Returns the book title as stored in $self->{title}. short, sweet, iconic. π
π
tocentry($index)
Takes one single argument: an integer index into the table of contents data stored in $self->{toc}. Returns the hashref corresponding to that TOC entry if it exists, or undef otherwise.
π¦ version()
Returns the version of the IMP format β as stored in $self->{version} β which is what determines the TOC and resource metadata size. Expected values are 1 (10-byte metadata) and 2 (20-byte metadata). two options, pick your fighter.
π write_images(%args)
Writes out the images, if there are any, into the output directory you specify. Filenames come out in the format JPEG_XXXX.jpg or PNG_XXXX.png, where XXXX is the image ID for that image type formatted as four hexadecimal characters. πΌοΈ
Arguments
"dir"β the output directory to write the file into. This will be created if it doesn't already exist. Defaults to the basename of the stored resource directory (see alsoresdirname()).
π write_imp($filename)
Takes one sole argument: the name of a file to write to. Then it writes a .imp file to that filename using the object attribute data. βοΈ
Returns 1 on success, or undef if required data (including the filename) was invalid or missing, or if the file couldn't be written. no filename, no file, bestie.
π§ write_resdir()
Writes a .RES resource directory from the object attribute data, using $self->{resdirname} as the directory name.
β write_text(%args)
Writes the uncompressed text, if there is any, into the output directory and file you specify.
Arguments
"dir"β the output directory to write the file into. This will be created if it doesn't already exist. Defaults to the basename of the stored resource directory (see alsoresdirname())."filename"β the filename of the output file to write. If it's not specified, a warning will be carped and the method will returnundef. π
π create_toc_from_resources()
Creates the appropriate table of contents data from the metadata sitting in $self->{resources}, in the format specified by $self->{version}. This will also set $self->{filecount} to match the actual number of resources. two birds, one method π¦
Returns the number of resources found.
πΈ parse_eti_server_data($data)
Parses ETI server data β the stuff that can potentially be found appended to the end of .imp book properties, or to an RSRC.INF resource file, on encrypted books downloaded directly from the ETI servers.
Takes one single argument: a string containing just the extra appended data. It stores the parsed values in $self->{etiserverdata} as a hash.
β οΈ real talk: parsing requires knowledge of the length of the book properties at the time this data was inserted. So if the book properties haven't been properly parsed, or if they've been modified, the resulting behaviour of this method is not defined. you have been warned. π¨
Returns the number of bytes handled, or zero if no data was provided.
[0-3 bytes]β padding data whose whole job is making sure the next chunk lands on a 4-byte boundary. Lives in the key"pad". π[4 bytes, big-endian unsigned long int]β an unknown value, usually2. Stored in the key"unknown1". We literally don't know what it does, bestie, we just log it. π[4 bytes, big-endian unsigned long int]β issue number for periodicals (it's always0xfffffffffor books). Stored in the key"issuenumber". π[variable-length null-terminated string]β the content feed for periodicals, or a null string for books. Stored in the key"contentfeed". ποΈ[variable-length null-terminated string]β the source string, formatted exactly like'SOURCE_ID:SOURCE_TYPE:None', where"SOURCE_ID"is usually'3'and"SOURCE_TYPE"is usually'B'. π§[4 bytes, big-endian unsigned long int]β another unknown value, stored in the key"unknown2". Heads up tho: this value may not be present at all. So don't go assuming it's there. π
πΈ parse_imp_book_properties($propdata) πΈ
Takes a single argument: a string holding the book properties data. It then sets the object variables from whatever's inside. That data should be seven null-terminated strings, in this exact order:
- Identifier
- Category
- Subcategory
- Title
- Last Name
- Middle Name
- First Name
Okay so real talk: the entire name gets shoved into the "First Name" component all the time, and the "Last Name" and "Middle Name" components just sit there blank. It's giving abandoned. π«
And plot twist π β for encrypted books downloaded from ETI servers, ETI server data may get appended to this data. If it's there, it lands in the hash $self->{etiserverdata}. Full deets live in "parse_eti_server_data($data)".
Also, if the length of the parsed properties (null string terminators included) doesn't equal the length of the data you handed in, you get a warning carped at you. Not mad, just disappointed. π
π₯ parse_imp_header() π₯
Parses the first 48 bytes of a .IMP file and sets the object variables. If you give it anything more or less than 48 bytes, the method croaks. It is not flexible. It is not chill. 48 bytes or we riot. π
Header Format β¨
- Offset
0x00β[2 bytes, big-endian unsigned short int]: the Version. Expected values are1or2, and this version is what changes the format of the table of contents header. If it's not1or2, the method carps a warning and returnsundef. π© - Offset
0x02β[8 bytes]: the Identifier. This is always'BOOKDOUG', and if it isn't, the method carps a warning and returnsundef. Like, that's the vibe check and you failed it, no cap. - Offset
0x0Aβ[8 bytes]: unknown data, stored in$self->{unknown0x0a}. Use with caution β this value may be renamed if more information is obtained. π€· - Offset
0x12β[2 bytes, big-endian unsigned short int]: number of included files, stored in$self->{filecount}. ποΈ - Offset
0x14β[2 bytes, big-endian unsigned short int]: length in bytes of the.RESdirectory name, stored in$self->{resdirlength}. - Offset
0x16β[2 bytes, big-endian unsigned short int]: offset from the point after this value to the.RESdirectory name, which also marks the end of the book properties. Stored in$self->{resdiroffset}. Okay listen carefully bestie, this one is a trap π¨ β it is NOT the length of the book properties. To get the length of the book properties, subtract24from this value (that's the number of bytes remaining in the header after this point). It is also NOT the offset from the beginning of the file to the.RESdirectory name β to find that, add24to this value (the number of bytes already parsed). Iconic math, honestly. - Offset
0x18β[4 bytes, big-endian unsigned long int?]: unknown value, stored in$self->{unknown0x18}. Use with caution β this value may be renamed if more information is obtained. - Offset
0x1Cβ[4 bytes, big-endian unsigned long int?]: unknown value, stored in$self->{unknown0x1c}. Use with caution β this value may be renamed if more information is obtained. - Offset
0x20β[4 bytes, big-endian unsigned long int]: compression type, stored in$self->{compression}. Expected values are0(no compression) and1(LZSS compression). ποΈ - Offset
0x24β[4 bytes, big-endian unsigned long int]: encryption type, stored in$self->{encryption}. Expected values are0(no encryption) and2(DES encryption). π - Offset
0x28β[2 bytes, big-endian unsigned short int]: unknown value, stored in$self->{unknown0x28}. Use with caution β this value may be renamed if more information is obtained. - Offset
0x2Aβ[1 byte]: unknown value, stored in$self->{unknown0x2A}. Use with caution β this value may be renamed if more information is obtained. - Offset
0x2Bβ[2 nybbles (1 byte)]: okay this one is two things in a trench coat π§₯ β the upper nybble at this position is the IMP reader device the e-book was designed for, stored in$self->{device}. Expected values are0(Softbook 200/250e),1(REB 1200/GEB 2150), and2(EBW 1150/GEB1150). The lower nybble marks the possible zoom states, stored in$self->{zoomstates}. Expected values are0(both zooms),1(small zoom), and2(large zoom). Two nybbles, two jobs, main character energy fr fr β¨ - Offset
0x2Cβ[4 bytes, big-endian unsigned long int]: unknown value, stored in$self->{unknown0x2c}. Use with caution β this value may be renamed if more information is obtained.
π¦ parse_resource_cm() π¦
Parses the "!!cm" resource that's been loaded into $self->{resources}, if present, and extracts the LZSS uncompression parameters into $self->{lzssoffsetbits} and $self->{lzsslengthbits}.
Returns 1 on success, or undef if no "!!cm" resource has been loaded yet, or if the resource data is invalid. Sometimes it just says no. π
π parse_resource_images() π
Parses the image data resources loaded into $self->{resources}, if present, and places the image data and metadata of every image it finds into $self->{jpg} and $self->{png}, keyed by 16-bit image resource ID. πΌοΈ
Returns the total number of images found and parsed.
Not me having to tell you this, but: this method is called automatically by "load()" and "load_resdir()". You don't have to do it yourself, bestie. π
See also the accessor methods "image(%args)" and "image_hashrefs(%args)". π
π parse_resource_imrn() π
Parses the index of text offsets to all images, stored in $self->{resources}->{'ImRn'} if present, and stores them in $self->{imrn} as a hash of hashrefs, indexed by its 32-bit integer offset to the 0x0F control code in the uncompressed text stored in the DATA.FRK resource.
Returns the total number of offsets found and parsed.
The hash keys of each offset hash are:
"width"β image display width in pixels. π"height"β image display height in pixels. π"id"β a 16-bit integer value used to uniquely identify the image inside a particular resource type."restype"β the four-letter resource type string. π€"constF1"β a 32-bit value of unknown purpose which should always be0xFFFFFFFF."constF2"β a second 32-bit value of unknown purpose which should always be0xFFFFFFFF."const0"β a 32-bit integer value of unknown purpose which should always be0x00000000."constB"β a 16-bit integer value of unknown purpose which could be0xFFFA,0xFFFB,0xFFFC, or0xFFFE."unknown16"β a 16-bit integer value of unknown purpose found only in 1150 resources. π"unknown32"β a 32-bit integer value of unknown purpose.
And again, since we're being thorough: this method is called automatically by "load()" and "load_resdir()". π«Ά
π parse_text() π
Parses the ' ' (DATA.FRK) resource loaded into $self->{resources}, if present, extracting the text into $self->{text}, uncompressing it if that's what's needed. LZSS uncompression will use the $self->{lzsslengthbits} and $self->{lzssoffsetbits} attributes if they're present, and defaults to 3 length bits and 14 offset bits otherwise.
Then HTML headers and footers get applied, and control codes get swapped out for the appropriate tags. β¨
Returns the length of the raw uncompressed text before any HTML modification happened, or undef if no text resource was found or if the text was encrypted. π
β parse_imp_toc_v1($tocdata) β
Takes a single argument β a string containing the table of contents data β and parses it into object attributes following the version 1 format, which is 10 bytes per entry. Clean. Simple. We stan. π
Format
- Offset
0x00β[4 bytes, text]: resource name. Stored in hash key"name". In the case of the'DATA.FRK'text resource, this will be four spaces (' '). Yes, four whole spaces. Iconic. - Offset
0x04β[2 bytes, big-endian unsigned short int]: unknown, but always zero or one. Stored in hash key"unknown1". - Offset
0x08β[4 bytes, big-endian unsigned long int]: size of the resource data in bytes. Stored in hash key"size".
π« parse_imp_toc_v2($tocdata) π«
Takes a single argument β a string with the table of contents data β and parses it into object attributes following the version 2 format, which is 20 bytes per entry. Double the bytes, double the drama. π
Format
- Offset
0x00β[4 bytes, text]: resource name. Stored in"name". In the case of the'DATA.FRK'text resource, this will be four spaces (' '). - Offset
0x04β[4 bytes, big-endian unsigned long int]: unknown, but always zero. Stored in"unknown1". - Offset
0x08β[4 bytes, big-endian unsigned long int]: size of the resource data in bytes. Stored in"size". - Offset
0x0Cβ[4 bytes, text]: resource type. Stored in"type", and used as the key for the stored resource hash. π That's the whole point of it, it's keyed, bestie. - Offset
0x10β[4 bytes, big-endian unsigned long int]: unknown, but always either zero or one. Stored in"unknown2".
π set_book_properties(%args) π
Sets whichever book properties you specify. Returns 1 on success, or undef if you specified no properties at all. Giving it nothing is a choice, and it will respect that choice by returning undef. π«
Arguments
"identifier"β the book identifier, like the kind an OPF<dc:identifier>element might provide."category"β the main book category, like the kind an OPF<dc:subject>element might provide."subcategory"β the subcategory, generally a set of search arguments for the ETI website. π"title"β the book title, like the kind an OPF<dc:title>element might provide."lastname"β the primary author's last name, but please read the"firstname"entry before you decide how to handle name storage. I'm serious, it changes everything."middlename"β the primary author's middle name, but same deal, go read the"firstname"entry before you decide how to handle name storage."firstname"β the primary author's first name, but heads up π¨ β a great many.impbooks use this field to store the entire name in"First Last"format. If you're using it that way,"lastname"and"middlename"must be blank. Blank blank. Nothing in there.
Example
$imp->set_book_properties(title => 'My Best Book',
category => 'Fiction',
firstname => 'John Q. Public');
That's the whole thing, and it's lowkey satisfying. π
β¨ PROCEDURES β¨
Okay so literally all procedures here are exportable, but bestie, none are exported by default. You have to ask for them, periodt.
detect_resource_type(\$data)
It takes as a sole argument a reference to the data component of a resource. If it detects successfully, it returns a 4-byte string containing the resource type. Otherwise? undef. That's it.
π₯ Heads up: detection will not work on the"DATA.FRK"(' ') resource. That one has to be detected separately by name/type. No cap.
parse_imp_resource_v1()
Takes as a sole argument a string containing the data (including the 10-byte header) of a version 1 IMP resource.
Returns a hashref containing that data separated into the following keys:
nameβ the four-letter name of the resource. πtypeβ the four-letter type of the resource. This is detected from the data, and is not part of the v1 header.unknown1β a16-bit unsigned intof unknown purpose. Expected values are0or1. Use with caution. This key may be renamed later if more information is found.sizeβ the expected size in bytes of the actual resource data. A warning will be carped if this does not match the actual size of the data following the header.dataβ the actual resource data.
parse_imp_resource_v2()
Takes as a sole argument a string containing the data (including the 20-byte header) of a version 2 IMP resource.
Returns a hashref containing that data separated into the following keys:
nameβ the four-letter name of the resource. πunknown1β a32-bit unsigned intof unknown purpose. Expected values are0or1. Use with caution. This key may be renamed later if more information is found.sizeβ the expected size in bytes of the actual resource data. A warning will be carped if this does not match the actual size of the data following the header.typeβ the four-letter type of the resource.unknown2β a32-bit unsigned intof unknown purpose. Expected values are0or1. Use with caution. This key may be renamed later if more information is found.dataβ the actual resource data.
π¨ BUGS AND LIMITATIONS π¨
- Not finished. Do not try to use yet. Like, fr fr, it's giving "do not pass go." π
- MacPaint PICT images are not well-supported. If present in the book, they will be saved, but a warning will be carped about invalid image data.
- Support for v1 files is completely untested and implemented with some guesswork. Bug reports welcome. π«Ά
π AUTHOR π
Zed Pobre <zed@debian.org> π
π«Ά THANKS π«Ά
Thanks are due to Nick Rapallo <nrapallo@yahoo.ca> for invaluable assistance in understanding the .IMP format and testing this code. π«Ά
Thanks are also due to Jeffrey Kraus-yao <krausyaoj@ameritech.net> for his work reverse-engineering the .IMP format to begin with, and the documentation at <http://krausyaoj.tripod.com/reb1200.htm>. π₯
π LICENSE AND COPYRIGHT π
Copyright 2008 Zed Pobre
Licensed to the public under the terms of the GNU GPL, version 2.
β¨ And that's the tea! This page is fromperl v5.40.0, dated2025-01-10, and it's theEBook:...ls::IMP(3pm)manual. Stay iconic, bestie π π
π¦ where this page comes from
- debian trixie: libebook-tools-perl 0.5.4-2 Β· GPL-2.0-only
- ubuntu resolute: libebook-tools-perl 0.5.4-2 Β· GPL-2.0-only
- ubuntu stonking: libebook-tools-perl 0.5.4-2 Β· GPL-2.0-only
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms