Compare commits

...

277 Commits

Author SHA1 Message Date
David Given
3601f9f487 Clean up the sequencer firmware somewhat, trying to look for this Amiga write
issue.
2021-07-09 21:25:58 +01:00
David Given
7f95f6b43f Merge pull request #299 from davidgiven/greaseweazle
Support GreaseWeazle versions above 24.
2021-07-08 10:08:59 +01:00
David Given
b2d7ba1a65 Treat all GreaseWeazle versions from 24 or above the same, as the USB protocol
has been frozen.
2021-07-08 10:50:31 +02:00
David Given
b550bbbd08 Don't link against libudev any more. 2021-07-08 10:49:57 +02:00
David Given
4cbb75df60 Merge pull request #273 from davidgiven/amiga
Fix the Amiga encoder after the protobuf rewrite.
2021-07-06 23:05:45 +01:00
David Given
7d8926659e Merge pull request #292 from wybren1971/patch-2
Fix distorted picture
2021-07-06 14:34:18 +01:00
David Given
37ca3f74c7 Make the Amiga encoder a unicorn. 2021-07-05 23:16:37 +02:00
David Given
56cbf39d59 Finally make the Amiga encoder work properly. Do some FM/MFM/bits refactoring. 2021-07-05 23:16:03 +02:00
Wybren van Duinen
a783e7f500 fix typo 2021-07-05 17:41:37 +02:00
Wybren van Duinen
2f4ac42684 Fix distorted picture
modify available space (width and height) with subtraction of two times the border (up en down, and left and right)
2021-07-05 16:56:57 +02:00
David Given
ec689e100e Merge from trunk. 2021-07-04 23:04:02 +02:00
David Given
6f0909d992 Merge pull request #289 from davidgiven/c64
Fix writing C64 disks
2021-07-04 11:21:20 +01:00
David Given
0092dec49e Hopefully make writing C64 disks work again by fixing the logical:physical
track mapping.
2021-07-03 12:09:10 +02:00
David Given
3447689c19 Don't crash when trying to encode tracks with no data. 2021-07-03 11:38:45 +02:00
David Given
d4563fd842 Merge pull request #282 from davidgiven/osx
Fix some OSX build inelegancies.
2021-06-19 13:28:44 +02:00
David Given
d2ecec6009 Typo fixes. 2021-06-19 12:54:06 +02:00
David Given
26f26aec50 Try to fix ranlib on Linux and Windows. 2021-06-19 12:35:18 +02:00
David Given
34dfc2767f Fix some OSX build inelegancies. 2021-06-19 12:24:47 +02:00
David Given
bc3d3cabce Merge pull request #276 from davidgiven/greaseweazle
Fix GreaseWeazle support.
2021-06-04 23:53:45 +02:00
David Given
23d80b93ae Update GreaseWeazle documentation for the new version. 2021-06-04 23:42:23 +02:00
David Given
0d59d3d195 Typo fix on the Linux/OSX side of things. 2021-06-04 23:36:25 +02:00
David Given
192427cf80 Finally figure out why Windows wasn't working --- the Win32 ReadFile / ::read
call waits until _all_ bytes are read, rather than returning as soon as any
bytes are read. Fixed.
2021-06-04 22:03:44 +01:00
David Given
98e4094d70 Set up the GreaseWeazle termios properly. 2021-06-02 22:51:57 +02:00
David Given
e88b939866 Allow multiple fallback parameters using =. 2021-06-02 22:51:41 +02:00
David Given
aed5a02ee1 Remove flag references from the Amiga encoder. 2021-05-28 16:58:28 +02:00
David Given
9f1e1bb2b6 Merge pull request #271 from hharte/rpm-retry
Perform retries when calculating drive RPM.
2021-05-27 11:29:51 +02:00
David Given
15e9383d0b Merge pull request #269 from hharte/fix-doc
Add mingw-w64-i686-protobuf to list of packages
2021-05-27 11:21:43 +02:00
David Given
4ec056900c Merge pull request #270 from hharte/track-ruler
Add ruler for track numbers.
2021-05-27 11:21:24 +02:00
David Given
983feb59b0 Merge pull request #272 from hharte/fix-typo
Fix typo.
2021-05-27 11:21:00 +02:00
Howard M. Harte
8b2ce33f83 Fix typo. 2021-05-26 23:12:09 -07:00
Howard M. Harte
44b452b30b Perform retries when calculating drive RPM.
The drive RPM measurement fails about 3% of the time.  Retry up to
five times until it succeeds, and exit with an error if it doesn't.
2021-05-26 23:02:34 -07:00
Howard M. Harte
f60c9981b8 Add mingw-w64-i686-protobuf to list of packages
When building under Windows, the mingw-w64-i686-protobuf support
package is required.
2021-05-26 22:54:20 -07:00
Howard M. Harte
7c3df93580 Add ruler for track numbers. 2021-05-26 22:50:13 -07:00
David Given
601d7dfcf8 Merge pull request #268 from davidgiven/windows
Make the Windows build static again.
2021-05-26 20:37:25 +02:00
David Given
41cb53a550 We neeed update:true to get the version of the protobuf package with the static
library in it.
2021-05-26 20:23:59 +02:00
David Given
87c13ae20c Make the Windows binary static again. 2021-05-26 18:51:33 +01:00
David Given
c30482af66 Merge pull request #266 from davidgiven/hharte-northstar
Merge in hharte's Northstar changes
2021-05-25 20:25:27 +02:00
David Given
5f4f2f10d7 Update FluxEngine components and rebuild firmware. 2021-05-25 19:11:32 +01:00
David Given
ae630a0e18 Merge pull request #267 from hharte/hharte-northstar
Northstar: Fix configurations.
2021-05-25 18:55:29 +02:00
Howard M. Harte
c66931cb12 Northstar: Fix configurations.
Tested read/write with 350K, 175K, and 87K disks.
2021-05-24 20:40:17 -07:00
David Given
fb2dbe25cd Add missing file. 2021-05-24 23:33:40 +02:00
David Given
bf4831be9f Migrate the Northstar code to the new framework. 2021-05-24 23:20:59 +02:00
David Given
0f83082cf0 Merge branch 'northstar' of https://github.com/hharte/fluxengine into hharte-northstar 2021-05-24 21:39:51 +02:00
David Given
fbfde604e7 Merge pull request #264 from davidgiven/salfter-local
Overhaul profiles and merge in salfter's IBM formats
2021-05-24 12:24:21 +02:00
David Given
5e2dfbed73 Add the 9-sector IBM formats. 2021-05-24 12:10:21 +02:00
David Given
571602d3de Go back to only having a single Atari ST read profile. 2021-05-24 12:10:01 +02:00
David Given
85693f2577 Show the profile comment in the help. 2021-05-24 11:15:21 +02:00
David Given
60bc8ad888 Add comment fields to all the configs; fix some documentation. 2021-05-23 23:57:38 +02:00
David Given
9bf309eb5c Add physical mapping options. 2021-05-23 23:57:22 +02:00
David Given
eeddfa87b6 Merge branch 'local' of https://github.com/salfter/fluxengine into salfter-local 2021-05-23 21:18:58 +02:00
David Given
861fe2466c Merge pull request #263 from davidgiven/hpingel-greaseweazle_v024_compat
Update the GreaseWeazle and USB support.
2021-05-23 21:12:03 +02:00
David Given
3a2cce78ca Update incorrect reference to usb.device. 2021-05-23 20:49:34 +02:00
David Given
4e01f1de0d Update GreaseWeazle documentation. 2021-05-23 20:41:10 +02:00
David Given
4f0a5e854c Attempt to rework the USB handling to make the GreaseWeazle work on Windows and
Mac --- we abandon autodetection for anything other that FluxEngines as libusb
is painfully inconsistent with serial devices.
2021-05-23 18:53:25 +01:00
David Given
c669a9c808 Add libudev autodetect for GreaseWeazle devices. 2021-05-23 17:44:01 +02:00
David Given
e4d827256f Add test code for platform detection. 2021-05-23 12:42:49 +02:00
David Given
1c1ad2725e Port the Greaseweazle driver to use the serial port rather than raw libusb. 2021-05-23 12:39:41 +02:00
David Given
d18e54c5a3 Add Greaseweazle support for firmwares 22 and 24. 2021-05-23 12:01:29 +02:00
David Given
7a885e23d3 Merge branch 'greaseweazle_v024_compat' of https://github.com/hpingel/fluxengine into hpingel-greaseweazle_v024_compat 2021-05-23 10:50:35 +02:00
David Given
33dde3a465 Merge pull request #262 from davidgiven/wybren1971-Commodore-64-encoder
Merge in wybren1971's 1541 encoder and D64 reader.
2021-05-22 23:42:52 +02:00
David Given
8d4ac59f03 Try and fix that intermittent dependency problem with the test protos. 2021-05-22 23:22:24 +02:00
David Given
4fbfc492ce Mark the C64 format as a unicorn. 2021-05-22 20:59:54 +02:00
David Given
ac1fb71bc5 Note the limitation when writing 1541 disks. 2021-05-22 20:58:28 +02:00
David Given
01d6b37b83 Try to update the dev build with the current date. 2021-05-22 10:36:23 +02:00
David Given
2284b4dce1 Upgrade to checkout@v2. 2021-05-22 10:21:43 +02:00
David Given
b92f80e6be And try to fix it again. 2021-05-22 00:01:00 +02:00
David Given
bd5596fa30 Fix the autorelease script. 2021-05-21 23:57:39 +02:00
David Given
4614b63c30 Convert wybren1971's D64 reader and 1541 encoder to work with the new
architecture.
2021-05-21 23:34:28 +02:00
David Given
cf41b6cbb2 Merge branch 'Commodore-64-encoder' of https://github.com/wybren1971/fluxengine into wybren1971-Commodore-64-encoder 2021-05-21 23:02:04 +02:00
David Given
87f3e0e291 Merge pull request #253 from davidgiven/protobuf
Rework the configuration system to use protobufs.
2021-05-21 22:36:58 +02:00
David Given
1997abcde6 Warning fix pass. 2021-05-21 22:12:14 +02:00
David Given
8b761298ee Using a tool which wants a profile with no parameters now lists the available
profiles.
2021-05-21 21:39:00 +02:00
David Given
df0356b841 Update documentation. 2021-05-21 21:27:42 +02:00
David Given
dc158ebff4 Add a documentation page for the Eco1. 2021-05-21 21:15:51 +02:00
David Given
f8192b90f4 Add README banner about the command line change. 2021-05-21 20:42:50 +02:00
David Given
5237049e2c Fix fe-inspect to work. 2021-05-21 20:40:52 +02:00
David Given
9d70fb261e Remove some obsolete tools. 2021-05-21 20:40:42 +02:00
David Given
8370b8a743 Adjust documentation formatting. 2021-05-20 23:00:28 +02:00
David Given
25656d81a1 Update documentation for the new version. 2021-05-20 22:41:03 +02:00
David Given
be97791428 Set sane defaults for rpm and seek. Adjust the aeslanier cylinder range. 2021-05-20 22:40:51 +02:00
David Given
18b683d22e Add drive:N as a valid flux source and sink specifier; remove the --drive
option. Also, a bunch of option cleanup.
2021-05-20 22:14:22 +02:00
David Given
34e9742f71 Use .st for the default Atari ST image filenames. 2021-05-20 19:07:15 +02:00
David Given
087eb7777e Bizarrely, we need to install vim to get xxd. 2021-05-19 23:03:16 +01:00
David Given
c469c5e3a1 Set the right msystem? 2021-05-19 22:56:34 +01:00
David Given
485c29bb49 Install maybe the right packages? 2021-05-19 22:53:53 +01:00
David Given
aa0cf150bd Apparently I've been using the wrong compiler all along. 2021-05-19 22:50:29 +01:00
David Given
91c52e91cd Merge. 2021-05-19 23:02:08 +02:00
David Given
a1f4014738 Rework the ibm encoder to allow per-track configurations. Rename everything for
consistency.
2021-05-19 23:01:32 +02:00
David Given
8a136ac4f6 Allow imgimagewriter to write mixed-format disks; add an eco1 disk format for the VDS Eco1. 2021-05-19 22:12:23 +02:00
David Given
ea43b3dc6a Apply Windows compatability hack. 2021-05-18 22:46:57 +01:00
David Given
57fc787819 You can load config files now. 2021-05-18 22:44:41 +02:00
David Given
a0164b8de3 Rip out dataspecs everywhere. 2021-05-18 22:22:06 +02:00
David Given
9df35c1814 Fix a bunch of documentation strings. 2021-05-18 21:10:59 +02:00
David Given
9d2a5fee86 Range ends default to range start. 2021-05-18 21:09:31 +02:00
David Given
a6b2e932fa copy-flux-to works. 2021-05-18 20:13:03 +02:00
David Given
05aaa2634b Don't hard-code references to the global config in the
updateConfigForFilename() methods.
2021-05-18 19:57:23 +02:00
David Given
2c98f5c542 Convert cwftoflux to a proper flux source. 2021-05-18 19:37:43 +02:00
David Given
d246fca9df Convert scptoflux to a proper flux source. 2021-05-18 01:09:55 +02:00
David Given
c79feb405c Switch to resource path type thingies for specifying flux file types, where
appropriate. Much better.
2021-05-18 00:40:16 +02:00
David Given
b1145f8da3 Flux sink type is now detected from file extension. 2021-05-18 00:23:38 +02:00
David Given
3588d681a2 Convert fluxscp to a proper flux sink. 2021-05-17 23:28:30 +02:00
David Given
55d894ae1f Convert fluxtovcd to a proper flux sink. 2021-05-17 22:12:44 +02:00
David Given
0509caf432 Allow the .au writer to write multiple tracks. 2021-05-17 21:53:15 +02:00
David Given
5d1d807e78 Convert fluxtoau to a real flux sink. 2021-05-17 21:42:25 +02:00
David Given
22ba38b2e0 Add rawread. 2021-05-17 01:06:09 +02:00
Howard M. Harte
42f858267c Hard Sectors: Fix index_irq assertion.
The index_irq could trigger one sector too late, in the case where
index_irq was reset just before the sector pulse was received.

Check the duration of the time between pulses before propagating the
hardsec_index_irq_primed to index_irq.

While this change is required for reading and writing North Star
floppies (due to lack of positioning information in the sector header)
it is also helpful for reading Micropolis disks, as currently,
occasionally the first sector will be missed, and will be found on the
next rotation of the disk.  This change is required for reliably writing
Micropolis disks though, when that functionality becomes available.
2021-05-16 15:41:55 -07:00
Howard M. Harte
5b1a3173f8 Add support for North Star hard-sectored floppies.
North Star Floppy disks use 10-sector hard sectored disks with either FM
or MFM encoding.  They may be single- or double-sided.  Each of the 10
sectors contains 256 (FM) or 512 (MFM) bytes of data.  The disk has 35
cylinders, with tracks 0-34 on side 0, and tracks 35-69 on side 1.
Tracks on side 1 are numbered "backwards" in that track 35 corresponds
to cylinder 34, side 1, and track 69 corresponds to cylinder 0, side 1.

The North Star sector format does not include any head positioning
information.  As such, reads from North Star floppies need to be
synchronized with the index pulse, in order to properly identify the
sector being read.  While there is a command line option:
--sync-with-index, that does this, the North Star reader forces this
behavior by default.

MFM sectors have 32 bytes of 00's followed by two sync characters,
specified in the North Star MDS manual as 0xFBFB.

This is true for most disks; however, I found a few disks, including an
original North Star DOS/BASIC v2.2.1 DQ disk, that uses 0xFBnn, where
nn is an incrementing pattern.

While searching for the start of a sector header, seekToPattern()
ignores the sector pulses.  If a sector header cannot be decoded for any
reason, seekToPattern() will advance past one or more sector pulses.
For this reason, the _hardSectorId is calulated after the sector header
is found.

Due to the nature of the track ordering on side 1, an .nsi image reader
and writer are provided for double-sided disks.  The .nsi image format
supports both single- and double-sided disks; however, single-sided .nsi
images are equivalent to .img images.
2021-05-16 15:41:55 -07:00
David Given
97822bd9a8 analysedriveresponse works. 2021-05-16 23:01:03 +02:00
David Given
b926f5a692 Setting boolean config fields works. 2021-05-16 23:00:34 +02:00
David Given
191dfdf13f Make seek work. 2021-05-16 20:13:02 +02:00
David Given
a100f1fe1e Merge. 2021-05-16 18:23:30 +02:00
David Given
471ccf281c Allow setting cylinder and head ranges. 2021-05-16 18:11:40 +02:00
David Given
6c959be188 Remove reference to dead code. 2021-05-16 00:13:17 +01:00
David Given
010887f18f Try and make build on Windows again. 2021-05-16 00:36:32 +02:00
David Given
e4b5e4c502 Make Kryoflux streams again. 2021-05-16 00:25:41 +02:00
David Given
2de3b4f92e Make rawwrite work (which replaces erase and writeflux). 2021-05-15 23:30:58 +02:00
David Given
225a93330d Rename more config stuff. 2021-05-15 22:37:29 +02:00
David Given
41b36649a9 Rename a bunch of config fields for clarity. 2021-05-15 22:23:42 +02:00
David Given
55c26ab1c4 Make rpm work. 2021-05-15 21:48:42 +02:00
David Given
8b1e60a1fd Remove unused dataspecs. 2021-05-15 21:35:38 +02:00
David Given
ed0f38748b Add helper flags to fe-read and fe-write for setting common parameters. 2021-05-15 21:28:02 +02:00
David Given
7d75a720ca Remove dependent FlagGroups, to make sure that we can't use them by mistake. 2021-05-15 19:00:45 +02:00
David Given
5a38db166f Remove reader flags. 2021-05-15 18:56:30 +02:00
David Given
3c9fb79263 The -Wl,--no-as-needed option isn't needed any more and doesn't work on OSX. 2021-05-15 18:30:49 +02:00
David Given
98d5a2dad9 Add missing file. 2021-05-15 18:07:05 +02:00
David Given
4ab8b4984d Rename all InputProto and OutputProtos for clarity. 2021-05-15 18:05:53 +02:00
David Given
f741ad058e Convert the ZilogMcz decoder. 2021-05-15 17:40:20 +02:00
David Given
2512a4fc32 Convert the Victor9k decoder. 2021-05-15 17:29:59 +02:00
David Given
5554d5e608 Convert the TIDS990 encoder and decoder. 2021-05-15 17:24:20 +02:00
David Given
48d5ed2ff9 Convert the MX decoder. 2021-05-15 17:06:28 +02:00
David Given
2632668d0e Convert (hopefully correctly) the IBM writables. 2021-05-15 14:26:06 +02:00
David Given
f46e444aa2 Convert the Micropolis decoder. 2021-05-15 14:10:08 +02:00
David Given
1149ad86a2 Convert the Apple2 decoder. 2021-05-15 14:04:44 +02:00
David Given
e1398d98b0 Remove some stray files. 2021-05-15 13:50:13 +02:00
David Given
8133e2b7aa Convert the Atari ST configuration. 2021-05-15 13:49:31 +02:00
David Given
ebe678b18b Fix IBM sector base default; convert the ampro configuration. 2021-05-15 13:47:34 +02:00
David Given
509217606c Remove sqlite flux sink flags. 2021-05-15 13:42:30 +02:00
David Given
6fb694669c Remove the fluxmapreader flags. 2021-05-15 13:34:26 +02:00
David Given
5a63172a86 Remove the hardware source/sink flags. 2021-05-15 13:06:53 +02:00
David Given
93dcc7e242 Convert the image readers and Mac encoder and decoder. 2021-05-15 12:33:22 +02:00
David Given
243eea33e9 Convert the FB100 decoder. 2021-05-15 12:11:11 +02:00
David Given
38a8367f62 Convert the F85 decoder. 2021-05-15 12:05:31 +02:00
David Given
a02953cccc Fix the last fix. 2021-05-15 11:56:50 +02:00
David Given
f13f96967e Try to fix the dependency issue with protobufs. 2021-05-15 11:09:31 +02:00
David Given
f7c31281e0 Convert the C64 decoder to the new configuration scheme. Also convert all the
imagewriters.
2021-05-15 00:25:32 +02:00
David Given
ac34a43d9b Add the missing Brother 120kB format. 2021-05-15 00:08:49 +02:00
David Given
c8d0950979 Convert the Amiga encoder and decoder to the new system. 2021-05-15 00:06:39 +02:00
David Given
a4ff59eccb Remember to actually hook the aeslanier decoder up. 2021-05-14 23:42:01 +02:00
David Given
c3a50f9442 Add missing file... 2021-05-14 23:39:47 +02:00
David Given
57e81ee72e Convert the AES Lanier decoder to the new configuration system. 2021-05-14 23:39:07 +02:00
David Given
05df0a37b1 Add (pretty basic) config dumping and documentation help. 2021-05-14 23:29:16 +02:00
David Given
25f2c3a8c1 Move the USB flags into the config file. 2021-05-13 23:35:05 +02:00
David Given
c3aa12db78 Add support for filename flags to the flags parser. Allow setting config values
from the command line.
2021-05-13 23:16:52 +02:00
David Given
a3bd7cc644 Programmatically create the readables and writables mappings. 2021-05-13 19:39:45 +02:00
David Given
5a186b6960 Rename all protos to end with 'Proto' to avoid name conflicts. 2021-05-13 18:05:08 +02:00
David Given
639588fa68 Hardware flux sources are configurable. 2021-05-13 17:39:49 +02:00
David Given
f9510c54b2 Split the encoder/decoder configuration away from inputs and outputs. Make the
test pattern creator a flux source.
2021-05-13 17:23:06 +02:00
David Given
3a8ddf8025 The writer now works with the new config system. 2021-05-13 15:55:05 +02:00
a2801ea88c add write support for various 5.25" formats 2021-05-12 17:28:59 -07:00
David Given
c2aae7ee18 fe-read now supports multiple readable formats. 2021-05-13 00:02:54 +02:00
David Given
9d0804eff4 Convert the IBM decoder to use the new config system. 2021-05-12 23:42:38 +02:00
David Given
6ff84b3693 Bash the imagewriter stuff into working with the new config system. 2021-05-12 23:08:17 +02:00
wybren1971
b641e0282b Update disk-c64.md
wrong quotes
2021-05-12 15:32:37 +02:00
wybren1971
37a467cabc updated the Doc 2021-05-12 15:24:35 +02:00
wybren1971
e01a7110ac Added format bytes (disk ID) to header from BAM 2021-05-12 14:55:32 +02:00
wybren1971
5dad5de548 removed a testmessage 2021-05-12 10:14:12 +02:00
wybren1971
295325a20b Added some checks on sector status 2021-05-12 10:11:51 +02:00
David Given
df0a9bac96 More config machinery: the reader now reads (but can't put the resulting image
anywhere).
2021-05-12 00:26:42 +02:00
David Given
cf9cef6f87 Added the machinery for including literal protobufs in the code. 2021-05-11 22:13:10 +02:00
wybren1971
f92814b24b Added option to write d64 images back to disk 2021-05-11 19:54:50 +02:00
David Given
331b59cd1e Merge pull request #256 from davidgiven/writeflux
Fix writeflux to actually work.
2021-05-11 00:04:10 +01:00
David Given
ed6d211aff Fix writeflux to actually work. 2021-05-11 00:22:19 +02:00
David Given
a8f1469d36 Flesh out the proto config stuff some more. 2021-05-10 22:38:04 +02:00
David Given
74cb332706 The dotted path setter should now work properly. 2021-05-08 23:56:45 +02:00
David Given
89165369b1 Import Snowhouse. 2021-05-08 23:01:40 +02:00
David Given
a54e3d33a6 Add a first draft of the dotted config file setter. 2021-05-08 22:58:29 +02:00
David Given
0f17984f41 Merge pull request #227 from hharte/ibm-presets
Add presets for 5.25" IBM PC floppies.
2021-05-08 15:41:45 +01:00
wybren1971
6527ceb913 Added comment 2021-05-08 12:41:11 +02:00
wybren1971
64a57ba837 Change sectorskew IMD starts numbering with 1 2021-05-08 12:39:28 +02:00
wybren1971
74da9330f8 IMD file expect sectornum to start with 1 not 0 2021-05-08 12:23:58 +02:00
David Given
4fa1dd6860 There aren't any static protobuf libraries, so we're going to have to build
dynamic Windows executables now.
2021-05-08 10:56:15 +01:00
Howard M. Harte
e55effc9ca Add presets for 5.25" IBM PC floppies.
For reading:
    --ibm-preset-360 - Read a 5.25" 360kB disk in a 48tpi drive.
    --ibm-preset-360-96tpi - Read a 5.25" 360kB disk in a 96tpi
        drive.
    --ibm-preset-1200 - Read a 5.25" 1.2MB disk.

For writing:
    --ibm-preset-360 - Write a 5.25" 360kB disk in a 48tpi drive.
    --ibm-preset-1200 - Write a 5.25" 1.2MB disk in a 96tpi drive.

Test:
    Test reading and writing using a Teac FD-55GFR-571 96tpi drive.
    Test reading and writing using an Alps DFC222B05A 48tpi drive.

Fixes davidgiven/fluxengine#189
2021-05-07 23:20:58 -07:00
David Given
924b862f7c Adjust protobuf configuration. 2021-05-07 23:58:41 +02:00
David Given
5f077762d5 Hopefully set the right protoc compiler? 2021-05-07 23:45:43 +02:00
David Given
1b0ec50711 Add lots more protobuf machinery. 2021-05-07 23:38:31 +02:00
David Given
22f320f1c4 Merge from master. 2021-05-07 22:27:45 +02:00
David Given
9949476584 Merge pull request #252 from davidgiven/40track
Add support for possibly writing 40-track disks on an 80-track drive.
2021-05-07 22:21:02 +02:00
David Given
1b5b170557 Add support for possibly writing 40-track disks on an 80-track drive. 2021-05-07 22:01:20 +02:00
David Given
925a3a4bdb Merge pull request #251 from davidgiven/40track
Better 40-track support.
2021-05-07 21:16:19 +02:00
David Given
720fe9f95f Make sure that places which use usbSeek() honour --40-track. 2021-05-07 20:45:46 +02:00
David Given
7c4f8e1443 Added documentation on 40-track disks and drives. 2021-05-07 20:40:22 +02:00
David Given
79d24dff46 Add boilerplate for a protobuf definition. 2021-05-07 20:05:50 +02:00
wybren1971
928435a31d Added error catching to exit gracely on an error 2021-05-07 15:59:03 +02:00
wybren1971
26ac92eaa3 Standardized messages in classes 2021-05-07 13:25:25 +02:00
wybren1971
2974c08b08 Fix bug in printing sectorskew to screen 2021-05-07 13:11:43 +02:00
wybren1971
5a7b0b3050 Warning was commented out. Fixed 2021-05-07 13:03:41 +02:00
wybren1971
cc2d9bbdd1 Bugfix wrong sectorsize used 2021-05-07 12:54:54 +02:00
David Given
e912152784 Common out the high-density flag too. 2021-05-07 00:27:10 +02:00
David Given
d00681f623 Make the new 40-track flag common between source and sink. 2021-05-07 00:21:13 +02:00
wybren1971
52942c3d2a Fixed a bug with sectorskew for 1.44 diskettes 2021-05-06 17:40:57 +02:00
wybren1971
dedabdd8d8 Added possibility to write IMD images 2021-05-06 14:26:21 +02:00
wybren1971
3061499860 Small bugfixes and added a check on the sectorskew 2021-05-06 14:18:40 +02:00
wybren1971
20f18755ed added an example in the comment 2021-05-06 14:17:37 +02:00
wybren1971
333f2aba54 added a bool for FM or MFM coding 2021-05-06 14:17:05 +02:00
wybren1971
1cb673ed80 Added a bool for knowing FM or MFM use 2021-05-06 14:16:32 +02:00
wybren1971
57e0bc784a Fix bug in startsectorID 2021-05-01 09:12:38 +02:00
wybren1971
425afa93da Better Comment reading with string 2021-04-30 21:45:22 +02:00
wybren1971
259b2cebc7 Added optional cylindermap and sector head map 2021-04-30 20:33:39 +02:00
wybren1971
06589826c8 Small Tweak in gap3 value 2021-04-30 19:43:57 +02:00
wybren1971
2245cd982a Leave destination as is 2021-04-30 19:38:02 +02:00
wybren1971
065b50769f Update IMD writing based on geometry in image 2021-04-30 15:26:06 +02:00
wybren1971
d1e99852bc Merge pull request #2 from davidgiven/master
Update fork with master commits
2021-04-21 08:29:54 +02:00
David Given
bf8f6ae687 Add imagereader offset and step modes, and hardware forty-track modes. Not sure
this is the right way to do it.
2021-04-21 00:41:36 +02:00
David Given
4ad6805ea1 Change to house style. 2021-04-19 20:29:55 +02:00
David Given
4f4e3f0b89 Qualify LIF options, to allow multiple LIF formats. 2021-04-19 20:29:48 +02:00
David Given
b51f2c1ec8 Fix C++ standard issue. 2021-04-19 20:27:44 +02:00
David Given
1bec06fd75 Merge branch 'master' of https://github.com/wybren1971/fluxengine into wybren1971-master 2021-04-19 19:28:53 +02:00
David Given
451d2e2d1d Merge pull request #244 from davidgiven/visualiser
Update the visualiser to use AGG.
2021-04-19 16:56:47 +02:00
David Given
9cee12f9f4 Add index mark and sector alignment. 2021-04-19 15:31:24 +02:00
David Given
f5d6011a77 Merge from trunk. 2021-04-19 13:25:27 +02:00
David Given
64b2ff19ea Mark the new releases as not being prereleases. 2021-04-19 13:25:10 +02:00
David Given
9c17a64773 Don't fail the deletion if no assets exist. 2021-04-19 13:15:38 +02:00
David Given
69c877ff66 Adjust wording. 2021-04-19 12:58:21 +02:00
David Given
ac557ffedc Update git checkout action. 2021-04-19 12:24:04 +02:00
David Given
b1e41bc583 I think that the git tag command doesn't like newlines in the message. Fix. 2021-04-19 12:12:24 +02:00
David Given
a144395ec9 Syntax fix. 2021-04-19 11:49:07 +02:00
David Given
0cf9d05489 Try a different tag action. 2021-04-19 11:47:39 +02:00
David Given
0e6c0a41d0 Merge pull request #245 from davidgiven/github
Switch to github CI for Windows rather than Appveyor.
2021-04-19 11:30:46 +02:00
David Given
8a83652d08 Add the github autorelease script. 2021-04-19 11:24:06 +02:00
David Given
91ee72a8d6 Need to use the C++ compiler. 2021-04-19 01:29:06 +02:00
David Given
91b1c8c13c Getting there. Configure ar. 2021-04-19 01:17:20 +02:00
David Given
26effeabe6 More path tweaking. 2021-04-19 01:13:55 +02:00
David Given
611c9740ed Getting there. More path adjustment. 2021-04-19 01:08:26 +02:00
David Given
2a048c3228 Add diagnostics. 2021-04-19 01:04:19 +02:00
David Given
f4fd83d999 Adjust package list again. 2021-04-19 00:55:42 +02:00
David Given
cf68585808 Remove the appveyor script. 2021-04-19 00:52:12 +02:00
David Given
9f9e926cff Try explicitly building with the mingw compiler. 2021-04-19 00:51:49 +02:00
wybren1971
9dc0dd75fd Fix bug in ibm-preset-720 2021-04-18 22:51:10 +02:00
David Given
9f285710f8 Update msys version. 2021-04-18 22:08:35 +02:00
David Given
ee1c448327 Try to set up build correctly. 2021-04-18 22:00:14 +02:00
David Given
e85bf1713e Enable github CI for Windows. 2021-04-18 21:55:55 +02:00
David Given
7341c71277 Cleanup. 2021-04-18 21:42:10 +02:00
David Given
d579863311 Try and make build on Windows. 2021-04-18 21:41:32 +02:00
David Given
c79cfc19aa Update the visualiser documentation. 2021-04-18 21:05:22 +02:00
David Given
997a6be267 Update the visualiser to use the new bitmap library instead of emitting SVG. 2021-04-18 21:01:21 +02:00
David Given
762bb3006d Move the visualiser code into fe-analyselayout. 2021-04-18 13:09:49 +02:00
wybren1971
07f2bd8cab small fix in tracks info 2021-04-18 09:49:06 +02:00
David Given
daf83db9b3 The standalone visualiser functions. 2021-04-18 01:21:52 +02:00
wybren1971
55c6e19af4 add IMD file reader 2021-04-17 19:32:03 +02:00
wybren1971
9cadc94c5a add IMD file reader 2021-04-17 19:12:52 +02:00
wybren1971
cacdf9ef56 add IMD file reader 2021-04-17 19:06:56 +02:00
wybren1971
a3042fc6c0 typo fix 2021-04-17 18:53:11 +02:00
wybren1971
3efd492525 add IMD file reader 2021-04-17 18:52:41 +02:00
wybren1971
55a5cbc356 Merge pull request #1 from davidgiven/master
update fork
2021-04-17 17:49:52 +02:00
wybren1971
2887b024ab add HP-LIF preset 2021-04-17 17:01:49 +02:00
wybren1971
917303edb9 add IMD file reader 2021-04-17 16:55:42 +02:00
wybren1971
c712c15a30 add IMD file reader 2021-04-17 16:55:25 +02:00
wybren1971
0c541db8e0 add IMD file reader 2021-04-17 16:55:07 +02:00
wybren1971
603b1520d7 add IMD file reader 2021-04-17 16:49:41 +02:00
David Given
c7eb8ad5c8 Add CSV reader and skeleton layout analyser. 2021-04-17 00:08:06 +02:00
David Given
0b285aa7f4 Remove the visualiser integration from the reader. 2021-04-16 21:38:26 +02:00
David Given
e8665bd00c Build a cscope cross-reference (if cscope is installed). 2021-04-16 21:38:08 +02:00
David Given
fb4eaa4332 Merge pull request #241 from wybren1971/patch-1
Update fe-writeibm.cc
2021-04-13 01:14:49 +02:00
wybren1971
874a9eae76 Update fe-writeibm.cc 2021-04-12 21:39:40 +02:00
wybren1971
8230520956 Update fe-writeibm.cc 2021-04-12 20:40:23 +02:00
wybren1971
66da9675f1 Update fe-writeibm.cc
Added a HP-LIF preset to write HP-LIF floppy disks for old HP analyzers in need of LIF formatted disks with 76 tracks and sectors of 1024 bytes.
2021-04-12 20:37:19 +02:00
David Given
61ff48c005 Merge pull request #236 from erichelgeson/eric/typo
Fix minor typo
2021-02-20 22:02:46 +01:00
Eric Helgeson
5fc8a1e52a Fix minor typo 2021-02-20 14:42:02 -06:00
David Given
df1ac74069 Merge pull request #233 from davidgiven/ibm
Refactor the IBM readers and writers.
2021-02-16 23:47:50 +01:00
David Given
91f718bf38 Fold the Atari ST writer into write ibm. Update documentation. 2021-02-16 23:24:41 +01:00
David Given
46e987e393 Refactor the IBM readers so they just set flags and readibm.cc is doing all the
work.
2021-02-16 23:07:25 +01:00
David Given
a59b4f7be7 Document writing 1581 disks. 2021-02-16 21:04:40 +01:00
David Given
ca66e3c35c Merge pull request #218 from hpingel/ibm_enc_swapsides_preset_1581
Adding preset for Commodore 1581 (cbm1581) to IBM writer frontend
2021-02-16 21:01:19 +01:00
hpingel
31e2ad8cba Enforce usage of Greaseweazle firmware v0.24 after adapting CMD_READ_FLUX and CMD_WRITE_FLUX 2021-01-21 23:44:13 +01:00
hpingel
320f32895a adding preset cbm1581 to ibm writer frontend 2021-01-18 23:08:33 +01:00
David Given
d4db131d3c Merge pull request #217 from davidgiven/writes
Update sequencer to not lose one tick from every non-zero interval.
2021-01-18 11:22:02 +01:00
David Given
27c2c9045e Update sequencer to not lose one tick from every non-zero interval. 2021-01-18 00:27:00 +01:00
323 changed files with 13507 additions and 4677 deletions

