Chocks Away to Wavefront convertor

© Christopher Bazley, 2018

Version 0.01 (02 Sep 2018)

Download ChocToObj (74  KB Zip archive, includes source code).


Introduction and Purpose

Chocks Away is a flight simulator for Acorn Archimedes computers, written by Andrew Hutchings. It was published in 1990 by The Fourth Dimension (and followed by Extra Missions in 1991). It is fondly remembered by many because of its smoothly animated 3D graphics, simple controls and inclusion of a split-screen dogfight mode. The game world is drawn using a combination of flat-shaded polygon meshes, lines and points. Its 3D models of aircraft, buildings, bridges and other structures are primitive but I think they have a certain charm.

This command-line program can be used to convert object models belonging to 'Chocks Away' from their original (optionally compressed) format into the simple object geometry format developed by Wavefront for their Advanced Visualizer software. Wavefront OBJ format is a de-facto standard for 3D computer graphics and it has the advantage of being human-readable.

The colour and other visual properties of objects are defined separately from the object geometry (OBJ) file in a companion file known as a material library (MTL) file. The supplied MTL file defines all of the colours in the default RISC OS 256-colour palette as named materials, assuming a constant colour illumination model (no texture map, ambient or specular reflectance).


Screenshots

The following screeenshots show the object mesh for a tank imported into the Open 3D Model Viewer.

Here are the converted object geometry and material library files: tank.obj and sf3k.mtl

Chocks Away tank in the Open 3D Model Viewer Chocks Away tank in the Open 3D Model Viewer


Requirements

The supplied executable files will only work on RISC OS machines. They have not been tested on any version of RISC OS earlier than 4, although they should also work on earlier versions provided that a suitable version of the 'SharedCLibrary' module is active. They should be compatible with 32 bit versions of RISC OS.

You will obviously require a copy of the game 'Chocks Away' or 'Chocks Away: Extra Missions' from which to rip the graphics.

Index and model data files for 'Chocks Away' can be found inside the !Chocks application. The actual models are stored in a file named !Chocks.Maps.Land. This data is indexed by an array of 200 addresses stored in a second file named !Chocks.Maps.Obj3D.

Index and model data files for 'Extra Missions' can be found inside the !Maps_2 application. This game requires additional models for some maps: instead of a single Obj3D file, alternative index files numbered from !Maps_2.Things.Obj3D0 to Obj3DF store between 109 and 159 model addresses. Model data in common between all maps is still stored in a single file named !Maps_2.Land and any extra model data is stored in files numbered LandEx0 to LandExF (many of which have length zero).


Quick Guide

Ensure that the !Chocks application has been 'seen' by the Filer, so that the system variable Chocks$Dir has been set.

Hold down the Shift key and double-click on the !Chocks application to open it.

Set the current selected directory to that containing the 'ChocToObj' program. On RISC OS 6, that can be done by opening the relevant directory display and choosing 'Set work directory' from the menu (or giving the directory display the input focus and then pressing F11).

Press Ctrl-F12 to open a task window and then invoke the conversion program using the following command:

*ChocToObj -list <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

This should list all of the objects addressed by the compressed index file 'Obj3D'. One of those should be named 'tiger', which is a 3D model of the Tiger Moth biplane flown by the player.

To convert the Tiger Moth model into a Wavefront OBJ file named 'tiger/obj', use the following command:

*ChocToObj -name tiger -human <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D tiger/obj

The -name argument selects which object is converted and the -human argument enables generation of human-readable material names such as 'maroon_0' in the output file.

To convert the same model from 'Chocks Away: Extra Missions', ensure that the !Maps_2 application has been 'seen' by the Filer, then use the following command:

*ChocToObj -extra -raw -name tiger -human <ExtraMaps$Dir>.Land <ExtraMaps$Dir>.Things.Obj3D0 tiger/obj

The -raw argument disables decompression of the model data and index files whilst the -extra argument (strictly redundant in this case) allows naming of models only named in Extra Missions.


Usage information

Command line syntax

The basic syntax of the program is summarised below. All switches are optional. Switch names may be abbreviated.

