├── .gitignore ├── README.md ├── doc ├── copying.txt └── wteledsk.htm ├── dos ├── tdconv.exe └── wteledsk.exe └── src ├── Makefile ├── tdcrc.c ├── tdlzhuf.c └── wteledsk.c /.gitignore: -------------------------------------------------------------------------------- 1 | /src/wteledsk 2 | /src/wteledsk.dSYM/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wteledsk 2 | converts teledisk to raw images 3 | 4 | ### About 5 | wteledsk was originally written by [Will Kranz](http://www.willsworks.net/) for reading Sydex Teledisk images from vintage computers. For more details see [here](http://www.willsworks.net/file-format/teledisk). 6 | 7 | The image format was used as well to make images from non-standard floppy disks used in electronic music equipment, such as the AKAI S1000 sampler, or e.g. the General Music S2/3 Music Processor. This software has been used successfully with floppy images of the latter. 8 | 9 | ### Usage 10 | ``` 11 | usage: wteledsk [-n#] -o [-p] [-s] [-r] 12 | -n limit scan to first n sectors 13 | -o output restructured blocks to a file 14 | -p display phantom sec_rec.nsec values 15 | -s warn instead of fatal error on skipped sectors 16 | -r if repeated sector found, write it out again 17 | Version 1.01 A 18 | ``` 19 | 20 | The most useful invocation for converting GEM S2 image files seems to be `wteledsk INFILE.TD0 -oOUTFILE.IMG -s`. 21 | -------------------------------------------------------------------------------- /doc/copying.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | 342 | 343 | -------------------------------------------------------------------------------- /doc/wteledsk.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | Teledisk File Format 4 | 17 | 19 | 21 | 22 | 23 |

Teledisk File Format

24 | 09/25/01 Created
25 | 11/11/02 Updated

26 | 27 | Historic Overview
28 | Structures and Algorithm
29 | Examples and Problems

30 | 31 | First a little history, Teledisk was a popular disk archival 32 | tool around 1990. My interest comes from the fact that there 33 | are a number of archives, 34 | ie Update and 35 | Metalab, 36 | that contain teledisk 37 | images of DEC RX50 disks for Rainbows, Decmates, and Pro3xxs. 38 | You can find some discussion regarding Teledisk in RX50faq.doc.

40 | 41 | Teledisk was originally released as a shareware program by Sydex. 42 | Its still available as a disk archival tool in a number of MSDOS 43 | program archives. The copy I found, 44 | 45 | version 2.12, contained files dated 46 | 12/12/1990. As near as I can tell Sydex still has a 47 | web site but the Teledisk 48 | product was purchased by New Technologies, Inc in 2000. They have a 49 | 50 | Teledisk web page, but are not even marketing the current version, 51 | 2.22, to the general public. It is definately no longer a shareware 52 | program!

53 | 54 | I sent email to both Sydex and New Technologies, Inc regarding 55 | the status of Teledisk and the archived images. I got a 56 | prompt reply from Miriam W. St.Clair for Sydex, Inc. 57 | She confirms all rights were sold to New Technologies, Inc and 58 | further that "TeleDisk was removed by Sydex from 59 | shareware distribution and support in April 1991". 60 | This is consistent with the comment in the rx50faq.doc above 61 | that teledisk V2.12 was the last shareware version and 62 | it has been discontinued from support.

63 | 64 | 65 | Some people I have talked to still say they can use it reliably, 66 | but my experience is that V2.12, which seems to have been used to create the RX50 67 | images I am interested in, does not appear to reliably recreate these images 68 | on current machines. One theory is that Teledisk has internal timing loops 69 | which depend on machine speed so it might reproduce these images on 70 | a vintage 1990 machine, but not on most of the systems normally 71 | in use today. My original motivation was to be able to recreate disks 72 | from the archived images. I'm the proud owner of several IBM AT and 73 | XT systems with 5.25" disk drives, however I have not been able 74 | to recreate RX50 disks using Teledisk on these machines. I'd be 75 | fascinated if anyone still has a machine running where Teledisk 76 | is reliable with an RX50, please describe it to me! I'm very interested 77 | in how it is different from the machines I've used for testing. Note 78 | that its only the 10 sector RX50 that causes problems, and it may be 79 | I did not follow the formating suggestions in RX50faq.doc. 80 |

81 | 82 | 83 | I am not interested in reverse engineering Teledisk, 84 | I just want to extract the compressed data to a sector by sector 85 | image of the disk with which I can work. My personal solution 86 | to recreating physical disks when I want them has been to use a DEC Rainbow 87 | to write the image back to disk using a real RX50. John Wilson's 88 | PUTR is an MSDOS program 89 | which can write RX50 images to 1.2 Mb 5.25" disk drives in 90 | a modern PC compatible. This is a good alternate approach if 91 | you want RX50s. One can also directly write sectors to 92 | conventional PC disks with the BIOS Int 0x13 however I have 93 | had problems with some machines when attempting to write the 94 | 10 sector disks required for an RX50. If you need help 95 | writing image files back to disk with MSDOS on a PC or Rainbow, 96 | I can make a disk utility program I wrote available. See the contact 97 | info at the end of this document. However I think PUTR is probably 98 | your best bet. If using PUTR with an RX50, mount the physical drive 99 | as /RX50 /FOREIGN and then use COPY /FILE/DEVICE/BINARY with the 100 | source file created by Wteledsk to create the RX50. (see the 101 | examples at the end of this file)

102 | 103 | Since starting this project, I've talked to a couple people who were 104 | interested in extracting the data from Teledisk image files to 105 | disk image files suitable for use with one of the PDP11 emulators. 106 | The code and algorithm presented below allow one to do this. 107 | The wteledsk program creates a physical disk image (sector order), 108 | not a logical image! (again see examples at the eof of this file)

109 | 110 | A year ago there was a significant amount of 111 | control information in the *.td0 files that I just didn't understand. 112 | I recently discovered that Sydex released a validation program. 113 | This appears to be available in a number of places on the net. 114 | There were at least two versions, you want TDCHECK V1.10 which 115 | I found in 116 | tdchk110.zip. 117 | I was able to modify key sections of some sample images and 118 | use the tdcheck error messages to determine the function of each 119 | region of the header data. However I'd still 120 | be pleased to have others review this and maybe pick up things 121 | I've missed. I'll try to point these areas out in the description 122 | below. For example most of the sample *.td0 images I found are 123 | for 512 byte sector RX50 disks. I did eventually find some 1024 byte 124 | sector Akai images which allowed me to determine how the 125 | sector size is encoded; however these have some 'phantom' sectors that 126 | I don't understand! My program seems to correctly decode V2.12 images 127 | of DEC PRO, Rainbow and DecMate disks. It also appears to work with V2.16 images created 128 | for 129 | Akai samplers when I ignore the phantom sectors.
130 | Also of interest is a 131 | Conversion 132 | Utility I found recently, unfortunately the limited documentation is not 133 | in a language I can read. This apparently converts teledisk images 134 | of ZX-Spectrum game disks to an emulator format. 135 | I have had a brief email conversation with 136 | the author, and he was able to put me on the right track regarding 137 | the advanced compression algorithm. 138 |

139 | 140 | 141 | The algorithm below seems to work 142 | for *.td0 images which were created with "Normal" compression. 143 | It turns out the "New Advanced" compression just 144 | overlays a secondary compression algorithm on top of a "normal" 145 | compression file. Sergey Erokhin, the author of tdcvt, 146 | pointed out that the teledisk documentation says it 147 | uses LZSS-Huffman compression. On Sergey's recommendation I 148 | did a search for public domain source code and found 149 | lzhuf.c 150 | which can be modified to decompress 151 | the teledsk images. I've included the source code and an MSDOS 152 | program to convert teledisk files from Advanced back to Normal 153 | compression. Although Wteledsk.exe will decompress on the fly, 154 | I only describe the Normal compression algorithm. I'm not qualified 155 | to explain LZSS 156 | decompression, I just lifted the code!

157 | 158 | In the discussion below I will refer to the following 159 | C structures from the source code, wteledsk.c: 160 |

161 | 162 |

163 | struct file_head {
164 | char sig[3];  // signature "TD", nul terminated string
165 | BYTE  unkwn,  // doesn't seem to be used?
166 |       ver,    // for v2.11 through v2.16 seems to be 0x15
167 |       data1,  // 0x00 (except for akai)
168 |       type,   // see bios drive type 1-4,
169 |       flag,   // among other things can indicate comment field
170 |       data2,  // often 0x00
171 |       sides;  // 0x01 (always with rx50 or 0x02 with most MSDOS disks)
172 | WORD  crc;  //   checksum of 1st 0xA bytes in record
173 | };
174 | 
175 | The first three bytes are the upper case string "TD" if the 176 | file was created using "Normal" compression and lower case 177 | "td" if it was created using "Advanced" compression. The next 178 | byte is a mystery, one can change it and TDCHECK doesn't care. 179 | As indicated above the ver field has been 0x15 in all the samples 180 | I've found. If one reduces this value to anywhere in the 181 | range 0xA - 0x13 TDCHECK says the image was created with 182 | the "Old Advanced" compression which says to me this is a 183 | version field. However it does not map directly to the version 184 | numbers of the Teledisk.exe program. Type is the bios drive 185 | type used to create the image. Flag appears to be a bitmap. 186 | The low order bit(s) appear to map to the physical drive number 187 | used when the image was created. The high bit is set if there 188 | is a comment block. The data1 and data2 fields are a mystery. 189 | Sides appears to be the upper limit for the head field. It 190 | is set to 1 if the disk is single sided or just side one 191 | is checked on a two sided disk. It is set to 2 for a two 192 | sided disk when both sides are check and when only the 2nd 193 | side is checked. The last word is a 16 bit CRC, but I don't 194 | know the formula they actually used. It appears to be a table 195 | driven algorithm. Note that this initial file header always 196 | occurs and is never encoded. However with advanced compression 197 | the remainder of the file is encoded. I see nothing in the 198 | file_head to indicate the number of tracks nor sectors per track. 199 | Apparently these are generated on the fly by the values in the individual 200 | track and sector records. It appears that the Teledisk format allows 201 | a different size (as a power of 128) for each individual sector. 202 | 203 |

204 |

205 | struct com_head {
206 | WORD crc, //  checksum of 8 bytes from &len to end of record
207 |      len;     // length of string data region following date
208 | BYTE yr,mon,day,
209 |      hr,min,sec; // date and time info....
210 | };
211 | 
212 | The structure above immediately follows the main file 213 | header if and only if there is a comment block, ie the 214 | high order bit is set in the flag field. The len field 215 | indicates the number of descriptive string bytes following 216 | this structure. Each of the descriptive strings is NUL 217 | terminated. The remaining six bytes are a time stamp, where 218 | the year is relative to 1900. 219 |

220 | Following this point the file loops through all the sectors 221 | for each track alternating between heads if both sides 222 | are being checked. The following track_rec preceeds 223 | each block of track/head data: 224 |

225 | struct track_rec {
226 | BYTE nsec,  // number of sectors on track
227 |      trk,   // track number, 0 based
228 |      head,  // head number, 0 based
229 |      crc;
230 | };
231 | 
232 | A 16 bit crc is calculated for the first 3 bytes in 233 | each track_rec. However only the low order 8 bits are 234 | saved for validation in the crc field. 235 |