View File

@@ -1,42 +0,0 @@
version: '{branch}.{build}'
clone_depth: 1
skip_tags: true
image: Visual Studio 2019
environment:
MSYSTEM: MINGW32
init:
- git config --global core.autocrlf input
install:
- set PATH=c:\msys64\mingw32\bin;c:\msys64\usr\bin;c:\msys64\bin;%PATH%
- echo %PATH%
- pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
build_script:
- make
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
artifacts:
- path: fluxengine.zip
name: fluxengine.zip
deploy:
release: FluxEngine Windows client version $(APPVEYOR_BUILD_NUMBER)
description: >
This is an automatically built version of the FluxEngine Windows client
which is generated whenever a significant checkin has happened. It's had
no testing whatsoever.
To use, download it, put it somewhere, and then run it from a cmd window
or other command line shell.
provider: GitHub
auth_token:
secure: dfJjj7fWCoDUz+Ni11OcNPB/U3TNJFwNA2AsL++ChFjniUsZLlC6SDWHiL/t4FZo
artifact: fluxengine.zip
draft: false
prerelease: false
on:
branch: master

View File

@@ -10,32 +10,45 @@ jobs:
with:
fetch-depth: 1
- name: apt
run: sudo apt update && sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build
run: sudo apt update && sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build protobuf-compiler
- name: make
run: make
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: brew
run: brew install sqlite pkg-config libusb ninja
run: brew install sqlite pkg-config libusb ninja protobuf
- name: make
run: make
# build-windows:
# runs-on: windows-latest
# steps:
# - uses: numworks/setup-msys2@v1
# with:
# path-type: inherit
# - uses: actions/checkout@v1
# - name: pacman
# run: |
# msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
# - name: build
# run: |
# msys2do make
build-windows:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: MINGW32
install: >-
make
ninja
mingw-w64-i686-libusb
mingw-w64-i686-sqlite3
mingw-w64-i686-zlib
mingw-w64-i686-gcc
zip
mingw-w64-i686-protobuf
vim
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: build
run: |
make