ChocToObj [switches] <model-file> [<index-file> [<output-file>]]

Input and output

Switches:

-outfile <file>
Write output to the named file instead of stdout
-raw
Model and index files are uncompressed raw data

When invoking ChocToObj, you must always specify the name of a model data file. Without this, it would only be possible to enumerate the number of models and their addresses in memory (from the index).

The other input to ChocToObj is an array of four-byte little-endian model data addresses (the index). Consequently, it's possible for several object numbers to alias the same model data.

Strictly, one would need to know the address at which the model data file will be loaded in order to correctly interpret the index; in practice, the lowest address in the index usually coincides with the start of the model data.

An output file name can be specified after the input file name or, by means of the -outfile parameter, before the model data file name.

If no index file is specified then addresses are read from 'stdin' (the standard input stream; keyboard unless redirected). If no output file is specified then OBJ-format output is written to 'stdout' (the standard output stream; screen unless redirected).

All of the following examples read the index from a file named 'obj3d' and write output to a file named 'chocks/obj':

*ChocToObj land obj3d chocks/obj

*ChocToObj -outfile chocks/obj land obj3d

*ChocToObj -outfile chocks/obj land <obj3d

*ChocToObj land obj3d >chocks/obj

Under UNIX-like operating systems, output can be piped directly into another program.

Search for a named object in a compressed index file named 'obj3d':

ChocToObj land obj3d | grep -i 'gotha'

By default, all input is assumed to be compressed. The switch -raw allows uncompressed input, which is useful for converting object models belonging to 'Chocks Away: Extra Missions'.

It isn't possible to mix compressed and uncompressed input, for example by using a compressed index with an uncompressed model data file.

Model data file

Switches:

-last N
Last object number to convert or list
-offset N
Signed byte offset to start of model data in file

Not all of the models for 'Chocks Away: Extra Missions' are stored in a single file; models in common between all maps are stored in a primary file whilst extra models required for specific maps are stored in a secondary file (which is appended to the first upon loading). The index can reference models in either file.

This presents a problem because ChocToObj can only read from one model file at at time. A solution is provided in the form of the -last N and -offset N parameters.

The -last N parameter is used to prevent ChocToObj reading beyond the end of the primary model data file. The object number specified should be one less than that reported in the error message that results from omitting this parameter (e.g. "Failed to seek object 109 at offset ...").

The -offset N parameter is used to specify that the secondary model data file is not loaded at the lowest address in the index. The offset specified should be the (decompressed) size of the primary model data file.

The following example illustrates how these concepts can be combined to list all of the models for map 4 of 'Chocks Away: Extra Missions', albeit in piecemeal fashion:

*FileInfo <ExtraMaps$Dir>.Land
Land         WR/WR   Data      21:50:19.02 14-Apr-2018 0000C478
*ChocToObj -list -extra -raw -last 108 <ExtraMaps$Dir>.Land <ExtraMaps$Dir>.Things.Obj3D4
*ChocToObj -list -extra -raw -offset 0xC478 <ExtraMaps$Dir>.LandEx4 <ExtraMaps$Dir>.Things.Obj3D4

Object selection

Switches:

-index N
Object number to convert or list (default is all)
-first N
First object number to convert or list
-last N
Last object number to convert or list
-name <name>
Object name to convert or list (default is all)
-extra
Enable object names from Extra Missions

The model data index can be filtered using the -index or -first and -last parameters to select a single object or range of objects to be processed. Using the -index parameter is equivalent to setting the first and last numbers to the same value. The lowest object number is 0 and the length of the index dictates the valid range of object numbers.

The index can also be filtered using the -name parameter to select a single object to be processed. The -extra switch enables additional object names for things that are only named in 'Chocks Away: Extra Missions'. Any filter specified applies when listing object definitions as well as when converting them.

If no range of object numbers and no name is specified then all entries in the index are used.

Convert the first object in file 'Obj3D', outputting to the screen:

*ChocToObj -index 0 <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

If an object name is specified then the single object of the given name is processed (provided that it falls within the specified range of object numbers, if any).

Convert the Gotha G IV bomber object in file 'Obj3D' (see below for object naming):