236 | The following sec_rec preceeds each block of sector data. 237 | secsz has been 2 in all RX50 images (512 bytes) and 3 for Akai 238 | (1024) byte sectors. I believe its a power of two which 239 | is then multiplied by 128 to get the sector size. 240 | Normally the sec_rec is the full 9 bytes long, however 241 | when cntrl is 0x10 the record has a different structure 242 | and the last 3 bytes are not included in the file. This requires 243 | one to read this structure in two steps. 244 |

 
245 | struct sec_rec {
246 | BYTE trk,head,sec; // trk,head,sector following dat is for
247 | BYTE secsz,        // bytes in sec = 128 * (2 ** secsz)
248 |      cntrl,
249 |      crc, // if there is sector data, this is low byte of crc for entire sector
250 |           // its NOT crc of 1st 5 bytes, see DSCRC
251 |      unknwn[2],    // please tell me if you have a guess!
252 |      flag;         // controls extra bytes and use
253 | 
254 | }; 
255 | 
256 | I've identified three methods of filling in the sector data:
257 | if sec_rec.flag == 0
258 |    read the full sector directly from the file
259 | else if (sec_rec.cntrl = 0x10)
260 |    the sector is empty, I fill it with zeros but who knows?
261 |    Note only 6 bytes of sec_rec were in the file in this case
262 | else if sec_rec.flag == 1
263 |    read next 4 bytes and treat as a rep_rec
264 |    fill sector with rep_rec.count repeats of rep_rec.pat[]
265 |    I believe this implies count is always half the sector size.
266 | else if sec_rec.flag == 2
267 |    repeat until sector buffer is full:
268 |        read 2 bytes and treat as start of pat_rec
269 |        flag value and count determine action.
270 |        I call this a fragmented read as multiple
271 |        pat_rec reads are required to fill the buffer.
272 | 
273 | struct rep_rec {
274 |      WORD count;
275 |      BYTE pat[2];
276 | };
277 | 
278 | struct pat_rec {
279 | BYTE flag,count,pat[]; //if flag > 0, repeat pat count times
280 | };
281 | 
282 | In a fragmented read pat_rec.flag and pat_rec.count 283 | are always read, and they control the way additional 284 | data is optained from the file and written to the sector 285 | buffer. Since the pat_rec.count is 286 | a BYTE, the maximum value is 0xff. As indicated below if the 287 | flag == 0 only the first two bytes are used and the following count bytes 288 | in the file are raw sector data which should be appended to the current sector buffer. 289 | If flag >= 1 it determines the number of bytes of pat[] to 290 | be read from the file and copied count times to the sector buffer. 291 | To date I've only seen flag values of 0 and 1 from Teledisk V2.12, 292 | however Teledisk V2.16 uses pat_rec.flag as a power of two for the number 293 | of bytes in the pattern, ie 1 => 2 bytes, 2 => 4 bytes etc. 294 |

295 |

296 | The following algorithm seems to create a physical disk image,
297 | its the logic in main() for V2.12: (ignore AKAI V2.16 stuff)
298 | Open the output file in binary mode
299 | Open the input file in binary mode
300 | Read struct file_head
301 |    Optionally validate the sig[] field.
302 | Test file_head.flag, if high bit set there is comment data
303 |    read the com_head
304 |    read com_head.len bytes of descriptive strings
305 |    optionally display this data, its not part of the output image
306 | While not at the end of the file read struct trk_rec
307 |    if trk_rec.nsec == 0xff  STOP  you are done
308 |    for each sector, trk_rec.nsec, 
309 | 	process one sector per algorithm below and write to output
310 | 
311 | 
312 | The following is the logic for the routine do_sector() which
313 | is called to process each sector, one at a time.  It returns
314 | a completely filled in sector buffer to the caller:
315 | 
316 | Read first 6 bytes of struct sec_rec, its variable length
317 | if sec_rec.cntrl == 0x10, alternate 6 byte record
318 |     set sec_rec,flag =1 (fudge it to a full record)
319 | else read last 3 bytes of struct sec_rec from input
320 | At this point I've seen 4 possibilities for the sector data:
321 | if sec_rec.cntrl == 0x10
322 |     there is no sector data in the file, Nul out sector buffer
323 | else if(sec_rec.flag == 0)
324 |     read a full sector directly from input file
325 | else if(sec_rec.flag == 1) 
326 |     read 4 bytes from input, treat as rep_rec
327 | else if(sec_rec.flag == 2)  
328 |     do a fragmented read as described above to fill sector
329 |     buffer.  The basic algorithm for this do_read() routine 
330 |     is shown below:
331 |   set count of bytes in sector, sec_cnt, to 0
332 | 
333 |   BLK_SZ = 128 * (2 ** secsz)
334 |   while sec_cnt < BLK_SZ
335 |     read struct data_rec  
336 |     if data_rec.flag == 0   the data's in the input file
337 | 	append data_rec.count bytes from input to sector buffer
338 | 	increment sec_cnt by # of bytes appended (max is 0xff)
339 |     else if data_rec.flag >= 1 a byte pattern is in input file
340 | 	with length = patlen = 2 * (2 ** data_rec.flag)
341 | 	treat it as a pat_rec and read patlen bytes into pat[]
342 | 	repeat  the following data_rec.count times
343 |             append the patlen pat[] bytes to sector buffer
344 | 	    increment sec_cnt by patlen 
345 | 
346 | 347 | WARNING: since originally writing the section above, I extended the algorithm 348 | to deal with some new wrinkles I found in V2.16 AKAI samples. 349 | These are indicated in the source code, but not above. If you 350 | comment out the line "#define AKAI 1" near the begining of the 351 | program it follows the logic above. Otherwise it attempts to deal 352 | with what seem to be phantom sectors with (sec_rec.sec & 0x60) > 0. 353 | This frequently occurs in the V2.16 images, but it appears that 354 | ignoring this "phantom" data works just fine. Naturally I do not 355 | feel real comfortable with this solution, but it seems to work. 356 | To decode most of the V2.16 images I have looked at one must use the "-s" 357 | command line option to allow skipped sectors which V2.16 seems to do 358 | fairly often. I'm not clear if these are unreadable sectors, or 359 | what. This hasn't been an issue in most of the DEC images I am actually 360 | interested in so I have not persued the issue. 361 | 362 |

363 | 364 | The program I wrote, wteledsk.c V1.01, 365 | is offered as an LHA 366 | archive under the 367 | GNU General Public License. It should compile under 368 | Linux via gcc and MSDOS with one of the Microsoft C compilers. 369 | I encourage people to look at it and improve it. Please give 370 | me some credit if you do, and more importantly let me know if 371 | you make it better! The archive contains this file, the C source 372 | code, and the MSDOS executables for creating raw images from 373 | *.td0 wteledsk files, as well as a tdconv.exe 374 | which converts an ADVANCED compression *.td0 file back to 375 | NORMAL compression.

376 | 377 | The program is conditionally compiled, and there are more conditionals 378 | than I'd like (sorry). It looks for MSDOS 379 | which the Microsoft compilers define. If defined 380 | an MSDOS environment is assumed, otherwise a GNU/Linux environment 381 | is assumed with gcc. I also have a DUMP define, when defined the 382 | header and block dump options are enabled for debugging. 383 | In this case the user must provide and link with their own dump() 384 | routine, see the prototype in the source. There is also a 385 | DISK conditional, which should NOT be defined, which enables 386 | absolute floppy disk routines not provided with this distribution. 387 | The DCRC and DCOMP defines control whether CRC checks and/or 388 | advanced compression are supported. The supplimental source files 389 | tdcrc.c and tdlzhuf.c are provided for those that want to turn 390 | these options on. Finally there is an AKAI define to enable 391 | V2.16 compatiblity. 392 |

393 | 394 | 395 | This version "1.01 AC" of Wteledsk was created with the conditional DUMP 396 | and AKAI defined to enable the debug options that 397 | I used to test the system. Additionally DCOMP and DCRC are 398 | defined so it handles compressed images. You can ignore most of the usage 399 | flags below unless you are having a problem with an image and 400 | want to get more information, see discussion at the end of this file.

401 | 402 |