63
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,63 @@
name: Autorelease
on:
push:
branches:
- "master"
jobs:
dev-release:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: MINGW32
install: >-
make
ninja
mingw-w64-i686-libusb
mingw-w64-i686-sqlite3
mingw-w64-i686-zlib
mingw-w64-i686-gcc
zip
mingw-w64-i686-protobuf
vim
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: build
run: |
make
- name: zip
run: |
zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
- name: date
run: |
echo "RELEASE_DATE=$(date --rfc-3339=date)" >> ${GITHUB_ENV}
- name: tag
uses: EndBug/latest-tag@latest
with:
tag-name: dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: delete-old-assets
uses: mknejp/delete-release-assets@v1
with:
token: ${{ github.token }}
tag: dev
assets: |
fluxengine.zip
fail-if-no-assets: false
- name: release
uses: softprops/action-gh-release@v1
with:
name: Development build ${{ env.RELEASE_DATE }}
files: |
fluxengine.zip
tag_name: dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,246 +1,246 @@
:4000000000800020110000003110000031100000064A08B5136843F020031360044B1A6803F53F5302331A6001F020F8E8460040FA46004010B5054C237833B9044B13B18B
:400040000448AFF300800123237010BD6881FF1F00000000B8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000051
:400080006C81FF1FB8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1FCC
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F0000800E4BDA681AB901215F
:4001000019745A7410E05A7C1A741A7C0AB1002207E059699A69D8688A1A82428CBF002201225A745A699A611B7C13B1002002F043B970478881FF1F10B5C4B2204601F054
:4001400057F90128FAD110BD08B572B60F4B0F495A6901325A61DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002B0CBF02230023002814BF184660
:4001800043F0010002F080FE62B608BD8881FF1F38B50446C5B2284602F0B0F8062002F0CDFA44F00200C0B202F0A8F8062002F0C5FA284602F0A2F8BDE83840062002F0C2
:4001C000A7BA10B5642402F093F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F003F9012805D0204601F01CFA2846FFF79FFF204601F000F9AD
:40020000314605460246204601F0BCF9204601F0EFF80028FAD1284670BD000038B5044D0024285D013402F039FA402CF9D138BDAC81FF1F08B502F053FC002002F05CFCBD
:4002400002F06EFC02F078FC80B208BD10B50446012002F06BF8642002F05AFAFFF7EAFF2080002002F062F8642002F051FAFFF7E1FF608010BD08B502F05EFD002002F02B
:4002800067FD02F079FD02F083FD80B208BD10B50446FFF796FF322002F03AFAFFF7EBFF20800120FFF774FF322002F031FAFFF7E2FF608010BD0FB400B593B014AB53F835
:4002C000042B402102A8019302F0DEFE02A802F07AF802F084F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF62782146BDE81040042001F0D0B8B5
:40030000CC38000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0D8FF002002F06BFD002384F864304E
:4003400010BD00BF8881FF1F38B5104D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F865302279094B1A71A378002B14BF0220012002F04AFDE07802F02E
:4003800041FD2079BDE8384002F078BD8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7D1FF012001F09CFF4FF47A7002F0AEF984F86A506369E366012384F84D
:4003C0006430BDE8384002F0E1B900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF767FFFFF7EBFE0120002384F86A00236702F0A1F92A46216F184872
:40040000FFF759FF144E0027236F9D4216D001F06FFF00B13767236F9D4205DD0120FFF7B7FE336F013305E005DA0020FFF7B0FE336F013B336702F0A9F9E5E7322002F047
:4004400067F92A2DCCBF0020012002F023FDBDE8F8400448FFF72FBF8881FF1FD9380000E0380000FD3800002DE9F04F99B062B602F0F6F99D49042002F01AFA9C4801F0D9
:4004800043FF9C4802F0E6FC9B4801F077FF02F0C7FB02F099FA002002F0BAFC01F092FF0221002000F05AFF944C012001F0D2F8002384F86730FFF76DFFFFF782FE84F8A3
:4004C0007400FFF72FFF012384F86730FFF762FFFFF777FE84F87500FFF724FF884B94F87400884994F875202546002A14BF0A461A46002808BF19468348FFF7DCFE032177
:40050000084602F023F9264602F040F994F8643043B1EA6E6B699B1A41F28832934201D9FFF700FF00F052FF18B97848FFF7C3FE04E000F051FF0028F7D10BE000F046FF85
:4005400010B902F023F9F9E77148FFF7B4FE032001F06CF8032000F04BFF0128D4D16D48FFF7F2FE6C490320FFF738FE94F876106A48FFF7A0FE94F87630023B142B00F229
:40058000D483DFE813F01500D2031E00D2032400D2034F00D2037500D203D700D203BF01D2030603D2032A03D2033103D2034B0303238DF820308DF821300F238DF8223084
:4005C00028E394F87800FFF703FF554B1FE3FFF7E1FE002323746069227C02F0FF0132B96B691B1AB3F57A7FF6DD0B4611E083B10022174696F878107069277494F810E007
:40060000BEF1000F02D16B691B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F7E20220FFF787FD4FF000080DF1200A02F0ABF84FF480790027C9EB080303
:40064000DA1907F80A200137402FF9D10220FFF773FD3A465146022000F022FFB9F10109EBD108F10108B8F1400FE2D12D4B38E04FF0010A4FF000080DF1200B02F086F844
:400680004FF0000959460120FFF7A8FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B464A461E48FFF702FE4FF0000A0137402FEBD109F101091B
:4006C000B9F5807FDED108F10108B8F1400FD5D151461648FFF7EFFDBAF1000F00F01A81134B1B8807A8ADF81C3094E249010000F900000091000000C50000008881FF1F0B
:400700000F3900000B390000123900002A3900003D390000ED81FF1FFE81FF1F47390000BC380000BE3800005639000072390000C0380000206FFFF74BFE94F8780001F081
:40074000F5FD94F8780001F0D9FD02F009FCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0F7FB0220FFF7DCFCF9
:40078000012141F6FF734FF48042084602F046FB84F8B60001F068FF08F807000137402FF8D1DFF8B0A200270AF195091FFA89F80137402F14BF3A4600221AF8010F2244C0
:4007C000062392F82420402101F082FF424646F24E419AF8000001F08DFF08F14008402F1FFA88F8E4D196F8793053B196F87C30F36000233374237C002BFCD000233374E1
:40080000F36000234FF0FF32236062602372236894F8B600234493F8241001F0DDFE94F8B60001F09BFE012194F8B60001F06EFE2368002BFCD0002398467360D6F814A0D4
:40084000012701F0A3FF6369B4F87A20CAEB030393420DD367B1042195F8B60001F0C8FE94F8B60001F0D4FE0028F9D107463072237AFBB96A682B689A4202D1002FE0D1D9
:4008800018E00220FFF758FC6968402209EB8111022000F005FE6A68674B01321340002BBEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF73EFC0022114687
:4008C000022000F0EDFD0220FFF736FCFFB2FFF7A5FC002001F012FD37B15848FFF7EBFC0220FFF70FFD06E0554B08A81B88ADF82030FFF7F5FC227C4146237A5148FFF7BB
:40090000DAFC15E25048FFF7D6FCD4F87A7017F03F0701D0032009E2286FFFF759FD95F8780001F003FD95F8780001F0E7FC012001F002FD02F014FB444BDFF814811A7857
:4009400042F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F003FB01214FF4804341F6FF72084601F0E9FC85F8B60001F077FE08F8070001378C
:40098000402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F82420402101F090FE414646F24B5299F8000001F09BFE08F1FF
:4009C0004008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF0010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03B
:400A00003F026068B8BF013282426FD02BB1227A002A7AD12A7C002A77D12068049A059302EB8010BAF1000F16D040223F2102F0F7FA1CE09E6400403F0000807C390000C9
:400A4000C238000096390000A93900009B650040AC81FF1FAB81FF1F014601370120FFF7BDFBC7EB0903D3F1000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F0BB
:400A80003F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F080FC85F808806B6895F8B6002B4493F8241001F092FD95F8B60001F050FD012195F8BF
:400AC000B60001F023FD95F87E30EB6085F81080237C002BFCD04FF00008012086F8108001F06AFC404601F027FC00E023B1237A5BB92B7C4BB90123626842453FF477AFC1
:400B00000BF1010BD5F8048071E701F04FFC012001F012FC002001F04FFC042194F8B60001F066FD94F8B60001F072FD80460028F8D196F8B60001F0FFFC337C327A029318
:400B4000012303920193CDF800A05B463A4649467C48FFF7B0FBC6F80C80BAF1000F0BD0FFF75CFB002001F0C9FB237A63B17648FFF7A1FB0220D9E0B945F1D07349012035
:400B8000FFF72CFB0137F7E77148FFF794FB714B3DE094F8780001F0C9FB206FFFF718FC6D48FFF788FB94F87930E36000232374237C002BFCD0012001F0FEFB00233374CE
:400BC000237C002BFCD0002001F0F6FB00236348F360FFF770FB624B19E0002084F86A00FFF7F6FB5F4B12E094F8743023B195F875200AB985F8782094F875201AB113B9F6
:400C0000012385F878305848FFF79EFB574B1B88ADF8203008A8FFF763FB89E0FFF782FB02F07CF8002002F01FF82A2701F04AFF002001F0EDFE3A46002108A802F0F0F9E0
:400C400017238DF820308DF8217001F09FFD002001F048FB002002F0DBF8C82001F058FD0DF12200FFF7F2FA0DF13600FFF70FFB01F08CFD012002F0CBF8322001F048FD4D
:400C80000DF12600FFF7E2FA0DF13A00FFF7FFFA012001F027FB4FF4967001F039FD01F075FD0DF12E00FFF7D1FA0DF14200FFF7EEFA002001F016FB4FF4967001F028FD84
:400CC00001F064FD022002F0A3F8322001F020FD0DEB0700FFF7BAFA0DF13E00FFF7D7FA012001F0FFFA4FF4967001F011FD01F04DFD0DF13200FFF7A9FA0DF14600FFF756
:400D0000C6FA002001F0EEFA4FF4967001F000FD01F03CFD002002F07BF8002384F86A3001F07EFF01F050FE74E70120FFF7EAFA032000F07BFC0E48FFF7BDFAFFF7E4BBB6
:400D40003F000080B3390000E33900004092FF1FED390000C4380000F5390000033A0000C6380000C8380000FE81FF1FCA380000103A00002DE9F04172B6884B61221A70F9
:400D8000A3F5F06301221A801924854A9C7092E803008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F25122A6
:400DC00023F8022C33784FF4F07003F0010343EA450502F0BDF8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F893
:400E0000032C0422DA716D4A6D4C13786D4E43F00103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FBAD
:400E4000056300219A881868013502F0E9F8072DF5D15E485E4E002550F8041F05F1105303F1480221F0FF074933C9B20B4452005B0002329A4206D012F802EC12F801CC0C
:400E80000EF807C0F5E7B0420D44E5D1514A002313609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F002031374F7
:400EC0002378A2F5863243F040032370413A137843F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C33B
:400F00001A80404A083307CA03C31A803E4A3F4BA2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C329F
:400F40001B091170F6B2537045F003033B7046F0030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F0C7
:400F8000DDFB284B88F8006044223D70264D1A7094E80F0007C52B80BDE8F081004800405C0900480F010049A146004025420040224200400440004006400040A2430040AF
:400FC000A0430040153A0000E8460040FCFFFF478C0000480076004064090048F8460040207600406809004828760040035001401C090048C051004028090048300900485A
:401000003C090048480900483251004054090048CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0C9FF03680C2B00D1FEE7FEE79D
:40104000084908B50B68084A1844821A802A01DC086005E001F0B8FF0C2303604FF0FF33184608BDCC80FF1F9093FF1F80B51148114B0025C0B1A3F1100192C92246043933
:40108000161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F094FFFFF7DCF9FEE700BF01000000E43B0000124A134BF0
:4010C00010B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8CA
:40110000104001F0EDB900BF0004FA050CED00E014ED00E0000000000080FF1F31100000BC760040C080FF1F08ED00E0F8B501F017FF4B4A01271378022643F001031370EA
:40114000137C484C43F001031374474B02F5E3521F700B3203F8946C1378054603F07F031370002001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B4325
:40118000237001F0DBFA282001F0D8FA384B30461A7802F07F021A701A7802F0BF021A7023783343237001F0C9FA2378314A43F0040323700023137053702F4AFF2199544A
:4011C0000133092BFBD1284601F0CEFE0721172001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052032
:4012000001F0E4FA2049052001F0D2FA0721062001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162047
:4012400001F0C4FA1449162001F0B2FA07210C2001F0BCFABDE8F84010490C2001F0A8BAA5430040944300409D60004012600040F851004084600040B592FF1FFB1A00008A
:4012800035190000F91A00002D1A0000591A0000891A0000C11A0000011B0000751B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A1370D9
:4012C0001F4A13701F4A13701F4B4FF400021A604FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7851
:4013000002F0FE0202E01A7842F001021A70114B03221A70802203F8202C012001F018FE0D4B04221A7010BDD092FF1FD692FF1FD492FF1FD592FF1FD192FF1FC092FF1F97
:40134000D392FF1F4893FF1F00E100E09E6000409C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF8092FF1F7A
:401380000A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047D692FF1FD492FF1FD592FF1FD192FF1FC092FF1F05
:4013C000D392FF1F4893FF1F28600040014B1878704700BFD592FF1F044B1A7802F0FF001AB118780022C0B21A707047D492FF1F024A0C2303FB002040787047DC92FF1FB8
:40140000431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044670
:4014400019BF8AB24FF480438BB24FF48042032D18D8DFE805F002070C110021084601F01BF80DE00021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF3E
:40148000054B1855EDB2072D03D801F0EDF8034B185538BDDC92FF1FAC92FF1FB592FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB274
:4014C000C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FBBF
:40150000047200215170B9F1000F28D03DB31B4F385D01F011F811232946FE2218F8040001F0D6F806F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F004
:401540004BF80121385D00F0E1FF735D43F002037355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFDC92FF1FFC5F0040B592FF1FAC92FF1FD4
:40158000706000402DE9F047044615468846002940D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF893
:4015C000040000F0B9FF1223FE222946305D01F07FF807F5C0411FFA88F27831305D01F089F8DFF84490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0F1
:40160000020307F8093017F8093003F0FD0307F8093002E00D4600E000252846BDE8F087B592FF1FAC92FF1F70600040431E072B0AD8064A0C2303FB002300225A705A7991
:40164000034BD2B200011A54704700BFDC92FF1FFE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488E7
:40168000D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F23237022
:4016C000022301E0022323701370094B1870087030BD00BF4C93FF1F4893FF1F00600040C492FF1FC192FF1FD692FF1FD292FF1F4993FF1F074B02221A70074B80221A70AE
:40170000064B0F221A70064A00231370054A012013707047D692FF1FD292FF1FC192FF1F4893FF1F4993FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80CC
:40174000124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A70DF
:4017800030BD00BF296000404C93FF1F00600040C492FF1F4993FF1FD292FF1FC192FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFD692FF1F52
:4017C000C192FF1FD292FF1F4993FF1F054B9A683AB19A68044910709A680988518000229A607047C492FF1F4C93FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A1E
:40180000137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F012FC04E001F0A5FB01E000F046FD10B9034B03221A7008BD00BF28600040C192FF1FC0
:401840000060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4C93FF1F4893FF1FD692FF1FC192FF1F08B50C4B1B78DBB25E
:40188000042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFD692FF1FC192FF1F08B5054B002201201A70FFF7B6
:4018C00085FF034B03221A7008BD00BFD692FF1FC192FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFA5
:40190000C092FF1F08600040D692FF1FC192FF1F074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047D692FF1F38B51D4C2378DBB2DD0634D51B
:4019400018060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13705F
:401980001278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040C192FF1FD292FF1F4993FF1F29600040AD
:4019C000054A00231380054A916819B191680B7092685380704700BF4C93FF1FC492FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FFAC
:401A0000094AD1799379028843EA012392B2934238BF0380FFF728FE012008BDC492FF1FD692FF1FD292FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A07
:401A40001EBFDA7B82F08002DA7301225A7370470B600040DC92FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BFFE
:401A80000B600040DC92FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040DC92FF1F78
:401AC0000B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040DC92FF1F7047FFF741BC000081
:401B0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4C35
:401B4000EDB204F80E50840706D507FB036425792D0658BF84F801C090700133DBB24908D7E7F0BD9F600040DC92FF1F70600040FE5F004000F0ACBC70B50446184B88B021
:401B800003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB12223237001AD03232846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F053
:401BC0000F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BD3F3A00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02580B
:401C00001401234418705F70164998F805902144B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71A2
:401C4000CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE8F08300BFDC92FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EB6F
:401C8000C003586900207047D092FF1FA03A00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C260027FE
:401CC0004FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0A6
:401D0000B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEAA3
:401D400002191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F8FE
:401D80000790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BFD592FF1FDC92FF1FD192FF1FFC5F004070600040C292FF1F08B5064B187801384A
:401DC000C0B2FFF753FF20B143681B7900EBC300406908BDD592FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD37013819372537031
:401E00005371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010273
:401E400002F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B34576
:401E80001AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092D89
:401EC000C3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D38012890131134463
:401F00009BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE8F88F00BFDC92FF1FC292FF1F4A93FF1FD592FF1FD392FF1FD892FF1F114B1B7903F07F035A1E072A3C
:401F400019D80F490C2202FB031291781B0141F0010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF0060004065
:401F8000DC92FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F013
:401FC000FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040DC92FF1FFC5F004010B50D4BA5
:402000000D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDD592FF1F00600040C292FF1FB6
:402040004A93FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F20581DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B9C
:4020800009D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF7EE
:4020C0008FBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF1346634A917881B106E0187801320028F1D018780344EAE764499278097C914203D180
:402100006248FFF739FD614B1A78002A00F0AD801A78228018E0BDE8384000F029BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BC3
:40214000D2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE7012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F0377608
:402180004F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A7891425FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF793
:4021C0001DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B47
:4022000033D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1B7
:40224000154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BF00600040C492FF1FD092FF1FA03A000049
:40228000043B00008C3A0000773B00006893FF1FDC92FF1F8192FF1FD392FF1FD592FF1FC292FF1FC092FF1FD492FF1FD192FF1F4A93FF1FD792FF1F074B1A78120609D546
:4022C0005B78012B06D1054B054A5A6012781A80FFF786BB0020704700600040C492FF1F643A0000014B1870704700BF7A640040014B1878704700BF6B650040014B18702D
:40230000704700BF79640040064A0123136002F688321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A650040014B1870704700BF89
:402340007865004073B515461E460B4C05230022019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A04232E
:40238000136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF30
:4023C0007E640040704738B505460078012428B100F066FD285D0134E4B2F8E738BD08B50D2000F05DFDBDE808400A2000F058BDF7B516461F460B4C002303250193009359
:402400000A4601462846257000F00EF93A463146207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257030
:4024400000F0F2F83A463146207800F0ADF82946207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146DE
:40248000207800F091F80221207800F07BF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F8022120782A
:4024C00000F060F8207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFCC
:40250000E480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001EA
:40254000C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F001034354002070470120704740
:402580001070004017280BD8064A0001835C490003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBF85
:4025C000C00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBFDC
:402600001143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC4422
:40264000D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F079FC0A4A5378182B0AD91478013B5370E30003F1FB
:40268000804303F5F0431B78137000E0FF2400F06BFC204610BD00BFE480FF1F030610B5044611D400F05CFC084AE300117803F1804303F5F0431970537814700133537094
:4026C000BDE8104000F050BC10BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5D2
:40270000F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F017FCA4F50044F6E7034B58686043BDE8384000F00DBC00BFEC80FF1F024B1B7AB4
:40274000584300F005BC00BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F060
:4027800010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230009
:4027C00042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040334
:4028000013600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD8492FF1F8D28000010E000E0EC80FF1F14E000E018E000E0024A136843F0020334
:402840001360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8492FF1F024B186891
:40288000C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8892FF1F024B03EB80035868596070478492FF1F1F
:4028C000134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA024382
:402900000A4A4360127843EA02634360704700BF0301004904010049EC460040020100490101004900010049050100490601004910B500F015FB204A044613780A2043F07D
:4029400002031370137C43F00203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F837
:40298000012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D8BAAB4300400E59004026
:4029C0002F5B004080E200E008B500F0C9FA0F4A137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE0388
:402A00009371BDE8084000F0AFBA00BF08590040044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B2042812
:402A4000137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040503A00005293FF1FB7
:402A80005493FF1F5893FF1F08B5102000F0A6F907210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BD812B0000095900402E
:402AC0005093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABFA092FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0F1
:402B0000010403D10028F9D0002404E02046FFF715FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF5893FF1FA25B00400E4A13881BB223B1D5
:402B400011880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475293FF1F5493FF1F5093FF1F29
:402B80007047000010B500F0EBF9214A044613780A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F0030343
:402BC0001370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074BEE
:402C000008222046BDE810401A6000F0ADB900BFAB43004006590040275B004080E200E008B500F09DF90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C9E
:402C400003F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F083B900BF00590040044A137803F03F0343EA8010C0B21070704700BF0059004016
:402C8000082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBDD
:402CC000F1F305490B60054B1A807047025900405A3A00005E93FF1F6493FF1F5C93FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F035
:402D000006031370FFF7BCFF034B00221A8008BDD92D0000015900406093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BFA192FF1F044B1A7802F0FB02D6
:402D40001A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B88108822
:402D8000181A00B2704700BF5C93FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF86
:402DC0005B42134493FBF1F000B270475E93FF1F6493FF1F6093FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF9A
:402E000006410040014B1870704700BF78640040014B1870704700BF7965004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7AB
:402E4000B3FB02212078FFF79DFB207802B070BDFC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B18708C
:402E8000704700BF7C640040014B1870704700BF7B640040014B1870704700BF7F640040FEB5494652465B460EB40746244909688A46244A12682448022100F071F803009B
:402EC00020480068C018204900F06AF8143883460121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B705846AA
:402F000000F026F80136072EF2D9002001300138013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF3008039
:402F4000D480FF1FF880FF1F00C20100000000000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D181
:402F800013790C2103F07F02044B01FB02339B7A00E013790020704700600040DC92FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB0B
:402FC00011217047704700BF014B1868704700BF6081FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F0DEFD084B094C1E46E41AA41000251E
:40300000A54204D056F8253098470135F8E770BDBC3B0000BC3B0000BC3B0000C43B000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B6026
:403040004FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B0022F6
:403080001A701BB030BD000007B5009313460A46014603480068FFF7CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD025685F
:4030C000096902236F1A656905EB450595FBF3F57B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A462B
:4031000000F098FB064670B92169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF4646494632462068B9
:4031400000F0B3FAA36800209B1BA36023681E442660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F8D4
:403180000030CDE040236B610023099320238DF82930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF7EA
:4031C00071FF013000F0A780099B4B4409933B78002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46E7
:40320000801A06FA00F018430490EFE7D90644BF20228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F002006C
:40324000079204900BE027463B780134303B092B03D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E010
:40328000002319460593781C0A2407463A780130303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B01371843049053
:4032C000397806221B487E1C8DF8281000F0D4F988B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A937
:40330000404600F093F8B0F1FF3F824603D0099B5344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BF8B3B0000913B0000953B000000000000A5300000D1
:403340002DE9F04791461F460A698B6806469342B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F00605F2
:4033800010D104F1190A07E00123524639463046C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F84E
:4033C00045102244023382F8431003E04FF0FF30BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500252C
:403400009342C4BF9B1AED184FF000091A344D4509D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9BE3
:4034400001F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B880A0
:403480000A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F104011960E2
:4034C00002D0B2F9003000E01368002B3CDA2D225B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A40
:4035000014BF0A2208221BE078225A4984F845202268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F020022260102266
:40354000002084F8430001E049490A226568002DA56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D131
:40358000082A0BD12368DA0708D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F1040087
:4035C0001860136801D0198000E0196000232361754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F8A9
:403600004330CDF800803B4603AA21463046FFF797FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046F8
:40364000C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F0833F3A00009C3B000010B5C9B202449042CF
:40368000034605D01C7801308C42F8D1184610BD002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21A41
:4036C000D34204D013F8014D01F8014DF8E710BD994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B912
:403700006360146030E0A3420DD92268A018834201BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D1F2
:4037400010685268014419605A600DE002D90C232B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA892FF1F70B5CD1C25F003051A
:4037800008350C2D38BF0C25002D064601DBA94202D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B6896
:4037C0001360636018BF0C4615E00C464968E9E7174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B005E
:40380000231D20F00700C31A0ED05A42E25070BD211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA892FF1FA492FF1FF8B5074615460E4621B91146D1
:40384000BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD40
:4038800038B5064C0023054608462360FDF7D8FB431C02D1236803B12B6038BD8C93FF1F7047704751F8040C0028BEBF091851F8043CC018043870470000000005020902A2
:4038C0000B020D020F021102130215027265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E08
:403900006973686564207365656B00796573006E6F00647269766520303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E00555342F3
:4039400020726561647900636F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E646544
:403980007272756E206166746572202564207061636B65747300636F756E743D256420693D256420643D256400636D645F777269746500703D25642063723D256420637729
:4039C0003D256420663D256420773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E21007375636365737300737461727420657261736912
:403A00006E670073746F702065726173696E670069646C650000510040100040510040300000000140001000140140000800400140000A004C0140000200500140200030F9
:403A400031323334353637383941424344454600000100000004000000100001000000040000001028000000000104000100000000000000000157494E5553420000303043
:403A800030303100000000000000000012034D005300460054003100300030000100000001000000A83A000001000000773B0000000000000000000001000000C03A000003
:403AC00001000000493B000004000000E23A0000000000000000000000000000E03A0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000F7
:403B0000FF00020304030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0003
:403B400067006900650073000009022E0001010080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080921
:403B800012006E0100020180014300232D302B2000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000094
:403BC000BD100000F8B500BFF8BC08BC9E46704735000000E83B0000C880FF1FA00000002812000000000000000000009093FF1FFF000000675000400C0000000700000097
:403C0000FFFFFFFF7F8000003F0000000000007D00FA0000400000000090D003FF000000000000000000000000000000000000000000000000000000000000000000000031
:403C4000893B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:403C80000081FF1F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065
:4000000000800020110000004910000049100000064A08B5136843F020031360044B1A6803F53F5302331A6001F02CF8E8460040FA46004010B5054C237833B9044B13B14F
:400040000448AFF300800123237010BD6881FF1F00000000D0380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000039
:400080006C81FF1FD0380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1FB4
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080114BDA68196919B9FD
:4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F04AB9704700BF6C
:400140008881FF1F10B5C4B2204601F05DF90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002B0F
:400180000CBF02230023002814BF184643F0010002F086FE62B608BD8881FF1F38B50446C5B2284602F0B6F8062002F0D3FA44F00200C0B202F0AEF8062002F0CBFA2846F9
:4001C00002F0A8F8BDE83840062002F0ADBA10B5642402F099F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F009F9012805D0204601F022FA20
:400200002846FFF79FFF204601F006F9314605460246204601F0C2F9204601F0F5F80028FAD1284670BD000038B5044D0024285D013402F03FFA402CF9D138BDAC81FF1FBB
:4002400008B502F059FC002002F062FC02F074FC02F07EFC80B208BD10B50446012002F071F8642002F060FAFFF7EAFF2080002002F068F8642002F057FAFFF7E1FF60807C
:4002800010BD08B502F064FD002002F06DFD02F07FFD02F089FD80B208BD10B50446FFF796FF322002F040FAFFF7EBFF20800120FFF774FF322002F037FAFFF7E2FF6080BA
:4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0E4FE02A802F080F802F08AF813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF62780A
:400300002146BDE81040042001F0D6B8E438000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0DEFFDE
:40034000002002F071FD002384F8643010BD00BF8881FF1F38B5104D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F865302279094B1A71A378002B14BF41
:400380000220012002F050FDE07802F047FD2079BDE8384002F07EBD8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7D1FF012001F0A2FF4FF47A7002F0B4F954
:4003C00084F86A50E368E366012384F86430BDE8384002F0E7B900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF767FFFFF7EBFE0120002384F86A0078
:40040000236702F0A7F92A46216F1848FFF759FF144E0027236F9D4216D001F075FF00B13767236F9D4205DD0120FFF7B7FE336F013305E005DA0020FFF7B0FE336F013B03
:40044000336702F0AFF9E5E7322002F06DF92A2DCCBF0020012002F029FDBDE8F8400448FFF72FBF8881FF1FF1380000F8380000153900002DE9F04F99B062B602F0FCF91F
:400480009E49042002F020FA9D4801F049FF9D4802F0ECFC9C4801F07DFF02F0CDFB02F09FFA002002F0C0FC01F098FF0221002000F060FF954C012001F0D8F8002384F868
:4004C0006730FFF76DFFFFF782FE84F87400FFF72FFF012384F86730FFF762FFFFF777FE84F87500FFF724FF894B94F87400894994F875202546002A14BF0A461A4600286F
:4005000008BF19468448FFF7DCFE0321084602F029F9264602F046F994F8643043B1EA6EEB689B1A41F28832934201D9FFF700FF00F058FF18B97948FFF7C3FE04E000F06D
:4005400057FF0028F7D10BE000F04CFF10B902F029F9F9E77248FFF7B4FE032001F072F8032000F051FF0128D4D16E48FFF7F2FE6D490320FFF738FE94F876106B48FFF7E5
:40058000A0FE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D4033303D4034D0303238DF851
:4005C00020308DF821300F238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD0B4611E083B100227E
:40060000174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F8E20220FFF786FD4FF000080DF1200A54
:4006400002F0B0F84FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF772FD3A465146022000F027FFB9F10109EBD108F10108B8F1400FE2D12E4B38E04FF06E
:40068000010A4FF000080DF1200B02F08BF84FF0000959460120FFF7A7FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B464A461F48FFF701FEDC
:4006C0004FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400FD5D151461648FFF7EEFDBAF1000F00F01B81144B1B8807A8ADF81C3095E200BF5501000004
:40070000F900000091000000C50000008881FF1F27390000233900002A3900004239000055390000ED81FF1FFE81FF1F5F390000D4380000D63800006E3900008A390000D6
:40074000D8380000206FFFF749FE94F8780001F0F9FD94F8780001F0DDFD02F00DFCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A786C
:4007800002F0FE021A7002F0FBFB0220FFF7DAFC012141F6FF734FF48042084602F04AFB84F8B60001F06CFF08F807000137402FF8D1DFF8B0A200270AF195091FFA89F80A
:4007C0000137402F14BF3A4600221AF8010F2244062392F82420402101F086FF424646F24F419AF8000001F091FF08F14008402F1FFA88F8E4D196F8793053B196F87C30B1
:40080000336100233375237D002BFCD000233375336100234FF0FF32236062602372236894F8B600234493F8241001F0E1FE94F8B60001F09FFE012194F8B60001F072FE9E
:400840002368002BFCD0002398467360D6F80CA0012701F0A7FFE368B4F87A20CAEB030393420DD367B1042195F8B60001F0CCFE94F8B60001F0D8FE0028F9D107463072C3
:40088000237AFBB96A682B689A4202D1002FE0D118E00220FFF756FC6968402209EB8111022000F009FE6A68674B01321340002BBEBF03F1FF3363F03F03013308F101081C
:4008C0006360C6E70220277AFFF73CFC00221146022000F0F1FD0220FFF734FCFFB2FFF7A3FC002001F016FD37B15848FFF7E9FC0220FFF70DFD06E0554B08A81B88ADF869
:400900002030FFF7F3FC227D4146237A5148FFF7D8FC15E25048FFF7D4FCD4F87A7017F03F0701D0032009E2286FFFF757FD95F8780001F007FD95F8780001F0EBFC01204C
:4009400001F006FD02F018FB444BDFF814811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F007FB01214FF4804341F6FF72084601F0BF
:40098000EDFC85F8B60001F07BFE08F807000137402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F82420402101F094FEA6
:4009C000414646F24A5299F8000001F09FFE08F14008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF0010A4FF0000A2168BA
:400A0000114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426FD02BB1227A002A7AD12A7D002A77D12068049A059302EB8010BAF1000F16D040223F2102F079
:400A4000FBFA1CE09F6400403F00008094390000DA380000AE390000C13900009A650040AC81FF1FAB81FF1F014601370120FFF7BBFBC7EB0903D3F1000A4AEB030A21684C
:400A8000B34A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F084FC85F808806B6895F8B6002B4493F82410D5
:400AC00001F096FD95F8B60001F054FD012195F8B60001F027FD95F87E302B6185F81480237D002BFCD04FF00008012086F8148001F06EFC404601F02BFC00E023B1237A40
:400B00005BB92B7D4BB90123626842453FF477AF0BF1010BD5F8048071E701F053FC012001F016FC002001F053FC042194F8B60001F06AFD94F8B60001F076FD804600285F
:400B4000F8D196F8B60001F003FD337D327A0293012303920193CDF800A05B463A4649467C48FFF7AEFBC6F81080BAF1000F0BD0FFF75AFB002001F0CDFB237A63B176484B
:400B8000FFF79FFB0220D9E0B945F1D073490120FFF72AFB0137F7E77148FFF792FB714B3DE094F8780001F0CDFB206FFFF716FC6D48FFF786FB94F87930236100232375F4
:400BC000237D002BFCD0012001F002FC00233375237D002BFCD0002001F0FAFB002363483361FFF76EFB624B19E0002084F86A00FFF7F4FB5F4B12E094F8743023B195F867
:400C000075200AB985F8782094F875201AB113B9012385F878305848FFF79CFB574B1B88ADF8203008A8FFF761FB89E0FFF780FB02F080F8002002F023F82A2701F04EFFDD
:400C4000002001F0F1FE3A46002108A802F0F4F917238DF820308DF8217001F0A3FD002001F04CFB002002F0DFF8C82001F05CFD0DF12200FFF7F0FA0DF13600FFF70DFBE9
:400C800001F090FD012002F0CFF8322001F04CFD0DF12600FFF7E0FA0DF13A00FFF7FDFA012001F02BFB4FF4967001F03DFD01F079FD0DF12E00FFF7CFFA0DF14200FFF703
:400CC000ECFA002001F01AFB4FF4967001F02CFD01F068FD022002F0A7F8322001F024FD0DEB0700FFF7B8FA0DF13E00FFF7D5FA012001F003FB4FF4967001F015FD01F023
:400D000051FD0DF13200FFF7A7FA0DF14600FFF7C4FA002001F0F2FA4FF4967001F004FD01F040FD002002F07FF8002384F86A3001F082FF01F054FE74E70120FFF7E8FA75
:400D4000032000F07FFC0E48FFF7BBFAFFF7E2BB3F000080CB390000FB3900004092FF1F053A0000DC3800000D3A00001B3A0000DE380000E0380000FE81FF1FE238000096
:400D8000283A00002DE9F04172B6874A874D1378002443F020031370062606FB045300219A881868013402F047F9072CF5D18048804D002450F8041F04F1105303F14C02D0
:400DC00021F0FF064D33C9B20B4452005B0002329A4206D012F8027C12F801EC07F806E0F5E7A8420C44E5D1734A00231360936013619361714A724D1168724A724C116044
:400E0000724A19261168724A724F116072490A7842F002020A700A7C42F002020A742A78A1F5863142F040022A7041390A7842F010020A70694A07CA03C469492280086863
:400E4000684A2834106009791171674A07CA03C42280664AE83C07CA03C42280644A083407CA03C42280634A03CAC4F80A006248C4F80E100178614C41F008010170604995
:400E800061200870A1F5F061012008808E7092E803005C4A84E80300062111705221A2F580721170584A594912781C46D2B20A70574A4FF4E261118041F2512122F8021C31
:400EC0003B784FF4F07003F0010343EA440303F0030402F049F8013E64D0032CF0D14D4A4FF4807313804C49072313704B4800230B704821017013705371494B02221A706B
:400F000004229A7290F89A33464F43F0010380F89A3390F89933DFF82C8143F0030380F899332B78C12043F080032B70344B18703D4B3E481B783C78DBB298F80060E4B280
:400F400003F0070ED34080F800E0F6B2437044F003033B7046F0030388F80030344B1970344940230B708B700B728B72082381F81F32202180F8B51280F8BA222E4A0A208F
:400F8000117082F82D3001F0E5FB2C4B88F8006044223C702A4C1A7095E80F0007C42380BDE8F081FEE700BFE84600402D3A0000FCFFFF4794000048007600405C0B004894
:400FC000A043004020760040C0510040600B0048287600402542004003500140140B0048200B0048CB510040280B0048340B0048400B00484C0B004822430040004100404B
:4010000000480040004300400F010049A14600402242004004400040064000400840004001400040F8460040CF0100496E5800401D510040015900402B5B004076580040B5
:40104000B0430040F946004008B501F0C9FF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E001F0B8FF0C2303604FF0FF33184608BDCC80FF1F34
:401080009093FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F8046C8C1AA64202D041F8045BF9E701381033E5E757
:4010C00001F094FFFFF7D6F9FEE700BF01000000FC3B0000124A134B10B51A60124A134C1368134843F4007313600023032B98BF54F823204FEA830188BF0E4A0133302B02
:401100004250F3D10C4B1A780C4B1A700C4B084A1A60FFF737FEBDE8104001F0EDB900BF0004FA050CED00E014ED00E0000000000080FF1F49100000BC760040C080FF1F72
:4011400008ED00E0F8B501F017FF4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3203F8946C1378054603F07F031370002001F0EAFA93
:401180002378404A03F0F90323701378384603F0DF03137023783B43237001F0DBFA282001F0D8FA384B30461A7802F07F021A701A7802F0BF021A7023783343237001F0BE
:4011C000C9FA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0CEFE0721172001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182051
:4012000001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052001F0E4FA2049052001F0D2FA0721062001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A490720CB
:4012400001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162001F0C4FA1449162001F0B2FA07210C2001F0BCFABDE8F84010490C2001F0A8BAA54300409443004068
:401280009D60004012600040F851004084600040B592FF1F131B00004D190000111B0000451A0000711A0000A11A0000D91A0000191B00008D1B0000214B224A10B51870D4
:4012C00000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080721A604FF400121A6020221A601860802018604FF4F9
:4013000080701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70802203F8202C012001F018FE0D4B04221A7010BD04
:40134000D092FF1FD692FF1FD492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F00E100E09E6000409C600040286000401260004070B5074C054623780E461BB9B6
:40138000FFF7E0FE0123237031462846BDE87040FFF792BF8092FF1F0A4A002313700A4A13700A4A13700A4A13700A4A13700A4A13700A4A13700A4B03221A70802203F84C
:4013C000202C7047D692FF1FD492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F28600040014B1878704700BFD592FF1F044B1A7802F0FF001AB118780022C0B28E
:401400001A707047D492FF1F024A0C2303FB002040787047DC92FF1F431E072B0CD8074A064B00010344805C5B7800F00F0043EA0020023880B2704700207047FC5F004062
:401440001A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE805F002070C110021084601F01BF80DE000210846F4
:4014800000F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF054B1855EDB2072D03D801F0EDF8034B185538BDDC92FF1FAC92FF1FB592FF1F431E072B2DE9F0470D
:4014C0000446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7884BFC5F50075ADB2254A43EA15230601B354B244E9
:40150000EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D01F011F811232946FE2218F8040001F0D6F806F5C04262
:4015400078321FFA89F118F8040001F0DFF8124D18F80410385D01F04BF80121385D00F0E1FF735D43F002037355735D03F0FD037355BDE8F08703FB04746379DBB28AF8A7
:401580000230BDE8F08700BFDC92FF1FFC5F0040B592FF1FAC92FF1F706000402DE9F047044615468846002940D0431E072B3FD8FFF732FFA84203D22046FFF72DFF0546CA
:4015C0001D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F0B9FF1223FE222946305D01F07FF807F5C0411FFA88F27831305D01F089F8DFF84490315DE9
:401600001AF8040000F0F4FF01211AF8040000F089FF17F8093043F0020307F8093017F8093003F0FD0307F8093002E00D4600E000252846BDE8F087B592FF1FAC92FF1F51
:4016400070600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFDC92FF1FFE5F0040431E072B9FBF024B000108221A547047FE5F00407B
:4016800030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E85450680133013050601088013880B21080ECE718460B78A4
:4016C0000E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BF4C93FF1F4893FF1F00600040C492FF1FC192FF1F34
:40170000D692FF1FD292FF1F4993FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012013707047D692FF1FD292FF1FC192FF1F4893FF1F4993FF1F11
:4017400030B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013BC9B229705168DBB20131516011880130013989B2B5
:401780001180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404C93FF1F00600040C492FF1F4993FF1FD292FF1FC192FF1F064A06231370064ACF
:4017C00001201370054B80221A70054B00221A70704700BFD692FF1FC192FF1FD292FF1F4993FF1F054B9A683AB19A68044910709A680988518000229A607047C492FF1F2D
:401800004C93FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202B05D0402B06D043B900F012FC04E001F0A5FB01E04F
:4018400000F046FD10B9034B03221A7008BD00BF28600040C192FF1F0060004008B5084A084B0120197813880B449BB21380064B00221A70FFF7B6FF044B03221A7008BDC9
:401880004C93FF1F4893FF1FD692FF1FC192FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A70E0
:4018C00008BD00BFD692FF1FC192FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFD692FF1FC192FF1F08B50A4B1A7832B11A78094942F080020A700022E6
:401900001A70074B002201201A70FFF76BFF054B03221A7008BD00BFC092FF1F08600040D692FF1FC192FF1F074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF742
:40194000C5BFFFF7D3BF7047D692FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5DF
:40198000FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB2000605D41378DBB20B700B7803F00F0328788342F1D1E2
:4019C00038BD38BD28600040C192FF1FD292FF1F4993FF1F29600040054A00231380054A916819B191680B7092685380704700BF4C93FF1FC492FF1F0E4808B503889BB255
:401A000013B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF0380FFF728FE012008BDC492FF1FD692FF1FD292FF1F00
:401A400000600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B600040DC92FF1F094B02221A700F3B93F82230074B1A7EF8
:401A800002F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040DC92FF1F0B4B04221A700F3B93F83230094B93F8242002F00302012A1EBF93F8272082F048
:401AC000800283F82720012283F82520704700BF0B600040DC92FF1F0B4B08221A700F3B93F84230094B93F8302002F00302012A1EBF93F8332082F0800283F833200122D0
:401B000083F83120704700BF0B600040DC92FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B3CA0720D5144A4FEA031E7244947850782040C5074E
:401B40000DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF84F801C090700133DBB24908D7E7F0BD9F60004080
:401B8000DC92FF1F70600040FE5F004000F0ACBC70B50446184B88B003AA03F11006154618685968083303C5B3422A46F7D11B782B70FCB12223237001AD0323284663703F
:401BC00000F08AFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC1700371417100F10400EAD108B070BD573A00002DE9F043B7
:401C00001C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F07D098F8044024064CBF887081F802C001E081F877
:401C400002E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF221A70BDE8F08300BFDC92FF1F70600040FC5F004028
:401C80000A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047D092FF1FB83A00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFEA
:401CC000A8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891425ED19A7893F8039002F07F0206FB02FA05EB77
:401D00000A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E007
:401D400009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F81A
:401D800006A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F307224A71083394E7BDE8F88F00BFD592FF1FDC92FF1F91
:401DC000D192FF1FFC5F004070600040C292FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BDD592FF1F00212DE9F84F0B464E4E0C2707FB01F46E
:401E000001313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8E0
:401E4000F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB02094B4531D093F802A00AF07F06AE4229D10E89B3F8A4
:401E800004B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F06F0030607DA012E0CBF07260D264E7181F80180C8
:401EC00006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF737
:401F000067FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF767BEBDE8F88F00BFDC92FF1FC292FF1F4A93FF1F7A
:401F4000D592FF1FD392FF1FD892FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F0010191700021D170517841F002015170127912F0800F074A54
:401F80001A4414BF8D2389239370FFF715BC0020704700BF00600040DC92FF1FFC5F004030B4194B1A7902F07F02531E072B27D8164B0C2404FB02339978154D01F0FE0155
:401FC00099700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB3C
:40200000002030BC704700BF00600040DC92FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B798B4201D2012909D8074A0848535CDBB24354A378F6
:402040000120DBB2535410BD002010BDD592FF1F00600040C292FF1F4A93FF1F38B58A4A8A4C13780021DBB221801806517840F18D800A2900F20581DFE811F05D00030155
:4020800003010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B686360122310E0CB78022B12D18878FFF7E5FD002800F0E18000
:4020C000436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B2854209D3664A91786A4AEE2908BF1346634A917881B106E046
:40210000187801320028F1D018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD801A78228018E0BDE8384000F029BF13F0030313D0022B40F0A3
:40214000A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E702222280514A11784F4AC9B2117053706260ACE7012323804D4BEFE722
:402180000123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB25AE0937803F0FF0153B9404B1A7891425FD01970404B012062
:4021C0001870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB2D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF788
:4022000053FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B2BD1214A137803F0FD0315E003F00303012B13D008D3022B3C
:402240001FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A795AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF71A
:402280009BBA002038BD00BF00600040C492FF1FD092FF1FB83A00001C3B0000A43A00008F3B00006893FF1FDC92FF1F8192FF1FD392FF1FD592FF1FC292FF1FC092FF1FB0
:4022C000D492FF1FD192FF1F4A93FF1FD792FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A80FFF786BB0020704700600040C492FF1F7C3A0000014B18707E
:40230000704700BF7B650040014B1878704700BF6B640040014B1870704700BF75650040064A0123136002F688321268E0211064034A1170A2F540721360704780E100E038
:4023400000E400E0014B1870704700BF7E640040014B1870704700BF7D64004073B515461E460B4C05230022019200920A4601461846237000F064F932462946207800F0D1
:402380001FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A1170A2F202321360704780E100E002E400E0014B04221A607047FE
:4023C00000E100E0014B04221A60704780E100E0014B1870704700BF78650040704738B505460078012428B100F066FD285D0134E4B2F8E738BD08B50D2000F05DFDBDE81C
:4024000008400A2000F058BDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A463146207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1F52
:40244000F7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF82946207800F097F8207803B0F0BDE180FF1FF7B516461F460B4CCE
:4024800000230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207803B0F0BDE280FF1F73B515461E460B4C0023019300930A46DE
:4024C00001461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1F024B1878C0F38010704700BF8F450040074A7F2380211370A9
:402500005170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD8084B0001C25C11B142F0200201E002F0DF02C254C25C42F096
:402540000102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25400207047012070471070004017280DD8074900010B460344B6
:402580001A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490003F0F10301F00E0119438154002070470120704710700040C7
:4025C00041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC280002001207047000017289FBF034B00011954002088BF012020
:40260000704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF1470004017289FBF034B0001185C00F0070088BFFF207047147000402E
:40264000172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213DBB263709DF80C30002003F00F03A370E07010BD012010BDC8
:4026800010B500F079FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F06BFC204610BD00BFE480FF1F030610B5044611D400F05CFC32
:4026C000084AE300117803F1804303F5F04319705378147001335370BDE8104000F050BC10BD00BFE480FF1F30B504060CD411F4704509D1C40004F1804404F5F0442180BE
:40270000A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC280002001207047000038B50446084DB4F5004F05D9286800F017FCA4F50044F6E70B
:40274000034B58686043BDE8384000F00DBC00BFEC80FF1F024B1B7A584300F005BC00BFEC80FF1F0E4B00F003001A78490102F0FC02104318701A7801F0600142F0800287
:402780001A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B01221A70704784430040044B00F00F021B6853F8220043F82210C4
:4027C000704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0000F16040490100F56440C9B2017070470F4B10B50F490024CC
:402800000F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A1360084B4FF400421C60C3F8E82010BD8492FF1FA5280000F5
:4028400010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A136843F00103136008BD00BF10E000E010B5054CA3691BB9F6
:40288000FFF7BAFF0123A361BDE81040FFF7E8BF8492FF1F024B1868C0F30040704700BF10E000E038B5FFF7F5FF012808D1054D002455F8243003B198470134052CF8D1C3
:4028C00038BD00BF8892FF1F024B03EB80035868596070478492FF1F134B144A1B78DBB20360127843EA0223114A0360127843EA0243104A0360127843EA026303600E4B60
:402900000E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF0301004904010049EC46004002010049010100490001004991
:40294000050100490601004910B500F015FB204A044613780A2043F002031370137C43F00203137412F80A3C43F0010302F80A3C937943F00103937102F5AB52137843F0C1
:4029800003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222183B1A70094A137843F008031370FFF7CAFE4B
:4029C000064B10222046BDE810401A6000F0D8BAAB4300400E5900402F5B004080E200E008B500F0C9FA0F4A137803F0FE031370A2F5AA521D3A137803F0FD031370137CD7
:402A000003F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0AFBA00BF08590040044A137803F03F0343EA8010C0B21070704700BF085900401A
:402A4000082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FB1F
:402A8000F1F305490B60054B1A8070470A590040683A00005293FF1F5493FF1F5893FF1F08B5102000F0A6F907210420FFF79AFE07490420FFF788FE064A0C20137843F002
:402AC00006031370FFF7BCFF034B00221A8008BD992B0000095900405093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF72ABFA092FF1F044B1A7802F0FB0262
:402B00001A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF715FE024B1B78204610BD00BF09590040034A044B1B88108826
:402B4000181A00B2704700BF5893FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BFCA
:402B80005B42134493FBF1F000B270475293FF1F5493FF1F5093FF1F7047000010B500F0EBF9214A044613780A2043F001031370137C43F00103137412F80A3C43F00203F7
:402BC00002F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F5CD
:402C000097530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0ADB900BFAB43004006590040275B004080E200E008B500F09DF90F4AAB
:402C4000137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F80A3C937903F0FD039371BDE8084000F083B900BF005900406D
:402C8000044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F910102D
:402CC0000A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A80704702590040723A00005E93FF1F6493FF1F5C93FF1F08B5102000F084F8A9
:402D000007210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BDF12D0000015900406093FF1F10B5054C23781BB9FFF7DCFFC0
:402D400001232370BDE81040FFF728BFA192FF1F044B1A7802F0FB021A701A7842F001021A7070470059004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7A0
:402D8000E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5C93FF1FA05B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B8A
:402DC0001B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475E93FF1F6493FF1F6093FF1F70470000034A00F0F80013780343137066
:402E0000704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF77650040014B1870704700BF7465004073B515461E460B4C04230022C3
:402E4000019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDFC80FF1F074A0223136002F688321268E0215064044A1170A6
:402E80006FF440710A441360704700BF80E100E001E400E0014B1870704700BF78640040014B1870704700BF7B640040014B1870704700BF79650040FEB5494652465B460F
:402EC0000EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C460125002600F041F8814651460B7823400B7016
:402F00005846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B78234003430B705846043000F016F8484600F07F
:402F40001FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BF01380046FCD17047EFF3108072B67047A4
:402F800080F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E013790020704700600040DC92FF1F002902D0B0FBF1F0F4
:402FC000704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF6081FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8C7
:40300000253098470135F8E700F0DEFD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BDD43B0000D43B0000D43B0000DC3B000003460244934202D0A0
:4030400003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029305934FF6FF7300910491ADF80E300246EB
:403080001E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF7CBFF03B05DF804FB6081FF1F2DE9F04703
:4030C0008E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BF1D4653050FD5294600F04AFB064698B1FA
:403100003A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A3894FF0FF3043F04003A381BDE8F087266174
:403140003E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F08700002DE9F04F9DB003938B8980461C060D46C4
:4031800016460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB2C
:4031C000060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A08000234FF0FF3204930793059206938DF8533038
:403200001A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A0744BF2B228DF8532022782A2A03D0079AE1
:4032400000210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB02320121F5E701B107923B782E2B1ED17B782A2BC0
:403280000AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB01210123F5E703B1059103223978224843
:4032C00000F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039B073323F007030833039314E003AB00936E
:403300002A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB895B0601D4099801E04FF0FF301DB0BDE883
:40334000F08F00BFA33B0000A93B0000AD3B000000000000BD3000002DE9F04791461F460A698B6806469342B8BF1346C9F8003091F843200C46DDF8208012B10133C9F83C
:4033800000302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD00135E368D9F800209B1A9D42F1DB94F843306A
:4033C0002268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F1430239463046C0470130F4D02268D9F8005092
:40340000E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D00123224639463046C0470130D5D009F10109F3E700207C
:40344000BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0BC
:40348000692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F14205111D1960136884F84230A8E02168C0
:4034C0001A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E021681A6811F0800F02D0111D196007E072
:4035000011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F00F104051D6003D1550601D5038800E061
:403540000368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F0040020602BB9002D7DD175460CE0002B3D
:4035800079D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A687C
:4035C00010F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111D1960156800216268284600F049F808B172
:40360000401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF0FF3026E023692A4639463046C047013070
:40364000F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842B8BF184605E00B7804F1420584F8423012
:403680008AE705B0BDE8F083573A0000B43B000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431E0A44914204D011F8014B03F8014FF8E7C8
:4036C00010BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B02F8014FF8E710BD38B50546002944D089
:4037000051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B681218226063600C6023E0A24203D813465A68A1
:40374000002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018824201BF106852680918216062605C609A
:403780002846BDE8384000F098B838BDA892FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E000F082F8234B1C681A462146A1B10B683D
:4037C0005B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB9304600F052F820602946304600F04DF8431C4D
:4038000018D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F80130EBD10C233360304600F03EF800206A
:4038400070BD00BFA892FF1FA492FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F885420ED929463846FFF78BFF044650B131468C
:403880002A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7D8FB431C02D1236803B12B6038BD8C93FF1F7047704751F8040C10
:4038C0000028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215027265706C792030782530327800686F6D696E6700626567696E6E696E71
:4039000067207365656B2066726F6D20256420746F2025640066696E6973686564207365656B00796573006E6F00647269766520303A20257320647269766520313A202589
:40394000730057616974696E6720666F72205553422E2E2E0055534220726561647900636F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025B5
:40398000642C206E6F74202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636F756E743D256420693D256420643D256436
:4039C00000636D645F777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D256420756E64657272756E3D256400756E6465723A
:403A000072756E2100737563636573730073746172742065726173696E670073746F702065726173696E670069646C65000051004010004051004030000000014000100080
:403A4000140140000800400140000A004C0140000200500140200030313233343536373839414243444546000001000000040000001000010000000400000010280000002A
:403A8000000104000100000000000000000157494E5553420000303030303100000000000000000012034D005300460054003100300030000100000001000000C03A00005A
:403AC000010000008F3B0000000000000000000001000000D83A000001000000613B000004000000FA3A0000000000000000000000000000F83A0000FF000000010240009F
:403B0000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00CF
:403B4000610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FF00000107050102400000070582024000E6
:403B8000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C00656667454647003031323334353637383961629F
:403BC0006364656600000000F8B500BFF8BC08BC9E46704759000000D5100000F8B500BFF8BC08BC9E46704735000000003C0000C880FF1FA0000000281200000000000046
:403C0000000000009093FF1FFF000000675000400C00000007000000FFFFFFFF7F8000003F0000000000C05D80BB000030000000006CDC02FF00000000000000000000002F
:403C4000000000000000000000000000000000000000000000000000A13B000000000000000000000000000000000000000000000000000000000000000000000000000068
:403C80000000000000000000000000000000000000000000000000000081FF1F00000000000000000000000000000000000000000000000000000000000000000000000065
:403CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4
:403D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
:403D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043
@@ -4098,52 +4098,52 @@
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
:0200000480007A
:400000000145004009520040015B0040016500400101014003030140010501400307014052080140500901404F0A0140480B0140490C0140470D0140400E0140360F01409E
:4000400002150140031701405C18014057190140471A0140531B01400B400140124101400B4201400D43014002440140074501400B4601400C470140104801401149014069
:40008000154C01400D4D014005500140045101407E02080209411080110218041902600C61157C402721290AE204E601EA20EE02E202E608EA10EE04000801080404050568
:4000C0000708080809080C400D080E1011011201130E143A16401708180819081C021E042305240825042701280629082A782C202D012E402F02307F310F327F330F380AE4
:400100003B0A580459045B045C995F018203830187018A108C018D018E248F08907C9282930E961097019A809D019E1C9F04A101A210A302A510A610AA10AB01AC01AE48FD
:40014000B01FB10FB4F0B510BA20BB02BF10D608D80BD90BDB04DC99DD90DF01012202010488051007800A860B100C020DA00E2011011208134414021504162017201804BE
:4001800019021A041B011CA01E202008220126012790282029202A012C022E282F4032493514360139013B443C803D2A5980614069906F0278417B0288049141928093D450
:4001C00094249602970A982299249A019B409C019D019EC0A188A208A402A504A709B404B502B604C0FBC2FFC4EFCAF7CCEBCEFBD608D808DE01E608EA10EE0400FC0108A1
:4002000002020310071408200AD00BE40CE10D100E080F8212031314161017141D1F1E101F4020802110223C2321261028402A302B142CE12E04301F310F33F034E03B08A2
:40024000580B590B5B045C995F0180368209852A86128740882D89098A128B148E098F209080924093C09440968098079C249D089F13A03FA180A320A840A908ADEAB0076D
:40028000B1E0B280B301B438B518B640B706BE44D808D904DB04DC99DF0100820120040A07050A420B100EA80F01110112091304171219201A401D8A1E081F4020602101B0
:4002C000231025102652275029202A012C022F483249364239033B543D283E806102620169406C0C6F0178017F0187058C028D04900292809345946495029642971298206E
:4003000099049B069C019D219E449F01A041A208A418A701B040B204B7C0C0FBC2FDC45FCAB5CC9BCE7FDE11E40800020104040806140A010C1C101012081301140215050D
:400340001601170220032B022D062E022F0130043107320336183A083F015608580459045B045C095D905F018128829083118401853F86448A9C8C208D3F8EC090C191FFA2
:40038000922895149690972199019A039B0E9D409E90A0FCA202A902AA90AB01ADC0AE90B140B330B41FB50FB6E0B780BA80D608D80BD90BDB04DC99DD90DF0101A604240E
:4003C0000501070208400A240B400EA0120113121401164019801A041B401D841EA02080229C2344260129602C082F0230023294370539063A053B403F01580259025A0213
:400400005B405F806140670168036A806B026C016F06780182019440950296E4974698209C429D209E049F02A412A580A741B240B420B540C0FFC23FC49BCA3CCCCFCE8BAA
:40044000D618D818DE01EA20EE0203900401060407900A100B9C0D010E100F4812101520161017C01A031B031DFC1E1C1F02201C2202239025C127242A102B902C012E08A9
:400480002F9033E0341F351F3B08420147E0482049FF4AFF4BFF4F83580859085A045B045C995D095F018310871C8B108D018F0893109F03A310A501A704AB10AD1CAF0254
:4004C000B71FD908DB04DC90DF0100400120032008420A281110134219101A0820042120220826802701284229202A042B202C422D202F20312032483402368039123B4544
:400500003C203D103F454180520159105A445B0160046208638269406E8078807C807F018201C007C20FC40BCAFFCC9ECEFFD008D60FD80FDE18EA01EE08E020E623EE0BF2
:40054000022005880612083209880C080E010F011049118812321432151017A0182119461A1C1BB81D9A1E091F20206321422204230424322588282029882A022C322D04B6
:400580003020313F324033C1341F37C1398A3A203E055608580459045B045C995D905F018401860289018D028E039002920199089B049D049F08A004B002B102B204B304D8
:4005C000B401B501B708B822B988BE15BF55C043C520C802C9FFCAFFCBFFCD20CEF0D110D804D904DA04DB04DD09DF01E108E240E340E480E640E740000801400320051496
:40060000060107400A800D800EA8108016441799181019021B101C401E081F0420012111220424A02505264227A02D202F52324033203504360137A039903C243E4040504B
:40064000488049204A0459405A205D045E085F4064016580670268046A806C036E406F01831085118B4491D0938094A89504970A984099449B509C019D119EC89F20A0500A
:40068000A188A211A402A535A708A980C0F5C2F8C4F8CAF0CCFCCE7CD003D61CD810E022E620EA04EE0B010129022C013102350136013E403F115608580459045B045D9048
:4006C0005F0180478208830885218626874E880189018A388B708D4E8E40914E92029426974E98269B029D809E11A010A201A321A520A604A701A811AA26AB04AC26AD0FF1
:40070000AF10B040B380B43FB57FB63FBAA0BB20BF04C003C50EC70CC811C9FFCAFFCBFFD004D601D804D904DA04DB04DC99DD09DF01E2C0040205940DA20E201002150580
:40074000162017A01B401D111E011F102040221024042530260827082B042E642F40344035043602371038013C803D283E0145084F04570858905C406001630868026C042B
:400780006D016E046F02768981028410860888808D028E049002910492409380940C96209724984099869A049BB09D119E899F08A040A18CA212A530A74AA881AE04B74049
:4007C000C0F0C2F0C4F1CAF4CCF0CEF1D040D61CD80CE008E404E602E8041B011F083180330836843B408340C630CCF0CE10E22032043380364037023B043F808180A0042B
:40080000A340A580A604AE80AF41B004CCF0CE60E680EE40531057208510960897049F02A004A644A780D460E240860491209608972498809F02A004A640A7A0AA04B48062
:40084000E610EE201680C40458405E019A80A404AC04D401D6011B04844096019C40A404A710B680B710C608EA08EE0808080B080E020F4087048A209601A404A710AB043D
:40088000C20FE00425808004871089808B04912097249880A004A720AB02AE40AF80C820E6C0EED0511054045880700477809008912098809B80A004AB80AF20D4E0DC8000
:4008C000DE20EA80EE1005200A400C100F201C0852225620610186029641A220A404A710AA41AC08AF40C001C20DC601D407D80270018001852086018810960198109920A0
:40090000AA20B501DC01E204EC0201010D010F0111011D0100FF01AB02021105BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B88D
:4009400047004700000100008000000282008200000000000007070007000000270018012700180100040000000500000000000000000000000000000000000000000000C4
:400980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037
:4009C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F7
:400A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B6
:400A40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076
:400A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
:400AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F6
:400B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B5
:400B40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075
:400000000145004009520040015B0040016400400301014002030140080501400C0701405A0801404F0901405E0A0140480B0140510C0140490D01404F0E01403E0F014057
:40004000261401403215014028160140371701405B1801404A190140521A0140561B01400C4001400D4101400B4201400E4301400244014007450140094601400E47014042
:400080000D4801400F490140194C01400D4D014006500140045101407E02084409021080110218011904601361197C4027212A0A8840E204E620A440EE028602980CA44036
:4000C000A808AC04E282EA08EE04870289028A048F10980C9940A202A440AD40E64AEA01EE0C02080301041706E80701080809020A400C800D030E08100F11021408150176
:4001000016201702181019021A071C081EF01F0220012204230224012502260628012A022B012CFF2D0330FF35033E013F105608580459045B045D905F01808081748301C8
:400140008480867E8752884089528A108C408D528E20907E910793089524975B983E9A409B049C409D329E049FC0A040A152A202A440A552A608A87EA952AD20AE01AF8055
:40018000B103B31CB6FFB7E0BB88BE40D804D904DB04DC90DF010008011002800301051007620A050B400D200E020F501080114412091554170119801E8020402101228854
:4001C00023612704284029942D512F103001312032883510368939023B943C083E013F505C80640268016A406D806F08840288019140928293809408950A964097159801FC
:400200009B089C229D159E069F20A004A128A201A401A540A648A708AC80C0FFC2FBC4FFCAFFCCFFCEFFD610D810E280E608E808EA01EE0C002001110250050106100901A8
:400240000A030B1E0D110E1C107C110D120213221440151116301D041E1020802461260428612A082E103060310332803330341F350437083E043F1040234520480249FF27
:400280004AFF4BFF4D204EF05110580B59045A045C995D095F0161086240634064806640674081408201840188078B128C078D368E088F0992109309940799249A069C019B
:4002C0009D079E02A006A209A401A604A801A92DAA06AB12AC0FAD3FB140B401B507B61EB738BE10BF01D804D90BDB04DC99DF0100800122032005550A410B200E800F1598
:400300001080112012221401154A172019221A101D401E061F40201821AA22012308244026A027042E402F0831823208342036803701381839423D903E02421448805C40CC
:400340006D106F0178027C028701891091409281932096409C029D229E049F04A040A110A202A320A401A940AF48B340C0FFC2FDC4FFCA30CCBBCEBFD006D610DE81E2802C
:40038000007C0160028206100710080109400A240D7C0E030F02121013031501161C1704180119011A481B081E101F1022102310268027102B102E102F1C301F311F3320DC
:4003C000354036F03A805608580B590B5B045C995D905F01800482118314851086E08782881F89088A208B108D1F8F40901F9240951097219B149C019E1E9F14A3E4A41F55
:40040000A680A808AA11AC02AE11AF14B1F0B30FB4F0B60FBA20BB02D80BD90BDB04DC99DF010102024003240444068007240A410C800D220F201222131014011502170861
:4004400019C01A031B301C401F412080210223B024182530262128402A402C812D822E102F2031403324344036213784380839023A803B303C143EC03F015F40670169402A
:4004800078027C0291029290960297129C029D629E049F04A202A441A522A621A730AA12C0FEC2F9C4B7CAF9CCFECEFFD610D810DE81EE02001C010102020306061008010D
:4004C00009040A040B010E0312101507180119071A081E102102221023012A1C2D072E10301F3107580B590B5B045C995F0181E782908310878488208AC08B188E908F84EC
:40050000929093849508969097229801990E9A489B419D019E039F06A29CA3E4A4C1A624A784AA90AB84ACFCAE02B21FB4E0B5F8B707BA20BB80C202C60EC804C9FFCAFF6A
:40054000CBFFCF83D80BD90BDA04DB04DC99DD09DF01004203640552070109220A020D620F201080120213101522174418401D181F10210122022550269029022B012C803A
:400580002D022E102F203120330436A0370539223B103C8A3D203F0245484605470848044B1057405C405E225F086520660167226A8078027C0283408D48C0DFC2FBC4FBE9
:4005C000CAE9CCF6CEF5D020D6F0D8F0DE810A010C01130115012301260134013501382039203E103F105608580459045B045D905F018C029001930195019C04A602A902A1
:40060000B002B204B301B401B702B802B908BE15BF44D804D904DB04DF01030507010E400F011308170418041E281F022304268027102B402F08330537055B405D805E2030
:40064000614065C06B056E406F0883488B068C048E808F4093019F10A604A844AA10AF50B001B220B404C083C290C424CA21CCC3D638D808E204E6A0E811EA08EE035608FE
:400680005B045D9080018505870289088D068F019002930194049510972098089B029D04A130A204A520A710A908AA08AD08B002B107B208B330B404B508B601BE41BF11A1
:4006C000D608D804D904DB04DC99DD90DF01000208080A080B8010041302188419081A012080210822202809290A3004329238083A803B2059605F406110628067016801DA
:4007000081088204868087408A018D209208980C99429A809B029D109F10A202A440A910B280C008C20EC40ACA0FCC0FCE0ED61CD81CE001E408E662EC04EE0A00100260BB
:4007400003010453070209020A080B200C010D030F0410401210130115081604170119091A531BF21C011D801E021F022053210223102401250226022740280129022BFC12
:400780002C132DFF2E203070320F340F35FF360F3A023F10580459045B045C095F018020840185028602870188208C028D018E01902094F89604970198109A039C049D0478
:4007C0009E40A020A10BA480A508A604A813A902AAE4AC07AE08AF02B104B2FFB308B503B703BBA0BE04BF01D804D904DB04DC90DF01002A03010501072509200B920D227D
:400800000E211365155619081C021D051F1420102180228223162610280229082A212C042D402F64324835203680372938403B043E093F506A406F027D017E808204842025
:4008400088208E4290409142928293C19408951D9605973498029A019B2C9CE09D159E049F03A128A209A308A480AA02B140B504B740C0FFC2FFC4FFCAFFCCFACEFAE210B7
:40088000E480E662EE2A0001011F02080401050106400704080109010A200B080C010D1F0E021001121013401401154016FE18041B1F1C801D011E011F0221012201231E7A
:4008C00027202804291F2AF92CFF2D012F1032FF333F35403E043F10400246E0470C481849FF4AFF4BFF50045601580459045A045B045C905D095F0162C08040843286C42E
:4009000088408C068E08922294F8960498809A049C40A040A410A602A840AE01B0FFB4FFB822D804DB04DC09DF010052032004500640072009A00B900E620F04114012402C
:40094000132114441540170819081D101F21202421042204231029282A82312832013340351036103956400442804301502466086710680469146A416B416F02708071025A
:40098000720683028440860490509142920293859408951C96059702980499209B289C409D109E089F01A001A1A8A201A4B4A502A720AB08AF42B320B480B620C0FFC2FFD1
:4009C000C4FBCA0FCC0FCE0FD004E040E210E440E831EC40EE201B011F083020330836843B4083408820C630CCF0CE10E22032043380364037043B043F80A340A604AE80F7
:400A0000AF41CCF0CE60EE405010570484408A80960897049F04A644A780D460E24086048C10942096089704A040A280A640A784AA04AB04E610EA80EE201420C40458108D
:400A40005E019820A404AC04D401D6011B049502960198209C10A404B101C608EA0408080B080F41870495029601970199809C10A404AB05AC20AD80C20F240883048B04A7
:400A80008E409704A040A280A704AE40AF80C820E620EE50528054405A40722076808AA09240A040A280AA80B404D4E0DC80DE20EC20070208020D811C085320568059806E
:400AC0005F0185808A018F039502960199809C10A404AC08AF40C001C20DC601D407D601E208E40975018C028D018F209802A720AA80B010DE04E002E208E402E804010148
:400B00000D010F0111011B011D0100FF01AB020211050000BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B8470047000001000013
:400B400080000002820082000000000000070700070000001D0018011D00180100040000000500000000000000000000000000000000000000000000000000000000000065
:400B80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035
:400BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F5
:400C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B4
@@ -4615,12 +4615,12 @@
:0200000490105A
:04000000BC90ACAF55
:0200000490303A
:02000000CEC56B
:0200000064D4C6
:0200000490402A
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:0200000490501A
:0C00000000012E16106900002E2FDF2ECC
:0C00000000012E16106900002E30753D26
:00000001FF