*ChocToObj -name gotha <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

Named objects in 'Chocks Away':

Number Name Object
0 gun Ground gun base
1 store Store building
2 tank Tank
3 headquarters Headquarters ('Head quarters' in the game)
4 tower Control tower
5 boat Patrol boat
18 tiger Tiger moth
19 twin Fokker V7 twin
22 gotha Gotha G IV bomber
23 s_tiger Shadow of tiger moth
24 s_twin Shadow of Fokker V7 twin
25 s_gotha Shadow of Gotha G IV bomber
26 s_eindecker Shadow of Fokker Eindecker IV
27 s_scout Shadow of Albatros DIII scout
28 s_triplane Shadow of Fokker VIII triplane
29 eindecker Fokker Eindecker IV
30 triplane Fokker VIII triplane
31 scout Albatros DIII scout

Additional named objects in 'Chocks Away: Extra Missions':

Number Name Object
46 bridge Bridge
52 carrier Aircraft carrier
54 yacht Yacht
68 factory Factory
72 airship Airship
73 balloon Barrage balloon
78 terminal Control terminal
79 tanker Oil tanker
81 gunboat Gunboat ('Gun boat' in the game)
85 train Train
77 biplane Fokker DE5 biplane
75 triengine Fokker V3 triengine
74 cargo Cargo aircraft
87 station Railway station
102 s_biplane Shadow of Fokker DE5 biplane
103 s_triengine Shadow of Fokker V3 triengine
104 s_cargo Shadow of cargo aircraft
107 ground_jet Jet fighter
108 jet Jet fighter

A complete list of objects in 'Extra Missions' can be found elsewhere.

Listing and summarizing objects

Switches:

-list
List objects instead of converting them
-summary
Summarize objects instead of converting them

If the switch -list is used then ChocToObj lists object definitions instead of converting them to Wavefront OBJ format. Only object definitions matching any filter specified using the -index, -first, -last, and -name parameters are listed.

No output file can be specified because no OBJ-format output is generated and the object list or summary is always sent to the standard output stream.

List all object definitions indexed by file 'Obj3D':

*ChocToObj -list <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

Output is a table with the following format:

Index  Name          Verts  Prims  SimpV  SimpP      Offset        Size
    0  gun               8      5      6      1           0         208

If the switch -summary is used then ChocToObj summarizes object definitions instead of converting them to Wavefront OBJ format. This switch can be used in conjunction with -list, in which case the summary is displayed after the list.

Summarize all object definitions indexed by file 'Obj3D':

*ChocToObj -summary <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

Output is a total in the following format:

Found 200 objects in the input

Materials

Switches:

-mtllib name
Specify a material library file (default sf3k.mtl)
-human
Output readable material names
-false
Assign false colours for visualization

By default, ChocToObj emits usemtl commands that refer to colours in the standard RISC OS 256-colour palette by derived material names such as 'riscos_31'. (This naming convention is for compatibility with a similar program, SF3KtoObj.)

Convert the player's aircraft, outputting colour numbers to the screen:

*ChocToObj -index 18 <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

# 2 primitives
g tiger tiger_0
usemtl riscos_19
f 27 26 25 24
usemtl riscos_24
f 1 2 4 3
...

If the switch -human is used then ChocToObj instead generates human-readable material names such as 'black_0'.

Convert the player's aircraft, outputting readable material names to the screen:

*ChocToObj -index 18 -human <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

# 2 primitives
g tiger tiger_0
usemtl maroon_3
f 27 26 25 24
usemtl tyrianpurple_0
f 1 2 4 3
...

By default, ChocToObj emits a mtllib command which references sf3k.mtl as the material library file to be used when drawing objects; this is the same as the name of the supplied MTL file.

An alternative material library file can be specified using the switch -mtllib. The named file is not created, read or written by ChocToObj.

False colours can be assigned to help visualise boundaries between polygons, especially between coplanar polygons of the same colour. This mode, which is mainly useful for debugging, is enabled by specifying the switch -false.

When false colours are enabled, the physical colours in the input file are ignored.

Clipping

Switches:

