Add Microsoft Works works

This commit is contained in:
~lucidiot 2023-12-11 00:43:59 +01:00
parent 4f8d249fc3
commit ef3b53169f
3 changed files with 1033 additions and 0 deletions

41
assets/ksy/wklnhst.ksy Normal file
View File

@ -0,0 +1,41 @@
meta:
id: wklnhst
title: Microsoft Works Data Store History
application: Microsoft Works Task Launcher
file-extension: dat
license: AGPL-3.0
encoding: UTF-16LE
endian: le
doc: |
This file stores the recently opened and saved documents using any program
of the Microsoft Works suite, so they can be displayed within the History tab
of the Task Launcher.
This is stored under %AppData%\wklnhst.dat
seq:
- id: count
type: u4
- id: history
type: entry
repeat: expr
repeat-expr: count
types:
entry:
-webide-representation: '{path}'
seq:
- id: application_id
type: u4
doc: ID of the application, as set in the XTR files and the task cache
- id: task_id
type: u4
doc: |
ID of a task associated with this file, if the file was created using
a particular task of the Task Launcher.
If there is no associated task, this is set to zero.
- id: len_path
type: u4
- id: path
size: len_path * 2
type: str
doc: Path to the referenced file.

211
assets/xsd/TipFormat.xsd Normal file
View File

@ -0,0 +1,211 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Microsoft Works Tip of the Day XML schema
Copyright (c) 2023 ~lucidiot
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<xs:schema
xmlns="http://MSWorks/TipFormat.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified"
elementFormDefault="qualified"
version="9.0"
targetNamespace="http://MSWorks/TipFormat.xsd"
>
<xs:annotation>
<xs:documentation>Microsoft Works 9.0 Tip of the Day XML schema</xs:documentation>
<xs:documentation>
Derived from a Works 9.0 installation on Windows XP localized in English (United States),
using the file at %ProgramFiles%\Microsoft Works\1033\WksTips.xml
</xs:documentation>
<xs:documentation>This may be valid for older versions of Works, but this has not been tested yet.</xs:documentation>
</xs:annotation>
<xs:simpleType name="LocalizationActionType">
<xs:restriction base="xs:string">
<xs:enumeration value="locNone">
<xs:annotation>
<xs:documentation>Do not localize.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="locData">
<xs:annotation>
<xs:documentation>Localize this data.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TagLocalizationAction">
<xs:annotation>
<xs:documentation>
Defines what task should be done by a translator to localize an XML element.
</xs:documentation>
</xs:annotation>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="_loc" type="LocalizationActionType" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="DefaultLocalizationAction">
<xs:annotation>
<xs:documentation>
The default localization action for every element whose localization has not been explicitly defined.
</xs:documentation>
</xs:annotation>
<xs:attribute name="_loc" type="LocalizationActionType" use="required" />
</xs:complexType>
<xs:complexType name="LocalizationDefinition">
<xs:annotation>
<xs:documentation>
Defines the tasks that should be done by a translator to localize this XML document.
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="_locDefault" type="DefaultLocalizationAction" minOccurs="0" maxOccurs="1" />
<xs:element name="_locTag" type="TagLocalizationAction" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="LocalizableString">
<xs:annotation>
<xs:documentation>
An element with string content that may be localized by translators.
</xs:documentation>
</xs:annotation>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="_locID" type="xs:ID" use="optional">
<xs:annotation>
<xs:documentation>
A unique identifier that will be used by translation software.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:simpleType name="LinkType">
<xs:restriction base="xs:string">
<xs:enumeration value="Help">
<xs:annotation>
<xs:documentation>This is a link to a local Works help page.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Web">
<xs:annotation>
<xs:documentation>This is a link to an online help page.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Link">
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute name="Link" type="LinkType" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="Tip">
<xs:sequence>
<xs:element name="Header" type="LocalizableString">
<xs:annotation>
<xs:documentation>Title of the tip.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Content" type="LocalizableString">
<xs:annotation>
<xs:documentation>Description of the tip.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" name="LearnMore" type="Link">
<xs:annotation>
<xs:documentation>
A link to a help page to learn more about the feature described in this tip.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
<xs:attribute name="ID" type="xs:ID" use="required" />
</xs:complexType>
<xs:simpleType name="CategoryName">
<xs:restriction base="xs:string">
<xs:enumeration value="Common">
<xs:annotation>
<xs:documentation>Tips that can be displayed in any Works program.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="WP">
<xs:annotation>
<xs:documentation>Tips that are specific to Works Word Processor.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SS">
<xs:annotation>
<xs:documentation>Tips that are specific to Works Spreadsheet.</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="DB">
<xs:annotation>
<xs:documentation>Tips that are specific to Works Database.</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TipCategory">
<xs:annotation>
<xs:documentation>
A category of tips, used to differentiate tips that are general
to the Works suite or specific to a single Works program.
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="Tip" type="Tip" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="Name" type="CategoryName" use="required" />
</xs:complexType>
<xs:complexType name="TipCollection">
<xs:annotation>
<xs:documentation>
A collection of tips displayed in the Tip of the Day section of the Task Pane
of Microsoft Works Word Processor, Spreadsheet and Database.
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="_locDefinition" type="LocalizationDefinition" minOccurs="0" maxOccurs="1" />
<xs:element name="TipCategory" type="TipCategory" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="Version" fixed="9.0">
<xs:annotation>
<xs:documentation>Microsoft Works version that this tips collection is for.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:element name="TipCollection" type="TipCollection">
<xs:unique name="UniqueCategories">
<xs:selector xpath="TipCategory" />
<xs:field xpath="@Name" />
</xs:unique>
</xs:element>
</xs:schema>