View File

@@ -272,7 +272,7 @@
<Data key="derive_type" value="AUTO" />
<Data key="desired_freq" value="1600000" />
<Data key="desired_unit" value="0" />
<Data key="divider" value="40" />
<Data key="divider" value="30" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
@@ -390,7 +390,7 @@
<Data key="derive_type" value="AUTO" />
<Data key="desired_freq" value="1600000" />
<Data key="desired_unit" value="0" />
<Data key="divider" value="40" />
<Data key="divider" value="30" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
@@ -604,7 +604,7 @@
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="BUILTIN" />
<Data key="desired_freq" value="64" />
<Data key="desired_freq" value="48" />
<Data key="desired_unit" value="6" />
<Data key="divider" value="0" />
<Data key="domain" value="0" />
@@ -814,7 +814,7 @@
</Group>
<Group key="Component">
<Group key="v1">
<Data key="cy_boot" value="cy_boot_v5_81" />
<Data key="cy_boot" value="cy_boot_v6_10" />
<Data key="Em_EEPROM_Dynamic" value="Em_EEPROM_Dynamic_v2_20" />
<Data key="LIN_Dynamic" value="LIN_Dynamic_v5_0" />
</Group>
@@ -4214,7 +4214,7 @@
</Group>
<Group key="System3">
<Data key="CYDEV_CONFIG_FASTBOOT_ENABLED" value="True" />
<Data key="CYDEV_CONFIG_UNUSED_IO" value="AllowButWarn" />
<Data key="CYDEV_CONFIG_UNUSED_IO" value="Disallowed" />
<Data key="CYDEV_CONFIGURATION_ECC" value="True" />
<Data key="CYDEV_CONFIGURATION_MODE" value="COMPRESSED" />
<Data key="CYDEV_DEBUGGING_DPS" value="Disable" />

View File

@@ -2913,6 +2913,110 @@
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
<filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_2" persistent="">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
<dependencies>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_2.c" persistent="Generated_Source\PSoC5\Clock_2.c">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;CortexM3;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_2.h" persistent="Generated_Source\PSoC5\Clock_2.h">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
</dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
<filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_3" persistent="">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
<dependencies>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_3.c" persistent="Generated_Source\PSoC5\Clock_3.c">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;CortexM3;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_3.h" persistent="Generated_Source\PSoC5\Clock_3.h">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
</dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
<filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_5" persistent="">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
<dependencies>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_5.c" persistent="Generated_Source\PSoC5\Clock_5.c">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;CortexM3;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="Clock_5.h" persistent="Generated_Source\PSoC5\Clock_5.h">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
</dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
<filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="USBFS_ep5" persistent="">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
<dependencies>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="USBFS_ep5_dma.c" persistent="Generated_Source\PSoC5\USBFS_ep5_dma.c">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;CortexM3;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="USBFS_ep5_dma.h" persistent="Generated_Source\PSoC5\USBFS_ep5_dma.h">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
</dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
<filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
</dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
@@ -3480,8 +3584,8 @@
</platforms>
<project_current_platform v="c9323d49-d323-40b8-9b59-cc008d68a989" />
<last_selected_tab v="Cypress" />
<WriteAppVersionLastSavedWith v="4.2.0.641" />
<WriteAppMarketingVersionLastSavedWith v=" 4.2" />
<WriteAppVersionLastSavedWith v="4.4.0.80" />
<WriteAppMarketingVersionLastSavedWith v=" 4.4" />
<project_id v="ff3eb327-f593-4eb3-a00f-72497469e963" />
<GenerateDescriptionFiles v="False" />
</CyGuid_49cfd574-032a-4a64-b7be-d4eeeaf25e43>
@@ -3493,9 +3597,7 @@
<library_dep persistent="${CyRoot}\psoc\content\default\CyAnnotationLibrary\CyAnnotationLibrary.cylib\CyAnnotationLibrary.cyprj" />
</library_deps>
<CyGuid_b0d670ad-d48f-47cb-9d0b-b1642bab195c type_name="CyDesigner.Common.Base.CyExprTypeMgr" version="1" />
<ignored_deps>
<library_dep persistent="C:\Users\dg\Documents\PSoC Creator\4.1\Downloads ( 4.1).cylib\Downloads ( 4.1).cyprj" />
</ignored_deps>
<ignored_deps />
</CyGuid_495451fe-d201-4d01-b22d-5d3f5609ac37>
<boot_component v="" />
<current_generation v="150" />

View File