-clip
Clip overlapping coplanar polygons
-thick N
Line thickness (N=0..100, default 0)

Some objects are liable to suffer from a phenomenon known as "Z-fighting" if they are part of a scene rendered using a depth (Z) buffer. It is caused by overlapping faces in the same geometric plane and typically manifests as a shimmering pattern in a rendered image. Essentially two or more polygons occupy the same points in 3D space.

The game uses painter's algorithm to ensure that overlapping objects are drawn correctly (drawing more distant objects before nearer ones), instead of using a depth buffer. It also draws the polygons of each object in a predictable order, which allows overlapping polygons to be used as decals (e.g. as doors and windows on buildings).

If the switch -clip is used then the rearmost of two overlapping polygons is split by dividing it along the line obtained by extending one edge of the front polygon to infinity in both directions. This process is repeated until no two edges intersect. Any polygons that are fully hidden (typically behind decals) are deleted.

The following diagrams illustrate how one polygon (B: 1 2 3 4) overlapped by another (A: 5 6 7 8) is split into five polygons (B..F) during the clipping process. The last polygon (F) is then deleted because it duplicates the overlapping polygon (A).

Some of the game's models include lines which are coplanar with (and completely contained by) a polygon that is drawn first. Examples include the middle stripe on a road. This causes "Z-fighting" between the line and the polygon.

If the parameter -thick N is used then such lines will be replaced with rectangular polygons with the same length and centreline as the original line but a width specified by the user. This serves two purposes:

  1. It allows the coplanar polygon clipper (if enabled) to split the rearmost polygon, thereby solving the "Z-fighting" issue.
  2. It prevents the line from appearing disproportionately thin if the resolution of the frame buffer is higher than that used by the game.

Normal correction

Switches:

-flip
Flip back-facing polygons coplanar with z=0

The order in which vertices are specified in a primitive definition determines the direction of a polygon's normal vector and therefore whether or not it is facing the camera in any given scene.

The game draws primitives in the order they are defined without using a depth buffer. Some models are sufficiently complex that back-face culling is required to prevent near polygons being overdrawn by far polygons; others require polygons to be visible from both sides.

Back-face culling is enabled per object instance in the game's map data, therefore it isn't possible to predict whether the order of vertices in a polygon definition is significant without knowing the model's intended usage. One aerodrome has two runways and one taxiway defined anti-clockwise (facing the sky) but two other taxiways defined clockwise (facing the earth).

ChocToObj can infer that polygons should not be facing the earth if they belong to a model in which all primitives are coplanar with the xy plane at z=0. The switch -flip reverses the order of vertices for such polygons to make them face the sky instead.

Model simplification

Switches:

-simple
Output simplified models

The game supports two levels of detail for each model, selected according to its distance from the camera (e.g. a distant house might be drawn without its windows). By default, ChocToObj outputs primitives that belong to both high-and low-detail versions of a model in group 0 and any remaining primitives (required only for the high-detail model) in group 1.

If the switch -simple is used then ChocToObj reads only vertices and primitives belonging to the simplified version of each model. The effect is not quite as straightforward as only outputting primitives in group 0: if a polygon is always simplified at the same distance as (or closer than) the distance at which the whole model would be simplified then that polygon must be replaced with a line. In many cases this is necessary to avoid references to vertices that are not included in a simplified model's vertex count.

Output of faces

Switches:

-fans
Split complex polygons into triangle fans
-strips
Split complex polygons into triangle strips
-negative
Use negative vertex indices

The Wavefront OBJ format specification does not restrict the maximum number of vertices in a face element. Nevertheless, some programs cannot correctly display faces with more than three vertices. Two switches, -fans and -strips, are provided to split complex polygons into triangles.

Original Triangle fans Triangle strips
f 1 2 3 4 5 6
f 1 2 3
f 1 3 4
f 1 4 5
f 1 5 6
f 1 2 3
f 6 1 3
f 6 3 4
f 5 6 4

Vertices in a face element are normally indexed by their position in the output file, counting upwards from 1. If the output comprises more than one object definition then it can be more useful to count backwards from the most recent vertex definition, which is assigned index -1. The -negative switch enables this output mode, which allows object models to be separated, extracted or rearranged later.