781
content/msworks.md Normal file
View File

@ -0,0 +1,781 @@
---
title: Microsoft Works
---
Among the various old Microsoft things I mess with on my Windows 2000 and XP virtual machines or laptops is Microsoft Works, which is to Office what Outlook Express is to Outlook; a separate implementation of the same product but with less features. I have seen it used by a teacher in middle school, probably because it was preinstalled on her EeePC running XP, and was confused by this strange kinda-Office that I didn't know about at the time.
Since I like to try to reverse engineer and document the old file formats and other technologies I find, here are some notes from the things I learnt while messing with Works.
I am not planning to do much with the Word Processor, Spreadsheet and Database, even though they are the three main programs bundled in the Works suite. Their file formats are [already documented][justsolve-works], and there are converters available. I am focusing on the weirder parts, the things most people don't care much about.
I am for now focusing on Microsoft Works 8.0 and 9.0, as 8.0 is the last version with Windows 2000 support, and 9.0 is the latest version. I might go back to earlier versions later.
## Task Launcher
The Microsoft Works Task Launcher, sometimes just called *Microsoft Works*, can display events from the Works Calendar, keep a history of files edited with Microsoft Works, and allows the user to start all of the programs of the Works suite in different ways.
A *Quick Launch* panel lets you open Word Processor, Spreadsheet or Database directly, and if they are installed, it may also include Microsoft Picture It!, later known as Digital Image, or Microsoft Money. I think the Task Launcher was designed to tie the many products thrown together within the [Works Suite][works-suite], which also included Encarta, Streets & Trips, Home Publishing, etc. It did support Microsoft Money 2011 Sunset Deluxe and Digital Image 2006, but neither Encarta 2009 French nor Streets & Trips 2011. I do not know how that panel lists all the software and if it would be configurable.
The *Programs* and *Templates* tabs both list tasks that you can perform with various programs, but grouped differently. The *Programs* tab groups tasks by programs, sometimes including an extra task to just start the program and nothing more. The *Templates* tab groups tasks by categories, like *Email & Internet* or *Travel*, and does not include a link to just start a program.
### Tasks
Tasks in Microsoft Works have three types:
Regular
: Open a file, optionally with some command-line arguments. If the file does not exist, the Task Launcher will ask the user if they want to delete the task entirely.
Online
: Open a URL, any URL. It actually supports anything that the *Run* dialog supports, so you can also open programs, files, or use other protocols like Gopher, Telnet or MSN.
Templates
: Load a Works Wizard file within the Task Launcher to let the user select one of multiple templates, then run an EXE with the template as a command-line argument.
Other Microsoft tools can integrate with the Task Launcher to provide their own tasks, so that it truly becomes *the* dashboard for anything Works' target audience can do with Microsoft software. This is done by using one XML file per application, and letting each app define its tasks, sort them, place them into categories, etc.
On top of providing tasks for its own applications, Works 8.0 ships with some files for Palm and Windows CE synchronisation support via ActiveSync, as well as lots of links to MSN services. Works 9.0 ships without ActiveSync tasks and with less MSN links, but instead includes files for Office 2007 if it installed, including the PowerPoint 2007 Viewer for Office 2003.
The Task Launcher looks up a list of XML files to load through the `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Works\Launcher\XTRFiles` registry key. Each value of that registry key is a path to an XML file. Each value is of a `REG_SZ` type, and their data usually contains the name of the related application. That data is not actually used at all, so that can be used as a comment for those peeking through the registry.
#### XTR files
The XML files have a `.xtr` extension, and those provided by Microsoft include an XML comment: `<!--Created with Mirage WTR Tool version 1.0-->`. Since those initialisms are not defined anywhere, my best guess is that XTR and WTR mean *XML Task Registration* and *Works Task Registration*. The files hold the following information:
* The locale identifier that this file has been translated into;
* The application's details, including an icon, a name, a description, an optional path to an executable and some command line arguments, etc.;
* One or more tasks, with a name, a description, an icon, a version number, one or more categories, the parameters specific to their types, etc.
Tasks that have a locale identifier other than the one of the installed version of Works will be skipped.
<!--
TODO: XSD
Note that <online> tasks can use registry keys
%HKLM\software\microsoft\IE Setup\SETUP\Path%\Connection Wizard\icwconn1.exe
-->
#### Cache
The XTR files are a static source of tasks, but tasks may also be dynamically added. `WkDStore.exe`, the Works Data Store, runs alongside all Works applications, allowing them to retrieve and store tasks and recently used files. You can view its type library using OleView, one of the tools distributed along with Visual Studio 6 and the Windows SDK.
To store those dynamically updated tasks, the data store saves everything to a binary file under `%AppData%\Microsoft\Works\wklntsk1.dat`. I am working on a Kaitai Struct schema and a documentation for this file's structure.
<!-- TODO: .ksy and documentation -->
#### Personal Templates
Works Word Processor, Spreadsheet and Database all support saving templates. On all three, saving a template causes the Task Launcher's cache file to be updated to inject some additional tasks that do not exist in the XML files. Those tasks are categorized under `WorksPersonalTemplate`, and the Task Launcher takes care of displaying the correct string to the user, so in English *Personal Templates*. The original XTR files are not updated.
As for how templates are saved however, the Word Processor behaves differently than the other two. In Works Spreadsheet and Database, there are no specific file extensions or formats for templates. To save a template, the Save dialog includes a *Template…* button. Clicking this button opens a different, simpler dialog box, where you can type the template's name and optionally check a box to set this template as the default template for all new documents from now on.
Your template will be saved as a regular document under `%AppData%\Template`, not even using the `%AppData%\Microsoft\Works` directory that other parts of Works uses. To configure the default template for blank documents, the `HKEY_CURRENT_USER\Software\Microsoft\Works\9.0\Template Section` key holds two values: `SSDefaultTemplate` defines the path to the default template for Works Spreadsheet, and `DBDefaultTemplate` defines the one for Works Database.
In the Word Processor, you can save a template by selecting the `.wpt` option in the Save dialog. When selecting this option, the dialog box changes immediately to the templates directory, but you can save elsewhere. If you save elsewhere, the Task Launcher will also include a task for your template.
### My Projects Organizer
The Task Launcher is not just a dashboard with a bunch of shortcuts; it is a to-do list manager too. The *Projects* tab, whose full name is *My Projects Organizer* according to the History tab, can display to-do items grouped into projects. Various project templates are available, and users can also just create blank projects. Both projects and to-do items have optional due dates, and configuring one will automatically add events to the Works Calendar to give reminders.
Projects just have a name, an optional due date and to-do items. To-do items have a text, an optional due date, a multiline text box for notes, and can optionally be associated with an action: using any Works task, opening any local file, or opening a URL. The user interface says *Works template* but you can use any task of any type.
Projects are stored within the <em>My Projects</em> folder under <em>My Documents</em> as XML files with a `.wpj` extension.
#### .NET Lists service
<!-- TODO: Maybe move this to a separate Hailstorm wiki page? -->
The XML documents are using an altered version of the Lists service of the abandoned [.NET My Services](https://en.wikipedia.org/wiki/.NET_My_Services) project, also known by its codename *Hailstorm*, which says something about when the Projects feature must have been developed: sometime in 2001.
Hailstorm was intended to be a bunch of XML Web services: HSDL, an initialism that is never properly defined, defines a way to query an SOAP endpoint to query, create, update, replace, delete, or subscribe to changes in the data stored in the various services. There were a lot of those services. They are listed and documented in detail in [Microsoft .NET My Services Specification][hailstorm-spec], ISBN 9780735615564, and [an abandoned patent application][hailstorm-patent], which had been rejected because it was so vaguely written that it conflicts with Office 97, includes a figure that lists 14 services.
Hailstorm was eventually abandoned because of yet another case of anti-trust troubles for Microsoft. Nowadays, Microsoft, Google and others do provide all of the listed services, with Google having the monopoly over consumers and Microsoft over businesses. The hailstorm just got calmer and became a regular boring cloud instead.
Some of the XML schemas for the Hailstorm services were recovered and [published on GitHub][hailstorm-github]. Two schemas are however missing: the Lists and Notifications services. I am working on writing XML schemas for both.
#### Works extensions
My Projects Organizer uses a separate XML namespace to add an extension to the original .NET Lists service. This extension provides the file association feature on project to-dos, where you can assign a template, an existing local file or a URL to your task. I will also be writing an XML schema for that extension.
### Wizards
Wizards are what typical software would call templates. They are used within the XTR files. They are found under the `<LCID>\Wizards` directory, where `<LCID>` is a locale identifier. They use the `.wwp`, `.wws` and `.wwd` extensions, which distinguish wizards for the Word Processor, Spreadsheet or Database respectively. They are nothing more than a normal document of their respective software; you can copy them, rename them to `.wps`, `.xlr` or `.wdb`, and use them like that.
My Projects Organizer just has its own project templates stored as `.wpj` files, the default extension, in that same directory. You can double-click them to open them, but they do not seem to be editable within Works, even when removing the read-only attribute from the file.
## History
The *History* tab shows the recently opened and saved files from within any of the Microsoft Works applications. Each entry can be removed individually, or the whole history can be cleared. Just like with tasks, the separate Data Store process manages this history. Entries do not appear in this history if it has been disabled by the task in an XTR file.
The history is saved under `%AppData%\wklnhst.dat`, again ignoring the `Microsoft\Works` directory. Its structure is rather simple:
```svgbob
+-------+---------+---------+-----+
| Count | Entry 1 | Entry 2 | ... |
+-------+---------+---------+-----+
```
First, a 32-bit little endian integer, for the amount of history entries stored in the files. Then a series of said history entries, structured like this:
```svgbob
+--------+---------+-------------+------+
| App ID | Task ID | Path length | Path |
+--------+---------+-------------+------+
```
App ID
: 32-bit little endian integer. The application ID, as written in the XTR files and the task cache file.
Task ID
: 32-bit little endian integer. The task ID, as written in the XTR files and the task cache file. This is used to associate a file as having been created using a specific template. If this file is not associated with a template, this value is set to zero.
Path length
: 32-bit little endian integer. The length of the file's path, in characters. Since the file path is encoded as UTF-16, you need to multiply this by two to get the bytes.
Path
: UTF-16LE string. Path to the file referenced by the history entry.
You can download the [Kaitai Struct schema for the Works Data Store History file](./ksy/wklnhst.ksy).
## Calendar
The Works Calendar stores all its data into `%ALLUSERSPROFILE%\Application Data\Microsoft\Works\`. This means all calendars are shared with all users on the computer! The calendar does not include any access control settings, and the files are quite easy to play with.
The most important file is `mswkscal.wcd`, whose file extension probably means *Works Calendar Database*. It is a Jet database, with the very recognizable `Standard Jet DB` signature. Jet is the engine behind Microsoft Office Access, and you can indeed open the file with Access to read it. With Access 2003, I have to go through three different security alerts telling me to block untrusted macros on that database, but it does open correctly.
There are three tables and two queries. Their properties say they were created in 1997/1998. Here's an approximative representation of the database as SQL:
```sql
CREATE TABLE Events (
ID BIGINT NOT NULL PRIMARY KEY AUTOINCREMENT,
lDuration BIGINT,
szSubject TEXT CHECK (szSubject <> ''),
szBody TEXT CHECK (szBody <> ''),
szLocation VARCHAR(255) CHECK (szLocation <> ''),
rgRecurOptions BLOB NOT NULL,
fRecurring BIGINT DEFAULT FALSE,
lCountryCode BIGINT DEFAULT 0,
lCodePage BIGINT DEFAULT 0,
lCharSet BIGINT DEFAULT 0,
MasterId BIGINT DEFAULT 0,
dtModify TIMESTAMP,
rgCats BLOB, -- An array of category IDs
-- Unsigned. ID of the client application with whom this record is associated
ulClientID BIGINT,
-- Unsigned. ID of the record in the context of the client application
ulClientRecordID BIGINT,
-- Type of the event depending on the client application.
-- For the Works Address Book, this indicates Birthday or Anniversary.
ulClientValueType BIGINT,
-- Introduced in Works 9
szClientRecStrId VARCHAR(255)
);
CREATE INDEX lCountryCode ON Events (lCountryCode);
CREATE INDEX MasterId ON Events (MasterId);
CREATE INDEX ulClientID ON Events (ulClientID);
CREATE INDEX ulClientRecordID ON Events (ulClientRecordID);
-- This is a duplicate index, just like in the original DB.
CREATE UNIQUE INDEX ID ON Events (ID);
CREATE TABLE EventInstance (
dtStart TIMESTAMP,
dtEnd TIMESTAMP,
EventID BIGINT NOT NULL REFERENCES Events (ID) ON UPDATE CASCADE ON DELETE CASCADE,
-- Indicates if this is the master record
fRecurRec BIGINT DEFAULT FALSE,
-- Indiciates if this is a deleted recurring record
fDeleted BIGINT DEFAULT FALSE,
-- When a reminder should be shown to the user
dtRemind TIMESTAMP,
-- Number of seconds before the event when a reminder should be shown
lRemind BIGINT DEFAULT 0,
-- Used to distinguish recurring event records
-- The original DB defines this as auto-increment, but without it being a primary key,
-- which is not supported in SQLite.
SequenceId BIGINT NOT NULL
);
-- Duplicate of Events.rgCats, but a more normal way of representing arrays
CREATE TABLE CatToEvents (
CategoryId BIGINT NOT NULL,
EventId BIGINT NOT NULL REFERENCES Events (ID) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE VIEW EventQuery AS
SELECT ID, dtStart, dtEnd, dtRemind, lDuration, lRemind, szSubject, szBody, szLocation
FROM Events
INNER JOIN EventInstance ON Events.ID = EventInstance.EventID;
```
There are two queries stored in that database. One of them is defined as a view above, but the other takes parameters, which means an SQLite equivalent will require more than what SQLite can provide! Here is an attempt using [the `define` extension of SQLean][sqlean-define]:
```sql
-- Helper function to split a string into a table of integers
CREATE VIRTUAL TABLE parse_integer_list USING define((
WITH RECURSIVE splitter (value, str) AS (
SELECT NULL, :str
UNION
SELECT
CASE
WHEN INSTR(str, :sep) > 0
THEN SUBSTR(str, 1, INSTR(str, :sep) - 1)
ELSE str
END,
SUBSTR(str, INSTR(str, :sep) + 1)
FROM splitter
)
SELECT CAST(value AS INTEGER)
FROM splitter
WHERE value IS NOT NULL
));
CREATE VIRTUAL TABLE QEventToCat USING define((
SELECT Events.*, *
FROM Events INNER JOIN CatToEvents ON Events.ID = CatToEvents.EventId
WHERE CategoryId in (SELECT parse_integer_list(:szCatList))
AND dtStart > :QdtEnd AND dtEnd < :QdtStart;
));
```
That last query retrieves all events, filtered by a list of category IDs and in a defined interval. Events in multiple categories will be duplicated, once per category, with a different `CategoryId` returned.
That database is a mess. I rewrote some of the comments for the fields. The original comments mention *WAB*, which is usually the intialism for the Windows Address Book. In this case, this instead refers to the *Works Address Book*, a feature removed in Works 5.0.
The Works Address Book was introduced roughly at the same time as the Windows Address Book, though the Windows Address Book was part of Internet Explorer 3, so not necessarily available for everyone. It was a special kind of Works Database that would automatically add reminders in your calendar for your contact's birthdays, hence the whole "client application" thing in the calendar's database. All versions of Works starting from 5.0 instead provide an *Address Book Converter*, which takes the Works Database of the old address book and imports it into the Windows Address Book. There are a few text files lying around in the installation directory related to this, trying to map field names in the database to Windows Address Book fields, as those in the database are localized.
<!-- TODO: CalMRU.dat, logins.ini, wkcalcat.dat -->
## Portfolio
The Works Portfolio is Works' answer to Microsoft Office Binder. It is a lot more basic than Binder though; where Binder can transform itself into whatever program is needed to display each supported file, Portfolio just groups some files together, supports copy-pasting to and from those collections of files, lets you add comments, and lets you send those files to a new Word Processor document, a printer, an e-mail recipient or a folder. You can get a preview for a few supported formats, but to open a file, you will first have to send it to a folder and open it yourself. If you need to update the file, first remove the old file, and then put the new one back in. This is more like the Office Clipboard Manager than Binder!
The executable for Works Portfolio is named `WksSb.exe`, which might mean *Works Shoebox*. This is not to be confused with the Shoebox file format.
Coming soon™, a look into the file format used by that thing...
## Shoebox
The clip art that can be inserted into Works documents relies on multiple files:
* `%ProgramFiles%\Common Files\Microsoft Shared\Shoebox\wks7.sbc`
* `%ProgramFiles%\Common Files\Microsoft Shared\Shoebox\Blank.sbc`
* `%AppData%\Microsoft\Shoebox\user.sbc`
* `%ProgramFiles%\Microsoft Works\WKSv7std.sbs`
* `%ProgramFiles%\Microsoft Works\WKSv7std.sbt`
The `.sbc` are Microsoft Jet databases that can be opened by Microsoft Access. They appear to contain tables that point to the `.sbs` and `.sbt` files, which both appear to be Compiled HTML files (`.chm`), as they start with the magic number `ITSF`. The `.sbt` file contains thumbnails, and the `.sbs` file contains the actual clip art. The `Blank.sbc` and `user.sbc` files imply that it is possible to add custom media to this clip art gallery, but this does not appear to be possible within Works.
TODO: Look into those files, compare with the Office Clip organizer, see what's in Photo Story 3 and Digital Image 2006
## Default fonts
With Works Spreadsheet and Works Database, you can set a default font for new spreadsheets and databases under the *Font* tab of the *Format* dialog box. This updates a few registry values. Here is a `.reg` export from the registry editor with the default values for the default values:
```
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Works\9.0\MS Works SS]
"DefaultFont"="12,Times New Roman,roman"
"DefaultStyle"="128"
[HKEY_CURRENT_USER\Software\Microsoft\Works\9.0\MS Works DB]
"DefaultFont"="10,Arial,swiss"
"DefaultStyle"="0"
```
`DefaultFont` defines the selected font size, font name and font family, in that order, separated with commas. The *font family* in this case is a value from the [FamilyFont][familyfont] enumeration, which specifies a general category of fonts: `roman` (serif), `swiss` (sans serif), `modern` (monospace), `script` (cursive), `decorative` (fantasy), or... `dontcare`.
`DefaultStyle` is composed of four higher bits used to set the color, and six lower bits used for formatting:
<table>
<tbody>
<tr>
<th scope="col">Bit</th>
<td>9</td><td>8</td><td>7</td><td>6</td><td>5</td><td>4</td><td>3</td><td>2</td><td>1</td><td>0</td>
</tr>
<tr>
<th scope="col">Value</th>
<td>512</td><td>256</td><td>128</td><td>64</td><td>32</td><td>16</td><td>8</td><td>4</td><td>2</td><td>1</td>
</tr>
<tr>
<th scope="col">Usage</th>
<td colspan="4">Color</td>
<td><s>Strikethrough</s></td>
<td colspan="2">Unused</td>
<td><u>Underline</u></td>
<td><i>Italic</i></td>
<td><b>Bold</b></td>
</tr>
</tbody>
</table>
The available color values are the following:
| Color name | RGB | Value | Offset value |
| ------------ |:--------:| -----:| ------------:|
| Automatic | — | 0 | 0 |
| Black | `000000` | 1 | 64 |
| Blue | `0000FF` | 2 | 128 |
| Turquoise | `00FFFF` | 3 | 192 |
| Bright green | `00FF00` | 4 | 256 |
| Pink | `FF00FF` | 5 | 320 |
| Red | `FF0000` | 6 | 384 |
| Yellow | `FFFF00` | 7 | 448 |
| Gray 50% | `808080` | 8 | 512 |
| White | `FFFFFF` | 9 | 576 |
| Dark blue | `000080` | 10 | 640 |
| Teal | `008080` | 11 | 704 |
| Green | `008000` | 12 | 768 |
| Violet | `800080` | 13 | 832 |
| Dark red | `800000` | 14 | 896 |
| Gray 25% | `C0C0C0` | 15 | 960 |
In the above table, the value is the actual, decoded value when the `DefaultStyle` is properly parsed, and the offset value is the value you would see if you were to read the style as a single base 10 integer.
This color palette does not match the usual palette of the more common Windows font selection dialog, as the "dark yellow" option has been sacrificed to leave `0` as the "automatic" color, the default that is just black most of the time but not necessarily all of the time.
## Format Gallery
Under the *Format* menu of Works Word Processor, the *Format Gallery* dialog box allows formatting a selection or the entire document using a *font set* and a *color set*. They both have a single word as a name, so the combination creates a two-word style, for example *Hefty Spice* or *Casual Ocean*. Different styles are being used depending on the template, but for any non-built-in template or for blank documents, a "general" style file will be used. Up to 64 custom styles can also be saved.
The default styles for templates and for blank documents are text files with a `.fmt` extension, found at `%Program Files%\Microsoft Works\<LCID>\WkThm*.fmt`. The default style for blank documents ("general") is called `WkThmGen.fmt`. Removing all the other `.fmt` files does not seem to have any effect, so I am not sure what they really are for.
### Format files
Coming soon: a description of the file format!
<!-- TODO: Document the file format! -->
### Custom styles
Custom styles are stored as registry keys, with a lot more detail than for Spreadsheet or Database. Here is the default style in a blank document, stored as a custom style in the registry:
```
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Works\9.0\Themes\CustomStyles\00000000]
"pidBold"=dword:00000000
"pidItalic"=dword:00000000
"pidForeColor"=dword:ff676983
"pidTextForeColor"=dword:ff676983
"pidFontName"="Times New Roman"
"pidFontSize"=dword:41200000
"pidEASpacing"=dword:00000000
"pidStrikeThrough"=dword:00000000
"pidUnderline"=dword:00000000
"pidCaps"=dword:00000000
"pidEmboss"=dword:00000000
"pidEngrave"=dword:00000000
"pidShadow"=dword:00000000
"pidOutline"=dword:00000000
"pidSuperSub"=dword:00000000
"pidAlign"=dword:00000000
"pidLeftIndent"=dword:00000000
"pidRightIndent"=dword:00000000
"pidFirstLineIndent"=dword:00000000
"pidSpaceBefore"=dword:00000000
"pidSpaceAfter"=dword:00000000
"pidLineSpacingRule"=dword:00000000
"pidEnglishWordwrap"=dword:00000001
```
A `dword` in the Windows registry is a 32-bit integer. Most of the ones set here are actually booleans, because a 32-bit integer is the closest thing we have in the registry for booleans! And there are a lot more values than the ones listed above; those are just the few required keys that are output for the default style. Some are only included when further customization is done. Let's have a look at each of those values.
#### Font
`pidBold`
: Boolean. Whether or not the text is bold.
`pidItalic`
: Boolean. Whether or not the text is italic.
`pidForeColor`
: Color of the text. The DWORD is split as such: `0x00BBGGRR`, where B, G and R are the blue, green and red components. When the color is set to *Automatic*, the DWORD contains `0xff676983`.
`pidTextForeColor`
: Color of the text, only within the Format Gallery's previews. The DWORD is split as such: `0x00BBGGRR`, where B, G and R are the blue, green and red components. When the color is set to *Automatic*, the DWORD contains `0xff676983`.
`pidFontName`
: String. The name of the font.
`pidFontNameEA`
: I found this key embedded into some DLLs. This is likely the name of a font with East Asian support, aka using UCS-2 or UTF-16, to use when a font that does not support that is set.
`pidFontSize`
: Single-precision floating-point number. The size of the font, in points.
`pidEASpacing`
: Single-precision floating-point number. Extra space, in points, added between each character.
This does not seem to be configurable directly within Works 8 or 9, but any value is accepted, even negative ones. This might be related to support for East Asian languages, hence the `EA`.
In the Format Gallery, hovering over a style with a non-zero spacing will show a tooltip that includes *Expanded `X` pt* or *Collapsed `X` pt*, depending on whether the value is positive or negative. The value will be displayed as a floating-point number, optionally with scientific notation if the value is weird enough.
`pidStrikeThrough`
: Boolean. Whether or not the text has a horizontal line going through its center.
`pidUnderline`
: `0`
: No underline.
`1`
: Regular underline.
`2`
: Only words, not whitespace, are underlined.
`3`
: Double underline.
`4`
: Dotted underline.
`6`
: Thick underline.
`7`
: Dashed underline.
`9`
: Dot-dashed underline.
`10`
: Dot-dot-dashed underline.
`11`
: Wavy underline.
`16`
: Thick wavy underline.
`17`
: Thick dotted underline.
`18`
: Thick dashed underline.
`19`
: Thick dot-dashed underline.
`20`
: Thick dot-dot-dashed underline.
`21`
: Dashed underline, with long dashes.
`22`
: Thick dashed underline, with long dashes.
`23`
: Double wavy underline.
Other values will cause the Word Processor to leave the existing underline on the formatted text unmodified.
`pidCaps`
: `0`
: No automatic capitalization.
`1`
: Small capitals.
`2`
: All capitals.
`pidEmboss`
: Boolean. Whether an embossing effect is applied to the text, making it appear as if it was protruding out of the page. This cannot be set simultaneously with `pidEngrave`, `pidShadow` or `pidOutline`.
`pidEngrave`
: Boolean. Whether an engraving effect is applied to the text, the opposite of embossing. This cannot be set simultaneously with `pidEmboss`, `pidShadow` or `pidOutline`.
`pidShadow`
: Boolean. Whether a shadow is projected to the bottom-right of the text. Embossing applies an equivalent of a very thin shadow, but this actual shadow gets larger with larger font sizes. This cannot be set simultaneously with `pidEmboss` or `pidEngrave`.
`pidOutline`
: Boolean. Whether only the outline of each letter is drawn in the text color, with the inner parts remaining transparent. This cannot be set simultaneously with `pidEmboss` or `pidEngrave`.
`pidSuperSub`
: `0`
: Normal script.
`1`
: Superscript.
`2`
: Subscript.
#### Paragraph
`pidAlign`
: `0`
: Text aligned to the left.
`1`
: Text aligned to the right.
`2`
: Centered text.
`3`
: Justified text.
`4`
: Justified text, but instead of only adjusting the width of whitespace to evenly distribute the words across the line, the spacing between all characters is adjusted. This value cannot be set directly in Works, but is displayed correctly within the Format Gallery and called *Distributed Letter Aligned*.
`5`
: Justified text, but when a line only contains fullwidth characters from East Asian languages, the space between those characters is also adjusted, not only the width of whitespace. This results in better text justification when the text includes those characters. This value cannot be set directly in Works, at least not in a Western edition, but is displayed within the UI as the regular justified alignment (value `3`). The Format Gallery displays it as *East Asian Justified Aligned* in the style's tooltip.
`6`
: Justified text, but when a line includes only one word, the spacing between the word's characters is adjusted so the word spreads out through the whole line, as in *Distributed Letter Aligned*. This results in better text justification with a large font size or narrow paragraph width. This value cannot be set directly in Works and is rather poorly handled in the UI, but the Format Gallery displays *Newspaper Aligned* in the style's tooltip.
`7`
: The combination of the behaviors of values `5` and `6`, displayed in the Format Gallery as *East Asian Newspaper Aligned*. When a line only contains fullwidth characters from East Asian languages, or a single word in other alphabets, then the space between all characters is adjusted, not just the width of whitespace. This is poorly handled in the UI, as if no alignment at all was selected.
`pidLeftIndent`
: Single-precision floating-point number. Indentation of a paragraph from the left side, in points.
`pidRightIndent`
: Single-precision floating-point number. Indentation of a paragraph from the right side, in points.
`pidFirstLineIndent`
: Single-precision floating-point number. Indentation of the first line of each paragraph from the left side, in points. This overrides `pidLeftIndent`, instead of combining both.
`pidSpaceBefore`
: Single-precision floating-point number. Vertical space before the paragraph, in points.
`pidSpaceAfter`
: Single-precision floating-point number. Vertical space after the paragraph, in points.
`pidLineSpacingRule`
: Configures the line spacing:
`0`
: Single
`1`
: 1.5 lines
`2`
: Double
`5`
: Custom value defined by `pidLineSpacing`
`pidLineSpacing`
: Single-precision floating-point number. Space between the lines within a paragraph, in lines. Applied only when `pidLineSpacingRule` is set to `5`.
`pidEnglishWordwrap`
: Boolean, always set to true. This does not seem to have any effect. Defining two styles in the registry that have the same properties except for this one makes Works think the styles are duplicates.
`pidEALineBreaking`
: A key that I found in some DLLs that likely allows enabling line-breaking rules specific to East Asian languages, but I have not been able to make it have any effect in Works and do not know its type.
#### Bullets and Numbering
`pidBulletString`
: Integer. Code point of one character used as the bullet for bulleted lists. When using a numbered list, this is set to `0x02a0974c`, but any other value is also ignored.
`pidListAlign`
: Alignment of the bullets, using the same values as for `pidAlign`. This is useful for numbered lists using Roman numerals or having many items, where some bullets will have a different amount of characters. This cannot be configured within Works, but is supported.
`pidListFont`
: String. Name of the font where the bullet symbol is taken from. When this is empty, the text's current font (`pidFontName`) is used. This is ignored for numbered lists.
`pidListStart`
: Integer. Starting number, for numbered lists. Negative values are unsupported and will default to 1.
When the value `0` is set, which is possible through the *Bullets and Numbering* dialog box, only some numbered lists will work correctly:
* Arabic numerals will work correctly: `0) 1) 2)`.
* Alphabetic numbering will result in a duplicate: `A) A) B)`.
* Roman numerals will have an empty bullet: `) I) II)`.
* Chinese numerals will work correctly: `零) 一) 二)`.
When a list is long enough in a document that the number 2147483647 is reached, any subsequent items in the list will have no number attached: `2147483646) 2147483647) ) ) ) )`
`pidListTab`
: Single-precision floating-point number. Indentation added to the text after the bullet or number, in points.
`pidListType`
: Configures the bullet or numbered list style:
`0`
: No list style.
`2`
: Numbered list, in the form `%d)` where `%d` is the current number: `1), 2), 3)`
`3`
: Numbered list, in the form `%d)` where `%d` is the current lowercase letter: `a) b) c)`. This is not available in the *Bullets and Numbering* dialog box, but is supported.
`4`
: Numbered list, in the form `%d)` where `%d` is the current letter: `A) B) C)`
`5`
: Numbered list, in the form `%d)` where `%d` is the current number, in lowercase Roman numerals: `i) ii) iii)`. This is not available in the *Bullets and Numbering* dialog box, but is supported.
`6`
: Numbered list, in the form `%d)` where `%d` is the current number, in uppercase Roman numerals: `I) II) III)`
`8`
: Numbered list, in the form `%d)` where `%d` is the current number, in Chinese numerals: `一) 二) 三)`.
`9`
: Numbered list, in the form `%d)` where `%d` is the current number, in Traditional Chinese financial numerals: `壹) 貳) 參)`
`10`
: Numbered list, in the form `%d)` where `%d` is the current number, in Chinese numerals: `一) 二) 三)`.
Adding `0x20000` to the above values will cause the numbered list to use a dot `.` instead of a closing parenthesis `)` to separate the number and the text.
Invalid values will cause Works to detect a numbered list and display it in the toolbars and the dialog box, but the list will be rendered in the document as a bulleted list with the default dot bullet. When the value is below `0x10000` and the bullet string is set to the numbered list mode (`0x02a0974c`), the Format Gallery displays `L` as the bullet character in its preview. When it is greater than or equal to `0x10000`, it displays `1.`.
#### Borders and shading
`pidBorder`
: Bit flags. Defines on which sides of the paragraph the borders are applied. Only the last four bits of the DWORD are used:
Bit 0
: Enable the top border.
Bit 1
: Enable the bottom border.
Bit 2
: Enable the left border.
Bit 3
: Enable the right border.
`pidBorderColor`
: Color of the border. The DWORD is split as such: `0x00BBGGRR`, where B, G and R are the blue, green and red components. When the color is set to *Automatic*, the DWORD contains `0xff676983`.
`pidBorderLineStyle`
: `0`
: No border.
`1`
: Solid line.
`2`
: Dotted line.
`3`
: Dashed line.
`7`
: Double line.
`12`
: Solid line on the outside, solid line twice thinner on the inside.
`13`
: Solid line on the outside, solid line twice thiccer on the inside.
`14`
: Two solid lines around one twice thiccer solid line.
`18`
: Diagonal stripes, from top-left to bottom-right, like on barricade tape.
:
: On my installed versions of Works (9.7.613.0 in French on Windows XP, and 8.4.623.0 in English (U.S.) on Windows 2000), applying this style from the Format Gallery applies style `19` instead. I can set this style fine using the *Borders and Shading* format menu, and saving the style will result in the correct value.
`19`
: Diagonal stripes, from top-right to bottom-left, like on barricade tape.
`21`
: Dithered line; a checkerboard is drawn using the border color, depending on how much you zoom in on the page.
`pidBorderLineWidth`
: Single-precision floating-point number. Width of the border, in points. On border styles that include multiple lines of different widths, this determines the width of the thiccest lines. This cannot be configured directly in Works, other than by selecting a border style (which changes both the style, width and spacing). You can set this to values that are unachievable with the UI, and Works will accept them.
`pidBorderSpacing`
: Single-precision floating-point number. Spacing between the multiple lines that compose a border. If the border style only includes one line, this has no effect. This cannot be configured directly in Works, other than by selecting a border style (which changes the style, width and spacing). You can set this to values that are unachievable with the UI, and Works will accept them.
`pidShadingBackColor`
: The background color for the shading, defined as *Color 2* in the user interface. The DWORD is split as such: `0x00BBGGRR`, where B, G and R are the blue, green and red components. When the color is set to *Automatic*, the DWORD contains `0xff676983`.
`pidShadingForeColor`
: The foreground color for the shading, defined as *Color 1* in the user interface. The DWORD is split as such: `0x00BBGGRR`, where B, G and R are the blue, green and red components. When the color is set to *Automatic*, the DWORD contains `0xff676983`.
`pidShadingStyle`
: `0`
: The paragraph has no background.
`1`
: Solid fill, 100% foreground color.
`3`
: Solid fill, 10% foreground color, 90% background color.
`4`
: Solid fill, 90% foreground color, 10% background color.
`6`
: Solid fill, 80% foreground color, 20% background color.
`7`
: Solid fill, 70% foreground color, 30% background color.
`8`
: Solid fill, 60% foreground color, 40% background color.
`9`
: Solid fill, 50% of each color.
`10`
: Solid fill, 40% foreground color, 60% background color.
`12`
: Solid fill, 30% foreground color, 70% background color.
`13`
: Solid fill, 20% foreground color, 80% background color.
`14`
: Horizontal lines, alternating between both colors.
`15`
: Vertical lines, alternating between both colors.
`16`
: Diagonal lines from top-left to bottom-right, alternating between both colors.
`17`
: Diagonal lines from top-right to bottom-left, alternating between both colors.
`18`
: Lines in both diagonals of the foreground color above the background color.
`19`
: Dashes of the background color visible over the foreground color.
`20`
: Thin horizontal lines in the foreground color spaced far apart, above the background color.
`21`
: Thin vertical lines in the foreground color spaced far apart, above the background color.
`22`
: Thin diagonal lines from top-right to bottom-left, in the foreground color, spaced far apart above the background color.
`23`
: Thin diagonal lines from top-left to bottom-right, in the foreground color, spaced far apart above the background color.
`24`
: Grid of thin horizontal and vertical lines in the foreground color, spaced far apart above the background color.
`25`
: Thin diagonal lines in both directions, in the foreground color, spaced far apart above the background color.
## Tip of the Day
Starting with Works 9.0, an XML file stores all of the tips shown under the *Tip of the Day* section of the Task Pane of Word Processor, Spreadsheet and Database. The XML file is located at `<LCID>\WksTips.xml` under the installation directory, where `<LCID>` is a locale identifier.
Of course, I wrote [an XML schema for it](xsd/TipFormat.xsd).
[familyfont]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/9a632766-1f1c-4e2b-b1a4-f5b1a45f99ad
[justsolve-works]: http://fileformats.archiveteam.org/wiki/Microsoft_Works
[hailstorm-github]: https://github.com/pvginkel/HailStorm/
[hailstorm-patent]: https://patents.google.com/patent/US20040060002
[hailstorm-spec]: https://archive.org/details/microsoftnetmyse00micr/
[sqlean-define]: https://github.com/nalgeon/sqlean/blob/main/docs/define.md
[works-suite]: https://en.wikipedia.org/wiki/Microsoft_Works#Works_Suite