@@ -20,13 +20,14 @@ module Sequencer (
localparam STATE_LOAD = 0;
localparam STATE_WRITING = 1;
localparam STATE_WAITING = 2;
reg state;
reg [1:0] state;
reg [5:0] countdown;
reg pulsepending;
assign req = (!reset && (state == STATE_LOAD));
assign wdata = (!reset && (state == STATE_WRITING) && (countdown == 0) && pulsepending);
assign wdata = (!reset && (state == STATE_WAITING) && (countdown == 0) && pulsepending);
assign debug_state = 0;
reg olddataclock;
@@ -37,11 +38,6 @@ assign dataclocked = !olddataclock && dataclock;
reg oldsampleclock;
reg sampleclocked;
reg oldindex;
wire indexed;
always @(posedge clock) oldindex <= index;
assign indexed = !oldindex && index;
always @(posedge clock)
begin
if (reset)
@@ -65,7 +61,7 @@ begin
if (dataclocked)
begin
pulsepending <= opcode[7];
countdown <= opcode[5:0];
countdown <= opcode[5:0] - 1; /* compensate for extra tick in state machine */
state <= STATE_WRITING;
end
@@ -76,12 +72,15 @@ begin
if (sampleclocked)
begin
if (countdown == 0)
state <= STATE_LOAD;
state <= STATE_WAITING;
else
countdown <= countdown - 1;
sampleclocked <= 0;
end
end
STATE_WAITING:
state <= STATE_LOAD;
endcase
end
end

View File

Binary file not shown.

View File

@@ -79,6 +79,7 @@ CY_ISR(index_irq_cb)
* the track. */
static bool hardsec_index_irq_primed = false;
static uint32_t hardsec_last_pulse_time = 0;
uint32_t index_pulse_duration = clock - hardsec_last_pulse_time;
if (!hardsec_index_threshold)
{
@@ -87,12 +88,18 @@ CY_ISR(index_irq_cb)
}
else
{
index_irq = hardsec_index_irq_primed;
/* It's only an index pulse if the previous pulse is less than
* the threshold.
*/
index_irq = (index_pulse_duration <= hardsec_index_threshold) ?
hardsec_index_irq_primed : false;
if (index_irq)
hardsec_index_irq_primed = false;
else
hardsec_index_irq_primed =
clock - hardsec_last_pulse_time <= hardsec_index_threshold;
index_pulse_duration <= hardsec_index_threshold;
hardsec_last_pulse_time = clock;
}
@@ -275,7 +282,6 @@ static void seek_to(int track)
CyWdtClear();
}
CyDelay(STEP_SETTLING_TIME);
TK43_REG_Write(track < 43); /* high if 0..42, low if 43 or up */
print("finished seek");
}
@@ -305,7 +311,7 @@ static void cmd_measure_speed(struct measurespeed_frame* f)
while (!index_irq)
{
elapsed = clock - start_clock;
if (elapsed > 1000)
if (elapsed > 1500)
{
elapsed = 0;
break;
@@ -416,7 +422,6 @@ static void cmd_read(struct read_frame* f)
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
/* Do slow setup *before* we go into the real-time bit. */
{
@@ -562,7 +567,6 @@ static void cmd_write(struct write_frame* f)
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
{
uint8_t i = CyEnterCriticalSection();
@@ -627,7 +631,6 @@ static void cmd_write(struct write_frame* f)
/* Wait for the index marker. While this happens, the DMA engine
* will prime the FIFO. */
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
@@ -693,7 +696,7 @@ abort:
static void cmd_erase(struct erase_frame* f)
{
SIDE_REG_Write(f->side);
seek_to(current_track);
seek_to(current_track);
/* Disk is now spinning. */
print("start erasing");

View File

@@ -1,21 +1,23 @@
PACKAGES = zlib sqlite3 libusb-1.0
PACKAGES = zlib sqlite3 libusb-1.0 protobuf
export CFLAGS = --std=c++14 -ffunction-sections -fdata-sections
export LDFLAGS =
export CFLAGS = -x c++ --std=c++14 -ffunction-sections -fdata-sections
export LDFLAGS = -pthread
export COPTFLAGS = -Os
export LDOPTFLAGS = -Os -s
export LDOPTFLAGS = -Os
export CDBGFLAGS = -O0 -g
export LDDBGFLAGS = -O0 -g
ifeq ($(OS), Windows_NT)
export PROTOC = /mingw32/bin/protoc
export CXX = /mingw32/bin/g++
export AR = /mingw32/bin/ar rcs
export AR = /mingw32/bin/ar rc
export RANLIB = /mingw32/bin/ranlib
export STRIP = /mingw32/bin/strip
export CFLAGS += -I/mingw32/include/libusb-1.0
export CFLAGS += -I/mingw32/include/libusb-1.0 -I/mingw32/include
export LDFLAGS +=
export LIBS = -static -lz -lsqlite3 -lusb-1.0
export LIBS += -L/mingw32/lib -static -lz -lsqlite3 -lusb-1.0 -lprotobuf
export EXTENSION = .exe
else
@@ -25,21 +27,31 @@ $(warning These pkg-config packages are installed: $(shell pkg-config --list-all
$(error You must have these pkg-config packages installed: $(PACKAGES))
endif
export PROTOC = protoc
export CXX = g++
export AR = ar rcs
export AR = ar rc
export RANLIB = ranlib
export STRIP = strip
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
export LDFLAGS +=
export LIBS = $(shell pkg-config --libs $(PACKAGES))
export LIBS += $(shell pkg-config --libs $(PACKAGES))
export EXTENSION =
ifeq ($(shell uname),Darwin)
AR = ar rcS
RANLIB += -c -no_warning_for_no_symbols
endif
endif
export XXD = xxd
CFLAGS += -Ilib -Idep/fmt -Iarch
export OBJDIR = .obj
all: .obj/build.ninja
@ninja -f .obj/build.ninja
@if command -v cscope > /dev/null; then cscope -bRq; fi
clean:
@echo CLEAN

View File

@@ -4,6 +4,11 @@ FluxEngine
(If you're reading this on GitHub, the formatting's a bit messed up. [Try the
version on cowlark.com instead.](http://cowlark.com/fluxengine/)
**Breaking news!** As of 2021-05-21, the command line environment has changed
_substantially_ (to make it more consistent and flexible, and allow some new
backend features like multi-format IBM scheme disks, which are popular with
CP/M). If things don't work the way you expect, please check the documentation.
What?
-----
@@ -85,20 +90,20 @@ people who've had it work).
### Old disk formats
| Format | Read? | Write? | Notes |
|:-----------------------------------------|:-----:|:------:|-------|
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
| [Amiga](doc/disk-amiga.md) | 🦄 | | |
| [Commodore 64 1541](doc/disk-c64.md) | 🦖 | | and probably the other GCR formats |
| [Brother 120kB](doc/disk-brother.md) | 🦄 | | |
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | and probably the 400kB too |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
| Format | Read? | Write? | Notes |
|:------------------------------------------|:-----:|:------:|-------|
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
| [Amiga](doc/disk-amiga.md) | 🦄 | 🦄 | |
| [Commodore 64 1541/1581](doc/disk-c64.md) | 🦄 | 🦄 | and probably the other formats |
| [Brother 120kB](doc/disk-brother.md) | 🦄 | 🦖 | |
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | and probably the 400kB too |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
{: .datatable }
`*`: these formats are variations of the generic IBM format, and since the
@@ -118,7 +123,9 @@ at least, check the CRC so what data's there is probably good.
| [AES Superplus / No Problem](doc/disk-aeslanier.md) | 🦖 | | hard sectors! |
| [Durango F85](doc/disk-durangof85.md) | 🦖 | | 5.25" |
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
| [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format |
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
| [Northstar(doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
@@ -219,3 +226,7 @@ written by Sean T Barett (and others). It is public domain/Unlicense/MIT
licensed, at your choice. Please see the contents of the directory for the full
text.
As an exception, `dep/snowhouse` contains the snowhouse assertion library,
taken from https://github.com/banditcpp/snowhouse. It is Boost Standard License
1.0 licensed. Please see the contents of the directory for the full text.

View File

@@ -7,10 +7,12 @@
class Sector;
class Fluxmap;
class AesLanierDecoderProto;
class AesLanierDecoder : public AbstractDecoder
{
public:
AesLanierDecoder(const AesLanierDecoderProto&) {}
virtual ~AesLanierDecoder() {}
RecordType advanceToNextRecord();

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message AesLanierDecoderProto {}

View File

@@ -12,10 +12,13 @@
class Sector;
class Fluxmap;
class SectorSet;
class AmigaDecoderProto;
class AmigaEncoderProto;
class AmigaDecoder : public AbstractDecoder
{
public:
AmigaDecoder(const AmigaDecoderProto&) {}
virtual ~AmigaDecoder() {}
RecordType advanceToNextRecord();
@@ -27,13 +30,16 @@ public:
class AmigaEncoder : public AbstractEncoder
{
public:
AmigaEncoder(const AmigaEncoderProto& config):
_config(config) {}
virtual ~AmigaEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup amigaEncoderFlags;
private:
const AmigaEncoderProto& _config;
};
extern uint32_t amigaChecksum(const Bytes& bytes);
extern Bytes amigaInterleave(const Bytes& input);

13
arch/amiga/amiga.proto Normal file
View File

@@ -0,0 +1,13 @@
syntax = "proto2";
import "lib/common.proto";
message AmigaDecoderProto {}
message AmigaEncoderProto {
optional double clock_rate_us = 1
[default=2.00, (help)="Encoded data clock rate."];
optional double post_index_gap_ms = 2
[default=0.5, (help)="Post-index gap before first sector header."];
}

View File

@@ -6,18 +6,7 @@
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
FlagGroup amigaEncoderFlags;
static DoubleFlag clockRateUs(
{ "--clock-rate" },
"Encoded data clock rate (microseconds).",
2.00);
static DoubleFlag postIndexGapMs(
{ "--post-index-gap" },
"Post-index gap before first sector header (milliseconds).",
0.5);
#include "arch/amiga/amiga.pb.h"
static bool lastBit;
@@ -33,13 +22,14 @@ static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vec
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
@@ -49,19 +39,16 @@ static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data,
}
}
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
assert(!(bytes.size() & 3));
Bytes interleaved = amigaInterleave(bytes);
encodeMfm(bits, cursor, interleaved, lastBit);
}
ByteReader br(bytes);
BitReader bitr(br);
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data)
{
Bytes b(4);
ByteWriter bw(b);
bw.write_be32(data);
write_interleaved_bytes(bits, cursor, b);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
@@ -69,11 +56,27 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
if ((sector->data.size() != 512) && (sector->data.size() != 528))
Error() << "unsupported sector size --- you must pick 512 or 528";
uint32_t checksum = 0;
auto write_interleaved_bytes = [&](const Bytes& bytes)
{
Bytes interleaved = amigaInterleave(bytes);
Bytes mfm = encodeMfm(interleaved, lastBit);
checksum ^= amigaChecksum(mfm);
checksum &= 0x55555555;
write_bits(bits, cursor, mfm);
};
auto write_interleaved_word = [&](uint32_t word)
{
Bytes b(4);
b.writer().write_be32(word);
write_interleaved_bytes(b);
};
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
std::vector<bool> headerBits(20*16);
unsigned headerCursor = 0;
checksum = 0;
Bytes header =
{
0xff, /* Amiga 1.0 format byte */
@@ -81,22 +84,16 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
(uint8_t) sector->logicalSector,
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
};
write_interleaved_bytes(headerBits, headerCursor, header);
write_interleaved_bytes(header);
Bytes recoveryInfo(16);
if (sector->data.size() == 528)
recoveryInfo = sector->data.slice(512, 16);
write_interleaved_bytes(headerBits, headerCursor, recoveryInfo);
write_interleaved_bytes(recoveryInfo);
write_interleaved_word(checksum);
std::vector<bool> dataBits(512*16);
unsigned dataCursor = 0;
write_interleaved_bytes(dataBits, dataCursor, sector->data);
write_bits(bits, cursor, headerBits);
uint32_t headerChecksum = amigaChecksum(toBytes(headerBits));
write_interleaved_bytes(bits, cursor, headerChecksum);
uint32_t dataChecksum = amigaChecksum(toBytes(dataBits));
write_interleaved_bytes(bits, cursor, dataChecksum);
write_bits(bits, cursor, dataBits);
Bytes data = sector->data.slice(0, 512);
write_interleaved_word(amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
write_interleaved_bytes(data);
}
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
@@ -105,11 +102,11 @@ std::unique_ptr<Fluxmap> AmigaEncoder::encode(
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
int bitsPerRevolution = 200000.0 / clockRateUs;
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false });
fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false });
lastBit = false;
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
@@ -123,7 +120,7 @@ std::unique_ptr<Fluxmap> AmigaEncoder::encode(
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
fluxmap->appendBits(bits, _config.clock_rate_us()*1e3);
return fluxmap;
}

View File

@@ -9,10 +9,12 @@
class Sector;
class Fluxmap;
class Apple2DecoderProto;
class Apple2Decoder : public AbstractDecoder
{
public:
Apple2Decoder(const Apple2DecoderProto&) {}
virtual ~Apple2Decoder() {}
RecordType advanceToNextRecord();

4
arch/apple2/apple2.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message Apple2DecoderProto {}

View File

@@ -14,11 +14,15 @@
#define BROTHER_SECTORS_PER_TRACK 12
class Sector;
class SectorSet;
class Fluxmap;
class BrotherDecoderProto;
class BrotherEncoderProto;
class BrotherDecoder : public AbstractDecoder
{
public:
BrotherDecoder(const BrotherDecoderProto& config) {}
virtual ~BrotherDecoder() {}
RecordType advanceToNextRecord();
@@ -29,20 +33,17 @@ public:
class BrotherEncoder : public AbstractEncoder
{
public:
BrotherEncoder(int format, int bias):
_format(format),
_bias(bias)
BrotherEncoder(const BrotherEncoderProto& config):
_config(config)
{}
virtual ~BrotherEncoder() {}
private:
int _format;
int _bias;
const BrotherEncoderProto& _config;
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup brotherEncoderFlags;
#endif

View File

@@ -0,0 +1,20 @@
syntax = "proto2";
message BrotherDecoderProto {}
enum BrotherFormat {
BROTHER240 = 0;
BROTHER120 = 1;
};
message BrotherEncoderProto {
optional double clock_rate_us = 1 [default = 3.83];
optional double post_index_gap_ms = 2 [default = 1.0];
optional double sector_spacing_ms = 3 [default = 16.2];
optional double post_header_spacing_ms = 4 [default = 0.69];
optional string sector_skew = 5 [default = "05a3816b4927"];
optional BrotherFormat format = 6 [default = BROTHER240];
optional int32 bias = 7 [default = 0];
}

View File

@@ -6,6 +6,7 @@
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "arch/brother/brother.pb.h"
FlagGroup brotherEncoderFlags;
@@ -132,17 +133,17 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
int logicalTrack;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _bias;
switch (_format)
physicalTrack -= _config.bias();
switch (_config.format())
{
case 120:
case BROTHER120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack/2;
break;
case 240:
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack;

View File

@@ -1,16 +1,42 @@
#ifndef C64_H
#define C64_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define C64_SECTOR_RECORD 0xffd49
#define C64_DATA_RECORD 0xffd57
#define C64_SECTOR_LENGTH 256
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
5. Data block 55...4A (325 GCR bytes)
6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
1. Header sync (SYNC for the next sector)
*/
#define C64_HEADER_DATA_SYNC 0xFF
#define C64_HEADER_BLOCK_ID 0x08
#define C64_DATA_BLOCK_ID 0x07
#define C64_HEADER_GAP 0x55
#define C64_INTER_SECTOR_GAP 0x55
#define C64_PADDING 0x0F
#define C64_TRACKS_PER_DISK 40
#define C64_BAM_TRACK 17
class Sector;
class Fluxmap;
class Commodore64DecoderProto;
class Commodore64EncoderProto;
class Commodore64Decoder : public AbstractDecoder
{
public:
Commodore64Decoder(const Commodore64DecoderProto&) {}
virtual ~Commodore64Decoder() {}
RecordType advanceToNextRecord();
@@ -18,4 +44,25 @@ public:
void decodeDataRecord();
};
class Commodore64Encoder : public AbstractEncoder
{
public:
Commodore64Encoder(const Commodore64EncoderProto& config):
_config(config)
{}
virtual ~Commodore64Encoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
void writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector) const;
private:
const Commodore64EncoderProto& _config;
uint8_t _formatByte1;
uint8_t _formatByte2;
};
#endif

13
arch/c64/c64.proto Normal file
View File

@@ -0,0 +1,13 @@
syntax = "proto2";
import "lib/common.proto";
message Commodore64DecoderProto {}
message Commodore64EncoderProto {
optional double post_index_gap_us = 1 [default=0.0,
(help) = "post-index gap before first sector header."];
optional double clock_compensation_factor = 2 [default=1.0,
(help) = "scale the output clock by this much."];
}

349
arch/c64/encoder.cc Normal file
View File

@@ -0,0 +1,349 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "c64.h"
#include "crc.h"
#include "sectorset.h"
#include "sector.h"
#include "writer.h"
#include "fmt/format.h"
#include "arch/c64/c64.pb.h"
#include <ctype.h>
#include "bytes.h"
static bool lastBit;
static double clockRateUsForTrack(unsigned track)
{
/*
* Track # Sectors/Track Speed Zone bits/rotation
* 1 17 21 3 61,538.4
* 18 24 19 2 57,142.8
* 25 30 18 1 53,333.4
* 31 35 17 0 50,000.0
*/
if (track < 17)
return 200000.0/61538.4;
if (track < 24)
return 200000.0/57142.8;
if (track < 30)
return 200000.0/53333.4;
return 200000.0/50000.0;
}
static unsigned sectorsForTrack(unsigned track)
{
/*
* Track Sectors/track # Sectors Storage in Bytes
* ----- ------------- --------- ----------------
* 1-17 21 357 7820
* 18-24 19 133 7170
* 25-30 18 108 6300
* 31-40(*) 17 85 6020
* ---
* 683 (for a 35 track image)
*/
if (track < 17)
return 21;
if (track < 24)
return 19;
if (track < 30)
return 18;
return 17;
}
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src) //Range-based for loop
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
void bindump(std::ostream& stream, std::vector<bool>& buffer)
{
size_t pos = 0;
while ((pos < buffer.size()) and (pos <520))
{
stream << fmt::format("{:5d} : ", pos);
for (int i=0; i<40; i++)
{
if ((pos+i) < buffer.size())
stream << fmt::format("{:01b}", (buffer[pos+i]));
else
stream << "-- ";
if ((((pos + i + 1) % 8) == 0) and i != 0)
stream << " ";
}
stream << std::endl;
pos += 40;
}
}
static std::vector<bool> encode_data(uint8_t input)
{
/*
* Four 8-bit data bytes are converted to four 10-bit GCR bytes at a time by
* the 1541 DOS. RAM is only an 8-bit storage device though. This hardware
* limitation prevents a 10-bit GCR byte from being stored in a single
* memory location. Four 10-bit GCR bytes total 40 bits - a number evenly
* divisible by our overriding 8-bit constraint. Commodore sub- divides the
* 40 GCR bits into five 8-bit bytes to solve this dilemma. This explains
* why four 8-bit data bytes are converted to GCR form at a time. The
* following step by step example demonstrates how this bit manipulation is
* performed by the DOS.
*
* STEP 1. Four 8-bit Data Bytes
* $08 $10 $00 $12
*
* STEP 2. Hexadecimal to Binary Conversion
* 1. Binary Equivalents
* $08 $10 $00 $12
* 00001000 00010000 00000000 00010010
*
* STEP 3. Binary to GCR Conversion
* 1. Four 8-bit Data Bytes
* 00001000 00010000 00000000 00010010
* 2. High and Low Nybbles
* 0000 1000 0001 0000 0000 0000 0001 0010
* 3. High and Low Nybble GCR Equivalents
* 01010 01001 01011 01010 01010 01010 01011 10010
* 4. Four 10-bit GCR Bytes
* 0101001001 0101101010 0101001010 0101110010
*
* STEP 4. 10-bit GCR to 8-bit GCR Conversion
* 1. Concatenate Four 10-bit GCR Bytes
* 0101001001010110101001010010100101110010
* 2. Five 8-bit Subdivisions
* 01010010 01010110 10100101 00101001 01110010
*
* STEP 5. Binary to Hexadecimal Conversion
* 1. Hexadecimal Equivalents
* 01010010 01010110 10100101 00101001 01110010
* $52 $56 $A5 $29 $72
*
* STEP 6. Four 8-bit Data Bytes are Recorded as Five 8-bit GCR Bytes
* $08 $10 $00 $12
*
* are recorded as
* $52 $56 $A5 $29 $72
*/
std::vector<bool> output(10, false);
uint8_t hi = 0;
uint8_t lo = 0;
uint8_t lo_GCR = 0;
uint8_t hi_GCR = 0;
//Convert the byte in high and low nibble
lo = input >> 4; //get the lo nibble shift the bits 4 to the right
hi = input & 15; //get the hi nibble bij masking the lo bits (00001111)
lo_GCR = encode_data_gcr(lo); //example value: 0000 GCR = 01010
hi_GCR = encode_data_gcr(hi); //example value: 1000 GCR = 01001
//output = [0,1,2,3,4,5,6,7,8,9]
//value = [0,1,0,1,0,0,1,0,0,1]
// 01010 01001
int b = 4;
for (int i = 0; i < 10; i++)
{
if (i < 5) //01234
{ //i = 0 op
output[4-i] = (lo_GCR & 1); //01010
//01010 -> & 00001 -> 00000 output[4] = 0
//00101 -> & 00001 -> 00001 output[3] = 1
//00010 -> & 00001 -> 00000 output[2] = 0
//00001 -> & 00001 -> 00001 output[1] = 1
//00000 -> & 00001 -> 00000 output[0] = 0
lo_GCR >>= 1;
} else
{
output[i+b] = (hi_GCR & 1); //01001
//01001 -> & 00001 -> 00001 output[9] = 1
//00100 -> & 00001 -> 00000 output[8] = 0
//00010 -> & 00001 -> 00000 output[7] = 0
//00001 -> & 00001 -> 00001 output[6] = 1
//00000 -> & 00001 -> 00000 output[5] = 0
hi_GCR >>= 1;
b = b-2;
}
}
return output;
}
void Commodore64Encoder::writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector) const
{
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
* 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
* 2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
* 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
* 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
* 5. Data block 55...4A (325 GCR bytes)
* 6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
* 1. Header sync (SYNC for the next sector)
*/
if ((sector->status == Sector::OK) or (sector->status == Sector::BAD_CHECKSUM))
{
// There is data to encode to disk.
if ((sector->data.size() != C64_SECTOR_LENGTH))
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector->data.size());
// 1. Write header Sync (not GCR)
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 2. Write Header info 10 GCR bytes
/*
* The 10 byte header info (#2) is GCR encoded and must be decoded to
* it's normal 8 bytes to be understood. Once decoded, its breakdown is
* as follows:
*
* Byte $00 - header block ID ($08)
* 01 - header block checksum 16 (EOR of $02-$05)
* 02 - Sector
* 03 - Track
* 04 - Format ID byte #2
* 05 - Format ID byte #1
* 06-07 - $0F ("off" bytes)
*/
uint8_t encodedTrack = ((sector->logicalTrack) + 1); // C64 track numbering starts with 1. Fluxengine with 0.
uint8_t encodedSector = sector->logicalSector;
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
// uint8_t formatByte2 = C64_FORMAT_ID_BYTE2;
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ _formatByte1 ^ _formatByte2);
write_bits(bits, cursor, encode_data(C64_HEADER_BLOCK_ID));
write_bits(bits, cursor, encode_data(headerChecksum));
write_bits(bits, cursor, encode_data(encodedSector));
write_bits(bits, cursor, encode_data(encodedTrack));
write_bits(bits, cursor, encode_data(_formatByte2));
write_bits(bits, cursor, encode_data(_formatByte1));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
// 3. Write header GAP not GCR
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_HEADER_GAP, 1*8); /* header gap */
// 4. Write Data sync not GCR
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 5. Write data block 325 GCR bytes
/*
* The 325 byte data block (#5) is GCR encoded and must be decoded to its
* normal 260 bytes to be understood. The data block is made up of the following:
*
* Byte $00 - data block ID ($07)
* 01-100 - 256 bytes data
* 101 - data block checksum (EOR of $01-100)
* 102-103 - $00 ("off" bytes, to make the sector size a multiple of 5)
*/
write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID));
uint8_t dataChecksum = xorBytes(sector->data);
ByteReader br(sector->data);
int i = 0;
for (i = 0; i < C64_SECTOR_LENGTH; i++)
{
uint8_t val = br.read_8();
write_bits(bits, cursor, encode_data(val));
}
write_bits(bits, cursor, encode_data(dataChecksum));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
//6. Write inter-sector gap 9 - 12 bytes nor gcr
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_INTER_SECTOR_GAP, 1*8); /* sync */
}
}
std::unique_ptr<Fluxmap> Commodore64Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
/* The format ID Character # 1 and # 2 are in the .d64 image only present
* in track 18 sector zero which contains the BAM info in byte 162 and 163.
* it is written in every header of every sector and track. headers are not
* stored in a d64 disk image so we have to get it from track 18 which
* contains the BAM.
*/
const auto& sectorData = allSectors.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
if (sectorData)
{
ByteReader br(sectorData->data);
br.seek(162); //goto position of the first Disk ID Byte
_formatByte1 = br.read_8();
_formatByte2 = br.read_8();
}
else
_formatByte1 = _formatByte2 = 0;
int logicalTrack = physicalTrack / 2;
double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(logicalTrack);
unsigned writtenSectors = 0;
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (sectorData)
{
writeSector(bits, cursor, sectorData);
writtenSectors++;
}
}
if (writtenSectors == 0)
return std::unique_ptr<Fluxmap>();
if (cursor >= bits.size())
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
// vim: sw=4 ts=4 et

View File

@@ -7,10 +7,12 @@
class Sector;
class Fluxmap;
class F85DecoderProto;
class DurangoF85Decoder : public AbstractDecoder
{
public:
DurangoF85Decoder(const F85DecoderProto&) {}
virtual ~DurangoF85Decoder() {}
RecordType advanceToNextRecord();

4
arch/f85/f85.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message F85DecoderProto {}

View File

@@ -8,10 +8,12 @@
class Sector;
class Fluxmap;
class Track;
class Fb100DecoderProto;
class Fb100Decoder : public AbstractDecoder
{
public:
Fb100Decoder(const Fb100DecoderProto&) {}
virtual ~Fb100Decoder() {}
RecordType advanceToNextRecord();

4
arch/fb100/fb100.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message Fb100DecoderProto {}

View File

@@ -6,6 +6,8 @@
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "record.h"
#include "arch/ibm/ibm.pb.h"
#include "proto.h"
#include <string.h>
static_assert(std::is_trivially_copyable<IbmIdam>::value,
@@ -89,6 +91,11 @@ const FluxMatchers ANY_RECORD_PATTERN(
}
);
std::set<unsigned> IbmDecoder::requiredSectors(Track& track) const
{
return iterate(_config.required_sectors());
}
AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
@@ -131,14 +138,14 @@ void IbmDecoder::decodeSectorRecord()
br.read_8(); /* skip ID byte */
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalSector = br.read_8() - _sectorBase;
_sector->logicalSector = br.read_8() - _config.sector_id_base();
_currentSectorSize = 1 << (br.read_8() + 7);
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
if (wantCrc == gotCrc)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
if (_ignoreSideByte)
if (_config.ignore_side_byte())
_sector->logicalSide = _sector->physicalSide;
}

View File

@@ -6,6 +6,7 @@
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "arch/ibm/ibm.pb.h"
#include "fmt/format.h"
#include <ctype.h>
@@ -76,21 +77,6 @@ void IbmEncoder::writeRawBits(uint32_t data, int width)
}
}
void IbmEncoder::writeBytes(const Bytes& bytes)
{
if (_parameters.useFm)
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void IbmEncoder::writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
@@ -99,24 +85,56 @@ static uint8_t decodeUint16(uint16_t raw)
return decodeFmMfm(b.toBits())[0];
}
void IbmEncoder::getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
}
std::unique_ptr<Fluxmap> IbmEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
if (_parameters.swapSides)
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
auto writeBytes = [&](const Bytes& bytes)
{
if (trackdata.use_fm())
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
};
auto writeFillerBytes = [&](int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
};
if (trackdata.swap_sides())
physicalSide = 1 - physicalSide;
double clockRateUs = 1e3 / _parameters.clockRateKhz;
if (!_parameters.useFm)
double clockRateUs = 1e3 / trackdata.clock_rate_khz();
if (!trackdata.use_fm())
clockRateUs /= 2.0;
int bitsPerRevolution = (_parameters.trackLengthMs * 1000.0) / clockRateUs;
int bitsPerRevolution = (trackdata.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(_parameters.idamByte);
uint8_t damUnencoded = decodeUint16(_parameters.damByte);
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
uint8_t sectorSize = 0;
{
int s = _parameters.sectorSize >> 7;
int s = trackdata.sector_size() >> 7;
while (s > 1)
{
s >>= 1;
@@ -124,32 +142,35 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
}
}
uint8_t gapFill = _parameters.useFm ? 0x00 : 0x4e;
uint8_t gapFill = trackdata.use_fm() ? 0x00 : 0x4e;
writeBytes(_parameters.gap0, gapFill);
if (_parameters.emitIam)
writeFillerBytes(trackdata.gap0(), gapFill);
if (trackdata.emit_iam())
{
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
writeRawBits(_parameters.useFm ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeBytes(_parameters.gap1, gapFill);
writeRawBits(trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeFillerBytes(trackdata.gap1(), gapFill);
}
bool first = true;
for (char sectorChar : _parameters.sectorSkew)
for (char sectorChar : trackdata.sector_skew())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_parameters.gap3, gapFill);
writeFillerBytes(trackdata.gap3(), gapFill);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
{
/* If there are any missing sectors, this is an empty track. */
return std::unique_ptr<Fluxmap>();
}
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
@@ -163,8 +184,8 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
Bytes header;
ByteWriter bw(header);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
@@ -172,53 +193,53 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector + _parameters.startSectorId);
bw.write_8(sectorData->logicalSector + trackdata.start_sector_id());
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(_parameters.idamByte, 16);
writeRawBits(trackdata.idam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeBytes(_parameters.gap2, gapFill);
writeFillerBytes(trackdata.gap2(), gapFill);
{
Bytes data;
ByteWriter bw(data);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize);
Bytes truncatedData = sectorData->data.slice(0, trackdata.sector_size());
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(_parameters.damByte, 16);
writeRawBits(trackdata.dam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
@@ -228,7 +249,7 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, gapFill);
writeFillerBytes(1, gapFill);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);

View File

@@ -3,6 +3,7 @@
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "arch/ibm/ibm.pb.h"
/* IBM format (i.e. ordinary PC floppies). */
@@ -32,51 +33,27 @@ struct IbmIdam
class IbmDecoder : public AbstractDecoder
{
public:
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false,
const std::set<unsigned> requiredSectors=std::set<unsigned>()):
_sectorBase(sectorBase),
_ignoreSideByte(ignoreSideByte),
_requiredSectors(requiredSectors)
IbmDecoder(const IbmDecoderProto& config):
_config(config)
{}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const
{ return _requiredSectors; }
std::set<unsigned> requiredSectors(Track& track) const;
private:
unsigned _sectorBase;
bool _ignoreSideByte;
std::set<unsigned> _requiredSectors;
const IbmDecoderProto& _config;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
struct IbmParameters
{
int trackLengthMs;
int sectorSize;
bool emitIam;
int startSectorId;
int clockRateKhz;
bool useFm;
uint16_t idamByte;
uint16_t damByte;
int gap0;
int gap1;
int gap2;
int gap3;
std::string sectorSkew;
bool swapSides;
};
class IbmEncoder : public AbstractEncoder
{
public:
IbmEncoder(const IbmParameters& parameters):
_parameters(parameters)
IbmEncoder(const IbmEncoderProto& config):
_config(config)
{}
virtual ~IbmEncoder() {}
@@ -86,12 +63,12 @@ public:
private:
void writeRawBits(uint32_t data, int width);
void writeBytes(const Bytes& bytes);
void writeBytes(int count, uint8_t value);
void writeSync();
void getTrackFormat(IbmEncoderProto::TrackdataProto& format, unsigned track, unsigned side);
private:
IbmParameters _parameters;
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;

34
arch/ibm/ibm.proto Normal file
View File

@@ -0,0 +1,34 @@
syntax = "proto2";
import "lib/common.proto";
message IbmDecoderProto {
optional int32 sector_id_base = 1 [default = 1, (help) = "ID of first sector"];
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
optional RangeProto required_sectors = 3 [(help) = "require these sectors to exist for a good read"];
}
message IbmEncoderProto {
message TrackdataProto {
optional int32 cylinder = 15 [(help) = "if set, the format applies only to this track"];
optional int32 head = 16 [(help) = "if set, the format applies only to this head"];
optional double track_length_ms = 1 [(help) = "length of track"];
optional int32 sector_size = 2 [default=512, (help) = "number of bytes per sector"];
optional bool emit_iam = 3 [default=true, (help) = "whether to emit an IAM record"];
optional int32 start_sector_id = 4 [default=1, (help) = "ID of first sector"];
optional double clock_rate_khz = 5 [(help) = "data clock rate"];
optional bool use_fm = 6 [default=false, (help) = "whether to use FM encoding rather than MFM"];
optional int32 idam_byte = 7 [default=0x5554, (help) = "16-bit raw bit pattern of IDAM byte"];
optional int32 dam_byte = 8 [default=0x5545, (help) = "16-bit raw bit pattern of DAM byte"];
optional int32 gap0 = 9 [default=80, (help) = "size of gap 1 (the post-index gap)"];
optional int32 gap1 = 10 [default=50, (help) = "size of gap 2 (the post-ID gap)"];
optional int32 gap2 = 11 [default=22, (help) = "size of gap 3 (the pre-data gap)"];
optional int32 gap3 = 12 [default=80, (help) = "size of gap 4 (the post-data or format gap)"];
optional string sector_skew = 13 [(help) = "order to emit sectors"];
optional bool swap_sides = 14 [default=false, (help) = "swap side bytes when writing"];
}
repeated TrackdataProto trackdata = 1;
}

View File

@@ -15,10 +15,13 @@
class Sector;
class Fluxmap;
class MacintoshDecoderProto;
class MacintoshEncoderProto;
class MacintoshDecoder : public AbstractDecoder
{
public:
MacintoshDecoder(const MacintoshDecoderProto&) {}
virtual ~MacintoshDecoder() {}
RecordType advanceToNextRecord();
@@ -31,6 +34,7 @@ public:
class MacintoshEncoder : public AbstractEncoder
{
public:
MacintoshEncoder(const MacintoshEncoderProto&) {}
virtual ~MacintoshEncoder() {}
public:

View File

@@ -0,0 +1,5 @@
syntax = "proto2";
message MacintoshDecoderProto {}
message MacintoshEncoderProto {}

View File

@@ -1,14 +1,16 @@
#ifndef ZILOGMCZ_H
#define ZILOGMCZ_H
#ifndef MICROPOLIS_H
#define MICROPOLIS_H
#define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6)
class Sector;
class Fluxmap;
class MicropolisDecoderProto;
class MicropolisDecoder : public AbstractDecoder
{
public:
MicropolisDecoder(const MicropolisDecoderProto&) {}
virtual ~MicropolisDecoder() {}
RecordType advanceToNextRecord();

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message MicropolisDecoderProto {}

View File

@@ -3,9 +3,12 @@
#include "decoders/decoders.h"
class MxDecoderProto;
class MxDecoder : public AbstractDecoder
{
public:
MxDecoder(const MxDecoderProto&) {}
virtual ~MxDecoder() {}
void beginTrack();

4
arch/mx/mx.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message MxDecoderProto {}

175
arch/northstar/decoder.cc Normal file
View File

@@ -0,0 +1,175 @@
/* Decoder for North Star 10-sector hard-sectored disks.
*
* Supports both single- and double-density. For the sector format and
* checksum algorithm, see pp. 33 of the North Star Double Density Controller
* manual:
*
* http://bitsavers.org/pdf/northstar/boards/Northstar_MDS-A-D_1978.pdf
*
* North Star disks do not contain any track/head/sector information
* encoded in the sector record. For this reason, we have to be absolutely
* sure that the hardSectorId is correct.
*/
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "northstar.h"
#include "bytes.h"
#include "fmt/format.h"
/*
* MFM sectors have 32 bytes of 00's followed by two sync characters,
* specified in the North Star MDS manual as 0xFBFB.
*
* This is true for most disks; however, I found a few disks, including an
* original North Star DOS/BASIC v2.2.1 DQ disk) that uses 0xFBnn, where
* nn is an incrementing pattern.
*
* 00 00 00 F B
* 0000 0000 0000 0000 0000 0000 0101 0101 0100 0101
* A A A A A A 5 5 4 5
*/
static const FluxPattern MFM_PATTERN(64, 0xAAAAAAAAAAAA5545LL);
/* FM sectors have 16 bytes of 00's followed by 0xFB.
* 00 FB
* 0000 0000 1111 1111 1110 1111
* A A F F E F
*/
static const FluxPattern FM_PATTERN(64, 0xAAAAAAAAAAAAFFEFLL);
const FluxMatchers ANY_SECTOR_PATTERN(
{
&MFM_PATTERN,
&FM_PATTERN,
}
);
/* Search for FM or MFM sector record */
AbstractDecoder::RecordType NorthstarDecoder::advanceToNextRecord()
{
nanoseconds_t now = _fmr->tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0) {
_fmr->seekToIndexMark();
now = _fmr->tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (_fmr->getDuration() - 21e6)) {
_fmr->seekToIndexMark();
return(UNKNOWN_RECORD);
}
int msSinceIndex = std::round(now / 1e6);
const FluxMatcher* matcher = nullptr;
/* Note that the seekToPattern ignores the sector pulses, so if
* a sector is not found for some reason, the seek will advance
* past one or more sector pulses. For this reason, calculate
* _hardSectorId after the sector header is found.
*/
_sector->clock = _fmr->seekToPattern(ANY_SECTOR_PATTERN, matcher);
int sectorFoundTimeRaw = std::round((_fmr->tell().ns()) / 1e6);
int sectorFoundTime;
/* Round time to the nearest 20ms */
if ((sectorFoundTimeRaw % 20) < 10) {
sectorFoundTime = (sectorFoundTimeRaw / 20) * 20;
}
else {
sectorFoundTime = ((sectorFoundTimeRaw + 20) / 20) * 20;
}
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
// std::cout << fmt::format(
// "Sector ID {}: hole at {}ms, sector start at {}ms",
// _hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl;
if (matcher == &MFM_PATTERN) {
_sectorType = SECTOR_TYPE_MFM;
readRawBits(48);
return SECTOR_RECORD;
}
if (matcher == &FM_PATTERN) {
_sectorType = SECTOR_TYPE_FM;
readRawBits(48);
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
}
/* Checksum is initially 0.
* For each data byte, XOR with the current checksum.
* Rotate checksum left, carrying bit 7 to bit 0.
*/
uint8_t northstarChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
while (!br.eof()) {
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
return checksum;
}
void NorthstarDecoder::decodeSectorRecord()
{
unsigned recordSize, payloadSize, headerSize;
if (_sectorType == SECTOR_TYPE_MFM) {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
headerSize = NORTHSTAR_HEADER_SIZE_DD;
}
else {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_SD;
headerSize = NORTHSTAR_HEADER_SIZE_SD;
}
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
uint8_t sync_char;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalTrack;
sync_char = br.read_8(); /* Sync char: 0xFB */
if (_sectorType == SECTOR_TYPE_MFM) {
sync_char = br.read_8();/* MFM second Sync char, usually 0xFB */
}
_sector->data = br.read(payloadSize);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize, payloadSize));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> NorthstarDecoder::requiredSectors(Track& track) const
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
return sectors;
}

130
arch/northstar/encoder.cc Normal file
View File

@@ -0,0 +1,130 @@
#include "globals.h"
#include "northstar.h"
#include "sectorset.h"
#define GAP_FILL_SIZE_SD 30
#define PRE_HEADER_GAP_FILL_SIZE_SD 9
#define GAP_FILL_SIZE_DD 62
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define TOTAL_SECTOR_BYTES ()
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
{
int preambleSize = 0;
int encodedSectorSize = 0;
int gapFillSize = 0;
int preHeaderGapFillSize = 0;
bool doubleDensity;
switch (sector->data.size()) {
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else {
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true) {
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true) {
writer.write_8(northstarChecksum(sectorData.slice(2)));
} else {
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (sector->logicalSector != 9) {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize;
} else {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
}
bool lastBit = false;
if (doubleDensity == true) {
encodeMfm(bits, cursor, fullSector, lastBit);
}
else {
encodeFm(bits, cursor, fullSector);
}
}
std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
if ((physicalTrack < 0) || (physicalTrack >= 35))
return std::unique_ptr<Fluxmap>();
const auto& sector = allSectors.get(physicalTrack, physicalSide, 0);
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
} else {
clockRateUs /= 2.00;
}
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
if (cursor > bits.size())
Error() << "track data overrun";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}

View File

@@ -0,0 +1,72 @@
#ifndef NORTHSTAR_H
#define NORTHSTAR_H
/* Northstar floppies are 10-hard sectored disks with a sector format as follows:
*
* |----------------------------------|
* | SYNC Byte | Payload | Checksum |
* |------------+----------+----------|
* | 1 (0xFB) | 256 (SD) | 1 |
* | 2 (0xFBFB) | 512 (DD) | |
* |----------------------------------|
*
*/
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define NORTHSTAR_PREAMBLE_SIZE_SD (16)
#define NORTHSTAR_PREAMBLE_SIZE_DD (32)
#define NORTHSTAR_HEADER_SIZE_SD (1)
#define NORTHSTAR_HEADER_SIZE_DD (2)
#define NORTHSTAR_PAYLOAD_SIZE_SD (256)
#define NORTHSTAR_PAYLOAD_SIZE_DD (512)
#define NORTHSTAR_CHECKSUM_SIZE (1)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_SD (NORTHSTAR_HEADER_SIZE_SD + NORTHSTAR_PAYLOAD_SIZE_SD + NORTHSTAR_CHECKSUM_SIZE)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_DD (NORTHSTAR_HEADER_SIZE_DD + NORTHSTAR_PAYLOAD_SIZE_DD + NORTHSTAR_CHECKSUM_SIZE)
#define SECTOR_TYPE_MFM (0)
#define SECTOR_TYPE_FM (1)
class NorthstarEncoderProto;
class NorthstarDecoderProto;
class NorthstarDecoder : public AbstractDecoder
{
public:
NorthstarDecoder(const NorthstarDecoderProto& config):
_config(config)
{
_sectorType = SECTOR_TYPE_MFM;
}
virtual ~NorthstarDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
std::set<unsigned> requiredSectors(Track& track) const;
private:
const NorthstarDecoderProto& _config;
uint8_t _sectorType;
uint8_t _hardSectorId;
};
class NorthstarEncoder : public AbstractEncoder
{
public:
NorthstarEncoder(const NorthstarEncoderProto& config):
_config(config)
{}
virtual ~NorthstarEncoder() {}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
const NorthstarEncoderProto& _config;
};
extern FlagGroup northstarEncoderFlags;
extern uint8_t northstarChecksum(const Bytes& bytes);
#endif /* NORTHSTAR */

View File

@@ -0,0 +1,5 @@
syntax = "proto2";
message NorthstarDecoderProto {}
message NorthstarEncoderProto {}

View File

@@ -40,7 +40,7 @@ const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
AbstractDecoder::RecordType TiDs990Decoder::advanceToNextRecord()
AbstractDecoder::RecordType Tids990Decoder::advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -51,7 +51,7 @@ AbstractDecoder::RecordType TiDs990Decoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD;
}
void TiDs990Decoder::decodeSectorRecord()
void Tids990Decoder::decodeSectorRecord()
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
@@ -71,7 +71,7 @@ void TiDs990Decoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
}
void TiDs990Decoder::decodeDataRecord()
void Tids990Decoder::decodeDataRecord()
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);

View File

@@ -6,55 +6,9 @@
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "arch/tids990/tids990.pb.h"
#include <fmt/format.h>
FlagGroup tids990EncoderFlags;
static IntFlag trackLengthMs(
{ "--tids990-track-length-ms" },
"Length of a track in milliseconds.",
166);
static IntFlag sectorCount(
{ "--tids990-sector-count" },
"Number of sectors per track.",
26);
static IntFlag clockRateKhz(
{ "--tids990-clock-rate-khz" },
"Clock rate of data to write.",
500);
static HexIntFlag am1Byte(
{ "--tids990-am1-byte" },
"16-bit RAW bit pattern to use for the AM1 ID byte",
0x2244);
static HexIntFlag am2Byte(
{ "--tids990-am2-byte" },
"16-bit RAW bit pattern to use for the AM2 ID byte",
0x2245);
static IntFlag gap1(
{ "--tids990-gap1-bytes" },
"Size of gap 1 (the post-index gap).",
80);
static IntFlag gap2(
{ "--tids990-gap2-bytes" },
"Size of gap 2 (the post-ID gap).",
21);
static IntFlag gap3(
{ "--tids990-gap3-bytes" },
"Size of gap 3 (the post-data or format gap).",
51);
static StringFlag sectorSkew(
{ "--tids990-sector-skew" },
"Order to emit sectors.",
"1mhc72nid83oje94pkfa50lgb6");
static int charToInt(char c)
{
if (isdigit(c))
@@ -62,7 +16,7 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a';
}
void TiDs990Encoder::writeRawBits(uint32_t data, int width)
void Tids990Encoder::writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
@@ -75,12 +29,12 @@ void TiDs990Encoder::writeRawBits(uint32_t data, int width)
}
}
void TiDs990Encoder::writeBytes(const Bytes& bytes)
void Tids990Encoder::writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void TiDs990Encoder::writeBytes(int count, uint8_t byte)
void Tids990Encoder::writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
@@ -95,25 +49,25 @@ static uint8_t decodeUint16(uint16_t raw)
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> TiDs990Encoder::encode(
std::unique_ptr<Fluxmap> Tids990Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
double clockRateUs = 1e3 / clockRateKhz / 2.0;
int bitsPerRevolution = (trackLengthMs * 1000.0) / clockRateUs;
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t am1Unencoded = decodeUint16(am1Byte);
uint8_t am2Unencoded = decodeUint16(am2Byte);
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
writeBytes(gap1, 0x55);
writeBytes(_config.gap1_bytes(), 0x55);
bool first = true;
for (char sectorChar : sectorSkew.get())
for (char sectorChar : _config.sector_skew())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(gap3, 0x55);
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
@@ -136,17 +90,17 @@ std::unique_ptr<Fluxmap> TiDs990Encoder::encode(
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorCount);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeRawBits(am1Byte, 16);
writeRawBits(_config.am1_byte(), 16);
writeBytes(header.slice(1));
}
writeBytes(gap2, 0x55);
writeBytes(_config.gap2_bytes(), 0x55);
{
Bytes data;
@@ -159,7 +113,7 @@ std::unique_ptr<Fluxmap> TiDs990Encoder::encode(
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeRawBits(am2Byte, 16);
writeRawBits(_config.am2_byte(), 16);
writeBytes(data.slice(1));
}
}

View File