Convert the Fokker VIII triplane with positive vertex indices:

*ChocToObj -index 30 <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

# 1 primitives
g triplane triplane_0
usemtl riscos_67
f 27 26 25 24

# 22 primitives
g triplane triplane_1
usemtl riscos_73
f 34 35 37 36
f 1 2 4 3
...

Convert the same object with negative vertex indices:

*ChocToObj -index 30 -neg <Chocks$Dir>.Maps.Land <Chocks$Dir>.Maps.Obj3D

# 1 primitives
g triplane triplane_0
usemtl riscos_67
f -11 -12 -13 -14

# 22 primitives
g triplane triplane_1
usemtl riscos_73
f -4 -3 -1 -2
f -37 -36 -34 -35
...

Hidden data

Switches:

-unused
Include unused vertices in the output
-duplicate
Include duplicate vertices in the output

It's common for model data to include vertex definitions that are not referenced by any primitive definition. Such vertices are not included in the output unless the -unused switch is used.

Some object definitions also include two or more vertices with the same coordinates, where both vertices are referenced by primitives. An example is vertices 6 and 7 of the aircraft carrier model. Such pairs of vertices are automatically merged unless the -duplicate switch is specified.

Getting diagnostic information

Switches:

-time
Show the total time for each file processed
-verbose or -debug
Emit debug information (and keep bad output)

If either of the switches -verbose and -debug is used then the program emits information about its internal operation on the standard output stream. However, this makes it slower and prevents output being piped to another program.

If the switch -time is used then the total time for each file processed (to centisecond precision) is printed. This can be used independently of -verbose and -debug.

When debugging output or the timer is enabled, you must specify an output file name. This is to prevent OBJ-format output being sent to the standard output stream and becoming mixed up with the diagnostic information.


Colour names

The colour names were taken from a variety of online sources including Wikipedia articles, W3C standards, the X Window System, and the results of the web comic XKCD's colour naming survey.