403 | usage: wteledsk <filename> [-o<outputfile>] [-n#] [-dh] [-db] [-s] [-r]
404 |        -d to dump headers and/or restrutured block data
405 |        -n limit scan to first n sectors
406 |        -o output restructured blocks to a file
407 |        -p display phantom sec_rec.nsec values
408 |        -s warn instead of fatal error on skipped or repeated sectors
409 | 	  this ignores any data in repeated sectors
410 |        -r similar to above, but write repeated sectors to disk
411 | 
412 | The input <filename> is required
413 | Use the -o option to generate an output file
414 |    You can omit it for debugging, or just to test file compatibility.
415 |    Note there is no white space between the "-o" and 
416 | -n limits the number of sectors processed
417 | -dh dumps the header control blocks encountered
418 | -db dumps the sector buffer data after it is restructured
419 |     (the -d options are only available when conditional DUMP defined)
420 | 
421 | 422 |

423 | I'd be pleased to have people contact me 424 | to report success or failure. If you have problems I'm interested 425 | cause you probably have an image I don't. However please DO NOT just email 426 | it to me, I've got an old slow internet connection at the end of 427 | a dirt road! 428 |

429 | Enhancements with Version 1.01 AC
430 | It now correctly handles the case where there is no comment data
431 | It displays the time stamp in the comment field (most of the 432 | time it does this correctly but a couple files come out oddly).
433 | It now pads the sector with a fixed pattern when sec_rec.flag ==1 434 | whereas previously it just skipped over the sector.
435 | It correctly handles the "new" ADVANCED compression. 436 |

437 | Most of the validation done used John Wilson's 438 | PUTR in one 439 | way or another as described below. Most source files mentioned 440 | came from Update. 441 | In general they are also available from 442 | Sunsite 443 | in both *.td0 and logical *.dsk format. I just noticed this, and it would 444 | be a good validation method, but its not the one I used! 445 |

446 | Note the rx50faq.doc at Update and Sunsite contains a fair 447 | amount of discussion regarding interleave optimization based 448 | on the target system. I have NO IDEA how to control this. 449 | It could be that this information is in the two unknwn[] 450 | bytes in the struct sec_rec, however I can't figure it out. 451 | One may end up with non-optimal disks using PUTR as described 452 | below, but they boot! What Wteledisk produces is a raw/physical 453 | sector by sector image of the disk. A number of the test 454 | images from the /PRO directory at Update were for the DEC PRO series and 455 | are in FILES11 format. In PUTR one can use the following mount command 456 | where <file> is the path/name of the output file from Wteledsk:
457 | MOUNT du0: <file> /files11 /nointer

458 | 459 | Once mounted RSX style directory and type commands work. Although it probably 460 | doesn't matter, I believe PUTR determines its an RX50 from the file length. The fact 461 | that I can access the text files on the following reconstructed images suggests 462 | that the images are valid: 463 |

464 | source file       text file viewed via PUTR's type command
465 | f77.td0           du0:[install]install.cmd;1
466 | decusc1.td0       du0:[userfiles]readme.txt;1 
467 | 181.td0           du0:[util]k11hlp.hlp;1
468 | 
469 | Then I dug out some bootable disks, and tried them on
470 | an appropriate target machine.  I created an image file with
471 | Wteledsk, then used PUTR with the following commands:
472 |    mount b: /rx50 /foreign
473 |    format b:
474 |    copy /files/device/binary <image file> b:
475 | 
476 | source of <image file>
477 | cpm80sys.td0      tried to boot on a Decmate III, it knows its
478 | 		  supposed to be a CPM disk, but I had no CPM card.
479 | 		  Not a great test!
480 | wpsystem.td0      booted on Decmate III
481 | dos310b.td0       booted on a Rainbow
482 | 179-1.td0         booted on a PRO 350  (PRO disk maintainence diskette)
483 | vnx2xfer.td0      booted on a PRO 350  (Venix install disk from Venix2.zip)
484 | 
485 | The results above suggest I have a fairly robust decoder.  All the samples
486 | above decode without the -s or -r options.  There are 80 sequential 
487 | tracks each with 10 sequential sectors in the images.  There is also a 
488 | wps?.td0 collection for the PRO at update.  Like the Venix2 distribution 
489 | these are in the ADVANCED compression format, but they all contain skipped 
490 | and/or repeated sectors.  I suspect this means that when they were created 
491 | the source machine was having a hard time reading the target disks and 
492 | tried more than once on several of the tracks.  When my code detects a 
493 | skipped sector it inserts a test message in the image file, 
494 |    "Skip %2d blocks".
495 | Even with the -r option which allows it to overwrite with repeated
496 | sectors these images end up 20 - 30 skipped blocks out of the total 800.
497 | 
498 | I do not understand what wps1.td0 is supposed to be, probably an empty
499 | disk.  wps6.td0 is in DECVMSEXCHNG format and I have no good way
500 | to validate it.  The others can be mounted and read using PUTR, but
501 | casual examination of some of the text files reveals several have
502 | holes in them where a block was skipped.  Using the -r options helps,
503 | but does not eliminate the problem for script.com.  
504 | My guess is these are not usable images, but it could indicate a 
505 | problem with Wteledsk.
506 | 
507 | source file       text file viewed via PUTR's type command
508 | wps2.td0          du0:[001054]script.com;1
509 | wps3.td0          du0:[zzsys]sir.msg;1
510 |                   du0:[zzsys]setup.msg;1
511 | wps4.td0          du0:[zzsys]futl.hlp;1
512 | 
513 | wps5.td0 has a different problem.  The second trk_rec read is 
514 | #1 which is expected, but then the next sec_rec is for track #2
515 | rather than track #1.  Never seen an image like this, and my code
516 | currently aborts on this condition.
517 | 
518 | In closing I give a sample header dump using the "-dh" command
519 | line option from blank.td0.  This was posted on the vmsnet.pdp-11
520 | newsgroup by Kevin McQuiggin on 02/07/1999 as a sample of a blank
521 | RX50 formated for a PDP11.  I present an abridged version of
522 | the initial screen output resulting form "wteledsk blank.td0 -dh":
523 | ------------------------------------------------------
524 | 0000: 54 44 00 57  15 00 02 80  00 01 EE F3              |TD.W........    
525 | 000C: 3F DA 3D 00  61 02 08 08  2D 2F                    |?.=.a...-/      
526 | 
527 | This is DEC blank RX50 for testing Part # BL-N402A-BK
528 | 
529 | 
530 | created Mar 08, 1997  08:45:47
531 | string len 0x3d  start variable 0x53 = 0x3d with 8 NULs
532 | file length 0x2a38 = 10808
533 | 
534 | 
535 | 10 sectors for head 0 physical track  0 (decimal)
536 | 0053: 0A 00 00 88                                        |....            
537 | 0057: 00 00 01 02  00 A8 05 00  01 00 01 E5  E5          |.............   
538 | 0064: 00 00 02 02  00 A8 05 00  01 00 01 E5  E5          |.............   
539 | 0071: 00 00 03 02  00 A8 05 00  01 00 01 E5  E5          |.............   
540 | 007E: 00 00 04 02  00 A8 05 00  01 00 01 E5  E5          |.............   
541 | 008B: 00 00 05 02  00 A8 05 00  01 00 01 E5  E5          |.............   
542 | 0098: 00 00 06 02  00 A8 05 00  01 00 01 E5  E5          |.............   
543 | 00A5: 00 00 07 02  00 A8 05 00  01 00 01 E5  E5          |.............   
544 | 00B2: 00 00 08 02  00 A8 05 00  01 00 01 E5  E5          |.............   
545 | 00BF: 00 00 09 02  00 A8 05 00  01 00 01 E5  E5          |.............   
546 | 00CC: 00 00 0A 02  00 A8 05 00  01 00 01 E5  E5          |.............   
547 | 
548 | 10 sectors for head 0 physical track  1 (decimal)
549 | 00D9: 0A 01 00 01                                        |....            
550 | 00DD: 01 00 01 02  00 A8 05 00  01 00 01 E5  E5          |.............   
551 | 00EA: 01 00 02 02  00 A8 05 00  01 00 01 E5  E5          |.............   
552 | 00F7: 01 00 03 02  00 A8 05 00  01 00 01 E5  E5          |.............   
553 | 
554 | ........ this repeats through all tracks with 10 sectors
555 | per track.  Note tracks are zero based and sectors are
556 | one based in this format.  The 7th and 8th bytes in the 
557 | sec_rec are unknwn[]  = {05 00} for all sectors.  This 
558 | makes me think it has nothing to do with the hardware
559 | sector ids, but what do I know!
560 | 
561 | If an output image is produced from blank.td0 its just
562 | a 409,600 byte file where each byte has the value 0xE5.
563 | 
564 | 
565 | 566 | 567 | -------------------------------------------------------------------------------- /dos/tdconv.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmechnich/wteledsk/1054af69eddb040a7d887cdf67e2e31fb6399661/dos/tdconv.exe -------------------------------------------------------------------------------- /dos/wteledsk.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmechnich/wteledsk/1054af69eddb040a7d887cdf67e2e31fb6399661/dos/wteledsk.exe -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -g -O3 -Wall 2 | SOURCES = wteledsk.c 3 | 4 | ## advanced compression 5 | CFLAGS += -DDCOMP 6 | SOURCES += tdlzhuf.c 7 | 8 | ## CRC check 9 | CFLAGS += -DCRC 10 | SOURCES += tdcrc.c 11 | 12 | wteledsk: $(SOURCES) Makefile 13 | $(CC) $(CFLAGS) -o $@ $(filter %.c,$^) 14 | 15 | clean: 16 | rm -f wteledsk *~ 17 | -------------------------------------------------------------------------------- /src/tdcrc.c: -------------------------------------------------------------------------------- 1 | /* tdcrc.c - a crc routine to mimic tdcheck.exe 2 | 3 | This program is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 2 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | http://www.gnu.org/licenses/gpl.txt 17 | 18 | 19 | 10/19/02 got it working! 20 | Contains conditionals to build a test program 21 | or just use as a module. 22 | Define DOMAIN if *.exe is desired 23 | if not defined get a linkable crc module 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | static unsigned char table[] = { 30 | 0x00,0xa0,0xe1,0x41,0x63,0xc3,0x82,0x22,0xc7,0x67,0x26,0x86,0xa4,0x04,0x45,0xe5, 31 | 0x2f,0x8f,0xce,0x6e,0x4c,0xec,0xad,0x0d,0xe8,0x48,0x09,0xa9,0x8b,0x2b,0x6a,0xca, 32 | 0x5e,0xfe,0xbf,0x1f,0x3d,0x9d,0xdc,0x7c,0x99,0x39,0x78,0xd8,0xfa,0x5a,0x1b,0xbb, 33 | 0x71,0xd1,0x90,0x30,0x12,0xb2,0xf3,0x53,0xb6,0x16,0x57,0xf7,0xd5,0x75,0x34,0x94, 34 | 0xbc,0x1c,0x5d,0xfd,0xdf,0x7f,0x3e,0x9e,0x7b,0xdb,0x9a,0x3a,0x18,0xb8,0xf9,0x59, 35 | 0x93,0x33,0x72,0xd2,0xf0,0x50,0x11,0xb1,0x54,0xf4,0xb5,0x15,0x37,0x97,0xd6,0x76, 36 | 0xe2,0x42,0x03,0xa3,0x81,0x21,0x60,0xc0,0x25,0x85,0xc4,0x64,0x46,0xe6,0xa7,0x07, 37 | 0xcd,0x6d,0x2c,0x8c,0xae,0x0e,0x4f,0xef,0x0a,0xaa,0xeb,0x4b,0x69,0xc9,0x88,0x28, 38 | 39 | 0xd8,0x78,0x39,0x99,0xbb,0x1b,0x5a,0xfa,0x1f,0xbf,0xfe,0x5e,0x7c,0xdc,0x9d,0x3d, 40 | 0xf7,0x57,0x16,0xb6,0x94,0x34,0x75,0xd5,0x30,0x90,0xd1,0x71,0x53,0xf3,0xb2,0x12, 41 | 0x86,0x26,0x67,0xc7,0xe5,0x45,0x04,0xa4,0x41,0xe1,0xa0,0x00,0x22,0x82,0xc3,0x63, 42 | 0xa9,0x09,0x48,0xe8,0xca,0x6a,0x2b,0x8b,0x6e,0xce,0x8f,0x2f,0x0d,0xad,0xec,0x4c, 43 | 0x64,0xc4,0x85,0x25,0x07,0xa7,0xe6,0x46,0xa3,0x03,0x42,0xe2,0xc0,0x60,0x21,0x81, 44 | 0x4b,0xeb,0xaa,0x0a,0x28,0x88,0xc9,0x69,0x8c,0x2c,0x6d,0xcd,0xef,0x4f,0x0e,0xae, 45 | 0x3a,0x9a,0xdb,0x7b,0x59,0xf9,0xb8,0x18,0xfd,0x5d,0x1c,0xbc,0x9e,0x3e,0x7f,0xdf, 46 | 0x15,0xb5,0xf4,0x54,0x76,0xd6,0x97,0x37,0xd2,0x72,0x33,0x93,0xb1,0x11,0x50,0xf0, 47 | 48 | 0x00,0x97,0xb9,0x2e,0xe5,0x72,0x5c,0xcb,0xca,0x5d,0x73,0xe4,0x2f,0xb8,0x96,0x01, 49 | 0x03,0x94,0xba,0x2d,0xe6,0x71,0x5f,0xc8,0xc9,0x5e,0x70,0xe7,0x2c,0xbb,0x95,0x02, 50 | 0x06,0x91,0xbf,0x28,0xe3,0x74,0x5a,0xcd,0xcc,0x5b,0x75,0xe2,0x29,0xbe,0x90,0x07, 51 | 0x05,0x92,0xbc,0x2b,0xe0,0x77,0x59,0xce,0xcf,0x58,0x76,0xe1,0x2a,0xbd,0x93,0x04, 52 | 0x0c,0x9b,0xb5,0x22,0xe9,0x7e,0x50,0xc7,0xc6,0x51,0x7f,0xe8,0x23,0xb4,0x9a,0x0d, 53 | 0x0f,0x98,0xb6,0x21,0xea,0x7d,0x53,0xc4,0xc5,0x52,0x7c,0xeb,0x20,0xb7,0x99,0x0e, 54 | 0x0a,0x9d,0xb3,0x24,0xef,0x78,0x56,0xc1,0xc0,0x57,0x79,0xee,0x25,0xb2,0x9c,0x0b, 55 | 0x09,0x9e,0xb0,0x27,0xec,0x7b,0x55,0xc2,0xc3,0x54,0x7a,0xed,0x26,0xb1,0x9f,0x08, 56 | 57 | 0x8f,0x18,0x36,0xa1,0x6a,0xfd,0xd3,0x44,0x45,0xd2,0xfc,0x6b,0xa0,0x37,0x19,0x8e, 58 | 0x8c,0x1b,0x35,0xa2,0x69,0xfe,0xd0,0x47,0x46,0xd1,0xff,0x68,0xa3,0x34,0x1a,0x8d, 59 | 0x89,0x1e,0x30,0xa7,0x6c,0xfb,0xd5,0x42,0x43,0xd4,0xfa,0x6d,0xa6,0x31,0x1f,0x88, 60 | 0x8a,0x1d,0x33,0xa4,0x6f,0xf8,0xd6,0x41,0x40,0xd7,0xf9,0x6e,0xa5,0x32,0x1c,0x8b, 61 | 0x83,0x14,0x3a,0xad,0x66,0xf1,0xdf,0x48,0x49,0xde,0xf0,0x67,0xac,0x3b,0x15,0x82, 62 | 0x80,0x17,0x39,0xae,0x65,0xf2,0xdc,0x4b,0x4a,0xdd,0xf3,0x64,0xaf,0x38,0x16,0x81, 63 | 0x85,0x12,0x3c,0xab,0x60,0xf7,0xd9,0x4e,0x4f,0xd8,0xf6,0x61,0xaa,0x3d,0x13,0x84, 64 | 0x86,0x11,0x3f,0xa8,0x63,0xf4,0xda,0x4d,0x4c,0xdb,0xf5,0x62,0xa9,0x3e,0x10,0x87 65 | }; 66 | 67 | 68 | // basically same logic as below, but doesn't initialize crc so can append 69 | void updt_crc(unsigned short *crc,unsigned char *b, unsigned short len) 70 | { 71 | unsigned char tmp; 72 | while(len-- != 0) 73 | { 74 | tmp = *b ^ (*crc >> 8); 75 | *crc = ((table[tmp] ^ (*crc & 0xff)) << 8) + table[tmp+0x100]; 76 | b++; 77 | } 78 | 79 | } 80 | 81 | // let unsigned char *b point to start of buffer 82 | // this initializes crc, then does updt_crc logic 83 | unsigned short do_crc(unsigned char *b, unsigned short len) 84 | { 85 | unsigned char tmp; 86 | unsigned short i,crc = 0; 87 | for(i=0;i> 8); 90 | crc = ((table[tmp] ^ (crc & 0xff)) << 8) + table[tmp+0x100]; 91 | } 92 | return(crc); 93 | } 94 | 95 | #ifdef DOMAIN 96 | #ifndef MSDOS 97 | #define O_BINARY 0 // not defined for Linux gcc 98 | #define strnicmp strncasecmp 99 | #endif 100 | 101 | #define BLKSZ 256 102 | 103 | main(int argc,char *argv[]) 104 | { int rd,fp=EOF; 105 | unsigned char buf[BLKSZ]; 106 | unsigned short crc=0; 107 | long start = -1,end = -1,off = 0,bytes = 0; 108 | if(argc < 2) 109 | printf("\nusage: tdcrc [-s#] [-e#]"); 110 | else if((fp = open(argv[1],O_RDONLY|O_BINARY)) == EOF) 111 | printf("\nfailed to open %s",argv[1]); 112 | else 113 | { 114 | for(rd=2;rd < argc;rd++) 115 | { 116 | if(strnicmp(argv[rd],"-s",2) == 0 && 117 | sscanf(argv[rd]+2,"%lx",&start) == 1) 118 | { 119 | printf("\nstart offset 0x%lx",start); 120 | if(lseek(fp,start,SEEK_SET) != start) 121 | { 122 | printf(" -- seek failed!"); 123 | start = -1; 124 | } 125 | else 126 | off = start; 127 | } 128 | if(strnicmp(argv[rd],"-e",2) == 0 && 129 | sscanf(argv[rd]+2,"%lx",&end) == 1) 130 | printf("\nend offset 0x%lx",end); 131 | } 132 | while(1) 133 | { 134 | rd = read(fp,buf,BLKSZ); 135 | if(end > 0 && off + rd > end) 136 | rd = end - off; 137 | if(rd <= 0) 138 | break; 139 | updt_crc(&crc,buf,rd); 140 | bytes += rd; 141 | off += rd; 142 | } 143 | } 144 | if(fp != EOF) 145 | { 146 | printf("\nfile crc = 0x%x = %u",crc,crc); 147 | printf("\nread %d bytes",bytes); 148 | close(fp); 149 | } 150 | putchar('\n'); 151 | 152 | } 153 | 154 | #endif 155 | 156 | -------------------------------------------------------------------------------- /src/tdlzhuf.c: -------------------------------------------------------------------------------- 1 | /* tdlzhuf.c module to perform lzss-huffman decompression 2 | as is used in teledisk.exe 3 | Conditionally compiled main to convert *.td0 advanced 4 | compression file back normal compression file. 5 | derived from lzhuf.c 1.0 per below 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | http://www.gnu.org/licenses/gpl.txt 21 | 22 | 23 | 11/10/02 this was working with struct tdlzhuf * passed to 24 | both Decode() and init_Decode() as first arg. Save this as 25 | tdlzhuf1.c and then make this structure locally static and try 26 | to switch to unsigned shorts where it matters so works in linux. 27 | 28 | Started with program below: 29 | * LZHUF.C English version 1.0 30 | * Based on Japanese version 29-NOV-1988 31 | * LZSS coded by Haruhiko OKUMURA 32 | * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI 33 | * Edited and translated to English by Kenji RIKITAKE 34 | 35 | In summary changes by WTK: 36 | wrote a new conditionally compiled main() 37 | remove Encode() modules and arrays 38 | make remaing arrays and variables static to hide from external modules 39 | add struct tdlzhuf to provide state retension between calls to Decode() 40 | change from fgetc(FILE *) to read(int fp) so read 41 | a block at a time into an input buffer. Now the 42 | Decode() routine can be called instead of read() 43 | by a user, ie from wteledisk.c 44 | change size of data elements for Linux, 45 | int -> short 46 | unsigned int -> unsigned short 47 | */ 48 | 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #ifndef msdos 55 | #include 56 | #endif 57 | 58 | /* WTK adds top level control structure to give Decode() 59 | a memory between calls 60 | */ 61 | #define BUFSZ 512 // new input buffer 62 | 63 | static struct tdlzhuf { 64 | int fp; // source file handle, opened by caller 65 | // next four variables were local in original main() 66 | // we need to save these values between calls to Decode() 67 | unsigned short r, 68 | bufcnt,bufndx,bufpos, // string buffer 69 | // the following to allow block reads from input in next_word() 70 | ibufcnt,ibufndx; // input buffer counters 71 | unsigned char inbuf[BUFSZ]; // input buffer 72 | } tdctl; 73 | 74 | 75 | 76 | 77 | /* LZSS Parameters */ 78 | 79 | #define N 4096 /* Size of string buffer */ 80 | #define F 60 /* Size of look-ahead buffer */ 81 | #define THRESHOLD 2 82 | #define NIL N /* End of tree's node */ 83 | 84 | static unsigned char 85 | text_buf[N + F - 1]; 86 | static short match_position, match_length, 87 | lson[N + 1], rson[N + 257], dad[N + 1]; 88 | 89 | void InitTree(void) /* Initializing tree */ 90 | { 91 | int i; 92 | 93 | for (i = N + 1; i <= N + 256; i++) 94 | rson[i] = NIL; /* root */ 95 | for (i = 0; i < N; i++) 96 | dad[i] = NIL; /* node */ 97 | } 98 | 99 | void InsertNode(int r) /* Inserting node to the tree */ 100 | { 101 | int i, p, cmp; 102 | unsigned char *key; 103 | unsigned c; 104 | 105 | cmp = 1; 106 | key = &text_buf[r]; 107 | p = N + 1 + key[0]; 108 | rson[r] = lson[r] = NIL; 109 | match_length = 0; 110 | for ( ; ; ) { 111 | if (cmp >= 0) { 112 | if (rson[p] != NIL) 113 | p = rson[p]; 114 | else { 115 | rson[p] = r; 116 | dad[r] = p; 117 | return; 118 | } 119 | } else { 120 | if (lson[p] != NIL) 121 | p = lson[p]; 122 | else { 123 | lson[p] = r; 124 | dad[r] = p; 125 | return; 126 | } 127 | } 128 | for (i = 1; i < F; i++) 129 | if ((cmp = key[i] - text_buf[p + i]) != 0) 130 | break; 131 | if (i > THRESHOLD) { 132 | if (i > match_length) { 133 | match_position = ((r - p) & (N - 1)) - 1; 134 | if ((match_length = i) >= F) 135 | break; 136 | } 137 | if (i == match_length) { 138 | if ((c = ((r - p) & (N - 1)) - 1) < match_position) { 139 | match_position = c; 140 | } 141 | } 142 | } 143 | } 144 | dad[r] = dad[p]; 145 | lson[r] = lson[p]; 146 | rson[r] = rson[p]; 147 | dad[lson[p]] = r; 148 | dad[rson[p]] = r; 149 | if (rson[dad[p]] == p) 150 | rson[dad[p]] = r; 151 | else 152 | lson[dad[p]] = r; 153 | dad[p] = NIL; /* remove p */ 154 | } 155 | 156 | void DeleteNode(int p) /* Deleting node from the tree */ 157 | { 158 | int q; 159 | 160 | if (dad[p] == NIL) 161 | return; /* unregistered */ 162 | if (rson[p] == NIL) 163 | q = lson[p]; 164 | else 165 | if (lson[p] == NIL) 166 | q = rson[p]; 167 | else { 168 | q = lson[p]; 169 | if (rson[q] != NIL) { 170 | do { 171 | q = rson[q]; 172 | } while (rson[q] != NIL); 173 | rson[dad[q]] = lson[q]; 174 | dad[lson[q]] = dad[q]; 175 | lson[q] = lson[p]; 176 | dad[lson[p]] = q; 177 | } 178 | rson[q] = rson[p]; 179 | dad[rson[p]] = q; 180 | } 181 | dad[q] = dad[p]; 182 | if (rson[dad[p]] == p) 183 | rson[dad[p]] = q; 184 | else 185 | lson[dad[p]] = q; 186 | dad[p] = NIL; 187 | } 188 | 189 | /* Huffman coding parameters */ 190 | 191 | #define N_CHAR (256 - THRESHOLD + F) 192 | /* character code (= 0..N_CHAR-1) */ 193 | #define T (N_CHAR * 2 - 1) /* Size of table */ 194 | #define R (T - 1) /* root position */ 195 | #define MAX_FREQ 0x8000 196 | /* update when cumulative frequency */ 197 | /* reaches to this value */ 198 | 199 | typedef unsigned char uchar; 200 | 201 | /* 202 | * Tables for encoding/decoding upper 6 bits of 203 | * sliding dictionary pointer 204 | */ 205 | 206 | /* decoder table */ 207 | static uchar d_code[256] = { 208 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 209 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 210 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 211 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 212 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 213 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 214 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 215 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 216 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 217 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 218 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 219 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 220 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 221 | 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 222 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 223 | 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 224 | 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 225 | 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 226 | 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 227 | 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 228 | 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 229 | 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 230 | 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 231 | 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 232 | 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, 233 | 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, 234 | 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 235 | 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 236 | 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 237 | 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 238 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 239 | 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 240 | }; 241 | 242 | static uchar d_len[256] = { 243 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 244 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 245 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 246 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 247 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 248 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 249 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 250 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 251 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 252 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 253 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 254 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 255 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 256 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 257 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 258 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 259 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 260 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 261 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 262 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 263 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 264 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 265 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 266 | 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 267 | 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 268 | 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 269 | 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 270 | 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 271 | 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 272 | 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 273 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 274 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 275 | }; 276 | 277 | static unsigned short freq[T + 1]; /* cumulative freq table */ 278 | 279 | /* 280 | * pointing parent nodes. 281 | * area [T..(T + N_CHAR - 1)] are pointers for leaves 282 | */ 283 | short prnt[T + N_CHAR]; 284 | 285 | /* pointing children nodes (son[], son[] + 1)*/ 286 | short son[T]; 287 | 288 | static unsigned short getbuf = 0; 289 | static uchar getlen = 0; 290 | 291 | /* this is old code duplicated in GetBit() and GetByte() 292 | while (getlen <= 8) { 293 | if ((i = getc(infile)) < 0) i = 0; 294 | getbuf |= i << (8 - getlen); 295 | getlen += 8; 296 | } 297 | 298 | replace this with next_word() routine that handles block read 299 | */ 300 | int next_word(int fp) 301 | { 302 | if(tdctl.ibufndx >= tdctl.ibufcnt) 303 | { 304 | tdctl.ibufndx = 0; 305 | tdctl.ibufcnt = read(fp,tdctl.inbuf,BUFSZ); 306 | if(tdctl.ibufcnt <= 0) 307 | return(-1); 308 | } 309 | while (getlen <= 8) { // typically reads a word at a time 310 | getbuf |= tdctl.inbuf[tdctl.ibufndx++] << (8 - getlen); 311 | getlen += 8; 312 | } 313 | return(0); 314 | } 315 | 316 | 317 | int GetBit(int fp) /* get one bit */ 318 | { 319 | short i; 320 | if(next_word(fp) < 0) 321 | return(-1); 322 | i = getbuf; 323 | getbuf <<= 1; 324 | getlen--; 325 | if(i < 0) 326 | return(1); 327 | else 328 | return(0); 329 | } 330 | 331 | int GetByte(int fp) /* get a byte */ 332 | { 333 | unsigned short i; 334 | if(next_word(fp) != 0) 335 | return(-1); 336 | i = getbuf; 337 | getbuf <<= 8; 338 | getlen -= 8; 339 | i = i >> 8; 340 | return((int) i); 341 | } 342 | 343 | 344 | 345 | /* initialize freq tree */ 346 | 347 | void StartHuff() 348 | { 349 | int i, j; 350 | 351 | for (i = 0; i < N_CHAR; i++) { 352 | freq[i] = 1; 353 | son[i] = i + T; 354 | prnt[i + T] = i; 355 | } 356 | i = 0; j = N_CHAR; 357 | while (j <= R) { 358 | freq[j] = freq[i] + freq[i + 1]; 359 | son[j] = i; 360 | prnt[i] = prnt[i + 1] = j; 361 | i += 2; j++; 362 | } 363 | freq[T] = 0xffff; 364 | prnt[R] = 0; 365 | } 366 | 367 | 368 | /* reconstruct freq tree */ 369 | 370 | void reconst() 371 | { 372 | short i, j, k; 373 | unsigned short f, l; 374 | 375 | /* halven cumulative freq for leaf nodes */ 376 | j = 0; 377 | for (i = 0; i < T; i++) { 378 | if (son[i] >= T) { 379 | freq[j] = (freq[i] + 1) / 2; 380 | son[j] = son[i]; 381 | j++; 382 | } 383 | } 384 | /* make a tree : first, connect children nodes */ 385 | for (i = 0, j = N_CHAR; j < T; i += 2, j++) { 386 | k = i + 1; 387 | f = freq[j] = freq[i] + freq[k]; 388 | for (k = j - 1; f < freq[k]; k--); 389 | k++; 390 | l = (j - k) * 2; 391 | 392 | /* movmem() is Turbo-C dependent 393 | rewritten to memmove() by Kenji */ 394 | 395 | /* movmem(&freq[k], &freq[k + 1], l); */ 396 | (void)memmove(&freq[k + 1], &freq[k], l); 397 | freq[k] = f; 398 | /* movmem(&son[k], &son[k + 1], l); */ 399 | (void)memmove(&son[k + 1], &son[k], l); 400 | son[k] = i; 401 | } 402 | /* connect parent nodes */ 403 | for (i = 0; i < T; i++) { 404 | if ((k = son[i]) >= T) { 405 | prnt[k] = i; 406 | } else { 407 | prnt[k] = prnt[k + 1] = i; 408 | } 409 | } 410 | } 411 | 412 | 413 | /* update freq tree */ 414 | 415 | void update(int c) 416 | { 417 | int i, j, k, l; 418 | 419 | if (freq[R] == MAX_FREQ) { 420 | reconst(); 421 | } 422 | c = prnt[c + T]; 423 | do { 424 | k = ++freq[c]; 425 | 426 | /* swap nodes to keep the tree freq-ordered */ 427 | if (k > freq[l = c + 1]) { 428 | while (k > freq[++l]); 429 | l--; 430 | freq[c] = freq[l]; 431 | freq[l] = k; 432 | 433 | i = son[c]; 434 | prnt[i] = l; 435 | if (i < T) prnt[i + 1] = l; 436 | 437 | j = son[l]; 438 | son[l] = i; 439 | 440 | prnt[j] = c; 441 | if (j < T) prnt[j + 1] = c; 442 | son[c] = j; 443 | 444 | c = l; 445 | } 446 | } while ((c = prnt[c]) != 0); /* do it until reaching the root */ 447 | } 448 | 449 | 450 | short DecodeChar(int fp) 451 | { 452 | int ret; 453 | unsigned short c; 454 | 455 | c = son[R]; 456 | 457 | /* 458 | * start searching tree from the root to leaves. 459 | * choose node #(son[]) if input bit == 0 460 | * else choose #(son[]+1) (input bit == 1) 461 | */ 462 | while (c < T) { 463 | if((ret = GetBit(fp)) < 0) 464 | return(-1); 465 | c += (unsigned) ret; 466 | c = son[c]; 467 | } 468 | c -= T; 469 | update(c); 470 | return c; 471 | } 472 | 473 | short DecodePosition(int fp) 474 | { 475 | short bit; 476 | unsigned short i, j, c; 477 | 478 | /* decode upper 6 bits from given table */ 479 | if((bit=GetByte(fp)) < 0) 480 | return(-1); 481 | i = (unsigned short) bit; 482 | c = (unsigned short)d_code[i] << 6; 483 | j = d_len[i]; 484 | 485 | /* input lower 6 bits directly */ 486 | j -= 2; 487 | while (j--) { 488 | if((bit = GetBit(fp)) < 0) 489 | return(-1); 490 | i = (i << 1) + bit; 491 | } 492 | return(c | (i & 0x3f)); 493 | } 494 | 495 | /* DeCompression 496 | 497 | split out initialization code to init_Decode() 498 | 499 | */ 500 | 501 | void init_Decode(int fp) 502 | { 503 | int i; 504 | tdctl.ibufcnt= tdctl.ibufndx = 0; // input buffer is empty 505 | tdctl.bufcnt = 0; 506 | StartHuff(); 507 | for (i = 0; i < N - F; i++) 508 | text_buf[i] = ' '; 509 | tdctl.r = N - F; 510 | tdctl.fp = fp; 511 | } 512 | 513 | 514 | int Decode(unsigned char *buf, int len) /* Decoding/Uncompressing */ 515 | { 516 | short c,pos; 517 | int count; // was an unsigned long, seems unnecessary 518 | for (count = 0; count < len; ) { 519 | if(tdctl.bufcnt == 0) { 520 | if((c = DecodeChar(tdctl.fp)) < 0) 521 | return(count); // fatal error 522 | if (c < 256) { 523 | *(buf++) = c; 524 | text_buf[tdctl.r++] = c; 525 | tdctl.r &= (N - 1); 526 | count++; 527 | } 528 | else { 529 | if((pos = DecodePosition(tdctl.fp)) < 0) 530 | return(count); // fatal error 531 | tdctl.bufpos = (tdctl.r - pos - 1) & (N - 1); 532 | tdctl.bufcnt = c - 255 + THRESHOLD; 533 | tdctl.bufndx = 0; 534 | } 535 | } 536 | else { // still chars from last string 537 | while( tdctl.bufndx < tdctl.bufcnt && count < len ) { 538 | c = text_buf[(tdctl.bufpos + tdctl.bufndx) & (N - 1)]; 539 | *(buf++) = c; 540 | tdctl.bufndx++; 541 | text_buf[tdctl.r++] = c; 542 | tdctl.r &= (N - 1); 543 | count++; 544 | } 545 | // reset bufcnt after copy string from text_buf[] 546 | if(tdctl.bufndx >= tdctl.bufcnt) 547 | tdctl.bufndx = tdctl.bufcnt = 0; 548 | } 549 | } 550 | return(count); // count == len, success 551 | } 552 | 553 | #ifdef DOFILE 554 | #include 555 | #include 556 | #include 557 | #ifndef MSDOS 558 | #define O_BINARY 0 559 | #endif 560 | unsigned char obuf[BUFSZ]; 561 | 562 | main(int argc, char *argv[]) 563 | { 564 | unsigned char *ptr,head[12],chead[10]; 565 | int fp,fo,rd; 566 | long off=0; 567 | unsigned short crc=0; 568 | 569 | 570 | if(argc < 3) 571 | { 572 | printf("\nusage: tdconv "); 573 | exit(0); 574 | } 575 | else if((fo = open(argv[2],O_RDWR|O_BINARY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE)) 576 | == EOF) 577 | { 578 | printf("\nfailed to open output file %s",argv[2]); 579 | exit(1); 580 | } 581 | else if((fp = open(argv[1],O_RDONLY|O_BINARY)) == EOF || 582 | read(fp,head,sizeof(head)) != sizeof(head)) 583 | printf("\nfailed to open %s",argv[1]); 584 | else if(strncmp(head,"td",2) != 0) 585 | printf("\nthis is not a compressed teledisk image"); 586 | else 587 | { 588 | off = sizeof(head); 589 | init_Decode(fp); 590 | 591 | head[0] = 'T'; 592 | head[1] = 'D'; // make it a normal compression image! 593 | // but now need to fix crc 594 | crc = do_crc(head,10); 595 | *((unsigned short *)head +5) = crc; 596 | if(write(fo,head,sizeof(head)) != sizeof(head)) 597 | printf("\nerror writing header"); 598 | else 599 | do 600 | { 601 | if((rd = Decode(obuf,BUFSZ)) > 0) 602 | write(fo,obuf,rd); 603 | off += rd; 604 | } while(rd == BUFSZ); 605 | } 606 | printf("\nwrote %ld bytes to output file\n",off); 607 | } 608 | 609 | #endif 610 | 611 | -------------------------------------------------------------------------------- /src/wteledsk.c: -------------------------------------------------------------------------------- 1 | /* wteledsk.c - teledisk *.td0 file decoding 2 | 3 | This program is free software; you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation; either version 2 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | http://www.gnu.org/licenses/gpl.txt 17 | 18 | see wteldskc.c for development history notes 19 | see wteledsk.htm for a summary of key structures and algorithm 20 | 21 | Under MSDOS file redirection causes a run time stack overflow 22 | unless a large stack size is forced with /ST:4096 23 | 24 | Define DCOMP if advanced decompression handling required and 25 | link with tdlzhuf.obj 26 | 27 | Define DCRC if crc testing desired and link with tdcrc.obj 28 | 29 | In code below both AKAI and DUMP are defined, for your application 30 | you may choose to comment these out. With DUMP defined you need 31 | to supply a dump module (see protype below define). 32 | 33 | There is also a DISK define which I have used to recreate 34 | physical disks with the PC's low level bios routines. This 35 | code is not part of the general distribution and DISK should 36 | not be defined. 37 | 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include // define memset 45 | #ifdef MSDOS 46 | #include // for lseek 47 | #else 48 | #include 49 | #ifndef O_BINARY 50 | #define O_BINARY 0 // not defined for Linux gcc 51 | #endif 52 | #define strnicmp strncasecmp 53 | #endif 54 | #include 55 | #include /* for open() defines */ 56 | 57 | typedef unsigned char BYTE; 58 | typedef unsigned short WORD; 59 | 60 | // below are return defines from do_sector() so main knows whats going on 61 | // size of block is returned in ctl! 62 | #define AKAI_SEC 100 // special return flag for unnumbered AKAI sector 63 | #define AKAI_END 101 // termination for V2.16 phantom last track 64 | #define SKIP_MASK 0x200 // bit 9 set if skipping a record, # skipped in low byte 65 | #define REPT_MASK 0x400 // set bit 10 if repeating a sector, sec # in low byte 66 | 67 | #define AKAI 1 // defining this makes it V2.16 compatible 68 | // not required for V2.12 ie the DEC images 69 | #define BLKSZ 2048 70 | /* 2048 is good for lots of stuff, dynamically allocated in main 71 | average size of AKAI is 1024, DEC RX50 512 72 | */ 73 | 74 | 75 | #define SHSZ 6 // sector header size, this is base size, its variable 76 | 77 | //#define DUMP 1 // enable dump options, if defined you must supply 78 | // your own dump routine per the prototype below 79 | 80 | extern int dump(FILE *fp,unsigned char *buf,unsigned len,unsigned adr); 81 | 82 | // force big stack was getting overflow if redirected output 83 | 84 | unsigned adr; // global address for dump() calls, ultimately remove 85 | 86 | #pragma pack(1) // BYTE pack structures in MSC 87 | 88 | 89 | #define COMMENT 0x80 90 | 91 | struct file_head { 92 | char sig[3]; // signature "TD", nul terminated string 93 | BYTE unkwn, // doesn't seem to be used? 94 | ver, // for v2.11 through v2.16 seems to be 0x15 95 | data1, // 0x00 (except for akai) 96 | type, // see bios drive type 1-4, 97 | flag, // among other things can indicate comment field 98 | data2, // often 0x00 99 | sides; // 0x01 (always with rx50 or 0x02 with most MSDOS disks) 100 | WORD crc; // checksum of 1st 0xA bytes in record 101 | }; 102 | /* 103 | in most data1 = 0, but in akai its 0x02 104 | flag value above has been 0x80, 0x81, and 0 105 | see COMMENT define above, bitmaped to comment header below included 106 | looks like low order bits are physical drive number used 107 | if sides above is right, its odd, did each side separately on 108 | 2 sided MSDOS disk, value was 0x01 for side 1 only and 0x02 109 | for side two only and for both sides. 110 | */ 111 | 112 | struct com_head { 113 | WORD crc, // checksum of 8 bytes from &len to end of record 114 | len; // length of string data region following date 115 | BYTE yr,mon,day, 116 | hr,min,sec; // date and time info.... 117 | }; 118 | /* note com_head.len only makes sense as a byte, 119 | but high byte of word has been 0.... 120 | year won't wrap into there until 21xx 121 | 122 | track_rec.crc below calculates crc for 1st three bytes 123 | of record, but only used the low byte of calculated value 124 | for comparison. 125 | */ 126 | 127 | struct track_rec { 128 | BYTE nsec,trk,head,crc; 129 | }; 130 | 131 | struct sec_rec { 132 | BYTE trk,head,sec; 133 | BYTE secsz, // sector size as power of 2 = 128 * (2 ** secsz) 134 | cntrl, // see note below, 0x10 forces a truncated record 135 | crc, // if there is sector data, this is low byte of crc for entire sector 136 | // its NOT crc of 1st 5 bytes, see DSCRC 137 | unknwn[2], 138 | flag; // controls extra bytes and use 139 | 140 | }; /* 141 | flag value determins processing. see notes in do_sector() 142 | secsz has always been 2 for 512 byte sector images, 143 | however secsz = 3 for 1024 byte sector AKAI images. 144 | apparently sectors size = 128 * (2**secsz) 145 | cntrl often 0, but if 0x10 then record only 6 bytes long 146 | if clear its the full 9 byte record and there is extra data 147 | in unknwn[] I currently ignore, may be initializtion info? 148 | */ 149 | 150 | 151 | struct pat_rec { 152 | BYTE flag,count,pat[16]; // if flag > 0, repeat pat[] count times 153 | }; 154 | /* my V2.14 Teledisk only uses flag == 1 for RX50, ie 2 bytes of repeated data 155 | but AKAI records, V2.16, seem to use at least 2 - 4 to create pattern sizes 156 | which are a power of 2, ie 2**flag, see do_read() 157 | 158 | The case where flag = 0 is different, in this case count is 159 | the number of bytes <= 0xff to read from file to disk 160 | */ 161 | 162 | struct rep_rec { 163 | WORD count; 164 | BYTE pat[2]; 165 | }; 166 | /* this record only seems to occur once after sec_rec in the case 167 | that sec_rec.flag is valid and equal to 1 168 | I suspect count is redundant, that pat[] always repeated for entire 169 | sector? 170 | */ 171 | 172 | 173 | // control flags: values for globa ctl setup in main() 174 | #define DHEAD 0x8 175 | #define DBLOCK 0x10 176 | #define DCNT 0x20 // limit range of dump 177 | #define DWRITE 0x40 // write to disk sectors 178 | #define WARN 0x80 // just a warning if skip sectors 179 | #define REPEAT 0x100 // flag to write repeated sectors 180 | #define PHANTOM 0x200 // flag to display phantom sec_rec.nsec value 181 | #define BLKSZMSK 0x7 // low order 3 bits, only expect to use 2 of them 182 | 183 | 184 | int block_size(unsigned ctl) 185 | { 186 | int j,sz=128; 187 | j = ctl & BLKSZMSK; 188 | while(j-- > 0) 189 | sz *=2; 190 | return(sz); 191 | } 192 | 193 | unsigned char docomp = 0; // global flag set to 1 if advanced compression 194 | 195 | #ifdef DCOMP 196 | extern int Decode(unsigned char *buf,int len); 197 | extern void init_Decode(int fp); 198 | #else 199 | // dummy decompression to satisfy linker 200 | int Decode(unsigned char *buf,int len) 201 | { 202 | printf("\nfatal error decompression not supported in this version\n"); 203 | exit(-1); 204 | } 205 | #endif 206 | 207 | #ifdef DCRC 208 | extern unsigned short do_crc(unsigned char *b, unsigned short len); 209 | extern void updt_crc(unsigned short *crc,unsigned char *b, unsigned short len); 210 | #endif 211 | 212 | int cread(int fp,void *buf,int len) 213 | { 214 | int ret; 215 | if(docomp) 216 | { 217 | // see init_Decode() called from main which sets file handle 218 | ret = Decode(buf,len); 219 | } 220 | else 221 | ret = read(fp,buf,len); 222 | return(ret); 223 | } 224 | 225 | #ifdef DCOMP 226 | #define read cread // redefine read 227 | #endif 228 | 229 | // append data to sbuf 230 | int do_read( 231 | int fd, 232 | int *off, // caller sets to 0, update as append data 233 | BYTE *sbuf, // sector buffer 234 | unsigned ctl) 235 | { 236 | char buf[20]; 237 | struct pat_rec *prec = (struct pat_rec *)buf; 238 | 239 | int suc=0,i=0,j=0,npat=0,rd=0,blksz=0; 240 | blksz = block_size(ctl); 241 | while(suc == 0 && *off < blksz) 242 | { 243 | if((rd = read(fd,buf,2)) != 2) 244 | suc = 1; 245 | else if((i=buf[0]) > 0 && i < 5) // fill with pattern 246 | { 247 | npat = 2; 248 | while(i-- > 1) 249 | npat *=2; // bytes to repeat is a power of 2 250 | if((i = read(fd,&buf[rd],npat)) != npat) 251 | suc = 1; 252 | else 253 | { 254 | for(i=0; i < prec->count;i++) // times to repeat pattern 255 | { 256 | for(j=0;jpat[j]; 258 | } 259 | rd += npat; 260 | } 261 | } 262 | else if(buf[0] == 0) 263 | { // in this case count is # chars to read from file <= 0xff 264 | if((i = read(fd,sbuf+ *off,(int)prec->count)) != prec->count) 265 | suc = 1; 266 | else 267 | { 268 | *off += i; 269 | } 270 | } 271 | else 272 | { 273 | suc = 2; 274 | printf("\nunknwn flag in do_read"); 275 | } 276 | #ifdef DUMP 277 | if(ctl & DHEAD) 278 | dump(stdout,buf,rd,adr); 279 | #endif 280 | adr += rd; 281 | if(buf[0] == 0) // add this in after dump so just dump header 282 | adr += i; // update adr as also read from file to append 283 | 284 | } 285 | return(suc); 286 | } 287 | 288 | // step through headers and write it out 289 | int do_sector( 290 | int fd, 291 | struct track_rec *trk, // current track record 292 | BYTE *sbuf, 293 | int sec, // current sector # 294 | WORD *ctl) 295 | { 296 | BYTE *cptr,buf[30]; // biggest record I've seen is 15 bytes 297 | struct sec_rec *psec=(struct sec_rec *) buf; 298 | struct rep_rec *rbuf = (struct rep_rec *) (buf+sizeof(struct sec_rec)); 299 | // if it exists the rep_rec immediately follows a full sec_rec when sec_rec.flag=1 300 | int off=0,i,blksz=128,rd,suc=0; 301 | if((rd = read(fd,buf,SHSZ)) != SHSZ) // base record, 6 bytes long always there 302 | suc = 1; 303 | else if(trk->trk != psec->trk || trk->head != psec->head) 304 | { 305 | printf("\nsector record mismatch, trk or head value unexpected\n"); 306 | suc = 2; 307 | } 308 | #ifdef AKAI 309 | else if(!(*ctl & WARN) && (psec->sec & 0x60) != 0x60 && sec != psec->sec) 310 | { 311 | printf("\nsector record mismatch\n"); 312 | suc = 2; // its a fatal error 313 | } 314 | #else 315 | else if(!(*ctl & WARN) && sec != psec->sec) 316 | { 317 | printf("\nsector record mismatch\n"); 318 | suc = 2; // its a fatal error 319 | } 320 | #endif 321 | else if(psec->cntrl != 0x10) // 9 byte record, ie full struct I defined 322 | // cntrl == 0x10 is oddball 323 | { 324 | if ((i = read(fd,&buf[SHSZ],3)) != 3) // read last 3 bytes 325 | suc = 1; 326 | else 327 | { 328 | rd += i; 329 | if(psec->flag == 1) 330 | { 331 | // rep_rec immediate follow, read now so dump() displays 332 | if((i= read(fd,rbuf,4)) != 4) 333 | suc = 1; 334 | else 335 | rd+= i; 336 | } 337 | } 338 | } 339 | // else its just a 6 byte record 340 | // see fudge after dump below, which may adjust fields 341 | 342 | #ifdef DUMP 343 | #ifdef AKAI 344 | if((*ctl & PHANTOM) && (psec->sec & 0x60) == 0x60) 345 | { 346 | printf("Phantom sector # 0x%x on logical track %d\n", 347 | psec->sec,2*trk->trk+trk->head); 348 | } 349 | #endif 350 | if(*ctl & DHEAD) 351 | dump(stdout,buf,rd,adr); 352 | #endif 353 | adr += rd; 354 | 355 | 356 | if(psec->sec == 0x65) // termination flag 357 | return(AKAI_END); // end of valid data, early termination 358 | /* 359 | this return is done here as found some AKAI images 360 | where it used a 2048 phantom block size. This was a 361 | problem when BLKSZ was 1024! Have increased BLKSZ 362 | in case this happens elsewhere, but since I ignore 363 | phantom sector data, it makes sense to return here 364 | */ 365 | 366 | *ctl &= ~BLKSZMSK; // clear block size bits 367 | i = psec->secsz; 368 | while(i-- > 0) 369 | blksz *=2; 370 | if(blksz <= BLKSZ) 371 | *ctl |= psec->secsz; 372 | else 373 | { 374 | printf("\nsector size of %d is too large for %d byte allocated buffer", 375 | blksz,BLKSZ); 376 | suc = 3; 377 | } 378 | 379 | if(psec->cntrl == 0x10) // fudge it now as 9 byte record 380 | // data in file only 6 bytes long, set up last bytes 381 | { 382 | // don't really understand this one, seems to work with this fudge 383 | // should it be a NUL sector? 384 | psec->unknwn[0] = 0; 385 | psec->unknwn[1] = 5; 386 | psec->flag = 1; //fake a skipped record type 387 | } 388 | if(suc != 0) 389 | printf("\n abort"); 390 | else if(psec->flag == 0) // read entire sector from record 391 | { 392 | if((off = read(fd,sbuf,blksz)) != blksz) 393 | suc = 1; 394 | else 395 | adr += off; 396 | } 397 | else if(psec->flag == 1) // skip sector its empty 398 | { 399 | if(psec->cntrl == 0x10) // just do a skip make buf blank, no crc? 400 | memset(sbuf,0,blksz); // optional extra data may be set info? 401 | else // note read rbuf above so the dump() displays it 402 | { // new logic 10/19/02 403 | if(rbuf->count *2 != blksz) 404 | { 405 | printf("\nrbuf->count %x does not match sector size %x", 406 | rbuf->count,blksz); 407 | suc = 3; 408 | } 409 | else 410 | { // fill with pattern 411 | rd = rbuf->count; 412 | cptr = sbuf; 413 | while(rd-- > 0) 414 | { 415 | for(i=0;i<2;i++,cptr++) 416 | *cptr = rbuf->pat[i]; 417 | } 418 | } 419 | // now do crc! 420 | } 421 | off = blksz; 422 | } 423 | else if(psec->flag == 2) // fragmented read 424 | { 425 | suc = do_read(fd,&off,sbuf,*ctl); 426 | } 427 | else 428 | { 429 | suc = 2; 430 | printf("\nunknwn flag in do_sector"); 431 | } 432 | 433 | if(suc == 1) 434 | printf("\nerror reading sector data"); 435 | else if(suc == 0) 436 | { 437 | #ifdef AKAI 438 | if((psec->sec & 0x60) == 0x60) 439 | suc = AKAI_SEC; // tell caller its screwy "phantom" record 440 | else 441 | 442 | #endif 443 | if((*ctl & WARN) && sec != psec->sec) 444 | { 445 | if(sec < psec->sec) 446 | { 447 | printf("\nWarning skipping %u sectors (%u through %u) on track %u\n", 448 | psec->sec - sec,sec,psec->sec-1,trk->trk); 449 | suc = SKIP_MASK | ((psec->sec - sec) & 0xff); // # to skip 450 | } 451 | else 452 | { 453 | printf("\nWarning repeating sector %u on track %u\n", 454 | psec->sec,trk->trk); 455 | suc = REPT_MASK | (psec->sec & 0xff); // repeated sector 456 | } 457 | } 458 | 459 | #ifdef DCRC 460 | // lets look at crc now, suspect if cntrl != 0x10 it should be valid 461 | // there is valid data in sbuf 462 | if(suc == 0 && psec->cntrl != 0x10) 463 | { 464 | rd = do_crc(sbuf,blksz); 465 | 466 | if( (rd & 0xff) != psec->crc) 467 | printf("\nwarning sector crc error"); 468 | } 469 | #endif 470 | 471 | } 472 | 473 | return(suc); 474 | } 475 | 476 | 477 | // advance over skipped blocks if writing output 478 | int write_skip(int fo,int skipped,int blksz,int trkcnt) 479 | { 480 | char skip_msg[25]; 481 | long offset,fpos; 482 | int rd=0,suc=0; 483 | offset = (long)blksz * skipped -16; 484 | sprintf(skip_msg,"Skip %2d blocks ",skipped); 485 | if(((rd=write(fo,skip_msg,16)) != 16 || 486 | (fpos = lseek(fo,offset,SEEK_CUR)) < 0L)) 487 | { 488 | printf("\nfatal seek error skipping %d block(s)",skipped); 489 | suc = -1; 490 | } 491 | 492 | return(suc); 493 | } 494 | 495 | /* this doesn't happen unless the image is questionable 496 | could make it smarter but on good images this doesn't occur 497 | so I'm not spending a lot of time here. Just curious 498 | The pro\wps?.td0 images do a lot of this. I added 499 | some logic in main to see how often skipped sectors are rewritten 500 | */ 501 | int write_repeat(int fo,unsigned char *buf,int blksz,int csec,int rsec) 502 | { 503 | long fpos=0,rpos=0,off; 504 | int rd=0,suc=1; 505 | // csec is expected sector, rsec is # of sector repeating 506 | // calculate # bytes we seek backward from current position 507 | off = (rsec - csec) * blksz; 508 | if(off < 0 && (fpos = lseek(fo,0L,SEEK_CUR)) > 0L && 509 | (rpos = lseek(fo,off,SEEK_CUR)) >= 0L && 510 | (rd = write(fo,buf,blksz)) == blksz && 511 | lseek(fo,fpos,SEEK_SET) > 0L) 512 | suc = 0; 513 | return(suc); 514 | } 515 | 516 | 517 | char *mons[] = {"Jan","Feb","Mar","April","May","June","July", 518 | "Aug","Sept","Oct","Nov","Dec"}; 519 | 520 | #define MAXSEC 20 // size of array to see which sectors in a track written 521 | 522 | int main(int argc,char *argv[]) 523 | { 524 | int cnt=0,csec=0,fd=EOF,fo=EOF,rd,i=-1,tnul = 0,nnul = 0,suc=-1,seccnt=0,trkcnt=0; 525 | int blksz=0,skip_cnt=0,rept_cnt=0,max_sec=0; 526 | int skipped=0,ovrwrite=0,tot_rept=0,tot_skip=0; 527 | WORD ctl=0; 528 | #ifdef DCRC 529 | WORD crc=0; 530 | #endif 531 | long flen=0,bloc=0; 532 | BYTE drvb=0,*block=0,written[MAXSEC+1]={0}; 533 | char drive=0; 534 | struct file_head fh_buf,*fhead=&fh_buf; 535 | struct com_head ch_buf,*chead=&ch_buf; 536 | struct track_rec trk; 537 | 538 | if(argc > 2) 539 | for(i=2;i must be A or B"); 571 | 572 | } 573 | else if(strnicmp(argv[i],"-o",2) == 0) 574 | if((fo=open(argv[i]+2,O_BINARY|O_RDWR|O_CREAT|O_TRUNC, 575 | S_IREAD|S_IWRITE)) == EOF) 576 | printf("\nfailed to open output file: %s",argv[i]+2); 577 | } 578 | if(argc < 2) 579 | { 580 | printf("\nusage: wteledsk [-n#] -o [-p] [-s] [-r]"); 581 | #ifdef DISK 582 | printf(" [-w]"); 583 | #endif 584 | #ifdef DUMP 585 | printf(" [-dh] [-db]\n -d to dump headers or restrutured block data"); 586 | #endif 587 | 588 | printf("\n -n limit scan to first n sectors"); 589 | printf("\n -o output restructured blocks to a file"); 590 | printf("\n -p display phantom sec_rec.nsec values"); 591 | printf("\n -s warn instead of fatal error on skipped sectors"); 592 | printf("\n -r if repeated sector found, write it out again"); 593 | #ifdef DISK 594 | printf("\n -w write restructured blocks to floppy disk sectors"); 595 | #endif 596 | printf("\n Version 1.01 "); 597 | #ifdef DCOMP 598 | putchar('A'); // supports 'new' advanced compression 599 | #endif 600 | #ifdef DCRC 601 | putchar('C'); // doing CRC checks 602 | #endif 603 | } 604 | else if ((fd = open(argv[1],O_BINARY|O_RDONLY)) == EOF) 605 | printf("\ncould not open %s",argv[1]); 606 | else if((rd = read(fd,fhead,sizeof(struct file_head))) == 607 | sizeof(struct file_head) && 608 | strnicmp((char *)fhead,"TD",2) ==0 ) 609 | { // always have a file header 610 | flen = lseek(fd,0L,SEEK_END); // length of file 611 | lseek(fd,(long)rd,SEEK_SET); // back to where we were 612 | // was flen = filelength(fd); // remove for linux 613 | #ifdef DUMP 614 | if(ctl & DHEAD) 615 | dump(stdout,(BYTE *)fhead,sizeof(struct file_head),0); // fhead 616 | #endif 617 | // 1st two chars determine type of compression 618 | if(strncmp((char *)fhead,"td",2) == 0) 619 | { 620 | #ifdef DCOMP 621 | docomp = 1; // set global flag to redirect read() 622 | 623 | init_Decode(fd); // initialize decompression routine 624 | #else 625 | printf("\nthis file not supported, it uses advanced compression\n"); 626 | exit(0); 627 | #endif 628 | } 629 | 630 | #ifdef DCRC 631 | crc = do_crc((BYTE *)fhead,0xa); // check 1st 10 bytes 632 | if(fhead->crc != crc) 633 | printf("\nwarning crc error in file header"); 634 | #endif 635 | if((block = (unsigned char *) malloc(BLKSZ)) == NULL) 636 | { 637 | printf("\nfailed to allocate space for sector buffer"); 638 | exit(1); 639 | } 640 | 641 | if(fhead->flag & COMMENT) // there is a comment block 642 | { 643 | rd = 1; 644 | // if chead exists, it immediately follows fhead 645 | if(read(fd,chead,sizeof(struct com_head)) != 646 | sizeof(struct com_head)) 647 | printf("\nfailed to read comment header"); 648 | else if(BLKSZ < chead->len) // not likely! 649 | printf("\nallocated buffer too small for comment of length %d", 650 | chead->len); 651 | else if(read(fd,block,chead->len) != chead->len) 652 | { 653 | printf("\nfailed to read comment"); 654 | } 655 | else 656 | rd = 0; 657 | 658 | if(rd != 0) 659 | { 660 | if(block) free(block); 661 | exit(1); // one of fatal errors above 662 | } 663 | 664 | #ifdef DCRC 665 | // check all but 1st 2 bytes of comment region 666 | crc = do_crc((BYTE *)chead+2,8); // header 667 | updt_crc(&crc,block,chead->len); // comment data 668 | if(chead->crc != crc) 669 | printf("\nwarning crc error in comment header"); 670 | #endif 671 | 672 | #ifdef DUMP 673 | if(ctl & DHEAD) 674 | dump(stdout,(BYTE *)chead,sizeof(struct com_head),sizeof(struct file_head)); // chead 675 | // we just printed comment, don't bother to dump 676 | #endif 677 | putchar('\n'); 678 | // this includes old logic that coounted # of NULs 679 | // convert each NUL to CR\LR 680 | for(i=0;ilen;i++) 681 | { 682 | if(block[i] == 0) 683 | { 684 | putchar('\n'); 685 | nnul++;tnul++; 686 | if(tnul >= 8) 687 | break; 688 | } 689 | else 690 | { 691 | putchar(block[i]); 692 | nnul = 0; // clear consecutive NUL count 693 | } 694 | } 695 | printf("\ncreated %s %02d, %4d %02d:%02d:%02d", 696 | mons[chead->mon],chead->day,chead->yr+1900, 697 | chead->hr,chead->min,chead->sec); 698 | printf("\nstring len 0x%2x start variable 0x%2x = 0x%2x with %d NULs", 699 | chead->len,chead->len+0x16,i+1,tnul); 700 | } 701 | else 702 | { 703 | printf("\n no comment data included in file"); 704 | } 705 | printf("\nfile length 0x%lx = %ld\n\n",flen,flen); 706 | suc = 0; 707 | 708 | } 709 | else if(rd >= 0x10) 710 | printf("\ninvalid id %2.2s", (char*) fhead); 711 | else 712 | printf("\nread error, header not long enough"); 713 | adr = sizeof(struct file_head); // set track data to start after fhead 714 | if(fhead->flag & COMMENT) 715 | adr += sizeof(struct com_head) + chead->len; // skip variable length comment 716 | bloc = adr; // skip block header, set global file offset 717 | 718 | if(suc == 0 && fd != EOF) 719 | { 720 | while(suc == 0 && (i=read(fd,&trk,sizeof(struct track_rec))) 721 | == sizeof(struct track_rec)) 722 | { // do tracks 723 | memset(written,0,MAXSEC); // clear written array 724 | #ifdef DUMP 725 | if(ctl & DHEAD) 726 | { 727 | printf("\n%2d sectors for head %d physical track %2d (decimal)\n", 728 | trk.nsec,trk.head,trk.trk); 729 | dump(stdout,(BYTE *)&trk,i,adr); 730 | } 731 | #endif 732 | if(trk.nsec == 0xff) 733 | { 734 | printf("\nNormal EOF on track %d #sectors in track record = 0xff", 735 | trk.trk); 736 | break; // end of file 737 | } 738 | 739 | #ifdef DCRC 740 | // note last crc when trk.nsec == 0xff doesn't match 741 | crc = do_crc((BYTE *)&trk,3); // check 1st 3 bytes 742 | if(trk.crc != (crc & 0xff)) // byte crc 743 | printf("\nwarning crc error in track record 0x%x", 744 | trk.trk); 745 | #endif 746 | rept_cnt = skip_cnt = 0; // assume in sync 747 | adr += i; // read track record 748 | for(i=1;suc == 0 && i<=trk.nsec;i++) 749 | { 750 | // allow early abort for testing 751 | if((ctl & DCNT) && cnt-- <=0) 752 | break; 753 | skipped = 0; // assume signal no skip occured 754 | if((suc = do_sector(fd,&trk,block,i+skip_cnt-rept_cnt,&ctl)) == 0 || 755 | suc == AKAI_SEC || (suc & SKIP_MASK) || (suc & REPT_MASK) ) 756 | { 757 | #ifdef DUMP 758 | if(ctl & DBLOCK) 759 | { 760 | dump(stdout,block,BLKSZ,0); 761 | putchar('\n'); 762 | } 763 | #endif 764 | blksz = block_size(ctl); 765 | 766 | if(suc & SKIP_MASK) 767 | { 768 | skipped = (suc & 0xff); 769 | if(fo != EOF && write_skip(fo,skipped,blksz,trkcnt) < 0) 770 | { 771 | suc++; 772 | break; 773 | } 774 | 775 | skip_cnt += skipped; 776 | tot_skip += skipped; 777 | suc = 0; // all is well 778 | } 779 | /* code above skips forward in file if skip a sector 780 | but still need to write sector just filled in by 781 | do_sector(). As of 10/03/02 somehow starting 782 | at offset 0x18 block has been corrupted by the 783 | warning message via printf() in do_sector()?? 784 | */ 785 | 786 | if(suc == AKAI_SEC) 787 | { 788 | suc = 0; 789 | skip_cnt--; // ignore this sector for now 790 | // must decrement skip_cnt for do_sector() check 791 | } // skip output below for AKAI_SEC 792 | else if(suc & REPT_MASK) 793 | { 794 | csec = i+skip_cnt-rept_cnt; // current sector # 795 | if(ctl & REPEAT) 796 | { 797 | if(fo != EOF && csec < MAXSEC && 798 | written[csec] == 0 && 799 | write_repeat(fo,block,blksz,csec,suc & 0xff)) 800 | { 801 | printf("\nfatal error writting repeated sector"); 802 | suc++; 803 | break; 804 | } 805 | if((csec = (suc & 0xff)) < MAXSEC && written[csec] == 0) 806 | { 807 | written[csec] = 1; 808 | seccnt++; 809 | ovrwrite++; // overwrote a skipped sector 810 | printf("Rewrite skipped sector %d\n",csec); 811 | } 812 | } 813 | rept_cnt++; 814 | tot_rept++; 815 | suc = 0; // clear suc so looping continues 816 | } 817 | else 818 | { // process this sector, prossibly writting to disk 819 | seccnt ++; // # physical sectors, ignores phantom & skipped 820 | #ifdef DISK 821 | // conditionally allow direct write to a physical disk drive 822 | if(ctl & DWRITE) 823 | { 824 | if(blksz != 512) 825 | { 826 | printf("\nfatal error, direct write with block size %d not allowed",blksz); 827 | suc++; 828 | } 829 | else if((rd=aputsecs(drvb,(int)trk.head,(int)trk.trk, 830 | i+skip_cnt- rept_cnt,i+skip_cnt- rept_cnt,block)) != 0) 831 | { 832 | printf("\nerror writing sector to disk logical track %d sector %d", 833 | (int)trk.trk,i+skip_cnt- rept_cnt); 834 | suc++; 835 | } 836 | } 837 | #endif 838 | if(fo != EOF && 839 | (rd = write(fo,block,blksz)) != blksz) 840 | { 841 | printf("\n data write error logical track %d sector %d", 842 | (int)trk.trk,i+skip_cnt- rept_cnt); 843 | suc++; 844 | } 845 | else if((csec = i+skip_cnt-rept_cnt) < MAXSEC) 846 | written[csec] = 1; // wrote it 847 | } // special write skip for AKAI_SEC 848 | } 849 | } 850 | if(suc == AKAI_END) 851 | break; // don't do error check below on phantom track 852 | 853 | /* 854 | add error checks below because found at least one test 855 | case where skipped last sector in track. No way to 856 | detect this with information I currently understand! 857 | By doing both cases below can catch on 1st track 858 | do_sector() will warn/catch if out of sequence 859 | */ 860 | if(max_sec < trk.nsec + skip_cnt - rept_cnt) 861 | { 862 | max_sec = trk.nsec + skip_cnt - rept_cnt; 863 | if(trkcnt > 0) 864 | printf("\nnew max sector/track %d on logical track %d\n", 865 | max_sec,trkcnt); 866 | /* don't see how to fix on track 0 if it doesn't 867 | write last sector, as don't know for sure till 868 | have written track 2. Could add cmd line option 869 | but that's pretty clunky 870 | */ 871 | } 872 | else if(max_sec > trk.nsec + skip_cnt - rept_cnt) 873 | { 874 | printf("\nmax sector/track %d on logical track %d less the others", 875 | trk.nsec + skip_cnt - rept_cnt,trkcnt); 876 | i = max_sec -(trk.nsec + skip_cnt - rept_cnt); 877 | tot_skip += i; 878 | if(ctl & WARN) 879 | printf("\nWarning skipping %d sectors at end of logical track %d\n", 880 | i,trkcnt); 881 | else 882 | putchar('\n'); 883 | 884 | if(fo != EOF && write_skip(fo,i,blksz,trkcnt) < 0) 885 | break; // fatal 886 | 887 | } 888 | trkcnt++; // increment my track count 889 | 890 | } 891 | if(suc == AKAI_END) 892 | { 893 | printf("\nEarly termination, found AKAI END sector flag 0x65"); 894 | } 895 | 896 | if((bloc = lseek(fd,0L,SEEK_CUR)) > 0) 897 | printf("\nfinal file position 0x%lx",bloc); 898 | if(flen == bloc) printf(" => this is EOF"); 899 | printf("\nparsed %d logical tracks, %d sectors with data ", 900 | trkcnt,seccnt); 901 | printf("\n found %d skipped sectors and %d repeated", 902 | tot_skip,tot_rept); 903 | if(ctl & REPEAT) 904 | printf(" (%d skipped overwritten)",ovrwrite); 905 | else printf(" (ignored)\n"); 906 | printf("\nmax data sectors/track %d",max_sec); 907 | if(seccnt != (i = trkcnt * max_sec)) 908 | printf("\n WARNING implies total sectors should be %d",i); 909 | 910 | } 911 | putchar('\n'); // make linux output a little prettier 912 | if(block) free(block); 913 | 914 | return 0; 915 | } 916 | --------------------------------------------------------------------------------