@@ -9,21 +9,27 @@ class Sector;
class SectorSet;
class Fluxmap;
class Track;
class Tids990DecoderProto;
class Tids990EncoderProto;
class TiDs990Decoder : public AbstractDecoder
class Tids990Decoder : public AbstractDecoder
{
public:
virtual ~TiDs990Decoder() {}
Tids990Decoder(const Tids990DecoderProto&) {}
virtual ~Tids990Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
class TiDs990Encoder : public AbstractEncoder
class Tids990Encoder : public AbstractEncoder
{
public:
virtual ~TiDs990Encoder() {}
Tids990Encoder(const Tids990EncoderProto& config):
_config(config)
{}
virtual ~Tids990Encoder() {}
private:
void writeRawBits(uint32_t data, int width);
@@ -35,6 +41,7 @@ public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;

View File

@@ -0,0 +1,26 @@
syntax = "proto2";
import "lib/common.proto";
message Tids990DecoderProto {}
message Tids990EncoderProto {
optional double track_length_ms = 1 [ default = 166,
(help) = "length of a track" ];
optional int32 sector_count = 2 [ default = 26,
(help) = "number of sectors per track" ];
optional double clock_rate_khz = 3 [ default = 500,
(help) = "clock rate of data to write" ];
optional int32 am1_byte = 4 [ default = 0x2244,
(help) = "16-bit RAW bit pattern to use for the AM1 ID byte" ];
optional int32 am2_byte = 5 [ default = 0x2245,
(help) = "16-bit RAW bit pattern to use for the AM2 ID byte" ];
optional int32 gap1_bytes = 6 [ default = 80,
(help) = "size of gap 1 (the post-index gap)" ];
optional int32 gap2_bytes = 7 [ default = 21,
(help) = "size of gap 2 (the post-ID gap)" ];
optional int32 gap3_bytes = 8 [ default = 51,
(help) = "size of gap 3 (the post-data or format gap)" ];
optional string sector_skew = 9 [ default = "1mhc72nid83oje94pkfa50lgb6",
(help) = "order to emit sectors" ];
}

View File

@@ -8,10 +8,12 @@
class Sector;
class Fluxmap;
class Victor9kDecoderProto;
class Victor9kDecoder : public AbstractDecoder
{
public:
Victor9kDecoder(const Victor9kDecoderProto&) {}
virtual ~Victor9kDecoder() {}
RecordType advanceToNextRecord();

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message Victor9kDecoderProto {}

View File

@@ -3,10 +3,12 @@
class Sector;
class Fluxmap;
class ZilogMczDecoderProto;
class ZilogMczDecoder : public AbstractDecoder
{
public:
ZilogMczDecoder(const ZilogMczDecoderProto&) {}
virtual ~ZilogMczDecoder() {}
RecordType advanceToNextRecord();

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message ZilogMczDecoderProto {}

View File

@@ -50,7 +50,7 @@ namespace agg
template<class FilterF> void calculate(const FilterF& filter,
bool normalization=true)
{
filter; // prevent erroneous C4100 in MSVC
(void)filter; // prevent erroneous C4100 in MSVC
double r = filter.radius();
realloc_lut(r);
unsigned i;

View File

@@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.1)
project(snowhouse)
option(SNOWHOUSE_BUILD_TESTS "Build the Snowhouse tests" OFF)
option(SNOWHOUSE_RUN_TESTS "Run the Snowhouse tests" OFF)
set(SNOWHOUSE_CXX_STANDARD "C++11" CACHE STRING "The C++ standard the examples are compiled with")
set_property(CACHE SNOWHOUSE_CXX_STANDARD PROPERTY STRINGS "C++11" "C++14" "C++17")
add_library(snowhouse INTERFACE)
target_include_directories(snowhouse INTERFACE include)
if(SNOWHOUSE_CXX_STANDARD STREQUAL "C++11")
set(CMAKE_CXX_STANDARD 11)
elseif(SNOWHOUSE_CXX_STANDARD STREQUAL "C++14")
set(CMAKE_CXX_STANDARD 14)
elseif(SNOWHOUSE_CXX_STANDARD STREQUAL "C++17")
set(CMAKE_CXX_STANDARD 17)
else()
message(WARNING "C++ standard \"${SNOWHOUSE_CXX_STANDARD}\" not known, falling back to default")
endif()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin)
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP ")
else()
# Assume GCC-style arguments
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
-Wall -Wextra -pedantic -Wdeprecated -Wdeprecated-declarations -Wnon-virtual-dtor \
-Wshadow -Wfloat-equal -Wundef -Wendif-labels -Wno-error=unknown-pragmas")
endif()
message(STATUS "C++ compiler flags: ${CMAKE_CXX_FLAGS}")
if (SNOWHOUSE_BUILD_TESTS)
FILE(GLOB SnowhouseSpecSourceFiles example/*.cpp)
add_executable(snowhouse-tests ${SnowhouseSpecSourceFiles})
target_link_libraries(snowhouse-tests PRIVATE snowhouse)
endif()
if (SNOWHOUSE_BUILD_TESTS AND SNOWHOUSE_RUN_TESTS)
add_custom_command(TARGET snowhouse-tests
POST_BUILD
COMMAND snowhouse-tests
WORKING_DIRECTORY ./bin)
elseif (SNOWHOUSE_RUN_TESTS)
message(WARNING "Unable to run snowhouse tests - set:\n option(SNOWHOUSE_BUILD_TESTS, \"Build the Snowhouse tests\" ON)\nand clear your CMakeCache.txt")
endif()

View File

@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

560
dep/snowhouse/README.md Normal file
View File

@@ -0,0 +1,560 @@
snowhouse
=========
[![Travis CI Status](https://travis-ci.org/banditcpp/snowhouse.svg?branch=master)](https://travis-ci.org/banditcpp/snowhouse)
[![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/banditcpp/snowhouse?branch=master&svg=true)](https://ci.appveyor.com/project/banditcpp/snowhouse)
[![GitHub Actions CI](https://github.com/banditcpp/snowhouse/workflows/CI/badge.svg)](https://github.com/banditcpp/snowhouse/actions?query=workflow%3ACI+branch%3Amaster)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/6a8695cf158c4f70a2d01517d12c2c13)](https://www.codacy.com/app/sbeyer/snowhouse?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=banditcpp/snowhouse&amp;utm_campaign=Badge_Grade)
An assertion library for C++
Snowhouse is a stand-alone assertion framework for C++.
It is a header-only library.
Snowhouse requires a C++11-compatible compiler since version 5.0.0.
Feel free to use Snowhouse with major version 4 if you want to use it
in a pre-C++11 setting.
Major version 4 is still maintained in the `maint-v4` branch (bug fixes, etc.).
For inclusion in your projects, you have several options:
a) You can copy the code and just use it as the license allows.
b) You can use the `headers-only` branch as a submodule:
```sh
git submodule add -b headers-only https://github.com/banditcpp/snowhouse snowhouse
git submodule update --init --recursive
```
c) If you use CMake >= 3.1 in your project,
you can use Snowhouse with the provided library target.
Assuming you have cloned the `master` branch into a `snowhouse` subdirectory,
your `CMakeLists.txt` might contain lines like the following:
```cmake
add_subdirectory(snowhouse)
add_executable(app main.cpp)
target_link_libraries(app snowhouse)
```
## Usage
```C++
#include <snowhouse/snowhouse.h>
using namespace snowhouse;
int main()
{
std::cout << "Testing that 23 is 23" << std::endl;
AssertThat(23, Is().EqualTo(23));
try
{
AssertThat(12, Is().LessThan(11).And().GreaterThan(99));
}
catch (const AssertionException& ex)
{
std::cout << "Apparently this failed:" << std::endl;
std::cout << ex.what() << std::endl;
}
return 0;
}
```
### Assertions
Snowhouse uses a constraint-based assertion model that is heavily inspired by the
model used in [NUnit](http://nunit.org/). An assertion in Snowhouse is written
using the following format:
```cpp
AssertThat(actual_value, <constraint expression>);
```
where `<constraint expression>` is an expression that `actual_value` is
evaluated against when the test is executed.
Constraint expressions come in two basic forms: composite and fluent expressions.
#### Composite Expressions
With composite expressions, you can create compact, powerful expressions that
combine a set of predefined constraints with ones that you provide yourself.
Example:
```cpp
AssertThat(length, IsGreaterThan(4) && !Equals(10));
```
Composite expressions can be any combination of constraints and the
standard logical C++ operators.
You can also add your own constraints to be used within composite expressions.
#### Fluent Expressions
With fluent expressions, you can create assertions that better convey the intent
of a test without exposing implementation-specific details.
Fluent expressions aim to help you create tests that are not just by developers
for developers, but rather can be read and understood by domain experts.
Fluent expressions also have the ability to make assertions on the elements in a
container in a way you cannot achieve with composite expressions.
Example:
```cpp
AssertThat(length, Is().GreaterThan(4).And().Not().EqualTo(10));
```
### Basic Constraints
#### Equality Constraint
Used to verify equality between actual and expected.
```cpp
AssertThat(x, Equals(12));
AssertThat(x, Is().EqualTo(12));
```
#### EqualityWithDelta Constraint
Used to verify equality between actual and expected,
allowing the two to differ by a delta.
```cpp
AssertThat(2.49, EqualsWithDelta(2.5, 0.1));
AssertThat(2.49, Is().EqualToWithDelta(2.5, 0.1));
```
#### GreaterThan Constraint
Used to verify that actual is greater than a value.
```cpp
AssertThat(x, IsGreaterThan(4));
AssertThat(x, Is().GreaterThan(4));
```
#### LessThan Constraint
Used to verify that actual is less than a value.
```cpp
AssertThat(x, IsLessThan(3));
AssertThat(x, Is().LessThan(3));
```
#### GreaterThanOrEqualTo Constraint
Used to verify that actual is greater than or equal to a value.
```cpp
AssertThat(x, IsGreaterThanOrEqualTo(5));
AssertThat(x, Is().GreaterThanOrEqualTo(5));
```
#### LessThanOrEqualTo Constraint
Used to verify that actual is less than or equal to a value.
```cpp
AssertThat(x, IsLessThanOrEqualTo(6));
AssertThat(x, Is().LessThanOrEqualTo(6));
```
### Pointer Constraints
Used to check for `nullptr` equality.
```cpp
AssertThat(x, IsNull());
AssertThat(x, Is().Null());
```
### String Constraints
String assertions in Snowhouse are used to verify the values of
STL strings (`std::string`).
#### Equality Constraints
Used to verify that actual is equal to an expected value.
```cpp
AssertThat(actual_str, Equals("foo"));
AssertThat(actual_str, Is().EqualTo("foo"));
```
#### Contains Constraint
Used to verify that a string contains a substring.
```cpp
AssertThat(actual_str, Contains("foo"));
AssertThat(actual_str, Is().Containing("foo"));
```
#### EndsWith Constraint
Used to verify that a string ends with an expected substring.
```cpp
AssertThat(actual_str, EndsWith("foo"));
AssertThat(actual_str, Is().EndingWith("foo"));
```
#### StartsWith Constraint
Used to verify that a string starts with an expected substring.
```cpp
AssertThat(actual_str, StartsWith("foo"));
AssertThat(actual_str, Is().StartingWith("foo"));
```
#### HasLength Constraint
Used to verify that a string is of the expected length.
```cpp
AssertThat(actual_str, HasLength(5));
AssertThat(actual_str, Is().OfLength(5));
```
### Constraints on Multiline Strings
If you have a string that contains multiple lines, you can use the collection
constraints to make assertions on the content of that string.
This may be handy if you have a string that, for instance, represents the
resulting content of a file or a network transmission.
Snowhouse can handle both Windows (CR+LF) and Unix (LF) line endings.
```cpp
std::string lines = "First line\r\nSecond line\r\nThird line";
AssertThat(lines, Has().Exactly(1).StartingWith("Second"));
```
### Container Constraints
The following constraints can be applied to containers in the standard template library.
#### Contains Constraint
Used to verify that a container contains an expected value.
```cpp
AssertThat(container, Contains(12));
AssertThat(container, Is().Containing(12));
```
#### HasLength Constraint
Used to verify that a container has the expected length.
```cpp
AssertThat(container, HasLength(3));
AssertThat(container, Is().OfLength(3));
```
#### IsEmpty Constraint
Used to verify that a container is empty.
```cpp
AssertThat(container, IsEmpty());
AssertThat(container, Is().Empty());
```
#### All
Used to verify that all elements of a STL sequence container matches an expectation.
```cpp
AssertThat(container, Has().All().LessThan(5).Or().EqualTo(66));
```
#### AtLeast
Used to verify that at least a specified amount of elements in a STL sequence
container matches an expectation.
```cpp
AssertThat(container, Has().AtLeast(3).StartingWith("foo"));
```
#### AtMost
Used to verify that at most a specified amount of elements in a STL sequence
container matches an expectation.
```cpp
AssertThat(container, Has().AtMost(2).Not().Containing("failed"));
```
#### Exactly
Used to verify that a STL sequence container has exactly a specified amount
of elements that matches an expectation.
```cpp
AssertThat(container, Has().Exactly(3).GreaterThan(10).And().LessThan(20));
```
#### EqualsContainer
Used to verify that two STL sequence containers are equal.
```cpp
AssertThat(container1, EqualsContainer(container2));
AssertThat(container1, Is().EqualToContainer(container2));
```
##### Predicate functions
You can supply a predicate function or a functor to `EqualsContainer` to
customize how to compare the elements in the two containers.
With a predicate function:
```cpp
static bool are_my_types_equal(const my_type& lhs, const my_type& rhs)
{
return lhs.my_val_ == rhs.my_val_;
}
AssertThat(container1, EqualsContainer(container2, are_my_types_equal));
```
With a functor as predicate:
```cpp
struct within_delta
{
within_delta(int delta) : delta_(delta) {}
bool operator()(const my_type& lhs, const my_type& rhs) const
{
return abs(lhs.my_val_ - rhs.my_val_) <= delta_;
}
private:
int delta_;
};
AssertThat(container1, Is().EqualToContainer(container1, within_delta(1));
```
### Exceptions
Exception constraints can be used to verify that your code throws the correct exceptions.
#### AssertThrows
`AssertThrows` succeeds if the exception thrown by the call is of the supplied
type (or one of its subtypes).
```cpp
AssertThrows(std::logic_error, myObject.a_method(42));
```
#### Making Assertions on the Thrown Exceptions
If `AssertThrows` succeeds, it will store the thrown exception so that you can
make more detailed assertions on it.
```cpp
AssertThrows(std::logic_error, myObject.a_method(42));
AssertThat(LastException<std::logic_error>().what(), Is().Containing("logic failure"));
```
The `LastException<>` is available in the scope of the call to `AssertThrows`.
An exception is not available between specs in order to avoid the result of
one spec contaminating another.
### Custom Constraints
You can add your own constraints to Snowhouse to create more expressive specifications.
#### Fulfills Constraints
By defining the following matcher
```cpp
struct IsEvenNumber
{
bool Matches(const int actual) const
{
return (actual % 2) == 0;
}
friend std::ostream& operator<<(std::ostream& stm, const IsEvenNumber& );
};
std::ostream& operator<<(std::ostream& stm, const IsEvenNumber& )
{
stm << "An even number";
return stm;
}
```
You can create the following constraints in Snowhouse:
```cpp
AssertThat(42, Fulfills(IsEvenNumber()));
AssertThat(42, Is().Fulfilling(IsEvenNumber()));
```
Your custom matcher should implement a method called `Matches()` that takes
a parameter of the type you expect and returns true if the passed parameter
fulfills the constraint.
To get more expressive failure messages, you should also implement the
streaming operator as in the example above.
## Getting better output for your types
Whenever Snowhouse prints an error message for a type, it will use the
stream operator for that type, otherwise it will print "[unsupported type]"
as a placeholder.
```cpp
struct MyType { int x; char c; };
AssertThat(myType, Fulfills(MyConstraint());
```
Will output the following if the constraint fails:
```
Expected: To fulfill my constraint
Actual: [unsupported type]
```
If we add a stream operator:
```cpp
std::ostream& operator<<(std::ostream& stream, const MyType& a)
{
stream << a.c << a.x;
return stream;
}
```
the output will be a bit more readable:
```
Expected: To fulfill my constraint
Actual: f23
```
If it is necessary to print an object in a different manner than the
usual output stream operator provides, for example, to output more detailed
information, we can use a specialization of the `Stringizer` class template:
```cpp
namespace snowhouse
{
template<>
struct Stringizer<MyType>
{
static std::string ToString(const MyType& a)
{
std::stringstream stream;
stream << "MyType(x = " << a.x << ", c = " << int(a.c) << "('" << a.c << "'))";
return stream.str();
}
};
}
```
with output:
```
Expected: To fulfill my constraint
Actual: MyType(x = 23, c = 102('f'))
```
## Configurable Failure Handlers
You can provide Snowhouse with custom failure handlers, for example to
call `std::terminate` instead of throwing an exception.
See `DefaultFailureHandler` for an example of a failure handler.
You can derive your own macros with custom failure handlers using
`SNOWHOUSE_ASSERT_THAT` and `SNOWHOUSE_ASSERT_THROWS`.
See the definitions of `AssertThat` and `AssertThrows` for examples of these.
Define `SNOWHOUSE_NO_MACROS` to disable the unprefixed macros `AssertThat`
and `AssertThrows`.
### Example Use Cases
#### Assert Program State
Log an error immediately as we may crash if we try to continue.
Do not attempt to unwind the stack as we may be inside a destructor
or `nothrow` function.
We may want to call `std::terminate`, or attempt to muddle along
with the rest of the program.
#### Assert Program State in Safe Builds
As above, but only in debug builds.
#### Test Assert
Assert that a test behaved as expected.
Throw an exception and let our testing framework deal with the test failure.
## Versioning
Snowhouse uses [Semantic Versioning 2.0.0](http://semver.org/spec/v2.0.0.html) since
version 3.0.0.
The macros `SNOWHOUSE_MAJOR`, `SNOWHOUSE_MINOR` and `SNOWHOUSE_PATCH` are defined
accordingly and `SNOWHOUSE_VERSION` contains the version string.
Note that in prior versions `SNOWHOUSE_VERSION` was the only defined macro.
Compatibility-breaking changes since version 3.0.0:
* Since version 4.0.0, the display of booleans and strings has changed.
Booleans are now displayed as `true` or `false`.
Strings are put into quotation marks for improved readability.
* Since version 5.0.0, the support for C++ versions prior to C++11 are dropped.
The definition of the macro `SNOWHOUSE_HAS_NULLPTR` is removed.
Our exceptions are now derived from the `std::exception` hierarchy,
thus their method names changed.
## Contributing
The development of Snowhouse takes place on [GitHub](//github.com/banditcpp/snowhouse).
Snowhouse is licensed under the Boost Software License.
See LICENSE_1_0.txt for further information.
By making available code for inclusion into Snowhouse (e.g., by opening a
pull request on GitHub), you guarantee that the code is licensed under the
same license as Snowhouse.
Please make sure to be consistent with the project's coding style.
The `.clang-format` file allows easy checking and implementation of the
coding style.
C++ code should comply to C++11.
Please use `__cplusplus` guards if you want to use language features of
a certain C++ version.
## Responsibilities
Snowhouse was originally developed as part of the [Igloo](//github.com/joakimkarlsson/igloo)
testing framework by [Joakim Karlsson](//github.com/joakimkarlsson).
It has been extracted to be usable in other contexts, for example,
[Bandit](//github.com/banditcpp/bandit).
Snowhouse is maintained by [Stephan Beyer](//github.com/sbeyer) since
[October 2016](//twitter.com/JHKarlsson/status/789332548799332352).

View File

@@ -0,0 +1,222 @@
#include <stdexcept>
#include "tests.h"
using namespace snowhouse;
static void throwRuntimeError()
{
throw std::runtime_error("This is expected");
}
struct IgnoreErrors
{
template<typename ExpectedType, typename ActualType>
static void Handle(const ExpectedType&, const ActualType&, const char*, int)
{
}
static void Handle(const std::string&)
{
}
};
void BasicAssertions()
{
describe("Basic assertions");
it("handles integer equality");
{
AssertThat(5, Is().EqualTo(5));
}
it("detects integer inequality");
{
AssertTestFails(AssertThat(5, Is().EqualTo(4)), "equal to 4");
}
it("detects if Not() fails");
{
AssertTestFails(AssertThat(5, Is().Not().EqualTo(5)), "Expected: not equal to 5\nActual: 5\n");
}
it("handles strings");
{
AssertThat(std::string("joakim"), Is().EqualTo(std::string("joakim")));
}
it("handles strings without explicit template specialization");
{
AssertThat("kim", Is().EqualTo("kim"));
}
it("handles GreaterThan()");
{
AssertThat(5, Is().GreaterThan(4));
}
it("detects when GreaterThan() fails");
{
AssertTestFails(AssertThat(5, Is().GreaterThan(5)),
"Expected: greater than 5\nActual: 5\n");
}
it("handles LessThan()");
{
AssertThat(5, Is().LessThan(6));
}
it("detects when LessThan() fails");
{
AssertTestFails(AssertThat(6, Is().LessThan(5)),
"Expected: less than 5\nActual: 6\n");
}
it("throws explicit failure message");
{
AssertTestFails(Assert::Failure("foo"), "foo");
}
it("contains location information");
{
int line;
std::string file;
try
{
Assert::That(5, Equals(2), "filename", 32);
}
catch (const AssertionException& e)
{
line = e.line();
file = e.file();
}
AssertThat(line, Equals(32));
AssertThat(file, Equals("filename"));
}
it("ensures exception is thrown");
{
AssertThrows(std::runtime_error, throwRuntimeError());
}
it("ignores the error");
{
ConfigurableAssert<IgnoreErrors>::That(1, Equals(2));
}
describe("Assertion expression templates");
it("handles integer equality");
{
AssertThat(5, Equals(5));
}
it("detects integer inequality");
{
AssertTestFails(AssertThat(5, Equals(4)), "equal to 4");
}
it("detects if !Equals() fails");
{
AssertTestFails(AssertThat(5, !Equals(5)),
"Expected: not equal to 5\nActual: 5\n");
}
it("handles strings");
{
AssertThat(std::string("joakim"), Equals(std::string("joakim")));
}
it("handles strings without explicit template specialization");
{
AssertThat("kim", Equals("kim"));
}
it("handles IsGreaterThan()");
{
AssertThat(5, IsGreaterThan(4));
}
it("handles IsGreaterThanOrEqualTo()");
{
AssertThat(4, IsGreaterThanOrEqualTo(4));
AssertThat(5, IsGreaterThanOrEqualTo(4));
}
it("detects when IsGreaterThan() fails");
{
AssertTestFails(AssertThat(5, IsGreaterThan(5)),
"Expected: greater than 5\nActual: 5\n");
}
it("detects when IsGreaterThanOrEqualTo() fails");
{
AssertTestFails(AssertThat(4, IsGreaterThanOrEqualTo(5)),
"Expected: greater than or equal to 5\nActual: 4\n");
}
it("handles IsLessThan()");
{
AssertThat(5, IsLessThan(6));
}
it("handles IsLessThanOrEqualTo()");
{
AssertThat(5, IsLessThanOrEqualTo(6));
AssertThat(6, IsLessThanOrEqualTo(6));
}
it("detects when IsLessThan() fails");
{
AssertTestFails(AssertThat(6, IsLessThan(5)),
"Expected: less than 5\nActual: 6\n");
}
it("detects when IsLessThanOrEqualTo() fails");
{
AssertTestFails(AssertThat(6, IsLessThanOrEqualTo(5)),
"Expected: less than or equal to 5\nActual: 6\n");
}
it("handles IsNull()");
{
AssertThat(nullptr, IsNull());
}
it("handles Is().Null()");
{
AssertThat(nullptr, Is().Null());
}
it("handles !IsNull()");
{
int anInt = 0;
AssertThat(&anInt, !IsNull());
}
it("detects when IsNull() fails (real address)");
{
int anInt = 0;
std::ostringstream message;
message << "Expected: equal to nullptr\nActual: " << &anInt << "\n";
AssertTestFails(AssertThat(&anInt, IsNull()), message.str());
}
it("detects when Is().Null() fails");
{
int anInt = 0;
std::ostringstream message;
message << "Expected: equal to nullptr\nActual: " << &anInt << "\n";
AssertTestFails(AssertThat(&anInt, Is().Null()), message.str());
}
it("detects when !IsNull() fails (nullptr)");
{
std::ostringstream message;
message << "Expected: not equal to nullptr\nActual: nullptr\n";
AssertTestFails(AssertThat(nullptr, !IsNull()), message.str());
}
}

View File

@@ -0,0 +1,45 @@
#include "tests.h"
using namespace snowhouse;
void BooleanOperators()
{
describe("Boolean operators");
it("handles IsFalse()");
{
AssertThat(false, IsFalse());
}
it("handles failing IsFalse()");
{
AssertTestFails(AssertThat(true, IsFalse()), "Expected: false");
}
it("handles IsTrue()");
{
AssertThat(true, IsTrue());
}
it("handles failing IsTrue()");
{
AssertTestFails(AssertThat(false, IsTrue()), "Expected: true");
}
it("handles Is().True()");
{
AssertThat(true, Is().True());
AssertTestFails(AssertThat(false, Is().True()), "Expected: true");
}
it("handles Is().False()");
{
AssertThat(false, Is().False());
AssertTestFails(AssertThat(true, Is().False()), "Expected: false");
}
it("treats assert without constraint as boolean constrains");
{
Assert::That(true);
}
}

View File

@@ -0,0 +1,85 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <list>
#include "tests.h"
using namespace snowhouse;
struct my_type
{
explicit my_type(int my_val)
: my_val_(my_val)
{
}
friend bool operator==(const my_type&, const my_type&);
friend bool operator!=(const my_type&, const my_type&);
friend std::ostream& operator<<(std::ostream&, const my_type&);
int my_val_;
};
bool operator==(const my_type& lhs, const my_type& rhs)
{
return lhs.my_val_ == rhs.my_val_;
}
bool operator!=(const my_type& lhs, const my_type& rhs)
{
return !(lhs == rhs);
}
std::ostream& operator<<(std::ostream& stream, const my_type& item)
{
stream << "(my_type: my_val_=" << item.my_val_ << " )";
return stream;
}
static bool are_my_types_equal(const my_type& lhs, const my_type& rhs)
{
return lhs.my_val_ == rhs.my_val_;
}
void ContainerConstraints()
{
describe("Container constraints");
it("is able to compare containers of custom types");
{
const my_type e[] = {my_type(1), my_type(3)};
const std::list<my_type> expected(e, e + sizeof(e) / sizeof(e[0]));
std::list<my_type> my_container_;
my_container_.push_back(my_type(1));
my_container_.push_back(my_type(3));
AssertThat(my_container_, EqualsContainer(expected));
}
it("handles failing comparisons");
{
const my_type e[] = {my_type(1), my_type(2)};
const std::list<my_type> expected(e, e + sizeof(e) / sizeof(e[0]));
std::list<my_type> my_container_;
my_container_.push_back(my_type(1));
my_container_.push_back(my_type(3));
AssertTestFails(AssertThat(my_container_, EqualsContainer(expected)),
"Expected: [ (my_type: my_val_=1 ), (my_type: my_val_=2 ) ]");
}
it("handles comparison with a predicate function");
{
const my_type e[] = {my_type(1), my_type(3)};
const std::list<my_type> expected(e, e + sizeof(e) / sizeof(e[0]));
std::list<my_type> my_container_;
my_container_.push_back(my_type(1));
my_container_.push_back(my_type(3));
AssertThat(my_container_, EqualsContainer(expected, are_my_types_equal));
AssertThat(my_container_, Is().EqualToContainer(expected, are_my_types_equal));
}
}

View File

@@ -0,0 +1,61 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "tests.h"
using namespace snowhouse;
struct IsEvenNumberNoStreamOperator
{
bool Matches(const int actual) const
{
return (actual % 2) == 0;
}
};
struct IsEvenNumberWithStreamOperator
{
bool Matches(const int actual) const
{
return (actual % 2) == 0;
}
friend std::ostream& operator<<(std::ostream& stm,
const IsEvenNumberWithStreamOperator&);
};
std::ostream& operator<<(std::ostream& stm,
const IsEvenNumberWithStreamOperator&)
{
stm << "An even number";
return stm;
}
void CustomMatchers()
{
describe("Custom matchers");
it("handles custom matcher");
{
AssertThat(2, Fulfills(IsEvenNumberNoStreamOperator()));
}
it("handles custom matcher with fluent");
{
AssertThat(2, Is().Fulfilling(IsEvenNumberNoStreamOperator()));
}
it("outputs correct message when fails");
{
AssertTestFails(AssertThat(3, Fulfills(IsEvenNumberNoStreamOperator())),
"Expected: [unsupported type]\nActual: 3");
}
it("outputs correct message when custom stream operator is defined");
{
AssertTestFails(AssertThat(3, Fulfills(IsEvenNumberWithStreamOperator())),
"Expected: An even number\nActual: 3");
}
}

View File

@@ -0,0 +1,105 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <stdexcept>
#include "tests.h"
using namespace snowhouse;
struct ClassWithExceptions
{
int LogicError()
{
throw std::logic_error("not logical!");
}
double RangeError()
{
throw std::range_error("range error!");
}
void NoError()
{
}
};
struct ExpectedException : public std::exception
{
const char* what() const noexcept override
{
return "Description of the exception we expected";
}
};
void ExceptionTests()
{
ClassWithExceptions objectUnderTest;
describe("Exceptions");
it("detects exceptions");
{
AssertThrows(std::exception, objectUnderTest.LogicError());
}
it("asserts on LastException()");
{
AssertThrows(std::logic_error, objectUnderTest.LogicError());
AssertThat(LastException<std::logic_error>().what(), Contains("not logical!"));
}
it("detects when wrong exception is thrown");
{
AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.RangeError()), "Wrong exception");
}
it("prints expected exception type when wrong exception is thrown");
{
AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.RangeError()), "Expected std::logic_error");
}
it("has several exception assertions in same spec");
{
AssertThrows(std::logic_error, objectUnderTest.LogicError());
AssertThat(LastException<std::logic_error>().what(), Contains("not logical!"));
AssertThrows(std::range_error, objectUnderTest.RangeError());
AssertThat(LastException<std::range_error>().what(), Contains("range error!"));
}
it("has several exception assertion for the same exception in same spec");
{
AssertThrows(std::logic_error, objectUnderTest.LogicError());
AssertThat(LastException<std::logic_error>().what(), Contains("not logical!"));
AssertThrows(std::logic_error, objectUnderTest.LogicError());
AssertThat(LastException<std::logic_error>().what(), Contains("not logical!"));
}
it("detects when no exception is thrown");
{
AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.NoError()), "No exception");
}
it("prints expected exception when no exception is thrown");
{
AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.NoError()), "Expected std::logic_error");
}
it("destroys exceptions when out-of-scope");
{
{
AssertThrows(std::logic_error, objectUnderTest.LogicError());
}
AssertThrows(AssertionException, LastException<std::logic_error>());
AssertThat(LastException<AssertionException>().what(), Contains("No exception was stored"));
}
it("prints description of unwanted exception");
{
AssertTestFails(AssertThrows(ExpectedException, objectUnderTest.LogicError()), "Expected ExpectedException. Wrong exception was thrown. Description of unwanted exception: not logical!");
}
}

View File

@@ -0,0 +1,25 @@
#include "tests.h"
using namespace snowhouse;
void ExpressionErrorHandling()
{
describe("Expression error handling");
std::vector<int> collection;
collection.push_back(1);
collection.push_back(2);
collection.push_back(3);
it("reports an invalid All() properly");
{
AssertTestFails(AssertThat(collection, Has().All()),
"The expression after \"all\" operator does not yield any result");
}
it("reports an invalid AtLeast() properly");
{
AssertTestFails(AssertThat(collection, Has().AtLeast(2)),
"The expression after \"at least 2\" operator does not yield any result");
}
}

View File

@@ -0,0 +1,45 @@
#include "tests.h"
using namespace snowhouse;
void BooleanOperators();
void BasicAssertions();
void ContainerConstraints();
void CustomMatchers();
void ExceptionTests();
void ExpressionErrorHandling();
void MapTests();
void OperatorTests();
void SequenceContainerTests();
void StringLineTests();
void StringTests();
void StringizeTests();
int main()
{
std::cout << "Spec for Snowhouse " SNOWHOUSE_VERSION << std::endl;
try
{
BasicAssertions();
BooleanOperators();
ContainerConstraints();
CustomMatchers();
ExceptionTests();
ExpressionErrorHandling();
MapTests();
OperatorTests();
SequenceContainerTests();
StringLineTests();
StringTests();
StringizeTests();
}
catch (const AssertionException& e)
{
std::cout << "Tests failed!" << std::endl;
std::cout << e.what() << std::endl;
return 1;
}
return 0;
}

View File

@@ -0,0 +1,36 @@
#include "tests.h"
using namespace snowhouse;
void MapTests()
{
describe("Containing (std::map)");
std::map<std::string, int> ages;
ages["joakim"] = 38;
ages["maria"] = 36;
ages["hanna"] = 6;
ages["moa"] = 4;
it("determines if key exists");
{
AssertThat(ages, Is().Containing("joakim"));
}
it("gives a proper message when fails");
{
AssertTestFails(AssertThat(ages, Is().Not().Containing("hanna")),
"Expected: not contains \"hanna\"");
}
it("determines if key exists");
{
AssertThat(ages, Contains("joakim"));
}
it("gives a proper message when Contains() fails");
{
AssertTestFails(AssertThat(ages, !Contains("hanna")),
"Expected: not contains \"hanna\"");
}
}

View File

@@ -0,0 +1,132 @@
#include "tests.h"
using namespace snowhouse;
void OperatorTests()
{
describe("Operators");
it("handles &&");
{
AssertThat(5, IsLessThan(6) && IsGreaterThan(4));
}
it("handles And()");
{
AssertThat(5, Is().LessThan(6).And().GreaterThan(4));
}
it("handles failing &&");
{
AssertTestFails(AssertThat(5, IsLessThan(7) && IsGreaterThan(5)),
"less than 7 and greater than 5");
}
it("handles failing And()");
{
AssertTestFails(AssertThat(5, Is().LessThan(7).And().GreaterThan(5)),
"less than 7 and greater than 5");
}
it("handles Or()");
{
AssertThat(12, Is().LessThan(7).Or().GreaterThan(5));
}
it("handles ||");
{
AssertThat(12, IsLessThan(7) || IsGreaterThan(5));
}
it("handles failing Or()");
{
AssertTestFails(AssertThat(67, Is().LessThan(12).Or().GreaterThan(99)),
"less than 12 or greater than 99");
}
it("handles failing ||");
{
AssertTestFails(AssertThat(67, IsLessThan(12) || IsGreaterThan(99)),
"less than 12 or greater than 99");
}
it("handles Not()");
{
AssertThat(5, Is().Not().EqualTo(4));
}
it("handles !");
{
AssertThat(5, !Equals(4));
}
it("handles failing Not()");
{
AssertTestFails(AssertThat(12, Is().Not().EqualTo(12)), "not equal to 12");
}
it("handles failing !");
{
AssertTestFails(AssertThat(12, !Equals(12)), "not equal to 12");
}
it("handles Not() for strings");
{
AssertThat("joakim", Is().Not().EqualTo("harry"));
}
it("handles failing ! for strings");
{
AssertThat("joakim", !Equals("harry"));
}
it("handles both left and right associative operators");
{
AssertThat(5, Is().GreaterThan(4).And().Not().LessThan(3));
}
it("handles both left and right associative operators expression templates");
{
AssertThat(5, IsGreaterThan(4) && !IsLessThan(3));
}
it("yields error on malformed expression");
{
AssertTestFails(AssertThat(4, Is().Not()),
"The expression contains a \"not\" operator without any operand");
}
it("handles failing EqualsWithDelta() when larger than delta");
{
AssertTestFails(AssertThat(3.9, EqualsWithDelta(3, 0.5)),
"Expected: equal to 3 (+/- 0.5)");
}
it("handles failing EqualsWithDelta() when less than delta");
{
AssertTestFails(AssertThat(2.49, EqualsWithDelta(3, 0.5)),
"Expected: equal to 3 (+/- 0.5)");
}
it("handles EqualsWithDelta()");
{
AssertThat(2, EqualsWithDelta(1.9, 0.1));
}
it("handles failing Is().EqualToWithDelta() when larger than delta");
{
AssertTestFails(AssertThat(3.9, Is().EqualToWithDelta(3, 0.5)),
"Expected: equal to 3 (+/- 0.5)");
}
it("handles failing Is().EqualToWithDelta() when less than delta");
{
AssertTestFails(AssertThat(2.49, Is().EqualToWithDelta(3, 0.5)),
"Expected: equal to 3 (+/- 0.5)");
}
it("handles Is().EqualToWithDelta()");
{
AssertThat(2, Is().EqualToWithDelta(1.9, 0.1));
}
}

View File

@@ -0,0 +1,288 @@
#include <deque>
#include <list>
#include <set>
#include <array>
#include <forward_list>
#include "tests.h"
using namespace snowhouse;
static const char* ExpectedActual = "\nActual: [ 1, 2, 3, 5, 8 ]";
template<typename T>
static void insert_numbers(T& container)
{
container.push_back(1);
container.push_back(2);
container.push_back(3);
container.push_back(5);
container.push_back(8);
}
template<>
void insert_numbers(std::multiset<int>& container)
{
container.insert(1);
container.insert(2);
container.insert(3);
container.insert(5);
container.insert(8);
}
template<>
void insert_numbers(std::set<int>& container)
{
container.insert(1);
container.insert(2);
container.insert(3);
container.insert(5);
container.insert(8);
}
template<>
void insert_numbers(std::array<int, 5>& container)
{
container[0] = 1;
container[1] = 2;
container[2] = 3;
container[3] = 5;
container[4] = 8;
}
template<>
void insert_numbers(std::forward_list<int>& container)
{
container.push_front(8);
container.push_front(5);
container.push_front(3);
container.push_front(2);
container.push_front(1);
}
template<typename T>
static void TestHasAll(const T& container)
{
it("handles All()");
{
AssertThat(container, Has().All().GreaterThan(1).Or().LessThan(4));
}
it("handles failing All()");
{
AssertTestFails(AssertThat(container, Has().All().GreaterThan(4)), std::string("Expected: all greater than 4") + ExpectedActual);
}
it("handles invalid expression after All()");
{
AssertTestFails(AssertThat(container, Has().All().Not()), "The expression contains a \"not\" operator without any operand");
}
it("handles no expression after All()");
{
AssertTestFails(AssertThat(container, Has().All()), "The expression after \"all\" operator does not yield any result");
}
}
template<>
void TestHasAll(const std::forward_list<int>&)
{
// The constraint is size-based but there is no size() method available
}
template<typename T>
static void TestLength(const T& container)
{
it("handles HasLength()");
{
AssertThat(container, HasLength(5));
}
it("handles failing HasLength()");
{
AssertTestFails(AssertThat(container, HasLength(7)), std::string("of length 7") + ExpectedActual);
}
it("handles Is().OfLength()");
{
AssertThat(container, Is().OfLength(5));
}
it("handles failing Is().OfLength()");
{
AssertTestFails(AssertThat(container, Is().OfLength(7)), std::string("of length 7") + ExpectedActual);
}
}
template<>
void TestLength(const std::forward_list<int>&)
{
// There is no size() method available
}
template<typename T, typename TEmpty>
static void TestEmpty(const T& container, const TEmpty& is_empty)
{
it("handles IsEmpty()");
{
AssertThat(is_empty, IsEmpty());
}
it("handles failing IsEmpty()");
{
AssertTestFails(AssertThat(container, IsEmpty()), "empty");
}
it("handles Is().Empty()");
{
AssertThat(is_empty, Is().Empty());
}
it("handles failing Is().Empty()");
{
AssertTestFails(AssertThat(container, Is().Empty()), "empty");
}
}
template<typename T>
void TestEmpty(const T& container)
{
T is_empty;
TestEmpty(container, is_empty);
}
template<>
void TestEmpty(const std::array<int, 5>& container)
{
std::array<int, 0> is_empty;
TestEmpty(container, is_empty);
}
template<typename T>
static void SequenceContainerActual()
{
T container;
insert_numbers(container);
TestHasAll(container);
it("handles AtLeast()");
{
AssertThat(container, Has().AtLeast(1).LessThan(5));
}
it("handles failing AtLeast()");
{
AssertTestFails(AssertThat(container, Has().AtLeast(2).LessThan(2)), std::string("Expected: at least 2 less than 2") + ExpectedActual);
}
it("handles Exactly()");
{
AssertThat(container, Has().Exactly(1).EqualTo(3));
}
it("handles failing Exactly()");
{
AssertTestFails(AssertThat(container, Has().Exactly(2).EqualTo(3)), std::string("Expected: exactly 2 equal to 3") + ExpectedActual);
}
it("handles AtMost()");
{
AssertThat(container, Has().AtMost(1).EqualTo(5));
}
it("handles failing AtMost()");
{
AssertTestFails(AssertThat(container, Has().AtMost(1).EqualTo(3).Or().EqualTo(5)), std::string("Expected: at most 1 equal to 3 or equal to 5") + ExpectedActual);
}
it("handles None()");
{
AssertThat(container, Has().None().EqualTo(666));
}
it("handles failing None()");
{
AssertTestFails(AssertThat(container, Has().None().EqualTo(5)), std::string("Expected: none equal to 5") + ExpectedActual);
}
it("handles Contains()");
{
AssertThat(container, Contains(3));
}
it("detects failing Contains()");
{
AssertTestFails(AssertThat(container, Contains(99)), std::string("contains 99") + ExpectedActual);
}
it("handles Is().Containing()");
{
AssertThat(container, Is().Containing(3));
}
it("detects failing Is().Containing()");
{
AssertTestFails(AssertThat(container, Is().Containing(99)), std::string("contains 99") + ExpectedActual);
}
TestLength(container);
TestEmpty(container);
it("handles EqualsContainer()");
{
std::list<int> expected;
expected.assign(container.begin(), container.end());
AssertThat(container, EqualsContainer(expected));
}
it("handles failing EqualsContainer()");
{
const int e[] = {4, 2, 4};
std::list<int> expected(e, e + sizeof(e) / sizeof(e[0]));
AssertTestFails(AssertThat(container, EqualsContainer(expected)), "Expected: [ 4, 2, 4 ]");
}
it("handles Is().EqualToContainer()");
{
std::list<int> expected;
expected.assign(container.begin(), container.end());
AssertThat(container, Is().EqualToContainer(expected));
}
it("handles failing Is().EqualToContainer()");
{
const int e[] = {4, 2, 4};
std::list<int> expected(e, e + sizeof(e) / sizeof(e[0]));
AssertTestFails(AssertThat(container, Is().EqualToContainer(expected)), "Expected: [ 4, 2, 4 ]");
}
}
void SequenceContainerTests()
{
describe("Sequence containers (std::vector)");
SequenceContainerActual<std::vector<int>>();
describe("Sequence containers (std::list)");
SequenceContainerActual<std::list<int>>();
describe("Sequence containers (std::deque)");
SequenceContainerActual<std::deque<int>>();
describe("Sequence containers (std::set)");
SequenceContainerActual<std::set<int>>();
describe("Sequence containers (std::multiset)");
SequenceContainerActual<std::multiset<int>>();
describe("Sequence containers (std::array)");
SequenceContainerActual<std::array<int, 5>>();
describe("Sequence containers (std::forward_list)");
SequenceContainerActual<std::forward_list<int>>();
}

View File

@@ -0,0 +1,173 @@
#include "tests.h"
using namespace snowhouse;
void StringLineTests()
{
describe("String lines");
it("asserts that at least one line in a stream matches");
{
AssertThat("First line\n", Has().AtLeast(1).EqualTo("First line"));
}
it("detects when assertion fails");
{
AssertTestFails(AssertThat("First line\n", Has().AtLeast(1).EqualTo("Second line")), "Expected: at least 1 equal to \"Second line\"");
}
it("handles line missing newline");
{
AssertThat("First line", Has().AtLeast(1).EqualTo("First line"));
}
it("handles several lines");
{
std::string lines = "First line\nSecond line";
AssertThat(lines, Has().Exactly(2).EndingWith("line"));
}
it("handles Windows line endings");
{
std::string lines = "First line\r\nSecond line\r\nThird line";
AssertThat(lines, Has().Exactly(3).EndingWith("line"));
}
it("matches beginning of lines with Windows line endings");
{
std::string lines = "First line\nSecond line\r\nThird line";
AssertThat(lines, Has().Exactly(1).StartingWith("Second"));
}
it("handles empty lines when using Windows line endings");
{
std::string lines = "\r\nSecond line\r\n\r\n";
AssertThat(lines, Has().Exactly(2).OfLength(0));
}
it("handles last line missing newline for Windows line endings");
{
std::string lines = "First line\r\nSecond line";
AssertThat(lines, Has().Exactly(2).EndingWith("line"));
}
it("handles all empty lines");
{
AssertThat("\n\n\n\n\n\n", Has().Exactly(6).OfLength(0));
}
it("handles all empty lines with Windows line endings");
{
AssertThat("\r\n\r\n\r\n", Has().Exactly(3).OfLength(0));
}
describe("StringLineParser");
it("parses an empty string");
{
std::vector<std::string> res;
StringLineParser::Parse("", res);
AssertThat(res, HasLength(0));
}
it("parses a single line");
{
std::vector<std::string> res;
StringLineParser::Parse("Simple line", res);
AssertThat(res, HasLength(1));
AssertThat(res, Has().Exactly(1).EqualTo("Simple line"));
}
it("parses two lines");
{
std::vector<std::string> res;
StringLineParser::Parse("One line\nTwo lines", res);
AssertThat(res, HasLength(2));
AssertThat(res, Has().Exactly(1).EqualTo("One line"));
AssertThat(res, Has().Exactly(1).EqualTo("Two lines"));
}
it("parses three lines");
{
std::vector<std::string> res;
StringLineParser::Parse("One line\nTwo lines\nThree lines", res);
AssertThat(res, HasLength(3));
AssertThat(res, Has().Exactly(1).EqualTo("One line"));
AssertThat(res, Has().Exactly(1).EqualTo("Two lines"));
AssertThat(res, Has().Exactly(1).EqualTo("Three lines"));
}
it("handles string ending with newline");
{
std::vector<std::string> res;
StringLineParser::Parse("One line\n", res);
AssertThat(res, HasLength(1));
AssertThat(res, Has().Exactly(1).EqualTo("One line"));
}
it("handles single line with Windows line ending");
{
std::vector<std::string> res;
StringLineParser::Parse("One line\r\n", res);
AssertThat(res, HasLength(1));
AssertThat(res, Has().Exactly(1).EqualTo("One line"));
}
it("handles two lines with Windows line endings");
{
std::vector<std::string> res;
StringLineParser::Parse("One line\r\nTwo lines", res);
AssertThat(res, HasLength(2));
AssertThat(res, Has().Exactly(1).EqualTo("One line"));
AssertThat(res, Has().Exactly(1).EqualTo("Two lines"));
}
it("handles empty line with newline");
{
std::vector<std::string> res;
StringLineParser::Parse("\n", res);
AssertThat(res, Is().OfLength(1).And().Exactly(1).OfLength(0));
}
it("handles two empty lines");
{
std::vector<std::string> res;
StringLineParser::Parse("\n\n", res);
AssertThat(res, HasLength(2));
AssertThat(res, Has().Exactly(2).OfLength(0));
}
it("handles two empty lines with Windows line endings");
{
std::vector<std::string> res;
StringLineParser::Parse("\r\n\r\n", res);
AssertThat(res, HasLength(2));
AssertThat(res, Has().Exactly(2).OfLength(0));
}
it("handles carriage return only");
{
std::vector<std::string> res;
StringLineParser::Parse("One line\rTwo lines", res);
AssertThat(res, HasLength(2));
AssertThat(res, Has().Exactly(1).EqualTo("One line"));
AssertThat(res, Has().Exactly(1).EqualTo("Two lines"));
}
it("handles carriage return only at end of string");
{
std::vector<std::string> res;
StringLineParser::Parse("One line\r\nTwo lines\r", res);
AssertThat(res, HasLength(2));
AssertThat(res, Has().Exactly(1).EqualTo("One line"));
AssertThat(res, Has().Exactly(1).EqualTo("Two lines"));
}
}

View File

@@ -0,0 +1,78 @@
#include "tests.h"
using namespace snowhouse;
void StringTests()
{
describe("Strings");
it("handles string Contains()");
{
AssertThat("abcdef", Contains("bcde"));
}
it("handles match at beginning of string");
{
AssertThat("abcdef", Contains("a"));
}
it("detects failing Contains()");
{
AssertTestFails(AssertThat("abcdef", Contains("hello")), "contains \"hello\"");
}
it("handles string StartsWith()");
{
AssertThat("abcdef", StartsWith("abc"));
}
it("handles string EndsWith()");
{
AssertThat("abcdef", EndsWith("def"));
}
it("handles operators for strings");
{
AssertThat("abcdef", StartsWith("ab") && EndsWith("ef"));
}
it("handles strings with multiple operators");
{
AssertThat("abcdef", StartsWith("ab") && !EndsWith("qwqw"));
}
it("handles HasLength()");
{
AssertThat("12345", HasLength(5));
}
it("handles failing HasLength()");
{
AssertTestFails(AssertThat("1234", HasLength(5)), "of length 5");
}
it("handles IsEmpty()");
{
AssertThat("", IsEmpty());
}
it("handles failing IsEmpty()");
{
AssertTestFails(AssertThat("not empty", IsEmpty()), "empty");
}
it("handles weird long expressions");
{
AssertThat("12345", HasLength(5) && StartsWith("123") && !EndsWith("zyxxy"));
}
it("handles std::string");
{
AssertThat("12345", Contains(std::string("23")));
}
it("handles simple char");
{
AssertThat("12345", StartsWith('1'));
}
}

View File

@@ -0,0 +1,127 @@
#include "tests.h"
using namespace snowhouse;
namespace
{
// No overload for operator<<(std::ostream&) or specialization of snowhouse::Stringizer
struct WithoutStreamOperator
{
explicit WithoutStreamOperator(int id)
: m_id(id)
{
}
bool operator==(const WithoutStreamOperator& rhs) const
{
return m_id == rhs.m_id;
}
int m_id;
};
// Has operator<<(std::ostream&)
struct WithStreamOperator : public WithoutStreamOperator
{
explicit WithStreamOperator(int id)
: WithoutStreamOperator(id)
{
}
};
std::ostream& operator<<(std::ostream& stream, const WithStreamOperator& a)
{
stream << a.m_id;
return stream;
}
// Has no operator<<(std::ostream&), but a specialization of snowhouse::Stringizer
struct WithoutStreamOperatorButWithStringizer : public WithoutStreamOperator
{
explicit WithoutStreamOperatorButWithStringizer(int id)
: WithoutStreamOperator(id)
{
}
};
}
namespace snowhouse
{
template<>
struct Stringizer<WithoutStreamOperatorButWithStringizer>
{
static std::string ToString(const WithoutStreamOperatorButWithStringizer& value)
{
return snowhouse::Stringize(value.m_id);
}
};
}
void StringizeTests()
{
describe("Stringize");
it("handles types with stream operators");
{
WithStreamOperator a(12);
WithStreamOperator b(13);
AssertTestFails(AssertThat(a, Is().EqualTo(b)), "Expected: equal to 13\nActual: 12");
}
it("handles types without stream operators");
{
WithoutStreamOperator a(12);
WithoutStreamOperator b(13);
AssertTestFails(AssertThat(a, Is().EqualTo(b)), "Expected: equal to [unsupported type]\nActual: [unsupported type]");
}
it("handles types with traits");
{
WithoutStreamOperatorButWithStringizer a(12);
WithoutStreamOperatorButWithStringizer b(13);
AssertTestFails(AssertThat(a, Is().EqualTo(b)), "Expected: equal to 13\nActual: 12");
}
it("provides bools as true or false");
{
AssertTestFails(AssertThat(false, Is().True()), "Expected: true\nActual: false");
}
it("provides strings in quotation marks");
{
AssertTestFails(AssertThat("wrong", Is().EqualTo("right")), "Expected: equal to \"right\"\nActual: \"wrong\"");
}
describe("Stringize expression templates");
it("handles types with stream operators");
{
WithStreamOperator a(12);
WithStreamOperator b(13);
AssertTestFails(AssertThat(a, Equals(b)), "Expected: equal to 13\nActual: 12");
}
it("handles types without stream operators");
{
WithoutStreamOperator a(12);
WithoutStreamOperator b(13);
AssertTestFails(AssertThat(a, Equals(b)), "Expected: equal to [unsupported type]\nActual: [unsupported type]");
}
it("handles types with traits");
{
WithoutStreamOperatorButWithStringizer a(12);
WithoutStreamOperatorButWithStringizer b(13);
AssertTestFails(AssertThat(a, Equals(b)), "Expected: equal to 13\nActual: 12");
}
it("provides bools as true or false");
{
AssertTestFails(AssertThat(true, IsFalse()), "Expected: false\nActual: true");
}
it("provides strings in quotation marks");
{
AssertTestFails(AssertThat("wrong", Equals("right")), "Expected: equal to \"right\"\nActual: \"wrong\"");
}
}

View File

@@ -0,0 +1,31 @@
#ifndef SNOWHOUSE_EXAMPLES_TEST_H
#define SNOWHOUSE_EXAMPLES_TEST_H
#include <snowhouse/snowhouse.h>
// clang-format off
#define AssertTestFails(assertion, expected_error_text) \
std::string SNOWHOUSE_INTERNAL_expected_error = "Test did not fail"; \
try \
{ \
assertion; \
} \
catch (const AssertionException& exception_from_snowhouse_assertion) \
{ \
SNOWHOUSE_INTERNAL_expected_error = exception_from_snowhouse_assertion.what(); \
} \
AssertThat(SNOWHOUSE_INTERNAL_expected_error, Is().Containing(expected_error_text));
// clang-format on
inline void describe(const char* title)
{
std::cout << std::endl
<< title << ":" << std::endl;
}
inline void it(const char* title)
{
std::cout << " - " << title << std::endl;
}
#endif

View File

@@ -0,0 +1,116 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ASSERT_H
#define SNOWHOUSE_ASSERT_H
#include "assertionexception.h"
#include "fluent/expressionbuilder.h"
// clang-format off
#define SNOWHOUSE_ASSERT_THAT(P1, P2, FAILURE_HANDLER) \
::snowhouse::ConfigurableAssert<FAILURE_HANDLER>::That((P1), (P2), __FILE__, __LINE__)
#ifndef SNOWHOUSE_NO_MACROS
# define AssertThat(P1, P2) \
SNOWHOUSE_ASSERT_THAT((P1), (P2), ::snowhouse::DefaultFailureHandler)
#endif
// clang-format on
namespace snowhouse
{
struct DefaultFailureHandler
{
template<typename ExpectedType, typename ActualType>
static void Handle(const ExpectedType& expected, const ActualType& actual, const char* file_name, int line_number)
{
std::ostringstream str;
str << "Expected: " << snowhouse::Stringize(expected) << std::endl;
str << "Actual: " << snowhouse::Stringize(actual) << std::endl;
throw AssertionException(str.str(), file_name, line_number);
}
static void Handle(const std::string& message)
{
throw AssertionException(message);
}
};
template<typename FailureHandler>
struct ConfigurableAssert
{
template<typename ActualType, typename ConstraintListType>
static void That(const ActualType& actual, ExpressionBuilder<ConstraintListType> expression, const char* file_name = "", int line_number = 0)
{
try
{
ResultStack result;
OperatorStack operators;
expression.Evaluate(result, operators, actual);
while (!operators.empty())
{
ConstraintOperator* op = operators.top();
op->PerformOperation(result);
operators.pop();
}
if (result.empty())
{
throw InvalidExpressionException("The expression did not yield any result");
}
if (!result.top())
{
FailureHandler::Handle(expression, actual, file_name, line_number);
}
}
catch (const InvalidExpressionException& e)
{
FailureHandler::Handle("Malformed expression: \"" + snowhouse::Stringize(expression) + "\"\n" + e.what());
}
}
template<typename ConstraintListType>
static void That(const char* actual, ExpressionBuilder<ConstraintListType> expression, const char* file_name = "", int line_number = 0)
{
return That(std::string(actual), expression, file_name, line_number);
}
template<typename ActualType, typename ExpressionType>
static void That(const ActualType& actual, const ExpressionType& expression, const char* file_name = "", int line_number = 0)
{
if (!expression(actual))
{
FailureHandler::Handle(expression, actual, file_name, line_number);
}
}
template<typename ExpressionType>
static void That(const char* actual, const ExpressionType& expression, const char* file_name = "", int line_number = 0)
{
return That(std::string(actual), expression, file_name, line_number);
}
static void That(bool actual)
{
if (!actual)
{
FailureHandler::Handle("Expected: true\nActual: false");
}
}
static void Failure(const std::string& message)
{
FailureHandler::Handle(message);
}
};
using Assert = ConfigurableAssert<DefaultFailureHandler>;
}
#endif

View File

@@ -0,0 +1,44 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ASSERTIONEXCEPTION_H
#define SNOWHOUSE_ASSERTIONEXCEPTION_H
#include <stdexcept>
#include <string>
#include "macros.h"
namespace snowhouse
{
struct AssertionException : public std::runtime_error
{
explicit AssertionException(const std::string& message, const std::string& filename, unsigned int line_number)
: std::runtime_error(message), m_file(filename), m_line(line_number)
{
}
explicit AssertionException(const std::string& message)
: AssertionException(message, "", 0)
{
}
std::string file() const
{
return m_file;
}
unsigned int line() const
{
return m_line;
}
private:
std::string m_file;
unsigned int m_line;
};
}
#endif

View File

@@ -0,0 +1,23 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_CONSTRAINTS_H
#define SNOWHOUSE_CONSTRAINTS_H
#include "containsconstraint.h"
#include "endswithconstraint.h"
#include "equalsconstraint.h"
#include "haslengthconstraint.h"
#include "isemptyconstraint.h"
#include "isgreaterthanconstraint.h"
#include "isgreaterthanorequaltoconstraint.h"
#include "islessthanconstraint.h"
#include "islessthanorequaltoconstraint.h"
#include "startswithconstraint.h"
#include "fulfillsconstraint.h"
#include "equalswithdeltaconstraint.h"
#include "equalscontainerconstraint.h"
#endif

View File

@@ -0,0 +1,82 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_CONTAINSCONSTRAINT_H
#define SNOWHOUSE_CONTAINSCONSTRAINT_H
#include <algorithm>
#include <map>
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ContainerType>
struct find_in_container_traits
{
template<typename ExpectedType>
static bool find(const ContainerType& container, const ExpectedType& expected)
{
return std::find(container.begin(), container.end(), expected) != container.end();
}
};
template<typename KeyType, typename ValueType>
struct find_in_container_traits<std::map<KeyType, ValueType>>
{
template<typename ExpectedType>
static bool find(const std::map<KeyType, ValueType>& container, const ExpectedType& expected)
{
return container.find(expected) != container.end();
}
};
template<typename ExpectedType>
struct ContainsConstraint : Expression<ContainsConstraint<ExpectedType>>
{
ContainsConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return find_in_container_traits<ActualType>::find(actual, m_expected);
}
bool operator()(const std::string& actual) const
{
return actual.find(m_expected) != std::string::npos;
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline ContainsConstraint<ExpectedType> Contains(const ExpectedType& expected)
{
return ContainsConstraint<ExpectedType>(expected);
}
inline ContainsConstraint<std::string> Contains(const char* expected)
{
return ContainsConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<ContainsConstraint<ExpectedType>>
{
static std::string ToString(const ContainsConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "contains " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,54 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ENDSWITHCONSTRAINT_H
#define SNOWHOUSE_ENDSWITHCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct EndsWithConstraint : Expression<EndsWithConstraint<ExpectedType>>
{
EndsWithConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
bool operator()(const std::string& actual) const
{
size_t expectedPos = actual.length() - m_expected.length();
return actual.find(m_expected) == expectedPos;
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline EndsWithConstraint<ExpectedType> EndsWith(const ExpectedType& expected)
{
return EndsWithConstraint<ExpectedType>(expected);
}
inline EndsWithConstraint<std::string> EndsWith(const char* expected)
{
return EndsWithConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<EndsWithConstraint<ExpectedType>>
{
static std::string ToString(const EndsWithConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "ends with " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,78 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_EQUALSCONSTRAINT_H
#define SNOWHOUSE_EQUALSCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct EqualsConstraint : Expression<EqualsConstraint<ExpectedType>>
{
EqualsConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return (m_expected == actual);
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline EqualsConstraint<ExpectedType> Equals(const ExpectedType& expected)
{
return EqualsConstraint<ExpectedType>(expected);
}
inline EqualsConstraint<std::string> Equals(const char* expected)
{
return EqualsConstraint<std::string>(expected);
}
inline EqualsConstraint<bool> IsFalse()
{
return EqualsConstraint<bool>(false);
}
inline EqualsConstraint<bool> IsTrue()
{
return EqualsConstraint<bool>(true);
}
inline EqualsConstraint<std::nullptr_t> IsNull()
{
return EqualsConstraint<std::nullptr_t>(nullptr);
}
template<>
struct Stringizer<EqualsConstraint<bool>>
{
static std::string ToString(const EqualsConstraint<bool>& constraint)
{
return constraint.m_expected ? "true" : "false";
}
};
template<typename ExpectedType>
struct Stringizer<EqualsConstraint<ExpectedType>>
{
static std::string ToString(const EqualsConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "equal to " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,75 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_EQUALSCONTAINERCONSTRAINT_H
#define SNOWHOUSE_EQUALSCONTAINERCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
namespace constraint_internal
{
template<typename T>
inline bool default_comparer(const T& lhs, const T& rhs)
{
return lhs == rhs;
}
}
template<typename ExpectedType, typename BinaryPredicate>
struct EqualsContainerConstraint : Expression<EqualsContainerConstraint<ExpectedType, BinaryPredicate>>
{
EqualsContainerConstraint(const ExpectedType& expected, const BinaryPredicate predicate)
: m_expected(expected), m_predicate(predicate)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
typename ActualType::const_iterator actual_it;
typename ExpectedType::const_iterator expected_it;
for (actual_it = actual.begin(), expected_it = m_expected.begin(); actual_it != actual.end() && expected_it != m_expected.end(); ++actual_it, ++expected_it)
{
if (!m_predicate(*actual_it, *expected_it))
{
return false;
}
}
return actual_it == actual.end() && expected_it == m_expected.end();
}
const ExpectedType m_expected;
const BinaryPredicate m_predicate;
};
template<typename ExpectedType>
inline EqualsContainerConstraint<ExpectedType, bool (*)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&)> EqualsContainer(const ExpectedType& expected)
{
return EqualsContainerConstraint<ExpectedType, bool (*)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&)>(expected, constraint_internal::default_comparer);
}
template<typename ExpectedType, typename BinaryPredicate>
inline EqualsContainerConstraint<ExpectedType, BinaryPredicate> EqualsContainer(const ExpectedType& expected, const BinaryPredicate predicate)
{
return EqualsContainerConstraint<ExpectedType, BinaryPredicate>(expected, predicate);
}
template<typename ExpectedType, typename BinaryPredicate>
struct Stringizer<EqualsContainerConstraint<ExpectedType, BinaryPredicate>>
{
static std::string ToString(const EqualsContainerConstraint<ExpectedType, BinaryPredicate>& constraint)
{
std::ostringstream builder;
builder << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,50 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_EQUALSWITHDELTACONSTRAINT_H
#define SNOWHOUSE_EQUALSWITHDELTACONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType, typename DeltaType>
struct EqualsWithDeltaConstraint : Expression<EqualsWithDeltaConstraint<ExpectedType, DeltaType>>
{
EqualsWithDeltaConstraint(const ExpectedType& expected, const DeltaType& delta)
: m_expected(expected), m_delta(delta)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return ((m_expected <= (actual + m_delta)) && (m_expected >= (actual - m_delta)));
}
ExpectedType m_expected;
DeltaType m_delta;
};
template<typename ExpectedType, typename DeltaType>
inline EqualsWithDeltaConstraint<ExpectedType, DeltaType> EqualsWithDelta(const ExpectedType& expected, const DeltaType& delta)
{
return EqualsWithDeltaConstraint<ExpectedType, DeltaType>(expected, delta);
}
template<typename ExpectedType, typename DeltaType>
struct Stringizer<EqualsWithDeltaConstraint<ExpectedType, DeltaType>>
{
static std::string ToString(const EqualsWithDeltaConstraint<ExpectedType, DeltaType>& constraint)
{
std::ostringstream builder;
builder << "equal to " << snowhouse::Stringize(constraint.m_expected) << " (+/- " << snowhouse::Stringize(constraint.m_delta) << ")";
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,45 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ANDEXPRESSION_H
#define SNOWHOUSE_ANDEXPRESSION_H
#include "../../stringize.h"
#include "expression_fwd.h"
namespace snowhouse
{
template<typename LeftExpression, typename RightExpression>
struct AndExpression : Expression<AndExpression<LeftExpression, RightExpression>>
{
AndExpression(const LeftExpression& left, const RightExpression& right)
: m_left(left), m_right(right)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return (m_left(actual) && m_right(actual));
}
LeftExpression m_left;
RightExpression m_right;
};
template<typename LeftExpression, typename RightExpression>
struct Stringizer<AndExpression<LeftExpression, RightExpression>>
{
static std::string ToString(const AndExpression<LeftExpression, RightExpression>& expression)
{
std::ostringstream builder;
builder << Stringize(expression.m_left) << " and " << Stringize(expression.m_right);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,37 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_EXPRESSION_H
#define SNOWHOUSE_EXPRESSION_H
#include "notexpression.h"
#include "andexpression.h"
#include "orexpression.h"
namespace snowhouse
{
template<typename T>
struct Expression
{
NotExpression<T> operator!() const
{
return NotExpression<T>(static_cast<const T&>(*this));
}
template<typename Right>
AndExpression<T, Right> operator&&(const Right& right) const
{
return AndExpression<T, Right>(static_cast<const T&>(*this), right);
}
template<typename Right>
OrExpression<T, Right> operator||(const Right& right) const
{
return OrExpression<T, Right>(static_cast<const T&>(*this), right);
}
};
}
#endif

View File

@@ -0,0 +1,15 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_EXPRESSION_FWD_H
#define SNOWHOUSE_EXPRESSION_FWD_H
namespace snowhouse
{
template<typename T>
struct Expression;
}
#endif

View File

@@ -0,0 +1,44 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_NOTEXPRESSION_H
#define SNOWHOUSE_NOTEXPRESSION_H
#include "../../stringize.h"
#include "expression_fwd.h"
namespace snowhouse
{
template<typename ExpressionType>
struct NotExpression : Expression<NotExpression<ExpressionType>>
{
explicit NotExpression(const ExpressionType& expression)
: m_expression(expression)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return !m_expression(actual);
}
ExpressionType m_expression;
};
template<typename ExpressionType>
struct Stringizer<NotExpression<ExpressionType>>
{
static std::string ToString(const NotExpression<ExpressionType>& expression)
{
std::ostringstream builder;
builder << "not " << snowhouse::Stringize(expression.m_expression);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,45 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_OREXPRESSION_H
#define SNOWHOUSE_OREXPRESSION_H
#include "../../stringize.h"
#include "expression_fwd.h"
namespace snowhouse
{
template<typename LeftExpression, typename RightExpression>
struct OrExpression : Expression<OrExpression<LeftExpression, RightExpression>>
{
OrExpression(const LeftExpression& left, const RightExpression& right)
: m_left(left), m_right(right)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return (m_left(actual) || m_right(actual));
}
LeftExpression m_left;
RightExpression m_right;
};
template<typename LeftExpression, typename RightExpression>
struct Stringizer<OrExpression<LeftExpression, RightExpression>>
{
static std::string ToString(const OrExpression<LeftExpression, RightExpression>& expression)
{
std::ostringstream builder;
builder << snowhouse::Stringize(expression.m_left) << " or " << snowhouse::Stringize(expression.m_right);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,49 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_FULFILLSCONSTRAINT_H
#define SNOWHOUSE_FULFILLSCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename MatcherType>
struct FulfillsConstraint : Expression<FulfillsConstraint<MatcherType>>
{
FulfillsConstraint(const MatcherType& matcher)
: m_matcher(matcher)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return m_matcher.Matches(actual);
}
MatcherType m_matcher;
};
template<typename MatcherType>
inline FulfillsConstraint<MatcherType> Fulfills(const MatcherType& matcher)
{
return FulfillsConstraint<MatcherType>(matcher);
}
template<typename MatcherType>
struct Stringizer<FulfillsConstraint<MatcherType>>
{
static std::string ToString(const FulfillsConstraint<MatcherType>& constraint)
{
std::ostringstream builder;
builder << snowhouse::Stringize(constraint.m_matcher);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,56 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_HASLENGTHCONSTRAINT_H
#define SNOWHOUSE_HASLENGTHCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct HasLengthConstraint : Expression<HasLengthConstraint<ExpectedType>>
{
HasLengthConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
using SizeType = typename ActualType::size_type;
SizeType expectedSize = static_cast<SizeType>(m_expected);
return (actual.size() == expectedSize);
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline HasLengthConstraint<ExpectedType> HasLength(const ExpectedType& expected)
{
return HasLengthConstraint<ExpectedType>(expected);
}
inline HasLengthConstraint<std::string> HasLength(const char* expected)
{
return HasLengthConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<HasLengthConstraint<ExpectedType>>
{
static std::string ToString(const HasLengthConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "of length " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,42 @@
// Copyright (C) 2017 Stephan Beyer
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ISEMPTYCONSTRAINT_H
#define SNOWHOUSE_ISEMPTYCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
struct IsEmptyConstraint : Expression<IsEmptyConstraint>
{
// The ignored default argument is a workaround to make this class
// compatible to ConstraintAdapterType
IsEmptyConstraint(int = 0)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return actual.empty();
}
};
inline IsEmptyConstraint IsEmpty()
{
return IsEmptyConstraint();
}
template<>
struct Stringizer<IsEmptyConstraint>
{
static std::string ToString(const IsEmptyConstraint&)
{
return "empty";
}
};
}
#endif

View File

@@ -0,0 +1,54 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ISGREATERTHANCONSTRAINT_H
#define SNOWHOUSE_ISGREATERTHANCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct IsGreaterThanConstraint : Expression<IsGreaterThanConstraint<ExpectedType>>
{
IsGreaterThanConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return (actual > m_expected);
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline IsGreaterThanConstraint<ExpectedType> IsGreaterThan(const ExpectedType& expected)
{
return IsGreaterThanConstraint<ExpectedType>(expected);
}
inline IsGreaterThanConstraint<std::string> IsGreaterThan(const char* expected)
{
return IsGreaterThanConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<IsGreaterThanConstraint<ExpectedType>>
{
static std::string ToString(const IsGreaterThanConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "greater than " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,54 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ISGREATERTHANOREQUALTOCONSTRAINT_H
#define SNOWHOUSE_ISGREATERTHANOREQUALTOCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct IsGreaterThanOrEqualToConstraint : Expression<IsGreaterThanOrEqualToConstraint<ExpectedType>>
{
IsGreaterThanOrEqualToConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return (actual >= m_expected);
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline IsGreaterThanOrEqualToConstraint<ExpectedType> IsGreaterThanOrEqualTo(const ExpectedType& expected)
{
return IsGreaterThanOrEqualToConstraint<ExpectedType>(expected);
}
inline IsGreaterThanOrEqualToConstraint<std::string> IsGreaterThanOrEqualTo(const char* expected)
{
return IsGreaterThanOrEqualToConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<IsGreaterThanOrEqualToConstraint<ExpectedType>>
{
static std::string ToString(const IsGreaterThanOrEqualToConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "greater than or equal to " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,53 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ISLESSTHANCONSTRAINT_H
#define SNOWHOUSE_ISLESSTHANCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct IsLessThanConstraint : Expression<IsLessThanConstraint<ExpectedType>>
{
IsLessThanConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return (actual < m_expected);
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline IsLessThanConstraint<ExpectedType> IsLessThan(const ExpectedType& expected)
{
return IsLessThanConstraint<ExpectedType>(expected);
}
inline IsLessThanConstraint<std::string> IsLessThan(const char* expected)
{
return IsLessThanConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<IsLessThanConstraint<ExpectedType>>
{
static std::string ToString(const IsLessThanConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "less than " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,54 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ISLESSTHANOREQUALTOCONSTRAINT_H
#define SNOWHOUSE_ISLESSTHANOREQUALTOCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct IsLessThanOrEqualToConstraint : Expression<IsLessThanOrEqualToConstraint<ExpectedType>>
{
IsLessThanOrEqualToConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
template<typename ActualType>
bool operator()(const ActualType& actual) const
{
return (actual <= m_expected);
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline IsLessThanOrEqualToConstraint<ExpectedType> IsLessThanOrEqualTo(const ExpectedType& expected)
{
return IsLessThanOrEqualToConstraint<ExpectedType>(expected);
}
inline IsLessThanOrEqualToConstraint<std::string> IsLessThanOrEqualTo(const char* expected)
{
return IsLessThanOrEqualToConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<IsLessThanOrEqualToConstraint<ExpectedType>>
{
static std::string ToString(const IsLessThanOrEqualToConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "less than or equal to " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,53 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_STARTSWITHCONSTRAINT_H
#define SNOWHOUSE_STARTSWITHCONSTRAINT_H
#include "expressions/expression.h"
namespace snowhouse
{
template<typename ExpectedType>
struct StartsWithConstraint : Expression<StartsWithConstraint<ExpectedType>>
{
StartsWithConstraint(const ExpectedType& expected)
: m_expected(expected)
{
}
bool operator()(const std::string& actual) const
{
return actual.find(m_expected) == 0;
}
ExpectedType m_expected;
};
template<typename ExpectedType>
inline StartsWithConstraint<ExpectedType> StartsWith(const ExpectedType& expected)
{
return StartsWithConstraint<ExpectedType>(expected);
}
inline StartsWithConstraint<std::string> StartsWith(const char* expected)
{
return StartsWithConstraint<std::string>(expected);
}
template<typename ExpectedType>
struct Stringizer<StartsWithConstraint<ExpectedType>>
{
static std::string ToString(const StartsWithConstraint<ExpectedType>& constraint)
{
std::ostringstream builder;
builder << "starts with " << snowhouse::Stringize(constraint.m_expected);
return builder.str();
}
};
}
#endif

View File

@@ -0,0 +1,134 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_EXCEPTIONS_H
#define SNOWHOUSE_EXCEPTIONS_H
#include "assert.h"
namespace snowhouse
{
template<typename ExceptionType>
struct ExceptionStorage
{
static void last_exception(ExceptionType*** e, bool clear = false)
{
static ExceptionType* last = nullptr;
if (clear && last)
{
delete last;
return;
}
*e = &last;
silly_warning_about_unused_arg(e);
}
static ExceptionType*** silly_warning_about_unused_arg(ExceptionType*** e)
{
return e;
}
static void store(const ExceptionType& e)
{
ExceptionType** last = nullptr;
last_exception(&last);
if (*last)
{
delete *last;
*last = nullptr;
}
*last = new ExceptionType(e);
}
void compiler_thinks_i_am_unused()
{
}
~ExceptionStorage()
{
ExceptionType** e = nullptr;
last_exception(&e);
if (*e)
{
delete *e;
*e = nullptr;
}
}
};
template<typename ExceptionType>
inline ExceptionType& LastException()
{
ExceptionType** e = nullptr;
ExceptionStorage<ExceptionType>::last_exception(&e);
if (*e == nullptr)
{
Assert::Failure("No exception was stored");
}
return **e;
}
}
// clang-format off
#define SNOWHOUSE_CONCAT2(a, b) a##b
#define SNOWHOUSE_CONCAT(a, b) SNOWHOUSE_CONCAT2(a, b)
#define SNOWHOUSE_LINESUFFIX(a) SNOWHOUSE_CONCAT(a, __LINE__)
#define SNOWHOUSE_TEMPVAR(a) SNOWHOUSE_CONCAT(SNOWHOUSE_, SNOWHOUSE_LINESUFFIX(a ## _))
#define SNOWHOUSE_ASSERT_THROWS(EXCEPTION_TYPE, METHOD, FAILURE_HANDLER_TYPE) \
::snowhouse::ExceptionStorage<EXCEPTION_TYPE> SNOWHOUSE_TEMPVAR(storage); \
SNOWHOUSE_TEMPVAR(storage).compiler_thinks_i_am_unused(); \
bool SNOWHOUSE_TEMPVAR(wrong_exception) = false; \
bool SNOWHOUSE_TEMPVAR(no_exception) = false; \
bool SNOWHOUSE_TEMPVAR(more_info) = true; \
std::string SNOWHOUSE_TEMPVAR(info_string); \
try \
{ \
METHOD; \
SNOWHOUSE_TEMPVAR(no_exception) = true; \
} \
catch (const EXCEPTION_TYPE& SNOWHOUSE_TEMPVAR(catched_exception)) \
{ \
::snowhouse::ExceptionStorage<EXCEPTION_TYPE>::store(SNOWHOUSE_TEMPVAR(catched_exception)); \
} \
catch (...) \
{ \
SNOWHOUSE_TEMPVAR(wrong_exception) = true; \
if (auto eptr = std::current_exception()) { \
try { \
std::rethrow_exception(eptr); \
} catch (const std::exception& e) { \
SNOWHOUSE_TEMPVAR(more_info) = true; \
SNOWHOUSE_TEMPVAR(info_string) = e.what(); \
} catch (...) {} \
} \
} \
if (SNOWHOUSE_TEMPVAR(no_exception)) \
{ \
::std::ostringstream SNOWHOUSE_TEMPVAR(stm); \
SNOWHOUSE_TEMPVAR(stm) << "Expected " #EXCEPTION_TYPE ". No exception was thrown."; \
::snowhouse::ConfigurableAssert<FAILURE_HANDLER_TYPE>::Failure(SNOWHOUSE_TEMPVAR(stm).str()); \
} \
if (SNOWHOUSE_TEMPVAR(wrong_exception)) \
{ \
::std::ostringstream SNOWHOUSE_TEMPVAR(stm); \
SNOWHOUSE_TEMPVAR(stm) << "Expected " #EXCEPTION_TYPE ". Wrong exception was thrown."; \
if (SNOWHOUSE_TEMPVAR(more_info)) { \
SNOWHOUSE_TEMPVAR(stm) << " Description of unwanted exception: " << SNOWHOUSE_TEMPVAR(info_string); \
} \
::snowhouse::ConfigurableAssert<FAILURE_HANDLER_TYPE>::Failure(SNOWHOUSE_TEMPVAR(stm).str()); \
} \
do {} while (false)
#ifndef SNOWHOUSE_NO_MACROS
# define AssertThrows(EXCEPTION_TYPE, METHOD) \
SNOWHOUSE_ASSERT_THROWS(EXCEPTION_TYPE, (METHOD), ::snowhouse::DefaultFailureHandler)
#endif
// clang-format on
#endif

View File

@@ -0,0 +1,42 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_CONSTRAINTADAPTER_H
#define SNOWHOUSE_CONSTRAINTADAPTER_H
#include "../stringize.h"
#include "constraintlist.h"
namespace snowhouse
{
template<typename ConstraintType>
struct ConstraintAdapter
{
explicit ConstraintAdapter(const ConstraintType& constraint)
: m_constraint(constraint)
{
}
template<typename ConstraintListType, typename ActualType>
void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
{
result.push(m_constraint(actual));
EvaluateConstraintList(list.m_tail, result, operators, actual);
}
ConstraintType m_constraint;
};
template<typename ConstraintType>
struct Stringizer<ConstraintAdapter<ConstraintType>>
{
static std::string ToString(const ConstraintAdapter<ConstraintType>& constraintAdapter)
{
return snowhouse::Stringize(constraintAdapter.m_constraint);
}
};
}
#endif

View File

@@ -0,0 +1,102 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_CONSTRAINTLIST_H
#define SNOWHOUSE_CONSTRAINTLIST_H
#include <stack>
namespace snowhouse
{
struct ConstraintOperator;
using ResultStack = std::stack<bool>;
using OperatorStack = std::stack<ConstraintOperator*>;
template<typename HT, typename TT>
struct ConstraintList
{
using HeadType = HT;
using TailType = TT;
ConstraintList(const HeadType& head, const TailType& tail)
: m_head(head), m_tail(tail)
{
}
HeadType m_head;
TailType m_tail;
};
struct Nil
{
Nil()
{
}
Nil(const Nil&)
{
}
};
// ---- These structs defines the resulting types of list concatenation operations
template<typename L1, typename L2>
struct type_concat
{
using t = ConstraintList<typename L1::HeadType, typename type_concat<typename L1::TailType, L2>::t>;
};
template<typename L2>
struct type_concat<Nil, L2>
{
using t = L2;
};
template<typename L3>
inline L3 tr_concat(const Nil&, const Nil&)
{
return Nil();
}
// ---- These structs define the concatenation operations.
template<typename LeftList, typename RightList, typename ResultList>
struct ListConcat
{
static ResultList Concatenate(const LeftList& left, const RightList& right)
{
return ResultList(left.m_head, ListConcat<typename LeftList::TailType, RightList, typename type_concat<typename LeftList::TailType, RightList>::t>::Concatenate(left.m_tail, right));
}
};
// Concatenating an empty list with a second list yields the second list
template<typename RightList, typename ResultList>
struct ListConcat<Nil, RightList, ResultList>
{
static ResultList Concatenate(const Nil&, const RightList& right)
{
return right;
}
};
// Concatenating two empty lists yields an empty list
template<typename ResultList>
struct ListConcat<Nil, Nil, ResultList>
{
static ResultList Concatenate(const Nil&, const Nil&)
{
return Nil();
}
};
// ---- The concatenation operation
template<typename L1, typename L2>
inline typename type_concat<L1, L2>::t Concatenate(const L1& list1, const L2& list2)
{
return ListConcat<L1, L2, typename type_concat<L1, L2>::t>::Concatenate(list1, list2);
}
}
#endif

View File

@@ -0,0 +1,365 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_EXPRESSIONBUILDER_H
#define SNOWHOUSE_EXPRESSIONBUILDER_H
#include "../constraints/constraints.h"
#include "constraintadapter.h"
#include "operators/andoperator.h"
#include "operators/notoperator.h"
#include "operators/oroperator.h"
#include "operators/collections/alloperator.h"
#include "operators/collections/noneoperator.h"
#include "operators/collections/atleastoperator.h"
#include "operators/collections/exactlyoperator.h"
#include "operators/collections/atmostoperator.h"
namespace snowhouse
{
// ---- Evaluation of list of constraints
template<typename ConstraintListType, typename ActualType>
inline void EvaluateConstraintList(ConstraintListType& constraint_list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
{
constraint_list.m_head.Evaluate(constraint_list, result, operators, actual);
}
template<typename ActualType>
inline void EvaluateConstraintList(Nil&, ResultStack&, OperatorStack&, const ActualType&)
{
}
template<typename ConstraintListType>
struct ExpressionBuilder
{
explicit ExpressionBuilder(const ConstraintListType& list)
: m_constraint_list(list)
{
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<ExpectedType>>, Nil>>::t>
EqualTo(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<EqualsConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ExpectedType, typename DeltaType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsWithDeltaConstraint<ExpectedType, DeltaType>>, Nil>>::t>
EqualToWithDelta(const ExpectedType& expected, const DeltaType& delta)
{
using ConstraintAdapterType = ConstraintAdapter<EqualsWithDeltaConstraint<ExpectedType, DeltaType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(EqualsWithDeltaConstraint<ExpectedType, DeltaType>(expected, delta));
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename MatcherType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<FulfillsConstraint<MatcherType>>, Nil>>::t>
Fulfilling(const MatcherType& matcher)
{
using ConstraintAdapterType = ConstraintAdapter<FulfillsConstraint<MatcherType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(matcher);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<bool>>, Nil>>::t>
False()
{
return EqualTo<bool>(false);
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<bool>>, Nil>>::t>
True()
{
return EqualTo<bool>(true);
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<std::nullptr_t>>, Nil>>::t>
Null()
{
return EqualTo<std::nullptr_t>(nullptr);
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<std::string>>, Nil>>::t>
EqualTo(const char* expected)
{
return EqualTo<std::string>(std::string(expected));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsGreaterThanConstraint<ExpectedType>>, Nil>>::t>
GreaterThan(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<IsGreaterThanConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsGreaterThanOrEqualToConstraint<ExpectedType>>, Nil>>::t>
GreaterThanOrEqualTo(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<IsGreaterThanOrEqualToConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsLessThanConstraint<ExpectedType>>, Nil>>::t>
LessThan(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<IsLessThanConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsLessThanOrEqualToConstraint<ExpectedType>>, Nil>>::t>
LessThanOrEqualTo(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<IsLessThanOrEqualToConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<ContainsConstraint<ExpectedType>>, Nil>>::t>
Containing(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<ContainsConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<ContainsConstraint<std::string>>, Nil>>::t>
Containing(const char* expected)
{
return Containing<std::string>(std::string(expected));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EndsWithConstraint<ExpectedType>>, Nil>>::t>
EndingWith(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<EndsWithConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EndsWithConstraint<std::string>>, Nil>>::t>
EndingWith(const char* expected)
{
return EndingWith(std::string(expected));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<StartsWithConstraint<ExpectedType>>, Nil>>::t>
StartingWith(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<StartsWithConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<StartsWithConstraint<std::string>>, Nil>>::t>
StartingWith(const char* expected)
{
return StartingWith(std::string(expected));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<HasLengthConstraint<ExpectedType>>, Nil>>::t>
OfLength(const ExpectedType& expected)
{
using ConstraintAdapterType = ConstraintAdapter<HasLengthConstraint<ExpectedType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(expected);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsEmptyConstraint>, Nil>>::t>
Empty()
{
using ConstraintAdapterType = ConstraintAdapter<IsEmptyConstraint>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(0);
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ExpectedType>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsContainerConstraint<ExpectedType, bool (*)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&)>>, Nil>>::t>
EqualToContainer(const ExpectedType& expected)
{
using DefaultBinaryPredicateType = bool (*)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&);
using ConstraintAdapterType = ConstraintAdapter<EqualsContainerConstraint<ExpectedType, DefaultBinaryPredicateType>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(EqualsContainerConstraint<ExpectedType, DefaultBinaryPredicateType>(expected, constraint_internal::default_comparer<typename ExpectedType::value_type>));
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ExpectedType, typename BinaryPredicate>
ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsContainerConstraint<ExpectedType, BinaryPredicate>>, Nil>>::t>
EqualToContainer(const ExpectedType& expected, const BinaryPredicate predicate)
{
using ConstraintAdapterType = ConstraintAdapter<EqualsContainerConstraint<ExpectedType, BinaryPredicate>>;
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil>>::t>;
ConstraintAdapterType constraint(EqualsContainerConstraint<ExpectedType, BinaryPredicate>(expected, predicate));
ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
using AndOperatorNode = ConstraintList<AndOperator, Nil>;
using OrOperatorNode = ConstraintList<OrOperator, Nil>;
using NotOperatorNode = ConstraintList<NotOperator, Nil>;
using AllOperatorNode = ConstraintList<AllOperator, Nil>;
using AtLeastOperatorNode = ConstraintList<AtLeastOperator, Nil>;
using ExactlyOperatorNode = ConstraintList<ExactlyOperator, Nil>;
using AtMostOperatorNode = ConstraintList<AtMostOperator, Nil>;
using NoneOperatorNode = ConstraintList<NoneOperator, Nil>;
ExpressionBuilder<typename type_concat<ConstraintListType, AllOperatorNode>::t> All()
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, AllOperatorNode>::t>;
AllOperator op;
AllOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, AtLeastOperatorNode>::t> AtLeast(unsigned int expected)
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, AtLeastOperatorNode>::t>;
AtLeastOperator op(expected);
AtLeastOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, ExactlyOperatorNode>::t> Exactly(unsigned int expected)
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, ExactlyOperatorNode>::t>;
ExactlyOperator op(expected);
ExactlyOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, AtMostOperatorNode>::t> AtMost(unsigned int expected)
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, AtMostOperatorNode>::t>;
AtMostOperator op(expected);
AtMostOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, NoneOperatorNode>::t> None()
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, NoneOperatorNode>::t>;
NoneOperator op;
NoneOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, AndOperatorNode>::t> And()
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, AndOperatorNode>::t>;
AndOperator op;
AndOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, OrOperatorNode>::t> Or()
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, OrOperatorNode>::t>;
OrOperator op;
OrOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
ExpressionBuilder<typename type_concat<ConstraintListType, NotOperatorNode>::t> Not()
{
using BuilderType = ExpressionBuilder<typename type_concat<ConstraintListType, NotOperatorNode>::t>;
NotOperator op;
NotOperatorNode node(op, Nil());
return BuilderType(Concatenate(m_constraint_list, node));
}
template<typename ActualType>
void Evaluate(ResultStack& result, OperatorStack& operators, const ActualType& actual)
{
EvaluateConstraintList(m_constraint_list, result, operators, actual);
}
ConstraintListType m_constraint_list;
};
template<typename T>
inline void StringizeConstraintList(const T& list, std::ostringstream& stm)
{
if (stm.tellp() > 0)
stm << " ";
stm << snowhouse::Stringize(list.m_head);
StringizeConstraintList(list.m_tail, stm);
}
inline void StringizeConstraintList(const Nil&, std::ostringstream&)
{
}
template<typename ConstraintListType>
struct Stringizer<ExpressionBuilder<ConstraintListType>>
{
static std::string ToString(const ExpressionBuilder<ConstraintListType>& builder)
{
std::ostringstream stm;
StringizeConstraintList(builder.m_constraint_list, stm);
return stm.str();
}
};
}
#endif

View File

@@ -0,0 +1,24 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_FLUENT_H
#define SNOWHOUSE_FLUENT_H
#include "expressionbuilder.h"
namespace snowhouse
{
inline ExpressionBuilder<Nil> Is()
{
return ExpressionBuilder<Nil>(Nil());
}
inline ExpressionBuilder<Nil> Has()
{
return ExpressionBuilder<Nil>(Nil());
}
}
#endif

View File

@@ -0,0 +1,55 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ANDOPERATOR_H
#define SNOWHOUSE_ANDOPERATOR_H
#include "constraintoperator.h"
namespace snowhouse
{
struct AndOperator : public ConstraintOperator
{
template<typename ConstraintListType, typename ActualType>
void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
{
EvaluateOperatorsWithLessOrEqualPrecedence(*this, operators, result);
operators.push(this);
EvaluateConstraintList(list.m_tail, result, operators, actual);
}
void PerformOperation(ResultStack& result) override
{
if (result.size() < 2)
{
throw InvalidExpressionException("The expression contains an \"and\" operator with too few operands");
}
bool right = result.top();
result.pop();
bool left = result.top();
result.pop();
result.push(left && right);
}
int Precedence() const override
{
return 3;
}
};
template<>
struct Stringizer<AndOperator>
{
static std::string ToString(const AndOperator&)
{
return "and";
}
};
}
#endif

View File

@@ -0,0 +1,35 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ALLOPERATOR_H
#define SNOWHOUSE_ALLOPERATOR_H
#include "collectionoperator.h"
#include "collectionconstraintevaluator.h"
namespace snowhouse
{
struct AllOperator : public CollectionOperator
{
template<typename ConstraintListType, typename ActualType>
void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
{
unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
result.push(passed_elements == actual.size());
}
};
template<>
struct Stringizer<AllOperator>
{
static std::string ToString(const AllOperator&)
{
return "all";
}
};
}
#endif

View File

@@ -0,0 +1,44 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ATLEASTOPERATOR_H
#define SNOWHOUSE_ATLEASTOPERATOR_H
#include "collectionoperator.h"
#include "collectionconstraintevaluator.h"
namespace snowhouse
{
struct AtLeastOperator : public CollectionOperator
{
explicit AtLeastOperator(unsigned int expected)
: m_expected(expected)
{
}
template<typename ConstraintListType, typename ActualType>
void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
{
unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
result.push(passed_elements >= m_expected);
}
unsigned int m_expected;
};
template<>
struct Stringizer<AtLeastOperator>
{
static std::string ToString(const AtLeastOperator& op)
{
std::ostringstream stm;
stm << "at least " << op.m_expected;
return stm.str();
}
};
}
#endif

View File

@@ -0,0 +1,44 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_ATMOSTOPERATOR_H
#define SNOWHOUSE_ATMOSTOPERATOR_H
#include "collectionoperator.h"
#include "collectionconstraintevaluator.h"
namespace snowhouse
{
struct AtMostOperator : public CollectionOperator
{
explicit AtMostOperator(unsigned int expected)
: m_expected(expected)
{
}
template<typename ConstraintListType, typename ActualType>
void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
{
unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
result.push(passed_elements <= m_expected);
}
unsigned int m_expected;
};
template<>
struct Stringizer<AtMostOperator>
{
static std::string ToString(const AtMostOperator& op)
{
std::ostringstream stm;
stm << "at most " << op.m_expected;
return stm.str();
}
};
}
#endif

View File

@@ -0,0 +1,110 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_COLLECTIONCONSTRAINTEVALUATOR_H
#define SNOWHOUSE_COLLECTIONCONSTRAINTEVALUATOR_H
#include <vector>
#include "../constraintoperator.h"
namespace snowhouse
{
template<typename ConstraintListType, typename ActualType>
struct CollectionConstraintEvaluator
{
static unsigned int Evaluate(const ConstraintOperator& op,
ConstraintListType& expression, ResultStack& result,
OperatorStack& operators, const ActualType& actual)
{
ConstraintOperator::EvaluateOperatorsWithLessOrEqualPrecedence(op,
operators, result);
unsigned int passed_elements = 0;
for (const auto& member : actual)
{
if (ConstraintOperator::EvaluateElementAgainstRestOfExpression(expression, member))
{
++passed_elements;
}
}
return passed_elements;
}
};
struct StringLineParser
{
static void Parse(const std::string& str, std::vector<std::string>& res)
{
size_t start = 0;
size_t newline = FindNewline(str, start);
while (newline != std::string::npos)
{
StoreLine(str, start, newline, res);
start = MoveToNextLine(str, newline);
newline = FindNewline(str, start);
}
if (start < str.size())
{
StoreLine(str, start, std::string::npos, res);
}
}
private:
static size_t FindNewline(const std::string& str, size_t start)
{
return str.find_first_of("\r\n", start);
}
static void StoreLine(const std::string& str, size_t start, size_t end,
std::vector<std::string>& res)
{
std::string line = str.substr(start, end - start);
res.push_back(line);
}
static size_t MoveToNextLine(const std::string& str, size_t newline)
{
if (str.find("\r\n", newline) == newline)
{
return newline + 2;
}
if (str.find('\n', newline) == newline)
{
return newline + 1;
}
if (str.find('\r', newline) == newline)
{
return newline + 1;
}
std::ostringstream stm;
stm << "This string seems to contain an invalid line ending at position "
<< newline << ":" << std::endl
<< str << std::endl;
throw InvalidExpressionException(stm.str());
}
};
template<typename ConstraintListType>
struct CollectionConstraintEvaluator<ConstraintListType, std::string>
{
static unsigned int Evaluate(const ConstraintOperator& op,
ConstraintListType& expression, ResultStack& result,
OperatorStack& operators, const std::string& actual)
{
std::vector<std::string> lines;
StringLineParser::Parse(actual, lines);
return CollectionConstraintEvaluator<ConstraintListType, std::vector<std::string>>::Evaluate(op, expression, result, operators, lines);
}
};
}
#endif

View File

@@ -0,0 +1,26 @@
// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef SNOWHOUSE_COLLECTIONOPERATOR_H
#define SNOWHOUSE_COLLECTIONOPERATOR_H
#include "../constraintoperator.h"
namespace snowhouse
{
struct CollectionOperator : public ConstraintOperator
{
void PerformOperation(ResultStack&) override
{
}
int Precedence() const override
{
return 1;
}
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More