Index Web T0 T1 T2 T3 Name Notes
0 #000000 black * Identical to HTML 'black'
4 #440000 darkmaroon By analogy with 'darkred'
8 #000044 darknavy By analogy with 'darkblue'
12 #440044 darkpurple By analogy with 'darkmagenta'
16 #880000 maroon * Like HTML colour #800000
20 #cc0000 mediumred By analogy with 'mediumblue'
24 #880044 tyrianpurple Like Wikipedia colour #66023c
28 #cc0044 crimson * Like X11 colour #dc143c
32 #004400 darkgreen + Like X11 colour #006400
36 #444400 darkolive Like XKCD colour #373e02
40 #004444 darkteal Like XKCD colour #014d4e
44 #444444 darkgrey + Darker than X11 'dimgray'
48 #884400 brown * Like Wikipedia colour #964b00
52 #cc4400 mahogany Like Wikipedia colour #c04000
56 #884444 cordovan Like Wikipedia colour #893f45
60 #cc4444 brickred Like Wikipedia colour #cb4154
64 #008800 green * Like HTML colour #008000
68 #448800 avocado Like Wikipedia colour #568203
72 #008844 pigmentgreen Like Wikipedia colour #00a550
76 #448844 ferngreen Like Wikipedia colour #4f7942
80 #888800 olive * Like HTML colour #808000
84 #cc8800 harvestgold Like Wikipedia colour #da9100
88 #888844 darktan Like Wikipedia colour #918151
92 #cc8844 peru * Like X11 colour #cd853f
96 #00cc00 mediumgreen By analogy with 'mediumblue'
100 #44cc00 napiergreen Like Wikipedia colour #2a8000
104 #00cc44 darkpastelgreen Like Wikipedia colour #03c03c
108 #44cc44 limegreen * Like X11 colour #32cd32
112 #88cc00 applegreen Like Wikipedia colour #8db600
116 #cccc00 peridot Like Wikipedia colour #e6e200
120 #88cc44 yellowgreen * Like X11 colour #9acd32
124 #cccc44 oldgold Like Wikipedia colour #cfb53b
128 #000088 navy * Like HTML colour #000080
132 #440088 indigo * Like X11 colour #4b0082
136 #0000cc mediumblue * Like X11 colour #0000cd
140 #4400cc violetblue Like XKCD colour #510ac9
144 #880088 purple * Like HTML colour #800080
148 #cc0088 mediumvioletred * Like X11 colour #c71585
152 #8800cc darkviolet * Like X11 colour #9400d3
156 #cc00cc deepmagenta Like Wikipedia colour #cc00cc
160 #004488 mediumelectricblue Like Wikipedia colour #035096
164 #444488 darkslateblue * Like X11 colour #483d8b
168 #0044cc royalazure Like Wikipedia colour #0038a8
172 #4444cc pigmentblue Like Wikipedia colour #333399
176 #884488 plum + Like Wikipedia colour #8e4585
180 #cc4488 mulberry Like Wikipedia colour #c54b8c
184 #8844cc lavenderindigo Like Wikipedia colour #9457eb
188 #cc44cc deepfuchsia Like Wikipedia colour #c154c1
192 #008888 teal * Like HTML colour #008080
196 #448888 dustyteal Like XKCD colour #4c9085
200 #0088cc honolulublue Like Wikipedia colour #007fbf
204 #4488cc celestialblue Like Wikipedia colour #4997d0
208 #888888 grey * Like HTML colour #808080
212 #cc8888 oldrose Like Wikipedia colour #c08081
216 #8888cc ube Like Wikipedia colour #8878c3
220 #cc88cc pastelviolet Like Wikipedia colour #cb99c9
224 #00cc88 caribbeangreen Like Wikipedia colour #00cc99
228 #44cc88 mint Like Wikipedia colour #3eb479
232 #00cccc darkturquoise * Like X11 colour #00ced1
236 #44cccc mediumturquoise * Like X11 colour #48d1cc
240 #88cc88 darkseagreen * Like X11 colour #8fbc8f
244 #cccc88 lightbeige Like Ford colour #d2d08e
248 #88cccc pearlaqua Like Wikipedia colour #88d8c0
252 #cccccc lightgrey * Like X11 colour #d3d3d3

* means that a colour has the same name as one of the standard X11 colours and closely resembles it.

+ means that a colour has the same name as one of the standard X11 colours but is significantly darker.


Program history

0.01 (02 Sep 2018)


Compiling the software

Source code is only supplied for the command-line program. To compile and link the program you will also require an ISO 9899:1999 standard 'C' library, release 55 (or later) of CBLibrary, and a new library named 3dObjLib.

Two make files are supplied:

The APCS variant specified for the Norcroft compiler is 32 bit for compatibility with ARMv5 and fpe2 for compatibility with older versions of the floating point emulator. Generation of unaligned data loads/stores is disabled for compatibility with ARMv6. When building the code for release, it is linked with RISCOS Ltd's generic C library stubs ('StubsG').

Before compiling the program for other platforms, rename the C source and header files with .c and .h file extensions then move them out of their respective subdirectories. You must also ensure that the macros DIR_SEPARATOR and EXT_SEPARATOR (defined in CBlibrary) have appropriate values for the target platform's file system (e.g. '\' and '.' on Windows).


Licence and Disclaimer

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public Licence as published by the Free Software Foundation; either version 2 of the Licence, or (at your option) any later version.

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 Licence for more details.

You should have received a copy of the GNU General Public Licence along with these programs; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

This program uses CBLibrary and 3dObjLib, which are © 2018 Christopher Bazley. These libraries and their use are covered by the GNU Lesser General Public Licence.


Credits

ChocToObj was designed and programmed by Christopher Bazley.

My information on the Wavefront Object File and Material Library File formats came from Paul Bourke's copies of the file format specifications for the Advanced Visualizer software (© 1995 Alias|Wavefront, Inc.)

Some colour names were taken from a survey run by the web comic XKCD. The data is in the public domain, available under a Creative Commons licence. Thanks to Keith McKillop for suggesting this source.

The game Chocks Away is © 1990, The Fourth Dimension.

Valid HTML 4.0!